sbMediaFileManager.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 // Songbird includes
28 #include "sbMediaFileManager.h"
29 #include <sbILibraryUtils.h>
30 #include <sbIMediaItem.h>
31 #include <sbIPropertyInfo.h>
32 #include <sbLibraryUtils.h>
33 #include <sbPropertiesCID.h>
35 #include <sbStandardProperties.h>
36 #include <sbStringBundle.h>
37 #include <sbStringUtils.h>
38 #include <sbURIUtils.h>
39 
40 // Mozilla includes
41 #include <nsCRT.h>
42 #include <nsDirectoryServiceDefs.h>
43 #include <nsStringAPI.h>
44 #include <nsIFile.h>
45 #include <nsIFileURL.h>
46 #include <nsIPrefService.h>
47 #include <nsIPropertyBag2.h>
48 #include <nsIStringBundle.h>
49 #include <nsUnicharUtils.h>
50 
51 #define PERMISSIONS_FILE 0644
52 
53 #ifndef MFM_MUSIC_FOLDER_KEY
54 # if defined(XP_MACOSX)
55 # define MFM_MUSIC_FOLDER_KEY NS_OSX_MUSIC_DOCUMENTS_DIR
56 # elif defined (XP_UNIX)
57 # define MFM_MUSIC_FOLDER_KEY NS_UNIX_XDG_MUSIC_DIR
58 # else
59 # define MFM_MUSIC_FOLDER_KEY "Music"
60 # endif
61 #endif // #ifndef MFM_MUSIC_FOLDER_KEY
62 
63 #ifndef MFM_VIDEO_FOLDER_KEY
64 # if defined(XP_MACOSX)
65 # define MFM_VIDEO_FOLDER_KEY NS_OSX_MOVIE_DOCUMENTS_DIR
66 # elif defined (XP_UNIX)
67 # define MFM_VIDEO_FOLDER_KEY NS_UNIX_XDG_VIDEOS_DIR
68 # else
69 # define MFM_VIDEO_FOLDER_KEY "Video"
70 # endif
71 #endif // #ifndef MFM_VIDEO_FOLDER_KEY
72 
73 #define _MFM_SEPARATOR ":"
74 
75 
77 
84 #include "prlog.h"
85 #ifdef PR_LOGGING
86 static PRLogModuleInfo* gMediaFileManagerLog = nsnull;
87 #define TRACE(args) PR_LOG(gMediaFileManagerLog, PR_LOG_DEBUG, args)
88 #define LOG(args) PR_LOG(gMediaFileManagerLog, PR_LOG_WARN, args)
89 #ifdef __GNUC__
90 #define __FUNCTION__ __PRETTY_FUNCTION__
91 #endif /* __GNUC__ */
92 #else
93 #define TRACE(args) /* nothing */
94 #define LOG(args) /* nothing */
95 #endif /* PR_LOGGING */
96 
101  : mInitialized(PR_FALSE)
102 {
103 #ifdef PR_LOGGING
104  if (!gMediaFileManagerLog) {
105  gMediaFileManagerLog = PR_NewLogModule("sbMediaFileManager");
106  }
107 #endif
108  TRACE(("%s", __FUNCTION__));
109 }
110 
115 {
116  TRACE(("%s", __FUNCTION__));
117 }
118 
122 NS_IMETHODIMP sbMediaFileManager::Init(nsIPropertyBag2 *aProperties)
123 {
124  TRACE(("%s", __FUNCTION__));
125 
126  NS_NAMED_LITERAL_STRING(KEY_FILE_FORMAT, "file-format");
127 
128  nsresult rv;
129 
130  PRBool hasKey;
131  nsCOMPtr<nsIPropertyBag2> properties = aProperties;
132  if (!properties) {
133  properties = do_CreateInstance("@mozilla.org/hash-property-bag;1");
134  NS_ENSURE_TRUE(properties, NS_ERROR_OUT_OF_MEMORY);
135  }
136 
137  // Get the NetUtils
138  mNetUtil = do_GetService("@mozilla.org/network/util;1", &rv);
139  NS_ENSURE_SUCCESS(rv, rv);
140 
141  // Get the property manager for the property names
142  mPropertyManager = do_GetService(SB_PROPERTYMANAGER_CONTRACTID, &rv);
143  NS_ENSURE_SUCCESS(rv, rv);
144 
145  // Get the pref branch
146  nsCOMPtr<nsIPrefService> prefRoot =
147  do_GetService("@mozilla.org/preferences-service;1", &rv);
148  NS_ENSURE_SUCCESS (rv, rv);
149 
150  // Get the branch we are interested in
151  nsCOMPtr<nsIThread> mainThread;
152  rv = NS_GetMainThread(getter_AddRefs(mainThread));
153  NS_ENSURE_SUCCESS(rv, rv);
154  nsCOMPtr<nsIPrefBranch> prefBranch;
155  rv = prefRoot->GetBranch(PREF_MFM_ROOT, getter_AddRefs(prefBranch));
156  NS_ENSURE_SUCCESS(rv, rv);
157  rv = do_GetProxyForObject(mainThread,
158  prefBranch.get(),
159  NS_PROXY_SYNC | NS_PROXY_ALWAYS,
160  getter_AddRefs(mPrefBranch));
161  NS_ENSURE_SUCCESS(rv, rv);
162 
163  // Get the Track File Format
164  nsCString fileFormatString;
165  rv = properties->HasKey(KEY_FILE_FORMAT, &hasKey);
166  NS_ENSURE_SUCCESS(rv, rv);
167  if (hasKey) {
168  rv = properties->GetPropertyAsACString(KEY_FILE_FORMAT, fileFormatString);
169  NS_ENSURE_SUCCESS(rv, rv);
170  } else {
171  rv = mPrefBranch->GetCharPref(PREF_MFM_FILEFORMAT,
172  getter_Copies(fileFormatString));
173  NS_ENSURE_SUCCESS(rv, rv);
174  }
175 
176  // Split the string on , character (odd entries are properties, even ones are
177  // separators) and save for later.
178  nsString_Split(NS_ConvertUTF8toUTF16(fileFormatString),
179  NS_LITERAL_STRING(","),
180  mTrackNameTemplate);
181 
182  rv = InitFolderNameTemplates(properties);
183  NS_ENSURE_SUCCESS(rv, rv);
184 
185  rv = InitMediaFoldersMap(properties);
186  NS_ENSURE_SUCCESS(rv, rv);
187 
188  mInitialized = PR_TRUE;
189 
190  return NS_OK;
191 }
192 
200 NS_IMETHODIMP
201 sbMediaFileManager::OrganizeItem(sbIMediaItem *aMediaItem,
202  unsigned short aManageType,
203  nsIFile *aForceTargetFile,
204  PRBool *aRetVal)
205 {
206  TRACE(("%s", __FUNCTION__));
207  NS_ENSURE_ARG_POINTER(aMediaItem);
208  NS_ENSURE_ARG_POINTER(aRetVal);
209  NS_ENSURE_TRUE(mInitialized, NS_ERROR_NOT_INITIALIZED);
210 
211  // Assume things won't go well so that we have the right value in aRetVal
212  // if we except, regardless of any previous success
213  *aRetVal = PR_FALSE;
214 
215  if (aManageType == 0) {
216  // No action was requested, fail altogether
217  TRACE(("%s - no action requested (%04x)", __FUNCTION__, aManageType));
218  return NS_ERROR_INVALID_ARG;
219  }
220 
221  nsresult rv;
222 
223  // First thing is to check if the Managment folder exists, we need this because
224  // all operations require that folder, we only delete from that folder and we
225  // only copy to or move in that folder. We also need to make sure we have the
226  // management folder, if not get it from the preferences.
227 
228  // First see if we have a management folder for this media item
229  rv = CheckManagementFolder(aMediaItem);
230  NS_ENSURE_SUCCESS(rv, rv);
231 
232  // Now make sure that we're operating on a file
233 
234  // Get to the old file's path
235  nsCOMPtr<nsIURI> itemUri;
236  rv = aMediaItem->GetContentSrc(getter_AddRefs(itemUri));
237  NS_ENSURE_SUCCESS (rv, rv);
238 
239  // Get an nsIFileURL object from the nsIURI
240  nsCOMPtr<nsIFileURL> fileUrl(do_QueryInterface(itemUri, &rv));
241  if (NS_FAILED(rv) || !fileUrl) {
242  #if PR_LOGGING
243  nsCString spec;
244  rv = itemUri->GetSpec(spec);
245  if (NS_SUCCEEDED(rv)) {
246  TRACE(("%s: item %s is not a local file", __FUNCTION__, spec.get()));
247  }
248  #endif /* PR_LOGGING */
249  return NS_ERROR_INVALID_ARG;
250  }
251 
252  // Get the file object and check that it exists.
253  nsCOMPtr<nsIFile> itemFile;
254  rv = fileUrl->GetFile(getter_AddRefs(itemFile));
255  NS_ENSURE_SUCCESS(rv, rv);
256 
257  // Make sure the original file exists before doing any organizing.
258  PRBool exists;
259  rv = itemFile->Exists(&exists);
260  if (NS_FAILED(rv) || !exists) {
261  return NS_ERROR_FILE_NOT_FOUND;
262  }
263 
264  if (aManageType & sbIMediaFileManager::MANAGE_DELETE) {
265  rv = Delete(itemFile, aRetVal);
266  NS_ENSURE_SUCCESS(rv, rv);
267 
268  // MANAGE_DELETE does not make sense to use in conjunction with other flags,
269  // so if anymore action was requested, return false in aRetVal, but don't
270  // cause an exception.
271 
272  if (aManageType != sbIMediaFileManager::MANAGE_DELETE) {
273  LOG(("%s: Recieved a MANAGE_DELETE with other flags (%04x).",
274  __FUNCTION__,
275  aManageType));
276  *aRetVal = PR_FALSE;
277  }
278 
279  return NS_OK;
280  }
281 
282  if (!((aManageType & sbIMediaFileManager::MANAGE_MOVE) ||
283  (aManageType & sbIMediaFileManager::MANAGE_COPY) ||
284  (aManageType & sbIMediaFileManager::MANAGE_RENAME)))
285  {
286  LOG(("%s: nothing to manage", __FUNCTION__));
287  return NS_OK;
288  }
289 
290  // Get the target file
291  nsCOMPtr<nsIFile> newFile;
292 
293  if (aForceTargetFile) {
294  #if PR_LOGGING
295  nsString path;
296  rv = aForceTargetFile->GetPath(path);
297  if (NS_SUCCEEDED(rv)) {
298  TRACE(("%s: has forced file [%s]",
299  __FUNCTION__,
300  NS_ConvertUTF16toUTF8(path).get()));
301  } else {
302  TRACE(("%s: has forced file, unknown path (error %08x)", __FUNCTION__, rv));
303  }
304  #endif /* PR_LOGGING */
305  rv = aForceTargetFile->Clone(getter_AddRefs(newFile));
306  NS_ENSURE_SUCCESS(rv, rv);
307  } else {
308  rv = GetManagedPath(aMediaItem, aManageType, getter_AddRefs(newFile));
309  NS_ENSURE_SUCCESS(rv, rv);
310  if (rv == NS_SUCCESS_LOSS_OF_INSIGNIFICANT_DATA) {
311  // non-fatal failure, go to next item
312  LOG(("%s: failed to get managed path", __FUNCTION__));
313  return NS_OK;
314  }
315  #if PR_LOGGING
316  nsString path;
317  rv = newFile->GetPath(path);
318  if (NS_SUCCEEDED(rv)) {
319  TRACE(("%s: has a managed file [%s]",
320  __FUNCTION__,
321  NS_ConvertUTF16toUTF8(path).get()));
322  } else {
323  TRACE(("%s: has managed file, unknown path (error %08x)",
324  __FUNCTION__,
325  rv));
326  }
327  #endif /* PR_LOGGING */
328  }
329 
330  // Check if we are already organized;
331  PRBool isOrganized = PR_FALSE;
332  rv = newFile->Equals(itemFile, &isOrganized);
333  NS_ENSURE_SUCCESS(rv, rv);
334 
335  if (isOrganized) {
336  // Already managed so just return ok
337  TRACE(("%s: Already organized", __FUNCTION__));
338  *aRetVal = PR_TRUE;
339  return NS_OK;
340  }
341 
342  // Not managed so perform copy, move and/or rename
343  // NOTE: this will check that our destination is in the managed folder.
344  rv = CopyRename(aMediaItem, itemFile, newFile, aRetVal);
345  NS_ENSURE_SUCCESS(rv, rv);
346 
347  return NS_OK;
348 }
349 
356 /* nsIFile getManagedPath (in sbIMediaItem aItem, in unsigned short aManageType); */
357 NS_IMETHODIMP
358 sbMediaFileManager::GetManagedPath(sbIMediaItem *aItem,
359  PRUint16 aManageType,
360  nsIFile **_retval)
361 {
362  TRACE(("%s", __FUNCTION__));
363  NS_ENSURE_ARG_POINTER(aItem);
364  NS_ENSURE_ARG_POINTER(_retval);
365  NS_ENSURE_TRUE(mInitialized, NS_ERROR_NOT_INITIALIZED);
366 
367  if (aManageType == 0) {
368  // No action was requested, fail altogether
369  return NS_ERROR_INVALID_ARG;
370  }
371 
372  nsresult rv;
373 
374  // First see if we have a management folder for this media item
375  rv = CheckManagementFolder(aItem);
376  NS_ENSURE_SUCCESS(rv, rv);
377 
378  // Next make sure that we're operating on a file
379 
380  // Get to the old file's path
381  nsCOMPtr<nsIURI> itemUri;
382  rv = aItem->GetContentSrc(getter_AddRefs(itemUri));
383  NS_ENSURE_SUCCESS (rv, rv);
384 
385  // Get the file object
386  nsCOMPtr<nsIFile> oldItemFile;
387  // Get the file from the URI and delete if needed
388  nsCOMPtr<nsIFileURL> fileUrl(do_QueryInterface(itemUri, &rv));
389  if (NS_SUCCEEDED(rv)) {
390 
391  rv = fileUrl->GetFile(getter_AddRefs(oldItemFile));
392  NS_ENSURE_SUCCESS(rv, rv);
393 
394  if (aManageType & sbIMediaFileManager::MANAGE_DELETE) {
395  // file will be deleted; don't make a new path
396  oldItemFile.forget(_retval);
397  return NS_OK;
398  }
399  }
400  // If we're told to delete and this is not a file, that's bad
401  else if (aManageType & sbIMediaFileManager::MANAGE_DELETE) {
402  return NS_ERROR_INVALID_ARG;
403  }
404 
405  // Find the target file
406  nsCOMPtr<nsIFile> newItemFile;
407 
408  nsString filename;
409  nsString path;
410  PRBool success;
411 
412  // figure out the containing folder for the final path
413  if (aManageType & sbIMediaFileManager::MANAGE_MOVE) {
414  // Since we are organizing the folder path get the new one
415  rv = GetNewPath(aItem, path, &success);
416  NS_ENSURE_SUCCESS(rv, rv);
417  NS_ENSURE_TRUE(success, NS_SUCCESS_LOSS_OF_INSIGNIFICANT_DATA);
418  nsCOMPtr<nsILocalFile> localFile =
419  do_CreateInstance("@mozilla.org/file/local;1", &rv);
420  NS_ENSURE_SUCCESS(rv, rv);
421  rv = localFile->InitWithPath(path);
422  NS_ENSURE_SUCCESS(rv, rv);
423  newItemFile = do_QueryInterface(localFile, &rv);
424  NS_ENSURE_SUCCESS(rv, rv);
425  } else if (aManageType & sbIMediaFileManager::MANAGE_COPY) {
426  // Since we are only copying and not organizing the folder path just get
427  // the root of the managed folder.
428  nsCOMPtr<nsIFile> mediaFolder;
429  rv = GetMediaFolder(aItem, getter_AddRefs(mediaFolder));
430  NS_ENSURE_SUCCESS(rv, NS_SUCCESS_LOSS_OF_INSIGNIFICANT_DATA);
431  rv = mediaFolder->Clone(getter_AddRefs(newItemFile));
432  NS_ENSURE_SUCCESS(rv, NS_SUCCESS_LOSS_OF_INSIGNIFICANT_DATA);
433  } else {
434  // neither move nor copy is set, we need to abort
435  return NS_SUCCESS_LOSS_OF_INSIGNIFICANT_DATA;
436  }
437 
438  if (aManageType & sbIMediaFileManager::MANAGE_RENAME) {
439  rv = GetNewFilename(aItem, itemUri, filename, &success);
440  NS_ENSURE_SUCCESS(rv, rv);
441  // If there was an error trying to construct the new filename, abort now,
442  // but don't cause an exception
443  NS_ENSURE_TRUE(success, NS_SUCCESS_LOSS_OF_INSIGNIFICANT_DATA);
444  } else if (oldItemFile) {
445  // not renaming, use the old name
446  nsCOMPtr<sbILibraryUtils> libUtils =
447  do_GetService("@songbirdnest.com/Songbird/library/Manager;1", &rv);
448  NS_ENSURE_SUCCESS(rv, rv);
449  nsCOMPtr<nsIFile> canonicalOldFile;
450  rv = libUtils->GetCanonicalPath(oldItemFile,
451  getter_AddRefs(canonicalOldFile));
452  NS_ENSURE_SUCCESS(rv, rv);
453  rv = canonicalOldFile->GetLeafName(filename);
454  NS_ENSURE_SUCCESS(rv, rv);
455  } else {
456  // Use the item URL file name.
457  nsCAutoString cFileName;
458  nsCOMPtr<nsIURL> itemURL = do_MainThreadQueryInterface(itemUri, &rv);
459  NS_ENSURE_SUCCESS(rv, rv);
460  rv = itemURL->GetFileName(cFileName);
461  NS_ENSURE_SUCCESS(rv, rv);
462  filename = NS_ConvertUTF8toUTF16(cFileName);
463  }
464  rv = newItemFile->Append(filename);
465  NS_ENSURE_SUCCESS(rv, rv);
466 
467  newItemFile.forget(_retval);
468 
469  return NS_OK;
470 }
471 
472 nsresult
473 sbMediaFileManager::InitMediaFoldersMap(nsIPropertyBag2 * aProperties)
474 {
475  TRACE(("%s", __FUNCTION__));
476  nsresult rv;
477 
478  // There will be at most three media folders, one for audio, one
479  // for video, and one to rule them all:
480  mMediaFolders.Init(3);
481 
482  PRBool ok = PR_FALSE;
483 
484  // Check the property bag for a custom media folder. A custom
485  // folder, if provided, will override the default folders:
486  nsCOMPtr<nsIFile> customFolder;
487  if (aProperties) {
488  NS_NAMED_LITERAL_STRING(KEY_CUSTOM_MEDIA_FOLDER, "media-folder");
489  PRBool hasKey = PR_FALSE;
490  rv = aProperties->HasKey(KEY_CUSTOM_MEDIA_FOLDER, &hasKey);
491  NS_ENSURE_SUCCESS(rv, rv);
492  if (hasKey) {
493  rv = aProperties->GetPropertyAsInterface(KEY_CUSTOM_MEDIA_FOLDER,
494  NS_GET_IID(nsIFile),
495  getter_AddRefs(customFolder));
496  NS_ENSURE_SUCCESS(rv, rv);
497 
498  // Got a custom media folder. Set it as the default and only folder
499  // for all content types and return. The empty key designates the
500  // default folder to use if no other folder is defined for the desired
501  // content type:
502  ok = mMediaFolders.Put(EmptyString(), customFolder);
503  NS_ENSURE_TRUE(ok, NS_ERROR_OUT_OF_MEMORY);
504  }
505  }
506 
507  nsCOMPtr<nsIProperties> dirService =
508  do_GetService(NS_DIRECTORY_SERVICE_CONTRACTID, &rv);
509  NS_ENSURE_SUCCESS(rv, rv);
510 
511  // Use a custom music folder, if provided, or any generic custom folder
512  // that may have been provided above. Otherwise, use the designated
513  // music folder for the current platform:
514  nsCOMPtr<nsIFile> musicDir = customFolder;
515  if (aProperties) {
516  NS_NAMED_LITERAL_STRING(KEY_CUSTOM_MUSIC_FOLDER, "media-folder:audio");
517  PRBool hasKey = PR_FALSE;
518  rv = aProperties->HasKey(KEY_CUSTOM_MUSIC_FOLDER, &hasKey);
519  NS_ENSURE_SUCCESS(rv, rv);
520  if (hasKey) {
521  aProperties->GetPropertyAsInterface(KEY_CUSTOM_MUSIC_FOLDER,
522  NS_GET_IID(nsIFile),
523  getter_AddRefs(musicDir));
524  NS_ENSURE_SUCCESS(rv, rv);
525  }
526  }
527 
528  // Resort to the platform music folder only if no custom folder
529  // was supplied:
530  if (!musicDir) {
531  dirService->Get(MFM_MUSIC_FOLDER_KEY,
532  NS_GET_IID(nsIFile),
533  getter_AddRefs(musicDir));
534  }
535 
536  if (musicDir) {
537  // Got a music folder. Set it as the media folder for audio content:
538  ok = mMediaFolders.Put(NS_LITERAL_STRING("audio"), musicDir);
539  NS_ENSURE_TRUE(ok, NS_ERROR_OUT_OF_MEMORY);
540  }
541  else {
542  NS_WARNING("Could not get music folder");
543  }
544 
545  // Use a custom video folder, if provided, or any generic custom folder
546  // that may have been provided above. Otherwise, use the designated
547  // video folder for the current platform:
548  nsCOMPtr<nsIFile> videoDir = customFolder;
549  if (aProperties) {
550  NS_NAMED_LITERAL_STRING(KEY_CUSTOM_VIDEO_FOLDER, "media-folder:video");
551  PRBool hasKey = PR_FALSE;
552  rv = aProperties->HasKey(KEY_CUSTOM_VIDEO_FOLDER, &hasKey);
553  NS_ENSURE_SUCCESS(rv, rv);
554  if (hasKey) {
555  aProperties->GetPropertyAsInterface(KEY_CUSTOM_VIDEO_FOLDER,
556  NS_GET_IID(nsIFile),
557  getter_AddRefs(videoDir));
558  NS_ENSURE_SUCCESS(rv, rv);
559  }
560  }
561 
562  // Resort to the platform video folder only if no custom folder
563  // was supplied:
564  if (!videoDir) {
565  dirService->Get(MFM_VIDEO_FOLDER_KEY,
566  NS_GET_IID(nsIFile),
567  getter_AddRefs(videoDir));
568  }
569 
570  if (videoDir) {
571  // Got a video folder. Set it as the media folder for video content:
572  ok = mMediaFolders.Put(NS_LITERAL_STRING("video"), videoDir);
573  NS_ENSURE_TRUE(ok, NS_ERROR_OUT_OF_MEMORY);
574  }
575  else {
576  NS_WARNING("Could not get video folder");
577  }
578 
579  return NS_OK;
580 }
581 
582 nsresult
583 sbMediaFileManager::GetMediaFolder(sbIMediaItem * aMediaItem,
584  nsIFile ** aFolder)
585 {
586  TRACE(("%s", __FUNCTION__));
587  NS_ENSURE_ARG_POINTER(aMediaItem);
588  NS_ENSURE_ARG_POINTER(aFolder);
589 
590  nsresult rv;
591 
592  // Try to get a media folder appropriate for the content
593  // type of the media item:
594  nsAutoString contentType;
595  rv = aMediaItem->GetContentType(contentType);
596  NS_ENSURE_SUCCESS(rv, rv);
597 
598  PRBool defined = mMediaFolders.Get(contentType, aFolder);
599  if (!defined) {
600  // No content type-specific media folder. Try the default
601  // folder for all content types:
602  defined = mMediaFolders.Get(EmptyString(), aFolder);
603  NS_ENSURE_TRUE(defined, NS_ERROR_NOT_AVAILABLE);
604  }
605 
606  // Should have a folder now. Return it:
607  NS_ENSURE_TRUE(*aFolder, NS_ERROR_NOT_AVAILABLE);
608  (*aFolder)->AddRef();
609 
610  return NS_OK;
611 }
612 
613 namespace
614 {
615  // Context for locating which media folder, if any, a file is in
616  struct OnEachMediaFolder_t
617  {
618  nsIFile * mFile;
619  nsIFile ** mFolder;
620  };
621 
622  // Check whether the folder designated by aData contains the file
623  // given in the userArg context:
624  PLDHashOperator
625  OnEachMediaFolder(nsStringHashKey::KeyType aKey,
626  nsIFile * aData,
627  void* userArg)
628  {
629  TRACE(("%s", __FUNCTION__));
630 
631  // Stop iterating if the context is invalid. If any other
632  // argument is invalid, skip to the next iteration:
633  NS_ENSURE_TRUE(aData, PL_DHASH_NEXT);
634  NS_ENSURE_TRUE(userArg, PL_DHASH_STOP);
635 
636  nsresult rv;
637 
638  const OnEachMediaFolder_t * context =
639  static_cast<const OnEachMediaFolder_t *>(userArg);
640  NS_ENSURE_TRUE(context->mFile, PL_DHASH_STOP);
641  NS_ENSURE_TRUE(context->mFolder, PL_DHASH_STOP);
642 
643 #if DEBUG
644  // For viewing in the debugger:
645  nsAutoString contentType;
646  contentType = aKey;
647  nsAutoString mediaFolder;
648  aData->GetPath(mediaFolder);
649  nsAutoString itemPath;
650  context->mFile->GetPath(itemPath);
651 #endif
652 
653  PRBool found = PR_FALSE;
654  rv = aData->Contains(context->mFile, PR_TRUE, &found);
655  NS_ENSURE_SUCCESS(rv, PL_DHASH_NEXT);
656 
657  // Keep iterating until a media folder containing the file
658  // is found:
659  if (!found) {
660  return PL_DHASH_NEXT;
661  }
662 
663  // Found the containing folder. Return it:
664  NS_ADDREF(*context->mFolder = aData);
665  return PL_DHASH_STOP;
666  }
667 }
668 
669 nsresult
670 sbMediaFileManager::GetMediaFolder(nsIFile * aFile,
671  nsIFile ** aFolder)
672 {
673  TRACE(("%s", __FUNCTION__));
674 
675  // Search the media folders for one containing the given file:
676  OnEachMediaFolder_t context;
677  context.mFile = aFile;
678  context.mFolder = aFolder;
679  mMediaFolders.EnumerateRead(OnEachMediaFolder, &context);
680 
681  return NS_OK;
682 }
683 
684 nsresult
685 sbMediaFileManager::InitFolderNameTemplates(nsIPropertyBag2 * aProperties)
686 {
687  TRACE(("%s", __FUNCTION__));
688  nsresult rv;
689 
690  NameTemplate nameTemplate;
691  nsAutoString key;
692  PRBool ok = PR_FALSE;
693 
694  mFolderNameTemplates.Init();
695 
696  // Check the property bag for a custom media subfolder path template.
697  // A custom template, if provided, will override the default templates:
698  if (aProperties) {
699  NS_NAMED_LITERAL_STRING(KEY_CUSTOM_DIR_TEMPLATE, "dir-format");
700  PRBool hasKey = PR_FALSE;
701  rv = aProperties->HasKey(KEY_CUSTOM_DIR_TEMPLATE, &hasKey);
702  NS_ENSURE_SUCCESS(rv, rv);
703  if (hasKey) {
704  nsCString templateAsString;
705  rv = aProperties->GetPropertyAsACString(KEY_CUSTOM_DIR_TEMPLATE,
706  templateAsString);
707  NS_ENSURE_SUCCESS(rv, rv);
708 
709  // Got a custom template. Split the string on the comma delimiter,
710  // set the result as the default and only template for all media
711  // files, and return:
712  nsString_Split(NS_ConvertUTF8toUTF16(templateAsString),
713  NS_LITERAL_STRING(","),
714  nameTemplate);
715  ok = mFolderNameTemplates.Put(EmptyString(), nameTemplate);
716  NS_ENSURE_TRUE(ok, NS_ERROR_OUT_OF_MEMORY);
717  return NS_OK;
718  }
719  }
720 
721  // No custom media subfolder path template was provided. Set
722  // mFolderNameTemplates to built-in defaults. mFolderNameTemplates
723  // maps specific values of specific properties to the template that
724  // should be used when that value describes a file. For
725  // example, the key
726  //
727  // http://songbirdnest.com/data/1.0#genre:Zydeco
728  //
729  // could specify the template to use for items with a genre of "Zydeco".
730  // Not all properties are considered, though, when searching for an
731  // appropriate subfolder template for a media item. Indeed, only the
732  // properties actually used below,
733  //
734  // SB_PROPERTY_IMPORTTYPE, and
735  // SB_PROPERTY_CONTENTTYPE
736  //
737  // are considered, in that order (see GetFolderNameTemplate()).
738  //
739  // The empty key designates the default template to use if no other
740  // template is found.
741 
742  // Default to an empty template, which will situate a media file
743  // at the root of its media folder:
744  nameTemplate.Clear();
745  ok = mFolderNameTemplates.Put(EmptyString(), nameTemplate);
746  NS_ENSURE_TRUE(ok, NS_ERROR_OUT_OF_MEMORY);
747 
748  // Audio files default to Artist/Album/
749  nameTemplate.Clear();
750  NS_ENSURE_TRUE(
751  nameTemplate.AppendElement(NS_LITERAL_STRING(SB_PROPERTY_ARTISTNAME)),
752  NS_ERROR_OUT_OF_MEMORY);
753  NS_ENSURE_TRUE(
754  nameTemplate.AppendElement(NS_LITERAL_STRING(FILE_PATH_SEPARATOR)),
755  NS_ERROR_OUT_OF_MEMORY);
756  NS_ENSURE_TRUE(
757  nameTemplate.AppendElement(NS_LITERAL_STRING(SB_PROPERTY_ALBUMNAME)),
758  NS_ERROR_OUT_OF_MEMORY);
759  NS_ENSURE_TRUE(
760  nameTemplate.AppendElement(NS_LITERAL_STRING(FILE_PATH_SEPARATOR)),
761  NS_ERROR_OUT_OF_MEMORY);
762 
763  key.AssignLiteral(SB_PROPERTY_CONTENTTYPE _MFM_SEPARATOR "audio");
764  ok = mFolderNameTemplates.Put(key, nameTemplate);
765  NS_ENSURE_TRUE(ok, NS_ERROR_OUT_OF_MEMORY);
766 
767  // Recordings get their own subfolder:
769  nsAutoString recordingsFolder(strings.Get(STRING_MFM_RECORDINGS_FOLDER,
770  "Recordings"));
771  recordingsFolder.AppendLiteral(FILE_PATH_SEPARATOR);
772 
773  nameTemplate.Clear();
774  NS_ENSURE_TRUE(
775  nameTemplate.AppendElement(EmptyString()),
776  NS_ERROR_OUT_OF_MEMORY);
777  NS_ENSURE_TRUE(
778  nameTemplate.AppendElement(recordingsFolder),
779  NS_ERROR_OUT_OF_MEMORY);
780 
781  key.AssignLiteral(SB_PROPERTY_IMPORTTYPE
782  _MFM_SEPARATOR
784  ok = mFolderNameTemplates.Put(key, nameTemplate);
785  NS_ENSURE_TRUE(ok, NS_ERROR_OUT_OF_MEMORY);
786 
787  key.AssignLiteral(SB_PROPERTY_IMPORTTYPE
788  _MFM_SEPARATOR
790  ok = mFolderNameTemplates.Put(key, nameTemplate);
791  NS_ENSURE_TRUE(ok, NS_ERROR_OUT_OF_MEMORY);
792 
793  return NS_OK;
794 }
795 
796 nsresult
797 sbMediaFileManager::GetFolderNameTemplate(sbIMediaItem * aMediaItem,
798  NameTemplate & aNameTemplate)
799 {
800  TRACE(("%s", __FUNCTION__));
801  NS_ENSURE_ARG_POINTER(aMediaItem);
802 
803  nsresult rv;
804 
805  // Consider these properties in this order when searching for
806  // a folder name template for the given media item:
807  static const char * const ORGANIZING_PROPERTIES[] = {
810  };
811  for (size_t i = 0; i < NS_ARRAY_LENGTH(ORGANIZING_PROPERTIES); i++) {
812  // Construct a key string from the property name and the media item's
813  // value for that property. For example:
814  //
815  // http://songbirdnest.com/data/1.0#contentType:audio
816 
817  nsAutoString key;
818  nsAutoString suffix;
819  key.AssignLiteral(ORGANIZING_PROPERTIES[i]);
820  rv = aMediaItem->GetProperty(key, suffix);
821  NS_ENSURE_SUCCESS(rv, rv);
822 
823  key.AppendLiteral(_MFM_SEPARATOR);
824  key.Append(suffix);
825 
826  // Check for a folder template with this key:
827  if (mFolderNameTemplates.Get(key, &aNameTemplate)) {
828  // Got a specific template for this property value. Return:
829  return NS_OK;
830  }
831  }
832 
833  // No folder name template specific to the media item's properties
834  // was found. Check for a default template:
835  if (mFolderNameTemplates.Get(EmptyString(), &aNameTemplate)) {
836  return NS_OK;
837  }
838 
839  // No default template either. Return an empty template, which
840  // will situate the media file at the root of its media folder:
841  aNameTemplate.Clear();
842  return NS_OK;
843 }
844 
853 nsresult
855  nsIURI *aItemUri,
856  nsString &aFilename,
857  PRBool *aRetVal)
858 {
859  TRACE(("%s", __FUNCTION__));
860  NS_ENSURE_ARG_POINTER(aMediaItem);
861  NS_ENSURE_ARG_POINTER(aItemUri);
862  NS_ENSURE_ARG_POINTER(aRetVal);
863 
864  nsresult rv;
865 
866  // Assume things won't go well so that we have the right value in aRetVal
867  // if we except, regardless of any previous success
868  *aRetVal = PR_FALSE;
869 
870  // Start clean
871  aFilename.Truncate();
872 
873  nsCString extension;
874 
875  // Get the previous filename extension
876  nsCOMPtr<nsIURL> url(do_QueryInterface(aItemUri, &rv));
877  if (NS_FAILED(rv)) {
878  rv = sbGetFileExtensionFromURI(aItemUri, extension);
879  // Likely to fail on malformed URI's
880  if (NS_FAILED(rv)) {
881  return rv;
882  }
883  }
884  else {
885  rv = url->GetFileExtension(extension);
886  NS_ENSURE_SUCCESS(rv, rv);
887  }
888 
889  nsString fullExtension;
890  if (!extension.IsEmpty()) {
891  fullExtension.Insert(PRUnichar('.'), 0);
892  fullExtension.Append(NS_ConvertUTF8toUTF16(extension));
893  }
894  // Format the file name
895  rv = GetFormattedFileFolder(mTrackNameTemplate,
896  aMediaItem,
897  PR_FALSE,
898  PR_FALSE, // Trim the full filename
899  fullExtension,
900  aFilename);
901  NS_ENSURE_SUCCESS(rv, rv);
902 
903  // If the resulting filename is empty, we have nothing to rename to, skip the
904  // file and report that renaming failed (though the function call succeeded).
905  if (aFilename.IsEmpty()) {
906  // aRetVal stays FALSE
907  return NS_OK;
908  }
909 
910  // Append the extension to the new filename
911  if (!fullExtension.IsEmpty()) {
912  aFilename.Append(fullExtension);
913  }
914 
915  // We successfully constructed the new filename
916  *aRetVal = PR_TRUE;
917 
918  return NS_OK;
919 }
920 
927 nsresult
929  nsString &aPath,
930  PRBool *aRetVal)
931 {
932  TRACE(("%s", __FUNCTION__));
933  NS_ENSURE_ARG_POINTER(aMediaItem);
934  NS_ENSURE_ARG_POINTER(aRetVal);
935 
936  nsresult rv;
937 
938  // Assume things won't go well so that we have the right value in aRetVal
939  // if we except, regardless of any previous success
940  *aRetVal = PR_FALSE;
941 
942  // First see if we have a management folder for this media item
943  rv = CheckManagementFolder(aMediaItem);
944  NS_ENSURE_SUCCESS(rv, rv);
945 
946  // Get the root of managed directory
947  nsCOMPtr<nsIFile> mediaFolder;
948  rv = GetMediaFolder(aMediaItem, getter_AddRefs(mediaFolder));
949  NS_ENSURE_SUCCESS(rv, rv);
950  rv = mediaFolder->GetPath(aPath);
951  NS_ENSURE_SUCCESS(rv, rv);
952  rv = NormalizeDir(aPath);
953  NS_ENSURE_SUCCESS(rv, rv);
954 
955  // Format the Folder
956  NameTemplate folderNameTemplate;
957  rv = GetFolderNameTemplate(aMediaItem, folderNameTemplate);
958  NS_ENSURE_SUCCESS(rv, rv);
959  rv = GetFormattedFileFolder(folderNameTemplate,
960  aMediaItem,
961  PR_TRUE,
962  PR_TRUE, // Trim each folder
963  EmptyString(),
964  aPath);
965  NS_ENSURE_SUCCESS(rv, rv);
966 
967  *aRetVal = PR_TRUE;
968 
969  return NS_OK;
970 }
971 
972 void
973 sbMediaFileManager::RemoveBadCharacters(nsString& aStringToParse)
974 {
975  TRACE(("%s", __FUNCTION__));
976 
977  // Sanitize the string so that it only contains characters that are valid for
978  // a filename
979  aStringToParse.StripChars(FILE_ILLEGAL_CHARACTERS);
980 
981  // We also need to strip path separators since filenames and folder names
982  // should not have them.
983  aStringToParse.StripChars(FILE_PATH_SEPARATOR);
984 
985  // Windows does not like Spaces at the begining or end of a file/folder name
986  // Windows also does not like dots at the begining or end of the file/folder
987  // name and dots are bad as well on some other operating systems as they
988  // represent a hidden file.
989  aStringToParse.Trim(" .", PR_TRUE, PR_TRUE);
990 }
991 
992 nsresult
993 sbMediaFileManager::GetUnknownValue(nsString aPropertyKey,
994  nsString& aUnknownValue)
995 {
996  TRACE(("%s (%s)", __FUNCTION__, NS_ConvertUTF16toUTF8(aPropertyKey).BeginReading()));
997  nsresult rv;
998 
999  // Make sure the result is empty for return in case we can not find
1000  // what we should put as Unknown
1001  aUnknownValue.Truncate();
1002 
1003  // Use the locale to create the default string for non-existant properties
1004  // then store it as a pref and always reuse it (so as to avoid it changing
1005  // on us when the user switches to a different locale)
1006  nsCString defaultPrefKey;
1007  defaultPrefKey.AssignLiteral(PREF_MFM_DEFPROPERTY);
1008  defaultPrefKey.Append(NS_ConvertUTF16toUTF8(aPropertyKey));
1009 
1010  PRBool prefExists;
1011  rv = mPrefBranch->PrefHasUserValue(defaultPrefKey.get(), &prefExists);
1012  NS_ENSURE_SUCCESS(rv, rv);
1013 
1014  // If the pref exists, reuse it
1015  if (prefExists) {
1016  nsCString value;
1017  rv = mPrefBranch->GetCharPref(defaultPrefKey.get(),
1018  getter_Copies(value));
1019  NS_ENSURE_SUCCESS(rv, rv);
1020  aUnknownValue.Assign(NS_ConvertUTF8toUTF16(value));
1021  } else {
1022  // If not, create it
1023  nsCOMPtr<sbIPropertyInfo> info;
1024  rv = mPropertyManager->GetPropertyInfo(aPropertyKey,
1025  getter_AddRefs(info));
1026  NS_ENSURE_SUCCESS(rv, rv);
1027 
1028  // try to get a custom localized string first
1029  sbStringBundle stringBundle;
1030 
1031  nsString unknownKey;
1032  rv = info->GetLocalizationKey(unknownKey);
1033  NS_ENSURE_SUCCESS(rv, rv);
1034  unknownKey.Insert(NS_LITERAL_STRING("."), 0);
1035  unknownKey.Insert(NS_LITERAL_STRING(STRING_MFM_UNKNOWNPROP), 0);
1036  aUnknownValue.Assign(stringBundle.Get(unknownKey, aPropertyKey));
1037 
1038  if (aUnknownValue.Equals(aPropertyKey) ||
1039  aUnknownValue.EqualsLiteral("%S"))
1040  {
1041  // no custom default value, use the fallback
1042 
1043  nsString propertyDisplayName;
1044  rv = info->GetDisplayName(propertyDisplayName);
1045  NS_ENSURE_SUCCESS(rv, rv);
1046 
1047  if (propertyDisplayName.IsEmpty()) {
1048  aUnknownValue.Assign(stringBundle.Get(STRING_MFM_UNKNOWNPROP_EMPTY,
1049  "Unknown"));
1050  } else {
1051  nsTArray<nsString> params;
1052  params.AppendElement(propertyDisplayName);
1053  aUnknownValue.Assign(stringBundle.Format(STRING_MFM_UNKNOWNPROP,
1054  params,
1055  "Unknown %S"));
1056  }
1057  }
1058 
1059  rv = mPrefBranch->SetCharPref(defaultPrefKey.get(),
1060  NS_ConvertUTF16toUTF8(aUnknownValue).get());
1061  NS_ENSURE_SUCCESS(rv, rv);
1062  }
1063 
1064  return NS_OK;
1065 }
1066 
1084 nsresult
1085 sbMediaFileManager::GetFormattedFileFolder(
1086  const NameTemplate & aNameTemplate,
1087  sbIMediaItem* aMediaItem,
1088  PRBool aAppendProperty,
1089  PRBool aTrimEachProperty,
1090  nsString aFileExtension,
1091  nsString& aRetVal)
1092 {
1093  TRACE(("%s", __FUNCTION__));
1094  NS_ENSURE_ARG_POINTER(aMediaItem);
1095 
1096  nsresult rv;
1097 
1098  for (PRUint32 i=0; i< aNameTemplate.Length(); i++) {
1099  nsString const & configValue = aNameTemplate[i];
1100  // Any empty template term evaluates to an empty string
1101  if (configValue.IsEmpty()) {
1102  continue;
1103  }
1104 
1105  if (i % 2 != 0) { // Use ! since we start at 0 not 1
1106  // Evens are the separators
1107  // We need to decode these before appending...
1108  nsCAutoString unescapedValue;
1109  rv = mNetUtil->UnescapeString(NS_ConvertUTF16toUTF8(configValue),
1110  nsINetUtil::ESCAPE_ALL,
1111  unescapedValue);
1112  NS_ENSURE_SUCCESS(rv, rv);
1113  aRetVal.AppendLiteral(unescapedValue.get());
1114  } else {
1115  // Odds are the properties
1116  // Get the value of the property for the item
1117  nsString propertyValue;
1118  rv = aMediaItem->GetProperty(configValue, propertyValue);
1119  NS_ENSURE_SUCCESS(rv, rv);
1120 
1121  if (!propertyValue.IsEmpty()) {
1122  if (aTrimEachProperty) {
1123  RemoveBadCharacters(propertyValue);
1124  }
1125 
1126  // Handle track number padding if the user has turned on the pref.
1127  // NOTE: Don't bother checking result value.
1128  PRBool shouldPadTrackNum = PR_FALSE;
1129  mPrefBranch->GetBoolPref(PREF_MFM_PADTRACKNUM, &shouldPadTrackNum);
1130  if (shouldPadTrackNum &&
1131  configValue.EqualsLiteral(SB_PROPERTY_TRACKNUMBER))
1132  {
1133  // Get the number of tracks for this mediaitem.
1134  // NOTE: Don't worry about checking the return value.
1135  nsString numTracksStr;
1136  aMediaItem->GetProperty(
1137  NS_LITERAL_STRING(SB_PROPERTY_TOTALTRACKS),
1138  numTracksStr);
1139 
1140  nsString formattedProperty;
1141  rv = ZeroPadTrackNumber(propertyValue,
1142  numTracksStr,
1143  formattedProperty);
1144  if (NS_SUCCEEDED(rv)) {
1145  propertyValue = formattedProperty;
1146  }
1147  }
1148 
1149  // We need to check the Track Name property for an extension at the end
1150  // and remove it if it exists.
1151  if (!aFileExtension.IsEmpty() &&
1152  configValue.EqualsLiteral(SB_PROPERTY_TRACKNAME)) {
1153  if (StringEndsWith(propertyValue, aFileExtension)) {
1154  propertyValue.SetLength(propertyValue.Length() - aFileExtension.Length());
1155  }
1156  }
1157  }
1158 
1159  // If the property had no associated value, set it to "unknown" if we can
1160  if (propertyValue.IsEmpty()) {
1161  TRACE(("%s: getting fallback for %s",
1162  __FUNCTION__,
1163  NS_ConvertUTF16toUTF8(configValue).get()));
1164  rv = GetUnknownValue(configValue, propertyValue);
1165  if (NS_FAILED(rv) || propertyValue.IsEmpty()) {
1166  // there was no data, _and_ the fallback value was empty
1167  // skip this property and the next separator
1168  i++;
1169  continue;
1170  }
1171  if (aTrimEachProperty) {
1172  RemoveBadCharacters(propertyValue);
1173  }
1174  }
1175 
1176  // We finally have something to add
1177  aRetVal.Append(propertyValue);
1178  }
1179  }
1180 
1181  if (!aTrimEachProperty && !aRetVal.IsEmpty()) {
1182  RemoveBadCharacters(aRetVal);
1183  }
1184 
1185  return NS_OK;
1186 }
1187 
1192 nsresult
1193 sbMediaFileManager::CheckManagementFolder(sbIMediaItem * aMediaItem)
1194 {
1195  TRACE(("%s", __FUNCTION__));
1196  NS_ENSURE_ARG_POINTER(aMediaItem);
1197 
1198  nsresult rv;
1199 
1200  nsCOMPtr<nsIFile> mediaFolder;
1201  rv = GetMediaFolder(aMediaItem, getter_AddRefs(mediaFolder));
1202  NS_ENSURE_SUCCESS (rv, rv);
1203 
1204  if (!mediaFolder) {
1205  return NS_ERROR_FAILURE;
1206  }
1207 
1208  // Always check that the media folder exists since it could have been
1209  // disconnected or removed from the system.
1210  PRBool exists = PR_FALSE;
1211  rv = mediaFolder->Exists(&exists);
1212  NS_ENSURE_SUCCESS(rv, rv);
1213  NS_ENSURE_TRUE(exists, NS_ERROR_FILE_TARGET_DOES_NOT_EXIST);
1214 
1215  return NS_OK;
1216 }
1217 
1223 nsresult sbMediaFileManager::NormalizeDir(nsString &aDir)
1224 {
1225  TRACE(("%s", __FUNCTION__));
1226  // If the directory string is not terminated by a separator, add one,
1227  // unless the string is empty (empty relative dir)
1228  nsString separator = NS_LITERAL_STRING(FILE_PATH_SEPARATOR);
1229  if (!aDir.IsEmpty() &&
1230  aDir.CharAt(aDir.Length()-1) != separator.CharAt(0)) {
1231  aDir.Append(separator);
1232  }
1233  return NS_OK;
1234 }
1235 
1245 nsresult
1246 sbMediaFileManager::ZeroPadTrackNumber(const nsAString & aTrackNumStr,
1247  const nsAString & aTotalTrackCountStr,
1248  nsString & aOutString)
1249 {
1250  nsString format(aTrackNumStr);
1251  nsString totalStr(aTotalTrackCountStr);
1252 
1253  PRInt32 padCount = 0;
1254 
1255  // Determine if padding is needed and how many zeros to pad with.
1256  if (format.Length() < totalStr.Length()) {
1257  padCount = totalStr.Length() - format.Length();
1258  }
1259  else if (totalStr.Length() == 0) {
1260  // Fallback and use at least a default of two (2) zeros if there isn't
1261  // any data for the total track count.
1262  padCount = 2 - format.Length();
1263  }
1264 
1265  if (padCount > 0) {
1266  for (PRInt32 i = 0; i < padCount; i++) {
1267  format.Insert(NS_LITERAL_STRING("0").get(), 0, 1);
1268  }
1269  }
1270 
1271  aOutString = format;
1272  return NS_OK;
1273 }
1274 
1284 nsresult
1286  nsIFile *aSrcFile,
1287  nsIFile *aDestFile,
1288  PRBool *aRetVal)
1289 {
1290  TRACE(("%s", __FUNCTION__));
1291  NS_ENSURE_ARG_POINTER(aMediaItem);
1292  NS_ENSURE_ARG_POINTER(aSrcFile);
1293  NS_ENSURE_ARG_POINTER(aDestFile);
1294  NS_ENSURE_ARG_POINTER(aRetVal);
1295 
1296  // Assume things won't go well so that we have the right value in aRetVal
1297  // if we except, regardless of any previous success
1298  *aRetVal = PR_FALSE;
1299 
1300  nsresult rv;
1301 
1302  // First see if we have a management folder for this media item
1303  rv = CheckManagementFolder(aMediaItem);
1304  NS_ENSURE_SUCCESS(rv, rv);
1305 
1306  // Make sure the Src and Dest files are not equal
1307  PRBool isSrcDestSame = PR_FALSE;
1308  rv = aSrcFile->Equals(aDestFile, &isSrcDestSame);
1309  NS_ENSURE_SUCCESS(rv, rv);
1310  if (isSrcDestSame) {
1311  return NS_ERROR_INVALID_ARG;
1312  }
1313 
1314  // Make sure the new file is under the managed folder
1315  nsCOMPtr<nsIFile> mediaFolder;
1316  rv = GetMediaFolder(aDestFile, getter_AddRefs(mediaFolder));
1317  NS_ENSURE_SUCCESS(rv, rv);
1318  if (!mediaFolder) {
1319  // aRetVal is false
1320  return NS_ERROR_INVALID_ARG;
1321  }
1322 
1323  // Create a Unique filename if the one we want already exists
1324  #if PR_LOGGING
1325  {
1326  nsString path;
1327  rv = aDestFile->GetPath(path);
1328  NS_ASSERTION(NS_SUCCEEDED(rv), "Failed to get dest path");
1329  TRACE(("%s: creating file %s",
1330  __FUNCTION__,
1331  NS_ConvertUTF16toUTF8(path).get()));
1332  }
1333  #endif
1334  rv = aDestFile->CreateUnique(nsIFile::NORMAL_FILE_TYPE, PERMISSIONS_FILE);
1335  NS_ENSURE_SUCCESS(rv, rv);
1336 
1337  // We have to delete the newly created file since we are copying to it.
1338  rv = aDestFile->Remove(PR_FALSE);
1339  NS_ENSURE_SUCCESS(rv, rv);
1340 
1341 #ifdef PR_LOGGING
1342  nsString newDestPath;
1343  rv = aDestFile->GetPath(newDestPath);
1344  if (NS_FAILED(rv)) {
1345  TRACE(("%s: Unable to get path we are copying to?", __FUNCTION__));
1346  } else {
1347  TRACE(("%s: Copying to %s", __FUNCTION__,
1348  NS_ConvertUTF16toUTF8(newDestPath).get()));
1349  }
1350 #endif
1351 
1352  // Get the old path
1353  nsString oldPath;
1354  rv = aSrcFile->GetPath(oldPath);
1355  NS_ENSURE_SUCCESS(rv, rv);
1356 
1357  // Get the new path
1358  nsString newPath;
1359  rv = aDestFile->GetPath(newPath);
1360  NS_ENSURE_SUCCESS(rv, rv);
1361 
1362  if (!mWatchFolderService) {
1363  mWatchFolderService =
1364  do_GetService("@songbirdnest.com/watch-folder-service;1", &rv);
1365  NS_ENSURE_SUCCESS(rv, rv);
1366  }
1367  rv = mWatchFolderService->AddIgnoreCount(oldPath, 1);
1368  NS_ENSURE_SUCCESS(rv, rv);
1369  rv = mWatchFolderService->AddIgnoreCount(newPath, 1);
1370  NS_ENSURE_SUCCESS(rv, rv);
1371 
1372  // Get the new filename
1373  nsString newFilename;
1374  rv = aDestFile->GetLeafName(newFilename);
1375  NS_ENSURE_SUCCESS(rv, rv);
1376 
1377  // Copy the old file to the new location with the new file name
1378  nsCOMPtr<nsIFile> newParentDir;
1379  rv = aDestFile->GetParent(getter_AddRefs(newParentDir));
1380  NS_ENSURE_SUCCESS(rv, rv);
1381 
1382  // If the original was in the managed folder do a move instead of a copy/delete
1383  mediaFolder = nsnull;
1384  rv = GetMediaFolder(aSrcFile, getter_AddRefs(mediaFolder));
1385  NS_ENSURE_SUCCESS(rv, rv);
1386  if (!mediaFolder) {
1387  // Copy since the original is not in the managed folder
1388  rv = aSrcFile->CopyTo(newParentDir, newFilename);
1389  NS_ENSURE_SUCCESS(rv, rv);
1390  // TODO: Do some checks to make sure we successfully copied the file.
1391  } else {
1392  // Move since the original is in the managed folder
1393  // First we need to create a copy of the original file object
1394  nsCOMPtr<nsIFile> holdOldFile;
1395  rv = aSrcFile->Clone(getter_AddRefs(holdOldFile));
1396  NS_ENSURE_SUCCESS(rv, rv);
1397  // Now move the file (aSrcFile will be changed to the new location)
1398  rv = aSrcFile->MoveTo(newParentDir, newFilename);
1399  NS_ENSURE_SUCCESS(rv, rv);
1400  // TODO: Do some checks to make sure we successfully moved the file.
1401  // Now check if we should remove the folder this item was in
1402  rv = CheckDirectoryForDeletion(holdOldFile);
1403  NS_ENSURE_SUCCESS(rv, rv);
1404  }
1405 
1406  // Make a new nsIURI object pointing at the new file
1407  nsCOMPtr<nsIURI> newURI;
1408  rv = sbLibraryUtils::GetFileContentURI(aDestFile, getter_AddRefs(newURI));
1409  NS_ENSURE_SUCCESS(rv, rv);
1410 
1411  // And send that URI back as the item contentsrc
1412  rv = aMediaItem->SetContentSrc(newURI);
1413  NS_ENSURE_SUCCESS(rv, rv);
1414 
1415  // Copying has successfully completed
1416  *aRetVal = PR_TRUE;
1417 
1418  return NS_OK;
1419 }
1420 
1427 {
1428  TRACE(("%s", __FUNCTION__));
1429  NS_ENSURE_ARG_POINTER(aItemFile);
1430  nsresult rv;
1431 
1432  // Get the parent file object
1433  nsCOMPtr<nsIFile> parent;
1434  rv = aItemFile->GetParent(getter_AddRefs(parent));
1435  NS_ENSURE_SUCCESS(rv, rv);
1436 
1437  // And check whether we need to remove it and its parent dirs (recursive call)
1438  rv = CheckDirectoryForDeletion_Recursive(parent);
1439  NS_ENSURE_SUCCESS(rv, rv);
1440 
1441  return NS_OK;
1442 }
1443 
1449 nsresult
1450 sbMediaFileManager::CheckDirectoryForDeletion_Recursive(nsIFile *aDirectory)
1451 {
1452  TRACE(("%s", __FUNCTION__));
1453  NS_ENSURE_ARG_POINTER(aDirectory);
1454  nsresult rv;
1455 
1456  // Make sure this folder is under the managed folder
1457  nsCOMPtr<nsIFile> mediaFolder;
1458  rv = GetMediaFolder(aDirectory, getter_AddRefs(mediaFolder));
1459  NS_ENSURE_SUCCESS(rv, rv);
1460  if (!mediaFolder) {
1461  TRACE(("%s: Folder Not Managed", __FUNCTION__));
1462  return NS_OK;
1463  }
1464 
1465  // Check whether the directory is empty
1466  nsCOMPtr<nsISimpleEnumerator> dirEntries;
1467  rv = aDirectory->GetDirectoryEntries(getter_AddRefs(dirEntries));
1468 
1469  PRBool hasMoreElements;
1470  rv = dirEntries->HasMoreElements(&hasMoreElements);
1471  NS_ENSURE_SUCCESS(rv, rv);
1472 
1473  if (hasMoreElements) {
1474  // Directory is not empty, exit recursion now.
1475  TRACE(("%s: Folder Not Empty", __FUNCTION__));
1476  return NS_OK;
1477  }
1478 
1479  // Directory is empty, and is a subdirectory of the root managed directory,
1480  // so delete it
1481  rv = aDirectory->Remove(PR_FALSE);
1482  NS_ENSURE_SUCCESS(rv, rv);
1483 
1484  // Get the parent directory
1485  nsCOMPtr<nsIFile> parent;
1486  rv = aDirectory->GetParent(getter_AddRefs(parent));
1487  NS_ENSURE_SUCCESS(rv, rv);
1488 
1489  // And recurse ...
1490  return CheckDirectoryForDeletion_Recursive(parent);
1491 }
1492 
1493 
1500 nsresult
1501 sbMediaFileManager::Delete(nsIFile *aItemFile,
1502  PRBool *aRetVal)
1503 {
1504  TRACE(("%s", __FUNCTION__));
1505  NS_ENSURE_ARG_POINTER(aItemFile);
1506  NS_ENSURE_ARG_POINTER(aRetVal);
1507 
1508  nsresult rv;
1509 
1510  // Assume things won't go well so that we have the right value in aRetVal
1511  // if we except, regardless of any previous success
1512  *aRetVal = PR_FALSE;
1513 
1514  // Make sure this file is under the managed folder
1515  nsCOMPtr<nsIFile> mediaFolder;
1516  rv = GetMediaFolder(aItemFile, getter_AddRefs(mediaFolder));
1517  NS_ENSURE_SUCCESS(rv, rv);
1518  if (!mediaFolder) {
1519  // aRetVal is false
1520  return NS_OK;
1521  }
1522 
1523  // Delete the file
1524  rv = aItemFile->Remove(PR_FALSE);
1525  NS_ENSURE_SUCCESS(rv, rv);
1526 
1527  // Check whether we need to also delete the directory (and parent directories)
1528  rv = CheckDirectoryForDeletion(aItemFile);
1529  NS_ENSURE_SUCCESS(rv, rv);
1530 
1531  // Deletion happened successfully
1532  *aRetVal = PR_TRUE;
1533 
1534  return NS_OK;
1535 }
1536 
const unsigned short MANAGE_MOVE
nsresult CheckDirectoryForDeletion(nsIFile *aItemFile)
Checks whether a directory and its parent directories need to be deleted.
const unsigned short MANAGE_RENAME
virtual ~sbMediaFileManager()
Destructor of the sbMediaFileManager component.
#define PERMISSIONS_FILE
#define SB_PROPERTY_IMPORTTYPE
nsresult Delete(nsIFile *aItemFile, PRBool *aRetVal)
Deletes a file associated with an item if it is located in the managed folder.
#define PREF_MFM_ROOT
return NS_OK
_updateCookies aPath
static nsresult sbGetFileExtensionFromURI(nsIURI *aURI, nsACString &_retval)
Definition: sbURIUtils.h:41
#define SB_VALUE_IMPORTTYPE_VOICE_RECORDING
#define PREF_MFM_DEFPROPERTY
nsresult GetNewPath(sbIMediaItem *aMediaItem, nsString &aPath, PRBool *aRetVal)
Construct the new path based on user settings.
static nsresult GetFileContentURI(nsIFile *aFile, nsIURI **_retval)
Return a library content URI for the file specified by aFile. Special processing is required to conve...
nsString Get(const nsAString &aKey, const nsAString &aDefault=SBVoidString())
#define TRACE(args)
nsString Format(const nsAString &aKey, nsTArray< nsString > &aParams, const nsAString &aDefault=SBVoidString())
#define STRING_MFM_RECORDINGS_FOLDER
sbMediaFileManager()
Constructor of the sbMediaFileManager component.
nsresult do_GetProxyForObject(nsIEventTarget *aTarget, REFNSIID aIID, nsISupports *aObj, PRInt32 aProxyType, void **aProxyObject)
#define SB_PROPERTY_TOTALTRACKS
nsresult NormalizeDir(nsString &aDir)
Makes sure that a directory ends with the path separator if it can.
NS_METHOD Init()
Initialize the sbMediaFileManager component.
#define MFM_VIDEO_FOLDER_KEY
nsresult CopyRename(sbIMediaItem *aMediaItem, nsIFile *aItemFile, nsIFile *aNewFile, PRBool *aRetVal)
Copys a file from the old location to the new location, ensures that the destination is in the manage...
#define SB_PROPERTYMANAGER_CONTRACTID
nsresult ZeroPadTrackNumber(const nsAString &aTrackNumStr, const nsAString &aTotalTrackCountStr, nsString &aOutString)
Format a track number to pad for zeros against the total track count.
var strings
Definition: Info.js:46
sbMainThreadQueryInterface do_MainThreadQueryInterface(nsISupports *aSupports, nsresult *aResult=nsnull)
#define SB_PROPERTY_CONTENTTYPE
void nsString_Split(const nsAString &aString, const nsAString &aDelimiter, nsTArray< nsString > &aSubStringArray)
#define STRING_MFM_UNKNOWNPROP_EMPTY
#define MFM_MUSIC_FOLDER_KEY
#define SB_PROPERTY_ARTISTNAME
NS_IMPL_THREADSAFE_ISUPPORTS1(sbMediaFileManager, sbIMediaFileManager)
function newURI(aURLString)
Songbird String Bundle Definitions.
#define PREF_MFM_FILEFORMAT
SimpleArrayEnumerator prototype hasMoreElements
function url(spec)
countRef value
Definition: FeedWriter.js:1423
#define SB_PROPERTY_ALBUMNAME
nsresult GetNewFilename(sbIMediaItem *aMediaItem, nsIURI *aItemUri, nsString &aFilename, PRBool *aRetVal)
Construct the new filename based on user settings.
#define _MFM_SEPARATOR
Interface that defines a single item of media in the system.
#define SB_PROPERTY_TRACKNAME
#define SB_VALUE_IMPORTTYPE_FM_RECORDING
#define PREF_MFM_PADTRACKNUM
#define LOG(args)
const unsigned short MANAGE_COPY
_getSelectedPageStyle s i
const unsigned short MANAGE_DELETE
#define STRING_MFM_UNKNOWNPROP
_updateTextAndScrollDataForFrame aData
#define SB_PROPERTY_TRACKNUMBER