nsresult
nsSafariProfileMigrator::CopyHistoryBatched(bool aReplace)
{
  nsCOMPtr<nsIProperties> fileLocator(do_GetService(NS_DIRECTORY_SERVICE_CONTRACTID));
  nsCOMPtr<nsILocalFile> safariHistoryFile;
  fileLocator->Get(NS_MAC_USER_LIB_DIR, NS_GET_IID(nsILocalFile),
                   getter_AddRefs(safariHistoryFile));
  safariHistoryFile->Append(NS_LITERAL_STRING("Safari"));
  safariHistoryFile->Append(SAFARI_HISTORY_FILE_NAME);

  CFDictionaryRef safariHistory =
    static_cast<CFDictionaryRef>(CopyPListFromFile(safariHistoryFile));
  if (!safariHistory)
    return NS_OK;

  if (!::CFDictionaryContainsKey(safariHistory, CFSTR("WebHistoryDates"))) {
    ::CFRelease(safariHistory);
    return NS_OK;
  }

  nsCOMPtr<nsIBrowserHistory> history(do_GetService(NS_GLOBALHISTORY2_CONTRACTID));

  CFArrayRef children = (CFArrayRef)
                ::CFDictionaryGetValue(safariHistory, CFSTR("WebHistoryDates"));
  if (children) {
    CFIndex count = ::CFArrayGetCount(children);
    for (PRInt32 i = 0; i < count; ++i) {
      CFDictionaryRef entry = (CFDictionaryRef)::CFArrayGetValueAtIndex(children, i);

      CFStringRef lastVisitedDate = (CFStringRef)
                        ::CFDictionaryGetValue(entry, CFSTR("lastVisitedDate"));
      nsAutoString url, title;
      if (GetDictionaryStringValue(entry, CFSTR(""), url) &&
          GetDictionaryStringValue(entry, CFSTR("title"), title) &&
          lastVisitedDate) {

        double lvd = ::CFStringGetDoubleValue(lastVisitedDate) + SAFARI_DATE_OFFSET;
        PRTime lastVisitTime;
        PRInt64 temp, million;
        LL_D2L(temp, lvd);
        LL_I2L(million, PR_USEC_PER_SEC);
        LL_MUL(lastVisitTime, temp, million);

        nsCOMPtr<nsIURI> uri;
        NS_NewURI(getter_AddRefs(uri), url);
        if (uri)
          history->AddPageWithDetails(uri, title.get(), lastVisitTime);
      }
    }
  }

  ::CFRelease(safariHistory);

  return NS_OK;
}
CFDictionaryRef CopySafariPrefs()
{
  nsCOMPtr<nsIProperties> fileLocator(do_GetService(NS_DIRECTORY_SERVICE_CONTRACTID));
  nsCOMPtr<nsILocalFile> safariPrefsFile;
  fileLocator->Get(NS_OSX_USER_PREFERENCES_DIR,
                   NS_GET_IID(nsILocalFile),
                   getter_AddRefs(safariPrefsFile));

  safariPrefsFile->Append(SAFARI_PREFERENCES_FILE_NAME);

  return static_cast<CFDictionaryRef>(CopyPListFromFile(safariPrefsFile));
}
nsresult
nsSafariProfileMigrator::CopyCookies(bool aReplace)
{
  nsCOMPtr<nsIProperties> fileLocator(do_GetService(NS_DIRECTORY_SERVICE_CONTRACTID));
  nsCOMPtr<nsILocalFile> safariCookiesFile;
  fileLocator->Get(NS_MAC_USER_LIB_DIR,
                   NS_GET_IID(nsILocalFile),
                   getter_AddRefs(safariCookiesFile));
  safariCookiesFile->Append(NS_LITERAL_STRING("Cookies"));
  safariCookiesFile->Append(SAFARI_COOKIES_FILE_NAME);

  CFArrayRef safariCookies = (CFArrayRef)CopyPListFromFile(safariCookiesFile);
  if (!safariCookies)
    return NS_OK;

  nsCOMPtr<nsICookieManager2> cookieManager(do_GetService(NS_COOKIEMANAGER_CONTRACTID));
  CFIndex count = ::CFArrayGetCount(safariCookies);
  for (PRInt32 i = 0; i < count; ++i) {
    CFDictionaryRef entry = (CFDictionaryRef)::CFArrayGetValueAtIndex(safariCookies, i);

    CFDateRef date = (CFDateRef)::CFDictionaryGetValue(entry, CFSTR("Expires"));

    nsCAutoString domain, path, name, value;
    if (date &&
        GetDictionaryCStringValue(entry, CFSTR("Domain"), domain,
                                  kCFStringEncodingUTF8) &&
        GetDictionaryCStringValue(entry, CFSTR("Path"), path,
                                  kCFStringEncodingUTF8) &&
        GetDictionaryCStringValue(entry, CFSTR("Name"), name,
                                  kCFStringEncodingASCII) &&
        GetDictionaryCStringValue(entry, CFSTR("Value"), value,
                                  kCFStringEncodingASCII)) {
      PRInt64 expiryTime;
      LL_D2L(expiryTime, (double)::CFDateGetAbsoluteTime(date));

      expiryTime += SAFARI_DATE_OFFSET;
      cookieManager->Add(domain, path, name, value,
                         false, // isSecure
                         false, // isHttpOnly
                         false, // isSession
                         expiryTime);
    }
  }
  ::CFRelease(safariCookies);

  return NS_OK;
}
nsresult
nsSafariProfileMigrator::CopyBookmarksBatched(bool aReplace)
{
  // If "aReplace" is true, merge into the root level of bookmarks. Otherwise, create
  // a folder called "Imported Safari Favorites" and place all the Bookmarks there.
  nsresult rv;

  nsCOMPtr<nsINavBookmarksService> bms =
    do_GetService(NS_NAVBOOKMARKSSERVICE_CONTRACTID, &rv);
  NS_ENSURE_SUCCESS(rv, rv);
  PRInt64 bookmarksMenuFolderId;
  rv = bms->GetBookmarksMenuFolder(&bookmarksMenuFolderId);
  NS_ENSURE_SUCCESS(rv, rv);

  PRInt64 folder;
  if (!aReplace) {
    nsCOMPtr<nsIStringBundleService> bundleService =
      do_GetService(NS_STRINGBUNDLE_CONTRACTID, &rv);
    NS_ENSURE_SUCCESS(rv, rv);
    nsCOMPtr<nsIStringBundle> bundle;
    rv = bundleService->CreateBundle(MIGRATION_BUNDLE, getter_AddRefs(bundle));
    NS_ENSURE_SUCCESS(rv, rv);

    nsString sourceNameSafari;
    rv = bundle->GetStringFromName(NS_LITERAL_STRING("sourceNameSafari").get(),
                                   getter_Copies(sourceNameSafari));
    NS_ENSURE_SUCCESS(rv, rv);

    const PRUnichar* sourceNameStrings[] = { sourceNameSafari.get() };
    nsString importedSafariBookmarksTitle;
    rv = bundle->FormatStringFromName(NS_LITERAL_STRING("importedBookmarksFolder").get(),
                                      sourceNameStrings, 1,
                                      getter_Copies(importedSafariBookmarksTitle));
    NS_ENSURE_SUCCESS(rv, rv);

    rv = bms->CreateFolder(bookmarksMenuFolderId,
                           NS_ConvertUTF16toUTF8(importedSafariBookmarksTitle),
                           nsINavBookmarksService::DEFAULT_INDEX, &folder);
    NS_ENSURE_SUCCESS(rv, rv);
  }
  else {
    nsCOMPtr<nsIFile> profile;
    GetProfilePath(nsnull, profile);
    rv = InitializeBookmarks(profile);
    NS_ENSURE_SUCCESS(rv, rv);
    // In replace mode we are merging at the top level.
    folder = bookmarksMenuFolderId;
  }

  nsCOMPtr<nsIProperties> fileLocator(do_GetService(NS_DIRECTORY_SERVICE_CONTRACTID));
  nsCOMPtr<nsILocalFile> safariBookmarksFile;
  fileLocator->Get(NS_MAC_USER_LIB_DIR, NS_GET_IID(nsILocalFile),
                   getter_AddRefs(safariBookmarksFile));
  safariBookmarksFile->Append(NS_LITERAL_STRING("Safari"));
  safariBookmarksFile->Append(SAFARI_BOOKMARKS_FILE_NAME);

  CFDictionaryRef safariBookmarks =
    static_cast<CFDictionaryRef>(CopyPListFromFile(safariBookmarksFile));
  if (!safariBookmarks)
    return NS_OK;

  // The Safari Bookmarks file looks like this:
  // At the top level are all the Folders, Special Folders and Proxies. Proxies
  // are references to other data sources such as History, Rendezvous etc.
  // We ignore these. Special folders exist for the Bookmarks Toolbar folder
  // (called "BookmarksBar" and the Bookmarks Menu (called "BookmarksMenu").
  // We put the contents of the "BookmarksBar" folder into our Personal Toolbar
  // and merge the contents of the "BookmarksMenu" folder and the other toplevel
  // non-special folders under our NC:BookmarksRoot.
  if (::CFDictionaryContainsKey(safariBookmarks, CFSTR("Children")) &&
      ::CFDictionaryContainsKey(safariBookmarks, CFSTR("WebBookmarkFileVersion")) ) {
    CFNumberRef intValue =
      (CFNumberRef)::CFDictionaryGetValue(safariBookmarks,
                                          CFSTR("WebBookmarkFileVersion"));
    PRInt32 value = 0;
    if (::CFNumberGetValue(intValue, kCFNumberSInt32Type, &value) && value ==1) {
      CFArrayRef children =
        (CFArrayRef)::CFDictionaryGetValue(safariBookmarks, CFSTR("Children"));
      if (children) {
        rv = ParseBookmarksFolder(children, folder, bms, true);
      }
    }
  }
  ::CFRelease(safariBookmarks);
  return rv;
}
nsresult
nsSafariProfileMigrator::CopyPreferences(bool aReplace)
{
  nsCOMPtr<nsIPrefBranch> branch(do_GetService(NS_PREFSERVICE_CONTRACTID));

  CFDictionaryRef safariPrefs = CopySafariPrefs();
  if (!safariPrefs)
    return NS_ERROR_FAILURE;

  // Traverse the standard transforms
  PrefTransform* transform;
  PrefTransform* end = gTransforms +
                       sizeof(gTransforms) / sizeof(PrefTransform);

  for (transform = gTransforms; transform < end; ++transform) {
    Boolean hasValue = ::CFDictionaryContainsKey(safariPrefs, transform->keyName);
    if (!hasValue)
      continue;

    transform->prefHasValue = false;
    switch (transform->type) {
    case _SPM(STRING): {
        CFStringRef stringValue = (CFStringRef)
                                  ::CFDictionaryGetValue(safariPrefs,
                                                         transform->keyName);
        char* value = GetNullTerminatedString(stringValue);
        if (value) {
          transform->stringValue = value;
          transform->prefHasValue = true;
        }
      }
      break;
    case _SPM(INT): {
        CFNumberRef intValue = (CFNumberRef)
                               ::CFDictionaryGetValue(safariPrefs,
                                                      transform->keyName);
        PRInt32 value = 0;
        if (::CFNumberGetValue(intValue, kCFNumberSInt32Type, &value)) {
          transform->intValue = value;
          transform->prefHasValue = true;
        }
      }
      break;
    case _SPM(BOOL): {
        CFBooleanRef boolValue = (CFBooleanRef)
                                 ::CFDictionaryGetValue(safariPrefs,
                                                        transform->keyName);
        transform->boolValue = boolValue == kCFBooleanTrue;
        transform->prefHasValue = true;
      }
      break;
    default:
      break;
    }

    if (transform->prefHasValue)
      transform->prefSetterFunc(transform, branch);

    if (transform->type == _SPM(STRING))
      FreeNullTerminatedString(transform->stringValue);
  }

  ::CFRelease(safariPrefs);

  // Safari stores the Cookie "Accept/Don't Accept/Don't Accept Foreign" cookie
  // setting in a separate WebFoundation preferences PList.
  nsCOMPtr<nsIProperties> fileLocator(do_GetService(NS_DIRECTORY_SERVICE_CONTRACTID));
  nsCOMPtr<nsILocalFile> safariWebFoundationPrefsFile;
  fileLocator->Get(NS_OSX_USER_PREFERENCES_DIR, NS_GET_IID(nsILocalFile),
                   getter_AddRefs(safariWebFoundationPrefsFile));
  safariWebFoundationPrefsFile->Append(SAFARI_COOKIE_BEHAVIOR_FILE_NAME);

  CFDictionaryRef safariWebFoundationPrefs =
    static_cast<CFDictionaryRef>(CopyPListFromFile(safariWebFoundationPrefsFile));
  if (safariWebFoundationPrefs) {
    // Mapping of Safari preference values to Firefox preference values:
    //
    // Setting                    Safari          Firefox
    // Always Accept              always          0
    // Accept from Originating    current page    1
    // Never Accept               never           2
    nsAutoString acceptCookies;
    if (GetDictionaryStringValue(safariWebFoundationPrefs,
                                 CFSTR("NSHTTPAcceptCookies"), acceptCookies)) {
      PRInt32 cookieValue = 0;
      if (acceptCookies.EqualsLiteral("never"))
        cookieValue = 2;
      else if (acceptCookies.EqualsLiteral("current page"))
        cookieValue = 1;

      branch->SetIntPref("network.cookie.cookieBehavior", cookieValue);
    }

    ::CFRelease(safariWebFoundationPrefs);
  }

  return NS_OK;
}
nsresult
nsSafariProfileMigrator::CopyBookmarks(PRBool aReplace)
{
    // If "aReplace" is true, merge into the root level of bookmarks. Otherwise, create
    // a folder called "Imported IE Favorites" and place all the Bookmarks there.
    nsresult rv;

#ifdef MOZ_PLACES
    nsCOMPtr<nsINavBookmarksService> bms(do_GetService(NS_NAVBOOKMARKSSERVICE_CONTRACTID, &rv));
    NS_ENSURE_SUCCESS(rv, rv);
    PRInt64 root;
    rv = bms->GetBookmarksRoot(&root);
    NS_ENSURE_SUCCESS(rv, rv);

    PRInt64 folder;
#else
    nsCOMPtr<nsIRDFService> rdf(do_GetService("@mozilla.org/rdf/rdf-service;1"));
    nsCOMPtr<nsIRDFResource> root;
    rdf->GetResource(NS_LITERAL_CSTRING("NC:BookmarksRoot"), getter_AddRefs(root));

    nsCOMPtr<nsIBookmarksService> bms(do_GetService("@mozilla.org/browser/bookmarks-service;1"));
    NS_ENSURE_TRUE(bms, NS_ERROR_FAILURE);
    PRBool dummy;
    bms->ReadBookmarks(&dummy);

    nsCOMPtr<nsIRDFResource> folder;
#endif
    if (!aReplace) {
        nsCOMPtr<nsIStringBundleService> bundleService = do_GetService(kStringBundleServiceCID, &rv);
        if (NS_FAILED(rv)) return rv;

        nsCOMPtr<nsIStringBundle> bundle;
        bundleService->CreateBundle(MIGRATION_BUNDLE, getter_AddRefs(bundle));

        nsXPIDLString sourceNameSafari;
        bundle->GetStringFromName(NS_LITERAL_STRING("sourceNameSafari").get(),
                                  getter_Copies(sourceNameSafari));

        const PRUnichar* sourceNameStrings[] = { sourceNameSafari.get() };
        nsXPIDLString importedSafariBookmarksTitle;
        bundle->FormatStringFromName(NS_LITERAL_STRING("importedBookmarksFolder").get(),
                                     sourceNameStrings, 1,
                                     getter_Copies(importedSafariBookmarksTitle));

#ifdef MOZ_PLACES
        bms->CreateFolder(root, importedSafariBookmarksTitle, -1, &folder);
#else
        bms->CreateFolderInContainer(importedSafariBookmarksTitle.get(), root, -1,
                                     getter_AddRefs(folder));
#endif
    }
    else {
        // In non-replace mode we are merging at the top level.
        folder = root;
    }

    nsCOMPtr<nsIProperties> fileLocator(do_GetService(NS_DIRECTORY_SERVICE_CONTRACTID));
    nsCOMPtr<nsILocalFile> safariBookmarksFile;
    fileLocator->Get(NS_MAC_USER_LIB_DIR, NS_GET_IID(nsILocalFile),
                     getter_AddRefs(safariBookmarksFile));
    safariBookmarksFile->Append(NS_LITERAL_STRING("Safari"));
    safariBookmarksFile->Append(SAFARI_BOOKMARKS_FILE_NAME);

    CFDictionaryRef safariBookmarks = (CFDictionaryRef)CopyPListFromFile(safariBookmarksFile);
    if (!safariBookmarks)
        return NS_OK;

    // The Safari Bookmarks file looks like this:
    // At the top level are all the Folders, Special Folders and Proxies. Proxies
    // are references to other data sources such as History, Rendezvous etc.
    // We ignore these. Special folders exist for the Bookmarks Toolbar folder
    // (called "BookmarksBar" and the Bookmarks Menu (called "BookmarksMenu").
    // We put the contents of the "BookmarksBar" folder into our Personal Toolbar
    // and merge the contents of the "BookmarksMenu" folder and the other toplevel
    // non-special folders under our NC:BookmarksRoot.
    if (!::CFDictionaryContainsKey(safariBookmarks, CFSTR("Children")) ||
            !::CFDictionaryContainsKey(safariBookmarks, CFSTR("WebBookmarkFileVersion")) ) {
        ::CFRelease(safariBookmarks);
        return NS_OK;
    }

    CFNumberRef intValue = (CFNumberRef)::CFDictionaryGetValue(safariBookmarks,
                           CFSTR("WebBookmark" \
                                 "FileVersion"));
    PRInt32 value = 0;
    if (!::CFNumberGetValue(intValue, kCFNumberSInt32Type, &value) || value != 1)
        return NS_OK;

    CFArrayRef children = (CFArrayRef)::CFDictionaryGetValue(safariBookmarks,
                          CFSTR("Children"));
    if (children) {
        nsresult rv = ParseBookmarksFolder(children, folder, bms, PR_TRUE);
        if (NS_FAILED(rv)) return rv;
    }

    ::CFRelease(safariBookmarks);

    return NS_OK;
}