DownloadDevice.cpp
Go to the documentation of this file.
1 /*
2 //
3 // BEGIN SONGBIRD GPL
4 //
5 // This file is part of the Songbird web player.
6 //
7 // Copyright(c) 2005-2008 POTI, Inc.
8 // http://songbirdnest.com
9 //
10 // This file may be licensed under the terms of of the
11 // GNU General Public License Version 2 (the "GPL").
12 //
13 // Software distributed under the License is distributed
14 // on an "AS IS" basis, WITHOUT WARRANTY OF ANY KIND, either
15 // express or implied. See the GPL for the specific language
16 // governing rights and limitations.
17 //
18 // You should have received a copy of the GPL along with this
19 // program. If not, go to http://www.gnu.org/licenses/gpl.html
20 // or write to the Free Software Foundation, Inc.,
21 // 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
22 //
23 // END SONGBIRD GPL
24 //
25 */
26 
27 /* *****************************************************************************
28  *******************************************************************************
29  *
30  * Download device.
31  *
32  *******************************************************************************
33  ******************************************************************************/
34 
40 /* *****************************************************************************
41  *
42  * Download device imported services.
43  *
44  ******************************************************************************/
45 
46 /* Local imports. */
47 #include "DownloadDevice.h"
48 
49 /* Mozilla imports. */
50 #include <nsArrayUtils.h>
51 #include <nsAutoLock.h>
52 #include <nsComponentManagerUtils.h>
53 #include <nsCRT.h>
54 #include <nsIDOMWindow.h>
55 #include <nsILocalFile.h>
56 #include <nsINetUtil.h>
57 #include <nsIProperties.h>
58 #include <nsIStandardURL.h>
59 #include <nsIFileURL.h>
60 #include <nsISupportsPrimitives.h>
61 #include <nsITreeView.h>
62 #include <nsISupportsPrimitives.h>
63 #include <nsIURL.h>
64 #include <nsIUTF8ConverterService.h>
65 #include <nsIWindowWatcher.h>
66 #include <nsIResumableChannel.h>
67 #include <nsServiceManagerUtils.h>
68 #include <nsNetUtil.h>
69 #include <nsUnicharUtils.h>
70 
71 #include <prmem.h>
72 
73 /* Songbird imports. */
74 #include <sbILibraryManager.h>
75 #include <sbILocalDatabaseLibrary.h>
76 #include <sbIFileMetadataService.h>
77 #include <sbIJobProgress.h>
78 #include <sbIPropertyManager.h>
79 #include <sbStandardProperties.h>
80 
81 
82 /* *****************************************************************************
83  *
84  * Download device configuration.
85  *
86  ******************************************************************************/
87 
88 /*
89  * SB_DOWNLOAD_DEVICE_CATEGORY Download device category name.
90  * SB_DOWNLOAD_DEVICE_ID Download device identifier.
91  * SB_DOWNLOAD_DIR_DR Default download directory data remote.
92  * SB_TMP_DIR Songbird temporary directory name.
93  * SB_DOWNLOAD_TMP_DIR Download device temporary directory name.
94  * SB_DOWNLOAD_LIST_NAME Download device media list name.
95  * SB_STRING_BUNDLE_CHROME_URL URL for Songbird string bundle.
96  * SB_DOWNLOAD_COL_SPEC Default download device playlist column spec.
97  * SB_PREF_DOWNLOAD_LIBRARY Download library preference name.
98  * SB_PREF_WEB_LIBRARY Web library GUID preference name.
99  * SB_DOWNLOAD_PROGRESS_UPDATE_PERIOD_MS
100  * Update period for download progress.
101  * SB_DOWNLOAD_IDLE_TIMEOUT_MS How long to wait after progress before
102  * considering a download to be failed.
103  * SB_DOWNLOAD_PROGRESS_TIMER_MS
104  * How often should we update progress.
105  *
106  */
107 
108 #define SB_DOWNLOAD_DEVICE_CATEGORY \
109  NS_LITERAL_STRING("Songbird Download Device").get()
110 #define SB_DOWNLOAD_DEVICE_ID "download"
111 #define SB_DOWNLOAD_DIR_DR "download.folder"
112 #define SB_TMP_DIR "Songbird"
113 #define SB_DOWNLOAD_TMP_DIR "DownloadDevice"
114 #define SB_DOWNLOAD_LIST_NAME \
115  "&chrome://songbird/locale/songbird.properties#device.download"
116 #define SB_STRING_BUNDLE_CHROME_URL \
117  "chrome://songbird/locale/songbird.properties"
118 #define SB_DOWNLOAD_COL_SPEC \
119  "http://songbirdnest.com/data/1.0#trackName 179 http://songbirdnest.com/data/1.0#artistName 115 http://songbirdnest.com/data/1.0#albumName 115 http://songbirdnest.com/data/1.0#originPageImage 43 http://songbirdnest.com/data/1.0#downloadDetails 266 http://songbirdnest.com/data/1.0#downloadButton 73"
120 
121 #define SB_PREF_DOWNLOAD_MEDIALIST "songbird.library.download"
122 #define SB_PREF_WEB_LIBRARY "songbird.library.web"
123 #define SB_DOWNLOAD_CUSTOM_TYPE "download"
124 #define SB_DOWNLOAD_PROGRESS_UPDATE_PERIOD_MS 1000
125 #define SB_DOWNLOAD_IDLE_TIMEOUT_MS (60*1000)
126 #define SB_DOWNLOAD_PROGRESS_TIMER_MS 1000
127 
128 
129 /* *****************************************************************************
130  *
131  * Download device logging services.
132  *
133  ******************************************************************************/
134 
135 #ifdef PR_LOGGING
136 static PRLogModuleInfo* gLog = PR_NewLogModule("sbDownloadDevice");
137 #define TRACE(args) PR_LOG(gLog, PR_LOG_DEBUG, args)
138 #define LOG(args) PR_LOG(gLog, PR_LOG_WARN, args)
139 #else
140 #define TRACE(args) /* nothing */
141 #define LOG(args) /* nothing */
142 #endif
143 
144 // XXXAus: this should be moved into a base64 utilities class.
145 /* XXXErik: comment out to prevent unused warnings
146 static unsigned char *base = (unsigned char *)"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
147  */
148 
149 // XXXAus: this should be moved into a base64 utilities class.
150 static PRInt32 codetovalue( unsigned char c )
151 {
152  if( (c >= (unsigned char)'A') && (c <= (unsigned char)'Z') )
153  {
154  return (PRInt32)(c - (unsigned char)'A');
155  }
156  else if( (c >= (unsigned char)'a') && (c <= (unsigned char)'z') )
157  {
158  return ((PRInt32)(c - (unsigned char)'a') +26);
159  }
160  else if( (c >= (unsigned char)'0') && (c <= (unsigned char)'9') )
161  {
162  return ((PRInt32)(c - (unsigned char)'0') +52);
163  }
164  else if( (unsigned char)'+' == c )
165  {
166  return (PRInt32)62;
167  }
168  else if( (unsigned char)'/' == c )
169  {
170  return (PRInt32)63;
171  }
172  else
173  {
174  return -1;
175  }
176 }
177 
178 // XXXAus: this should be moved into a base64 utilities class.
179 static PRStatus decode4to3( const unsigned char *src,
180  unsigned char *dest )
181 {
182  PRUint32 b32 = (PRUint32)0;
183  PRInt32 bits;
184  PRIntn i;
185 
186  for( i = 0; i < 4; i++ )
187  {
188  bits = codetovalue(src[i]);
189  if( bits < 0 )
190  {
191  return PR_FAILURE;
192  }
193 
194  b32 <<= 6;
195  b32 |= bits;
196  }
197 
198  dest[0] = (unsigned char)((b32 >> 16) & 0xFF);
199  dest[1] = (unsigned char)((b32 >> 8) & 0xFF);
200  dest[2] = (unsigned char)((b32 ) & 0xFF);
201 
202  return PR_SUCCESS;
203 }
204 
205 // XXXAus: this should be moved into a base64 utilities class.
206 static PRStatus decode3to2( const unsigned char *src,
207  unsigned char *dest )
208 {
209  PRUint32 b32 = (PRUint32)0;
210  PRInt32 bits;
211  PRUint32 ubits;
212 
213  bits = codetovalue(src[0]);
214  if( bits < 0 )
215  {
216  return PR_FAILURE;
217  }
218 
219  b32 = (PRUint32)bits;
220  b32 <<= 6;
221 
222  bits = codetovalue(src[1]);
223  if( bits < 0 )
224  {
225  return PR_FAILURE;
226  }
227 
228  b32 |= (PRUint32)bits;
229  b32 <<= 4;
230 
231  bits = codetovalue(src[2]);
232  if( bits < 0 )
233  {
234  return PR_FAILURE;
235  }
236 
237  ubits = (PRUint32)bits;
238  b32 |= (ubits >> 2);
239 
240  dest[0] = (unsigned char)((b32 >> 8) & 0xFF);
241  dest[1] = (unsigned char)((b32 ) & 0xFF);
242 
243  return PR_SUCCESS;
244 }
245 
246 // XXXAus: this should be moved into a base64 utilities class.
247 static PRStatus decode2to1( const unsigned char *src,
248  unsigned char *dest )
249 {
250  PRUint32 b32;
251  PRUint32 ubits;
252  PRInt32 bits;
253 
254  bits = codetovalue(src[0]);
255  if( bits < 0 )
256  {
257  return PR_FAILURE;
258  }
259 
260  ubits = (PRUint32)bits;
261  b32 = (ubits << 2);
262 
263  bits = codetovalue(src[1]);
264  if( bits < 0 )
265  {
266  return PR_FAILURE;
267  }
268 
269  ubits = (PRUint32)bits;
270  b32 |= (ubits >> 4);
271 
272  dest[0] = (unsigned char)b32;
273 
274  return PR_SUCCESS;
275 }
276 
277 // XXXAus: this should be moved into a base64 utilities class.
278 static PRStatus decode( const unsigned char *src,
279  PRUint32 srclen,
280  unsigned char *dest )
281 {
282  PRStatus rv = PR_SUCCESS;
283 
284  while( srclen >= 4 )
285  {
286  rv = decode4to3(src, dest);
287  if( PR_SUCCESS != rv )
288  {
289  return PR_FAILURE;
290  }
291 
292  src += 4;
293  dest += 3;
294  srclen -= 4;
295  }
296 
297  switch( srclen )
298  {
299  case 3:
300  rv = decode3to2(src, dest);
301  break;
302  case 2:
303  rv = decode2to1(src, dest);
304  break;
305  case 1:
306  rv = PR_FAILURE;
307  break;
308  case 0:
309  rv = PR_SUCCESS;
310  break;
311  default:
312  PR_NOT_REACHED("coding error");
313  }
314 
315  return rv;
316 }
317 
318 // XXXAus: this should be moved into a base64 utilities class.
319 char * SB_Base64Decode( const char *src,
320  PRUint32 srclen,
321  char *dest )
322 {
323  PRStatus status;
324  PRBool allocated = PR_FALSE;
325 
326  if( (char *)0 == src )
327  {
328  return (char *)0;
329  }
330 
331  if( 0 == srclen )
332  {
333  srclen = strlen(src);
334  }
335 
336  if( srclen && (0 == (srclen & 3)) )
337  {
338  if( (char)'=' == src[ srclen-1 ] )
339  {
340  if( (char)'=' == src[ srclen-2 ] )
341  {
342  srclen -= 2;
343  }
344  else
345  {
346  srclen -= 1;
347  }
348  }
349  }
350 
351  if( (char *)0 == dest )
352  {
353  PRUint32 destlen = ((srclen * 3) / 4);
354  dest = (char *)PR_MALLOC(destlen + 1);
355  if( (char *)0 == dest )
356  {
357  return (char *)0;
358  }
359  dest[ destlen ] = (char)0; /* null terminate */
360  allocated = PR_TRUE;
361  }
362 
363  status = decode((const unsigned char *)src, srclen, (unsigned char *)dest);
364  if( PR_SUCCESS != status )
365  {
366  if( PR_TRUE == allocated )
367  {
368  PR_DELETE(dest);
369  }
370 
371  return (char *)0;
372  }
373 
374  return dest;
375 }
376 
377 // XXXAus: this should be moved into a MIME utilities class.
378 nsCString GetContentDispositionFilename(const nsACString &contentDisposition)
379 {
380  NS_NAMED_LITERAL_CSTRING(DISPOSITION_ATTACHEMENT, "attachment");
381  NS_NAMED_LITERAL_CSTRING(DISPOSITION_FILENAME, "filename=");
382 
383  nsCAutoString unicodeDisposition(contentDisposition);
384  unicodeDisposition.StripWhitespace();
385 
386  PRInt32 pos = unicodeDisposition.Find(DISPOSITION_ATTACHEMENT,
387  CaseInsensitiveCompare);
388  if(pos == -1 )
389  return EmptyCString();
390 
391  pos = unicodeDisposition.Find(DISPOSITION_FILENAME,
392  CaseInsensitiveCompare);
393  if(pos == -1)
394  return EmptyCString();
395 
396  pos += DISPOSITION_FILENAME.Length();
397 
398  // if the string is quoted, we look for the next quote to know when the
399  // filename ends.
400  PRInt32 endPos = -1;
401  if(unicodeDisposition.CharAt(pos) == '"') {
402 
403  pos++;
404  endPos = unicodeDisposition.FindChar('\"', pos);
405 
406  if(endPos == -1)
407  return EmptyCString();
408  }
409  // if not, we find the next ';' or we take the rest.
410  else {
411  endPos = unicodeDisposition.FindChar(';', pos);
412 
413  if(endPos == -1) {
414  endPos = unicodeDisposition.Length();
415  }
416  }
417 
418  nsCString filename(Substring(unicodeDisposition, pos, endPos - pos));
419 
420  // string is encoded in a different character set.
421  if(StringBeginsWith(filename, NS_LITERAL_CSTRING("=?")) &&
422  StringEndsWith(filename, NS_LITERAL_CSTRING("?="))) {
423 
424  nsresult rv;
425  nsCOMPtr<nsIUTF8ConverterService> convServ =
426  do_GetService("@mozilla.org/intl/utf8converterservice;1", &rv);
427  NS_ENSURE_SUCCESS(rv, EmptyCString());
428 
429  pos = 2;
430  endPos = filename.FindChar('?', pos);
431 
432  if(endPos == -1)
433  return EmptyCString();
434 
435  // found the charset
436  nsCAutoString charset(Substring(filename, pos, endPos - pos));
437  pos = endPos + 1;
438 
439  // find what encoding for the character set is used.
440  endPos = filename.FindChar('?', pos);
441 
442  if(endPos == -1)
443  return EmptyCString();
444 
445  nsCAutoString encoding(Substring(filename, pos, endPos - pos));
446  pos = endPos + 1;
447 
448  ToLowerCase(encoding);
449 
450  // bad encoding.
451  if(!encoding.EqualsLiteral("b") &&
452  !encoding.EqualsLiteral("q")) {
453  return EmptyCString();
454  }
455 
456  // end of actual string to decode marked by ?=
457  endPos = filename.FindChar('?', pos);
458  // didn't find end, bad string
459  if(endPos == -1 ||
460  filename.CharAt(endPos + 1) != '=')
461  return EmptyCString();
462 
463  nsCAutoString convertedFilename;
464  nsCAutoString filenameToDecode(Substring(filename, pos, endPos - pos));
465 
466  if(encoding.EqualsLiteral("b")) {
467  char *str = SB_Base64Decode(filenameToDecode.get(), filenameToDecode.Length(), nsnull);
468 
469  nsDependentCString strToConvert(str);
470  rv = convServ->ConvertStringToUTF8(strToConvert, charset.get(), PR_TRUE, convertedFilename);
471 
472  PR_Free(str);
473  }
474  else if(encoding.EqualsLiteral("q")) {
475  NS_WARNING("XXX: No support for Q encoded strings!");
476  }
477 
478  if(NS_SUCCEEDED(rv)) {
479  filename = convertedFilename;
480  }
481  }
482 
483  ReplaceChars(filename, nsDependentCString(FILE_ILLEGAL_CHARACTERS), '_');
484 
485  return filename;
486 }
487 
488 /* *****************************************************************************
489  *
490  * Download device nsISupports implementation.
491  *
492  ******************************************************************************/
493 
495  nsIObserver,
499 
500 
501 /* *****************************************************************************
502  *
503  * Download device services.
504  *
505  ******************************************************************************/
506 
507 /*
508  * sbDownloadDevice
509  *
510  * This method is the constructor for the download device class.
511  */
512 
514 :
515  sbDeviceBase(),
516  mpDeviceMonitor(nsnull)
517 {
518 }
519 
520 
521 /*
522  * ~sbDownloadDevice
523  *
524  * This method is the destructor for the download device class.
525  */
526 
528 {
529 }
530 
531 
532 /* *****************************************************************************
533  *
534  * Download device nsIObserver implementation.
535  *
536  ******************************************************************************/
537 
542 NS_IMETHODIMP sbDownloadDevice::Observe(
543  nsISupports *aSubject,
544  const char *aTopic,
545  const PRUnichar *aData)
546 {
547  nsresult rv;
548 
549  /* Validate arguments. */
550  NS_ENSURE_ARG_POINTER(aTopic);
551 
552  if (!strcmp("quit-application-granted", aTopic)) {
553  /* Shutdown the session. */
554  if (mpDownloadSession)
555  {
556  mpDownloadSession->Shutdown();
557  mpDownloadSession = nsnull;
558  }
559 
560  // remember to remove the observer too
561  nsCOMPtr<nsIObserverService> obsSvc =
562  do_GetService("@mozilla.org/observer-service;1", &rv);
563  NS_ENSURE_SUCCESS(rv, rv);
564  rv = obsSvc->RemoveObserver(this, aTopic);
565  NS_ENSURE_SUCCESS(rv, rv);
566 
567  return NS_OK;
568  }
569  return NS_ERROR_NOT_IMPLEMENTED;
570 }
571 
572 
573 /* *****************************************************************************
574  *
575  * Download device sbIDeviceBase implementation.
576  *
577  ******************************************************************************/
578 
583 NS_IMETHODIMP sbDownloadDevice::Initialize()
584 {
585  nsCOMPtr<sbIPropertyManager>
586  pPropertyManager;
587  nsCOMPtr<sbIURIPropertyInfo>
588  pURIPropertyInfo;
589  nsCOMPtr<sbILibraryManager> pLibraryManager;
590  nsresult rv;
591 
592  /* Initialize the base services. */
593  rv = Init();
594  NS_ENSURE_SUCCESS(rv, rv);
595 
596  /* Set the download device identifier. */
597  mDeviceIdentifier = NS_LITERAL_STRING(SB_DOWNLOAD_DEVICE_ID);
598 
599  /* Initialize the device state. */
600  rv = InitDeviceState(mDeviceIdentifier);
601  NS_ENSURE_SUCCESS(rv, rv);
602 
603  /* Create the download device lock. */
604  mpDeviceMonitor = nsAutoMonitor::NewMonitor
605  ("sbDownloadDevice::mpDeviceMonitor");
606  NS_ENSURE_TRUE(mpDeviceMonitor, NS_ERROR_OUT_OF_MEMORY);
607 
608  /* Get the IO service. */
609  mpIOService = do_GetService("@mozilla.org/network/io-service;1", &rv);
610  NS_ENSURE_SUCCESS(rv, rv);
611 
612  /* Get the pref service. */
613  mpPrefBranch = do_GetService(NS_PREFSERVICE_CONTRACTID, &rv);
614  NS_ENSURE_SUCCESS(rv, rv);
615 
616  /* Get the library manager. */
617  pLibraryManager =
618  do_GetService("@songbirdnest.com/Songbird/library/Manager;1", &rv);
619  NS_ENSURE_SUCCESS(rv, rv);
620 
621  /* Get the string bundle. */
622  {
623  nsCOMPtr<nsIStringBundleService>
624  pStringBundleService;
625 
626  /* Get the download device string bundle. */
627  pStringBundleService = do_GetService(NS_STRINGBUNDLE_CONTRACTID, &rv);
628  NS_ENSURE_SUCCESS(rv, rv);
629  rv = pStringBundleService->CreateBundle(SB_STRING_BUNDLE_CHROME_URL,
630  getter_AddRefs(mpStringBundle));
631  NS_ENSURE_SUCCESS(rv, rv);
632  }
633 
634  /* Get some strings. */
635  rv = mpStringBundle->GetStringFromName
636  (NS_LITERAL_STRING("device.download.queued").get(),
637  getter_Copies(mQueuedStr));
638  NS_ENSURE_SUCCESS(rv, rv);
639 
640  /* Get the main library. */
641  rv = pLibraryManager->GetMainLibrary(getter_AddRefs(mpMainLibrary));
642  NS_ENSURE_SUCCESS(rv, rv);
643 
644  /* Get the web library. */
645  {
646  nsCOMPtr<nsISupportsString> pSupportsString;
647  nsAutoString webLibraryGUID;
648 
649  /* Read the web library GUID from the preferences. */
650  rv = mpPrefBranch->GetComplexValue(SB_PREF_WEB_LIBRARY,
651  NS_GET_IID(nsISupportsString),
652  getter_AddRefs(pSupportsString));
653  NS_ENSURE_SUCCESS(rv, rv);
654  rv = pSupportsString->GetData(webLibraryGUID);
655  NS_ENSURE_SUCCESS(rv, rv);
656 
657  /* Get the web library. */
658  rv = pLibraryManager->GetLibrary(webLibraryGUID,
659  getter_AddRefs(mpWebLibrary));
660  NS_ENSURE_SUCCESS(rv, rv);
661  }
662 
663  /* Initialize the download media list. */
664  rv = InitializeDownloadMediaList();
665  NS_ENSURE_SUCCESS(rv, rv);
666 
667  /* Listen to the main library to detect */
668  /* when the download media list is deleted. */
669  rv = mpMainLibrary->AddListener
670  (this,
671  PR_FALSE,
674  nsnull);
675  NS_ENSURE_SUCCESS(rv, rv);
676 
677  /* Create the device transfer queue. */
678  rv = CreateTransferQueue(mDeviceIdentifier);
679  NS_ENSURE_SUCCESS(rv, rv);
680 
681  /* Create temporary download directory. */
682  {
683  nsCOMPtr<nsIProperties> pProperties;
684  PRUint32 permissions;
685  PRBool exists;
686 
687  /* Get the system temporary directory. */
688  pProperties = do_GetService("@mozilla.org/file/directory_service;1",
689  &rv);
690  NS_ENSURE_SUCCESS(rv, rv);
691  rv = pProperties->Get("TmpD",
692  NS_GET_IID(nsIFile),
693  getter_AddRefs(mpTmpDownloadDir));
694  NS_ENSURE_SUCCESS(rv, rv);
695  rv = mpTmpDownloadDir->GetPermissions(&permissions);
696  NS_ENSURE_SUCCESS(rv, rv);
697 
698  /* Get the Songbird temporary directory and make sure it exists. */
699  rv = mpTmpDownloadDir->Append(NS_LITERAL_STRING(SB_TMP_DIR));
700  NS_ENSURE_SUCCESS(rv, rv);
701  rv = mpTmpDownloadDir->Exists(&exists);
702  NS_ENSURE_SUCCESS(rv, rv);
703  if (!exists)
704  {
705  rv = mpTmpDownloadDir->Create(nsIFile::DIRECTORY_TYPE, permissions);
706  NS_ENSURE_SUCCESS(rv, rv);
707  }
708 
709  /* Get the download temporary directory and make sure it's empty. If */
710  /* directory cannot be deleted, don't propagate error (bug #6453). */
711  rv = mpTmpDownloadDir->Append(NS_LITERAL_STRING(SB_DOWNLOAD_TMP_DIR));
712  NS_ENSURE_SUCCESS(rv, rv);
713  rv = mpTmpDownloadDir->Exists(&exists);
714  NS_ENSURE_SUCCESS(rv, rv);
715  if (exists)
716  {
717  rv = mpTmpDownloadDir->Remove(PR_TRUE);
718  if (NS_SUCCEEDED(rv))
719  exists = PR_FALSE;
720  }
721  if (!exists)
722  {
723  rv = mpTmpDownloadDir->Create(nsIFile::DIRECTORY_TYPE, permissions);
724  NS_ENSURE_SUCCESS(rv, rv);
725  }
726  }
727 
728  /* Watch for the app quitting so we can gracefully abort. */
729  {
730  nsCOMPtr<nsIObserverService> obsSvc =
731  do_GetService("@mozilla.org/observer-service;1", &rv);
732  NS_ENSURE_SUCCESS(rv, rv);
733  rv = obsSvc->AddObserver(this, "quit-application-granted", PR_FALSE);
734  NS_ENSURE_SUCCESS(rv, rv);
735  }
736 
737  // Need thread pool to process file moves off the main thread.
738  nsCOMPtr<nsIThreadPool> threadPool =
739  do_CreateInstance("@mozilla.org/thread-pool;1", &rv);
740  NS_ENSURE_SUCCESS(rv, rv);
741 
742  rv = threadPool->SetIdleThreadLimit(0);
743  NS_ENSURE_SUCCESS(rv, rv);
744 
745  rv = threadPool->SetIdleThreadTimeout(60000);
746  NS_ENSURE_SUCCESS(rv, rv);
747 
748  rv = threadPool->SetThreadLimit(1);
749  NS_ENSURE_SUCCESS(rv, rv);
750 
751  // Thread pool is setup.
752  threadPool.forget(getter_AddRefs(mFileMoveThreadPool));
753 
754  /* Resume incomplete transfers. */
755  ResumeTransfers();
756 
757  return NS_OK;
758 }
759 
760 
768 NS_IMETHODIMP sbDownloadDevice::Finalize()
769 {
770  /* Lock the download device for finalization. */
771  if (mpDeviceMonitor)
772  {
773  /* Lock the download device. */
774  nsAutoMonitor mon(mpDeviceMonitor);
775 
776  /* Dispose of any outstanding download sessions. */
777  if (mpDownloadSession)
778  {
779  mpDownloadSession->Shutdown();
780  mpDownloadSession = nsnull;
781  }
782 
783  /* Remove the device transfer queue. */
784  RemoveTransferQueue(mDeviceIdentifier);
785 
786  /* Remove main library listener. */
787  if (mpMainLibrary)
788  mpMainLibrary->RemoveListener(this);
789 
790  /* Finalize the download media list. */
791  FinalizeDownloadMediaList();
792  }
793 
794  /* Dispose of the device lock. */
795  if (mpDeviceMonitor) {
796  nsAutoMonitor::DestroyMonitor(mpDeviceMonitor);
797  mpDeviceMonitor = nsnull;
798  }
799 
800  mpMainLibrary = nsnull;
801  mpWebLibrary = nsnull;
802 
803  return (NS_OK);
804 }
805 
806 
816 NS_IMETHODIMP sbDownloadDevice::AddCallback(
817  sbIDeviceBaseCallback *aCallback)
818 {
819  return (sbDeviceBase::AddCallback(aCallback));
820 }
821 
822 
830  sbIDeviceBaseCallback *aCallback)
831 {
832  return (sbDeviceBase::RemoveCallback(aCallback));
833 }
834 
835 
844 NS_IMETHODIMP sbDownloadDevice::GetLibrary(
845  const nsAString &aDeviceIdentifier,
846  sbILibrary **aLibrary)
847 {
848  return (GetLibraryForDevice(mDeviceIdentifier, aLibrary));
849 }
850 
851 
861  const nsAString &aDeviceIdentifier,
862  PRUint32 *aState)
863 {
864  /* Lock the device. */
865  nsAutoMonitor mon(mpDeviceMonitor);
866 
867  /* Return results. */
868  return (sbDeviceBase::GetDeviceState(mDeviceIdentifier, aState));
869 }
870 
871 
884 NS_IMETHODIMP sbDownloadDevice::GetTransferLocation(
885  const nsAString &aDeviceIdentifier,
886  sbIMediaItem *aMediaItem,
887  nsIURI **aTransferLocationURI)
888 {
889  LOG(("1: GetTransferLocation\n"));
890 
891  return (NS_ERROR_NOT_IMPLEMENTED);
892 }
893 
894 
918 NS_IMETHODIMP sbDownloadDevice::TransferItems(
919  const nsAString &aDeviceIdentifier,
920  nsIArray *aMediaItems,
921  nsIURI *aDestinationPath,
922  PRUint32 aDeviceOperation,
923  PRBool aBeginTransferNow,
924  sbILibrary *aDestinationLibrary,
925  PRUint32 *aItemCount)
926 {
927  nsCOMPtr<sbIMediaItem> pMediaItem;
928  PRUint32 itemCount;
929  PRUint32 i;
930  nsresult rv;
931 
932  /* Validate parameters. */
933  NS_ENSURE_ARG_POINTER(aMediaItems);
934 
935  /* Do nothing unless operation is upload. Uploading an item to the */
936  /* download device will download the item to the destination library. */
937  if (aDeviceOperation != OP_UPLOAD)
938  return (NS_ERROR_NOT_IMPLEMENTED);
939 
940  /* Clear completed items. */
941  ClearCompletedItems();
942 
943  /* Add all the media items to the transfer queue. If any error */
944  /* occurs on an item, remove it and continue with remaining items. */
945  rv = aMediaItems->GetLength(&itemCount);
946  NS_ENSURE_SUCCESS(rv, rv);
947  for (i = 0; i < itemCount; i++)
948  {
949  /* Get the next media item. Skip to next item on error. */
950  pMediaItem = do_QueryElementAt(aMediaItems, i, &rv);
951  if (NS_FAILED(rv))
952  continue;
953 
954  /* Enqueue the media item for download. */
955  rv = EnqueueItem(pMediaItem);
956 
957  /* Remove item on error. */
958  if (NS_FAILED(rv))
959  {
960  mpDeviceLibraryListener->SetIgnoreListener(PR_TRUE);
961  mpDownloadMediaList->Remove(pMediaItem);
962  mpDeviceLibraryListener->SetIgnoreListener(PR_FALSE);
963  }
964  }
965 
966  /* Run the transfer queue. */
967  rv = RunTransferQueue();
968  NS_ENSURE_SUCCESS(rv, rv);
969 
970  return (NS_OK);
971 }
972 
973 
978 NS_IMETHODIMP sbDownloadDevice::UpdateItems(
979  const nsAString &aDeviceIdentifier,
980  nsIArray *aMediaItems,
981  PRUint32 *aItemCount)
982 {
983  //NS_WARNING("UpdateItems not implemented");
984  return NS_OK;
985 }
986 
987 
997 NS_IMETHODIMP sbDownloadDevice::DeleteItems(
998  const nsAString &aDeviceIdentifier,
999  nsIArray *aMediaItems,
1000  PRUint32 *aItemCount)
1001 {
1002  nsCOMPtr<sbIMediaItem> pMediaItem;
1003  PRUint32 arrayLength;
1004  PRUint32 i;
1005  PRBool equals;
1006  PRBool cancelSession = PR_FALSE;
1007  PRUint32 itemCount = 0;
1008  nsresult result1;
1009  nsresult result = NS_OK;
1010 
1011  /* Validate arguments. */
1012  NS_ENSURE_ARG_POINTER(aMediaItems);
1013  NS_ENSURE_ARG_POINTER(aItemCount);
1014 
1015  /* Lock the device. */
1016  nsAutoMonitor mon(mpDeviceMonitor);
1017 
1018  /* Remove the items. */
1019  result = aMediaItems->GetLength(&arrayLength);
1020  for (i = 0; (NS_SUCCEEDED(result) && (i < arrayLength)); i++)
1021  {
1022  /* Get the media item to remove. */
1023  pMediaItem = do_QueryElementAt(aMediaItems, i, &result);
1024 
1025  /* Remove the item from the transfer queue. */
1026  if (NS_SUCCEEDED(result))
1027  {
1028  result1 = RemoveItemFromTransferQueue(mDeviceIdentifier,
1029  pMediaItem);
1030  if (NS_SUCCEEDED(result1))
1031  itemCount++;
1032  }
1033 
1034  /* Reset the status target to new */
1035  if (NS_SUCCEEDED(result))
1036  {
1037  nsCOMPtr<sbIMediaItem> statusTarget;
1038  nsresult rv = GetStatusTarget(pMediaItem,
1039  getter_AddRefs(statusTarget));
1040  NS_ENSURE_SUCCESS(rv, rv);
1041  sbAutoDownloadButtonPropertyValue property(pMediaItem, statusTarget);
1042  if (property.value->GetMode() !=
1044  property.value->SetMode(sbDownloadButtonPropertyValue::eNew);
1045  }
1046  }
1047 
1048  /* Check if current session should be deleted. */
1049  if (NS_SUCCEEDED(result) && mpDownloadSession && !cancelSession)
1050  {
1051  result1 = pMediaItem->Equals(mpDownloadSession->mpMediaItem,
1052  &equals);
1053  if (NS_SUCCEEDED(result1) && equals)
1054  cancelSession = PR_TRUE;
1055  }
1056  }
1057 
1058  /* Cancel the session if needed. This counts as another deleted */
1059  /* item since the item would not be in the transfer queue. */
1060  if (cancelSession)
1061  {
1062  result1 = CancelSession();
1063  if (NS_SUCCEEDED(result1))
1064  itemCount++;
1065  }
1066 
1067  /* Return results. */
1068  *aItemCount = itemCount;
1069 
1070  return (result);
1071 }
1072 
1073 
1082 NS_IMETHODIMP sbDownloadDevice::DeleteAllItems(
1083  const nsAString &aDeviceIdentifier,
1084  PRUint32 *aItemCount)
1085 {
1086  nsCOMPtr<sbIMediaItem> pMediaItem;
1087  PRUint32 itemCount = 0;
1088  nsresult result1;
1089  nsresult result = NS_OK;
1090 
1091  /* Validate arguments. */
1092  NS_ENSURE_ARG_POINTER(aItemCount);
1093 
1094  /* Lock the device. */
1095  nsAutoMonitor mon(mpDeviceMonitor);
1096 
1097  /* Remove all the items in the queue. */
1098  while (GetNextTransferItem(getter_AddRefs(pMediaItem)))
1099  itemCount++;
1100 
1101  /* Cancel active download session. */
1102  if (mpDownloadSession)
1103  {
1104  result1 = CancelSession();
1105  if (NS_SUCCEEDED(result1))
1106  itemCount++;
1107  }
1108 
1109  /* Return results. */
1110  *aItemCount = itemCount;
1111 
1112  return (result);
1113 }
1114 
1115 
1121 NS_IMETHODIMP sbDownloadDevice::BeginTransfer(
1122  const nsAString &aDeviceIdentifier,
1123  sbIMediaItem **aMediaItem)
1124 {
1125  LOG(("1: BeginTransfer\n"));
1126 
1127  return (NS_ERROR_NOT_IMPLEMENTED);
1128 }
1129 
1130 
1142 NS_IMETHODIMP sbDownloadDevice::CancelTransfer(
1143  const nsAString &aDeviceIdentifier,
1144  nsIArray *aMediaItems,
1145  PRUint32 *aNumItems)
1146 {
1147  LOG(("1: CancelTransfer\n"));
1148 
1149  return (NS_ERROR_NOT_IMPLEMENTED);
1150 }
1151 
1152 
1163 NS_IMETHODIMP sbDownloadDevice::SuspendTransfer(
1164  const nsAString &aDeviceIdentifier,
1165  PRUint32 *aNumItems)
1166 {
1167  PRUint32 numItems = 0;
1168  nsresult rv;
1169 
1170  /* Validate arguments. */
1171  NS_ENSURE_ARG_POINTER(aNumItems);
1172 
1173  /* Lock the device. */
1174  nsAutoMonitor mon(mpDeviceMonitor);
1175 
1176  /* If there's a download session, suspend it. */
1177  if (mpDownloadSession)
1178  {
1179  rv = mpDownloadSession->Suspend();
1180  NS_ENSURE_SUCCESS(rv, rv);
1181  rv = SetDeviceState(mDeviceIdentifier, STATE_DOWNLOAD_PAUSED);
1182  NS_ENSURE_SUCCESS(rv, rv);
1183  numItems = 1;
1184  }
1185 
1186  /* Return results. */
1187  *aNumItems = numItems;
1188 
1189  return (NS_OK);
1190 }
1191 
1192 
1203 NS_IMETHODIMP sbDownloadDevice::ResumeTransfer(
1204  const nsAString &aDeviceIdentifier,
1205  PRUint32 *aNumItems)
1206 {
1207  PRUint32 numItems = 0;
1208  nsresult rv;
1209 
1210  /* Validate arguments. */
1211  NS_ENSURE_ARG_POINTER(aNumItems);
1212 
1213  /* Lock the device. */
1214  nsAutoMonitor mon(mpDeviceMonitor);
1215 
1216  /* If there's a download session, resume it. */
1217  if (mpDownloadSession)
1218  {
1219  rv = mpDownloadSession->Resume();
1220  NS_ENSURE_SUCCESS(rv, rv);
1221  rv = SetDeviceState(mDeviceIdentifier, STATE_DOWNLOADING);
1222  NS_ENSURE_SUCCESS(rv, rv);
1223  numItems = 1;
1224  }
1225 
1226  /* Return results. */
1227  *aNumItems = numItems;
1228 
1229  return (NS_OK);
1230 }
1231 
1232 
1241 NS_IMETHODIMP sbDownloadDevice::GetUsedSpace(
1242  const nsAString &aDeviceIdentifier,
1243  PRInt64 *aUsedSpace)
1244 {
1245  LOG(("1: GetUsedSpace\n"));
1246 
1247  return (NS_ERROR_NOT_IMPLEMENTED);
1248 }
1249 
1250 
1259 NS_IMETHODIMP sbDownloadDevice::GetAvailableSpace(
1260  const nsAString &aDeviceIdentifier,
1261  PRInt64 *aAvailableSpace)
1262 {
1263  LOG(("1: GetAvailableSpace\n"));
1264 
1265  return (NS_ERROR_NOT_IMPLEMENTED);
1266 }
1267 
1268 
1279 NS_IMETHODIMP sbDownloadDevice::GetSupportedFormats(
1280  const nsAString &aDeviceIdentifier,
1281  nsIArray **aSupportedFormats)
1282 {
1283  LOG(("1: GetSupportedFormats\n"));
1284 
1285  return (NS_ERROR_NOT_IMPLEMENTED);
1286 }
1287 
1288 
1297 NS_IMETHODIMP sbDownloadDevice::IsDownloadSupported(
1298  const nsAString &aDeviceIdentifier,
1299  PRBool *aDownloadSupported)
1300 {
1301  LOG(("1: IsDownloadSupported\n"));
1302 
1303  return (NS_ERROR_NOT_IMPLEMENTED);
1304 }
1305 
1306 
1317 NS_IMETHODIMP sbDownloadDevice::IsUploadSupported(
1318  const nsAString &aDeviceIdentifier,
1319  PRBool *aUploadSupported)
1320 {
1321  LOG(("1: IsUploadSupported\n"));
1322 
1323  return (NS_ERROR_NOT_IMPLEMENTED);
1324 }
1325 
1326 
1335 NS_IMETHODIMP sbDownloadDevice::IsDeleteSupported(
1336  const nsAString &aDeviceIdentifier,
1337  PRBool *aDeleteSupported)
1338 {
1339  LOG(("1: IsDeleteSupported\n"));
1340 
1341  return (NS_ERROR_NOT_IMPLEMENTED);
1342 }
1343 
1344 
1356 NS_IMETHODIMP sbDownloadDevice::IsUpdateSupported(
1357  const nsAString &aDeviceIdentifier,
1358  PRBool *aUpdateSupported)
1359 {
1360  LOG(("1: IsUpdateSupported\n"));
1361 
1362  return (NS_ERROR_NOT_IMPLEMENTED);
1363 }
1364 
1365 
1374 NS_IMETHODIMP sbDownloadDevice::IsEjectSupported(
1375  const nsAString &aDeviceIdentifier,
1376  PRBool *aEjectSupported)
1377 {
1378  LOG(("1: IsEjectSupported\n"));
1379 
1380  return (NS_ERROR_NOT_IMPLEMENTED);
1381 }
1382 
1383 
1392 NS_IMETHODIMP sbDownloadDevice::EjectDevice(
1393  const nsAString &aDeviceIdentifier,
1394  PRBool *aEjected)
1395 {
1396  LOG(("1: EjectDevice\n"));
1397 
1398  return (NS_ERROR_NOT_IMPLEMENTED);
1399 }
1400 
1401 
1402 /*
1403  * sbIDeviceBase attribute getters/setters.
1404  */
1405 
1406 NS_IMETHODIMP sbDownloadDevice::GetName(
1407  nsAString &aName)
1408 {
1409  LOG(("1: GetName\n"));
1410 
1411  return (NS_ERROR_NOT_IMPLEMENTED);
1412 }
1413 
1414 NS_IMETHODIMP sbDownloadDevice::SetName(
1415  const nsAString &aName)
1416 {
1417  LOG(("1: SetName\n"));
1418 
1419  return (NS_ERROR_NOT_IMPLEMENTED);
1420 }
1421 
1422 NS_IMETHODIMP sbDownloadDevice::GetDeviceCategory(
1423  nsAString &aDeviceCategory)
1424 {
1425  aDeviceCategory.Assign(SB_DOWNLOAD_DEVICE_CATEGORY);
1426  return (NS_OK);
1427 }
1428 
1429 NS_IMETHODIMP sbDownloadDevice::GetDeviceIdentifiers(
1430  nsIStringEnumerator **aDeviceIdentifiers)
1431 {
1432  LOG(("1: GetDeviceIdentifiers\n"));
1433 
1434  return (NS_ERROR_NOT_IMPLEMENTED);
1435 }
1436 
1437 NS_IMETHODIMP sbDownloadDevice::GetDeviceCount(
1438  PRUint32 *aDeviceCount)
1439 {
1440  LOG(("1: GetDeviceCount\n"));
1441 
1442  return (NS_ERROR_NOT_IMPLEMENTED);
1443 }
1444 
1445 
1446 /* *****************************************************************************
1447  *
1448  * Download device sbIDownloadDevice implementation.
1449  *
1450  ******************************************************************************/
1451 
1456 NS_IMETHODIMP sbDownloadDevice::ClearCompletedItems()
1457 {
1458  nsCOMPtr<sbIMediaItem> pMediaItem;
1459  PRUint32 itemCount;
1460  PRInt32 i;
1461  nsresult rv;
1462 
1463  /* Get the number of download items. */
1464  rv = mpDownloadMediaList->GetLength(&itemCount);
1465  NS_ENSURE_SUCCESS(rv, rv);
1466 
1467  /* Add all the media items to the transfer queue. If any */
1468  /* error occurs on an item, continue with remaining items. */
1469  for (i = itemCount - 1; i >= 0; i--)
1470  {
1471  /* Get the next item. Skip to next item on error. */
1472  rv= mpDownloadMediaList->GetItemByIndex(i, getter_AddRefs(pMediaItem));
1473  if (NS_FAILED(rv))
1474  continue;
1475 
1476  /* Remove item from download device library if complete. */
1477  sbAutoDownloadButtonPropertyValue property(pMediaItem, nsnull, PR_TRUE);
1478  if ( property.value->GetMode()
1480  {
1481  mpDeviceLibraryListener->SetIgnoreListener(PR_TRUE);
1482  rv = mpDownloadMediaList->Remove(pMediaItem);
1483  mpDeviceLibraryListener->SetIgnoreListener(PR_FALSE);
1484 
1485  /* Warn if item could not be removed. */
1486  if (NS_FAILED(rv))
1487  NS_WARNING("Failed to remove completed download item.\n");
1488  }
1489  }
1490 
1491  return (NS_OK);
1492 }
1493 
1494 
1495 /*
1496  * sbIDownloadDevice attribute getters/setters.
1497  */
1498 
1499 NS_IMETHODIMP sbDownloadDevice::GetDownloadMediaList(
1500  sbIMediaList **aDownloadMediaList)
1501 {
1502  nsresult rv;
1503 
1504  /* Validate arguments. */
1505  NS_ENSURE_ARG_POINTER(aDownloadMediaList);
1506 
1507  /* Ensure the download media list is initialized. */
1508  rv = InitializeDownloadMediaList();
1509  NS_ENSURE_SUCCESS(rv, rv);
1510 
1511  /* Return results. */
1512  NS_ADDREF(*aDownloadMediaList = mpDownloadMediaList);
1513 
1514  return (NS_OK);
1515 }
1516 
1517 NS_IMETHODIMP sbDownloadDevice::GetCompletedItemCount(
1518  PRUint32 *aCompletedItemCount)
1519 {
1520  nsCOMPtr<sbIMediaItem> pMediaItem;
1521  PRUint32 itemCount;
1522  PRUint32 completedItemCount = 0;
1523  PRUint32 i;
1524  nsresult rv;
1525 
1526  /* Validate arguments. */
1527  NS_ENSURE_ARG_POINTER(aCompletedItemCount);
1528 
1529  /* Get the number of download items. */
1530  rv = mpDownloadMediaList->GetLength(&itemCount);
1531  NS_ENSURE_SUCCESS(rv, rv);
1532 
1533  /* Count the number of completed items. */
1534  /*XXXeps there must be a quicker way to do this. */
1535  for (i = 0; i < itemCount; i++)
1536  {
1537  /* Get the next item. */
1538  rv= mpDownloadMediaList->GetItemByIndex(i, getter_AddRefs(pMediaItem));
1539  NS_ENSURE_SUCCESS(rv, rv);
1540 
1541  /* Increment item completed count if item has completed. */
1542  sbAutoDownloadButtonPropertyValue property(pMediaItem, nsnull, PR_TRUE);
1543  if ( property.value->GetMode()
1545  {
1546  completedItemCount++;
1547  }
1548  }
1549 
1550  /* Return results. */
1551  *aCompletedItemCount = completedItemCount;
1552 
1553  return (NS_OK);
1554 }
1555 
1556 
1557 /* *****************************************************************************
1558  *
1559  * Download device sbIMediaListListener implementation.
1560  *
1561  ******************************************************************************/
1562 
1572 NS_IMETHODIMP sbDownloadDevice::OnItemAdded(
1573  sbIMediaList *aMediaList,
1574  sbIMediaItem *aMediaItem,
1575  PRUint32 aIndex,
1576  PRBool *_retval)
1577 {
1578  /* Validate parameters. */
1579  NS_ENSURE_ARG_POINTER(_retval);
1580 
1581  *_retval = PR_TRUE;
1582  return (NS_OK);
1583 }
1584 
1585 
1595 NS_IMETHODIMP sbDownloadDevice::OnBeforeItemRemoved(
1596  sbIMediaList *aMediaList,
1597  sbIMediaItem *aMediaItem,
1598  PRUint32 aIndex,
1599  PRBool *_retval)
1600 {
1601  /* Validate parameters. */
1602  NS_ENSURE_ARG_POINTER(_retval);
1603 
1604  *_retval = PR_TRUE;
1605  return (NS_OK);
1606 }
1607 
1608 
1618 NS_IMETHODIMP sbDownloadDevice::OnAfterItemRemoved(
1619  sbIMediaList *aMediaList,
1620  sbIMediaItem *aMediaItem,
1621  PRUint32 aIndex,
1622  PRBool *_retval)
1623 {
1624  PRBool isEqual;
1625  nsresult rv;
1626 
1627  /* Validate parameters. */
1628  NS_ENSURE_ARG_POINTER(aMediaItem);
1629  NS_ENSURE_ARG_POINTER(_retval);
1630 
1631  /* If the download device media list was removed, re-initialize it. */
1632  rv = mpDownloadMediaList->Equals(aMediaItem, &isEqual);
1633  if (NS_SUCCEEDED(rv) && isEqual)
1634  InitializeDownloadMediaList();
1635 
1636  *_retval = PR_FALSE;
1637  return (NS_OK);
1638 }
1639 
1640 
1652 NS_IMETHODIMP sbDownloadDevice::OnItemUpdated(
1653  sbIMediaList *aMediaList,
1654  sbIMediaItem *aMediaItem,
1655  sbIPropertyArray *aProperties,
1656  PRBool *_retval)
1657 {
1658  /* Validate parameters. */
1659  NS_ENSURE_ARG_POINTER(_retval);
1660 
1661  *_retval = PR_TRUE;
1662  return (NS_OK);
1663 }
1664 
1665 NS_IMETHODIMP sbDownloadDevice::OnItemMoved(
1666  sbIMediaList *aMediaList,
1667  PRUint32 aFromIndex,
1668  PRUint32 aToIndex,
1669  PRBool *_retval)
1670 {
1671  /* Validate parameters. */
1672  NS_ENSURE_ARG_POINTER(_retval);
1673 
1674  *_retval = PR_TRUE;
1675  return (NS_OK);
1676 }
1677 
1688 NS_IMETHODIMP sbDownloadDevice::OnListCleared(
1689  sbIMediaList *aMediaList,
1690  PRBool aExcludeLists,
1691  PRBool *_retval)
1692 {
1693  /* Validate parameters. */
1694  NS_ENSURE_ARG_POINTER(_retval);
1695 
1696  /* Re-initialize the download device media list. */
1697  InitializeDownloadMediaList();
1698 
1699  *_retval = PR_FALSE;
1700  return (NS_OK);
1701 }
1702 
1713 NS_IMETHODIMP sbDownloadDevice::OnBeforeListCleared(
1714  sbIMediaList *aMediaList,
1715  PRBool aExcludeLists,
1716  PRBool *_retval)
1717 {
1718  /* Validate parameters. */
1719  NS_ENSURE_ARG_POINTER(_retval);
1720 
1721  *_retval = PR_FALSE;
1722  return (NS_OK);
1723 }
1724 
1725 NS_IMETHODIMP
1726 sbDownloadDevice::CreatePlaylists(const nsAString &aDeviceIdentifier,
1727  nsIArray *aMediaLists,
1728  PRUint32 *aItemCount)
1729 {
1730  return NS_OK;
1731 }
1732 
1733 NS_IMETHODIMP
1734 sbDownloadDevice::DeletePlaylists(const nsAString &aDeviceIdentifier,
1735  nsIArray *aMediaLists,
1736  PRUint32 *aItemCount)
1737 {
1738  return NS_OK;
1739 }
1740 
1741 NS_IMETHODIMP
1742 sbDownloadDevice::AddToPlaylist(const nsAString &aDeviceIdentifier,
1743  sbIMediaList *aMediaList,
1744  nsIArray *aMediaLists,
1745  PRUint32 aBeforeIndex,
1746  PRUint32 *aItemCount)
1747 {
1748  return NS_OK;
1749 }
1750 
1751 NS_IMETHODIMP
1752 sbDownloadDevice::RemoveFromPlaylist(const nsAString &aDeviceIdentifier,
1753  sbIMediaList *aMediaList,
1754  sbIMediaItem *aMediaItem,
1755  PRUint32 aIndex,
1756  PRUint32 *aItemCount)
1757 {
1758  return NS_OK;
1759 }
1760 
1761 NS_IMETHODIMP
1762 sbDownloadDevice::ClearPlaylist(const nsAString &aDeviceIdentifier,
1763  sbIMediaList *aMediaList,
1764  PRUint32 *aItemCount)
1765 {
1766  return NS_OK;
1767 }
1768 
1769 NS_IMETHODIMP
1770 sbDownloadDevice::MovePlaylistItem(const nsAString &aDeviceIdentifier,
1771  sbIMediaList *aMediaList,
1772  PRUint32 aFromIndex,
1773  PRUint32 aToIndex,
1774  PRUint32 *aItemCount)
1775 {
1776  return NS_OK;
1777 }
1778 
1790 NS_IMETHODIMP sbDownloadDevice::OnBatchBegin(
1791  sbIMediaList *aMediaList)
1792 {
1793  return (NS_OK);
1794 }
1795 
1796 
1808 NS_IMETHODIMP sbDownloadDevice::OnBatchEnd(
1809  sbIMediaList *aMediaList)
1810 {
1811  return (NS_OK);
1812 }
1813 
1814 
1815 /* *****************************************************************************
1816  *
1817  * Download device media list services.
1818  *
1819  ******************************************************************************/
1820 
1821 /*
1822  * InitializeDownloadMediaList
1823  *
1824  * This function initializes the download device media list. This function is
1825  * designed to be called multiple times to ensure that a valid download media
1826  * list is present and initialized.
1827  * If the media list has already been initialized and is still valid, this
1828  * function does nothing. If the media list already exists, this function
1829  * installs listeners and performs other initialization. If the media list does
1830  * not exist, this function will create it and ensure that no items are being
1831  * downloaded.
1832  */
1833 
1834 nsresult sbDownloadDevice::InitializeDownloadMediaList()
1835 {
1836  nsAutoString downloadMediaListGUID;
1837  nsCOMPtr<sbIMediaItem> pMediaItem;
1838  nsresult rv;
1839 
1840  /* Lock the download device. */
1841  NS_ENSURE_STATE(mpDeviceMonitor);
1842  nsAutoMonitor mon(mpDeviceMonitor);
1843 
1844  /* Handle case where download media list has already been initialized. */
1845  if (mpDownloadMediaList)
1846  {
1847  /* Do nothing if a valid download media list is already initialized. */
1848  rv = mpDownloadMediaList->GetGuid(downloadMediaListGUID);
1849  NS_ENSURE_SUCCESS(rv, rv);
1850  rv = mpMainLibrary->GetMediaItem(downloadMediaListGUID,
1851  getter_AddRefs(pMediaItem));
1852  if (NS_SUCCEEDED(rv))
1853  return (NS_OK);
1854 
1855  /* Finalize download media list. */
1856  FinalizeDownloadMediaList();
1857  }
1858 
1859  /* Get any existing download media list. */
1860  GetDownloadMediaList();
1861 
1862  /* Create the download media list if one does not exist. */
1863  if (!mpDownloadMediaList)
1864  {
1865  PRUint32 itemCount;
1866 
1867  /* Ensure all items are deleted from device. */
1868  rv = DeleteAllItems(mDeviceIdentifier, &itemCount);
1869  NS_ENSURE_SUCCESS(rv, rv);
1870 
1871  /* Create the download media list. */
1872  rv = CreateDownloadMediaList();
1873  NS_ENSURE_SUCCESS(rv, rv);
1874  }
1875 
1876  /* Update the download media list. */
1877  rv = UpdateDownloadMediaList();
1878  NS_ENSURE_SUCCESS(rv, rv);
1879 
1880  /* Create a download media list listener. */
1881  NS_NEWXPCOM(mpDeviceLibraryListener, sbDeviceBaseLibraryListener);
1882  NS_ENSURE_TRUE(mpDeviceLibraryListener, NS_ERROR_OUT_OF_MEMORY);
1883  rv = mpDeviceLibraryListener->Init(mDeviceIdentifier, this);
1884  NS_ENSURE_SUCCESS(rv, rv);
1885 
1886  /* Add the download device media list listener.*/
1887  rv = mpDownloadMediaList->AddListener
1888  (mpDeviceLibraryListener,
1889  PR_FALSE,
1893  nsnull);
1894  NS_ENSURE_SUCCESS(rv, rv);
1895  rv = SetListenerForDeviceLibrary(mDeviceIdentifier,
1896  mpDeviceLibraryListener);
1897  NS_ENSURE_SUCCESS(rv, rv);
1898 
1899  return (NS_OK);
1900 }
1901 
1902 
1903 /*
1904  * FinalizeDownloadMediaList
1905  *
1906  * This function finalizes the download media list. It removes listeners and
1907  * undoes any other initialization. It does not delete the download media list.
1908  */
1909 
1910 void sbDownloadDevice::FinalizeDownloadMediaList()
1911 {
1912  /* Remove download media list listener. */
1913  if (mpDownloadMediaList && mpDeviceLibraryListener)
1914  mpDownloadMediaList->RemoveListener(mpDeviceLibraryListener);
1915  mpDownloadMediaList = nsnull;
1916  mpDeviceLibraryListener = nsnull;
1917 }
1918 
1919 
1920 /*
1921  * CreateDownloadMediaList
1922  *
1923  * This function creates the download device media list.
1924  */
1925 
1926 nsresult sbDownloadDevice::CreateDownloadMediaList()
1927 {
1928  nsAutoString downloadMediaListGUID;
1929  nsresult rv;
1930 
1931  /* Create the download media list. */
1932  rv = mpMainLibrary->CreateMediaList(NS_LITERAL_STRING("simple"),
1933  nsnull,
1934  getter_AddRefs(mpDownloadMediaList));
1935  NS_ENSURE_SUCCESS(rv, rv);
1936 
1937  /* Save the media list GUID in the main */
1938  /* library properties so others can find it. */
1939  rv = mpDownloadMediaList->GetGuid(downloadMediaListGUID);
1940  NS_ENSURE_SUCCESS(rv, rv);
1941  rv = mpMainLibrary->SetProperty
1942  (NS_LITERAL_STRING(SB_PROPERTY_DOWNLOAD_MEDIALIST_GUID),
1943  downloadMediaListGUID);
1944  NS_ENSURE_SUCCESS(rv, rv);
1945 
1946  return (NS_OK);
1947 }
1948 
1949 
1950 /*
1951  * GetDownloadMediaList
1952  *
1953  * This function gets the download device media list.
1954  */
1955 
1956 void sbDownloadDevice::GetDownloadMediaList()
1957 {
1958  nsCOMPtr<nsISupportsString> pSupportsString;
1959  nsCOMPtr<sbIMediaItem> pMediaItem;
1960  nsAutoString downloadMediaListGUID;
1961  nsresult rv;
1962 
1963  /* Read the download media list GUID. */
1964  rv = mpMainLibrary->GetProperty
1965  (NS_LITERAL_STRING(SB_PROPERTY_DOWNLOAD_MEDIALIST_GUID),
1966  downloadMediaListGUID);
1967  if (NS_FAILED(rv) || downloadMediaListGUID.IsEmpty())
1968  {
1969  /* For backward compatibility, read the download */
1970  /* media list GUID from the preferences. */
1971  rv = mpPrefBranch->GetComplexValue(SB_PREF_DOWNLOAD_MEDIALIST,
1972  NS_GET_IID(nsISupportsString),
1973  getter_AddRefs(pSupportsString));
1974  if (NS_FAILED(rv)) return;
1975  rv = pSupportsString->GetData(downloadMediaListGUID);
1976  if (NS_FAILED(rv)) return;
1977 
1978  /* Set the download media list GUID main library property. */
1979  mpMainLibrary->SetProperty
1980  (NS_LITERAL_STRING(SB_PROPERTY_DOWNLOAD_MEDIALIST_GUID),
1981  downloadMediaListGUID);
1982  }
1983 
1984  /* Get the download media list from the main library. */
1985  rv = mpMainLibrary->GetMediaItem(downloadMediaListGUID,
1986  getter_AddRefs(pMediaItem));
1987  if (NS_FAILED(rv)) return;
1988  mpDownloadMediaList = do_QueryInterface(pMediaItem, &rv);
1989  if (NS_FAILED(rv))
1990  mpDownloadMediaList = nsnull;
1991 }
1992 
1993 
1994 /*
1995  * UpdateDownloadMediaList
1996  *
1997  * This function updates the download device media list.
1998  */
1999 
2000 nsresult sbDownloadDevice::UpdateDownloadMediaList()
2001 {
2002  nsresult rv;
2003 
2004  /* Set the download device media list name. */
2005  rv = mpDownloadMediaList->SetName(NS_LITERAL_STRING(SB_DOWNLOAD_LIST_NAME));
2006  NS_ENSURE_SUCCESS(rv, rv);
2007 
2008  /* Set the download device media list default column spec. */
2009  nsAutoString downloadColSpec;
2010  downloadColSpec.AppendLiteral(SB_DOWNLOAD_COL_SPEC);
2011  rv = mpDownloadMediaList->SetProperty
2012  (NS_LITERAL_STRING(SB_PROPERTY_DEFAULTCOLUMNSPEC),
2013  downloadColSpec);
2014  NS_ENSURE_SUCCESS(rv, rv);
2015 
2016  /* Set the download device media list custom type property. */
2017  rv = mpDownloadMediaList->SetProperty
2018  (NS_LITERAL_STRING(SB_PROPERTY_CUSTOMTYPE),
2019  NS_LITERAL_STRING(SB_DOWNLOAD_CUSTOM_TYPE));
2020 
2021  /* Set the sortable property to false. */
2022  rv = mpDownloadMediaList->SetProperty
2023  (NS_LITERAL_STRING(SB_PROPERTY_ISSORTABLE),
2024  NS_LITERAL_STRING("0"));
2025 
2026  NS_ENSURE_SUCCESS(rv, rv);
2027 
2028  return NS_OK;
2029 }
2030 
2031 
2032 /* *****************************************************************************
2033  *
2034  * Download device transfer services.
2035  *
2036  ******************************************************************************/
2037 
2038 /*
2039  * EnqueueItem
2040  *
2041  * --> apMediaItem Media item to enqueue.
2042  *
2043  * This function enqueues the media item specified by apMediaItem for
2044  * download.
2045  */
2046 
2047 nsresult sbDownloadDevice::EnqueueItem(
2048  sbIMediaItem *apMediaItem)
2049 {
2050  nsresult rv;
2051 
2052  /* Set the item transfer destination. */
2053  rv = SetTransferDestination(apMediaItem);
2054  NS_ENSURE_SUCCESS(rv, rv);
2055 
2056  /* Update the download button property to reflect the stating state. */
2057  nsCOMPtr<sbIMediaItem> statusTarget;
2058  rv = GetStatusTarget(apMediaItem, getter_AddRefs(statusTarget));
2059  NS_ENSURE_SUCCESS(rv, rv);
2060  sbAutoDownloadButtonPropertyValue property(apMediaItem, statusTarget);
2061  property.value->SetMode(sbDownloadButtonPropertyValue::eStarting);
2062 
2063  /* Mark the download detail property as queued. */
2064  rv = apMediaItem->SetProperty
2065  (NS_LITERAL_STRING(SB_PROPERTY_DOWNLOAD_DETAILS),
2066  mQueuedStr);
2067  NS_ENSURE_SUCCESS(rv, rv);
2068  if (statusTarget)
2069  {
2070  rv = statusTarget->SetProperty
2071  (NS_LITERAL_STRING(SB_PROPERTY_DOWNLOAD_DETAILS),
2072  mQueuedStr);
2073  NS_ENSURE_SUCCESS(rv, rv);
2074  }
2075 
2076  /* Add item to the transfer queue. */
2077  {
2078  nsAutoMonitor mon(mpDeviceMonitor);
2079  rv = AddItemToTransferQueue(mDeviceIdentifier, apMediaItem);
2080  NS_ENSURE_SUCCESS(rv, rv);
2081  }
2082 
2083  return (NS_OK);
2084 }
2085 
2086 
2087 /*
2088  * RunTransferQueue
2089  *
2090  * This function runs the transfer queue. It will start transferring the next
2091  * item in the transfer queue.
2092  */
2093 
2094 nsresult sbDownloadDevice::RunTransferQueue()
2095 {
2096  nsCOMPtr<sbIMediaItem> pMediaItem;
2097  PRBool initiated = PR_FALSE;
2098  nsresult result = NS_OK;
2099 
2100  /* Lock the device. */
2101  nsAutoMonitor mon(mpDeviceMonitor);
2102 
2103  /* Initiate transfers until queue is busy or empty. */
2104  while ( !mpDownloadSession
2105  && GetNextTransferItem(getter_AddRefs(pMediaItem)))
2106  {
2107  /* Initiate item transfer. */
2108  mpDownloadSession = new sbDownloadSession(this, pMediaItem);
2109  if (!mpDownloadSession)
2110  result = NS_ERROR_OUT_OF_MEMORY;
2111  if (NS_SUCCEEDED(result))
2112  result = mpDownloadSession->Initiate();
2113  if (NS_SUCCEEDED(result))
2114  initiated = PR_TRUE;
2115  else
2116  initiated = PR_FALSE;
2117 
2118  /* Send notification that the transfer started. */
2119  if (initiated)
2120  DoTransferStartCallback(pMediaItem);
2121 
2122  /* Release the download session if not initiated. */
2123  if (!initiated && mpDownloadSession)
2124  mpDownloadSession = nsnull;
2125  }
2126 
2127  /* Update device state. */
2128  if (mpDownloadSession)
2129  {
2130  if (!mpDownloadSession->IsSuspended())
2131  SetDeviceState(mDeviceIdentifier, STATE_DOWNLOADING);
2132  else
2133  SetDeviceState(mDeviceIdentifier, STATE_DOWNLOAD_PAUSED);
2134  }
2135  else
2136  {
2137  SetDeviceState(mDeviceIdentifier, STATE_IDLE);
2138  }
2139 
2140  return result;
2141 }
2142 
2143 
2144 /*
2145  * GetNextTransferItem
2146  *
2147  * <-- appMediaItem Next media item to transfer.
2148  *
2149  * <-- True The next media item to transfer was returned.
2150  * False The transfer queue is busy or no media is
2151  * available.
2152  *
2153  * This function returns in appMediaItem the next media item to transfer. If
2154  * a media item is available for transfer and the transfer queue is not busy,
2155  * this function returns true; otherwise, it returns false.
2156  */
2157 
2158 PRBool sbDownloadDevice::GetNextTransferItem(
2159  sbIMediaItem **appMediaItem)
2160 {
2161  nsCOMPtr<sbIMediaItem> pMediaItem;
2162  nsresult result = NS_OK;
2163 
2164  /* Get the next media item to transfer. */
2165  result = GetNextItemFromTransferQueue(mDeviceIdentifier,
2166  getter_AddRefs(pMediaItem));
2167  if (NS_SUCCEEDED(result))
2168  result = RemoveItemFromTransferQueue(mDeviceIdentifier, pMediaItem);
2169 
2170  /* Return results. */
2171  if (NS_SUCCEEDED(result))
2172  {
2173  NS_ADDREF(*appMediaItem = pMediaItem);
2174  return (PR_TRUE);
2175  }
2176  else
2177  {
2178  return (PR_FALSE);
2179  }
2180 }
2181 
2182 
2183 /*
2184  * ResumeTransfers
2185  *
2186  * This function resumes all uncompleted transfers in the download device
2187  * library.
2188  */
2189 
2190 nsresult sbDownloadDevice::ResumeTransfers()
2191 {
2192  nsCOMPtr<sbIMediaItem> pMediaItem;
2193  PRUint32 itemCount;
2194  PRUint32 queuedCount = 0;
2195  PRUint32 i;
2196  nsresult itemResult;
2197  nsresult result = NS_OK;
2198 
2199  result = mpDownloadMediaList->GetLength(&itemCount);
2200 
2201  /* Resume each incomplete item in list. */
2202  for (i = 0; (NS_SUCCEEDED(result) && (i < itemCount)); i++)
2203  {
2204  /* Get the next item. */
2205  itemResult = mpDownloadMediaList->GetItemByIndex
2206  (i, getter_AddRefs(pMediaItem));
2207  NS_ENSURE_SUCCESS(itemResult, itemResult);
2208 
2209  sbAutoDownloadButtonPropertyValue property(pMediaItem, nsnull, PR_TRUE);
2210 
2211  /* Add item to transfer queue if not complete. */
2212  if (property.value->GetMode() !=
2214 
2215  nsAutoMonitor mon(mpDeviceMonitor);
2216  itemResult = AddItemToTransferQueue(mDeviceIdentifier, pMediaItem);
2217  if (NS_SUCCEEDED(itemResult))
2218  queuedCount++;
2219  }
2220 
2221  }
2222 
2223  /* Run the transfer queue if any items were queued. */
2224  if (queuedCount > 0)
2225  RunTransferQueue();
2226 
2227  return (result);
2228 }
2229 
2230 
2231 /*
2232  * SetTransferDestination
2233  *
2234  * --> pMediaItem Media item for which to set transfer
2235  * destination.
2236  *
2237  * This function sets the transfer destination property for the media item
2238  * specified by pMediaItem. If the transfer destination is already set, this
2239  * function does nothing.
2240  */
2241 
2242 nsresult sbDownloadDevice::SetTransferDestination(
2243  nsCOMPtr<sbIMediaItem> pMediaItem)
2244 {
2245  nsString dstProp;
2246  nsCOMPtr<nsIFile> pDstFile;
2247  nsCOMPtr<nsIURI> pDstURI;
2248  nsCOMPtr<sbIDownloadDeviceHelper>
2249  pDownloadHelper;
2250  nsCString dstSpec;
2251  nsresult propertyResult;
2252  nsAutoString contentType;
2253  nsresult result = NS_OK;
2254 
2255  /* Do nothing if destination is already set. */
2256  propertyResult = pMediaItem->GetProperty
2257  (NS_LITERAL_STRING(SB_PROPERTY_DESTINATION),
2258  dstProp);
2259  if (NS_SUCCEEDED(propertyResult) && !dstProp.IsEmpty())
2260  return (result);
2261 
2262  /* Get the destination folder from the download helper. This will pop the */
2263  /* dialog to the user if needed. It will throw an error if the user */
2264  /* cancels the dialog. */
2265  if (NS_SUCCEEDED(result))
2266  {
2267  pDownloadHelper = do_GetService
2268  ("@songbirdnest.com/Songbird/DownloadDeviceHelper;1",
2269  &result);
2270  }
2271 
2272  if (NS_SUCCEEDED(result))
2273  result = pMediaItem->GetContentType (contentType);
2274 
2275  if (NS_SUCCEEDED(result))
2276  result = pDownloadHelper->GetDownloadFolder(contentType,
2277  getter_AddRefs(pDstFile));
2278 
2279  /* Create a unique local destination file object. */
2280  /* note that we only record the directory, we do not */
2281  /* append the filename until when the file finishes */
2282  /* to download */
2283  /* Get the destination URI spec. */
2284  if (NS_SUCCEEDED(result))
2285  result = mpIOService->NewFileURI(pDstFile, getter_AddRefs(pDstURI));
2286 
2287  if (NS_SUCCEEDED(result))
2288  result = pDstURI->GetSpec(dstSpec);
2289 
2290  /* Set the destination property. */
2291  if (NS_SUCCEEDED(result))
2292  {
2293  result = pMediaItem->SetProperty
2294  (NS_LITERAL_STRING(SB_PROPERTY_DESTINATION),
2295  NS_ConvertUTF8toUTF16(dstSpec));
2296  }
2297 
2298  return (result);
2299 }
2300 
2301 
2302 /*
2303  * CancelSession
2304  *
2305  * This function cancels the active download session and runs the transfer
2306  * queue.
2307  */
2308 
2309 nsresult sbDownloadDevice::CancelSession()
2310 {
2311  nsresult result = NS_OK;
2312 
2313  /* Shutdown the session. */
2314  if (mpDownloadSession)
2315  {
2316  mpDownloadSession->Shutdown();
2317  mpDownloadSession = nsnull;
2318  }
2319 
2320  /* Run the transfer queue. */
2321  RunTransferQueue();
2322 
2323  return (result);
2324 }
2325 
2326 
2327 /*
2328  * SessionCompleted
2329  *
2330  * --> apDownloadSession Completed session.
2331  * --> aStatus Final session status.
2332  *
2333  * This function is called when the download session specified by
2334  * apDownloadSession completes with the final status specified by aStatus.
2335  */
2336 
2337 void sbDownloadDevice::SessionCompleted(
2338  sbDownloadSession *apDownloadSession,
2339  PRInt32 aStatus)
2340 {
2341  /* Complete session. */
2342  {
2343  /* Lock the device. */
2344  nsAutoMonitor mon(mpDeviceMonitor);
2345 
2346  /* Deliver transfer completion callbacks. */
2347  DoTransferCompleteCallback(apDownloadSession->mpMediaItem, aStatus);
2348 
2349  /* Release the download session. */
2350  if (apDownloadSession == mpDownloadSession)
2351  mpDownloadSession = nsnull;
2352  }
2353 
2354  /* Run the transfer queue. */
2355  RunTransferQueue();
2356 }
2357 
2358 
2359 /* *****************************************************************************
2360  *
2361  * Private download device services.
2362  *
2363  ******************************************************************************/
2364 
2365 /*
2366  * GetTmpFile
2367  *
2368  * <-- ppTmpFile Temporary file.
2369  *
2370  * This function returns in ppTmpFile an nsIFile object for a unique temporary
2371  * file. This function does not create the file.
2372  */
2373 
2374 nsresult sbDownloadDevice::GetTmpFile(
2375  nsIFile **ppTmpFile)
2376 {
2377  nsCOMPtr<nsIFile> pTmpFile;
2378  nsString tmpFileName;
2379  PRInt32 fileNum;
2380  PRBool exists;
2381  nsresult result = NS_OK;
2382 
2383  /* Get a unique temporary download file. */
2384  fileNum = 1;
2385  do
2386  {
2387  /* Start at the temporary download directory. */
2388  result = mpTmpDownloadDir->Clone(getter_AddRefs(pTmpFile));
2389 
2390  /* Append the temporary file name. */
2391  if (NS_SUCCEEDED(result))
2392  {
2393  tmpFileName.AssignLiteral("tmp");
2394  tmpFileName.AppendInt(fileNum++);
2395  result = pTmpFile->Append(tmpFileName);
2396  }
2397 
2398  /* Check if the file exists. */
2399  if (NS_SUCCEEDED(result))
2400  result = pTmpFile->Exists(&exists);
2401  }
2402  while (exists && NS_SUCCEEDED(result));
2403 
2404  /* Return results. */
2405  if (NS_SUCCEEDED(result))
2406  NS_ADDREF(*ppTmpFile = pTmpFile);
2407 
2408  return (result);
2409 }
2410 
2411 
2412 /*
2413  * MakeFileUnique
2414  *
2415  * --> apFile File to make unique.
2416  *
2417  * This function makes the file object specified by apFile refer to a unique,
2418  * non-existent file. If the specified file does not exist, this function does
2419  * nothing. Otherwise, it tries different file leaf names until a non-existent
2420  * file is found and sets the specified file object's leaf name accordingly.
2421  */
2422 
2423 nsresult sbDownloadDevice::MakeFileUnique(
2424  nsIFile *apFile)
2425 {
2426  nsCOMPtr<nsIFile> pUniqueFile;
2427  nsAutoString leafName;
2428  nsAutoString uniqueLeafName;
2429  PRInt32 extOffset = -1;
2430  nsAutoString uniqueStr;
2431  PRInt32 uniqueNum = 1;
2432  PRBool exists;
2433  nsresult result = NS_OK;
2434 
2435  /* Do nothing if file does not exist. */
2436  result = apFile->Exists(&exists);
2437  if (NS_FAILED(result) || !exists)
2438  return (result);
2439 
2440  /* Clone the file object. */
2441  if (NS_SUCCEEDED(result))
2442  result = apFile->Clone(getter_AddRefs(pUniqueFile));
2443 
2444  /* Get the file leaf name. */
2445  if (NS_SUCCEEDED(result))
2446  result = pUniqueFile->GetLeafName(leafName);
2447  if (NS_SUCCEEDED(result))
2448  extOffset = leafName.RFindChar('.');
2449 
2450  /* Find a non-existent file name. */
2451  while (NS_SUCCEEDED(result) && exists)
2452  {
2453  /* Try producing a unique string. */
2454  uniqueStr.AssignLiteral("_");
2455  uniqueStr.AppendInt(uniqueNum++);
2456  uniqueStr.AppendLiteral("_");
2457 
2458  /* Add the unique string to the file leaf name. */
2459  uniqueLeafName.Assign(leafName);
2460  if (extOffset == -1)
2461  uniqueLeafName.Append(uniqueStr);
2462  else
2463  uniqueLeafName.Insert(uniqueStr, extOffset);
2464 
2465  /* Check if the file exists. */
2466  result = pUniqueFile->SetLeafName(uniqueLeafName);
2467  if (NS_SUCCEEDED(result))
2468  result = pUniqueFile->Exists(&exists);
2469 
2470  /* Limit number of tries. */
2471  if (exists && (uniqueNum > 1000))
2472  result = NS_ERROR_FILE_TOO_BIG;
2473  }
2474 
2475  /* Update the file with a unique leaf name. */
2476  if (NS_SUCCEEDED(result))
2477  result = apFile->SetLeafName(uniqueLeafName);
2478 
2479  return (result);
2480 }
2481 
2482 /*
2483  * OpenDialog
2484  *
2485  * --> aChromeURL URL to dialog chrome.
2486  * --> apDialogPB Dialog parameter block.
2487  *
2488  * This function opens a dialog window with the chrome specified by aChromeURL
2489  * and the parameter block specified by apDialogPB.
2490  */
2491 
2492 nsresult sbDownloadDevice::OpenDialog(
2493  char *aChromeURL,
2494  nsIDialogParamBlock *apDialogPB)
2495 {
2496  nsCOMPtr<nsIWindowWatcher> pWindowWatcher;
2497  nsCOMPtr<nsIDOMWindow> pActiveWindow;
2498  nsCOMPtr<nsIDOMWindow> pWindow;
2499  nsCOMPtr<sbIDataRemote> pDataRemote;
2500  nsCAutoString chromeFeatures;
2501  PRBool accessibilityEnabled;
2502  nsresult result = NS_OK;
2503 
2504  /* Get the window watcher services. */
2505  pWindowWatcher = do_GetService(NS_WINDOWWATCHER_CONTRACTID, &result);
2506 
2507  /* Get the active window. */
2508  if (NS_SUCCEEDED(result))
2509  result = pWindowWatcher->GetActiveWindow(getter_AddRefs(pActiveWindow));
2510 
2511  /* Check if accessibility is enabled. */
2512  if (NS_SUCCEEDED(result))
2513  {
2514  pDataRemote = do_CreateInstance
2515  ("@songbirdnest.com/Songbird/DataRemote;1",
2516  &result);
2517  }
2518  if (NS_SUCCEEDED(result))
2519  {
2520  result = pDataRemote->Init(NS_LITERAL_STRING("accessibility.enabled"),
2521  EmptyString());
2522  }
2523  if (NS_SUCCEEDED(result))
2524  result = pDataRemote->GetBoolValue(&accessibilityEnabled);
2525 
2526  /* Get the chrome feature set. */
2527  if (NS_SUCCEEDED(result))
2528  {
2529  chromeFeatures = NS_LITERAL_CSTRING
2530  ("chrome,centerscreen,modal=yes,resizable=no");
2531  if (accessibilityEnabled)
2532  chromeFeatures.AppendLiteral(",titlebar=yes");
2533  else
2534  chromeFeatures.AppendLiteral(",titlebar=no");
2535  }
2536 
2537  /* Open the dialog. */
2538  if (NS_SUCCEEDED(result))
2539  {
2540  pWindowWatcher->OpenWindow(pActiveWindow,
2541  aChromeURL,
2542  nsnull,
2543  chromeFeatures.get(),
2544  apDialogPB,
2545  getter_AddRefs(pWindow));
2546  }
2547 
2548  return (result);
2549 }
2550 
2551 
2552 /* static */ nsresult sbDownloadDevice::GetStatusTarget(
2553  sbIMediaItem *apMediaItem,
2554  sbIMediaItem **apStatusTarget)
2555 {
2556  NS_ASSERTION(apMediaItem, "apMediaItem is null");
2557  NS_ASSERTION(apStatusTarget, "apStatusTaret is null");
2558 
2559  nsresult rv;
2560  nsString target;
2561  rv = apMediaItem->GetProperty(NS_LITERAL_STRING(SB_PROPERTY_DOWNLOAD_STATUS_TARGET),
2562  target);
2563  NS_ENSURE_SUCCESS(rv, rv);
2564  PRInt32 pos = target.FindChar(',');
2565  if (pos >= 0) {
2566  nsDependentSubstring mediaItemGuid(target, pos + 1);
2567 
2568  nsString originalItemGuid;
2569  rv = apMediaItem->GetGuid(originalItemGuid);
2570  NS_ENSURE_SUCCESS(rv, rv);
2571 
2572  if (originalItemGuid.Equals(mediaItemGuid)) {
2573  *apStatusTarget = nsnull;
2574  return NS_OK;
2575  }
2576 
2577  nsCOMPtr<sbILibraryManager> libraryManager =
2578  do_GetService("@songbirdnest.com/Songbird/library/Manager;1", &rv);
2579  NS_ENSURE_SUCCESS(rv, rv);
2580 
2581  nsDependentSubstring libraryGuid(target, 0, pos);
2582  nsCOMPtr<sbILibrary> library;
2583  rv = libraryManager->GetLibrary(libraryGuid, getter_AddRefs(library));
2584  if (rv != NS_ERROR_NOT_AVAILABLE) {
2585  NS_ENSURE_SUCCESS(rv, rv);
2586  rv = library->GetItemByGuid(mediaItemGuid, apStatusTarget);
2587  if (rv != NS_ERROR_NOT_AVAILABLE) {
2588  NS_ENSURE_SUCCESS(rv, rv);
2589  }
2590  else {
2591  NS_WARNING("Status target media item not found");
2592  *apStatusTarget = nsnull;
2593  }
2594  }
2595  else {
2596  NS_WARNING("Status target library not found");
2597  *apStatusTarget = nsnull;
2598  }
2599  }
2600 
2601  return NS_OK;
2602 }
2603 
2604 /* *****************************************************************************
2605  *******************************************************************************
2606  *
2607  * Download session.
2608  *
2609  *******************************************************************************
2610  ******************************************************************************/
2611 
2612 /* *****************************************************************************
2613  *
2614  * Download session imported services.
2615  *
2616  ******************************************************************************/
2617 
2618 /* Mozilla imports. */
2619 #include <nsIChannel.h>
2620 #include <prprf.h>
2621 
2622 
2623 /* *****************************************************************************
2624  *
2625  * Public download session services.
2626  *
2627  ******************************************************************************/
2628 
2629 /*
2630  * sbDownloadSession
2631  *
2632  * This method is the constructor for the download session class.
2633  */
2634 
2636  sbDownloadDevice *pDownloadDevice,
2637  sbIMediaItem *pMediaItem)
2638 :
2639  mpMediaItem(pMediaItem),
2640  mpSessionLock(nsnull),
2641  mpDownloadDevice(pDownloadDevice),
2642  mShutdown(PR_FALSE),
2643  mSuspended(PR_FALSE),
2644  mInitialProgressBytes(0),
2645  mLastProgressBytes(0)
2646 {
2647  TRACE(("sbDownloadSession[0x%.8x] - ctor", this));
2648 }
2649 
2650 
2651 /*
2652  * ~sbDownloadSession
2653  *
2654  * This method is the destructor for the download session class.
2655  */
2656 
2658 {
2659  /* clean up any active downloads, if we still manage to have them */
2660  Shutdown();
2661 
2662  /* Dispose of the session lock. */
2663  if (mpSessionLock)
2664  nsAutoLock::DestroyLock(mpSessionLock);
2665 
2666  TRACE(("sbDownloadSession[0x%.8x] - dtor", this));
2667 }
2668 
2669 
2670 /*
2671  * Initiate
2672  *
2673  * This function initiates the download session.
2674  */
2676 {
2677  TRACE(("sbDownloadSession[0x%.8x] - Initiate", this));
2678  nsCOMPtr<sbILibraryManager> pLibraryManager;
2679  nsCOMPtr<nsIURI> pDstURI;
2680  nsString dstSpec;
2681  nsCString dstCSpec;
2682  nsCOMPtr<nsILocalFile> pDstFile;
2683  nsCString fileName;
2684  nsCOMPtr<nsIURI> pURI;
2685  nsCOMPtr<nsIStandardURL> pStandardURL;
2686  nsresult rv;
2687 
2688  /* Get the library manager and utilities services. */
2689  mpLibraryUtils =
2690  do_GetService("@songbirdnest.com/Songbird/library/Manager;1", &rv);
2691  NS_ENSURE_SUCCESS(rv, rv);
2692 
2693  pLibraryManager = do_GetService
2694  ("@songbirdnest.com/Songbird/library/Manager;1",
2695  &rv);
2696  NS_ENSURE_SUCCESS(rv, rv);
2697 
2698  /* Get the string bundle. */
2699  nsCOMPtr<nsIStringBundleService>
2700  pStringBundleService;
2701 
2702  /* Get the download device string bundle. */
2703  pStringBundleService = do_GetService(NS_STRINGBUNDLE_CONTRACTID,
2704  &rv);
2705  NS_ENSURE_SUCCESS(rv, rv);
2706 
2707  rv = pStringBundleService->CreateBundle
2709  getter_AddRefs(mpStringBundle));
2710  NS_ENSURE_SUCCESS(rv, rv);
2711 
2712  /* Get some strings. */
2713  rv = mpStringBundle->GetStringFromName
2714  (NS_LITERAL_STRING("device.download.complete").get(),
2715  getter_Copies(mCompleteStr));
2716  NS_ENSURE_SUCCESS(rv, rv);
2717 
2718  rv = mpStringBundle->GetStringFromName
2719  (NS_LITERAL_STRING("device.download.error").get(),
2720  getter_Copies(mErrorStr));
2721  NS_ENSURE_SUCCESS(rv, rv);
2722 
2723  /* Create the session lock. */
2724  mpSessionLock = nsAutoLock::NewLock("sbDownloadSession::mpSessionLock");
2725  if (!mpSessionLock)
2726  return NS_ERROR_OUT_OF_MEMORY;
2727 
2728  /* Get a unique temporary download file. */
2729  rv = mpDownloadDevice->GetTmpFile(getter_AddRefs(mpTmpFile));
2730  NS_ENSURE_SUCCESS(rv, rv);
2731 
2732  /* Set the origin URL */
2733  // Check if the origin URL is already set, if not copy from ContentSrc
2734  // We do this so that we don't overwrite the originURL with a downloaded
2735  // file location, then we can still see the original originURL.
2736  nsAutoString currentOriginURL;
2737  mpMediaItem->GetProperty(NS_LITERAL_STRING(SB_PROPERTY_ORIGINURL),
2738  currentOriginURL);
2739  if (currentOriginURL.IsEmpty()) {
2740  nsCOMPtr<nsIURI> pSrcURI;
2741  nsCString srcSpec;
2742  rv = mpMediaItem->GetContentSrc(getter_AddRefs(pSrcURI));
2743  NS_ENSURE_SUCCESS(rv, rv);
2744 
2745  rv = pSrcURI->GetSpec(srcSpec);
2746  NS_ENSURE_SUCCESS(rv, rv);
2747 
2748  mSrcURISpec = NS_ConvertUTF8toUTF16(srcSpec);
2749  rv = mpMediaItem->SetProperty
2750  (NS_LITERAL_STRING(SB_PROPERTY_ORIGINURL),
2751  mSrcURISpec);
2752  if (NS_FAILED(rv))
2753  {
2754  NS_WARNING("Failed to set originURL, this item may be duplicated later \
2755  because its origin cannot be tracked!");
2756  return rv;
2757  }
2758  }
2759 
2760  /* Get the status target, if any */
2761  rv = sbDownloadDevice::GetStatusTarget(mpMediaItem,
2762  getter_AddRefs(mpStatusTarget));
2763  NS_ENSURE_SUCCESS(rv, rv);
2764 
2765  /* Get the destination download (directory) URI. */
2766  rv = mpMediaItem->GetProperty
2767  (NS_LITERAL_STRING(SB_PROPERTY_DESTINATION),
2768  dstSpec);
2769  if (NS_SUCCEEDED(rv) && dstSpec.IsEmpty())
2770  rv = NS_ERROR_FAILURE;
2771  NS_ENSURE_SUCCESS(rv, rv);
2772 
2773  rv = NS_NewURI(getter_AddRefs(mpDstURI), dstSpec);
2774  NS_ENSURE_SUCCESS(rv, rv);
2775 
2776  {
2777  /* Keep a reference to the nsIFile pointing at the directory */
2778  nsCOMPtr<nsIFileURL> pFileURL;
2779  nsCOMPtr<nsIFile> pFile;
2780  pFileURL = do_QueryInterface(mpDstURI, &rv);
2781  NS_ENSURE_SUCCESS(rv, rv);
2782  rv = pFileURL->GetFile(getter_AddRefs(pFile));
2783  NS_ENSURE_SUCCESS(rv, rv);
2784  pDstFile = do_QueryInterface(pFile, &rv);
2785  NS_ENSURE_SUCCESS(rv, rv);
2786  }
2787 
2788  /* set the destination directory */
2789  rv = pDstFile->Clone(getter_AddRefs(mpDstFile));
2790  NS_ENSURE_SUCCESS(rv, rv);
2791 
2792  /* Get the destination library. */
2793  rv = pLibraryManager->GetMainLibrary(getter_AddRefs(mpDstLibrary));
2794  NS_ENSURE_SUCCESS(rv, rv);
2795 
2796  /* Get a URI for the source. */
2797  rv = mpMediaItem->GetContentSrc(getter_AddRefs(mpSrcURI));
2798  NS_ENSURE_SUCCESS(rv, rv);
2799 
2800  /* Start download */
2801  rv = SetUpRequest();
2802  NS_ENSURE_SUCCESS(rv, rv);
2803 
2804  /* Create the idle timer */
2805  mIdleTimer = do_CreateInstance(NS_TIMER_CONTRACTID, &rv);
2806  NS_ENSURE_SUCCESS(rv, rv);
2807 
2808  /* Create the progress timer */
2809  mProgressTimer = do_CreateInstance(NS_TIMER_CONTRACTID, &rv);
2810  NS_ENSURE_SUCCESS(rv, rv);
2811 
2812  return rv;
2813 }
2814 
2815 /*
2816  * SetUpRequest
2817  *
2818  * Starts the download or resumes a previously cancelled download.
2819  */
2820 
2821 nsresult sbDownloadSession::SetUpRequest()
2822 {
2823  nsresult rv;
2824 
2825  /* Create a persistent download web browser. */
2826  mpWebBrowser = do_CreateInstance
2827  ("@mozilla.org/embedding/browser/nsWebBrowserPersist;1",
2828  &rv);
2829  NS_ENSURE_SUCCESS(rv, rv);
2830 
2831  /* Create channel to download from. */
2832  nsCOMPtr<nsIInterfaceRequestor> ir(do_QueryInterface(mpWebBrowser));
2833  rv = NS_NewChannel(getter_AddRefs(mpRequest), mpSrcURI, nsnull, nsnull, ir);
2834  NS_ENSURE_SUCCESS(rv, rv);
2835 
2836  if (!mEntityID.IsEmpty())
2837  {
2838  /* We are resuming a download, initialize the channel to resume at the correct position */
2839  nsCOMPtr<nsIFile> clone;
2840  if (NS_FAILED(mpTmpFile->Clone(getter_AddRefs(clone))) ||
2841  NS_FAILED(clone->GetFileSize(&mInitialProgressBytes)))
2842  {
2843  NS_WARNING("Restarting download instead of resuming: failed \
2844  determining how much is already downloaded!");
2845  mInitialProgressBytes = 0;
2846  }
2847 
2848  if (mInitialProgressBytes)
2849  {
2850  nsCOMPtr<nsIResumableChannel> resumableChannel(do_QueryInterface(mpRequest));
2851  if (resumableChannel &&
2852  NS_SUCCEEDED(resumableChannel->ResumeAt(mInitialProgressBytes, mEntityID)))
2853  {
2854  /* We are resuming download, make sure to append to the existing file */
2855  rv = mpWebBrowser->SetPersistFlags(nsIWebBrowserPersist::PERSIST_FLAGS_APPEND_TO_FILE);
2856  NS_ENSURE_SUCCESS(rv, rv);
2857  }
2858  else
2859  {
2860  NS_WARNING("Restarting download instead of resuming: \
2861  nsIResumableChannel.ResumeAt failed");
2862  mInitialProgressBytes = 0;
2863  }
2864  }
2865  }
2866 
2867  /* Set up the listener. */
2868  mLastUpdate = PR_Now();
2869  rv = mpWebBrowser->SetProgressListener(this);
2870  NS_ENSURE_SUCCESS(rv, rv);
2871 
2872  /* Initiate the download. */
2873  rv = mpWebBrowser->SaveChannel(mpRequest, mpTmpFile);
2874  if (NS_FAILED(rv))
2875  {
2876  NS_WARNING("Failed initiating download");
2877  mpWebBrowser->SetProgressListener(nsnull);
2878  }
2879  return rv;
2880 }
2881 
2882 /*
2883  * Suspend
2884  *
2885  * This function suspends the download session.
2886  */
2887 
2889 {
2890  TRACE(("sbDownloadSession[0x%.8x] - Suspend", this));
2891  NS_ENSURE_STATE(!mShutdown);
2892 
2893  nsresult rv;
2894 
2895  /* Lock the session. */
2896  nsAutoLock lock(mpSessionLock);
2897 
2898  /* Do nothing if already suspended. */
2899  if (mSuspended)
2900  return NS_OK;
2901 
2902  /* Suspend request. */
2903  mEntityID.Truncate();
2904  nsCOMPtr<nsIResumableChannel> resumable(do_QueryInterface(mpRequest));
2905  if (resumable)
2906  resumable->GetEntityID(mEntityID);
2907 
2908  if (!mEntityID.IsEmpty())
2909  {
2910  /* Channel is resumable, simply cancel current request */
2911  rv = mpWebBrowser->Cancel(NS_BINDING_ABORTED);
2912  NS_ENSURE_SUCCESS(rv, rv);
2913 
2914  mpRequest = nsnull;
2915  mpWebBrowser->SetProgressListener(nsnull);
2916  mpWebBrowser = nsnull;
2917  }
2918  else
2919  {
2920  /* Channel not resumable, try suspending */
2921  rv = mpRequest->Suspend();
2922  NS_ENSURE_SUCCESS(rv, rv);
2923  }
2924 
2925  /* Update the download media item download button property. */
2926  sbAutoDownloadButtonPropertyValue property(mpMediaItem, mpStatusTarget);
2927  property.value->SetMode(sbDownloadButtonPropertyValue::ePaused);
2928 
2929  StopTimers();
2930 
2931  /* Mark session as suspended. */
2932  mSuspended = PR_TRUE;
2933 
2934  return NS_OK;
2935 }
2936 
2937 
2938 /*
2939  * Resume
2940  *
2941  * This function resumes the download session.
2942  */
2943 
2945 {
2946  TRACE(("sbDownloadSession[0x%.8x] - Resume", this));
2947  NS_ENSURE_STATE(!mShutdown);
2948 
2949  nsresult rv;
2950 
2951  /* Lock the session. */
2952  nsAutoLock lock(mpSessionLock);
2953 
2954  /* Do nothing if not suspended. */
2955  if (!mSuspended)
2956  return NS_OK;
2957 
2958  /* Resume the request. */
2959  if (!mEntityID.IsEmpty())
2960  {
2961  rv = SetUpRequest();
2962  NS_ENSURE_SUCCESS(rv, rv);
2963  }
2964  else
2965  {
2966  rv = mpRequest->Resume();
2967  NS_ENSURE_SUCCESS(rv, rv);
2968  }
2969 
2970  /* Update the download media item download button property. */
2971  sbAutoDownloadButtonPropertyValue property(mpMediaItem, mpStatusTarget);
2972  property.value->SetMode(sbDownloadButtonPropertyValue::eDownloading);
2973 
2974  StartTimers();
2975 
2976  /* Mark session as not suspended. */
2977  mSuspended = PR_FALSE;
2978 
2979  return NS_OK;
2980 }
2981 
2982 
2983 /*
2984  * Shutdown
2985  *
2986  * This function shuts down the download session.
2987  */
2988 
2990 {
2991  TRACE(("sbDownloadSession[0x%.8x] - Shutdown", this));
2992 
2993  // If shutdown is called, this means the library is shutting down and
2994  // we shouldn't be touching the library any more
2995  mpMediaItem = nsnull;
2996 
2997  // If there is no lock, this download was never fully initalized so exit
2998  // early
2999  if (!mpSessionLock) {
3000  return;
3001  }
3002 
3003  /* Lock the session. */
3004  nsAutoLock lock(mpSessionLock);
3005 
3006  /* Stop the timers */
3007  StopTimers();
3008 
3009  // Keep a ref to ourselves since the clean up code will release us too
3010  // early
3011  nsRefPtr<sbDownloadSession> kungFuDeathGrip(this);
3012 
3013  /* Mark session for shutdown. */
3014  mShutdown = PR_TRUE;
3015 
3016  mpRequest = nsnull;
3017 
3018  if (mpWebBrowser) {
3019  mpWebBrowser->CancelSave();
3020  mpWebBrowser->SetProgressListener(nsnull);
3021  mpWebBrowser = nsnull;
3022  }
3023 
3024 }
3025 
3026 
3027 /*
3028  * IsSuspended
3029  *
3030  * <-- PR_TRUE Download session is suspended.
3031  *
3032  * This function returns PR_TRUE if the download session is suspended;
3033  * otherwise, it returns PR_FALSE.
3034  */
3035 
3037 {
3038  /* Lock the session. */
3039  nsAutoLock lock(mpSessionLock);
3040 
3041  return mSuspended;
3042 }
3043 
3044 
3045 /* *****************************************************************************
3046  *
3047  * Download session nsISupports implementation.
3048  *
3049  ******************************************************************************/
3050 
3052  nsIWebProgressListener,
3054 
3055 /* *****************************************************************************
3056  *
3057  * Download session nsIWebProgressListener services.
3058  *
3059  ******************************************************************************/
3060 
3061 
3084 NS_IMETHODIMP sbDownloadSession::OnStateChange(
3085  nsIWebProgress *aWebProgress,
3086  nsIRequest *aRequest,
3087  PRUint32 aStateFlags,
3088  nsresult aStatus)
3089 {
3090  TRACE(("sbDownloadSession[0x%.8x] - OnStateChange "
3091  "aWebProgress 0x%.8x aRequest 0x%.8x "
3092  "aStateFlags 0x%.8x aStatus 0x%.8x",
3093  this, aWebProgress, aRequest, aStateFlags, aStatus));
3094 
3095  PRBool complete = PR_FALSE;
3096  nsresult status = aStatus;
3097  nsresult result = NS_OK;
3098 
3099  // Keep a ref to ourselves since the clean up code will release us too
3100  // early
3101  nsRefPtr<sbDownloadSession> kungFuDeathGrip(this);
3102 
3103  /* Process state change with the session locked. */
3104  {
3105  /* Lock the session. */
3106  nsAutoLock lock(mpSessionLock);
3107 
3108  if (aStateFlags & STATE_START) {
3109  /* start the timer */
3110  StartTimers();
3111  } else if (aStateFlags & STATE_STOP) {
3112  /* stop the timer */
3113  StopTimers();
3114  }
3115 
3116  /* Do nothing if download has not stopped or if shutting down. */
3117  if (!(aStateFlags & STATE_STOP) || mShutdown)
3118  return NS_OK;
3119 
3120  /* Do nothing on abort. */
3121  /* XXXeps This is a workaround for the fact that shutdown */
3122  /* isn't called until after channel is aborted. */
3123  if (status == NS_ERROR_ABORT)
3124  return NS_OK;
3125 
3126  /* Check HTTP response status. */
3127  if (NS_SUCCEEDED(status))
3128  {
3129  nsCOMPtr<nsIHttpChannel> pHttpChannel;
3130  PRBool requestSucceeded;
3131 
3132  /* Try to get HTTP channel. */
3133  pHttpChannel = do_QueryInterface(aRequest, &result);
3134 
3135  /* Check if request succeeded. */
3136  if (NS_SUCCEEDED(result))
3137  result = pHttpChannel->GetRequestSucceeded(&requestSucceeded);
3138  if (NS_SUCCEEDED(result) && !requestSucceeded)
3139  status = NS_ERROR_UNEXPECTED;
3140 
3141  /* Don't propagate errors from here. */
3142  result = NS_OK;
3143  }
3144 
3145  /* Complete the transfer on success. */
3146  if (NS_SUCCEEDED(result) && NS_SUCCEEDED(status))
3147  {
3148  result = CompleteTransfer(aRequest);
3149  if (NS_SUCCEEDED(result))
3150  complete = PR_TRUE;
3151  }
3152 
3153  if (complete) {
3154  // Set the progress to complete.
3155  sbAutoDownloadButtonPropertyValue property(mpMediaItem,
3156  mpStatusTarget);
3157  property.value->SetMode(sbDownloadButtonPropertyValue::eComplete);
3158  } else {
3159  // Set the progress to error.
3160  sbAutoDownloadButtonPropertyValue property(mpMediaItem,
3161  mpStatusTarget);
3162  property.value->SetMode(sbDownloadButtonPropertyValue::eFailed);
3163  }
3164 
3165  /* Set the final download status. */
3166  nsAutoString statusStr;
3167  if (complete)
3168  statusStr.Assign(mCompleteStr);
3169  else
3170  statusStr.Assign(mErrorStr);
3171  mpMediaItem->SetProperty
3172  (NS_LITERAL_STRING(SB_PROPERTY_DOWNLOAD_DETAILS),
3173  statusStr);
3174  if (mpStatusTarget)
3175  {
3176  mpStatusTarget->SetProperty
3177  (NS_LITERAL_STRING(SB_PROPERTY_DOWNLOAD_DETAILS),
3178  statusStr);
3179  }
3180  }
3181 
3182  /* Send notification of session completion. Do this */
3183  /* outside of session lock to prevent deadlock. */
3184  mpDownloadDevice->SessionCompleted(this, status);
3185 
3186  /* Clean up within a session lock. */
3187  {
3188  /* Lock the session. */
3189  nsAutoLock lock(mpSessionLock);
3190 
3191  /* Clean up. */
3192  mpRequest = nsnull;
3193  if (mpWebBrowser)
3194  {
3195  mpWebBrowser->CancelSave();
3196  mpWebBrowser->SetProgressListener(nsnull);
3197  }
3198  mpWebBrowser = nsnull;
3199  mpMediaItem = nsnull;
3200  }
3201 
3202  return NS_OK;
3203 }
3204 
3205 
3234 NS_IMETHODIMP sbDownloadSession::OnProgressChange(
3235  nsIWebProgress *aWebProgress,
3236  nsIRequest *aRequest,
3237  PRInt32 aCurSelfProgress,
3238  PRInt32 aMaxSelfProgress,
3239  PRInt32 aCurTotalProgress,
3240  PRInt32 aMaxTotalProgress)
3241 {
3242  TRACE(("sbDownloadSession[0x%.8x] - OnProgressChange "
3243  "aWebProgress 0x%.8x aRequest 0x%.8x "
3244  "aCurSelfProgress %d aMaxSelfProgress %d "
3245  "aCurTotalProgress %d aMaxTotalProgress %d",
3246  this, aWebProgress, aRequest, aCurSelfProgress, aMaxSelfProgress,
3247  aCurTotalProgress, aMaxTotalProgress));
3248 
3249  /* we got some progress, let's reset the idle timer */
3250  ResetTimers();
3251 
3252  /* Update progress. */
3253  UpdateProgress(aCurSelfProgress, aMaxSelfProgress);
3254 
3255  return NS_OK;
3256 }
3257 
3258 
3275 NS_IMETHODIMP sbDownloadSession::OnLocationChange(
3276  nsIWebProgress *aWebProgress,
3277  nsIRequest *aRequest,
3278  nsIURI *aLocation)
3279 {
3280  TRACE(("sbDownloadSession[0x%.8x] - OnLocationChange "
3281  "aWebProgress 0x%.8x aRequest 0x%.8x aLocation 0x%.8x",
3282  this, aWebProgress, aRequest, aLocation));
3283 
3284  return (NS_OK);
3285 }
3286 
3287 
3306 NS_IMETHODIMP sbDownloadSession::OnStatusChange(
3307  nsIWebProgress *aWebProgress,
3308  nsIRequest *aRequest,
3309  nsresult aStatus,
3310  const PRUnichar *aMessage)
3311 {
3312  TRACE(("sbDownloadSession[0x%.8x] - OnStatusChange "
3313  "aWebProgress 0x%.8x aRequest 0x%.8x aStatus 0x%.8x aMessage '%s'",
3314  this, aWebProgress, aRequest, aStatus, aMessage));
3315 
3316  return (NS_OK);
3317 }
3318 
3319 
3339 NS_IMETHODIMP sbDownloadSession::OnSecurityChange(
3340  nsIWebProgress *aWebProgress,
3341  nsIRequest *aRequest,
3342  PRUint32 aState)
3343 {
3344  TRACE(("sbDownloadSession[0x%.8x] - OnSecurityChange "
3345  "aWebProgress 0x%.8x aRequest 0x%.8x aState 0x%.8x",
3346  this, aWebProgress, aRequest, aState));
3347 
3348  return (NS_OK);
3349 }
3350 
3351 
3352 /* *****************************************************************************
3353  *
3354  * Download session nsITimerCallback services.
3355  *
3356  ******************************************************************************/
3357 NS_IMETHODIMP sbDownloadSession::Notify(nsITimer* aTimer)
3358 {
3359  if (aTimer == mIdleTimer) {
3360  /* Once the idle timer has timed out we should abort the request.
3361  * Our onStateChange handler will take care of reflecting the state
3362  * into the UI. */
3363  mpRequest->Cancel(NS_BINDING_ABORTED);
3364  } else if (aTimer == mProgressTimer) {
3365  UpdateProgress(mLastProgressBytes, mLastProgressBytesMax);
3366  }
3367  return NS_OK;
3368 }
3369 
3370 /* *****************************************************************************
3371  *
3372  * Private download session services.
3373  *
3374  ******************************************************************************/
3375 
3376 /*
3377  * CompleteTransfer
3378  *
3379  * This function completes transfer of the download file into the destination
3380  * library.
3381  */
3382 
3383 nsresult sbDownloadSession::CompleteTransfer(nsIRequest* aRequest)
3384 {
3385  nsCOMPtr<nsIFile> pFileDir;
3386  nsString fileName;
3387  nsCString srcSpec;
3388  nsCOMPtr<nsIURI> pSrcURI;
3389  nsCOMPtr<sbIMediaList> pDstMediaList;
3390  nsresult result = NS_OK;
3391  PRBool bIsDirectory;
3392  PRBool bChangedDstFile = PR_FALSE;
3393 
3394  // Get the the file name, if any, supplied in the MIME header:
3395  nsCString mimeFilename;
3396  nsCOMPtr<nsIHttpChannel> httpChannel =
3397  do_QueryInterface(aRequest, &result);
3398  if (NS_SUCCEEDED(result)) {
3399  nsCAutoString contentDisposition;
3400  result = httpChannel->GetResponseHeader(
3401  NS_LITERAL_CSTRING("content-disposition"),
3402  contentDisposition);
3403  if (NS_SUCCEEDED(result)) {
3404  mimeFilename = GetContentDispositionFilename(contentDisposition);
3405  }
3406  }
3407 
3408  /* Check if the destination is a directory. If the destination */
3409  /* does not exist, assume it's a file about to be created. */
3410  result = mpDstFile->IsDirectory(&bIsDirectory);
3411  if (result == NS_ERROR_FILE_NOT_FOUND || result == NS_ERROR_FILE_TARGET_DOES_NOT_EXIST)
3412  {
3413  bIsDirectory = PR_FALSE;
3414  result = NS_OK;
3415  }
3416  NS_ENSURE_SUCCESS(result, result);
3417 
3418  if (bIsDirectory)
3419  {
3420  // the destination is a directory; make a complete filename.
3421  // Try the MIME file name first. If there isn't any, we'll fall back
3422  // to the source url filename.
3423 
3424  nsCString escFileName(mimeFilename);
3425  if (escFileName.IsEmpty()) {
3426  // make a filename based on the source URL
3427  nsCOMPtr<nsIURI> pFinalSrcURI;
3428 
3429  nsCOMPtr<nsIChannel> channel = do_QueryInterface(aRequest, &result);
3430  NS_ENSURE_SUCCESS(result, result);
3431 
3432  result = channel->GetURI(getter_AddRefs(pFinalSrcURI));
3433  NS_ENSURE_SUCCESS(result, result);
3434 
3435  /* convert the uri into a URL */
3436  nsCOMPtr<nsIURL> pFinalSrcURL = do_QueryInterface(pFinalSrcURI, &result);
3437  NS_ENSURE_SUCCESS(result, result);
3438 
3439  result = pFinalSrcURL->GetFileName(escFileName);
3440  NS_ENSURE_SUCCESS(result, result);
3441  }
3442 
3443  /* Get the unescaped file name from the URL. */
3444  nsCOMPtr<nsINetUtil> netUtil;
3445  netUtil = do_GetService("@mozilla.org/network/util;1", &result);
3446  NS_ENSURE_SUCCESS(result, result);
3447  nsCString leafCName;
3448  result = netUtil->UnescapeString(escFileName,
3449  nsINetUtil::ESCAPE_URL_SKIP_CONTROL,
3450  leafCName);
3451  NS_ENSURE_SUCCESS(result, result);
3452 
3453  /* convert the leaf name to UTF 16 (since it can be invalid UTF8) */
3454  nsString leafName = NS_ConvertUTF8toUTF16(leafCName);
3455  if (leafName.IsEmpty()) {
3456  // not valid UTF8; use the escaped version instead :(
3457  leafName.Assign(NS_ConvertUTF8toUTF16(escFileName));
3458  if (leafName.IsEmpty()) {
3459  // still invalid; hard code something crappy
3460  leafName.AssignLiteral("unnamed");
3461  }
3462  }
3463 
3464  /* strip out characters not valid in file names */
3465  nsString illegalChars(NS_ConvertASCIItoUTF16(FILE_ILLEGAL_CHARACTERS));
3466  illegalChars.AppendLiteral(FILE_PATH_SEPARATOR);
3467  ReplaceChars(leafName, illegalChars, PRUnichar('_'));
3468 
3469  /* append to the path */
3470  result = mpDstFile->Append(leafName);
3471  NS_ENSURE_SUCCESS(result, result);
3472 
3473  /* ensure the filename is unique */
3474  result = sbDownloadDevice::MakeFileUnique(mpDstFile);
3475  NS_ENSURE_SUCCESS(result, result);
3476 
3477  bChangedDstFile = PR_TRUE;
3478  }
3479 
3480  // If the file name has no extension, use the one, if any, supplied
3481  // in the MIME header:
3482  result = mpDstFile->GetLeafName(fileName);
3483  NS_ENSURE_SUCCESS(result, result);
3484  if (fileName.RFindChar('.') == -1) {
3485  PRInt32 extension = mimeFilename.RFindChar('.');
3486  if (extension != -1) {
3487  fileName.Append(
3488  NS_ConvertUTF8toUTF16(Substring(mimeFilename, extension)));
3489  result = mpDstFile->SetLeafName(fileName);
3490  NS_ENSURE_SUCCESS(result, result);
3491 
3492  bChangedDstFile = PR_TRUE;
3493  }
3494  }
3495 
3496  if (bChangedDstFile) {
3497  /* Get the destination URI spec. */
3498  nsCOMPtr<nsIURI> pDstURI;
3499  result = mpLibraryUtils->GetFileContentURI(mpDstFile,
3500  getter_AddRefs(mpDstURI));
3501  NS_ENSURE_SUCCESS(result, result);
3502 
3503  nsCString dstCSpec;
3504  result = mpDstURI->GetSpec(dstCSpec);
3505  NS_ENSURE_SUCCESS(result, result);
3506 
3507  /* Set the destination property. */
3508  mDstURISpec = NS_ConvertUTF8toUTF16(dstCSpec);
3509  result = mpMediaItem->SetProperty
3510  (NS_LITERAL_STRING(SB_PROPERTY_DESTINATION),
3511  mDstURISpec);
3512  NS_ENSURE_SUCCESS(result, result);
3513  }
3514 
3515  /* Save the content URL. */
3516  if (NS_SUCCEEDED(result))
3517  result = mpMediaItem->GetContentSrc(getter_AddRefs(pSrcURI));
3518  if (NS_SUCCEEDED(result))
3519  result = pSrcURI->GetSpec(srcSpec);
3520 
3521  /* Update the download media item content source property. */
3522  if (NS_SUCCEEDED(result)) {
3523  result = mpMediaItem->SetContentSrc(mpDstURI);
3524  }
3525 
3526  /* Add the download media item to the destination library. */
3527  if (NS_SUCCEEDED(result))
3528  pDstMediaList = do_QueryInterface(mpDstLibrary, &result);
3529  if (NS_SUCCEEDED(result))
3530  result = pDstMediaList->Add(mpMediaItem);
3531 
3532  /* Move the temporary download file to the final location. */
3533  if (NS_SUCCEEDED(result))
3534  result = mpDstFile->GetParent(getter_AddRefs(pFileDir));
3535  if (NS_SUCCEEDED(result)) {
3536  nsRefPtr<sbDownloadSessionMoveHandler> moveHandler;
3537  moveHandler = new sbDownloadSessionMoveHandler(mpTmpFile,
3538  pFileDir,
3539  fileName,
3540  mpMediaItem);
3541  NS_ENSURE_TRUE(moveHandler, NS_ERROR_OUT_OF_MEMORY);
3542 
3543  result = mpDownloadDevice->mFileMoveThreadPool->Dispatch(moveHandler,
3544  nsIEventTarget::DISPATCH_NORMAL);
3545  }
3546 
3547  /* Update the web library with the local downloaded file URL. */
3548  if (NS_SUCCEEDED(result))
3549  {
3550  nsRefPtr<WebLibraryUpdater> pWebLibraryUpdater;
3551  nsCOMPtr<sbIMediaList> pWebMediaList;
3552 
3553  /* Get the web library media list. */
3554  if (NS_SUCCEEDED(result))
3555  {
3556  pWebMediaList = do_QueryInterface(mpDownloadDevice->mpWebLibrary,
3557  &result);
3558  }
3559 
3560  /* Update the web library. */
3561  if (NS_SUCCEEDED(result))
3562  {
3563  pWebLibraryUpdater = new WebLibraryUpdater(this);
3564  if (!pWebLibraryUpdater)
3565  result = NS_ERROR_OUT_OF_MEMORY;
3566  }
3567  if (NS_SUCCEEDED(result))
3568  {
3569  result = pWebMediaList->EnumerateItemsByProperty
3570  (NS_LITERAL_STRING(SB_PROPERTY_CONTENTURL),
3571  NS_ConvertUTF8toUTF16(srcSpec),
3572  pWebLibraryUpdater,
3574  }
3575 
3576  /* Don't propagate errors from here. */
3577  result = NS_OK;
3578  }
3579 
3580  return (result);
3581 }
3582 
3583 
3584 /*
3585  * UpdateDstLibraryMetadata
3586  *
3587  * This function updates the download media item metadata in the destination
3588  * library. If the metadata is already set in the media item, this function
3589  * does nothing.
3590  */
3591 
3592 nsresult sbDownloadSession::UpdateDstLibraryMetadata()
3593 {
3594  nsCOMPtr<sbIMediaList> pDstMediaList;
3595  nsCString dstSpec;
3596  nsRefPtr<LibraryMetadataUpdater>
3597  pLibraryMetadataUpdater;
3598  nsString durationStr;
3599  PRInt32 duration = 0;
3600  PRBool updateDstLibraryMetadata = PR_TRUE;
3601  nsresult result1;
3602  nsresult result = NS_OK;
3603 
3604  /* Check if the download media item has metadata. */
3605  result1 = mpMediaItem->GetProperty(NS_LITERAL_STRING(SB_PROPERTY_DURATION),
3606  durationStr);
3607  if (NS_SUCCEEDED(result1) && durationStr.IsEmpty())
3608  result = NS_ERROR_FAILURE;
3609 
3610  if (NS_SUCCEEDED(result1))
3611  duration = durationStr.ToInteger(&result1);
3612 
3613  if (NS_SUCCEEDED(result1) && (duration > 0))
3614  updateDstLibraryMetadata = PR_FALSE;
3615 
3616  /* Update the destination library metadata if needed. */
3617  if (updateDstLibraryMetadata)
3618  {
3619  /* Get the destination URI spec. */
3620  result = mpDstURI->GetSpec(dstSpec);
3621 
3622  /* Create a library metadata updater. */
3623  if (NS_SUCCEEDED(result))
3624  {
3625  pLibraryMetadataUpdater = new LibraryMetadataUpdater();
3626  if (!pLibraryMetadataUpdater)
3627  result = NS_ERROR_OUT_OF_MEMORY;
3628  }
3629 
3630  /* Update the download media item metadata in the library. */
3631  if (NS_SUCCEEDED(result))
3632  pDstMediaList = do_QueryInterface(mpDstLibrary, &result);
3633  if (NS_SUCCEEDED(result))
3634  {
3635  result = pDstMediaList->EnumerateItemsByProperty
3636  (NS_LITERAL_STRING(SB_PROPERTY_CONTENTURL),
3637  NS_ConvertUTF8toUTF16(dstSpec),
3638  pLibraryMetadataUpdater,
3640  }
3641  }
3642 
3643  return (result);
3644 }
3645 
3646 
3647 /*
3648  * UpdateProgress
3649  *
3650  * --> aProgress Current progress value.
3651  * --> aProgressMax Maximum progress value.
3652  *
3653  * This function updates the download session progress status with the
3654  * progress values specified by aProgress and aProgressMax.
3655  */
3656 
3657 void sbDownloadSession::UpdateProgress(
3658  PRUint64 aProgress,
3659  PRUint64 aProgressMax)
3660 {
3661  /* Correct progress values in case we are resuming a download */
3662  aProgress += mInitialProgressBytes;
3663  aProgressMax += mInitialProgressBytes;
3664 
3665  /* Update the download button property. */
3666  sbAutoDownloadButtonPropertyValue property(mpMediaItem, mpStatusTarget);
3667  property.value->SetMode(sbDownloadButtonPropertyValue::eDownloading);
3668  property.value->SetCurrent(aProgress);
3669  property.value->SetTotal(aProgressMax);
3670 
3671  /* Update the download details. */
3672  UpdateDownloadDetails(aProgress, aProgressMax);
3673 }
3674 
3675 
3676 /*
3677  * UpdateDownloadDetails
3678  *
3679  * --> aProgress Current progress value.
3680  * --> aProgressMax Maximum progress value.
3681  *
3682  * This function updates the download details with the download session
3683  * progress values specified by aProgress and aProgressMax.
3684  */
3685 
3686 void sbDownloadSession::UpdateDownloadDetails(
3687  PRUint64 aProgress,
3688  PRUint64 aProgressMax)
3689 {
3690  nsAutoString progressStr;
3691  PRTime now;
3692  PRUint64 elapsedUSecs;
3693  PRUint32 remainingSecs;
3694  nsresult rv;
3695 
3696  /* Determine the elapsed time since the last update. */
3697  now = PR_Now();
3698  elapsedUSecs = now - mLastUpdate;
3699 
3700  /* Limit the update rate. */
3701  if ( mLastUpdate
3702  && (elapsedUSecs < ( SB_DOWNLOAD_PROGRESS_UPDATE_PERIOD_MS
3703  * PR_USEC_PER_MSEC)))
3704  {
3705  return;
3706  }
3707 
3708  /* Update the download rate. */
3709  UpdateDownloadRate(aProgress, elapsedUSecs);
3710 
3711  /* Compute the remaining number of seconds to download. */
3712  if (mRate)
3713  {
3714  remainingSecs = (PRUint32)
3715  ((double(aProgressMax) - double(aProgress)) / mRate + 0.5);
3716  }
3717  else
3718  {
3719  remainingSecs = 0;
3720  }
3721 
3722  /* Produce the formatted progress string. */
3723  rv = FormatProgress(progressStr,
3724  aProgress,
3725  aProgressMax,
3726  mRate,
3727  remainingSecs);
3728  if (NS_FAILED(rv))
3729  progressStr.AssignLiteral("???");
3730 
3731  /* Update the download details property. Nothing to do on error. */
3732  mpMediaItem->SetProperty(NS_LITERAL_STRING(SB_PROPERTY_DOWNLOAD_DETAILS),
3733  progressStr);
3734  if (mpStatusTarget)
3735  {
3736  mpStatusTarget->SetProperty
3737  (NS_LITERAL_STRING(SB_PROPERTY_DOWNLOAD_DETAILS),
3738  progressStr);
3739  }
3740 
3741  /* Save update state. */
3742  mLastUpdate = now;
3743  mLastProgressBytes = aProgress;
3744  mLastProgressBytesMax = aProgressMax;
3745 }
3746 
3747 
3748 /*
3749  * UpdateDownloadRate
3750  *
3751  * --> aProgress Number of bytes downloaded.
3752  * --> aElapsedUSecs Number of microseconds elapsed since last
3753  * update.
3754  *
3755  * This function updates the download rate computed by the number of bytes
3756  * downloaded specified by aProgress and the elapsed time since the last update
3757  * specified by aElapsedUSecs.
3758  */
3759 
3760 void sbDownloadSession::UpdateDownloadRate(
3761  PRUint64 aProgress,
3762  PRUint64 aElapsedUSecs)
3763 {
3764  double elapsedSecs;
3765  PRUint64 dlByteCount;
3766  double rate;
3767 
3768  /* Get the number of elapsed seconds. */
3769  /* Do nothing if no time has elapsed. */
3770  elapsedSecs = double(aElapsedUSecs) / PR_USEC_PER_SEC;
3771  if (elapsedSecs <= 0.0)
3772  return;
3773 
3774  /* Compute the current download rate. */
3775  dlByteCount = aProgress - mLastProgressBytes;
3776  rate = double(dlByteCount) / elapsedSecs;
3777 
3778  /* Calculate smoothed download rate average. */
3779  if (mLastProgressBytes)
3780  mRate = 0.9 * mRate + 0.1 * rate;
3781  else
3782  mRate = rate;
3783 }
3784 
3785 
3786 /*
3787  * FormatProgress
3788  *
3789  * <-- aProgressStr Formatted progress string.
3790  * --> aProgress Current progress value.
3791  * --> aProgressMax Maximum progress value.
3792  * --> aRate Download rate.
3793  * --> aRemSeconds Reaminig download seconds.
3794  *
3795  * This function returns in aProgressStr a formatted string containing the
3796  * progress values specified by aProgress, aProgressMax, aRate, and aRemSeconds.
3797  */
3798 
3799 nsresult sbDownloadSession::FormatProgress(
3800  nsString &aProgressStr,
3801  PRUint64 aProgress,
3802  PRUint64 aProgressMax,
3803  double aRate,
3804  PRUint32 aRemSeconds)
3805 {
3806  nsAutoString byteProgressStr;
3807  nsAutoString rateStr;
3808  nsAutoString timeStr;
3809  const PRUnichar *stringList[3];
3810  nsresult rv;
3811 
3812  /* Format byte progress. */
3813  rv = FormatByteProgress(byteProgressStr, aProgress, aProgressMax);
3814  NS_ENSURE_SUCCESS(rv, rv);
3815  stringList[0] = byteProgressStr.get();
3816 
3817  /* Format download rate. */
3818  rv = FormatRate(rateStr, aRate);
3819  NS_ENSURE_SUCCESS(rv, rv);
3820  stringList[1] = rateStr.get();
3821 
3822  /* Format remaining download time. */
3823  rv = FormatTime(timeStr, aRemSeconds);
3824  NS_ENSURE_SUCCESS(rv, rv);
3825  stringList[2] = timeStr.get();
3826 
3827  /* Format download progress. */
3828  rv = mpStringBundle->FormatStringFromName
3829  (NS_LITERAL_STRING("device.download.statusFormat").get(),
3830  stringList,
3831  3,
3832  getter_Copies(aProgressStr));
3833  NS_ENSURE_SUCCESS(rv, rv);
3834 
3835  return (NS_OK);
3836 }
3837 
3838 
3839 /*
3840  * FormatRate
3841  *
3842  * <-- aRateStr Formatted rate string.
3843  * --> aRate Download rate.
3844  *
3845  * This function returns in aRateStr a formatted string containing the
3846  * download rate specified by aRate.
3847  */
3848 
3849 nsresult sbDownloadSession::FormatRate(
3850  nsString &aRateStr,
3851  double aRate)
3852 {
3853  char rateStr[32];
3854 
3855  /* Format the download rate. */
3856  PR_snprintf(rateStr, sizeof(rateStr), "%.1f", aRate / 1024.0 + 0.05);
3857  aRateStr.AssignLiteral(rateStr);
3858 
3859  return (NS_OK);
3860 }
3861 
3862 
3863 /*
3864  * FormatByteProgress
3865  *
3866  * <-- aBytesProgressStr Formatted byte progress string.
3867  * --> aBytes Number of bytes downloaded.
3868  * --> aBytesMax Maximum number of download bytes.
3869  *
3870  * This function returns in aBytesProgressStr a formatted string containing
3871  * the download byte progress values specified by aBytes and aBytesMax.
3872  */
3873 
3874 nsresult sbDownloadSession::FormatByteProgress(
3875  nsString &aByteProgressStr,
3876  PRUint64 aBytes,
3877  PRUint64 aBytesMax)
3878 {
3879  double bytesKB,
3880  bytesMB;
3881  double bytesMaxKB,
3882  bytesMaxMB;
3883  double bytesVal;
3884  double bytesMaxVal;
3885  char bytesValStr[32];
3886  char bytesMaxValStr[32];
3887  nsAutoString bytesValNSStr;
3888  nsAutoString bytesMaxValNSStr;
3889  nsAutoString stringName;
3890  const PRUnichar *stringList[2];
3891  nsresult rv;
3892 
3893  /* Get the number of download kilobytes and megabytes. */
3894  bytesKB = double(aBytes) / 1024.0;
3895  bytesMB = bytesKB / 1024.0;
3896 
3897  /* Get the number of max download kilobytes and megabytes. */
3898  bytesMaxKB = double(aBytesMax) / 1024.0;
3899  bytesMaxMB = bytesMaxKB / 1024.0;
3900 
3901  /* Determine format of byte progress status. */
3902  if (bytesMB >= 1.0)
3903  {
3904  stringName.AssignLiteral("device.download.statusFormatMBMB");
3905  bytesVal = bytesMB;
3906  bytesMaxVal = bytesMaxMB;
3907  }
3908  else if (bytesMaxMB >= 1.0)
3909  {
3910  stringName.AssignLiteral("device.download.statusFormatKBMB");
3911  bytesVal = bytesKB;
3912  bytesMaxVal = bytesMaxMB;
3913  }
3914  else
3915  {
3916  stringName.AssignLiteral("device.download.statusFormatKBKB");
3917  bytesVal = bytesKB;
3918  bytesMaxVal = bytesMaxKB;
3919  }
3920 
3921  /* Format the download bytes status. */
3922  PR_snprintf(bytesValStr, sizeof(bytesValStr), "%.1f", bytesVal);
3923  bytesValNSStr.AssignLiteral(bytesValStr);
3924  stringList[0] = bytesValNSStr.get();
3925 
3926  /* Format the max download bytes status. */
3927  PR_snprintf(bytesMaxValStr, sizeof(bytesMaxValStr), "%.1f", bytesMaxVal);
3928  bytesMaxValNSStr.AssignLiteral(bytesMaxValStr);
3929  stringList[1] = bytesMaxValNSStr.get();
3930 
3931  /* Format the download progress status. */
3932  rv = mpStringBundle->FormatStringFromName(stringName.get(),
3933  stringList,
3934  2,
3935  getter_Copies(aByteProgressStr));
3936  NS_ENSURE_SUCCESS(rv, rv);
3937 
3938  return (NS_OK);
3939 }
3940 
3941 
3942 /*
3943  * FormatTime
3944  *
3945  * <-- aTimeStr Formatted time string.
3946  * --> aSeconds Number of seconds.
3947  *
3948  * This function returns in aTimeStr a formatted time string for the number of
3949  * seconds specified by aSeconds.
3950  */
3951 
3952 nsresult sbDownloadSession::FormatTime(
3953  nsString &aTimeStr,
3954  PRUint32 aSeconds)
3955 {
3956  nsAutoString stringName;
3957  PRUint32 hours,
3958  minutes,
3959  seconds;
3960  nsAutoString hoursStr,
3961  minutesStr,
3962  secondsStr;
3963  const PRUnichar *stringList[3];
3964  nsresult rv;
3965 
3966  /* Initialize the time seconds field. */
3967  seconds = aSeconds;
3968 
3969  /* Extract the hours field. */
3970  hours = aSeconds / 3600;
3971  hoursStr.AppendInt(hours);
3972  seconds -= hours * 3600;
3973 
3974  /* Extract the minutes field. */
3975  minutes = aSeconds / 60;
3976  if (hours && minutes < 10)
3977  minutesStr.AssignLiteral("0");
3978  minutesStr.AppendInt(minutes);
3979  seconds -= minutes * 60;
3980 
3981  /* Extract the seconds field. */
3982  if (seconds < 10)
3983  secondsStr.AssignLiteral("0");
3984  secondsStr.AppendInt(seconds);
3985 
3986  /* Determine the time format. */
3987  if (hours)
3988  {
3989  stringName.AssignLiteral("device.download.longTimeFormat");
3990  stringList[0] = hoursStr.get();
3991  stringList[1] = minutesStr.get();
3992  stringList[2] = secondsStr.get();
3993  }
3994  else
3995  {
3996  stringName.AssignLiteral("device.download.shortTimeFormat");
3997  stringList[0] = minutesStr.get();
3998  stringList[1] = secondsStr.get();
3999  }
4000 
4001  /* Format the time string. */
4002  rv = mpStringBundle->FormatStringFromName(stringName.get(),
4003  stringList,
4004  3,
4005  getter_Copies(aTimeStr));
4006  NS_ENSURE_SUCCESS(rv, rv);
4007 
4008  return (NS_OK);
4009 }
4010 
4011 
4015 nsresult sbDownloadSession::StartTimers()
4016 {
4017  mProgressTimer->Cancel();
4018  mProgressTimer->InitWithCallback(this, SB_DOWNLOAD_PROGRESS_TIMER_MS,
4019  nsITimer::TYPE_REPEATING_SLACK);
4020 
4021  mIdleTimer->Cancel();
4022  mIdleTimer->InitWithCallback(this, SB_DOWNLOAD_IDLE_TIMEOUT_MS,
4023  nsITimer::TYPE_ONE_SHOT);
4024 
4025  return NS_OK;
4026 }
4027 
4028 
4032 nsresult sbDownloadSession::StopTimers()
4033 {
4034  if(mProgressTimer) {
4035  mProgressTimer->Cancel();
4036  }
4037 
4038  if(mIdleTimer) {
4039  mIdleTimer->Cancel();
4040  }
4041 
4042  return NS_OK;
4043 }
4044 
4045 
4049 nsresult sbDownloadSession::ResetTimers()
4050 {
4051  mProgressTimer->Cancel();
4052  mProgressTimer->InitWithCallback(this, SB_DOWNLOAD_PROGRESS_TIMER_MS,
4053  nsITimer::TYPE_REPEATING_SLACK);
4054 
4055  mIdleTimer->Cancel();
4056  return mIdleTimer->InitWithCallback(this, SB_DOWNLOAD_IDLE_TIMEOUT_MS,
4057  nsITimer::TYPE_ONE_SHOT);
4058 
4059  return NS_OK;
4060 }
4061 
4062 
4063 /* *****************************************************************************
4064  *
4065  * Download device LibraryMetadataUpdater services.
4066  *
4067  * This class updates a library items' metadata by scanning a local,
4068  * downloaded file.
4069  *
4070  ******************************************************************************/
4071 
4072 /* LibraryMetadataUpdater nsISupports implementation. */
4073 NS_IMPL_ISUPPORTS1(sbDownloadSession::LibraryMetadataUpdater,
4075 
4076 
4077 
4085 NS_IMETHODIMP sbDownloadSession::LibraryMetadataUpdater::OnEnumerationBegin(
4086  sbIMediaList *aMediaList,
4087  PRUint16 *_retval)
4088 {
4089  nsresult result = NS_OK;
4090 
4091  /* Validate arguments. */
4092  NS_ENSURE_ARG_POINTER(_retval);
4093 
4094  /* Create an array to contain the media items to scan. */
4095  mpMediaItemArray = do_CreateInstance("@songbirdnest.com/moz/xpcom/threadsafe-array;1", &result);
4096 
4097  /* Return results. */
4098  if (NS_SUCCEEDED(result))
4100  else
4102 
4103  return (result);
4104 }
4105 
4106 
4116 NS_IMETHODIMP sbDownloadSession::LibraryMetadataUpdater::OnEnumeratedItem(
4117  sbIMediaList *aMediaList,
4118  sbIMediaItem *aMediaItem,
4119  PRUint16 *_retval)
4120 {
4121  nsresult result = NS_OK;
4122 
4123  /* Validate arguments. */
4124  NS_ENSURE_ARG_POINTER(_retval);
4125 
4126  /* Put the media item into the scanning array. */
4127  result = mpMediaItemArray->AppendElement(aMediaItem, PR_FALSE);
4128 
4129  /* Return results. */
4131 
4132  return (NS_OK);
4133 }
4134 
4135 
4143 NS_IMETHODIMP sbDownloadSession::LibraryMetadataUpdater::OnEnumerationEnd(
4144  sbIMediaList *aMediaList,
4145  nsresult aStatusCode)
4146 {
4147  nsCOMPtr<sbIFileMetadataService>
4148  pMetadataService;
4149  nsCOMPtr<sbIJobProgress> pMetadataJob;
4150  nsresult result = NS_OK;
4151 
4152  /* Start a metadata scanning job. */
4153  pMetadataService = do_GetService
4154  ("@songbirdnest.com/Songbird/FileMetadataService;1",
4155  &result);
4156  if (NS_SUCCEEDED(result))
4157  {
4158  result = pMetadataService->Read(mpMediaItemArray,
4159  getter_AddRefs(pMetadataJob));
4160  }
4161 
4162  return (result);
4163 }
4164 
4165 
4166 /* *****************************************************************************
4167  *
4168  * Download session WebLibraryUpdater services.
4169  *
4170  * This class updates web library items' content source properties with
4171  * downloaded local URLs.
4172  *
4173  ******************************************************************************/
4174 
4175 /* WebLibraryUpdater nsISupports implementation. */
4176 NS_IMPL_ISUPPORTS1(sbDownloadSession::WebLibraryUpdater,
4178 
4179 
4180 
4188 NS_IMETHODIMP sbDownloadSession::WebLibraryUpdater::OnEnumerationBegin(
4189  sbIMediaList *aMediaList,
4190  PRUint16 *_retval)
4191 {
4192  nsresult result = NS_OK;
4193 
4194  /* Return results. */
4196 
4197  return (result);
4198 }
4199 
4200 
4210 NS_IMETHODIMP sbDownloadSession::WebLibraryUpdater::OnEnumeratedItem(
4211  sbIMediaList *aMediaList,
4212  sbIMediaItem *aMediaItem,
4213  PRUint16 *_retval)
4214 {
4215  nsresult result = NS_OK;
4216 
4217  /* Update the content source. */
4218  aMediaItem->SetContentSrc(mpDownloadSession->mpDstURI);
4219 
4220  /* Return results. */
4222 
4223  return (result);
4224 }
4225 
4226 
4234 NS_IMETHODIMP sbDownloadSession::WebLibraryUpdater::OnEnumerationEnd(
4235  sbIMediaList *aMediaList,
4236  nsresult aStatusCode)
4237 {
4238  nsresult result = NS_OK;
4239 
4240  return (result);
4241 }
4242 
4244  sbIMediaItem* aStatusTarget,
4245  PRBool aReadOnly) :
4246  value(nsnull),
4247  mMediaItem(aMediaItem),
4248  mStatusTarget(aStatusTarget),
4249  mReadOnly(aReadOnly)
4250 {
4251  NS_ASSERTION(mMediaItem, "mMediaItem is null");
4252 
4253  MOZ_COUNT_CTOR(sbAutoDownloadButtonPropertyValue);
4254 
4255  nsString propertyValue;
4256  mMediaItem->GetProperty(NS_LITERAL_STRING(SB_PROPERTY_DOWNLOADBUTTON),
4257  propertyValue);
4258  value = new sbDownloadButtonPropertyValue(propertyValue);
4259  NS_ASSERTION(value, "value is null");
4260 }
4261 
4263 {
4264  if (!mReadOnly && value) {
4265  nsString propertyValue;
4266  value->GetValue(propertyValue);
4267 
4268  nsresult rv;
4269  rv = mMediaItem->SetProperty(NS_LITERAL_STRING(SB_PROPERTY_DOWNLOADBUTTON),
4270  propertyValue);
4271  NS_ASSERTION(NS_SUCCEEDED(rv), "SetProperty failed");
4272 
4273  if (mStatusTarget) {
4274  rv = mStatusTarget->SetProperty(NS_LITERAL_STRING(SB_PROPERTY_DOWNLOADBUTTON),
4275  propertyValue);
4276  NS_ASSERTION(NS_SUCCEEDED(rv), "SetProperty failed");
4277  }
4278  }
4279  MOZ_COUNT_DTOR(sbAutoDownloadButtonPropertyValue);
4280 }
4281 
4282 
4284  nsIRunnable)
4285 
4286 NS_IMETHODIMP sbDownloadSessionMoveHandler::Run() {
4287  nsresult rv = NS_ERROR_UNEXPECTED;
4288 
4289  rv = mSourceFile->MoveTo(mDestinationPath, mDestinationFileName);
4290  NS_ENSURE_SUCCESS(rv, rv);
4291 
4292  nsCOMPtr<sbIFileMetadataService> metadataService;
4293  nsCOMPtr<sbIJobProgress> metadataJob;
4294 
4295  /* Create an array to contain the media items to scan. */
4296  nsCOMPtr<nsIMutableArray> itemArray =
4297  do_CreateInstance("@songbirdnest.com/moz/xpcom/threadsafe-array;1", &rv);
4298 
4299  rv = itemArray->AppendElement(mDestinationItem, PR_FALSE);
4300  NS_ENSURE_SUCCESS(rv, rv);
4301 
4302  /* Start a metadata scanning job. */
4303  metadataService =
4304  do_GetService("@songbirdnest.com/Songbird/FileMetadataService;1", &rv);
4305  NS_ENSURE_SUCCESS(rv, rv);
4306 
4307  return metadataService->Read(itemArray,
4308  getter_AddRefs(metadataJob));
4309 }
nsresult RemoveItemFromTransferQueue(const nsAString &aDeviceIdentifier, sbIMediaItem *aMediaItem)
Remove an item from the internal transfer queue.
const unsigned long OP_UPLOAD
Device Operation Constants.
void ReplaceChars(nsAString &aOldString, const nsAString &aOldChars, const PRUnichar aNewChar)
Definition: DeviceBase.cpp:95
nsCOMPtr< sbIMediaItem > mpMediaItem
NS_IMPL_ISUPPORTS4(sbDownloadDevice, nsIObserver, sbIDeviceBase, sbIDownloadDevice, sbIMediaListListener) sbDownloadDevice
#define SB_DOWNLOAD_DEVICE_CATEGORY
return NS_OK
friend class sbDownloadSession
nsresult SetDeviceState(const nsAString &aDeviceIdentifier, PRUint32 aDeviceState)
#define SB_PROPERTY_ORIGINURL
sbAutoDownloadButtonPropertyValue(sbIMediaItem *aMediaItem, sbIMediaItem *aStatusTarget=nsnull, PRBool aReadOnly=PR_FALSE)
_dialogDatepicker pos
static PRStatus decode2to1(const unsigned char *src, unsigned char *dest)
Interface used to enumerate the items in a media list.
const NS_PREFSERVICE_CONTRACTID
nsresult GetDeviceState(const nsAString &aDeviceIdentifier, PRUint32 *aDeviceState)
#define SB_DOWNLOAD_DEVICE_ID
NS_IMPL_THREADSAFE_ISUPPORTS2(sbDownloadSession, nsIWebProgressListener, nsITimerCallback) NS_IMETHODIMP sbDownloadSession
#define SB_DOWNLOAD_CUSTOM_TYPE
#define SB_STRING_BUNDLE_CHROME_URL
const NS_ERROR_ABORT
nsresult GetLibraryForDevice(const nsAString &aDeviceIdentifier, sbILibrary **aDeviceLibrary)
Get the library instance for a device.
A brief description of the contents of this interface.
#define SB_PROPERTY_ISSORTABLE
[SOON TO BE DEPRECATED AFTER 0.3] The callback class for sbIDeviceBase
static PRStatus decode(const unsigned char *src, PRUint32 srclen, unsigned char *dest)
nsresult CreateTransferQueue(const nsAString &aDeviceIdentifier)
Create an internal transfer queue for a device instance.
#define SB_DOWNLOAD_PROGRESS_TIMER_MS
Interface used to listen to changes to a media list.
#define SB_TMP_DIR
#define SB_DOWNLOAD_COL_SPEC
#define SB_DOWNLOAD_LIST_NAME
_hideDatepicker duration
[SOON TO BE DEPRECATED AFTER 0.3] Base interface for all supported devices.
#define SB_DOWNLOAD_TMP_DIR
nsresult InitDeviceState(const nsAString &aDeviceIdentifier)
readonly attribute unsigned long completedItemCount
The number of completed download items.
static PRStatus decode4to3(const unsigned char *src, unsigned char *dest)
nsresult RemoveTransferQueue(const nsAString &aDeviceIdentifier)
Remove an internal transfer queue for a device instance.
virtual ~sbDownloadDevice()
const PRUint32 STATE_IDLE
#define SB_PROPERTY_DESTINATION
const unsigned short ENUMERATIONTYPE_SNAPSHOT
This flag means that the list being enumerated is a copy that may become out of date.
nsresult AddCallback(sbIDeviceBaseCallback *aCallback)
Definition: DeviceBase.cpp:706
const unsigned long LISTENER_FLAGS_AFTERITEMREMOVED
#define SB_PROPERTY_DURATION
#define LOG(args)
var libraryManager
nsresult SetListenerForDeviceLibrary(const nsAString &aDeviceIdentifier, sbIMediaListListener *aMediaListListener)
#define SB_PROPERTY_DEFAULTCOLUMNSPEC
nsresult AddItemToTransferQueue(const nsAString &aDeviceIdentifier, sbIMediaItem *aMediaItem)
Add an item to the internal transfer queue.
NS_IMPL_THREADSAFE_ISUPPORTS1(sbDownloadSessionMoveHandler, nsIRunnable) NS_IMETHODIMP sbDownloadSessionMoveHandler
nsresult GetNextItemFromTransferQueue(const nsAString &aDeviceIdentifier, sbIMediaItem **aMediaItem)
Get the next item in the transfer queue.
static PRStatus decode3to2(const unsigned char *src, unsigned char *dest)
Media library abstraction.
Definition: sbILibrary.idl:82
const unsigned long LISTENER_FLAGS_LISTCLEARED
nsresult Init()
Initialize the base device class for use.
Definition: DeviceBase.cpp:694
static PRInt32 codetovalue(unsigned char c)
#define SB_PROPERTY_DOWNLOADBUTTON
_updateCookies aName
countRef value
Definition: FeedWriter.js:1423
const unsigned long LISTENER_FLAGS_ITEMADDED
#define SB_PREF_WEB_LIBRARY
#define SB_DOWNLOAD_IDLE_TIMEOUT_MS
nsresult RemoveCallback(sbIDeviceBaseCallback *aCallback)
Definition: DeviceBase.cpp:727
void DoTransferStartCallback(sbIMediaItem *aMediaItem)
Definition: DeviceBase.cpp:745
const PRUint32 STATE_DOWNLOADING
char * SB_Base64Decode(const char *src, PRUint32 srclen, char *dest)
NS_DECL_ISUPPORTS NS_DECL_NSIWEBPROGRESSLISTENER NS_DECL_NSITIMERCALLBACK sbDownloadSession(sbDownloadDevice *pDownloadDevice, sbIMediaItem *pMediaItem)
Songbird DownloadDevice Interface.
#define SB_PROPERTY_DOWNLOAD_STATUS_TARGET
const PRUint32 STATE_DOWNLOAD_PAUSED
function now()
Interface that defines a single item of media in the system.
virtual ~sbDownloadSession()
const NS_BINDING_ABORTED
#define SB_DOWNLOAD_PROGRESS_UPDATE_PERIOD_MS
nsAutoPtr< sbDownloadButtonPropertyValue > value
const nsISupportsString
nsCString GetContentDispositionFilename(const nsACString &contentDisposition)
void DoTransferCompleteCallback(sbIMediaItem *aMediaItem, PRInt32 aStatus)
Definition: DeviceBase.cpp:772
NS_IMPL_ISUPPORTS1(sbDownloadSession::LibraryMetadataUpdater, sbIMediaListEnumerationListener) NS_IMETHODIMP sbDownloadSession
Called when enumeration is about to begin.
#define SB_PROPERTY_CUSTOMTYPE
restoreWindow aState
#define SB_PROPERTY_CONTENTURL
An interface to carry around arrays of nsIProperty instances. Users of this interface should only QI ...
#define TRACE(args)
_getSelectedPageStyle s i
#define SB_PROPERTY_DOWNLOAD_MEDIALIST_GUID
nsITimerCallback
#define SB_PROPERTY_DOWNLOAD_DETAILS
#define SB_PREF_DOWNLOAD_MEDIALIST
nsCOMPtr< sbIMediaItem > mDestinationItem
Songbird DownloadDevice Component Definition.
_updateTextAndScrollDataForFrame aData
converter charset