nsresult
nsOperaProfileMigrator::ParseBookmarksFolder(nsILineInputStream* aStream,
        PRInt64 aParent,
        PRInt64 aToolbar,
        nsINavBookmarksService* aBMS)
{
    nsresult rv;
    PRBool moreData = PR_FALSE;
    nsAutoString buffer;
    EntryType entryType = EntryType_BOOKMARK;
    nsAutoString keyword, description;
    nsCAutoString url, name;
    PRBool onToolbar = PR_FALSE;
    do {
        nsCAutoString cBuffer;
        rv = aStream->ReadLine(cBuffer, &moreData);
        if (NS_FAILED(rv)) return rv;

        CopyUTF8toUTF16(cBuffer, buffer);
        nsString data;
        LineType type = GetLineType(buffer, getter_Copies(data));
        switch(type) {
        case LineType_FOLDER:
            entryType = EntryType_FOLDER;
            break;
        case LineType_BOOKMARK:
            entryType = EntryType_BOOKMARK;
            break;
        case LineType_SEPARATOR:
            // If we're here, we need to break out of the loop for the current folder,
            // essentially terminating this instance of ParseBookmarksFolder and return
            // to the calling function, which is either ParseBookmarksFolder for a parent
            // folder, or CopyBookmarks (which means we're done parsing all bookmarks).
            goto done;
        case LineType_NAME:
            name.Assign(NS_ConvertUTF16toUTF8(data));
            break;
        case LineType_URL:
            url.Assign(NS_ConvertUTF16toUTF8(data));
            break;
        case LineType_KEYWORD:
            keyword = data;
            break;
        case LineType_DESCRIPTION:
            description = data;
            break;
        case LineType_ONTOOLBAR:
            if (NS_LITERAL_STRING("YES").Equals(data))
                onToolbar = PR_TRUE;
            break;
        case LineType_NL: {
            // XXX We don't know for sure how Opera deals with IDN hostnames in URL.
            // Assuming it's in UTF-8 is rather safe because it covers two cases
            // (UTF-8 and ASCII) out of three cases (the last is a non-UTF-8
            // multibyte encoding).
            // XXX Todo: |description| is not saved.
            if (entryType == EntryType_BOOKMARK) {
                if (!name.IsEmpty() && !url.IsEmpty()) {
                    nsCOMPtr<nsIURI> uri;
                    rv = NS_NewURI(getter_AddRefs(uri), url);
                    if (NS_FAILED(rv))
                        continue;
                    PRInt64 id;
                    rv = aBMS->InsertBookmark(onToolbar ? aToolbar : aParent,
                                              uri, nsINavBookmarksService::DEFAULT_INDEX,
                                              name, &id);
                    if (NS_FAILED(rv))
                        continue;
                    name.Truncate();
                    url.Truncate();
                    keyword.Truncate();
                    description.Truncate();
                    onToolbar = PR_FALSE;
                }
            }
            else if (entryType == EntryType_FOLDER) {
                if (!name.IsEmpty()) {
                    PRInt64 newFolder;
                    rv = aBMS->CreateFolder(onToolbar ? aToolbar : aParent,
                                            name, nsINavBookmarksService::DEFAULT_INDEX, &newFolder);
                    if (NS_FAILED(rv))
                        continue;
                    rv = ParseBookmarksFolder(aStream, newFolder, aToolbar, aBMS);
                    name.Truncate();
                }
            }
            break;
        }
        case LineType_OTHER:
            break;
        }
    }
    while (moreData);

done:
    return rv;
}
nsresult
nsOperaProfileMigrator::CopyBookmarksBatched(PRBool aReplace)
{
    // Find Opera Bookmarks
    nsCOMPtr<nsIFile> operaBookmarks;
    mOperaProfile->Clone(getter_AddRefs(operaBookmarks));
    operaBookmarks->Append(OPERA_BOOKMARKS_FILE_NAME);

    nsCOMPtr<nsIInputStream> fileInputStream;
    NS_NewLocalFileInputStream(getter_AddRefs(fileInputStream), operaBookmarks);
    NS_ENSURE_TRUE(fileInputStream, NS_ERROR_OUT_OF_MEMORY);

    nsCOMPtr<nsILineInputStream> lineInputStream(do_QueryInterface(fileInputStream));

    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 parentFolder = bookmarksMenuFolderId;

    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);

    if (!aReplace) {
        nsString sourceNameOpera;
        rv = bundle->GetStringFromName(NS_LITERAL_STRING("sourceNameOpera").get(),
                                       getter_Copies(sourceNameOpera));
        NS_ENSURE_SUCCESS(rv, rv);

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

        rv = bms->CreateFolder(parentFolder,
                               NS_ConvertUTF16toUTF8(importedOperaHotlistTitle),
                               nsINavBookmarksService::DEFAULT_INDEX,
                               &parentFolder);
        NS_ENSURE_SUCCESS(rv, rv);
    }
    else {
        nsCOMPtr<nsIFile> profile;
        GetProfilePath(nsnull, profile);
        rv = InitializeBookmarks(profile);
        NS_ENSURE_SUCCESS(rv, rv);
    }

#if defined(XP_WIN) || (defined(XP_UNIX) && !defined(XP_MACOSX))
    CopySmartKeywords(bms, bundle, parentFolder);
#endif

    PRInt64 bookmarksToolbarFolderId;
    rv = bms->GetToolbarFolder(&bookmarksToolbarFolderId);
    NS_ENSURE_SUCCESS(rv, rv);

    rv = ParseBookmarksFolder(lineInputStream, parentFolder,
                              bookmarksToolbarFolderId, bms);
    NS_ENSURE_SUCCESS(rv, rv);

    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::ParseBookmarksFolder(CFArrayRef aChildren,
                                              PRInt64 aParentFolder,
                                              nsINavBookmarksService * aBookmarksService,
                                              bool aIsAtRootLevel)
{
  nsresult rv = NS_OK;

  CFIndex count = ::CFArrayGetCount(aChildren);
  for (PRInt32 i = 0; i < count; ++i) {
    CFDictionaryRef entry = (CFDictionaryRef)::CFArrayGetValueAtIndex(aChildren, i);

    nsAutoString type;
    if (!GetDictionaryStringValue(entry, CFSTR("WebBookmarkType"), type))
      continue;

    if (!type.EqualsLiteral("WebBookmarkTypeList") &&
        !type.EqualsLiteral("WebBookmarkTypeLeaf"))
      continue;

    if (::CFDictionaryContainsKey(entry, CFSTR("Children")) &&
        type.EqualsLiteral("WebBookmarkTypeList")) {
      nsAutoString title;
      if (!GetDictionaryStringValue(entry, CFSTR("Title"), title))
        continue;

      CFArrayRef children = (CFArrayRef)::CFDictionaryGetValue(entry,
                                                               CFSTR("Children"));

      // Look for the BookmarksBar Bookmarks and add them into the appropriate
      // Personal Toolbar Root
      if (title.EqualsLiteral("BookmarksBar") && aIsAtRootLevel) {
        PRInt64 toolbarFolder;
        aBookmarksService->GetToolbarFolder(&toolbarFolder);

        rv |= ParseBookmarksFolder(children,
                                   toolbarFolder,
                                   aBookmarksService,
                                   false);
      }
      // Look for the BookmarksMenu Bookmarks and flatten them into the top level
      else if (title.EqualsLiteral("BookmarksMenu") && aIsAtRootLevel) {
        rv |= ParseBookmarksFolder(children,
                                   aParentFolder,
                                   aBookmarksService,
                                   true);
      }
      else {
        // Encountered a Folder, so create one in our Bookmarks DataSource and then
        // parse the contents of the Safari one into it...
        PRInt64 folder;
        rv |= aBookmarksService->CreateFolder(aParentFolder, NS_ConvertUTF16toUTF8(title),
                                              nsINavBookmarksService::DEFAULT_INDEX,
                                              &folder);
        rv |= ParseBookmarksFolder(children,
                                   folder,
                                   aBookmarksService,
                                   false);
      }
    }
    else if (type.EqualsLiteral("WebBookmarkTypeLeaf")) {
      // Encountered a Bookmark, so add it to the current folder...
      CFDictionaryRef URIDictionary = (CFDictionaryRef)
                      ::CFDictionaryGetValue(entry, CFSTR("URIDictionary"));
      nsAutoString title;
      nsCAutoString url;
      if (GetDictionaryStringValue(URIDictionary, CFSTR("title"), title) &&
          GetDictionaryCStringValue(entry, CFSTR("URLString"), url, kCFStringEncodingUTF8)) {
        nsCOMPtr<nsIURI> uri;
        rv = NS_NewURI(getter_AddRefs(uri), url);
        if (NS_SUCCEEDED(rv)) {
          PRInt64 id;
          rv = aBookmarksService->InsertBookmark(aParentFolder, uri,
                                                 nsINavBookmarksService::DEFAULT_INDEX,
                                                 NS_ConvertUTF16toUTF8(title), &id);
        }
      }
    }
  }
  return rv;
}
nsresult
nsSafariProfileMigrator::ParseBookmarksFolder(CFArrayRef aChildren,
#ifdef MOZ_PLACES
        PRInt64 aParentFolder,
        nsINavBookmarksService * aBookmarksService,
#else
        nsIRDFResource* aParentResource,
        nsIBookmarksService* aBookmarksService,
#endif
        PRBool aIsAtRootLevel)
{
    nsresult rv;

    CFIndex count = ::CFArrayGetCount(aChildren);
    for (PRInt32 i = 0; i < count; ++i) {
        CFDictionaryRef entry = (CFDictionaryRef)::CFArrayGetValueAtIndex(aChildren, i);

        nsAutoString type;
        if (!GetDictionaryStringValue(entry, CFSTR("WebBookmarkType"), type))
            continue;

        if (!type.EqualsLiteral("WebBookmarkTypeList") &&
                !type.EqualsLiteral("WebBookmarkTypeLeaf"))
            continue;

        if (::CFDictionaryContainsKey(entry, CFSTR("Children")) &&
                type.EqualsLiteral("WebBookmarkTypeList")) {
            nsAutoString title;
            if (!GetDictionaryStringValue(entry, CFSTR("Title"), title))
                continue;

            CFArrayRef children = (CFArrayRef)::CFDictionaryGetValue(entry,
                                  CFSTR("Children"));

            // Look for the BookmarksBar Bookmarks and add them into the appropriate
            // Personal Toolbar Root
            if (title.EqualsLiteral("BookmarksBar") && aIsAtRootLevel) {
#ifdef MOZ_PLACES
                PRInt64 toolbarFolder;
                aBookmarksService->GetToolbarRoot(&toolbarFolder);
#else
                nsCOMPtr<nsIRDFResource> toolbarFolder;
                aBookmarksService->GetBookmarksToolbarFolder(getter_AddRefs(toolbarFolder));
#endif

                rv |= ParseBookmarksFolder(children,
                                           toolbarFolder,
                                           aBookmarksService,
                                           PR_FALSE);
            }
            // Look for the BookmarksMenu Bookmarks and flatten them into the top level
            else if (title.EqualsLiteral("BookmarksMenu") && aIsAtRootLevel) {
                rv |= ParseBookmarksFolder(children,
#ifdef MOZ_PLACES
                                           aParentFolder,
#else
                                           aParentResource,
#endif
                                           aBookmarksService,
                                           PR_TRUE);
            }
            else {
                // Encountered a Folder, so create one in our Bookmarks DataSource and then
                // parse the contents of the Safari one into it...
#ifdef MOZ_PLACES
                PRInt64 folder;
                rv |= aBookmarksService->CreateFolder(aParentFolder,
                                                      title, -1, &folder);
#else
                nsCOMPtr<nsIRDFResource> folder;
                rv |= aBookmarksService->CreateFolderInContainer(title.get(),
                        aParentResource,
                        -1,
                        getter_AddRefs(folder));
#endif
                rv |= ParseBookmarksFolder(children,
                                           folder,
                                           aBookmarksService,
                                           PR_FALSE);
            }
        }
        else if (type.EqualsLiteral("WebBookmarkTypeLeaf")) {
            // Encountered a Bookmark, so add it to the current folder...
            CFDictionaryRef URIDictionary = (CFDictionaryRef)
                                            ::CFDictionaryGetValue(entry, CFSTR("URIDictionary"));
            nsAutoString title, url;
            if (GetDictionaryStringValue(URIDictionary, CFSTR("title"), title) &&
                    GetDictionaryStringValue(entry, CFSTR("URLString"), url)) {
#ifdef MOZ_PLACES
                nsCOMPtr<nsIURI> uri;
                rv |= NS_NewURI(getter_AddRefs(uri), url);
                rv |= aBookmarksService->InsertItem(aParentFolder, uri, -1);
                rv |= aBookmarksService->SetItemTitle(uri, title);
#else
                nsCOMPtr<nsIRDFResource> bookmark;
                rv |= aBookmarksService->CreateBookmarkInContainer(title.get(),
                        url.get(),
                        nsnull,
                        nsnull,
                        nsnull,
                        nsnull,
                        aParentResource,
                        -1,
                        getter_AddRefs(bookmark));
#endif
            }
        }
    }
    return rv;
}
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;
}