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"
58 Downloader::Downloader ()
59 : DependencyObject (Type::DOWNLOADER
)
61 LOG_DOWNLOADER ("Downloader::Downloader ()\n");
63 downloader_state
= Downloader::create_state (this);
74 custom_header_support
= false;
75 disable_cache
= false;
85 Downloader::~Downloader ()
87 LOG_DOWNLOADER ("Downloader::~Downloader ()\n");
89 Downloader::destroy_state (downloader_state
);
96 // mms code relies on the internal downloader to be alive while it has a ref on the downloader
97 // update mms code if this assumption changes.
98 if (internal_dl
!= NULL
)
99 internal_dl
->unref ();
103 Downloader::InternalAbort ()
105 LOG_DOWNLOADER ("Downloader::InternalAbort ()\n");
109 abort_func (downloader_state
);
115 LOG_DOWNLOADER ("Downloader::Abort ()\n");
117 SetCurrentDeployment ();
119 if (!aborted
&& !failed_msg
) {
121 SetDownloadProgress (0.0);
128 Downloader::GetDownloadedFilename (const char *partname
)
130 LOG_DOWNLOADER ("Downloader::GetDownloadedFilename (%s)\n", filename
);
132 g_return_val_if_fail (internal_dl
!= NULL
&& internal_dl
->Is (Type::FILEDOWNLOADER
), NULL
);
134 // This is a horrible hack to work around mozilla bug #444160
135 // Basically if a very small file is downloaded (<64KB in mozilla as of Jan5/09
136 // it can be inserted into a shared cache map, and served up to us without ever
137 // giving us the filename for a NP_ASFILE request.
138 if (buffer
!= NULL
) {
139 FileDownloader
*fdl
= (FileDownloader
*) internal_dl
;
143 tmpfile
= g_build_filename (g_get_tmp_dir (), "mozilla-workaround-XXXXXX", NULL
);
144 if ((fd
= g_mkstemp (tmpfile
)) == -1) {
149 if (write_all (fd
, buffer
, (size_t) total
) == -1) {
158 fdl
->SetFilename (tmpfile
);
159 fdl
->SetUnlink (true);
165 return internal_dl
->GetDownloadedFilename (partname
);
169 Downloader::GetResponseText (const char *PartName
, gint64
*size
)
171 LOG_DOWNLOADER ("Downloader::GetResponseText (%s, %p)\n", PartName
, size
);
173 // This is a horrible hack to work around mozilla bug #444160
174 // Basically if a very small file is downloaded (<64KB in mozilla as of Jan5/09
175 // it can be inserted into a shared cache map, and served up to us without ever
176 // giving us the filename for a NP_ASFILE request.
177 if (PartName
== NULL
&& buffer
!= NULL
) {
183 TextStream
*stream
= new TextStream ();
185 if (!stream
->OpenBuffer (buffer
, total
)) {
190 buf
= g_byte_array_new ();
191 while ((nread
= stream
->Read (b
, sizeof (b
))) > 0)
192 g_byte_array_append (buf
, (const guint8
*) b
, nread
);
196 g_byte_array_append (buf
, (const guint8
*) "", 1);
197 data
= (char *) buf
->data
;
199 g_byte_array_free (buf
, false);
205 return internal_dl
->GetResponseText (PartName
, size
);
209 Downloader::InternalOpen (const char *verb
, const char *uri
)
211 LOG_DOWNLOADER ("Downloader::InternalOpen (%s, %s) requires custom header support: %i\n", verb
, uri
, custom_header_support
);
213 open_func (downloader_state
, verb
, uri
, custom_header_support
, disable_cache
);
217 same_scheme (const Uri
*uri1
, const Uri
*uri2
)
219 return uri1
->GetScheme () && uri2
->GetScheme () &&
220 !strcmp (uri1
->GetScheme (), uri2
->GetScheme ());
224 same_domain (const Uri
*uri1
, const Uri
*uri2
)
226 const char *host1
= uri1
->GetHost ();
227 const char *host2
= uri2
->GetHost ();
230 return g_ascii_strcasecmp (host1
, host2
) == 0;
232 if (!host1
&& !host2
)
238 // Reference: URL Access Restrictions in Silverlight 2
239 // http://msdn.microsoft.com/en-us/library/cc189008(VS.95).aspx
241 Downloader::CheckRedirectionPolicy (const char *url
)
247 Uri
*source
= GetUri ();
248 if (Uri::IsNullOrEmpty (source
))
251 // if the (original) source is relative then the (final) 'url' will be the absolute version of the uri
252 // or if the source scheme is "file" then no server is present for redirecting the url somewhere else
253 if (!source
->IsAbsolute () || source
->IsScheme ("file"))
256 char *strsrc
= source
->ToString ();
257 // if the original URI and the end URI are identical then there was no redirection involved
258 bool retval
= (g_ascii_strcasecmp (strsrc
, url
) == 0);
263 // the destination URI
264 Uri
*dest
= new Uri ();
265 if (dest
->Parse (url
)) {
266 // there was a redirection, but is it allowed ?
267 switch (access_policy
) {
268 case DownloaderPolicy
:
269 // Redirection allowed for 'same domain' and 'same scheme'
270 // note: if 'dest' is relative then it's the same scheme and site
271 if (!dest
->IsAbsolute () || (same_domain (source
, dest
) && same_scheme (source
, dest
)))
275 // Redirection allowed for: 'same scheme' and 'same or different sites'
276 // note: if 'dest' is relative then it's the same scheme and site
277 if (!dest
->IsAbsolute () || same_scheme (source
, dest
))
283 case StreamingPolicy
:
284 // Redirection NOT allowed
287 // no policy (e.g. downloading codec EULA and binary) is allowed
298 // Reference: URL Access Restrictions in Silverlight 2
299 // http://msdn.microsoft.com/en-us/library/cc189008(VS.95).aspx
301 validate_policy (const char *location
, const Uri
*source
, DownloaderAccessPolicy policy
)
303 if (!location
|| !source
)
306 if (!source
->IsAbsolute ()) {
307 //relative uri, not checking policy
311 Uri
*target
= new Uri ();
312 if (!target
->Parse (location
)) {
319 case DownloaderPolicy
:
320 //Allowed schemes: http, https
321 if (!target
->IsScheme ("http") && !target
->IsScheme ("https"))
324 if (!same_scheme (target
, source
))
326 //X-Domain: requires policy file
327 // FIXME only managed is implemented
328 if (!same_domain (target
, source
))
332 case MediaPolicy
: //Media, images, ASX
333 //Allowed schemes: http, https, file
334 if (!target
->IsScheme ("http") && !target
->IsScheme ("https") && !target
->IsScheme ("file"))
337 if (!same_scheme (target
, source
))
342 //Allowed schemes: http, https, file
343 if (!target
->IsScheme ("http") && !target
->IsScheme ("https") && !target
->IsScheme ("file"))
346 if (!same_scheme (target
, source
))
348 //X-domain: allowed if not HTTPS to HTTPS
349 if (!same_domain (target
, source
) && target
->IsScheme ("https") && source
->IsScheme ("https"))
353 //Allowed schemes: http, https, file
354 if (!target
->IsScheme ("http") && !target
->IsScheme ("https") && !target
->IsScheme ("file"))
357 if (!same_scheme (target
, source
))
360 if (!same_domain (target
, source
))
363 case StreamingPolicy
: //Streaming media
364 //Allowed schemes: http
365 if (!target
->IsScheme ("http"))
367 //X-scheme: Not from https
368 if (source
->IsScheme ("https") && !same_scheme (source
, target
))
370 //X-domain: allowed if not HTTPS to HTTPS
371 if (!same_domain (target
, source
) && target
->IsScheme ("https") && source
->IsScheme ("https"))
384 Downloader::OpenInitialize ()
402 Downloader::Open (const char *verb
, const char *uri
, DownloaderAccessPolicy policy
)
404 LOG_DOWNLOADER ("Downloader::Open (%s, %s)\n", verb
, uri
);
408 Uri
*url
= new Uri ();
409 if (url
->Parse (uri
))
410 Open (verb
, url
, policy
);
416 Downloader::Open (const char *verb
, Uri
*url
, DownloaderAccessPolicy policy
)
420 LOG_DOWNLOADER ("Downloader::Open (%s, %p)\n", verb
, url
);
424 access_policy
= policy
;
426 if (!url
->isAbsolute
) {
427 const char *source_location
= NULL
;
428 source_location
= GetDeployment ()->GetXapLocation ();
429 if (source_location
) {
430 src_uri
= new Uri ();
431 if (!src_uri
->Parse (source_location
)) {
435 src_uri
->Combine (url
);
440 //FIXME: ONLY VALIDATE IF USED FROM THE PLUGIN
442 (location
= g_strdup(GetDeployment ()->GetXapLocation ())) || (location
= g_strdup(GetSurface ()->GetSourceLocation ()));
443 if (!validate_policy (location
, url
, policy
)) {
444 LOG_DOWNLOADER ("aborting due to security policy violation\n");
445 failed_msg
= g_strdup ("Security Policy Violation");
453 if (url
->GetScheme () != NULL
&& strcmp (url
->GetScheme (), "mms") == 0) {
454 internal_dl
= (InternalDownloader
*) new MmsDownloader (this);
456 internal_dl
= (InternalDownloader
*) new FileDownloader (this);
462 char *struri
= url
->ToString ();
463 internal_dl
->Open (verb
, struri
);
469 Downloader::InternalSetHeader (const char *header
, const char *value
)
471 LOG_DOWNLOADER ("Downloader::InternalSetHeader (%s, %s)\n", header
, value
);
473 header_func (downloader_state
, header
, value
);
477 Downloader::InternalSetHeaderFormatted (const char *header
, char *value
)
479 InternalSetHeader (header
, (const char *) value
);
484 Downloader::InternalSetBody (void *body
, guint32 length
)
486 LOG_DOWNLOADER ("Downloader::InternalSetBody (%p, %u)\n", body
, length
);
488 body_func (downloader_state
, body
, length
);
492 Downloader::SendInternal ()
494 LOG_DOWNLOADER ("Downloader::SendInternal ()\n");
496 if (!GetSurface ()) {
497 // The plugin is already checking for surface before calling Send, so
498 // if we get here, it's either managed code doing something wrong or ourselves.
499 g_warning ("Downloader::SendInternal (): No surface!\n");
508 // Consumer is re-sending a request which finished successfully.
509 NotifyFinished (NULL
);
513 if (failed_msg
!= NULL
) {
514 // Consumer is re-sending a request which failed.
515 Emit (DownloadFailedEvent
, new ErrorEventArgs (DownloadError
, 1, failed_msg
));
522 send_func (downloader_state
);
526 send_async (EventObject
*user_data
)
528 Downloader
*downloader
= (Downloader
*) user_data
;
530 downloader
->SendInternal ();
536 LOG_DOWNLOADER ("Downloader::Send ()\n");
538 if (!GetSurface ()) {
539 // The plugin is already checking for surface before calling Send, so
540 // if we get here, it's either managed code doing something wrong or ourselves.
541 g_warning ("Downloader::Send (): No surface!\n");
551 AddTickCall (send_async
);
555 Downloader::SendNow ()
557 LOG_DOWNLOADER ("Downloader::SendNow ()\n");
567 // A zero write means that we are done
570 Downloader::Write (void *buf
, gint32 offset
, gint32 n
)
573 LOG_DOWNLOADER ("Downloader::Write (%p, %i, %i). Uri: %s\n", buf
, offset
, n
, (struri
= GetUri ()->ToString ()));
576 SetCurrentDeployment ();
584 internal_dl
->Write (buf
, offset
, n
);
588 Downloader::InternalWrite (void *buf
, gint32 offset
, gint32 n
)
590 LOG_DOWNLOADER ("Downloader::InternalWrite (%p, %i, %i)\n", buf
, offset
, n
);
598 if (file_size
>= 0) {
599 if ((progress
= total
/ (double) file_size
) > 1.0)
604 SetDownloadProgress (progress
);
606 Emit (DownloadProgressChangedEvent
);
609 writer (buf
, offset
, n
, user_data
);
611 // This is a horrible hack to work around mozilla bug #444160
612 // See Downloader::GetResponseText for an explanation
613 if (internal_dl
->GetObjectType () == Type::FILEDOWNLOADER
&& n
== total
&& total
< 65536) {
614 buffer
= (char *) g_malloc ((size_t) total
);
615 memcpy (buffer
, buf
, (size_t) total
);
620 Downloader::SetFilename (const char *fname
)
622 LOG_DOWNLOADER ("Downloader::SetFilename (%s)\n", fname
);
628 filename
= g_strdup (fname
);
630 internal_dl
->SetFilename (filename
);
634 Downloader::NotifyFinished (const char *final_uri
)
639 SetCurrentDeployment ();
644 SetDownloadProgress (1.0);
646 Emit (DownloadProgressChangedEvent
);
648 // HACK, we should provide the actual status text and code
649 SetStatusText ("OK");
654 Emit (CompletedEvent
, NULL
);
658 Downloader::NotifyFailed (const char *msg
)
660 LOG_DOWNLOADER ("Downloader::NotifyFailed (%s)\n", msg
);
662 /* if we've already been notified of failure, no-op */
666 SetCurrentDeployment ();
672 // For some reason the status is 0, not updated on errors?
674 Emit (DownloadFailedEvent
, new ErrorEventArgs (DownloadError
, 1, msg
));
676 // save the error in case someone else calls ::Send() on this
677 // downloader for the same uri.
678 failed_msg
= g_strdup (msg
);
682 Downloader::NotifySize (gint64 size
)
684 LOG_DOWNLOADER ("Downloader::NotifySize (%lld)\n", size
);
691 SetCurrentDeployment ();
697 notify_size (size
, user_data
);
701 Downloader::Started ()
703 LOG_DOWNLOADER ("Downloader::Started (): %i\n", started
);
709 Downloader::Completed ()
711 LOG_DOWNLOADER ("Downloader::Completed (), filename: %s\n", filename
);
717 Downloader::SetStreamFunctions (DownloaderWriteFunc writer
,
718 DownloaderNotifySizeFunc notify_size
,
721 LOG_DOWNLOADER ("Downloader::SetStreamFunctions\n");
723 this->notify_size
= notify_size
;
724 this->writer
= writer
;
725 this->user_data
= user_data
;
729 Downloader::SetFunctions (DownloaderCreateStateFunc create_state
,
730 DownloaderDestroyStateFunc destroy_state
,
731 DownloaderOpenFunc open
,
732 DownloaderSendFunc send
,
733 DownloaderAbortFunc abort
,
734 DownloaderHeaderFunc header
,
735 DownloaderBodyFunc body
,
736 DownloaderCreateWebRequestFunc request
,
737 DownloaderSetResponseHeaderCallbackFunc response_header_callback
,
738 DownloaderGetResponseFunc get_response
)
740 LOG_DOWNLOADER ("Downloader::SetFunctions\n");
741 Downloader::create_state
= create_state
;
742 Downloader::destroy_state
= destroy_state
;
743 Downloader::open_func
= open
;
744 Downloader::send_func
= send
;
745 Downloader::abort_func
= abort
;
746 Downloader::header_func
= header
;
747 Downloader::body_func
= body
;
748 Downloader::request_func
= request
;
749 Downloader::set_response_header_callback_func
= response_header_callback
;
750 Downloader::get_response_func
= get_response
;
755 * DownloaderRequest / DownloaderResponse
758 DownloaderResponse::~DownloaderResponse ()
760 if (request
!= NULL
&& request
->GetDownloaderResponse () == this)
761 request
->SetDownloaderResponse (NULL
);
762 GetDeployment ()->UnregisterDownloader (this);
765 DownloaderResponse::DownloaderResponse ()
773 SetDeployment (Deployment::GetCurrent ());
774 GetDeployment ()->RegisterDownloader (this);
777 DownloaderResponse::DownloaderResponse (DownloaderResponseStartedHandler started
, DownloaderResponseDataAvailableHandler available
, DownloaderResponseFinishedHandler finished
, gpointer context
)
779 this->aborted
= false;
780 this->started
= started
;
781 this->available
= available
;
782 this->finished
= finished
;
783 this->context
= context
;
784 this->request
= NULL
;
785 SetDeployment (Deployment::GetCurrent ());
786 GetDeployment ()->RegisterDownloader (this);
789 DownloaderRequest::DownloaderRequest (const char *method
, const char *uri
)
791 this->method
= g_strdup (method
);
792 this->uri
= g_strdup (uri
);
793 this->response
= NULL
;
794 SetDeployment (Deployment::GetCurrent ());
795 GetDeployment ()->RegisterDownloader (this);
798 DownloaderRequest::~DownloaderRequest ()
802 if (response
!= NULL
&& response
->GetDownloaderRequest () == this)
803 response
->SetDownloaderRequest (NULL
);
804 GetDeployment ()->UnregisterDownloader (this);
808 Downloader::CreateWebRequest (const char *method
, const char *uri
)
810 return GetRequestFunc () (method
, uri
, GetContext ());
814 Downloader::SetResponseHeaderCallback (DownloaderResponseHeaderCallback callback
, gpointer context
)
816 if (set_response_header_callback_func
!= NULL
)
817 set_response_header_callback_func (downloader_state
, callback
, context
);
821 Downloader::GetResponse ()
823 if (get_response_func
!= NULL
)
824 return get_response_func (downloader_state
);
829 downloader_write (Downloader
*dl
, void *buf
, gint32 offset
, gint32 n
)
831 dl
->Write (buf
, offset
, n
);
835 downloader_notify_finished (Downloader
*dl
, const char *fname
)
837 dl
->SetFilename (fname
);
838 dl
->NotifyFinished (NULL
);
842 downloader_notify_error (Downloader
*dl
, const char *msg
)
844 dl
->NotifyFailed (msg
);
848 downloader_notify_size (Downloader
*dl
, gint64 size
)
850 dl
->NotifySize (size
);
855 dummy_downloader_create_state (Downloader
* dl
)
857 g_warning ("downloader_set_function has never been called.\n");
862 dummy_downloader_destroy_state (gpointer state
)
864 g_warning ("downloader_set_function has never been called.\n");
868 dummy_downloader_open (gpointer state
, const char *verb
, const char *uri
, bool custom_header_support
, bool disble_cache
)
870 g_warning ("downloader_set_function has never been called.\n");
874 dummy_downloader_send (gpointer state
)
876 g_warning ("downloader_set_function has never been called.\n");
880 dummy_downloader_abort (gpointer state
)
882 g_warning ("downloader_set_function has never been called.\n");
886 dummy_downloader_header (gpointer state
, const char *header
, const char *value
)
888 g_warning ("downloader_set_function has never been called.\n");
892 dummy_downloader_body (gpointer state
, void *body
, guint32 length
)
894 g_warning ("downloader_set_function has never been called.\n");
898 dummy_downloader_create_web_request (const char *method
, const char *uri
, gpointer context
)
900 g_warning ("downloader_set_function has never been called.\n");
905 dummy_downloader_set_response_header_callback (gpointer state
, DownloaderResponseHeaderCallback callback
, gpointer context
)
907 g_warning ("downloader_set_function has never been called.\n");
910 static DownloaderResponse
*
911 dummy_downloader_get_response (gpointer state
)
913 g_warning ("downloader_set_function has never been called.\n");
917 DownloaderCreateStateFunc
Downloader::create_state
= dummy_downloader_create_state
;
918 DownloaderDestroyStateFunc
Downloader::destroy_state
= dummy_downloader_destroy_state
;
919 DownloaderOpenFunc
Downloader::open_func
= dummy_downloader_open
;
920 DownloaderSendFunc
Downloader::send_func
= dummy_downloader_send
;
921 DownloaderAbortFunc
Downloader::abort_func
= dummy_downloader_abort
;
922 DownloaderHeaderFunc
Downloader::header_func
= dummy_downloader_header
;
923 DownloaderBodyFunc
Downloader::body_func
= dummy_downloader_body
;
924 DownloaderCreateWebRequestFunc
Downloader::request_func
= dummy_downloader_create_web_request
;
925 DownloaderSetResponseHeaderCallbackFunc
Downloader::set_response_header_callback_func
= dummy_downloader_set_response_header_callback
;
926 DownloaderGetResponseFunc
Downloader::get_response_func
= dummy_downloader_get_response
;
929 downloader_init (void)