1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
3 * downloader.cpp: Downloader class.
5 * The downloader implements two modes of operation:
7 * bare bones: this is the interface expected by Javascript and C#
8 * this is the default if the caller does not call
9 * Downloader::SetWriteFunc
11 * progressive: this interface is used internally by the Image
12 * class to do progressive loading. If you want to
13 * use this mode, you must call the SetWriteFunc routine
14 * to install your callbacks before starting the download.
17 * Need a mechanism to notify the managed client of errors during
20 * Need to provide the buffer we downloaded to GetResponseText(string PartName)
21 * so we can return the response text for the given part name.
23 * The providers should store the files *somewhere* and should be able
24 * to respond to the "GetResponsetext" above on demand. The current
25 * code in demo.cpp and ManagedDownloader are not complete in this regard as
29 * Moonlight List (moonlight-list@lists.ximian.com)
31 * Copyright 2007 Novell, Inc. (http://www.novell.com)
33 * See the LICENSE file included with the distribution for details.
40 #include <sys/types.h>
45 #include "downloader.h"
46 #include "file-downloader.h"
47 #include "mms-downloader.h"
53 #include "deployment.h"
59 Downloader::Downloader ()
60 : DependencyObject (Type::DOWNLOADER
)
62 LOG_DOWNLOADER ("Downloader::Downloader ()\n");
64 downloader_state
= Downloader::create_state (this);
75 custom_header_support
= false;
76 disable_cache
= false;
86 Downloader::~Downloader ()
88 LOG_DOWNLOADER ("Downloader::~Downloader ()\n");
90 Downloader::destroy_state (downloader_state
);
97 // mms code relies on the internal downloader to be alive while it has a ref on the downloader
98 // update mms code if this assumption changes.
99 if (internal_dl
!= NULL
)
100 internal_dl
->unref ();
104 Downloader::InternalAbort ()
106 LOG_DOWNLOADER ("Downloader::InternalAbort ()\n");
110 abort_func (downloader_state
);
116 LOG_DOWNLOADER ("Downloader::Abort ()\n");
118 SetCurrentDeployment ();
120 if (!aborted
&& !failed_msg
) {
122 SetDownloadProgress (0.0);
129 Downloader::GetDownloadedFilename (const char *partname
)
131 LOG_DOWNLOADER ("Downloader::GetDownloadedFilename (%s)\n", filename
);
133 g_return_val_if_fail (internal_dl
!= NULL
&& internal_dl
->Is (Type::FILEDOWNLOADER
), NULL
);
135 // This is a horrible hack to work around mozilla bug #444160
136 // Basically if a very small file is downloaded (<64KB in mozilla as of Jan5/09
137 // it can be inserted into a shared cache map, and served up to us without ever
138 // giving us the filename for a NP_ASFILE request.
139 if (buffer
!= NULL
) {
140 FileDownloader
*fdl
= (FileDownloader
*) internal_dl
;
144 tmpfile
= g_build_filename (g_get_tmp_dir (), "mozilla-workaround-XXXXXX", NULL
);
145 if ((fd
= g_mkstemp (tmpfile
)) == -1) {
150 if (write_all (fd
, buffer
, (size_t) total
) == -1) {
159 fdl
->SetFilename (tmpfile
);
160 fdl
->SetUnlink (true);
166 return internal_dl
->GetDownloadedFilename (partname
);
170 Downloader::GetResponseText (const char *PartName
, gint64
*size
)
172 LOG_DOWNLOADER ("Downloader::GetResponseText (%s, %p)\n", PartName
, size
);
174 // This is a horrible hack to work around mozilla bug #444160
175 // Basically if a very small file is downloaded (<64KB in mozilla as of Jan5/09
176 // it can be inserted into a shared cache map, and served up to us without ever
177 // giving us the filename for a NP_ASFILE request.
178 if (PartName
== NULL
&& buffer
!= NULL
) {
184 TextStream
*stream
= new TextStream ();
186 if (!stream
->OpenBuffer (buffer
, total
)) {
191 buf
= g_byte_array_new ();
192 while ((nread
= stream
->Read (b
, sizeof (b
))) > 0)
193 g_byte_array_append (buf
, (const guint8
*) b
, nread
);
197 g_byte_array_append (buf
, (const guint8
*) "", 1);
198 data
= (char *) buf
->data
;
200 g_byte_array_free (buf
, false);
206 return internal_dl
->GetResponseText (PartName
, size
);
210 Downloader::InternalOpen (const char *verb
, const char *uri
)
212 LOG_DOWNLOADER ("Downloader::InternalOpen (%s, %s) requires custom header support: %i\n", verb
, uri
, custom_header_support
);
214 open_func (downloader_state
, verb
, uri
, custom_header_support
, disable_cache
);
218 same_scheme (const Uri
*uri1
, const Uri
*uri2
)
220 return uri1
->GetScheme () && uri2
->GetScheme () &&
221 !strcmp (uri1
->GetScheme (), uri2
->GetScheme ());
225 same_domain (const Uri
*uri1
, const Uri
*uri2
)
227 const char *host1
= uri1
->GetHost ();
228 const char *host2
= uri2
->GetHost ();
231 return g_ascii_strcasecmp (host1
, host2
) == 0;
233 if (!host1
&& !host2
)
239 // Reference: URL Access Restrictions in Silverlight 2
240 // http://msdn.microsoft.com/en-us/library/cc189008(VS.95).aspx
242 Downloader::CheckRedirectionPolicy (const char *url
)
248 Uri
*source
= GetUri ();
249 if (Uri::IsNullOrEmpty (source
))
252 // if the (original) source is relative then the (final) 'url' will be the absolute version of the uri
253 // or if the source scheme is "file" then no server is present for redirecting the url somewhere else
254 if (!source
->IsAbsolute () || source
->IsScheme ("file"))
257 char *strsrc
= source
->ToString ();
258 // if the original URI and the end URI are identical then there was no redirection involved
259 bool retval
= (g_ascii_strcasecmp (strsrc
, url
) == 0);
264 // the destination URI
265 Uri
*dest
= new Uri ();
266 if (dest
->Parse (url
)) {
267 // there was a redirection, but is it allowed ?
268 switch (access_policy
) {
269 case DownloaderPolicy
:
270 // Redirection allowed for 'same domain' and 'same scheme'
271 // note: if 'dest' is relative then it's the same scheme and site
272 if (!dest
->IsAbsolute () || (same_domain (source
, dest
) && same_scheme (source
, dest
)))
276 // Redirection allowed for: 'same scheme' and 'same or different sites'
277 // note: if 'dest' is relative then it's the same scheme and site
278 if (!dest
->IsAbsolute () || same_scheme (source
, dest
))
284 case StreamingPolicy
:
285 // Redirection NOT allowed
288 // no policy (e.g. downloading codec EULA and binary) is allowed
299 // Reference: URL Access Restrictions in Silverlight 2
300 // http://msdn.microsoft.com/en-us/library/cc189008(VS.95).aspx
302 validate_policy (const char *location
, const Uri
*source
, DownloaderAccessPolicy policy
)
304 if (!location
|| !source
)
307 if (!source
->IsAbsolute ()) {
308 //relative uri, not checking policy
312 Uri
*target
= new Uri ();
313 if (!target
->Parse (location
)) {
320 case DownloaderPolicy
:
321 //Allowed schemes: http, https
322 if (!target
->IsScheme ("http") && !target
->IsScheme ("https"))
325 if (!same_scheme (target
, source
))
327 //X-Domain: requires policy file
328 // FIXME only managed is implemented
329 if (!same_domain (target
, source
))
333 case MediaPolicy
: //Media, images, ASX
334 //Allowed schemes: http, https, file
335 if (!target
->IsScheme ("http") && !target
->IsScheme ("https") && !target
->IsScheme ("file"))
338 if (!same_scheme (target
, source
))
343 //Allowed schemes: http, https, file
344 if (!target
->IsScheme ("http") && !target
->IsScheme ("https") && !target
->IsScheme ("file"))
347 if (!same_scheme (target
, source
))
349 //X-domain: allowed if not HTTPS to HTTPS
350 if (!same_domain (target
, source
) && target
->IsScheme ("https") && source
->IsScheme ("https"))
354 //Allowed schemes: http, https, file
355 if (!target
->IsScheme ("http") && !target
->IsScheme ("https") && !target
->IsScheme ("file"))
358 if (!same_scheme (target
, source
))
361 if (!same_domain (target
, source
))
364 case StreamingPolicy
: //Streaming media
365 //Allowed schemes: http
366 if (!target
->IsScheme ("http"))
368 //X-scheme: Not from https
369 if (source
->IsScheme ("https") && !same_scheme (source
, target
))
371 //X-domain: allowed if not HTTPS to HTTPS
372 if (!same_domain (target
, source
) && target
->IsScheme ("https") && source
->IsScheme ("https"))
385 Downloader::OpenInitialize ()
403 Downloader::Open (const char *verb
, const char *uri
, DownloaderAccessPolicy policy
)
405 LOG_DOWNLOADER ("Downloader::Open (%s, %s)\n", verb
, uri
);
409 Uri
*url
= new Uri ();
410 if (url
->Parse (uri
))
411 Open (verb
, url
, policy
);
417 Downloader::ValidateDownloadPolicy (const char *source_location
, Uri
*uri
, DownloaderAccessPolicy policy
)
422 if (!uri
->isAbsolute
&& source_location
) {
423 src_uri
= new Uri ();
424 if (!src_uri
->Parse (source_location
, true)) {
429 src_uri
->Combine (uri
);
433 valid
= validate_policy (source_location
, uri
, policy
);
440 Downloader::Open (const char *verb
, Uri
*uri
, DownloaderAccessPolicy policy
)
442 const char *source_location
;
447 LOG_DOWNLOADER ("Downloader::Open (%s, %p)\n", verb
, uri
);
451 access_policy
= policy
;
453 if (!(source_location
= GetDeployment ()->GetXapLocation ()))
454 source_location
= GetDeployment ()->GetSurface ()->GetSourceLocation ();
456 // FIXME: ONLY VALIDATE IF USED FROM THE PLUGIN
457 if (!Downloader::ValidateDownloadPolicy (source_location
, uri
, policy
)) {
458 LOG_DOWNLOADER ("aborting due to security policy violation\n");
459 failed_msg
= g_strdup ("Security Policy Violation");
464 if (!uri
->isAbsolute
&& source_location
) {
465 src_uri
= new Uri ();
466 if (!src_uri
->Parse (source_location
, true)) {
471 src_uri
->Combine (uri
);
475 if (policy
== StreamingPolicy
) {
476 internal_dl
= (InternalDownloader
*) new MmsDownloader (this);
478 internal_dl
= (InternalDownloader
*) new FileDownloader (this);
485 str
= url
->ToString ();
488 internal_dl
->Open (verb
, str
);
493 Downloader::InternalSetHeader (const char *header
, const char *value
)
495 LOG_DOWNLOADER ("Downloader::InternalSetHeader (%s, %s)\n", header
, value
);
497 header_func (downloader_state
, header
, value
);
501 Downloader::InternalSetHeaderFormatted (const char *header
, char *value
)
503 InternalSetHeader (header
, (const char *) value
);
508 Downloader::InternalSetBody (void *body
, guint32 length
)
510 LOG_DOWNLOADER ("Downloader::InternalSetBody (%p, %u)\n", body
, length
);
512 body_func (downloader_state
, body
, length
);
516 Downloader::SendInternal ()
518 LOG_DOWNLOADER ("Downloader::SendInternal ()\n");
520 if (!IsAttached ()) {
521 // The plugin is already checking if we're attached before calling Send, so
522 // if we get here, it's either managed code doing something wrong or ourselves.
523 g_warning ("Downloader::SendInternal (): Not attached!\n");
532 // Consumer is re-sending a request which finished successfully.
533 NotifyFinished (NULL
);
537 if (failed_msg
!= NULL
) {
538 // Consumer is re-sending a request which failed.
539 Emit (DownloadFailedEvent
, new ErrorEventArgs (DownloadError
,
540 MoonError (MoonError::EXCEPTION
, 1, failed_msg
)));
547 send_func (downloader_state
);
551 send_async (EventObject
*user_data
)
553 Downloader
*downloader
= (Downloader
*) user_data
;
555 downloader
->SendInternal ();
561 LOG_DOWNLOADER ("Downloader::Send ()\n");
563 if (!IsAttached ()) {
564 // The plugin is already checking if we're attached before calling Send, so
565 // if we get here, it's either managed code doing something wrong or ourselves.
566 g_warning ("Downloader::Send (): Not attached!\n");
576 AddTickCall (send_async
);
580 Downloader::SendNow ()
582 LOG_DOWNLOADER ("Downloader::SendNow ()\n");
592 // A zero write means that we are done
595 Downloader::Write (void *buf
, gint32 offset
, gint32 n
)
598 LOG_DOWNLOADER ("Downloader::Write (%p, %i, %i). Uri: %s\n", buf
, offset
, n
, (struri
= GetUri ()->ToString ()));
601 SetCurrentDeployment ();
609 internal_dl
->Write (buf
, offset
, n
);
613 Downloader::InternalWrite (void *buf
, gint32 offset
, gint32 n
)
615 LOG_DOWNLOADER ("Downloader::InternalWrite (%p, %i, %i)\n", buf
, offset
, n
);
623 if (file_size
>= 0) {
624 if ((progress
= total
/ (double) file_size
) > 1.0)
629 SetDownloadProgress (progress
);
631 Emit (DownloadProgressChangedEvent
);
634 writer (buf
, offset
, n
, user_data
);
636 // This is a horrible hack to work around mozilla bug #444160
637 // See Downloader::GetResponseText for an explanation
638 if (internal_dl
->GetObjectType () == Type::FILEDOWNLOADER
&& n
== total
&& total
< 65536) {
639 buffer
= (char *) g_malloc ((size_t) total
);
640 memcpy (buffer
, buf
, (size_t) total
);
645 Downloader::SetFilename (const char *fname
)
647 LOG_DOWNLOADER ("Downloader::SetFilename (%s)\n", fname
);
653 filename
= g_strdup (fname
);
655 internal_dl
->SetFilename (filename
);
659 Downloader::NotifyFinished (const char *final_uri
)
664 SetCurrentDeployment ();
669 SetDownloadProgress (1.0);
671 Emit (DownloadProgressChangedEvent
);
673 // HACK, we should provide the actual status text and code
674 SetStatusText ("OK");
679 Emit (CompletedEvent
, NULL
);
683 Downloader::NotifyFailed (const char *msg
)
685 LOG_DOWNLOADER ("Downloader::NotifyFailed (%s)\n", msg
);
687 /* if we've already been notified of failure, no-op */
691 SetCurrentDeployment ();
697 // For some reason the status is 0, not updated on errors?
699 Emit (DownloadFailedEvent
, new ErrorEventArgs (DownloadError
,
700 MoonError (MoonError::EXCEPTION
, 1, msg
)));
702 failed_msg
= g_strdup (msg
);
706 Downloader::NotifySize (gint64 size
)
708 LOG_DOWNLOADER ("Downloader::NotifySize (%" G_GINT64_FORMAT
")\n", size
);
715 SetCurrentDeployment ();
721 notify_size (size
, user_data
);
725 Downloader::Started ()
727 LOG_DOWNLOADER ("Downloader::Started (): %i\n", started
);
733 Downloader::Completed ()
735 LOG_DOWNLOADER ("Downloader::Completed (), filename: %s\n", filename
);
741 Downloader::SetStreamFunctions (DownloaderWriteFunc writer
,
742 DownloaderNotifySizeFunc notify_size
,
745 LOG_DOWNLOADER ("Downloader::SetStreamFunctions\n");
747 this->notify_size
= notify_size
;
748 this->writer
= writer
;
749 this->user_data
= user_data
;
753 Downloader::SetFunctions (DownloaderCreateStateFunc create_state
,
754 DownloaderDestroyStateFunc destroy_state
,
755 DownloaderOpenFunc open
,
756 DownloaderSendFunc send
,
757 DownloaderAbortFunc abort
,
758 DownloaderHeaderFunc header
,
759 DownloaderBodyFunc body
,
760 DownloaderCreateWebRequestFunc request
,
761 DownloaderSetResponseHeaderCallbackFunc response_header_callback
,
762 DownloaderGetResponseFunc get_response
)
764 LOG_DOWNLOADER ("Downloader::SetFunctions\n");
765 Downloader::create_state
= create_state
;
766 Downloader::destroy_state
= destroy_state
;
767 Downloader::open_func
= open
;
768 Downloader::send_func
= send
;
769 Downloader::abort_func
= abort
;
770 Downloader::header_func
= header
;
771 Downloader::body_func
= body
;
772 Downloader::request_func
= request
;
773 Downloader::set_response_header_callback_func
= response_header_callback
;
774 Downloader::get_response_func
= get_response
;
779 * DownloaderRequest / DownloaderResponse
782 DownloaderResponse::~DownloaderResponse ()
784 if (request
!= NULL
&& request
->GetDownloaderResponse () == this)
785 request
->SetDownloaderResponse (NULL
);
786 GetDeployment ()->UnregisterDownloader (this);
789 DownloaderResponse::DownloaderResponse ()
797 SetDeployment (Deployment::GetCurrent ());
798 GetDeployment ()->RegisterDownloader (this);
801 DownloaderResponse::DownloaderResponse (DownloaderResponseStartedHandler started
, DownloaderResponseDataAvailableHandler available
, DownloaderResponseFinishedHandler finished
, gpointer context
)
803 this->aborted
= false;
804 this->started
= started
;
805 this->available
= available
;
806 this->finished
= finished
;
807 this->context
= context
;
808 this->request
= NULL
;
809 SetDeployment (Deployment::GetCurrent ());
810 GetDeployment ()->RegisterDownloader (this);
813 DownloaderRequest::DownloaderRequest (const char *method
, const char *uri
)
815 this->method
= g_strdup (method
);
816 this->uri
= g_strdup (uri
);
817 this->response
= NULL
;
818 SetDeployment (Deployment::GetCurrent ());
819 GetDeployment ()->RegisterDownloader (this);
822 DownloaderRequest::~DownloaderRequest ()
826 if (response
!= NULL
&& response
->GetDownloaderRequest () == this)
827 response
->SetDownloaderRequest (NULL
);
828 GetDeployment ()->UnregisterDownloader (this);
832 Downloader::CreateWebRequest (const char *method
, const char *uri
)
834 return GetRequestFunc () (method
, uri
, GetContext ());
838 Downloader::SetResponseHeaderCallback (DownloaderResponseHeaderCallback callback
, gpointer context
)
840 if (set_response_header_callback_func
!= NULL
)
841 set_response_header_callback_func (downloader_state
, callback
, context
);
845 Downloader::GetResponse ()
847 if (get_response_func
!= NULL
)
848 return get_response_func (downloader_state
);
853 downloader_write (Downloader
*dl
, void *buf
, gint32 offset
, gint32 n
)
855 dl
->Write (buf
, offset
, n
);
859 downloader_notify_finished (Downloader
*dl
, const char *fname
)
861 dl
->SetFilename (fname
);
862 dl
->NotifyFinished (NULL
);
866 downloader_notify_error (Downloader
*dl
, const char *msg
)
868 dl
->NotifyFailed (msg
);
872 downloader_notify_size (Downloader
*dl
, gint64 size
)
874 dl
->NotifySize (size
);
879 dummy_downloader_create_state (Downloader
* dl
)
881 g_warning ("downloader_set_function has never been called.\n");
886 dummy_downloader_destroy_state (gpointer state
)
888 g_warning ("downloader_set_function has never been called.\n");
892 dummy_downloader_open (gpointer state
, const char *verb
, const char *uri
, bool custom_header_support
, bool disble_cache
)
894 g_warning ("downloader_set_function has never been called.\n");
898 dummy_downloader_send (gpointer state
)
900 g_warning ("downloader_set_function has never been called.\n");
904 dummy_downloader_abort (gpointer state
)
906 g_warning ("downloader_set_function has never been called.\n");
910 dummy_downloader_header (gpointer state
, const char *header
, const char *value
)
912 g_warning ("downloader_set_function has never been called.\n");
916 dummy_downloader_body (gpointer state
, void *body
, guint32 length
)
918 g_warning ("downloader_set_function has never been called.\n");
922 dummy_downloader_create_web_request (const char *method
, const char *uri
, gpointer context
)
924 g_warning ("downloader_set_function has never been called.\n");
929 dummy_downloader_set_response_header_callback (gpointer state
, DownloaderResponseHeaderCallback callback
, gpointer context
)
931 g_warning ("downloader_set_function has never been called.\n");
934 static DownloaderResponse
*
935 dummy_downloader_get_response (gpointer state
)
937 g_warning ("downloader_set_function has never been called.\n");
941 DownloaderCreateStateFunc
Downloader::create_state
= dummy_downloader_create_state
;
942 DownloaderDestroyStateFunc
Downloader::destroy_state
= dummy_downloader_destroy_state
;
943 DownloaderOpenFunc
Downloader::open_func
= dummy_downloader_open
;
944 DownloaderSendFunc
Downloader::send_func
= dummy_downloader_send
;
945 DownloaderAbortFunc
Downloader::abort_func
= dummy_downloader_abort
;
946 DownloaderHeaderFunc
Downloader::header_func
= dummy_downloader_header
;
947 DownloaderBodyFunc
Downloader::body_func
= dummy_downloader_body
;
948 DownloaderCreateWebRequestFunc
Downloader::request_func
= dummy_downloader_create_web_request
;
949 DownloaderSetResponseHeaderCallbackFunc
Downloader::set_response_header_callback_func
= dummy_downloader_set_response_header_callback
;
950 DownloaderGetResponseFunc
Downloader::get_response_func
= dummy_downloader_get_response
;
953 downloader_init (void)