Back to index

lightning-sunbird  0.9+nobinonly
nsBookmarksService.cpp
Go to the documentation of this file.
00001 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
00002 /* ***** BEGIN LICENSE BLOCK *****
00003  * Version: MPL 1.1/GPL 2.0/LGPL 2.1
00004  *
00005  * The contents of this file are subject to the Mozilla Public License Version
00006  * 1.1 (the "License"); you may not use this file except in compliance with
00007  * the License. You may obtain a copy of the License at
00008  * http://www.mozilla.org/MPL/
00009  *
00010  * Software distributed under the License is distributed on an "AS IS" basis,
00011  * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
00012  * for the specific language governing rights and limitations under the
00013  * License.
00014  *
00015  * The Original Code is mozilla.org code.
00016  *
00017  * The Initial Developer of the Original Code is
00018  * Netscape Communications Corporation.
00019  * Portions created by the Initial Developer are Copyright (C) 1998
00020  * the Initial Developer. All Rights Reserved.
00021  *
00022  * Contributor(s):
00023  *   Robert John Churchill    <rjc@netscape.com>
00024  *   Chris Waterson           <waterson@netscape.com>
00025  *   David Hyatt              <hyatt@netscape.com>
00026  *   Ben Goodger              <ben@netscape.com>
00027  *   Andreas Otte             <andreas.otte@debitel.net>
00028  *   Jan Varga                <varga@ku.sk>
00029  *   Benjamin Smedberg        <bsmedberg@covad.net>
00030  *
00031  * Alternatively, the contents of this file may be used under the terms of
00032  * either of the GNU General Public License Version 2 or later (the "GPL"),
00033  * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
00034  * in which case the provisions of the GPL or the LGPL are applicable instead
00035  * of those above. If you wish to allow use of your version of this file only
00036  * under the terms of either the GPL or the LGPL, and not to allow others to
00037  * use your version of this file under the terms of the MPL, indicate your
00038  * decision by deleting the provisions above and replace them with the notice
00039  * and other provisions required by the GPL or the LGPL. If you do not delete
00040  * the provisions above, a recipient may use your version of this file under
00041  * the terms of any one of the MPL, the GPL or the LGPL.
00042  *
00043  * ***** END LICENSE BLOCK ***** */
00044 
00045 /*
00046   The global bookmarks service.
00047  */
00048 
00049 #include "nsBookmarksService.h"
00050 #include "nsIDOMWindow.h"
00051 #include "nsIObserverService.h"
00052 #include "nsIProfile.h"
00053 #include "nsIRDFContainer.h"
00054 #include "nsIRDFContainerUtils.h"
00055 #include "nsIRDFService.h"
00056 #include "nsIRDFXMLSerializer.h"
00057 #include "nsIRDFXMLSource.h"
00058 #include "nsRDFCID.h"
00059 #include "nsISupportsPrimitives.h"
00060 #include "rdf.h"
00061 #include "nsCRT.h"
00062 #include "nsEnumeratorUtils.h"
00063 #include "nsEscape.h"
00064 #include "nsAppDirectoryServiceDefs.h"
00065 #include "nsDirectoryServiceDefs.h"
00066 #include "nsUnicharUtils.h"
00067 #include "nsICmdLineHandler.h"
00068 
00069 #include "nsISound.h"
00070 #include "nsIPrompt.h"
00071 #include "nsIWindowWatcher.h"
00072 #include "nsWidgetsCID.h"
00073 
00074 #include "nsIURL.h"
00075 #include "nsIFileURL.h"
00076 #include "nsIFile.h"
00077 #include "nsIInputStream.h"
00078 #include "nsILineInputStream.h"
00079 #include "nsIOutputStream.h"
00080 #include "nsNetUtil.h"
00081 #include "nsICacheEntryDescriptor.h"
00082 
00083 #include "nsICharsetConverterManager.h"
00084 #include "nsICharsetAlias.h"
00085 #include "nsIPlatformCharset.h"
00086 #include "nsIPrefService.h"
00087 #include "nsIPrefBranch.h"
00088 #include "nsIPrefBranch2.h"
00089 
00090 #include "nsIWebNavigation.h"
00091 
00092 // for sorting
00093 #include "nsCollationCID.h"
00094 #include "nsILocaleService.h"
00095 #include "nsICollation.h"
00096 #include "nsVoidArray.h"
00097 #include "nsUnicharUtils.h"
00098 #include "nsAutoBuffer.h"
00099 
00100 
00101 #ifdef XP_WIN
00102 #include <shlobj.h>
00103 #include <intshcut.h>
00104 #ifdef CompareString
00105 #undef CompareString
00106 #endif
00107 #endif
00108 
00109 nsIRDFResource      *kNC_IEFavoritesRoot;
00110 nsIRDFResource      *kNC_SystemBookmarksStaticRoot;
00111 nsIRDFResource      *kNC_Bookmark;
00112 nsIRDFResource      *kNC_BookmarkSeparator;
00113 nsIRDFResource      *kNC_BookmarkAddDate;
00114 nsIRDFResource      *kNC_BookmarksTopRoot;
00115 nsIRDFResource      *kNC_BookmarksRoot;
00116 nsIRDFResource      *kNC_Description;
00117 nsIRDFResource      *kNC_Folder;
00118 nsIRDFResource      *kNC_FolderType;
00119 nsIRDFResource      *kNC_FolderGroup;
00120 nsIRDFResource      *kNC_IEFavorite;
00121 nsIRDFResource      *kNC_IEFavoriteFolder;
00122 nsIRDFResource      *kNC_Name;
00123 nsIRDFResource      *kNC_Icon;
00124 nsIRDFResource      *kNC_NewBookmarkFolder;
00125 nsIRDFResource      *kNC_NewSearchFolder;
00126 nsIRDFResource      *kNC_PersonalToolbarFolder;
00127 nsIRDFResource      *kNC_ShortcutURL;
00128 nsIRDFResource      *kNC_URL;
00129 nsIRDFResource      *kRDF_type;
00130 nsIRDFResource      *kRDF_nextVal;
00131 nsIRDFResource      *kWEB_LastModifiedDate;
00132 nsIRDFResource      *kWEB_LastVisitDate;
00133 nsIRDFResource      *kWEB_Schedule;
00134 nsIRDFResource      *kWEB_ScheduleActive;
00135 nsIRDFResource      *kWEB_Status;
00136 nsIRDFResource      *kWEB_LastPingDate;
00137 nsIRDFResource      *kWEB_LastPingETag;
00138 nsIRDFResource      *kWEB_LastPingModDate;
00139 nsIRDFResource      *kWEB_LastCharset;
00140 nsIRDFResource      *kWEB_LastPingContentLen;
00141 nsIRDFLiteral       *kTrueLiteral;
00142 nsIRDFLiteral       *kEmptyLiteral;
00143 nsIRDFDate          *kEmptyDate;
00144 nsIRDFResource      *kNC_Parent;
00145 nsIRDFResource      *kNC_BookmarkCommand_NewBookmark;
00146 nsIRDFResource      *kNC_BookmarkCommand_NewFolder;
00147 nsIRDFResource      *kNC_BookmarkCommand_NewSeparator;
00148 nsIRDFResource      *kNC_BookmarkCommand_DeleteBookmark;
00149 nsIRDFResource      *kNC_BookmarkCommand_DeleteBookmarkFolder;
00150 nsIRDFResource      *kNC_BookmarkCommand_DeleteBookmarkSeparator;
00151 nsIRDFResource      *kNC_BookmarkCommand_SetNewBookmarkFolder = nsnull;
00152 nsIRDFResource      *kNC_BookmarkCommand_SetPersonalToolbarFolder;
00153 nsIRDFResource      *kNC_BookmarkCommand_SetNewSearchFolder;
00154 nsIRDFResource      *kNC_BookmarkCommand_Import;
00155 nsIRDFResource      *kNC_BookmarkCommand_Export;
00156 
00157 #define BOOKMARK_TIMEOUT        15000       // fire every 15 seconds
00158 // #define  DEBUG_BOOKMARK_PING_OUTPUT  1
00159 
00161 
00162 static NS_DEFINE_CID(kRDFInMemoryDataSourceCID,   NS_RDFINMEMORYDATASOURCE_CID);
00163 static NS_DEFINE_CID(kRDFServiceCID,              NS_RDFSERVICE_CID);
00164 static NS_DEFINE_CID(kRDFContainerCID,            NS_RDFCONTAINER_CID);
00165 static NS_DEFINE_CID(kRDFContainerUtilsCID,       NS_RDFCONTAINERUTILS_CID);
00166 static NS_DEFINE_CID(kIOServiceCID,               NS_IOSERVICE_CID);
00167 static NS_DEFINE_CID(kCharsetConverterManagerCID, NS_ICHARSETCONVERTERMANAGER_CID);
00168 static NS_DEFINE_CID(kStringBundleServiceCID,     NS_STRINGBUNDLESERVICE_CID);
00169 static NS_DEFINE_CID(kPlatformCharsetCID,         NS_PLATFORMCHARSET_CID);
00170 static NS_DEFINE_CID(kCacheServiceCID,            NS_CACHESERVICE_CID);
00171 static NS_DEFINE_CID(kCharsetAliasCID,            NS_CHARSETALIAS_CID);
00172 static NS_DEFINE_CID(kCollationFactoryCID,        NS_COLLATIONFACTORY_CID);
00173 
00174 #define URINC_BOOKMARKS_TOPROOT_STRING            "NC:BookmarksTopRoot"
00175 #define URINC_BOOKMARKS_ROOT_STRING               "NC:BookmarksRoot"
00176 
00177 static const char kURINC_BookmarksTopRoot[]           = URINC_BOOKMARKS_TOPROOT_STRING; 
00178 static const char kURINC_BookmarksRoot[]              = URINC_BOOKMARKS_ROOT_STRING; 
00179 static const char kURINC_IEFavoritesRoot[]            = "NC:IEFavoritesRoot"; 
00180 static const char kURINC_SystemBookmarksStaticRoot[]  = "NC:SystemBookmarksStaticRoot"; 
00181 static const char kURINC_NewBookmarkFolder[]          = "NC:NewBookmarkFolder"; 
00182 static const char kURINC_PersonalToolbarFolder[]      = "NC:PersonalToolbarFolder"; 
00183 static const char kURINC_NewSearchFolder[]            = "NC:NewSearchFolder"; 
00184 static const char kBookmarkCommand[]                  = "http://home.netscape.com/NC-rdf#command?";
00185 
00186 #define bookmark_properties NS_LITERAL_CSTRING("chrome://communicator/locale/bookmarks/bookmarks.properties")
00187 
00189 
00190 PRInt32               gRefCnt=0;
00191 nsIRDFService        *gRDF;
00192 nsIRDFContainerUtils *gRDFC;
00193 nsICharsetAlias      *gCharsetAlias;
00194 nsICollation         *gCollation;
00195 PRBool                gImportedSystemBookmarks = PR_FALSE;
00196 
00197 static nsresult
00198 bm_AddRefGlobals()
00199 {
00200     if (gRefCnt++ == 0)
00201     {
00202         nsresult rv;
00203         rv = CallGetService(kRDFServiceCID, &gRDF);
00204         if (NS_FAILED(rv)) {
00205             NS_ERROR("unable to get RDF service");
00206             return rv;
00207         }
00208 
00209         rv = CallGetService(kRDFContainerUtilsCID, &gRDFC);
00210         if (NS_FAILED(rv)) {
00211             NS_ERROR("unable to get RDF container utils");
00212             return rv;
00213         }
00214 
00215         rv = CallGetService(kCharsetAliasCID, &gCharsetAlias);
00216         if (NS_FAILED(rv)) {
00217             NS_ERROR("unable to get charset alias service");
00218             return rv;
00219         }
00220 
00221         nsCOMPtr<nsILocaleService> ls = do_GetService(NS_LOCALESERVICE_CONTRACTID);
00222         if (ls) {
00223             nsCOMPtr<nsILocale> locale;
00224             ls->GetApplicationLocale(getter_AddRefs(locale));
00225             if (locale) {
00226                 nsCOMPtr<nsICollationFactory> factory = do_CreateInstance(kCollationFactoryCID);
00227                 if (factory) {
00228                     factory->CreateCollation(locale, &gCollation);
00229                 }
00230             }
00231         }
00232 
00233         gRDF->GetResource(NS_LITERAL_CSTRING(kURINC_BookmarksTopRoot),
00234                           &kNC_BookmarksTopRoot);
00235         gRDF->GetResource(NS_LITERAL_CSTRING(kURINC_BookmarksRoot),
00236                           &kNC_BookmarksRoot);
00237         gRDF->GetResource(NS_LITERAL_CSTRING(kURINC_IEFavoritesRoot),
00238                           &kNC_IEFavoritesRoot);
00239         gRDF->GetResource(NS_LITERAL_CSTRING(kURINC_SystemBookmarksStaticRoot),
00240                           &kNC_SystemBookmarksStaticRoot);
00241         gRDF->GetResource(NS_LITERAL_CSTRING(kURINC_NewBookmarkFolder),
00242                           &kNC_NewBookmarkFolder);
00243         gRDF->GetResource(NS_LITERAL_CSTRING(kURINC_PersonalToolbarFolder),
00244                           &kNC_PersonalToolbarFolder);
00245         gRDF->GetResource(NS_LITERAL_CSTRING(kURINC_NewSearchFolder),
00246                           &kNC_NewSearchFolder);
00247 
00248         gRDF->GetResource(NS_LITERAL_CSTRING(NC_NAMESPACE_URI "Bookmark"),
00249                           &kNC_Bookmark);
00250         gRDF->GetResource(NS_LITERAL_CSTRING(NC_NAMESPACE_URI "BookmarkSeparator"),
00251                           &kNC_BookmarkSeparator);
00252         gRDF->GetResource(NS_LITERAL_CSTRING(NC_NAMESPACE_URI "BookmarkAddDate"),
00253                           &kNC_BookmarkAddDate);
00254         gRDF->GetResource(NS_LITERAL_CSTRING(NC_NAMESPACE_URI "Description"),
00255                           &kNC_Description);
00256         gRDF->GetResource(NS_LITERAL_CSTRING(NC_NAMESPACE_URI "Folder"),
00257                           &kNC_Folder);
00258         gRDF->GetResource(NS_LITERAL_CSTRING(NC_NAMESPACE_URI "FolderType"),
00259                           &kNC_FolderType);
00260         gRDF->GetResource(NS_LITERAL_CSTRING(NC_NAMESPACE_URI "FolderGroup"),
00261                           &kNC_FolderGroup);
00262         gRDF->GetResource(NS_LITERAL_CSTRING(NC_NAMESPACE_URI "IEFavorite"),
00263                           &kNC_IEFavorite);
00264         gRDF->GetResource(NS_LITERAL_CSTRING(NC_NAMESPACE_URI "IEFavoriteFolder"),
00265                           &kNC_IEFavoriteFolder);
00266         gRDF->GetResource(NS_LITERAL_CSTRING(NC_NAMESPACE_URI "Name"),
00267                           &kNC_Name);
00268         gRDF->GetResource(NS_LITERAL_CSTRING(NC_NAMESPACE_URI "Icon"),
00269                           &kNC_Icon);
00270         gRDF->GetResource(NS_LITERAL_CSTRING(NC_NAMESPACE_URI "ShortcutURL"),
00271                           &kNC_ShortcutURL);
00272         gRDF->GetResource(NS_LITERAL_CSTRING(NC_NAMESPACE_URI "URL"),
00273                           &kNC_URL);
00274         gRDF->GetResource(NS_LITERAL_CSTRING(RDF_NAMESPACE_URI "type"),
00275                           &kRDF_type);
00276         gRDF->GetResource(NS_LITERAL_CSTRING(RDF_NAMESPACE_URI "nextVal"),
00277                           &kRDF_nextVal);
00278 
00279         gRDF->GetResource(NS_LITERAL_CSTRING(WEB_NAMESPACE_URI "LastModifiedDate"),
00280                           &kWEB_LastModifiedDate);
00281         gRDF->GetResource(NS_LITERAL_CSTRING(WEB_NAMESPACE_URI "LastVisitDate"),
00282                           &kWEB_LastVisitDate);
00283         gRDF->GetResource(NS_LITERAL_CSTRING(WEB_NAMESPACE_URI "LastCharset"),
00284                           &kWEB_LastCharset);
00285 
00286         gRDF->GetResource(NS_LITERAL_CSTRING(WEB_NAMESPACE_URI "Schedule"),
00287                           &kWEB_Schedule);
00288         gRDF->GetResource(NS_LITERAL_CSTRING(WEB_NAMESPACE_URI "ScheduleFlag"),
00289                           &kWEB_ScheduleActive);
00290         gRDF->GetResource(NS_LITERAL_CSTRING(WEB_NAMESPACE_URI "status"),
00291                           &kWEB_Status);
00292         gRDF->GetResource(NS_LITERAL_CSTRING(WEB_NAMESPACE_URI "LastPingDate"),
00293                           &kWEB_LastPingDate);
00294         gRDF->GetResource(NS_LITERAL_CSTRING(WEB_NAMESPACE_URI "LastPingETag"),
00295                           &kWEB_LastPingETag);
00296         gRDF->GetResource(NS_LITERAL_CSTRING(WEB_NAMESPACE_URI "LastPingModDate"),
00297                           &kWEB_LastPingModDate);
00298         gRDF->GetResource(NS_LITERAL_CSTRING(WEB_NAMESPACE_URI "LastPingContentLen"),
00299                           &kWEB_LastPingContentLen);
00300 
00301         gRDF->GetResource(NS_LITERAL_CSTRING(NC_NAMESPACE_URI "parent"), &kNC_Parent);
00302 
00303         gRDF->GetLiteral(NS_LITERAL_STRING("true").get(), &kTrueLiteral);
00304 
00305         gRDF->GetLiteral(EmptyString().get(), &kEmptyLiteral);
00306 
00307         gRDF->GetDateLiteral(0, &kEmptyDate);
00308 
00309         gRDF->GetResource(NS_LITERAL_CSTRING(NC_NAMESPACE_URI "command?cmd=newbookmark"),
00310                           &kNC_BookmarkCommand_NewBookmark);
00311         gRDF->GetResource(NS_LITERAL_CSTRING(NC_NAMESPACE_URI "command?cmd=newfolder"),
00312                           &kNC_BookmarkCommand_NewFolder);
00313         gRDF->GetResource(NS_LITERAL_CSTRING(NC_NAMESPACE_URI "command?cmd=newseparator"),
00314                           &kNC_BookmarkCommand_NewSeparator);
00315         gRDF->GetResource(NS_LITERAL_CSTRING(NC_NAMESPACE_URI "command?cmd=deletebookmark"),
00316                           &kNC_BookmarkCommand_DeleteBookmark);
00317         gRDF->GetResource(NS_LITERAL_CSTRING(NC_NAMESPACE_URI "command?cmd=deletebookmarkfolder"),
00318                           &kNC_BookmarkCommand_DeleteBookmarkFolder);
00319         gRDF->GetResource(NS_LITERAL_CSTRING(NC_NAMESPACE_URI "command?cmd=deletebookmarkseparator"),
00320                           &kNC_BookmarkCommand_DeleteBookmarkSeparator);
00321         gRDF->GetResource(NS_LITERAL_CSTRING(NC_NAMESPACE_URI "command?cmd=setnewbookmarkfolder"),
00322                           &kNC_BookmarkCommand_SetNewBookmarkFolder);
00323         gRDF->GetResource(NS_LITERAL_CSTRING(NC_NAMESPACE_URI "command?cmd=setpersonaltoolbarfolder"),
00324                           &kNC_BookmarkCommand_SetPersonalToolbarFolder);
00325         gRDF->GetResource(NS_LITERAL_CSTRING(NC_NAMESPACE_URI "command?cmd=setnewsearchfolder"),
00326                           &kNC_BookmarkCommand_SetNewSearchFolder);
00327         gRDF->GetResource(NS_LITERAL_CSTRING(NC_NAMESPACE_URI "command?cmd=import"),
00328                           &kNC_BookmarkCommand_Import);
00329         gRDF->GetResource(NS_LITERAL_CSTRING(NC_NAMESPACE_URI "command?cmd=export"),
00330                           &kNC_BookmarkCommand_Export);
00331     }
00332     return NS_OK;
00333 }
00334 
00335 
00336 
00337 static void
00338 bm_ReleaseGlobals()
00339 {
00340     if (--gRefCnt == 0)
00341     {
00342         NS_IF_RELEASE(gRDF);
00343         NS_IF_RELEASE(gRDFC);
00344         NS_IF_RELEASE(gCharsetAlias);
00345 
00346         NS_IF_RELEASE(gCollation);
00347 
00348         NS_IF_RELEASE(kNC_Bookmark);
00349         NS_IF_RELEASE(kNC_BookmarkSeparator);
00350         NS_IF_RELEASE(kNC_BookmarkAddDate);
00351         NS_IF_RELEASE(kNC_BookmarksTopRoot);
00352         NS_IF_RELEASE(kNC_BookmarksRoot);
00353         NS_IF_RELEASE(kNC_Description);
00354         NS_IF_RELEASE(kNC_Folder);
00355         NS_IF_RELEASE(kNC_FolderType);
00356         NS_IF_RELEASE(kNC_FolderGroup);
00357         NS_IF_RELEASE(kNC_IEFavorite);
00358         NS_IF_RELEASE(kNC_IEFavoriteFolder);
00359         NS_IF_RELEASE(kNC_IEFavoritesRoot);
00360         NS_IF_RELEASE(kNC_SystemBookmarksStaticRoot);
00361         NS_IF_RELEASE(kNC_Name);
00362         NS_IF_RELEASE(kNC_Icon);
00363         NS_IF_RELEASE(kNC_NewBookmarkFolder);
00364         NS_IF_RELEASE(kNC_NewSearchFolder);
00365         NS_IF_RELEASE(kNC_PersonalToolbarFolder);
00366         NS_IF_RELEASE(kNC_ShortcutURL);
00367         NS_IF_RELEASE(kNC_URL);
00368         NS_IF_RELEASE(kRDF_type);
00369         NS_IF_RELEASE(kRDF_nextVal);
00370         NS_IF_RELEASE(kWEB_LastModifiedDate);
00371         NS_IF_RELEASE(kWEB_LastVisitDate);
00372         NS_IF_RELEASE(kNC_Parent);
00373         NS_IF_RELEASE(kTrueLiteral);
00374         NS_IF_RELEASE(kEmptyLiteral);
00375         NS_IF_RELEASE(kEmptyDate);
00376         NS_IF_RELEASE(kWEB_Schedule);
00377         NS_IF_RELEASE(kWEB_ScheduleActive);
00378         NS_IF_RELEASE(kWEB_Status);
00379         NS_IF_RELEASE(kWEB_LastPingDate);
00380         NS_IF_RELEASE(kWEB_LastPingETag);
00381         NS_IF_RELEASE(kWEB_LastPingModDate);
00382         NS_IF_RELEASE(kWEB_LastPingContentLen);
00383         NS_IF_RELEASE(kWEB_LastCharset);
00384         NS_IF_RELEASE(kNC_BookmarkCommand_NewBookmark);
00385         NS_IF_RELEASE(kNC_BookmarkCommand_NewFolder);
00386         NS_IF_RELEASE(kNC_BookmarkCommand_NewSeparator);
00387         NS_IF_RELEASE(kNC_BookmarkCommand_DeleteBookmark);
00388         NS_IF_RELEASE(kNC_BookmarkCommand_DeleteBookmarkFolder);
00389         NS_IF_RELEASE(kNC_BookmarkCommand_DeleteBookmarkSeparator);
00390         NS_IF_RELEASE(kNC_BookmarkCommand_SetNewBookmarkFolder);
00391         NS_IF_RELEASE(kNC_BookmarkCommand_SetPersonalToolbarFolder);
00392         NS_IF_RELEASE(kNC_BookmarkCommand_SetNewSearchFolder);
00393         NS_IF_RELEASE(kNC_BookmarkCommand_Import);
00394         NS_IF_RELEASE(kNC_BookmarkCommand_Export);
00395     }
00396 }
00397 
00399 
00404 class BookmarkParser {
00405 private:
00406     nsCOMPtr<nsIUnicodeDecoder> mUnicodeDecoder;
00407     nsIRDFDataSource*           mDataSource;
00408     nsCString                   mIEFavoritesRoot;
00409     PRBool                      mFoundIEFavoritesRoot;
00410     PRBool                      mFoundPersonalToolbarFolder;
00411     PRBool                      mIsImportOperation;
00412     char*                       mContents;
00413     PRUint32                    mContentsLen;
00414     PRInt32                     mStartOffset;
00415     nsCOMPtr<nsIInputStream>    mInputStream;
00416 
00417     friend  class nsBookmarksService;
00418 
00419 protected:
00420     struct BookmarkField
00421     {
00422         const char      *mName;
00423         const char      *mPropertyName;
00424         nsIRDFResource  *mProperty;
00425         nsresult        (*mParse)(nsIRDFResource *arc, nsString& aValue, nsIRDFNode** aResult);
00426         nsIRDFNode      *mValue;
00427     };
00428 
00429     static BookmarkField gBookmarkFieldTable[];
00430     static BookmarkField gBookmarkHeaderFieldTable[];
00431 
00432     nsresult AssertTime(nsIRDFResource* aSource,
00433                         nsIRDFResource* aLabel,
00434                         PRInt32 aTime);
00435 
00436     nsresult setFolderHint(nsIRDFResource *newSource, nsIRDFResource *objType);
00437 
00438     nsresult Unescape(nsString &text);
00439 
00440     nsresult ParseMetaTag(const nsString &aLine, nsIUnicodeDecoder **decoder);
00441 
00442     nsresult ParseBookmarkInfo(BookmarkField *fields, PRBool isBookmarkFlag, const nsString &aLine,
00443                                const nsCOMPtr<nsIRDFContainer> &aContainer,
00444                                nsIRDFResource *nodeType, nsCOMPtr<nsIRDFResource> &bookmarkNode);
00445 
00446     nsresult ParseBookmarkSeparator(const nsString &aLine,
00447                                     const nsCOMPtr<nsIRDFContainer> &aContainer);
00448 
00449     nsresult ParseHeaderBegin(const nsString &aLine,
00450                               const nsCOMPtr<nsIRDFContainer> &aContainer);
00451 
00452     nsresult ParseHeaderEnd(const nsString &aLine);
00453 
00454     PRInt32 getEOL(const char *whole, PRInt32 startOffset, PRInt32 totalLength);
00455 
00456     nsresult updateAtom(nsIRDFDataSource *db, nsIRDFResource *src,
00457                         nsIRDFResource *prop, nsIRDFNode *newValue, PRBool *dirtyFlag);
00458 
00459     static    nsresult ParseResource(nsIRDFResource *arc, nsString& aValue, nsIRDFNode** aResult);
00460     static    nsresult ParseLiteral(nsIRDFResource *arc, nsString& aValue, nsIRDFNode** aResult);
00461     static    nsresult ParseDate(nsIRDFResource *arc, nsString& aValue, nsIRDFNode** aResult);
00462 
00463 public:
00464     BookmarkParser();
00465     ~BookmarkParser();
00466 
00467     nsresult Init(nsIFile *aFile, nsIRDFDataSource *aDataSource, 
00468                   PRBool aIsImportOperation = PR_FALSE);
00469     nsresult DecodeBuffer(nsString &line, char *buf, PRUint32 aLength);
00470     nsresult ProcessLine(nsIRDFContainer *aContainer, nsIRDFResource *nodeType,
00471                          nsCOMPtr<nsIRDFResource> &bookmarkNode, const nsString &line,
00472                          nsString &description, PRBool &inDescription, PRBool &isActiveFlag);
00473     nsresult Parse(nsIRDFResource* aContainer, nsIRDFResource *nodeType);
00474 
00475     nsresult SetIEFavoritesRoot(const nsCString& IEFavoritesRootURL)
00476     {
00477         mIEFavoritesRoot = IEFavoritesRootURL;
00478         return NS_OK;
00479     }
00480     nsresult ParserFoundIEFavoritesRoot(PRBool *foundIEFavoritesRoot)
00481     {
00482         *foundIEFavoritesRoot = mFoundIEFavoritesRoot;
00483         return NS_OK;
00484     }
00485     nsresult ParserFoundPersonalToolbarFolder(PRBool *foundPersonalToolbarFolder)
00486     {
00487         *foundPersonalToolbarFolder = mFoundPersonalToolbarFolder;
00488         return NS_OK;
00489     }
00490 };
00491 
00492 BookmarkParser::BookmarkParser() :
00493     mContents(nsnull),
00494     mContentsLen(0L),
00495     mStartOffset(0L)
00496 {
00497     bm_AddRefGlobals();
00498 }
00499 
00500 static const char kOpenAnchor[]    = "<A ";
00501 static const char kCloseAnchor[]   = "</A>";
00502 
00503 static const char kOpenHeading[]   = "<H";
00504 static const char kCloseHeading[]  = "</H";
00505 
00506 static const char kSeparator[]     = "<HR";
00507 
00508 static const char kOpenUL[]        = "<UL>";
00509 static const char kCloseUL[]       = "</UL>";
00510 
00511 static const char kOpenMenu[]      = "<MENU>";
00512 static const char kCloseMenu[]     = "</MENU>";
00513 
00514 static const char kOpenDL[]        = "<DL>";
00515 static const char kCloseDL[]       = "</DL>";
00516 
00517 static const char kOpenDD[]        = "<DD>";
00518 
00519 static const char kOpenMeta[]      = "<META ";
00520 
00521 static const char kNewBookmarkFolderEquals[]      = "NEW_BOOKMARK_FOLDER=\"";
00522 static const char kNewSearchFolderEquals[]        = "NEW_SEARCH_FOLDER=\"";
00523 static const char kPersonalToolbarFolderEquals[]  = "PERSONAL_TOOLBAR_FOLDER=\"";
00524 
00525 static const char kNameEquals[]            = "NAME=\"";
00526 static const char kHREFEquals[]            = "HREF=\"";
00527 static const char kTargetEquals[]          = "TARGET=\"";
00528 static const char kAddDateEquals[]         = "ADD_DATE=\"";
00529 static const char kFolderGroupEquals[]     = "FOLDER_GROUP=\"";
00530 static const char kLastVisitEquals[]       = "LAST_VISIT=\"";
00531 static const char kLastModifiedEquals[]    = "LAST_MODIFIED=\"";
00532 static const char kLastCharsetEquals[]     = "LAST_CHARSET=\"";
00533 static const char kShortcutURLEquals[]     = "SHORTCUTURL=\"";
00534 static const char kIconEquals[]            = "ICON=\"";
00535 static const char kScheduleEquals[]        = "SCHEDULE=\"";
00536 static const char kLastPingEquals[]        = "LAST_PING=\"";
00537 static const char kPingETagEquals[]        = "PING_ETAG=\"";
00538 static const char kPingLastModEquals[]     = "PING_LAST_MODIFIED=\"";
00539 static const char kPingContentLenEquals[]  = "PING_CONTENT_LEN=\"";
00540 static const char kPingStatusEquals[]      = "PING_STATUS=\"";
00541 static const char kIDEquals[]              = "ID=\"";
00542 static const char kContentEquals[]         = "CONTENT=\"";
00543 static const char kHTTPEquivEquals[]       = "HTTP-EQUIV=\"";
00544 static const char kCharsetEquals[]         = "charset=";        // note: no quote
00545 
00546 nsresult
00547 BookmarkParser::Init(nsIFile *aFile, nsIRDFDataSource *aDataSource, 
00548                      PRBool aIsImportOperation)
00549 {
00550     mDataSource = aDataSource;
00551     mFoundIEFavoritesRoot = PR_FALSE;
00552     mFoundPersonalToolbarFolder = PR_FALSE;
00553     mIsImportOperation = aIsImportOperation;
00554 
00555     nsresult rv;
00556 
00557     // determine default platform charset...
00558     nsCOMPtr<nsIPlatformCharset> platformCharset = 
00559         do_GetService(kPlatformCharsetCID, &rv);
00560     if (NS_SUCCEEDED(rv) && (platformCharset))
00561     {
00562         nsCAutoString    defaultCharset;
00563         if (NS_SUCCEEDED(rv = platformCharset->GetCharset(kPlatformCharsetSel_4xBookmarkFile, defaultCharset)))
00564         {
00565             // found the default platform charset, now try and get a decoder from it to Unicode
00566             nsCOMPtr<nsICharsetConverterManager> charsetConv = 
00567                 do_GetService(kCharsetConverterManagerCID, &rv);
00568             if (NS_SUCCEEDED(rv) && (charsetConv))
00569             {
00570                 rv = charsetConv->GetUnicodeDecoderRaw(defaultCharset.get(),
00571                                                        getter_AddRefs(mUnicodeDecoder));
00572             }
00573         }
00574     }
00575 
00576     nsCAutoString   str;
00577     BookmarkField   *field;
00578     for (field = gBookmarkFieldTable; field->mName; ++field)
00579     {
00580         str = field->mPropertyName;
00581         rv = gRDF->GetResource(str, &field->mProperty);
00582         if (NS_FAILED(rv))  return rv;
00583     }
00584     for (field = gBookmarkHeaderFieldTable; field->mName; ++field)
00585     {
00586         str = field->mPropertyName;
00587         rv = gRDF->GetResource(str, &field->mProperty);
00588         if (NS_FAILED(rv))  return rv;
00589     }
00590 
00591     if (aFile)
00592     {
00593         PRInt64 contentsLen;
00594         rv = aFile->GetFileSize(&contentsLen);
00595         NS_ENSURE_SUCCESS(rv, rv);
00596 
00597         if(LL_CMP(contentsLen, >, LL_INIT(0,0xFFFFFFFE)))
00598             return NS_ERROR_FILE_TOO_BIG;
00599 
00600         LL_L2UI(mContentsLen, contentsLen);
00601 
00602         if (mContentsLen > 0)
00603         {
00604             mContents = new char [mContentsLen + 1];
00605             if (mContents)
00606             {
00607                 nsCOMPtr<nsIInputStream> inputStream;
00608                 rv = NS_NewLocalFileInputStream(getter_AddRefs(inputStream),
00609                                                 aFile,
00610                                                 PR_RDONLY,
00611                                                 -1, 0);
00612 
00613                 if (NS_FAILED(rv))
00614                 {
00615                     delete [] mContents;
00616                     mContents = nsnull;
00617                 }
00618                 else
00619                 {
00620                     PRUint32 howMany;
00621                     rv = inputStream->Read(mContents, mContentsLen, &howMany);
00622                     if (NS_FAILED(rv))
00623                     {
00624                         delete [] mContents;
00625                         mContents = nsnull;
00626                         return NS_OK;
00627                     }
00628 
00629                     if (howMany == mContentsLen)
00630                     {
00631                         mContents[mContentsLen] = '\0';
00632                     }
00633                     else
00634                     {
00635                         delete [] mContents;
00636                         mContents = nsnull;
00637                     }
00638                 }                    
00639             }
00640         }
00641 
00642         if (!mContents)
00643         {
00644             // we were unable to read in the entire bookmark file at once,
00645             // so let's try reading it in a bit at a time instead
00646 
00647             rv = NS_NewLocalFileInputStream(getter_AddRefs(mInputStream),
00648                                             aFile, PR_RDONLY, -1, 0);
00649             NS_ENSURE_SUCCESS(rv, rv);
00650         }
00651     }
00652 
00653     return NS_OK;
00654 }
00655 
00656 BookmarkParser::~BookmarkParser()
00657 {
00658     if (mContents)
00659     {
00660         delete [] mContents;
00661         mContents = nsnull;
00662     }
00663     if (mInputStream)
00664     {
00665         mInputStream->Close();
00666     }
00667     BookmarkField   *field;
00668     for (field = gBookmarkFieldTable; field->mName; ++field)
00669     {
00670         NS_IF_RELEASE(field->mProperty);
00671     }
00672     for (field = gBookmarkHeaderFieldTable; field->mName; ++field)
00673     {
00674         NS_IF_RELEASE(field->mProperty);
00675     }
00676     bm_ReleaseGlobals();
00677 }
00678 
00679 PRInt32
00680 BookmarkParser::getEOL(const char *whole, PRInt32 startOffset, PRInt32 totalLength)
00681 {
00682     PRInt32 eolOffset = -1;
00683 
00684     while (startOffset < totalLength)
00685     {
00686         char    c;
00687         c = whole[startOffset];
00688         if ((c == '\n') || (c == '\r') || (c == '\0'))
00689         {
00690             eolOffset = startOffset;
00691             break;
00692         }
00693         ++startOffset;
00694     }
00695     return eolOffset;
00696 }
00697 
00698 nsresult
00699 BookmarkParser::DecodeBuffer(nsString &line, char *buf, PRUint32 aLength)
00700 {
00701     if (mUnicodeDecoder)
00702     {
00703         nsresult    rv;
00704         char        *aBuffer = buf;
00705         PRInt32     unicharBufLen = 0;
00706         mUnicodeDecoder->GetMaxLength(aBuffer, aLength, &unicharBufLen);
00707         
00708         nsAutoBuffer<PRUnichar, 256> stackBuffer;
00709         if (!stackBuffer.EnsureElemCapacity(unicharBufLen + 1))
00710           return NS_ERROR_OUT_OF_MEMORY;
00711         
00712         do
00713         {
00714             PRInt32     srcLength = aLength;
00715             PRInt32     unicharLength = unicharBufLen;
00716             PRUnichar *unichars = stackBuffer.get();
00717             
00718             rv = mUnicodeDecoder->Convert(aBuffer, &srcLength, stackBuffer.get(), &unicharLength);
00719             unichars[unicharLength]=0;  //add this since the unicode converters can't be trusted to do so.
00720 
00721             // Move the nsParser.cpp 00 -> space hack to here so it won't break UCS2 file
00722 
00723             // Hack Start
00724             for(PRInt32 i=0;i<unicharLength-1; i++)
00725                 if(0x0000 == unichars[i])   unichars[i] = 0x0020;
00726             // Hack End
00727 
00728             line.Append(unichars, unicharLength);
00729             // if we failed, we consume one byte by replace it with U+FFFD
00730             // and try conversion again.
00731             if(NS_FAILED(rv))
00732             {
00733                 mUnicodeDecoder->Reset();
00734                 line.Append( (PRUnichar)0xFFFD);
00735                 if(((PRUint32) (srcLength + 1)) > (PRUint32)aLength)
00736                     srcLength = aLength;
00737                 else 
00738                     srcLength++;
00739                 aBuffer += srcLength;
00740                 aLength -= srcLength;
00741             }
00742         } while (NS_FAILED(rv) && (aLength > 0));
00743 
00744     }
00745     else
00746     {
00747         line.AppendWithConversion(buf, aLength);
00748     }
00749     return NS_OK;
00750 }
00751 
00752 
00753 
00754 nsresult
00755 BookmarkParser::ProcessLine(nsIRDFContainer *container, nsIRDFResource *nodeType,
00756             nsCOMPtr<nsIRDFResource> &bookmarkNode, const nsString &line,
00757             nsString &description, PRBool &inDescription, PRBool &isActiveFlag)
00758 {
00759     nsresult    rv = NS_OK;
00760     PRInt32     offset;
00761 
00762     if (inDescription == PR_TRUE)
00763     {
00764         offset = line.FindChar('<');
00765         if (offset < 0)
00766         {
00767             if (!description.IsEmpty())
00768             {
00769                 description.Append(PRUnichar('\n'));
00770             }
00771             description += line;
00772             return NS_OK;
00773         }
00774 
00775         Unescape(description);
00776 
00777         if (bookmarkNode)
00778         {
00779             nsCOMPtr<nsIRDFLiteral> descLiteral;
00780             if (NS_SUCCEEDED(rv = gRDF->GetLiteral(description.get(), getter_AddRefs(descLiteral))))
00781             {
00782                 rv = mDataSource->Assert(bookmarkNode, kNC_Description, descLiteral, PR_TRUE);
00783             }
00784         }
00785 
00786         inDescription = PR_FALSE;
00787         description.Truncate();
00788     }
00789 
00790     if ((offset = line.Find(kHREFEquals, PR_TRUE)) >= 0)
00791     {
00792         rv = ParseBookmarkInfo(gBookmarkFieldTable, PR_TRUE, line, container, nodeType, bookmarkNode);
00793     }
00794     else if ((offset = line.Find(kOpenMeta, PR_TRUE)) >= 0)
00795     {
00796         rv = ParseMetaTag(line, getter_AddRefs(mUnicodeDecoder));
00797     }
00798     else if ((offset = line.Find(kOpenHeading, PR_TRUE)) >= 0 &&
00799          nsCRT::IsAsciiDigit(line.CharAt(offset + 2)))
00800     {
00801         // XXX Ignore <H1> so that bookmarks root _is_ <H1>
00802         if (line.CharAt(offset + 2) != PRUnichar('1'))
00803         {
00804             nsCOMPtr<nsIRDFResource>    dummy;
00805             rv = ParseBookmarkInfo(gBookmarkHeaderFieldTable, PR_FALSE, line, container, nodeType, dummy);
00806         }
00807     }
00808     else if ((offset = line.Find(kSeparator, PR_TRUE)) >= 0)
00809     {
00810         rv = ParseBookmarkSeparator(line, container);
00811     }
00812     else if ((offset = line.Find(kCloseUL, PR_TRUE)) >= 0 ||
00813          (offset = line.Find(kCloseMenu, PR_TRUE)) >= 0 ||
00814          (offset = line.Find(kCloseDL, PR_TRUE)) >= 0)
00815     {
00816         isActiveFlag = PR_FALSE;
00817         return ParseHeaderEnd(line);
00818     }
00819     else if ((offset = line.Find(kOpenUL, PR_TRUE)) >= 0 ||
00820          (offset = line.Find(kOpenMenu, PR_TRUE)) >= 0 ||
00821          (offset = line.Find(kOpenDL, PR_TRUE)) >= 0)
00822     {
00823         rv = ParseHeaderBegin(line, container);
00824     }
00825     else if ((offset = line.Find(kOpenDD, PR_TRUE)) >= 0)
00826     {
00827         inDescription = PR_TRUE;
00828         description = line;
00829         description.Cut(0, offset+sizeof(kOpenDD)-1);
00830     }
00831     else
00832     {
00833         // XXX Discard the line?
00834     }
00835     return rv;
00836 }
00837 
00838 
00839 
00840 nsresult
00841 BookmarkParser::Parse(nsIRDFResource *aContainer, nsIRDFResource *nodeType)
00842 {
00843     // tokenize the input stream.
00844     // XXX this needs to handle quotes, etc. it'd be nice to use the real parser for this...
00845 
00846     nsresult rv;
00847     nsCOMPtr<nsIRDFContainer> container =
00848             do_CreateInstance(kRDFContainerCID, &rv);
00849     if (NS_FAILED(rv)) return rv;
00850 
00851     rv = container->Init(mDataSource, aContainer);
00852     if (NS_FAILED(rv)) return rv;
00853 
00854     nsCOMPtr<nsIRDFResource>    bookmarkNode = aContainer;
00855     nsAutoString            description, line;
00856     PRBool              isActiveFlag = PR_TRUE, inDescriptionFlag = PR_FALSE;
00857 
00858     if ((mContents) && (mContentsLen > 0))
00859     {
00860         // we were able to read the entire bookmark file into memory, so process it
00861         char                *linePtr;
00862         PRInt32             eol;
00863 
00864         while ((isActiveFlag == PR_TRUE) && (mStartOffset < (signed)mContentsLen))
00865         {
00866             linePtr = &mContents[mStartOffset];
00867             eol = getEOL(mContents, mStartOffset, mContentsLen);
00868 
00869             PRInt32 aLength;
00870 
00871             if ((eol >= mStartOffset) && (eol < (signed)mContentsLen))
00872             {
00873                 // mContents[eol] = '\0';
00874                 aLength = eol - mStartOffset;
00875                 mStartOffset = eol + 1;
00876             }
00877             else
00878             {
00879                 aLength = mContentsLen - mStartOffset;
00880                 mStartOffset = mContentsLen + 1;
00881                 isActiveFlag = PR_FALSE;
00882             }
00883             if (aLength < 1)    continue;
00884 
00885             line.Truncate();
00886             DecodeBuffer(line, linePtr, aLength);
00887 
00888             rv = ProcessLine(container, nodeType, bookmarkNode,
00889                 line, description, inDescriptionFlag, isActiveFlag);
00890             if (NS_FAILED(rv))  break;
00891         }
00892     }
00893     else
00894     {
00895         NS_ENSURE_TRUE(mInputStream, NS_ERROR_NULL_POINTER);
00896 
00897         // we were unable to read in the entire bookmark file at once,
00898         // so let's try reading it in a bit at a time instead, and process it
00899         nsCOMPtr<nsILineInputStream> lineInputStream =
00900             do_QueryInterface(mInputStream);
00901         NS_ENSURE_TRUE(lineInputStream, NS_NOINTERFACE);
00902 
00903         PRBool moreData = PR_TRUE;
00904 
00905         while(NS_SUCCEEDED(rv) && isActiveFlag && moreData)
00906         {
00907             nsCAutoString cLine;
00908             rv = lineInputStream->ReadLine(cLine, &moreData);
00909 
00910             if (NS_SUCCEEDED(rv))
00911             {
00912                 CopyASCIItoUTF16(cLine, line);
00913                 rv = ProcessLine(container, nodeType, bookmarkNode,
00914                     line, description, inDescriptionFlag, isActiveFlag);
00915             }
00916         }
00917     }
00918     return rv;
00919 }
00920 
00921 nsresult
00922 BookmarkParser::Unescape(nsString &text)
00923 {
00924     // convert some HTML-escaped (such as "&lt;") values back
00925 
00926     PRInt32     offset=0;
00927 
00928     while((offset = text.FindChar((PRUnichar('&')), offset)) >= 0)
00929     {
00930         if (Substring(text, offset, 4).LowerCaseEqualsLiteral("&lt;"))
00931         {
00932             text.Cut(offset, 4);
00933             text.Insert(PRUnichar('<'), offset);
00934         }
00935         else if (Substring(text, offset, 4).LowerCaseEqualsLiteral("&gt;"))
00936         {
00937             text.Cut(offset, 4);
00938             text.Insert(PRUnichar('>'), offset);
00939         }
00940         else if (Substring(text, offset, 5).LowerCaseEqualsLiteral("&amp;"))
00941         {
00942             text.Cut(offset, 5);
00943             text.Insert(PRUnichar('&'), offset);
00944         }
00945         else if (Substring(text, offset, 6).LowerCaseEqualsLiteral("&quot;"))
00946         {
00947             text.Cut(offset, 6);
00948             text.Insert(PRUnichar('\"'), offset);
00949         }
00950         else if (Substring(text, offset, 5).Equals(NS_LITERAL_STRING("&#39;")))
00951         {
00952             text.Cut(offset, 5);
00953             text.Insert(PRUnichar('\''), offset);
00954         }
00955 
00956         ++offset;
00957     }
00958     return NS_OK;
00959 }
00960 
00961 nsresult
00962 BookmarkParser::ParseMetaTag(const nsString &aLine, nsIUnicodeDecoder **decoder)
00963 {
00964     nsresult    rv = NS_OK;
00965 
00966     *decoder = nsnull;
00967 
00968     // get the HTTP-EQUIV attribute
00969     PRInt32 start = aLine.Find(kHTTPEquivEquals, PR_TRUE);
00970     NS_ASSERTION(start >= 0, "no 'HTTP-EQUIV=\"' string: how'd we get here?");
00971     if (start < 0)  return NS_ERROR_UNEXPECTED;
00972     // Skip past the first double-quote
00973     start += (sizeof(kHTTPEquivEquals) - 1);
00974     // ...and find the next so we can chop the HTTP-EQUIV attribute
00975     PRInt32 end = aLine.FindChar(PRUnichar('"'), start);
00976     nsAutoString    httpEquiv;
00977     aLine.Mid(httpEquiv, start, end - start);
00978 
00979     // if HTTP-EQUIV isn't "Content-Type", just ignore the META tag
00980     if (!httpEquiv.LowerCaseEqualsLiteral("content-type"))
00981         return NS_OK;
00982 
00983     // get the CONTENT attribute
00984     start = aLine.Find(kContentEquals, PR_TRUE);
00985     NS_ASSERTION(start >= 0, "no 'CONTENT=\"' string: how'd we get here?");
00986     if (start < 0)  return NS_ERROR_UNEXPECTED;
00987     // Skip past the first double-quote
00988     start += (sizeof(kContentEquals) - 1);
00989     // ...and find the next so we can chop the CONTENT attribute
00990     end = aLine.FindChar(PRUnichar('"'), start);
00991     nsAutoString    content;
00992     aLine.Mid(content, start, end - start);
00993 
00994     // look for the charset value
00995     start = content.Find(kCharsetEquals, PR_TRUE);
00996     NS_ASSERTION(start >= 0, "no 'charset=' string: how'd we get here?");
00997     if (start < 0)  return NS_ERROR_UNEXPECTED;
00998     start += (sizeof(kCharsetEquals)-1);
00999     nsCAutoString    charset;
01000     charset.AssignWithConversion(Substring(content, start, content.Length() - start));
01001     if (charset.Length() < 1)   return NS_ERROR_UNEXPECTED;
01002 
01003     // found a charset, now try and get a decoder from it to Unicode
01004     nsICharsetConverterManager *charsetConv;
01005     rv = CallGetService(kCharsetConverterManagerCID, &charsetConv);
01006     if (NS_SUCCEEDED(rv))
01007     {
01008         rv = charsetConv->GetUnicodeDecoderRaw(charset.get(), decoder);
01009         NS_RELEASE(charsetConv);
01010     }
01011     return rv;
01012 }
01013 
01014 BookmarkParser::BookmarkField
01015 BookmarkParser::gBookmarkFieldTable[] =
01016 {
01017   // Note: the first entry MUST be the ID/resource of the bookmark
01018   { kIDEquals,              NC_NAMESPACE_URI  "ID",                nsnull,  BookmarkParser::ParseResource,  nsnull },
01019   { kHREFEquals,            NC_NAMESPACE_URI  "URL",               nsnull,  BookmarkParser::ParseLiteral,   nsnull },
01020   { kAddDateEquals,         NC_NAMESPACE_URI  "BookmarkAddDate",   nsnull,  BookmarkParser::ParseDate,      nsnull },
01021   { kLastVisitEquals,       WEB_NAMESPACE_URI "LastVisitDate",     nsnull,  BookmarkParser::ParseDate,      nsnull },
01022   { kLastModifiedEquals,    WEB_NAMESPACE_URI "LastModifiedDate",  nsnull,  BookmarkParser::ParseDate,      nsnull },
01023   { kShortcutURLEquals,     NC_NAMESPACE_URI  "ShortcutURL",       nsnull,  BookmarkParser::ParseLiteral,   nsnull },
01024   { kIconEquals,            NC_NAMESPACE_URI  "Icon",              nsnull,  BookmarkParser::ParseLiteral,   nsnull },
01025   { kLastCharsetEquals,     WEB_NAMESPACE_URI "LastCharset",       nsnull,  BookmarkParser::ParseLiteral,   nsnull },
01026   { kScheduleEquals,        WEB_NAMESPACE_URI "Schedule",          nsnull,  BookmarkParser::ParseLiteral,   nsnull },
01027   { kLastPingEquals,        WEB_NAMESPACE_URI "LastPingDate",      nsnull,  BookmarkParser::ParseDate,      nsnull },
01028   { kPingETagEquals,        WEB_NAMESPACE_URI "LastPingETag",      nsnull,  BookmarkParser::ParseLiteral,   nsnull },
01029   { kPingLastModEquals,     WEB_NAMESPACE_URI "LastPingModDate",   nsnull,  BookmarkParser::ParseLiteral,   nsnull },
01030   { kPingContentLenEquals,  WEB_NAMESPACE_URI "LastPingContentLen",nsnull,  BookmarkParser::ParseLiteral,   nsnull },
01031   { kPingStatusEquals,      WEB_NAMESPACE_URI "status",            nsnull,  BookmarkParser::ParseLiteral,   nsnull },
01032   // Note: end of table
01033   { nsnull,                 nsnull,                                nsnull,  nsnull,                         nsnull },
01034 };
01035 
01036 BookmarkParser::BookmarkField
01037 BookmarkParser::gBookmarkHeaderFieldTable[] =
01038 {
01039   // Note: the first entry MUST be the ID/resource of the bookmark
01040   { kIDEquals,                    NC_NAMESPACE_URI  "ID",                nsnull,  BookmarkParser::ParseResource,  nsnull },
01041   { kAddDateEquals,               NC_NAMESPACE_URI  "BookmarkAddDate",   nsnull,  BookmarkParser::ParseDate,      nsnull },
01042   { kLastModifiedEquals,          WEB_NAMESPACE_URI "LastModifiedDate",  nsnull,  BookmarkParser::ParseDate,      nsnull },
01043   { kFolderGroupEquals,           NC_NAMESPACE_URI  "FolderGroup",       nsnull,  BookmarkParser::ParseLiteral,   nsnull },
01044   // Note: these last three are specially handled
01045   { kNewBookmarkFolderEquals,     kURINC_NewBookmarkFolder,              nsnull,  BookmarkParser::ParseLiteral,   nsnull },
01046   { kNewSearchFolderEquals,       kURINC_NewSearchFolder,                nsnull,  BookmarkParser::ParseLiteral,   nsnull },
01047   { kPersonalToolbarFolderEquals, kURINC_PersonalToolbarFolder,          nsnull,  BookmarkParser::ParseLiteral,   nsnull },
01048   // Note: end of table
01049   { nsnull,                       nsnull,                                nsnull,  nsnull,                         nsnull },
01050 };
01051 
01052 nsresult
01053 BookmarkParser::ParseBookmarkInfo(BookmarkField *fields, PRBool isBookmarkFlag,
01054                 const nsString &aLine, const nsCOMPtr<nsIRDFContainer> &aContainer,
01055                 nsIRDFResource *nodeType, nsCOMPtr<nsIRDFResource> &bookmarkNode)
01056 {
01057     NS_PRECONDITION(aContainer != nsnull, "null ptr");
01058     if (! aContainer)
01059         return NS_ERROR_NULL_POINTER;
01060 
01061     bookmarkNode = nsnull;
01062 
01063     PRInt32     lineLen = aLine.Length();
01064 
01065     PRInt32     attrStart=0;
01066     if (isBookmarkFlag == PR_TRUE)
01067     {
01068         attrStart = aLine.Find(kOpenAnchor, PR_TRUE, attrStart);
01069         if (attrStart < 0)  return NS_ERROR_UNEXPECTED;
01070         attrStart += sizeof(kOpenAnchor)-1;
01071     }
01072     else
01073     {
01074         attrStart = aLine.Find(kOpenHeading, PR_TRUE, attrStart);
01075         if (attrStart < 0)  return NS_ERROR_UNEXPECTED;
01076         attrStart += sizeof(kOpenHeading)-1;
01077     }
01078 
01079     // free up any allocated data in field table BEFORE processing
01080     for (BookmarkField *preField = fields; preField->mName; ++preField)
01081     {
01082         NS_IF_RELEASE(preField->mValue);
01083     }
01084 
01085     // loop over attributes
01086     while((attrStart < lineLen) && (aLine[attrStart] != '>'))
01087     {
01088         while(nsCRT::IsAsciiSpace(aLine[attrStart]))   ++attrStart;
01089 
01090         PRBool  fieldFound = PR_FALSE;
01091 
01092         nsAutoString id;
01093         id.AssignWithConversion(kIDEquals);
01094         for (BookmarkField *field = fields; field->mName; ++field)
01095         {
01096             nsAutoString name;
01097             name.AssignWithConversion(field->mName);
01098             if (mIsImportOperation && name.Equals(id)) 
01099                 // For import operations, we don't want to save the unique identifier for folders,
01100                 // because this can cause bugs like 74969 (importing duplicate bookmark folder 
01101                 // hierachy causes bookmarks file to grow infinitely).
01102                 continue;
01103   
01104             if (aLine.Find(field->mName, PR_TRUE, attrStart, 1) == attrStart)
01105             {
01106                 attrStart += strlen(field->mName);
01107 
01108                 // skip to terminating quote of string
01109                 PRInt32 termQuote = aLine.FindChar(PRUnichar('\"'), attrStart);
01110                 if (termQuote > attrStart)
01111                 {
01112                     // process data
01113                     nsAutoString    data;
01114                     aLine.Mid(data, attrStart, termQuote-attrStart);
01115 
01116                     attrStart = termQuote + 1;
01117                     fieldFound = PR_TRUE;
01118 
01119                     if (!data.IsEmpty())
01120                     {
01121                         // XXX Bug 58421 We should not ever hit this assertion
01122                         NS_ASSERTION(!field->mValue, "Field already has a value");
01123                         // but prevent a leak if we do hit it
01124                         NS_IF_RELEASE(field->mValue);
01125 
01126                         nsresult rv = (*field->mParse)(field->mProperty, data, &field->mValue);
01127                         if (NS_FAILED(rv)) break;
01128                     }
01129                 }
01130                 break;
01131             }
01132         }
01133 
01134         if (fieldFound == PR_FALSE)
01135         {
01136             // skip to next attribute
01137             while((attrStart < lineLen) && (aLine[attrStart] != '>') &&
01138                 (!nsCRT::IsAsciiSpace(aLine[attrStart])))
01139             {
01140                 ++attrStart;
01141             }
01142         }
01143     }
01144 
01145     nsresult    rv;
01146 
01147     // Note: the first entry MUST be the ID/resource of the bookmark
01148     nsCOMPtr<nsIRDFResource> bookmark = do_QueryInterface(fields[0].mValue);
01149     if (!bookmark)
01150     {
01151         // We've never seen this bookmark/folder before. Assign it an anonymous ID
01152         rv = gRDF->GetAnonymousResource(getter_AddRefs(bookmark));
01153         NS_ASSERTION(NS_SUCCEEDED(rv), "unable to create anonymous resource for folder");
01154     }
01155     else if (bookmark.get() == kNC_PersonalToolbarFolder)
01156     {
01157         mFoundPersonalToolbarFolder = PR_TRUE;
01158     }
01159 
01160     if (bookmark)
01161     {
01162         const char* bookmarkURI;
01163         bookmark->GetValueConst(&bookmarkURI);
01164 
01165         bookmarkNode = bookmark;
01166 
01167         // assert appropriate node type
01168         PRBool isIEFavoriteRoot = PR_FALSE;
01169         if (!mIEFavoritesRoot.IsEmpty())
01170         {
01171             if (!nsCRT::strcmp(mIEFavoritesRoot.get(), bookmarkURI))
01172             {
01173                 mFoundIEFavoritesRoot = PR_TRUE;
01174                 isIEFavoriteRoot = PR_TRUE;
01175             }
01176         }
01177         if ((isIEFavoriteRoot == PR_TRUE) || ((nodeType == kNC_IEFavorite) && (isBookmarkFlag == PR_FALSE)))
01178         {
01179             rv = mDataSource->Assert(bookmark, kRDF_type, kNC_IEFavoriteFolder, PR_TRUE);
01180         }
01181         else if (nodeType == kNC_IEFavorite ||
01182                  nodeType == kNC_IEFavoriteFolder ||
01183                  nodeType == kNC_BookmarkSeparator)
01184         {
01185             rv = mDataSource->Assert(bookmark, kRDF_type, nodeType, PR_TRUE);
01186         }
01187 
01188         // process data
01189         for (BookmarkField *field = fields; field->mName; ++field)
01190         {
01191             if (field->mValue)
01192             {
01193                 // check for and set various folder hints
01194                 if (field->mProperty == kNC_NewBookmarkFolder)
01195                 {
01196                       rv = setFolderHint(bookmark, kNC_NewBookmarkFolder);
01197                 }
01198                 else if (field->mProperty == kNC_NewSearchFolder)
01199                 {
01200                       rv = setFolderHint(bookmark, kNC_NewSearchFolder);
01201                 }
01202                 else if (field->mProperty == kNC_PersonalToolbarFolder)
01203                 {
01204                       rv = setFolderHint(bookmark, kNC_PersonalToolbarFolder);
01205                       mFoundPersonalToolbarFolder = PR_TRUE;
01206                 }
01207                 else if (field->mProperty)
01208                 {
01209                     updateAtom(mDataSource, bookmark, field->mProperty,
01210                                field->mValue, nsnull);
01211                 }
01212             }
01213         }
01214 
01215         // look for bookmark name (and unescape)
01216         if (aLine[attrStart] == '>')
01217         {
01218             PRInt32 nameEnd;
01219             if (isBookmarkFlag == PR_TRUE)
01220                 nameEnd = aLine.Find(kCloseAnchor, PR_TRUE, ++attrStart);
01221             else
01222                 nameEnd = aLine.Find(kCloseHeading, PR_TRUE, ++attrStart);
01223 
01224             if (nameEnd > attrStart)
01225             {
01226                 nsAutoString    name;
01227                 aLine.Mid(name, attrStart, nameEnd-attrStart);
01228                 if (!name.IsEmpty())
01229                 {
01230                     Unescape(name);
01231 
01232                     nsCOMPtr<nsIRDFNode>    nameNode;
01233                     rv = ParseLiteral(kNC_Name, name, getter_AddRefs(nameNode));
01234                     if (NS_SUCCEEDED(rv) && nameNode)
01235                         updateAtom(mDataSource, bookmark, kNC_Name, nameNode, nsnull);
01236                 }
01237             }
01238         }
01239 
01240         if (isBookmarkFlag == PR_FALSE)
01241         {
01242             rv = gRDFC->MakeSeq(mDataSource, bookmark, nsnull);
01243             NS_ASSERTION(NS_SUCCEEDED(rv), "unable to make new folder as sequence");
01244             if (NS_SUCCEEDED(rv))
01245             {
01246                 // And now recursively parse the rest of the file...
01247                 rv = Parse(bookmark, nodeType);
01248                 NS_ASSERTION(NS_SUCCEEDED(rv), "unable to parse bookmarks");
01249             }
01250         }
01251 
01252         // prevent duplicates                                                       
01253         PRInt32 aIndex;                                                             
01254         nsCOMPtr<nsIRDFResource> containerRes;
01255         aContainer->GetResource(getter_AddRefs(containerRes));
01256         if (containerRes && NS_SUCCEEDED(gRDFC->IndexOf(mDataSource, containerRes, bookmark, &aIndex)) &&                 
01257             (aIndex < 0))                                                           
01258         {                                                                           
01259           // The last thing we do is add the bookmark to the container.           
01260           // This ensures the minimal amount of reflow.                           
01261           rv = aContainer->AppendElement(bookmark);                               
01262           NS_ASSERTION(NS_SUCCEEDED(rv), "unable to add bookmark to container");  
01263         }      
01264     }
01265 
01266     // free up any allocated data in field table AFTER processing
01267     for (BookmarkField *postField = fields; postField->mName; ++postField)
01268     {
01269         NS_IF_RELEASE(postField->mValue);
01270     }
01271 
01272     return NS_OK;
01273 }
01274 
01275 nsresult
01276 BookmarkParser::ParseResource(nsIRDFResource *arc, nsString& url, nsIRDFNode** aResult)
01277 {
01278     *aResult = nsnull;
01279 
01280     if (arc == kNC_URL)
01281     {
01282         // Now do properly replace %22's; this is particularly important for javascript: URLs
01283         static const char kEscape22[] = "%22";
01284         PRInt32 offset;
01285         while ((offset = url.Find(kEscape22)) >= 0)
01286         {
01287             url.SetCharAt('\"',offset);
01288             url.Cut(offset + 1, sizeof(kEscape22) - 2);
01289         }
01290 
01291         // XXX At this point, the URL may be relative. 4.5 called into
01292         // netlib to make an absolute URL, and there was some magic
01293         // "relative_URL" parameter that got sent down as well. We punt on
01294         // that stuff.
01295 
01296         // hack fix for bug # 21175:
01297         // if we don't have a protocol scheme, add "http://" as a default scheme
01298         if (url.FindChar(PRUnichar(':')) < 0)
01299         {
01300             url.Assign(NS_LITERAL_STRING("http://") + url);
01301         }
01302     }
01303 
01304     nsresult                    rv;
01305     nsCOMPtr<nsIRDFResource>    result;
01306     rv = gRDF->GetUnicodeResource(url, getter_AddRefs(result));
01307     if (NS_FAILED(rv)) return rv;
01308     return result->QueryInterface(NS_GET_IID(nsIRDFNode), (void**) aResult);
01309 }
01310 
01311 nsresult
01312 BookmarkParser::ParseLiteral(nsIRDFResource *arc, nsString& aValue, nsIRDFNode** aResult)
01313 {
01314     *aResult = nsnull;
01315 
01316     if (arc == kNC_ShortcutURL)
01317     {
01318         // lowercase the shortcut URL before storing internally
01319         ToLowerCase(aValue);
01320     }
01321     else if (arc == kWEB_LastCharset)
01322     {
01323         if (gCharsetAlias)
01324         {
01325             nsCAutoString charset; charset.AssignWithConversion(aValue);
01326             gCharsetAlias->GetPreferred(charset, charset);
01327             aValue.AssignWithConversion(charset.get());
01328         }
01329     }
01330     else if (arc == kWEB_LastPingETag)
01331     {
01332         // don't allow quotes in etag
01333         PRInt32 offset;
01334         while ((offset = aValue.FindChar('\"')) >= 0)
01335         {
01336             aValue.Cut(offset, 1);
01337         }
01338     }
01339 
01340     nsresult                rv;
01341     nsCOMPtr<nsIRDFLiteral> result;
01342     rv = gRDF->GetLiteral(aValue.get(), getter_AddRefs(result));
01343     if (NS_FAILED(rv)) return rv;
01344     return result->QueryInterface(NS_GET_IID(nsIRDFNode), (void**) aResult);
01345 }
01346 
01347 nsresult
01348 BookmarkParser::ParseDate(nsIRDFResource *arc, nsString& aValue, nsIRDFNode** aResult)
01349 {
01350     *aResult = nsnull;
01351 
01352     PRInt32 theDate = 0;
01353     if (!aValue.IsEmpty())
01354     {
01355         PRInt32 err;
01356         theDate = aValue.ToInteger(&err); // ignored.
01357     }
01358     if (theDate == 0)   return NS_RDF_NO_VALUE;
01359 
01360     // convert from seconds to microseconds (PRTime)
01361     PRInt64     dateVal, temp, million;
01362     LL_I2L(temp, theDate);
01363     LL_I2L(million, PR_USEC_PER_SEC);
01364     LL_MUL(dateVal, temp, million);
01365 
01366     nsresult                rv;
01367     nsCOMPtr<nsIRDFDate>    result;
01368     if (NS_FAILED(rv = gRDF->GetDateLiteral(dateVal, getter_AddRefs(result))))
01369     {
01370         NS_ERROR("unable to get date literal for time");
01371         return rv;
01372     }
01373     return result->QueryInterface(NS_GET_IID(nsIRDFNode), (void**) aResult);
01374 }
01375 
01376 nsresult
01377 BookmarkParser::updateAtom(nsIRDFDataSource *db, nsIRDFResource *src,
01378             nsIRDFResource *prop, nsIRDFNode *newValue, PRBool *dirtyFlag)
01379 {
01380     nsresult        rv;
01381     nsCOMPtr<nsIRDFNode>    oldValue;
01382 
01383     if (dirtyFlag != nsnull)
01384     {
01385         *dirtyFlag = PR_FALSE;
01386     }
01387 
01388     if (NS_SUCCEEDED(rv = db->GetTarget(src, prop, PR_TRUE, getter_AddRefs(oldValue))) &&
01389         (rv != NS_RDF_NO_VALUE))
01390     {
01391         rv = db->Change(src, prop, oldValue, newValue);
01392 
01393         if ((oldValue.get() != newValue) && (dirtyFlag != nsnull))
01394         {
01395             *dirtyFlag = PR_TRUE;
01396         }
01397     }
01398     else
01399     {
01400         rv = db->Assert(src, prop, newValue, PR_TRUE);
01401 
01402         if (prop == kWEB_Schedule)
01403         {
01404           // internally mark nodes with schedules so that we can find them quickly
01405           updateAtom(db, src, kWEB_ScheduleActive, kTrueLiteral, dirtyFlag);
01406         }
01407     }
01408     return rv;
01409 }
01410 
01411 nsresult
01412 BookmarkParser::ParseBookmarkSeparator(const nsString &aLine, const nsCOMPtr<nsIRDFContainer> &aContainer)
01413 {
01414     nsCOMPtr<nsIRDFResource>  separator;
01415     nsresult rv = gRDF->GetAnonymousResource(getter_AddRefs(separator));
01416     if (NS_FAILED(rv))
01417         return rv;
01418 
01419     PRInt32 lineLen = aLine.Length();
01420 
01421     PRInt32 attrStart = 0;
01422     attrStart = aLine.Find(kSeparator, PR_TRUE, attrStart);
01423     if (attrStart < 0)
01424         return NS_ERROR_UNEXPECTED;
01425     attrStart += sizeof(kSeparator)-1;
01426 
01427     while((attrStart < lineLen) && (aLine[attrStart] != '>')) {
01428         while(nsCRT::IsAsciiSpace(aLine[attrStart]))
01429             ++attrStart;
01430 
01431         if (aLine.Find(kNameEquals, PR_TRUE, attrStart, 1) == attrStart) {
01432             attrStart += sizeof(kNameEquals) - 1;
01433 
01434             // skip to terminating quote of string
01435             PRInt32 termQuote = aLine.FindChar(PRUnichar('\"'), attrStart);
01436             if (termQuote > attrStart) {
01437                 nsAutoString name;
01438                 aLine.Mid(name, attrStart, termQuote - attrStart);
01439                 attrStart = termQuote + 1;
01440                 if (!name.IsEmpty()) {
01441                     nsCOMPtr<nsIRDFLiteral> nameLiteral;
01442                     rv = gRDF->GetLiteral(name.get(), getter_AddRefs(nameLiteral));
01443                     if (NS_FAILED(rv))
01444                         return rv;
01445                     rv = mDataSource->Assert(separator, kNC_Name, nameLiteral, PR_TRUE);
01446                     if (NS_FAILED(rv))
01447                         return rv;
01448                 }
01449             }
01450         }
01451     }
01452 
01453     rv = mDataSource->Assert(separator, kRDF_type, kNC_BookmarkSeparator, PR_TRUE);
01454     if (NS_FAILED(rv))
01455         return rv;
01456 
01457     rv = aContainer->AppendElement(separator);
01458 
01459     return rv;
01460 }
01461 
01462 nsresult
01463 BookmarkParser::ParseHeaderBegin(const nsString &aLine, const nsCOMPtr<nsIRDFContainer> &aContainer)
01464 {
01465     return NS_OK;
01466 }
01467 
01468 nsresult
01469 BookmarkParser::ParseHeaderEnd(const nsString &aLine)
01470 {
01471     return NS_OK;
01472 }
01473 
01474 nsresult
01475 BookmarkParser::AssertTime(nsIRDFResource* aSource,
01476                            nsIRDFResource* aLabel,
01477                            PRInt32 aTime)
01478 {
01479     nsresult    rv = NS_OK;
01480 
01481     if (aTime != 0)
01482     {
01483         // Convert to a date literal
01484         PRInt64     dateVal, temp, million;
01485 
01486         LL_I2L(temp, aTime);
01487         LL_I2L(million, PR_USEC_PER_SEC);
01488         LL_MUL(dateVal, temp, million);     // convert from seconds to microseconds (PRTime)
01489 
01490         nsCOMPtr<nsIRDFDate>    dateLiteral;
01491         if (NS_FAILED(rv = gRDF->GetDateLiteral(dateVal, getter_AddRefs(dateLiteral))))
01492         {
01493             NS_ERROR("unable to get date literal for time");
01494             return rv;
01495         }
01496         updateAtom(mDataSource, aSource, aLabel, dateLiteral, nsnull);
01497     }
01498     return rv;
01499 }
01500 
01501 nsresult
01502 BookmarkParser::setFolderHint(nsIRDFResource *newSource, nsIRDFResource *objType)
01503 {
01504     nsresult            rv;
01505     nsCOMPtr<nsISimpleEnumerator>   srcList;
01506     if (NS_FAILED(rv = mDataSource->GetSources(kNC_FolderType, objType, PR_TRUE, getter_AddRefs(srcList))))
01507         return rv;
01508 
01509     PRBool  hasMoreSrcs = PR_TRUE;
01510     while(NS_SUCCEEDED(rv = srcList->HasMoreElements(&hasMoreSrcs))
01511         && (hasMoreSrcs == PR_TRUE))
01512     {
01513         nsCOMPtr<nsISupports>   aSrc;
01514         if (NS_FAILED(rv = srcList->GetNext(getter_AddRefs(aSrc))))
01515             break;
01516         nsCOMPtr<nsIRDFResource>    aSource = do_QueryInterface(aSrc);
01517         if (!aSource)   continue;
01518 
01519         if (NS_FAILED(rv = mDataSource->Unassert(aSource, kNC_FolderType, objType)))
01520             continue;
01521     }
01522 
01523     rv = mDataSource->Assert(newSource, kNC_FolderType, objType, PR_TRUE);
01524 
01525     return rv;
01526 }
01527 
01528 
01529 class SortInfo {
01530 public:
01531     SortInfo(PRInt32 aDirection, PRBool aFoldersFirst)
01532         : mDirection(aDirection),
01533           mFoldersFirst(aFoldersFirst) {
01534     }
01535 
01536 protected:
01537     PRInt32     mDirection;
01538     PRBool      mFoldersFirst;
01539 
01540     friend class nsBookmarksService;
01541 };
01542 
01543 class ElementInfo {
01544 public:
01545     ElementInfo(nsIRDFResource* aElement, nsIRDFNode* aNode, PRBool aIsFolder)
01546       : mElement(aElement), mNode(aNode), mIsFolder(aIsFolder) {
01547     }
01548 
01549 protected:
01550     nsCOMPtr<nsIRDFResource>    mElement;
01551     nsCOMPtr<nsIRDFNode>        mNode;
01552     PRBool                      mIsFolder;
01553 
01554     friend class nsBookmarksService;
01555 };
01556 
01557 // Note, that elements are deleted only when the array goes away.
01558 // Any call on this array that would result in an element being removed or
01559 // replaced should make sure that the element gets deleted.
01560 class ElementArray : public nsAutoVoidArray
01561 {
01562 public:
01563   virtual ~ElementArray();
01564 
01565   ElementInfo* ElementAt(PRInt32 aIndex) const {
01566     return NS_STATIC_CAST(ElementInfo*, nsAutoVoidArray::ElementAt(aIndex));
01567   }
01568 
01569   ElementInfo* operator[](PRInt32 aIndex) const {
01570     return ElementAt(aIndex);
01571   }
01572 
01573   void   Clear();
01574 };
01575 
01576 ElementArray::~ElementArray()
01577 {
01578     Clear();
01579 }
01580 
01581 void
01582 ElementArray::Clear()
01583 {
01584     PRInt32 index = Count();
01585     while (--index >= 0)
01586     {
01587         ElementInfo* elementInfo = ElementAt(index);
01588         delete elementInfo;
01589     }
01590     nsAutoVoidArray::Clear();
01591 }
01592 
01593 
01595 // nsBookmarksService implementation
01596 
01597 nsBookmarksService::nsBookmarksService() :
01598     mInner(nsnull),
01599     mUpdateBatchNest(0),
01600     mDirty(PR_FALSE)
01601 
01602 #if defined(XP_MAC) || defined(XP_MACOSX)
01603     ,mIEFavoritesAvailable(PR_FALSE)
01604 #endif
01605 { }
01606 
01607 nsBookmarksService::~nsBookmarksService()
01608 {
01609     if (mTimer)
01610     {
01611         // be sure to cancel the timer, as it holds a
01612         // weak reference back to nsBookmarksService
01613         mTimer->Cancel();
01614         mTimer = nsnull;
01615     }
01616 
01617     // Unregister ourselves from the RDF service
01618     if (gRDF)
01619         gRDF->UnregisterDataSource(this);
01620 
01621     // Note: can't flush in the DTOR, as the RDF service
01622     // has probably already been destroyed
01623     // Flush();
01624     bm_ReleaseGlobals();
01625     NS_IF_RELEASE(mInner);
01626 }
01627 
01628 nsresult
01629 nsBookmarksService::Init()
01630 {
01631     nsresult rv;
01632     rv = bm_AddRefGlobals();
01633     if (NS_FAILED(rv))  return rv;
01634 
01635     mNetService = do_GetService(kIOServiceCID, &rv);
01636     if (NS_FAILED(rv))  return rv;
01637 
01638     // create cache service/session, ignoring errors
01639     mCacheService = do_GetService(kCacheServiceCID, &rv);
01640     if (NS_SUCCEEDED(rv))
01641     {
01642         rv = mCacheService->CreateSession("HTTP", nsICache::STORE_ANYWHERE,
01643             nsICache::STREAM_BASED, getter_AddRefs(mCacheSession));
01644     }
01645 
01646     mTransactionManager = do_CreateInstance(NS_TRANSACTIONMANAGER_CONTRACTID, &rv);
01647     if (NS_FAILED(rv)) return rv;
01648 
01649     /* create a URL for the string resource file */
01650     nsCOMPtr<nsIURI> uri;
01651     mNetService->NewURI(bookmark_properties, nsnull, nsnull,
01652                         getter_AddRefs(uri));
01653     if (uri)
01654     {
01655         /* create a bundle for the localization */
01656         nsCOMPtr<nsIStringBundleService> stringService =
01657                 do_GetService(kStringBundleServiceCID);
01658         if (stringService)
01659         {
01660             nsCAutoString spec;
01661             uri->GetSpec(spec);
01662             if (!spec.IsEmpty())
01663             {
01664                 stringService->CreateBundle(spec.get(), getter_AddRefs(mBundle));
01665             }
01666         }
01667     }
01668 
01669     nsCOMPtr<nsIPrefBranch> prefBranch(do_GetService(NS_PREFSERVICE_CONTRACTID, &rv));
01670     if (NS_SUCCEEDED(rv))
01671     {
01672         // get browser icon prefs
01673         PRInt32 toolbarIcons = 0;
01674         prefBranch->GetIntPref("browser.chrome.load_toolbar_icons", &toolbarIcons);
01675         if (toolbarIcons > 0) {
01676               prefBranch->GetBoolPref("browser.chrome.site_icons", &mBrowserIcons);
01677               mAlwaysLoadIcons = (toolbarIcons > 1);
01678         } else
01679               mAlwaysLoadIcons = mBrowserIcons = PR_FALSE;
01680         
01681         // determine what the name of the Personal Toolbar Folder is...
01682         // first from user preference, then string bundle, then hard-coded default
01683         nsXPIDLCString prefValue;
01684         rv = prefBranch->GetCharPref("custtoolbar.personal_toolbar_folder", getter_Copies(prefValue));
01685         if (NS_SUCCEEDED(rv) && !prefValue.IsEmpty())
01686         {
01687             CopyUTF8toUTF16(prefValue, mPersonalToolbarName);
01688         }
01689 
01690         if (mPersonalToolbarName.IsEmpty())
01691         {
01692             // rjc note: always try to get the string bundle (see above) before trying this
01693             rv = mBundle->GetStringFromName(NS_LITERAL_STRING("DefaultPersonalToolbarFolder").get(), 
01694                                             getter_Copies(mPersonalToolbarName));
01695             if (NS_FAILED(rv) || mPersonalToolbarName.IsEmpty()) {
01696               // no preference, so fallback to a well-known name
01697               mPersonalToolbarName.AssignLiteral("Personal Toolbar Folder");
01698             }
01699         }
01700     }
01701 
01702     // Gets the default name for NC:BookmarksRoot
01703     // if the user has more than one profile: always include the profile name
01704     // otherwise, include the profile name only if it is not named 'default'
01705     // the profile "default" is not localizable and arises when there is no ns4.x install
01706     nsresult             useProfile;
01707     nsCOMPtr<nsIProfile> profileService(do_GetService(NS_PROFILE_CONTRACTID,&useProfile));
01708     if (NS_SUCCEEDED(useProfile))
01709     {
01710         nsXPIDLString        currentProfileName;
01711     
01712         useProfile = profileService->GetCurrentProfile(getter_Copies(currentProfileName));
01713         if (NS_SUCCEEDED(useProfile))
01714         {
01715             const PRUnichar *param[1] = {currentProfileName.get()};
01716             useProfile = mBundle->FormatStringFromName(NS_LITERAL_STRING("bookmarks_root").get(),
01717                                                     param, 1, getter_Copies(mBookmarksRootName));
01718             if (NS_SUCCEEDED(useProfile))
01719             {
01720                 PRInt32 profileCount;
01721                 useProfile = profileService->GetProfileCount(&profileCount);
01722                 if (NS_SUCCEEDED(useProfile) && profileCount == 1)
01723                 {
01724                     ToLowerCase(currentProfileName);
01725                     if (currentProfileName.EqualsLiteral("default"))
01726                         useProfile = NS_ERROR_FAILURE;
01727                 }
01728             }
01729         }
01730     }
01731 
01732     if (NS_FAILED(useProfile))
01733     {
01734         rv = mBundle->GetStringFromName(NS_LITERAL_STRING("bookmarks_default_root").get(),
01735                                         getter_Copies(mBookmarksRootName));
01736         if (NS_FAILED(rv) || mBookmarksRootName.IsEmpty()) {
01737             mBookmarksRootName.AssignLiteral("Bookmarks");
01738         }
01739     }
01740 
01741     // Register as an observer of profile changes
01742     nsCOMPtr<nsIObserverService> observerService = 
01743              do_GetService("@mozilla.org/observer-service;1", &rv);
01744     NS_ASSERTION(observerService, "Could not get observer service.");
01745     if (observerService) {
01746         observerService->AddObserver(this, "profile-before-change", PR_TRUE);
01747         observerService->AddObserver(this, "profile-after-change", PR_TRUE);
01748     }
01749 
01750     rv = initDatasource();
01751     if (NS_FAILED(rv))
01752         return rv;
01753 
01754     /* timer initialization */
01755     busyResource = nsnull;
01756 
01757     if (!mTimer)
01758     {
01759         busySchedule = PR_FALSE;
01760         mTimer = do_CreateInstance("@mozilla.org/timer;1", &rv);
01761         NS_ASSERTION(NS_SUCCEEDED(rv), "unable to create a timer");
01762         if (NS_FAILED(rv)) return rv;
01763         mTimer->InitWithFuncCallback(nsBookmarksService::FireTimer, this, BOOKMARK_TIMEOUT, 
01764                                      nsITimer::TYPE_REPEATING_SLACK);
01765         // Note: don't addref "this" as we'll cancel the timer in the nsBookmarkService destructor
01766     }
01767 
01768     // register this as a named data source with the RDF
01769     // service. Do this *last*, because if Init() fails, then the
01770     // object will be destroyed (leaving the RDF service with a
01771     // dangling pointer).
01772     rv = gRDF->RegisterDataSource(this, PR_FALSE);
01773     if (NS_FAILED(rv)) return rv;
01774 
01775     return NS_OK;
01776 }
01777 
01778 nsresult
01779 nsBookmarksService::getLocaleString(const char *key, nsString &str)
01780 {
01781     PRUnichar   *keyUni = nsnull;
01782     nsAutoString    keyStr;
01783     keyStr.AssignWithConversion(key);
01784 
01785     nsresult    rv = NS_RDF_NO_VALUE;
01786     if (mBundle && (NS_SUCCEEDED(rv = mBundle->GetStringFromName(keyStr.get(), &keyUni)))
01787         && (keyUni))
01788     {
01789         str = keyUni;
01790         NS_Free(keyUni);
01791     }
01792     else
01793     {
01794         str.Truncate();
01795     }
01796     return rv;
01797 }
01798 
01799 nsresult
01800 nsBookmarksService::ExamineBookmarkSchedule(nsIRDFResource *theBookmark, PRBool & examineFlag)
01801 {
01802     examineFlag = PR_FALSE;
01803     
01804     nsresult    rv = NS_OK;
01805 
01806     nsCOMPtr<nsIRDFNode>    scheduleNode;
01807     if (NS_FAILED(rv = mInner->GetTarget(theBookmark, kWEB_Schedule, PR_TRUE,
01808         getter_AddRefs(scheduleNode))) || (rv == NS_RDF_NO_VALUE))
01809         return rv;
01810     
01811     nsCOMPtr<nsIRDFLiteral> scheduleLiteral = do_QueryInterface(scheduleNode);
01812     if (!scheduleLiteral)   return NS_ERROR_NO_INTERFACE;
01813     
01814     const PRUnichar     *scheduleUni = nsnull;
01815     if (NS_FAILED(rv = scheduleLiteral->GetValueConst(&scheduleUni)))
01816         return rv;
01817     if (!scheduleUni)   return NS_ERROR_NULL_POINTER;
01818 
01819     nsAutoString        schedule(scheduleUni);
01820     if (schedule.Length() < 1)  return NS_ERROR_UNEXPECTED;
01821 
01822     // convert the current date/time from microseconds (PRTime) to seconds
01823     // Note: don't change now64, as its used later in the function
01824     PRTime      now64 = PR_Now(), temp64, million;
01825     LL_I2L(million, PR_USEC_PER_SEC);
01826     LL_DIV(temp64, now64, million);
01827     PRInt32     now32;
01828     LL_L2I(now32, temp64);
01829 
01830     PRExplodedTime  nowInfo;
01831     PR_ExplodeTime(now64, PR_LocalTimeParameters, &nowInfo);
01832     
01833     // XXX Do we need to do this?
01834     PR_NormalizeTime(&nowInfo, PR_LocalTimeParameters);
01835 
01836     nsAutoString    dayNum;
01837     dayNum.AppendInt(nowInfo.tm_wday, 10);
01838 
01839     // a schedule string has the following format:
01840     // Check Monday, Tuesday, and Friday | 9 AM thru 5 PM | every five minutes | change bookmark icon
01841     // 125|9-17|5|icon
01842 
01843     nsAutoString    notificationMethod;
01844     PRInt32     startHour = -1, endHour = -1, duration = -1;
01845 
01846     // should we be checking today?
01847     PRInt32     slashOffset;
01848     if ((slashOffset = schedule.FindChar(PRUnichar('|'))) >= 0)
01849     {
01850         nsAutoString    daySection;
01851         schedule.Left(daySection, slashOffset);
01852         schedule.Cut(0, slashOffset+1);
01853         if (daySection.Find(dayNum) >= 0)
01854         {
01855             // ok, we should be checking today.  Within hour range?
01856             if ((slashOffset = schedule.FindChar(PRUnichar('|'))) >= 0)
01857             {
01858                 nsAutoString    hourRange;
01859                 schedule.Left(hourRange, slashOffset);
01860                 schedule.Cut(0, slashOffset+1);
01861 
01862                 // now have the "hour-range" segment of the string
01863                 // such as "9-17" or "9-12" from the examples above
01864                 PRInt32     dashOffset;
01865                 if ((dashOffset = hourRange.FindChar(PRUnichar('-'))) >= 1)
01866                 {
01867                     nsAutoString    startStr, endStr;
01868 
01869                     hourRange.Right(endStr, hourRange.Length() - dashOffset - 1);
01870                     hourRange.Left(startStr, dashOffset);
01871 
01872                     PRInt32     errorCode2 = 0;
01873                     startHour = startStr.ToInteger(&errorCode2);
01874                     if (errorCode2) startHour = -1;
01875                     endHour = endStr.ToInteger(&errorCode2);
01876                     if (errorCode2) endHour = -1;
01877                     
01878                     if ((startHour >=0) && (endHour >=0))
01879                     {
01880                         if ((slashOffset = schedule.FindChar(PRUnichar('|'))) >= 0)
01881                         {
01882                             nsAutoString    durationStr;
01883                             schedule.Left(durationStr, slashOffset);
01884                             schedule.Cut(0, slashOffset+1);
01885 
01886                             // get duration
01887                             PRInt32     errorCode = 0;
01888                             duration = durationStr.ToInteger(&errorCode);
01889                             if (errorCode)  duration = -1;
01890                             
01891                             // what's left is the notification options
01892                             notificationMethod = schedule;
01893                         }
01894                     }
01895                 }
01896             }
01897         }
01898     }
01899         
01900 
01901 #ifdef  DEBUG_BOOKMARK_PING_OUTPUT
01902     char *methodStr = ToNewCString(notificationMethod);
01903     if (methodStr)
01904     {
01905         printf("Start Hour: %d    End Hour: %d    Duration: %d mins    Method: '%s'\n",
01906             startHour, endHour, duration, methodStr);
01907         delete [] methodStr;
01908         methodStr = nsnull;
01909     }
01910 #endif
01911 
01912     if ((startHour <= nowInfo.tm_hour) && (endHour >= nowInfo.tm_hour) &&
01913         (duration >= 1) && (!notificationMethod.IsEmpty()))
01914     {
01915         // OK, we're with the start/end time range, check the duration
01916         // against the last time we've "pinged" the server (if ever)
01917 
01918         examineFlag = PR_TRUE;
01919 
01920         nsCOMPtr<nsIRDFNode>    pingNode;
01921         if (NS_SUCCEEDED(rv = mInner->GetTarget(theBookmark, kWEB_LastPingDate,
01922             PR_TRUE, getter_AddRefs(pingNode))) && (rv != NS_RDF_NO_VALUE))
01923         {
01924             nsCOMPtr<nsIRDFDate>    pingLiteral = do_QueryInterface(pingNode);
01925             if (pingLiteral)
01926             {
01927                 PRInt64     lastPing;
01928                 if (NS_SUCCEEDED(rv = pingLiteral->GetValue(&lastPing)))
01929                 {
01930                     PRInt64     diff64, sixty;
01931                     LL_SUB(diff64, now64, lastPing);
01932                     
01933                     // convert from milliseconds to seconds
01934                     LL_DIV(diff64, diff64, million);
01935                     // convert from seconds to minutes
01936                     LL_I2L(sixty, 60L);
01937                     LL_DIV(diff64, diff64, sixty);
01938 
01939                     PRInt32     diff32;
01940                     LL_L2I(diff32, diff64);
01941                     if (diff32 < duration)
01942                     {
01943                         examineFlag = PR_FALSE;
01944 
01945 #ifdef  DEBUG_BOOKMARK_PING_OUTPUT
01946                         printf("Skipping URL, its too soon.\n");
01947 #endif
01948                     }
01949                 }
01950             }
01951         }
01952     }
01953     return rv;
01954 }
01955 
01956 nsresult
01957 nsBookmarksService::GetBookmarkToPing(nsIRDFResource **theBookmark)
01958 {
01959     nsresult    rv = NS_OK;
01960 
01961     *theBookmark = nsnull;
01962 
01963     nsCOMPtr<nsISimpleEnumerator>   srcList;
01964     if (NS_FAILED(rv = GetSources(kWEB_ScheduleActive, kTrueLiteral, PR_TRUE, getter_AddRefs(srcList))))
01965         return rv;
01966 
01967     nsCOMPtr<nsISupportsArray>  bookmarkList;
01968     if (NS_FAILED(rv = NS_NewISupportsArray(getter_AddRefs(bookmarkList))))
01969         return rv;
01970 
01971     // build up a list of potential bookmarks to check
01972     PRBool  hasMoreSrcs = PR_TRUE;
01973     while(NS_SUCCEEDED(rv = srcList->HasMoreElements(&hasMoreSrcs))
01974         && (hasMoreSrcs == PR_TRUE))
01975     {
01976         nsCOMPtr<nsISupports>   aSrc;
01977         if (NS_FAILED(rv = srcList->GetNext(getter_AddRefs(aSrc))))
01978             break;
01979         nsCOMPtr<nsIRDFResource>    aSource = do_QueryInterface(aSrc);
01980         if (!aSource)   continue;
01981 
01982         // does the bookmark have a schedule, and if so,
01983         // are we within its bounds for checking the URL?
01984 
01985         PRBool  examineFlag = PR_FALSE;
01986         if (NS_FAILED(rv = ExamineBookmarkSchedule(aSource, examineFlag))
01987             || (examineFlag == PR_FALSE))   continue;
01988 
01989         bookmarkList->AppendElement(aSource);
01990     }
01991 
01992     // pick a random entry from the list of bookmarks to check
01993     PRUint32    numBookmarks;
01994     if (NS_SUCCEEDED(rv = bookmarkList->Count(&numBookmarks)) && (numBookmarks > 0))
01995     {
01996         PRInt32     randomNum;
01997         LL_L2I(randomNum, PR_Now());
01998         PRUint32    randomBookmark = (numBookmarks-1) % randomNum;
01999 
02000         nsCOMPtr<nsISupports>   iSupports;
02001         if (NS_SUCCEEDED(rv = bookmarkList->GetElementAt(randomBookmark,
02002             getter_AddRefs(iSupports))))
02003         {
02004             nsCOMPtr<nsIRDFResource>    aBookmark = do_QueryInterface(iSupports);
02005             if (aBookmark)
02006             {
02007                 *theBookmark = aBookmark;
02008                 NS_ADDREF(*theBookmark);
02009             }
02010         }
02011     }
02012     return rv;
02013 }
02014 
02015 void
02016 nsBookmarksService::FireTimer(nsITimer* aTimer, void* aClosure)
02017 {
02018     nsBookmarksService *bmks = NS_STATIC_CAST(nsBookmarksService *, aClosure);
02019     if (!bmks)  return;
02020     nsresult            rv;
02021 
02022     if (bmks->mDirty)
02023     {
02024         bmks->Flush();
02025     }
02026 
02027     if (bmks->busySchedule == PR_FALSE)
02028     {
02029         nsCOMPtr<nsIRDFResource>    bookmark;
02030         if (NS_SUCCEEDED(rv = bmks->GetBookmarkToPing(getter_AddRefs(bookmark))) && (bookmark))
02031         {
02032             bmks->busyResource = bookmark;
02033 
02034             nsAutoString url;
02035             rv = bmks->GetURLFromResource(bookmark, url);
02036             if (NS_FAILED(rv))
02037                 return;
02038 
02039 #ifdef  DEBUG_BOOKMARK_PING_OUTPUT
02040             printf("nsBookmarksService::FireTimer - Pinging '%s'\n",
02041                    NS_ConvertUCS2toUTF8(url).get());
02042 #endif
02043 
02044             nsCOMPtr<nsIURI>    uri;
02045             if (NS_SUCCEEDED(rv = NS_NewURI(getter_AddRefs(uri), url)))
02046             {
02047                 nsCOMPtr<nsIChannel>    channel;
02048                 if (NS_SUCCEEDED(rv = NS_NewChannel(getter_AddRefs(channel), uri, nsnull)))
02049                 {
02050                     channel->SetLoadFlags(nsIRequest::VALIDATE_ALWAYS);
02051                     nsCOMPtr<nsIHttpChannel>    httpChannel = do_QueryInterface(channel);
02052                     if (httpChannel)
02053                     {
02054                         bmks->htmlSize = 0;
02055                         httpChannel->SetRequestMethod(NS_LITERAL_CSTRING("HEAD"));
02056                         if (NS_SUCCEEDED(rv = channel->AsyncOpen(bmks, nsnull)))
02057                         {
02058                             bmks->busySchedule = PR_TRUE;
02059                         }
02060                     }
02061                 }
02062             }
02063         }
02064     }
02065 #ifdef  DEBUG_BOOKMARK_PING_OUTPUT
02066     else
02067     {
02068         printf("nsBookmarksService::FireTimer - busy pinging.\n");
02069     }
02070 #endif
02071 }
02072 
02073 
02074 // stream observer methods
02075 
02076 NS_IMETHODIMP
02077 nsBookmarksService::OnStartRequest(nsIRequest* request, nsISupports *ctxt)
02078 {
02079     return NS_OK;
02080 }
02081 
02082 NS_IMETHODIMP
02083 nsBookmarksService::OnDataAvailable(nsIRequest* request, nsISupports *ctxt, nsIInputStream *aIStream,
02084                       PRUint32 sourceOffset, PRUint32 aLength)
02085 {
02086     // calculate html page size if server doesn't tell us in headers
02087     htmlSize += aLength;
02088 
02089     return NS_OK;
02090 }
02091 
02092 NS_IMETHODIMP
02093 nsBookmarksService::OnStopRequest(nsIRequest* request, nsISupports *ctxt,
02094                     nsresult status)
02095 {
02096     nsresult rv;
02097 
02098     nsAutoString url;
02099     if (NS_SUCCEEDED(rv = GetURLFromResource(busyResource, url)))
02100     {
02101 #ifdef  DEBUG_BOOKMARK_PING_OUTPUT
02102         printf("Finished polling '%s'\n", NS_ConvertUCS2toUTF8(url).get());
02103 #endif
02104     }
02105     nsCOMPtr<nsIChannel> channel = do_QueryInterface(request);
02106     nsCOMPtr<nsIHttpChannel>    httpChannel = do_QueryInterface(channel);
02107     if (httpChannel)
02108     {
02109         nsAutoString            eTagValue, lastModValue, contentLengthValue;
02110 
02111         nsCAutoString val;
02112         if (NS_SUCCEEDED(httpChannel->GetResponseHeader(NS_LITERAL_CSTRING("ETag"), val)))
02113             CopyASCIItoUCS2(val, eTagValue);
02114         if (NS_SUCCEEDED(httpChannel->GetResponseHeader(NS_LITERAL_CSTRING("Last-Modified"), val)))
02115             CopyASCIItoUCS2(val, lastModValue);
02116         if (NS_SUCCEEDED(httpChannel->GetResponseHeader(NS_LITERAL_CSTRING("Content-Length"), val)))
02117             CopyASCIItoUCS2(val, contentLengthValue);
02118         val.Truncate();
02119 
02120         PRBool      changedFlag = PR_FALSE;
02121 
02122         PRUint32    respStatus;
02123         if (NS_SUCCEEDED(rv = httpChannel->GetResponseStatus(&respStatus)))
02124         {
02125             if ((respStatus >= 200) && (respStatus <= 299))
02126             {
02127                 if (!eTagValue.IsEmpty())
02128                 {
02129 #ifdef  DEBUG_BOOKMARK_PING_OUTPUT
02130                     printf("eTag: '%s'\n", NS_ConvertUCS2toUTF8(eTagValue).get());
02131 #endif
02132                     nsAutoString        eTagStr;
02133                     nsCOMPtr<nsIRDFNode>    currentETagNode;
02134                     if (NS_SUCCEEDED(rv = mInner->GetTarget(busyResource, kWEB_LastPingETag,
02135                         PR_TRUE, getter_AddRefs(currentETagNode))) && (rv != NS_RDF_NO_VALUE))
02136                     {
02137                         nsCOMPtr<nsIRDFLiteral> currentETagLit = do_QueryInterface(currentETagNode);
02138                         if (currentETagLit)
02139                         {
02140                             const PRUnichar* currentETagStr = nsnull;
02141                             currentETagLit->GetValueConst(&currentETagStr);
02142                             if ((currentETagStr) &&
02143                                 !eTagValue.Equals(nsDependentString(currentETagStr),
02144                                                   nsCaseInsensitiveStringComparator()))
02145                             {
02146                                 changedFlag = PR_TRUE;
02147                             }
02148                             eTagStr.Assign(eTagValue);
02149                             nsCOMPtr<nsIRDFLiteral> newETagLiteral;
02150                             if (NS_SUCCEEDED(rv = gRDF->GetLiteral(eTagStr.get(),
02151                                 getter_AddRefs(newETagLiteral))))
02152                             {
02153                                 rv = mInner->Change(busyResource, kWEB_LastPingETag,
02154                                     currentETagNode, newETagLiteral);
02155                             }
02156                         }
02157                     }
02158                     else
02159                     {
02160                         eTagStr.Assign(eTagValue);
02161                         nsCOMPtr<nsIRDFLiteral> newETagLiteral;
02162                         if (NS_SUCCEEDED(rv = gRDF->GetLiteral(eTagStr.get(),
02163                             getter_AddRefs(newETagLiteral))))
02164                         {
02165                             rv = mInner->Assert(busyResource, kWEB_LastPingETag,
02166                                 newETagLiteral, PR_TRUE);
02167                         }
02168                     }
02169                 }
02170             }
02171         }
02172 
02173         if ((changedFlag == PR_FALSE) && (!lastModValue.IsEmpty()))
02174         {
02175 #ifdef  DEBUG_BOOKMARK_PING_OUTPUT
02176             printf("Last-Modified: '%s'\n", lastModValue.get());
02177 #endif
02178             nsAutoString        lastModStr;
02179             nsCOMPtr<nsIRDFNode>    currentLastModNode;
02180             if (NS_SUCCEEDED(rv = mInner->GetTarget(busyResource, kWEB_LastPingModDate,
02181                 PR_TRUE, getter_AddRefs(currentLastModNode))) && (rv != NS_RDF_NO_VALUE))
02182             {
02183                 nsCOMPtr<nsIRDFLiteral> currentLastModLit = do_QueryInterface(currentLastModNode);
02184                 if (currentLastModLit)
02185                 {
02186                     const PRUnichar* currentLastModStr = nsnull;
02187                     currentLastModLit->GetValueConst(&currentLastModStr);
02188                     if ((currentLastModStr) &&
02189                         !lastModValue.Equals(nsDependentString(currentLastModStr),
02190                                              nsCaseInsensitiveStringComparator()))
02191                     {
02192                         changedFlag = PR_TRUE;
02193                     }
02194                     lastModStr.Assign(lastModValue);
02195                     nsCOMPtr<nsIRDFLiteral> newLastModLiteral;
02196                     if (NS_SUCCEEDED(rv = gRDF->GetLiteral(lastModStr.get(),
02197                         getter_AddRefs(newLastModLiteral))))
02198                     {
02199                         rv = mInner->Change(busyResource, kWEB_LastPingModDate,
02200                             currentLastModNode, newLastModLiteral);
02201                     }
02202                 }
02203             }
02204             else
02205             {
02206                 lastModStr.Assign(lastModValue);
02207                 nsCOMPtr<nsIRDFLiteral> newLastModLiteral;
02208                 if (NS_SUCCEEDED(rv = gRDF->GetLiteral(lastModStr.get(),
02209                     getter_AddRefs(newLastModLiteral))))
02210                 {
02211                     rv = mInner->Assert(busyResource, kWEB_LastPingModDate,
02212                         newLastModLiteral, PR_TRUE);
02213                 }
02214             }
02215         }
02216 
02217         if ((changedFlag == PR_FALSE) && (!contentLengthValue.IsEmpty()))
02218         {
02219 #ifdef  DEBUG_BOOKMARK_PING_OUTPUT
02220             printf("Content-Length: '%s'\n", contentLengthValue.get());
02221 #endif
02222             nsAutoString        contentLenStr;
02223             nsCOMPtr<nsIRDFNode>    currentContentLengthNode;
02224             if (NS_SUCCEEDED(rv = mInner->GetTarget(busyResource, kWEB_LastPingContentLen,
02225                 PR_TRUE, getter_AddRefs(currentContentLengthNode))) && (rv != NS_RDF_NO_VALUE))
02226             {
02227                 nsCOMPtr<nsIRDFLiteral> currentContentLengthLit = do_QueryInterface(currentContentLengthNode);
02228                 if (currentContentLengthLit)
02229                 {
02230                     const PRUnichar *currentContentLengthStr = nsnull;
02231                     currentContentLengthLit->GetValueConst(&currentContentLengthStr);
02232                     if ((currentContentLengthStr) &&
02233                         !contentLengthValue.Equals(nsDependentString(currentContentLengthStr),
02234                                                    nsCaseInsensitiveStringComparator()))
02235                     {
02236                         changedFlag = PR_TRUE;
02237                     }
02238                     contentLenStr.Assign(contentLengthValue);
02239                     nsCOMPtr<nsIRDFLiteral> newContentLengthLiteral;
02240                     if (NS_SUCCEEDED(rv = gRDF->GetLiteral(contentLenStr.get(),
02241                         getter_AddRefs(newContentLengthLiteral))))
02242                     {
02243                         rv = mInner->Change(busyResource, kWEB_LastPingContentLen,
02244                             currentContentLengthNode, newContentLengthLiteral);
02245                     }
02246                 }
02247             }
02248             else
02249             {
02250                 contentLenStr.Assign(contentLengthValue);
02251                 nsCOMPtr<nsIRDFLiteral> newContentLengthLiteral;
02252                 if (NS_SUCCEEDED(rv = gRDF->GetLiteral(contentLenStr.get(),
02253                     getter_AddRefs(newContentLengthLiteral))))
02254                 {
02255                     rv = mInner->Assert(busyResource, kWEB_LastPingContentLen,
02256                         newContentLengthLiteral, PR_TRUE);
02257                 }
02258             }
02259         }
02260 
02261         // update last poll date
02262         nsCOMPtr<nsIRDFDate>    dateLiteral;
02263         if (NS_SUCCEEDED(rv = gRDF->GetDateLiteral(PR_Now(), getter_AddRefs(dateLiteral))))
02264         {
02265             nsCOMPtr<nsIRDFNode>    lastPingNode;
02266             if (NS_SUCCEEDED(rv = mInner->GetTarget(busyResource, kWEB_LastPingDate, PR_TRUE,
02267                 getter_AddRefs(lastPingNode))) && (rv != NS_RDF_NO_VALUE))
02268             {
02269                 rv = mInner->Change(busyResource, kWEB_LastPingDate, lastPingNode, dateLiteral);
02270             }
02271             else
02272             {
02273                 rv = mInner->Assert(busyResource, kWEB_LastPingDate, dateLiteral, PR_TRUE);
02274             }
02275             NS_ASSERTION(rv == NS_RDF_ASSERTION_ACCEPTED, "unable to assert new time");
02276 
02277 //          mDirty = PR_TRUE;
02278         }
02279         else
02280         {
02281             NS_ERROR("unable to get date literal for now");
02282         }
02283 
02284         // If its changed, set the appropriate info
02285         if (changedFlag == PR_TRUE)
02286         {
02287 #ifdef  DEBUG_BOOKMARK_PING_OUTPUT
02288             printf("URL has changed!\n\n");
02289 #endif
02290 
02291             nsAutoString        schedule;
02292 
02293             nsCOMPtr<nsIRDFNode>    scheduleNode;
02294             if (NS_SUCCEEDED(rv = mInner->GetTarget(busyResource, kWEB_Schedule, PR_TRUE,
02295                 getter_AddRefs(scheduleNode))) && (rv != NS_RDF_NO_VALUE))
02296             {
02297                 nsCOMPtr<nsIRDFLiteral> scheduleLiteral = do_QueryInterface(scheduleNode);
02298                 if (scheduleLiteral)
02299                 {
02300                     const PRUnichar     *scheduleUni = nsnull;
02301                     if (NS_SUCCEEDED(rv = scheduleLiteral->GetValueConst(&scheduleUni))
02302                         && (scheduleUni))
02303                     {
02304                         schedule = scheduleUni;
02305                     }
02306                 }
02307             }
02308 
02309             // update icon?
02310             if (FindInReadable(NS_LITERAL_STRING("icon"),
02311                                schedule,
02312                                nsCaseInsensitiveStringComparator()))
02313             {
02314                 nsCOMPtr<nsIRDFLiteral> statusLiteral;
02315                 if (NS_SUCCEEDED(rv = gRDF->GetLiteral(NS_LITERAL_STRING("new").get(), getter_AddRefs(statusLiteral))))
02316                 {
02317                     nsCOMPtr<nsIRDFNode>    currentStatusNode;
02318                     if (NS_SUCCEEDED(rv = mInner->GetTarget(busyResource, kWEB_Status, PR_TRUE,
02319                         getter_AddRefs(currentStatusNode))) && (rv != NS_RDF_NO_VALUE))
02320                     {
02321                         rv = mInner->Change(busyResource, kWEB_Status, currentStatusNode, statusLiteral);
02322                     }
02323                     else
02324                     {
02325                         rv = mInner->Assert(busyResource, kWEB_Status, statusLiteral, PR_TRUE);
02326                     }
02327                     NS_ASSERTION(rv == NS_RDF_ASSERTION_ACCEPTED, "unable to assert changed status");
02328                 }
02329             }
02330             
02331             // play a sound?
02332             if (FindInReadable(NS_LITERAL_STRING("sound"),
02333                                schedule,
02334                                nsCaseInsensitiveStringComparator()))
02335             {
02336                 nsCOMPtr<nsISound> soundInterface =
02337                         do_CreateInstance("@mozilla.org/sound;1", &rv);
02338                 if (NS_SUCCEEDED(rv))
02339                 {
02340                     // for the moment, just beep
02341                     soundInterface->Beep();
02342                 }
02343             }
02344             
02345             PRBool      openURLFlag = PR_FALSE;
02346 
02347             // show an alert?
02348             if (FindInReadable(NS_LITERAL_STRING("alert"),
02349                                schedule,
02350                                nsCaseInsensitiveStringComparator()))
02351             {
02352                 nsCOMPtr<nsIPrompt> prompter;
02353                 NS_QueryNotificationCallbacks(channel, prompter);
02354                 if (!prompter)
02355                 {
02356                     nsCOMPtr<nsIWindowWatcher> wwatch(do_GetService(NS_WINDOWWATCHER_CONTRACTID));
02357                     if (wwatch)
02358                         wwatch->GetNewPrompter(0, getter_AddRefs(prompter));
02359                 }
02360 
02361                 if (prompter)
02362                 {
02363                     nsAutoString    promptStr;
02364                     getLocaleString("WebPageUpdated", promptStr);
02365                     if (!promptStr.IsEmpty())   promptStr.AppendLiteral("\n\n");
02366 
02367                     nsCOMPtr<nsIRDFNode>    nameNode;
02368                     if (NS_SUCCEEDED(mInner->GetTarget(busyResource, kNC_Name,
02369                         PR_TRUE, getter_AddRefs(nameNode))))
02370                     {
02371                         nsCOMPtr<nsIRDFLiteral> nameLiteral = do_QueryInterface(nameNode);
02372                         if (nameLiteral)
02373                         {
02374                             const PRUnichar *nameUni = nsnull;
02375                             if (NS_SUCCEEDED(rv = nameLiteral->GetValueConst(&nameUni))
02376                                 && (nameUni))
02377                             {
02378                                 nsAutoString    info;
02379                                 getLocaleString("WebPageTitle", info);
02380                                 promptStr += info;
02381                                 promptStr.AppendLiteral(" ");
02382                                 promptStr += nameUni;
02383                                 promptStr.AppendLiteral("\n");
02384                                 getLocaleString("WebPageURL", info);
02385                                 promptStr += info;
02386                                 promptStr.AppendLiteral(" ");
02387                             }
02388                         }
02389                     }
02390                     promptStr.Append(url);
02391                     
02392                     nsAutoString    temp;
02393                     getLocaleString("WebPageAskDisplay", temp);
02394                     if (!temp.IsEmpty())
02395                     {
02396                         promptStr.AppendLiteral("\n\n");
02397                         promptStr += temp;
02398                     }
02399 
02400                     nsAutoString    stopOption;
02401                     getLocaleString("WebPageAskStopOption", stopOption);
02402                     PRBool      stopCheckingFlag = PR_FALSE;
02403                     rv = prompter->ConfirmCheck(nsnull, promptStr.get(), stopOption.get(),
02404                                   &stopCheckingFlag, &openURLFlag);
02405                     if (NS_FAILED(rv))
02406                     {
02407                         openURLFlag = PR_FALSE;
02408                         stopCheckingFlag = PR_FALSE;
02409                     }
02410                     if (stopCheckingFlag == PR_TRUE)
02411                     {
02412 #ifdef  DEBUG_BOOKMARK_PING_OUTPUT
02413                         printf("\nStop checking this URL.\n");
02414 #endif
02415                         rv = mInner->Unassert(busyResource, kWEB_Schedule, scheduleNode);
02416                         NS_ASSERTION(NS_SUCCEEDED(rv), "unable to unassert kWEB_Schedule");
02417                     }
02418                 }
02419             }
02420 
02421             // open the URL in a new window?
02422             if ((openURLFlag == PR_TRUE) ||
02423                 FindInReadable(NS_LITERAL_STRING("open"),
02424                                schedule,
02425                                nsCaseInsensitiveStringComparator()))
02426             {
02427                 if (NS_SUCCEEDED(rv))
02428                 {
02429                     nsCOMPtr<nsIWindowWatcher> wwatch(do_GetService(NS_WINDOWWATCHER_CONTRACTID));
02430                     if (wwatch)
02431                     {
02432                         nsCOMPtr<nsIDOMWindow> newWindow;
02433             nsCOMPtr<nsISupportsArray> suppArray;
02434             rv = NS_NewISupportsArray(getter_AddRefs(suppArray));
02435             if (NS_FAILED(rv)) return rv;
02436             nsCOMPtr<nsISupportsString> suppString(do_CreateInstance("@mozilla.org/supports-string;1", &rv));
02437             if (!suppString) return rv;
02438             rv = suppString->SetData(url);
02439             if (NS_FAILED(rv)) return rv;
02440             suppArray->AppendElement(suppString);
02441     
02442             nsCOMPtr<nsICmdLineHandler> handler(do_GetService("@mozilla.org/commandlinehandler/general-startup;1?type=browser", &rv));    
02443             if (NS_FAILED(rv)) return rv;
02444    
02445             nsXPIDLCString chromeUrl;
02446             rv = handler->GetChromeUrlForTask(getter_Copies(chromeUrl));
02447             if (NS_FAILED(rv)) return rv;
02448 
02449             wwatch->OpenWindow(0, chromeUrl, "_blank", "chrome,dialog=no,all", 
02450                                suppArray, getter_AddRefs(newWindow));
02451                     }
02452                 }
02453             }
02454         }
02455 #ifdef  DEBUG_BOOKMARK_PING_OUTPUT
02456         else
02457         {
02458             printf("URL has not changed status.\n\n");
02459         }
02460 #endif
02461     }
02462 
02463     busyResource = nsnull;
02464     busySchedule = PR_FALSE;
02465 
02466     return NS_OK;
02467 }
02468 
02469 
02470 // nsIObserver methods
02471 
02472 NS_IMETHODIMP nsBookmarksService::Observe(nsISupports *aSubject, const char *aTopic, const PRUnichar *someData)
02473 {
02474     nsresult rv = NS_OK;
02475 
02476     if (!nsCRT::strcmp(aTopic, "profile-before-change"))
02477     {
02478         // The profile has not changed yet.
02479         rv = Flush();
02480     
02481         if (!nsCRT::strcmp(someData, NS_LITERAL_STRING("shutdown-cleanse").get()))
02482         {
02483             if (mBookmarksFile)
02484             {
02485                 mBookmarksFile->Remove(PR_FALSE);
02486             }
02487         }
02488     }    
02489     else if (mBookmarksFile && !nsCRT::strcmp(aTopic, "profile-after-change"))
02490     {
02491         // The profile has already changed.
02492         rv = LoadBookmarks();
02493     }
02494     else if (!nsCRT::strcmp(aTopic, NS_PREFBRANCH_PREFCHANGE_TOPIC_ID))
02495     {
02496         rv = Flush();
02497         if (NS_SUCCEEDED(rv))
02498             rv = LoadBookmarks();
02499     }
02500 
02501     return rv;
02502 }
02503 
02504 
02506 // nsISupports methods
02507 
02508 NS_IMPL_ADDREF(nsBookmarksService)
02509 
02510 NS_IMETHODIMP_(nsrefcnt)
02511 nsBookmarksService::Release()
02512 {
02513     // We need a special implementation of Release() because our mInner
02514     // holds a Circular References back to us.
02515     NS_PRECONDITION(PRInt32(mRefCnt) > 0, "duplicate release");
02516     --mRefCnt;
02517     NS_LOG_RELEASE(this, mRefCnt, "nsBookmarksService");
02518 
02519     if (mInner && mRefCnt == 1) {
02520         nsIRDFDataSource* tmp = mInner;
02521         mInner = nsnull;
02522         NS_IF_RELEASE(tmp);
02523         return 0;
02524     }
02525     else if (mRefCnt == 0) {
02526         delete this;
02527         return 0;
02528     }
02529     else {
02530         return mRefCnt;
02531     }
02532 }
02533 
02534 NS_IMPL_QUERY_INTERFACE10(nsBookmarksService,
02535              nsIBookmarksService,
02536              nsIRDFDataSource,
02537              nsIRDFRemoteDataSource,
02538              nsIRDFPropagatableDataSource,
02539              nsIRDFObserver,
02540              nsIStreamListener,
02541              nsIRequestObserver,
02542              nsICharsetResolver,
02543              nsIObserver,
02544              nsISupportsWeakReference)
02545 
02546 
02547 
02548 // nsIBookmarksService
02549 
02550 nsresult
02551 nsBookmarksService::InsertResource(nsIRDFResource* aResource,
02552                                    nsIRDFResource* aParentFolder, PRInt32 aIndex)
02553 {
02554     nsresult rv = NS_OK;
02555     // Add to container if the parent folder is non null 
02556     if (aParentFolder)
02557     {
02558         nsCOMPtr<nsIRDFContainer> container(do_CreateInstance("@mozilla.org/rdf/container;1", &rv));
02559         if (NS_FAILED(rv)) 
02560             return rv;
02561         rv = container->Init(mInner, aParentFolder);
02562         if (NS_FAILED(rv)) 
02563             return rv;
02564         // if the index in the js call is null or undefined, aIndex will be equal to 0
02565         if (aIndex > 0) 
02566             rv = container->InsertElementAt(aResource, aIndex, PR_TRUE);
02567         else
02568             rv = container->AppendElement(aResource);
02569 
02570         mDirty = PR_TRUE;
02571     }
02572     return rv;
02573 }
02574 
02575 NS_IMETHODIMP
02576 nsBookmarksService::CreateFolder(const PRUnichar* aName, 
02577                                  nsIRDFResource** aResult)
02578 {
02579     nsresult rv;
02580 
02581     // Resource: Folder ID
02582     nsCOMPtr<nsIRDFResource> folderResource;
02583     rv = gRDF->GetAnonymousResource(getter_AddRefs(folderResource));
02584     if (NS_FAILED(rv)) 
02585         return rv;
02586 
02587     rv = gRDFC->MakeSeq(mInner, folderResource, nsnull);
02588     if (NS_FAILED(rv)) 
02589         return rv;
02590 
02591     // Literal: Folder Name
02592     nsCOMPtr<nsIRDFLiteral> nameLiteral;
02593     nsAutoString folderName; 
02594     folderName.Assign(aName);
02595     if (folderName.IsEmpty()) {
02596         getLocaleString("NewFolder", folderName);
02597 
02598         rv = gRDF->GetLiteral(folderName.get(), getter_AddRefs(nameLiteral));
02599         if (NS_FAILED(rv)) 
02600             return rv;
02601     }
02602     else
02603     {
02604         rv = gRDF->GetLiteral(aName, getter_AddRefs(nameLiteral));
02605         if (NS_FAILED(rv)) 
02606             return rv;
02607     }
02608 
02609     rv = mInner->Assert(folderResource, kNC_Name, nameLiteral, PR_TRUE);
02610     if (NS_FAILED(rv)) 
02611         return rv;
02612 
02613     // Date: Date of Creation
02614     // Convert the current date/time from microseconds (PRTime) to seconds.
02615     nsCOMPtr<nsIRDFDate> dateLiteral;
02616     rv = gRDF->GetDateLiteral(PR_Now(), getter_AddRefs(dateLiteral));
02617     if (NS_FAILED(rv)) 
02618         return rv;
02619     rv = mInner->Assert(folderResource, kNC_BookmarkAddDate, dateLiteral, PR_TRUE);
02620     if (NS_FAILED(rv)) 
02621         return rv;
02622 
02623     *aResult = folderResource;
02624     NS_ADDREF(*aResult);
02625 
02626     return rv;
02627 }
02628 
02629 NS_IMETHODIMP
02630 nsBookmarksService::CreateFolderInContainer(const PRUnichar* aName, 
02631                                             nsIRDFResource* aParentFolder, PRInt32 aIndex,
02632                                             nsIRDFResource** aResult)
02633 {
02634     nsresult rv = CreateFolder(aName, aResult);
02635     if (NS_SUCCEEDED(rv))
02636         rv = InsertResource(*aResult, aParentFolder, aIndex);
02637     return rv;
02638 }
02639 
02640 NS_IMETHODIMP
02641 nsBookmarksService::CreateGroup(const PRUnichar* aName, 
02642                                 nsIRDFResource** aResult)
02643 {
02644     nsresult rv;
02645     rv = CreateFolderInContainer(aName, nsnull, nsnull, aResult);
02646     if (NS_SUCCEEDED(rv))
02647         rv = mInner->Assert(*aResult, kNC_FolderGroup, kTrueLiteral, PR_TRUE);
02648     return rv;
02649 }
02650 
02651 NS_IMETHODIMP
02652 nsBookmarksService::CreateGroupInContainer(const PRUnichar* aName, 
02653                                            nsIRDFResource* aParentFolder, PRInt32 aIndex,
02654                                            nsIRDFResource** aResult)
02655 {
02656     nsresult rv = CreateGroup(aName, aResult);
02657     if (NS_SUCCEEDED(rv))
02658         rv = InsertResource(*aResult, aParentFolder, aIndex);
02659     return rv;
02660 }
02661 
02662 int
02663 nsBookmarksService::Compare(const void* aElement1, const void* aElement2, void* aData)
02664 {
02665     const ElementInfo* elementInfo1 =
02666       NS_STATIC_CAST(ElementInfo*, NS_CONST_CAST(void*, aElement1));
02667     const ElementInfo* elementInfo2 =
02668       NS_STATIC_CAST(ElementInfo*, NS_CONST_CAST(void*, aElement2));
02669     SortInfo* sortInfo = (SortInfo*)aData;
02670 
02671     if (sortInfo->mFoldersFirst) {
02672         if (elementInfo1->mIsFolder) {
02673             if (!elementInfo2->mIsFolder) {
02674                 return -1;
02675             }
02676         }
02677         else {
02678             if (elementInfo2->mIsFolder) {
02679                 return 1;
02680             }
02681         }
02682     }
02683  
02684     PRInt32 result = 0;
02685  
02686     nsIRDFNode* node1 = elementInfo1->mNode;
02687     nsIRDFNode* node2 = elementInfo2->mNode;
02688  
02689     // Literals?
02690     nsCOMPtr<nsIRDFLiteral> literal1 = do_QueryInterface(node1);
02691     if (literal1) {
02692         nsCOMPtr<nsIRDFLiteral> literal2 = do_QueryInterface(node2);
02693         if (literal2) {
02694             const PRUnichar* value1;
02695             literal1->GetValueConst(&value1);
02696             const PRUnichar* value2;
02697             literal2->GetValueConst(&value2);
02698 
02699             if (gCollation) {
02700                 gCollation->CompareString(nsICollation::kCollationCaseInSensitive,
02701                                           nsDependentString(value1),
02702                                           nsDependentString(value2),
02703                                           &result);
02704             }
02705             else {
02706                 result = ::Compare(nsDependentString(value1),
02707                                    nsDependentString(value2),
02708                                    nsCaseInsensitiveStringComparator());
02709             }
02710 
02711             return result * sortInfo->mDirection;
02712         }
02713     }
02714  
02715     // Dates?
02716     nsCOMPtr<nsIRDFDate> date1 = do_QueryInterface(node1);
02717     if (date1) {
02718         nsCOMPtr<nsIRDFDate> date2 = do_QueryInterface(node2);
02719         if (date2) {
02720             PRTime value1;
02721             date1->GetValue(&value1);
02722             PRTime value2;
02723             date2->GetValue(&value2);
02724 
02725             PRInt64 delta;
02726             LL_SUB(delta, value1, value2);
02727 
02728             if (LL_IS_ZERO(delta))
02729                 result = 0;
02730             else if (LL_GE_ZERO(delta))
02731                 result = 1;
02732             else
02733                 result = -1;
02734 
02735             return result * sortInfo->mDirection;
02736         }
02737     }
02738 
02739     // Ack! Apples & oranges.
02740     return 0;
02741 }
02742  
02743 nsresult
02744 nsBookmarksService::Sort(nsIRDFResource* aFolder, nsIRDFResource* aProperty,
02745                          PRInt32 aDirection, PRBool aFoldersFirst,
02746                          PRBool aRecurse)
02747 {
02748     nsresult rv;
02749     nsCOMPtr<nsIRDFContainer> container =
02750         do_CreateInstance("@mozilla.org/rdf/container;1", &rv);
02751     if (NS_FAILED(rv))
02752         return rv;
02753 
02754     rv = container->Init(mInner, aFolder);
02755     if (NS_FAILED(rv))
02756         return rv;
02757 
02758     nsCOMPtr<nsISimpleEnumerator> elements;
02759     rv = container->GetElements(getter_AddRefs(elements));
02760     if (NS_FAILED(rv))
02761         return rv;
02762 
02763     ElementArray elementArray;
02764 
02765     PRBool hasMore = PR_FALSE;
02766     while (NS_SUCCEEDED(rv = elements->HasMoreElements(&hasMore)) &&
02767            hasMore) {
02768         nsCOMPtr<nsISupports> supports;
02769         rv = elements->GetNext(getter_AddRefs(supports));
02770         if (NS_FAILED(rv))
02771             return rv;
02772 
02773         nsCOMPtr<nsIRDFResource> element = do_QueryInterface(supports, &rv);
02774         if (NS_FAILED(rv))
02775             return rv;
02776 
02777         nsCOMPtr<nsIRDFNode> node;
02778         rv = mInner->GetTarget(element, aProperty, PR_TRUE, getter_AddRefs(node));
02779         if (NS_FAILED(rv))
02780             return rv;
02781 
02782         if (!node) {
02783             if (aProperty == kNC_BookmarkAddDate ||
02784                 aProperty == kWEB_LastModifiedDate ||
02785                 aProperty == kWEB_LastVisitDate) {
02786                 node = do_QueryInterface(kEmptyDate);
02787             }
02788             else {
02789                 node = do_QueryInterface(kEmptyLiteral);
02790             }
02791         }
02792 
02793         PRBool isContainer;
02794         rv = gRDFC->IsContainer(mInner, element, &isContainer);
02795         if (NS_FAILED(rv))
02796             return rv;
02797 
02798         PRBool isGroup;
02799         rv = mInner->HasAssertion(element, kNC_FolderGroup, kTrueLiteral,
02800                                   PR_TRUE, &isGroup);
02801         if (NS_FAILED(rv))
02802             return rv;
02803 
02804         ElementInfo* elementInfo = new ElementInfo(element, node,
02805                                                    isContainer && !isGroup);
02806         if (!elementInfo)
02807             return NS_ERROR_OUT_OF_MEMORY;
02808 
02809         elementArray.AppendElement(elementInfo);
02810 
02811         if (isContainer && aRecurse) {
02812             rv = Sort(element, aProperty, aDirection, aFoldersFirst, aRecurse);
02813             if (NS_FAILED(rv))
02814                 return rv;
02815         }
02816     }
02817 
02818     SortInfo sortInfo(aDirection, aFoldersFirst);
02819     elementArray.Sort(Compare, &sortInfo);
02820 
02821     // XXXvarga If we ever make it so that ordinals are guaranteed to be unique,
02822     // the code below can be significantly simplified.
02823     for (PRInt32 j = elementArray.Count() - 1; j >= 0; --j) {
02824         ElementInfo* elementInfo = elementArray[j];
02825 
02826         PRInt32 oldIndex;
02827         rv = gRDFC->IndexOf(mInner, aFolder, elementInfo->mElement, &oldIndex);
02828         if (NS_FAILED(rv))
02829             return rv;
02830 
02831         // The old index is 1 based.
02832         if (oldIndex != j + 1) {
02833             nsCOMPtr<nsIRDFResource> oldOrdinal;
02834             rv = gRDFC->IndexToOrdinalResource(oldIndex, getter_AddRefs(oldOrdinal));
02835             if (NS_FAILED(rv))
02836                 return rv;
02837 
02838             nsCOMPtr<nsIRDFResource> newOrdinal;
02839             rv = gRDFC->IndexToOrdinalResource(j + 1, getter_AddRefs(newOrdinal));
02840             if (NS_FAILED(rv))
02841                 return rv;
02842 
02843             // We need to find the correct element for the old ordinal,
02844             // it happens that there are two elements with the same ordinal.
02845             nsCOMPtr<nsISimpleEnumerator> elements;
02846             rv = mInner->GetTargets(aFolder, oldOrdinal, PR_TRUE,
02847                                     getter_AddRefs(elements));
02848 
02849             PRBool hasMore = PR_FALSE;
02850             while (NS_SUCCEEDED(rv = elements->HasMoreElements(&hasMore)) &&
02851                    hasMore) {
02852                 nsCOMPtr<nsISupports> supports;
02853                 rv = elements->GetNext(getter_AddRefs(supports));
02854                 if (NS_FAILED(rv))
02855                     return rv;
02856 
02857                 nsCOMPtr<nsIRDFNode> element = do_QueryInterface(supports);
02858                 if (element == elementInfo->mElement) {
02859                     rv = mInner->Unassert(aFolder, oldOrdinal, element);
02860                     if (NS_FAILED(rv))
02861                         return rv;
02862 
02863                     rv = mInner->Assert(aFolder, newOrdinal, element, PR_TRUE);
02864                     if (NS_FAILED(rv))
02865                         return rv;
02866                     break;
02867                 }
02868             }
02869         }
02870     }
02871 
02872     return NS_OK;
02873 }
02874 
02875 NS_IMETHODIMP
02876 nsBookmarksService::SortFolder(nsIRDFResource* aFolder,
02877                                nsIRDFResource* aProperty,
02878                                PRInt32 aDirection,
02879                                PRBool aFoldersFirst,
02880                                PRBool aRecurse)
02881 {
02882 #ifdef DEBUG_varga
02883     PRIntervalTime startTime =  PR_IntervalNow();
02884 #endif
02885 
02886     BeginUpdateBatch();
02887     SetPropagateChanges(PR_FALSE);
02888     nsresult rv = Sort(aFolder, aProperty, aDirection, aFoldersFirst,
02889                        aRecurse);
02890     SetPropagateChanges(PR_TRUE);
02891     EndUpdateBatch();
02892 
02893 #ifdef DEBUG_varga
02894     PRIntervalTime endTime =  PR_IntervalNow();
02895     printf("Time spent in SortFolder(): %d msec\n", PR_IntervalToMilliseconds(endTime - startTime));
02896 #endif
02897 
02898     return rv;
02899 }
02900 
02901 nsresult
02902 nsBookmarksService::GetURLFromResource(nsIRDFResource* aResource,
02903                                        nsAString& aURL)
02904 {
02905     NS_ENSURE_ARG(aResource);
02906 
02907     nsCOMPtr<nsIRDFNode> urlNode;
02908     nsresult rv = mInner->GetTarget(aResource, kNC_URL, PR_TRUE, getter_AddRefs(urlNode));
02909     if (NS_FAILED(rv))
02910         return rv;
02911 
02912     if (urlNode) {
02913         nsCOMPtr<nsIRDFLiteral> urlLiteral = do_QueryInterface(urlNode, &rv);
02914         if (NS_FAILED(rv))
02915             return rv;
02916 
02917         const PRUnichar* url = nsnull;
02918         rv = urlLiteral->GetValueConst(&url);
02919         if (NS_FAILED(rv))
02920             return rv;
02921 
02922         aURL.Assign(url);
02923     }
02924 
02925     return NS_OK;
02926 }
02927 
02928 nsresult
02929 nsBookmarksService::CopyResource(nsIRDFResource* aOldResource,
02930                                  nsIRDFResource* aNewResource)
02931 {
02932     // Make all arcs coming out of aOldResource also come out of
02933     // aNewResource. Wallop any previous values.
02934     nsCOMPtr<nsISimpleEnumerator> arcsOut;
02935     nsresult rv = mInner->ArcLabelsOut(aOldResource, getter_AddRefs(arcsOut));
02936     if (NS_FAILED(rv))
02937         return rv;
02938 
02939     while (1) {
02940         PRBool hasMoreArcsOut;
02941         rv = arcsOut->HasMoreElements(&hasMoreArcsOut);
02942         if (NS_FAILED(rv))
02943             return rv;
02944 
02945         if (!hasMoreArcsOut)
02946             break;
02947 
02948         nsCOMPtr<nsISupports> supports;
02949         rv = arcsOut->GetNext(getter_AddRefs(supports));
02950         if (NS_FAILED(rv))
02951             return rv;
02952 
02953         nsCOMPtr<nsIRDFResource> property = do_QueryInterface(supports);
02954         if (!property)
02955             return NS_ERROR_UNEXPECTED;
02956 
02957         nsCOMPtr<nsIRDFNode> oldvalue;
02958         rv = mInner->GetTarget(aNewResource, property, PR_TRUE,
02959                                getter_AddRefs(oldvalue));
02960         if (NS_FAILED(rv))
02961             return rv;
02962 
02963         nsCOMPtr<nsIRDFNode> newvalue;
02964         rv = mInner->GetTarget(aOldResource, property, PR_TRUE,
02965                                getter_AddRefs(newvalue));
02966         if (NS_FAILED(rv))
02967             return rv;
02968 
02969         if (oldvalue) {
02970             if (newvalue) {
02971                  rv = mInner->Change(aNewResource, property, oldvalue, newvalue);
02972             }
02973             else {
02974                  rv = mInner->Unassert(aNewResource, property, oldvalue);
02975             }
02976         }
02977         else if (newvalue) {
02978             rv = mInner->Assert(aNewResource, property, newvalue, PR_TRUE);
02979         }
02980 
02981         if (NS_FAILED(rv))
02982             return rv;
02983     }
02984 
02985     // Make all arcs pointing to aOldResource now point to aNewResource.
02986     nsCOMPtr<nsISimpleEnumerator> arcsIn;
02987     rv = mInner->ArcLabelsIn(aOldResource, getter_AddRefs(arcsIn));
02988     if (NS_FAILED(rv))
02989         return rv;
02990                                                                                 
02991     while (1) {
02992         PRBool hasMoreArcsIn;
02993         rv = arcsIn->HasMoreElements(&hasMoreArcsIn);
02994         if (NS_FAILED(rv))
02995             return rv;
02996 
02997         if (!hasMoreArcsIn)
02998             break;
02999 
03000         nsCOMPtr<nsISupports> supports;
03001         rv = arcsIn->GetNext(getter_AddRefs(supports));
03002         if (NS_FAILED(rv))
03003             return rv;
03004                                                                                 
03005         nsCOMPtr<nsIRDFResource> property = do_QueryInterface(supports);
03006         if (!property)
03007             return NS_ERROR_UNEXPECTED;
03008 
03009         nsCOMPtr<nsISimpleEnumerator> sources;
03010         rv = GetSources(property, aOldResource, PR_TRUE,
03011                         getter_AddRefs(sources));
03012         if (NS_FAILED(rv))
03013             return rv;
03014 
03015         while (1) {
03016             PRBool hasMoreSrcs;
03017             rv = sources->HasMoreElements(&hasMoreSrcs);
03018             if (NS_FAILED(rv))
03019                 return rv;
03020 
03021             if (!hasMoreSrcs)
03022                 break;
03023 
03024             nsCOMPtr<nsISupports> supports;
03025             rv = sources->GetNext(getter_AddRefs(supports));
03026             if (NS_FAILED(rv))
03027                 return rv;
03028 
03029             nsCOMPtr<nsIRDFResource> source = do_QueryInterface(supports);
03030             if (!source)
03031                 return NS_ERROR_UNEXPECTED;
03032 
03033             rv = mInner->Change(source, property, aOldResource, aNewResource);
03034             if (NS_FAILED(rv))
03035                 return rv;
03036         }
03037     }
03038 
03039     return NS_OK;
03040 }
03041 
03042 nsresult
03043 nsBookmarksService::SetNewPersonalToolbarFolder(nsIRDFResource* aFolder)
03044 {
03045     nsCOMPtr<nsIRDFResource> tempResource;
03046     nsresult rv = gRDF->GetAnonymousResource(getter_AddRefs(tempResource));
03047     if (NS_FAILED(rv))
03048         return rv;
03049 
03050     rv = CopyResource(kNC_PersonalToolbarFolder, tempResource);
03051     if (NS_FAILED(rv))
03052         return rv;
03053 
03054     rv = CopyResource(aFolder, kNC_PersonalToolbarFolder);
03055     if (NS_FAILED(rv))
03056         return rv;
03057 
03058     return CopyResource(tempResource, aFolder);
03059 }
03060 
03061 NS_IMETHODIMP
03062 nsBookmarksService::CreateBookmark(const PRUnichar* aName,
03063                                    const PRUnichar* aURL, 
03064                                    const PRUnichar* aShortcutURL,
03065                                    const PRUnichar* aDescription,
03066                                    const PRUnichar* aDocCharSet, 
03067                                    nsIRDFResource** aResult)
03068 {
03069     // Resource: Bookmark ID
03070     nsCOMPtr<nsIRDFResource> bookmarkResource;
03071     nsresult rv = gRDF->GetAnonymousResource(getter_AddRefs(bookmarkResource));
03072 
03073     if (NS_FAILED(rv)) 
03074         return rv;
03075 
03076     // Literal: Folder Name
03077     nsCOMPtr<nsIRDFLiteral> nameLiteral;
03078     nsAutoString bookmarkName; 
03079     bookmarkName.Assign(aName);
03080     if (bookmarkName.IsEmpty()) {
03081         getLocaleString("NewBookmark", bookmarkName);
03082 
03083         rv = gRDF->GetLiteral(bookmarkName.get(), getter_AddRefs(nameLiteral));
03084         if (NS_FAILED(rv)) 
03085             return rv;
03086     }
03087     else
03088     {
03089         rv = gRDF->GetLiteral(aName, getter_AddRefs(nameLiteral));
03090         if (NS_FAILED(rv)) 
03091             return rv;
03092     }
03093 
03094     rv = mInner->Assert(bookmarkResource, kNC_Name, nameLiteral, PR_TRUE);
03095     if (NS_FAILED(rv)) 
03096         return rv;
03097 
03098     // Resource: URL
03099     nsAutoString url;
03100     url.Assign(aURL);
03101     nsCOMPtr<nsIRDFLiteral> urlLiteral;
03102     rv = gRDF->GetLiteral(url.get(), getter_AddRefs(urlLiteral));
03103     if (NS_FAILED(rv)) 
03104         return rv;
03105     rv = mInner->Assert(bookmarkResource, kNC_URL, urlLiteral, PR_TRUE);
03106     if (NS_FAILED(rv)) 
03107         return rv;
03108 
03109     // Literal: Shortcut URL
03110     if (aShortcutURL && *aShortcutURL) {
03111         nsCOMPtr<nsIRDFLiteral> shortcutLiteral;
03112         rv = gRDF->GetLiteral(aShortcutURL, getter_AddRefs(shortcutLiteral));
03113         if (NS_FAILED(rv)) 
03114             return rv;
03115         rv = mInner->Assert(bookmarkResource, kNC_ShortcutURL, shortcutLiteral, PR_TRUE);
03116         if (NS_FAILED(rv)) 
03117             return rv;
03118     }
03119 
03120     // Literal: Description
03121     if (aDescription && *aDescription) {
03122         nsCOMPtr<nsIRDFLiteral> descriptionLiteral;
03123         rv = gRDF->GetLiteral(aDescription, getter_AddRefs(descriptionLiteral));
03124         if (NS_FAILED(rv)) 
03125             return rv;
03126         rv = mInner->Assert(bookmarkResource, kNC_Description, descriptionLiteral, PR_TRUE);
03127         if (NS_FAILED(rv)) 
03128             return rv;
03129     }
03130 
03131     // Date: Date of Creation
03132     // Convert the current date/time from microseconds (PRTime) to seconds.
03133     nsCOMPtr<nsIRDFDate> dateLiteral;
03134     rv = gRDF->GetDateLiteral(PR_Now(), getter_AddRefs(dateLiteral));
03135     if (NS_FAILED(rv)) 
03136         return rv;
03137     rv = mInner->Assert(bookmarkResource, kNC_BookmarkAddDate, dateLiteral, PR_TRUE);
03138     if (NS_FAILED(rv)) 
03139         return rv;
03140 
03141     // Literal: Charset used when last visited
03142     nsAutoString charset; 
03143     charset.Assign(aDocCharSet);
03144     if (!charset.IsEmpty()) {
03145         nsCOMPtr<nsIRDFLiteral> charsetLiteral;
03146         rv = gRDF->GetLiteral(aDocCharSet, getter_AddRefs(charsetLiteral));
03147         if (NS_FAILED(rv)) 
03148             return rv;
03149         rv = mInner->Assert(bookmarkResource, kWEB_LastCharset, charsetLiteral, PR_TRUE);
03150         if (NS_FAILED(rv)) 
03151             return rv;
03152     }
03153 
03154     *aResult = bookmarkResource;
03155     NS_ADDREF(*aResult);
03156 
03157     return rv;
03158 }
03159 
03160 NS_IMETHODIMP
03161 nsBookmarksService::CreateBookmarkInContainer(const PRUnichar* aName,
03162                                               const PRUnichar* aURL, 
03163                                               const PRUnichar* aShortcutURL, 
03164                                               const PRUnichar* aDescription, 
03165                                               const PRUnichar* aDocCharSet, 
03166                                               nsIRDFResource* aParentFolder,
03167                                               PRInt32 aIndex,
03168                                               nsIRDFResource** aResult)
03169 {
03170     nsresult rv = CreateBookmark(aName, aURL, aShortcutURL, aDescription, aDocCharSet, aResult);
03171     if (NS_SUCCEEDED(rv))
03172         rv = InsertResource(*aResult, aParentFolder, aIndex);
03173     return rv;
03174 }
03175 
03176 NS_IMETHODIMP
03177 nsBookmarksService::CreateSeparator(nsIRDFResource** aResult)
03178 {
03179     nsresult rv;
03180 
03181     // create the anonymous resource for the separator
03182     nsCOMPtr<nsIRDFResource> separatorResource;
03183     rv = gRDF->GetAnonymousResource(getter_AddRefs(separatorResource));
03184     if (NS_FAILED(rv)) 
03185         return rv;
03186 
03187     // Assert the separator type arc
03188     rv = mInner->Assert(separatorResource, kRDF_type, kNC_BookmarkSeparator, PR_TRUE);
03189     if (NS_FAILED(rv))
03190         return rv;
03191 
03192     *aResult = separatorResource;
03193     NS_ADDREF(*aResult);
03194 
03195     return rv;
03196 }
03197 
03198 NS_IMETHODIMP
03199 nsBookmarksService::CloneResource(nsIRDFResource* aSource,
03200                                   nsIRDFResource** aResult)
03201 {
03202     nsCOMPtr<nsIRDFResource> newResource;
03203     nsresult rv = gRDF->GetAnonymousResource(getter_AddRefs(newResource));
03204     NS_ENSURE_SUCCESS(rv, rv);
03205 
03206     nsCOMPtr<nsISimpleEnumerator> arcs;
03207     rv = mInner->ArcLabelsOut(aSource, getter_AddRefs(arcs));
03208     NS_ENSURE_SUCCESS(rv, rv);
03209 
03210     PRBool hasMore = PR_FALSE;
03211     while (NS_SUCCEEDED(arcs->HasMoreElements(&hasMore)) && hasMore) {
03212         nsCOMPtr<nsISupports> supports;
03213         rv = arcs->GetNext(getter_AddRefs(supports));
03214         NS_ENSURE_SUCCESS(rv, rv);
03215 
03216         nsCOMPtr<nsIRDFResource> property = do_QueryInterface(supports, &rv);
03217         NS_ENSURE_SUCCESS(rv, rv);
03218       
03219         // Don't duplicate the folder type.
03220         // (NC:PersonalToolbarFolder, NC:NewBookmarkFolder, etc...)
03221         PRBool isFolderType;
03222         rv = property->EqualsNode(kNC_FolderType, &isFolderType);
03223         NS_ENSURE_SUCCESS(rv, rv);
03224         if (isFolderType)
03225             continue;
03226 
03227         nsCOMPtr<nsIRDFNode> target;
03228         rv = mInner->GetTarget(aSource, property, PR_TRUE, getter_AddRefs(target));
03229         NS_ENSURE_SUCCESS(rv, rv);
03230  
03231         // Test if the arc points to a child.
03232         PRBool isOrdinal;
03233         rv = gRDFC->IsOrdinalProperty(property, &isOrdinal);
03234         NS_ENSURE_SUCCESS(rv, rv);
03235 
03236         if (isOrdinal) {
03237             nsCOMPtr<nsIRDFResource> oldChild = do_QueryInterface(target);
03238             nsCOMPtr<nsIRDFResource> newChild;
03239             rv = CloneResource(oldChild, getter_AddRefs(newChild));
03240             NS_ENSURE_SUCCESS(rv, rv);
03241 
03242             rv = mInner->Assert(newResource, property, newChild, PR_TRUE);
03243         }
03244         else {
03245             rv = mInner->Assert(newResource, property, target, PR_TRUE);
03246         }
03247         NS_ENSURE_SUCCESS(rv, rv);
03248     }
03249 
03250     NS_ADDREF(*aResult = newResource);
03251 
03252     return NS_OK;
03253 }
03254 
03255 NS_IMETHODIMP
03256 nsBookmarksService::AddBookmarkImmediately(const PRUnichar *aURI,
03257                                            const PRUnichar *aTitle, 
03258                                            PRInt32 aBookmarkType, 
03259                                            const PRUnichar *aCharset)
03260 {
03261     nsresult rv;
03262 
03263     // Figure out where to add the new bookmark
03264     nsCOMPtr<nsIRDFResource> bookmarkFolder = kNC_NewBookmarkFolder;
03265 
03266     switch(aBookmarkType)
03267     {
03268     case BOOKMARK_SEARCH_TYPE:
03269     case BOOKMARK_FIND_TYPE:
03270         bookmarkFolder = kNC_NewSearchFolder;
03271         break;
03272     }
03273 
03274     nsCOMPtr<nsIRDFResource> destinationFolder;
03275     rv = getFolderViaHint(bookmarkFolder, PR_TRUE, getter_AddRefs(destinationFolder));
03276     if (NS_FAILED(rv)) 
03277         return rv;
03278 
03279     nsCOMPtr<nsIRDFResource> bookmark;
03280     return CreateBookmarkInContainer(aTitle, aURI, nsnull, nsnull, aCharset, destinationFolder, -1, 
03281                                      getter_AddRefs(bookmark));
03282 }
03283 
03284 NS_IMETHODIMP
03285 nsBookmarksService::IsBookmarkedResource(nsIRDFResource *bookmark, PRBool *isBookmarkedFlag)
03286 {
03287     if (!bookmark)      return NS_ERROR_UNEXPECTED;
03288     if (!isBookmarkedFlag)  return NS_ERROR_UNEXPECTED;
03289     if (!mInner)        return NS_ERROR_UNEXPECTED;
03290 
03291     // bookmark root is special (it isn't contained in a rdf seq)
03292     if (bookmark == kNC_BookmarksRoot)
03293     {
03294         *isBookmarkedFlag = PR_TRUE;
03295         return NS_OK;
03296     }
03297 
03298     *isBookmarkedFlag = PR_FALSE;
03299 
03300     // make sure it is referred to by an ordinal (i.e. is contained in a rdf seq)
03301     nsresult rv;
03302     nsCOMPtr<nsISimpleEnumerator>   enumerator;
03303     if (NS_FAILED(rv = mInner->ArcLabelsIn(bookmark, getter_AddRefs(enumerator))))
03304         return rv;
03305         
03306     PRBool  more = PR_TRUE;
03307     while(NS_SUCCEEDED(rv = enumerator->HasMoreElements(&more))
03308         && (more == PR_TRUE))
03309     {
03310         nsCOMPtr<nsISupports>       isupports;
03311         if (NS_FAILED(rv = enumerator->GetNext(getter_AddRefs(isupports))))
03312             break;
03313         nsCOMPtr<nsIRDFResource>    property = do_QueryInterface(isupports);
03314         if (!property)  continue;
03315 
03316         PRBool  flag = PR_FALSE;
03317         if (NS_FAILED(rv = gRDFC->IsOrdinalProperty(property, &flag)))  continue;
03318         if (flag == PR_TRUE)
03319         {
03320             *isBookmarkedFlag = PR_TRUE;
03321             break;
03322         }
03323     }
03324     return rv;
03325 }
03326 
03327 NS_IMETHODIMP
03328 nsBookmarksService::IsBookmarked(const char* aURL, PRBool* aIsBookmarked)
03329 {
03330     NS_ENSURE_ARG(aURL);
03331     NS_ENSURE_ARG_POINTER(aIsBookmarked);
03332 
03333     if (!mInner)
03334         return NS_ERROR_UNEXPECTED;
03335 
03336     *aIsBookmarked = PR_FALSE;
03337 
03338     nsCOMPtr<nsIRDFLiteral> urlLiteral;
03339     nsresult rv = gRDF->GetLiteral(NS_ConvertUTF8toUCS2(aURL).get(),
03340                                    getter_AddRefs(urlLiteral));
03341     if (NS_FAILED(rv))
03342         return rv;
03343 
03344     nsCOMPtr<nsIRDFResource> bookmark;
03345     rv = GetSource(kNC_URL, urlLiteral, PR_TRUE, getter_AddRefs(bookmark));
03346     if (NS_FAILED(rv))
03347         return rv;
03348 
03349     return IsBookmarkedResource(bookmark, aIsBookmarked);
03350 }
03351 
03352 NS_IMETHODIMP
03353 nsBookmarksService::RequestCharset(nsIWebNavigation* aWebNavigation,
03354                                    nsIChannel* aChannel,
03355                                    PRBool* aWantCharset,
03356                                    nsISupports** aClosure,
03357                                    nsACString& aResult)
03358 {
03359     if (!mInner)
03360         return NS_ERROR_UNEXPECTED;
03361     *aWantCharset = PR_FALSE;
03362     *aClosure = nsnull;
03363 
03364     nsresult rv;
03365     
03366     nsCOMPtr<nsIURI> uri;
03367     rv = aChannel->GetURI(getter_AddRefs(uri));
03368 
03369     nsCAutoString urlSpec;
03370     uri->GetSpec(urlSpec);
03371    
03372     nsCOMPtr<nsIRDFLiteral> urlLiteral;
03373     rv = gRDF->GetLiteral(NS_ConvertUTF8toUCS2(urlSpec).get(),
03374                                    getter_AddRefs(urlLiteral));
03375     if (NS_FAILED(rv))
03376         return rv;
03377 
03378     nsCOMPtr<nsIRDFResource> bookmark;
03379     rv = GetSource(kNC_URL, urlLiteral, PR_TRUE, getter_AddRefs(bookmark));
03380     if (NS_FAILED(rv))
03381         return rv;
03382 
03383     if (bookmark) {
03384         // Always use mInner! Otherwise, could get into an infinite loop
03385         // due to Assert/Change calling UpdateBookmarkLastModifiedDate().
03386 
03387         nsCOMPtr<nsIRDFNode> nodeType;
03388         GetSynthesizedType(bookmark, getter_AddRefs(nodeType));
03389         if (nodeType == kNC_Bookmark) {
03390             nsCOMPtr<nsIRDFNode>  charsetNode;
03391             rv = mInner->GetTarget(bookmark, kWEB_LastCharset, PR_TRUE,
03392                                    getter_AddRefs(charsetNode));
03393             if (NS_FAILED(rv))
03394                 return rv;
03395 
03396             if (charsetNode) {
03397                 nsCOMPtr<nsIRDFLiteral> charsetLiteral = do_QueryInterface(charsetNode);
03398                 if (charsetLiteral) {
03399                     const PRUnichar* charset;
03400                     charsetLiteral->GetValueConst(&charset);
03401                     LossyCopyUTF16toASCII(charset, aResult);
03402                     
03403                     return NS_OK;
03404                 }
03405             }
03406         }
03407     }
03408 
03409     aResult.Truncate();
03410     return NS_OK;
03411 }
03412 
03413 NS_IMETHODIMP
03414 nsBookmarksService::NotifyResolvedCharset(const nsACString& aCharset,
03415                                           nsISupports* aClosure)
03416 {
03417     NS_ERROR("Unexpected call to NotifyResolvedCharset -- we never set aWantCharset to true!");
03418     return NS_ERROR_NOT_IMPLEMENTED;
03419 }
03420 
03421 NS_IMETHODIMP
03422 nsBookmarksService::UpdateBookmarkIcon(const char *aURL, const PRUnichar *aIconURL)
03423 {
03424     nsCOMPtr<nsIRDFLiteral> urlLiteral;
03425     nsresult rv = gRDF->GetLiteral(NS_ConvertUTF8toUCS2(aURL).get(),
03426                                    getter_AddRefs(urlLiteral));
03427     if (NS_FAILED(rv))
03428         return rv;
03429 
03430     nsCOMPtr<nsISimpleEnumerator> bookmarks;
03431     rv = GetSources(kNC_URL, urlLiteral, PR_TRUE, getter_AddRefs(bookmarks));
03432     if (NS_FAILED(rv))
03433         return rv;
03434 
03435     PRBool hasMoreBookmarks = PR_FALSE;
03436     while (NS_SUCCEEDED(rv = bookmarks->HasMoreElements(&hasMoreBookmarks)) &&
03437            hasMoreBookmarks) {
03438         nsCOMPtr<nsISupports> supports;
03439         rv = bookmarks->GetNext(getter_AddRefs(supports));
03440         if (NS_FAILED(rv)) 
03441             return rv;
03442 
03443         nsCOMPtr<nsIRDFResource> bookmark = do_QueryInterface(supports);
03444         if (bookmark) {
03445             nsCOMPtr<nsIRDFNode> iconNode;
03446             rv = ProcessCachedBookmarkIcon(bookmark, aIconURL,
03447                                            getter_AddRefs(iconNode));
03448             if (NS_FAILED(rv))
03449                 return rv;
03450 
03451             if (iconNode) {
03452                 // Yes, that's right. Fake out RDF observers.
03453                 (void)OnAssert(this, bookmark, kNC_Icon, iconNode);
03454             }
03455         }
03456     }
03457 
03458     return NS_OK;
03459 }
03460 
03461 NS_IMETHODIMP
03462 nsBookmarksService::RemoveBookmarkIcon(const char *aURL, const PRUnichar *aIconURL)
03463 {
03464     nsCOMPtr<nsIRDFLiteral> urlLiteral;
03465     nsresult rv = gRDF->GetLiteral(NS_ConvertUTF8toUCS2(aURL).get(),
03466                                    getter_AddRefs(urlLiteral));
03467     if (NS_FAILED(rv))
03468         return rv;
03469 
03470     nsCOMPtr<nsISimpleEnumerator> bookmarks;
03471     rv = GetSources(kNC_URL, urlLiteral, PR_TRUE, getter_AddRefs(bookmarks));
03472     if (NS_FAILED(rv))
03473         return rv;
03474 
03475     PRBool hasMoreBookmarks = PR_FALSE;
03476     while (NS_SUCCEEDED(rv = bookmarks->HasMoreElements(&hasMoreBookmarks)) &&
03477            hasMoreBookmarks) {
03478         nsCOMPtr<nsISupports> supports;
03479         rv = bookmarks->GetNext(getter_AddRefs(supports));
03480         if (NS_FAILED(rv)) 
03481             return rv;
03482 
03483         nsCOMPtr<nsIRDFResource> bookmark = do_QueryInterface(supports);
03484         if (bookmark) {
03485             nsCOMPtr<nsIRDFLiteral> iconLiteral;
03486             rv = gRDF->GetLiteral(aIconURL, getter_AddRefs(iconLiteral));
03487             if (NS_FAILED(rv)) 
03488                 return rv;
03489 
03490             PRBool hasThisIconURL = PR_FALSE;
03491             rv = mInner->HasAssertion(bookmark, kNC_Icon, iconLiteral, PR_TRUE,
03492                                       &hasThisIconURL);
03493             if (NS_FAILED(rv)) 
03494                 return rv;
03495 
03496             if (hasThisIconURL) {
03497                 (void)mInner->Unassert(bookmark, kNC_Icon, iconLiteral);
03498 
03499                 mDirty = PR_TRUE;
03500             }
03501         }
03502     }
03503 
03504     return NS_OK;
03505 }
03506 
03507 NS_IMETHODIMP
03508 nsBookmarksService::UpdateLastVisitedDate(const char *aURL,
03509                                           const PRUnichar *aCharset)
03510 {
03511     NS_PRECONDITION(aURL != nsnull, "null ptr");
03512     if (! aURL)
03513         return NS_ERROR_NULL_POINTER;
03514 
03515     NS_PRECONDITION(aCharset != nsnull, "null ptr");
03516     if (! aCharset)
03517         return NS_ERROR_NULL_POINTER;
03518 
03519     nsCOMPtr<nsIRDFLiteral> urlLiteral;
03520     nsresult rv = gRDF->GetLiteral(NS_ConvertUTF8toUCS2(aURL).get(),
03521                                    getter_AddRefs(urlLiteral));
03522     if (NS_FAILED(rv))
03523         return rv;
03524 
03525     nsCOMPtr<nsISimpleEnumerator> bookmarks;
03526     rv = GetSources(kNC_URL, urlLiteral, PR_TRUE, getter_AddRefs(bookmarks));
03527     if (NS_FAILED(rv))
03528         return rv;
03529 
03530     PRBool hasMoreBookmarks = PR_FALSE;
03531     while (NS_SUCCEEDED(rv = bookmarks->HasMoreElements(&hasMoreBookmarks)) &&
03532            hasMoreBookmarks) {
03533         nsCOMPtr<nsISupports> supports;
03534         rv = bookmarks->GetNext(getter_AddRefs(supports));
03535         if (NS_FAILED(rv)) 
03536             return rv;
03537 
03538         nsCOMPtr<nsIRDFResource> bookmark = do_QueryInterface(supports);
03539         if (bookmark) {
03540             // Always use mInner! Otherwise, we could get into an infinite loop
03541             // due to Assert/Change calling UpdateBookmarkLastModifiedDate().
03542 
03543             nsCOMPtr<nsIRDFNode> nodeType;
03544             GetSynthesizedType(bookmark, getter_AddRefs(nodeType));
03545             if (nodeType == kNC_Bookmark) {
03546                 nsCOMPtr<nsIRDFDate> now;
03547                 rv = gRDF->GetDateLiteral(PR_Now(), getter_AddRefs(now));
03548                 if (NS_FAILED(rv))
03549                     return rv;
03550 
03551                 nsCOMPtr<nsIRDFNode> lastMod;
03552                 rv = mInner->GetTarget(bookmark, kWEB_LastVisitDate, PR_TRUE,
03553                                        getter_AddRefs(lastMod));
03554                 if (NS_FAILED(rv))
03555                     return rv;
03556 
03557                 if (lastMod) {
03558                     rv = mInner->Change(bookmark, kWEB_LastVisitDate, lastMod, now);
03559                 }
03560                 else {
03561                     rv = mInner->Assert(bookmark, kWEB_LastVisitDate, now, PR_TRUE);
03562                 }
03563                 if (NS_FAILED(rv))
03564                     return rv;
03565 
03566                 // Piggy-backing last charset.
03567                 if (aCharset && *aCharset) {
03568                     nsCOMPtr<nsIRDFLiteral> charsetliteral;
03569                     rv = gRDF->GetLiteral(aCharset,
03570                                           getter_AddRefs(charsetliteral));
03571                     if (NS_FAILED(rv))
03572                         return rv;
03573 
03574                     nsCOMPtr<nsIRDFNode> charsetNode;
03575                     rv = mInner->GetTarget(bookmark, kWEB_LastCharset, PR_TRUE,
03576                                            getter_AddRefs(charsetNode));
03577                     if (NS_FAILED(rv))
03578                         return rv;
03579 
03580                     if (charsetNode) {
03581                         rv = mInner->Change(bookmark, kWEB_LastCharset,
03582                                             charsetNode, charsetliteral);
03583                     }
03584                     else {
03585                         rv = mInner->Assert(bookmark, kWEB_LastCharset,
03586                                             charsetliteral, PR_TRUE);
03587                     }
03588                     if (NS_FAILED(rv))
03589                         return rv;
03590                 } 
03591 
03592                 // Also update bookmark's "status"!
03593                 nsCOMPtr<nsIRDFNode> statusNode;
03594                 rv = mInner->GetTarget(bookmark, kWEB_Status, PR_TRUE,
03595                                        getter_AddRefs(statusNode));
03596                 if (NS_SUCCEEDED(rv) && statusNode) {
03597                     rv = mInner->Unassert(bookmark, kWEB_Status, statusNode);
03598                     NS_ASSERTION(rv == NS_RDF_ASSERTION_ACCEPTED, "unable to Unassert changed status");
03599                 }
03600 
03601                 mDirty = PR_TRUE;
03602             }
03603         }
03604     }
03605 
03606     return rv;
03607 }
03608 
03609 nsresult
03610 nsBookmarksService::GetSynthesizedType(nsIRDFResource *aNode, nsIRDFNode **aType)
03611 {
03612     *aType = nsnull;
03613     nsresult rv = mInner->GetTarget(aNode, kRDF_type, PR_TRUE, aType);
03614     if (NS_FAILED(rv) || (rv == NS_RDF_NO_VALUE))
03615     {
03616         // if we didn't match anything in the graph, synthesize its type
03617         // (which is either a bookmark or a bookmark folder, since everything
03618         // else is annotated)
03619         PRBool isContainer = PR_FALSE;
03620         PRBool isBookmarkedFlag = PR_FALSE;
03621         (void)gRDFC->IsSeq(mInner, aNode, &isContainer);
03622 
03623         if (isContainer)
03624         {
03625             *aType =  kNC_Folder;
03626         }
03627         else if (NS_SUCCEEDED(rv = IsBookmarkedResource(aNode,
03628                                                         &isBookmarkedFlag)) && (isBookmarkedFlag == PR_TRUE))
03629         {
03630             *aType = kNC_Bookmark;
03631         }
03632 #ifdef XP_BEOS
03633         else
03634         {
03635             //solution for BeOS - bookmarks are stored as file attributes. 
03636             *aType = kNC_URL;
03637         }
03638 #endif
03639         NS_IF_ADDREF(*aType);
03640     }
03641     return NS_OK;
03642 }
03643 
03644 nsresult
03645 nsBookmarksService::UpdateBookmarkLastModifiedDate(nsIRDFResource *aSource)
03646 {
03647     nsCOMPtr<nsIRDFDate>    now;
03648     nsresult        rv;
03649 
03650     if (NS_SUCCEEDED(rv = gRDF->GetDateLiteral(PR_Now(), getter_AddRefs(now))))
03651     {
03652         nsCOMPtr<nsIRDFNode>    lastMod;
03653 
03654         // Note: always use mInner!! Otherwise, could get into an infinite loop
03655         // due to Assert/Change calling UpdateBookmarkLastModifiedDate()
03656 
03657         if (NS_SUCCEEDED(rv = mInner->GetTarget(aSource, kWEB_LastModifiedDate, PR_TRUE,
03658             getter_AddRefs(lastMod))) && (rv != NS_RDF_NO_VALUE))
03659         {
03660             rv = mInner->Change(aSource, kWEB_LastModifiedDate, lastMod, now);
03661         }
03662         else
03663         {
03664             rv = mInner->Assert(aSource, kWEB_LastModifiedDate, now, PR_TRUE);
03665         }
03666     }
03667     return rv;
03668 }
03669 
03670 NS_IMETHODIMP
03671 nsBookmarksService::GetLastCharset(const nsACString &aURL, nsAString &aCharset)
03672 {
03673     aCharset.Truncate(); 
03674 
03675     nsCOMPtr<nsIRDFLiteral> urlLiteral;
03676     nsresult rv = gRDF->GetLiteral(NS_ConvertUTF8toUCS2(aURL).get(),
03677                                    getter_AddRefs(urlLiteral));
03678 
03679     if (NS_FAILED(rv))
03680         return rv;
03681 
03682     nsCOMPtr<nsIRDFResource> bookmark;
03683     rv = GetSource(kNC_URL, urlLiteral, PR_TRUE, getter_AddRefs(bookmark));
03684     if (NS_FAILED(rv))
03685         return rv;
03686 
03687     nsCOMPtr<nsIRDFNode> nodeType;
03688     GetSynthesizedType(bookmark, getter_AddRefs(nodeType));
03689     if (nodeType == kNC_Bookmark) {
03690         nsCOMPtr<nsIRDFNode> charsetNode;
03691         rv = GetTarget(bookmark, kWEB_LastCharset, PR_TRUE,
03692                        getter_AddRefs(charsetNode));
03693         if (NS_FAILED(rv))
03694             return rv;
03695 
03696         if (charsetNode) {
03697             nsCOMPtr<nsIRDFLiteral> charsetData(do_QueryInterface(charsetNode));
03698             if (charsetData) {
03699                 const PRUnichar *charset;
03700                 charsetData->GetValueConst(&charset);
03701                 aCharset.Assign(charset);
03702             }
03703         }
03704     }
03705 
03706     return NS_OK;
03707 }
03708 
03709 NS_IMETHODIMP
03710 nsBookmarksService::ResolveKeyword(const PRUnichar *aUserInput, char **aShortcutURL)
03711 {
03712     NS_PRECONDITION(aUserInput != nsnull, "null ptr");
03713     if (! aUserInput)
03714         return NS_ERROR_NULL_POINTER;
03715 
03716     NS_PRECONDITION(aShortcutURL != nsnull, "null ptr");
03717     if (! aShortcutURL)
03718         return NS_ERROR_NULL_POINTER;
03719 
03720     // Shortcuts are always lowercased internally.
03721     nsAutoString shortcut(aUserInput);
03722     ToLowerCase(shortcut);
03723 
03724     nsCOMPtr<nsIRDFLiteral> shortcutLiteral;
03725     nsresult rv = gRDF->GetLiteral(shortcut.get(),
03726                                    getter_AddRefs(shortcutLiteral));
03727     if (NS_FAILED(rv))
03728         return rv;
03729 
03730     nsCOMPtr<nsIRDFResource> source;
03731     rv = GetSource(kNC_ShortcutURL, shortcutLiteral, PR_TRUE,
03732                    getter_AddRefs(source));
03733     if (NS_FAILED(rv))
03734         return rv;
03735 
03736     if (source) {
03737         nsAutoString url;
03738         rv = GetURLFromResource(source, url);
03739         if (NS_FAILED(rv))
03740            return rv;
03741 
03742         if (!url.IsEmpty()) {
03743             *aShortcutURL = ToNewUTF8String(url);
03744             return NS_OK;
03745         }
03746     }
03747 
03748     *aShortcutURL = nsnull;
03749     return NS_RDF_NO_VALUE;
03750 }
03751 
03752 #ifdef XP_WIN
03753 // Determines the URL for a shortcut file 
03754 static void ResolveShortcut(nsIFile* aFile, nsACString& aURI)
03755 {
03756     nsCOMPtr<nsIFileProtocolHandler> fph;
03757     nsresult rv = NS_GetFileProtocolHandler(getter_AddRefs(fph));
03758     if (NS_FAILED(rv))
03759         return;
03760 
03761     nsCOMPtr<nsIURI> uri;
03762     rv = fph->ReadURLFile(aFile, getter_AddRefs(uri));
03763     if (NS_FAILED(rv))
03764         return;
03765 
03766     uri->GetSpec(aURI);
03767 } 
03768 
03769 nsresult
03770 nsBookmarksService::ParseFavoritesFolder(nsIFile* aDirectory, nsIRDFResource* aParentResource)
03771 {
03772     nsresult rv;
03773 
03774     nsCOMPtr<nsISimpleEnumerator> entries;
03775     rv = aDirectory->GetDirectoryEntries(getter_AddRefs(entries));
03776     if (NS_FAILED(rv)) 
03777         return rv;
03778 
03779     do
03780     {
03781         PRBool hasMore = PR_FALSE;
03782         rv = entries->HasMoreElements(&hasMore);
03783         if (NS_FAILED(rv) || !hasMore) 
03784             break;
03785 
03786         nsCOMPtr<nsISupports> supp;
03787         rv = entries->GetNext(getter_AddRefs(supp));
03788         if (NS_FAILED(rv)) 
03789             break;
03790 
03791         nsCOMPtr<nsIFile> currFile(do_QueryInterface(supp));
03792     
03793         nsCOMPtr<nsIURI> uri;
03794         rv = NS_NewFileURI(getter_AddRefs(uri), currFile);
03795         if (NS_FAILED(rv)) 
03796             break;
03797 
03798         nsAutoString bookmarkName;
03799         currFile->GetLeafName(bookmarkName);
03800 
03801         PRBool isSymlink = PR_FALSE;
03802         PRBool isDir = PR_FALSE;
03803 
03804         currFile->IsSymlink(&isSymlink);
03805         currFile->IsDirectory(&isDir);
03806 
03807         if (isSymlink)
03808         {
03809             // It's a .lnk file.  Get the native path and check to see if it's
03810             // a dir.  If so, create a bookmark for the dir.  If not, then
03811             // simply do nothing and continue.
03812 
03813             // Get the native path that the .lnk file is pointing to.
03814             nsCAutoString path;
03815             rv = currFile->GetNativeTarget(path);
03816             if (NS_FAILED(rv)) 
03817                 continue;
03818 
03819             nsCOMPtr<nsILocalFile> localFile;
03820             rv = NS_NewNativeLocalFile(path, PR_TRUE, getter_AddRefs(localFile));
03821             if (NS_FAILED(rv)) 
03822                 continue;
03823 
03824             // Check for dir here.  If path is not a dir, just continue with
03825             // next import.
03826             rv = localFile->IsDirectory(&isDir);
03827             NS_ENSURE_SUCCESS(rv, rv);
03828             if (!isDir)
03829                 continue;
03830 
03831             nsCAutoString spec;
03832             nsCOMPtr<nsIFile> filePath(localFile);
03833             // Get the file url format (file:///...) of the native file path.
03834             rv = NS_GetURLSpecFromFile(filePath, spec);
03835             if (NS_FAILED(rv)) 
03836                 continue;
03837 
03838             // Look for and strip out the .lnk extension.
03839             NS_NAMED_LITERAL_STRING(lnkExt, ".lnk");
03840             PRInt32 lnkExtStart = bookmarkName.Length() - lnkExt.Length();
03841             if (StringEndsWith(bookmarkName, lnkExt,
03842                   nsCaseInsensitiveStringComparator()))
03843                 bookmarkName.Truncate(lnkExtStart);
03844 
03845             nsCOMPtr<nsIRDFResource> bookmark;
03846             // NS_GetURLSpecFromFile on Windows returns url-escaped URL in 
03847             // pure ASCII. However, in the future, we may return 'hostpart'
03848             // of a remote file in UTF-8. Therefore, using UTF-8 in place of
03849             // ASCII is not a bad idea.
03850             CreateBookmarkInContainer(bookmarkName.get(),
03851                                       NS_ConvertUTF8toUTF16(spec).get(),
03852                                       nsnull, nsnull, nsnull, aParentResource,
03853                                       -1, getter_AddRefs(bookmark));
03854             if (NS_FAILED(rv)) 
03855                 continue;
03856         }
03857         else if (isDir)
03858         {
03859             nsCOMPtr<nsIRDFResource> folder;
03860             rv = CreateFolderInContainer(bookmarkName.get(), aParentResource, -1, getter_AddRefs(folder));
03861             if (NS_FAILED(rv)) 
03862                 continue;
03863 
03864             rv = ParseFavoritesFolder(currFile, folder);
03865             if (NS_FAILED(rv)) 
03866                 continue;
03867         }
03868         else
03869         {
03870             nsCOMPtr<nsIURL> url(do_QueryInterface(uri));
03871             nsCAutoString extension;
03872 
03873             url->GetFileExtension(extension);
03874             if (!extension.LowerCaseEqualsLiteral("url"))
03875                 continue;
03876 
03877             nsAutoString name(Substring(bookmarkName, 0, 
03878                                         bookmarkName.Length() - extension.Length() - 1));
03879      
03880 
03881             nsCAutoString resolvedURL;
03882             ResolveShortcut(currFile, resolvedURL);
03883 
03884             nsCOMPtr<nsIRDFResource> bookmark;
03885             // ResolveShortcut gives us a UTF8 string (uri->GetSpec)
03886             rv = CreateBookmarkInContainer(name.get(),
03887                  NS_ConvertUTF8toUTF16(resolvedURL).get(),
03888                  nsnull, nsnull, nsnull, aParentResource, -1,
03889                  getter_AddRefs(bookmark));
03890             if (NS_FAILED(rv)) 
03891                 continue;
03892         }
03893     }
03894     while (1);
03895 
03896     return rv;
03897 }
03898 #endif
03899 
03900 NS_IMETHODIMP
03901 nsBookmarksService::ImportSystemBookmarks(nsIRDFResource* aParentFolder)
03902 {
03903     gImportedSystemBookmarks = PR_TRUE;
03904 
03905 #if defined(XP_WIN)
03906     nsresult rv;
03907 
03908     nsCOMPtr<nsIProperties> fileLocator(do_GetService("@mozilla.org/file/directory_service;1", &rv));
03909     if (NS_FAILED(rv)) 
03910         return rv;
03911 
03912     nsCOMPtr<nsIFile> favoritesDirectory;
03913     fileLocator->Get("Favs", NS_GET_IID(nsIFile), getter_AddRefs(favoritesDirectory));
03914 
03915     // If |favoritesDirectory| is null, it means that we're on a Windows 
03916     // platform that does not have a Favorites folder, e.g. Windows 95 
03917     // (early SRs, before IE integrated with the shell). Only try to 
03918     // read Favorites folder if it exists on the machine. 
03919     if (favoritesDirectory) 
03920         return ParseFavoritesFolder(favoritesDirectory, aParentFolder);
03921 #elif defined(XP_MAC) || defined(XP_MACOSX)
03922     nsCOMPtr<nsIFile> ieFavoritesFile;
03923     nsresult rv = NS_GetSpecialDirectory(NS_MAC_PREFS_DIR, getter_AddRefs(ieFavoritesFile));
03924     NS_ENSURE_SUCCESS(rv, rv);
03925 
03926     ieFavoritesFile->Append(NS_LITERAL_STRING("Explorer"));
03927     ieFavoritesFile->Append(NS_LITERAL_STRING("Favorites.html"));
03928 
03929     BookmarkParser parser;
03930     parser.Init(ieFavoritesFile, mInner);
03931     BeginUpdateBatch();
03932     parser.Parse(aParentFolder, kNC_Bookmark);
03933     EndUpdateBatch();
03934 #endif
03935 
03936     return NS_OK;
03937 }
03938 
03939 #if defined(XP_WIN) || defined(XP_MAC) || defined(XP_MACOSX)
03940 void
03941 nsBookmarksService::HandleSystemBookmarks(nsIRDFNode* aNode) 
03942 {
03943     if (!gImportedSystemBookmarks && aNode == kNC_SystemBookmarksStaticRoot)
03944     {
03945         PRBool isSeq = PR_TRUE;
03946         gRDFC->IsSeq(mInner, kNC_SystemBookmarksStaticRoot, &isSeq);
03947     
03948         if (!isSeq)
03949         {
03950             nsCOMPtr<nsIRDFContainer> ctr;
03951             gRDFC->MakeSeq(mInner, kNC_SystemBookmarksStaticRoot, getter_AddRefs(ctr));
03952       
03953             ImportSystemBookmarks(kNC_SystemBookmarksStaticRoot);
03954         }
03955     }
03956 #if defined(XP_MAC) || defined(XP_MACOSX)
03957     // on the Mac, IE favorites are stored in an HTML file.
03958     // Defer importing the contents of this file until necessary.
03959     else if ((aNode == kNC_IEFavoritesRoot) && (mIEFavoritesAvailable == PR_FALSE))
03960         ReadFavorites();
03961 #endif
03962 }
03963 #endif
03964 
03965 NS_IMETHODIMP
03966 nsBookmarksService::GetTransactionManager(nsITransactionManager** aTransactionManager)
03967 {
03968     NS_ENSURE_ARG_POINTER(aTransactionManager);
03969 
03970     NS_ADDREF(*aTransactionManager = mTransactionManager);
03971 
03972     return NS_OK;
03973 }
03974 
03976 // nsIRDFDataSource
03977 
03978 NS_IMETHODIMP
03979 nsBookmarksService::GetURI(char* *aURI)
03980 {
03981     *aURI = nsCRT::strdup("rdf:bookmarks");
03982     if (! *aURI)
03983         return NS_ERROR_OUT_OF_MEMORY;
03984 
03985     return NS_OK;
03986 }
03987 
03988 static PRBool
03989 isBookmarkCommand(nsIRDFResource *r)
03990 {
03991     PRBool      isBookmarkCommandFlag = PR_FALSE;
03992     const char  *uri = nsnull;
03993     
03994     if (NS_SUCCEEDED(r->GetValueConst( &uri )) && (uri))
03995     {
03996         if (!strncmp(uri, kBookmarkCommand, sizeof(kBookmarkCommand) - 1))
03997         {
03998             isBookmarkCommandFlag = PR_TRUE;
03999         }
04000     }
04001     return isBookmarkCommandFlag;
04002 }
04003 
04004 NS_IMETHODIMP
04005 nsBookmarksService::GetTarget(nsIRDFResource* aSource,
04006                               nsIRDFResource* aProperty,
04007                               PRBool aTruthValue,
04008                               nsIRDFNode** aTarget)
04009 {
04010     *aTarget = nsnull;
04011 
04012     nsresult    rv;
04013 
04014     if (aTruthValue && (aProperty == kRDF_type))
04015     {
04016         rv = GetSynthesizedType(aSource, aTarget);
04017         return rv;
04018     }
04019     else if (aTruthValue && isBookmarkCommand(aSource) && (aProperty == kNC_Name))
04020     {
04021         nsAutoString    name;
04022         if (aSource == kNC_BookmarkCommand_NewBookmark)
04023             getLocaleString("NewBookmark", name);
04024         else if (aSource == kNC_BookmarkCommand_NewFolder)
04025             getLocaleString("NewFolder", name);
04026         else if (aSource == kNC_BookmarkCommand_NewSeparator)
04027             getLocaleString("NewSeparator", name);
04028         else if (aSource == kNC_BookmarkCommand_DeleteBookmark)
04029             getLocaleString("DeleteBookmark", name);
04030         else if (aSource == kNC_BookmarkCommand_DeleteBookmarkFolder)
04031             getLocaleString("DeleteFolder", name);
04032         else if (aSource == kNC_BookmarkCommand_DeleteBookmarkSeparator)
04033             getLocaleString("DeleteSeparator", name);
04034         else if (aSource == kNC_BookmarkCommand_SetNewBookmarkFolder)
04035             getLocaleString("SetNewBookmarkFolder", name);
04036         else if (aSource == kNC_BookmarkCommand_SetPersonalToolbarFolder)
04037             getLocaleString("SetPersonalToolbarFolder", name);
04038         else if (aSource == kNC_BookmarkCommand_SetNewSearchFolder)
04039             getLocaleString("SetNewSearchFolder", name);
04040         else if (aSource == kNC_BookmarkCommand_Import)
04041             getLocaleString("Import", name);
04042         else if (aSource == kNC_BookmarkCommand_Export)
04043             getLocaleString("Export", name);
04044 
04045         if (!name.IsEmpty())
04046         {
04047             *aTarget = nsnull;
04048             nsCOMPtr<nsIRDFLiteral> literal;
04049             if (NS_FAILED(rv = gRDF->GetLiteral(name.get(), getter_AddRefs(literal))))
04050                 return rv;
04051             *aTarget = literal;
04052             NS_IF_ADDREF(*aTarget);
04053             return rv;
04054         }
04055     }
04056     else if (aProperty == kNC_Icon)
04057     {
04058         rv = ProcessCachedBookmarkIcon(aSource, nsnull, aTarget);
04059         return rv;
04060     }
04061 
04062     rv = mInner->GetTarget(aSource, aProperty, aTruthValue, aTarget);
04063     return rv;
04064 }
04065 
04066 nsresult
04067 nsBookmarksService::ProcessCachedBookmarkIcon(nsIRDFResource* aSource,
04068                                               const PRUnichar *iconURL, nsIRDFNode** aTarget)
04069 {
04070     *aTarget = nsnull;
04071 
04072     if (!mBrowserIcons)
04073     {
04074         return NS_RDF_NO_VALUE;
04075     }
04076 
04077     // if it is in fact a bookmark or favorite (but NOT a folder, or a separator, etc)...
04078 
04079     nsCOMPtr<nsIRDFNode> nodeType;
04080     GetSynthesizedType(aSource, getter_AddRefs(nodeType));
04081     if ((nodeType != kNC_Bookmark) && (nodeType != kNC_IEFavorite))
04082     {
04083         return NS_RDF_NO_VALUE;
04084     }
04085 
04086     nsresult rv;
04087     nsCAutoString path;
04088     nsCOMPtr<nsIRDFNode>    oldIconNode;
04089 
04090     // if we have a new icon URL, save it away into our internal graph
04091     if (iconURL)
04092     {
04093         path.AssignWithConversion(iconURL);
04094 
04095         nsCOMPtr<nsIRDFLiteral> iconLiteral;
04096         if (NS_FAILED(rv = gRDF->GetLiteral(iconURL, getter_AddRefs(iconLiteral))))
04097         {
04098             return rv;
04099         }
04100 
04101         rv = mInner->GetTarget(aSource, kNC_Icon, PR_TRUE, getter_AddRefs(oldIconNode));
04102         if (NS_SUCCEEDED(rv) && (rv != NS_RDF_NO_VALUE) && (oldIconNode))
04103         {
04104             (void)mInner->Unassert(aSource, kNC_Icon, oldIconNode);
04105         }
04106         (void)mInner->Assert(aSource, kNC_Icon, iconLiteral, PR_TRUE);
04107 
04108         mDirty = PR_TRUE;
04109     }
04110     else
04111     {
04112         // otherwise, just check and see if we have an internal icon reference
04113         rv = mInner->GetTarget(aSource, kNC_Icon, PR_TRUE, getter_AddRefs(oldIconNode));
04114     }
04115     
04116     if (oldIconNode)
04117     {
04118         nsCOMPtr<nsIRDFLiteral> tempLiteral = do_QueryInterface(oldIconNode);
04119         if (tempLiteral)
04120         {
04121             const PRUnichar *uni = nsnull;
04122             tempLiteral->GetValueConst(&uni);
04123             if (uni)    path.AssignWithConversion(uni);
04124         }
04125     }
04126 
04127     PRBool forceLoad = mAlwaysLoadIcons;
04128 
04129     // if no internal icon reference, try and synthesize a URL
04130     if (path.IsEmpty())
04131     {
04132         const char  *uri;
04133         forceLoad = PR_FALSE;
04134         if (NS_FAILED(rv = aSource->GetValueConst( &uri )))
04135         {
04136             return rv;
04137         }
04138 
04139         nsCOMPtr<nsIURI>    nsURI;
04140         if (NS_FAILED(rv = mNetService->NewURI(nsDependentCString(uri), nsnull, nsnull, getter_AddRefs(nsURI))))
04141         {
04142             return rv;
04143         }
04144         
04145         // only allow http/https URLs for favicon
04146         PRBool  isHTTP = PR_FALSE;
04147         nsURI->SchemeIs("http", &isHTTP);
04148         if (!isHTTP)
04149         {
04150             nsURI->SchemeIs("https", &isHTTP);
04151         }
04152         if (!isHTTP)
04153         {
04154             return NS_RDF_NO_VALUE;
04155         }
04156 
04157         nsCAutoString prePath;
04158         if (NS_FAILED(rv = nsURI->GetPrePath(prePath)))
04159         {
04160             return rv;
04161         }
04162         path.Assign(prePath);
04163         path.Append("/favicon.ico");
04164     }
04165 
04166     if (!forceLoad) {
04167         // only return favicon reference if its in the cache
04168         // (that is, never go out onto the net)
04169         if (!mCacheSession)
04170         {
04171             return NS_RDF_NO_VALUE;
04172         }
04173         nsCOMPtr<nsICacheEntryDescriptor> entry;
04174         rv = mCacheSession->OpenCacheEntry(path, nsICache::ACCESS_READ,
04175                                            nsICache::NON_BLOCKING, getter_AddRefs(entry));
04176         if (NS_FAILED(rv) || (!entry))
04177         {
04178             return NS_RDF_NO_VALUE;
04179         }
04180         if (entry) 
04181         {
04182             PRUint32 expTime;
04183             entry->GetExpirationTime(&expTime);
04184             if (expTime != PR_UINT32_MAX)
04185                 entry->SetExpirationTime(PR_UINT32_MAX);
04186         }
04187         entry->Close();
04188     }
04189 
04190     // ok, have a cached icon entry, so return the URL's associated favicon
04191     nsAutoString litStr;
04192     litStr.AssignWithConversion(path.get());
04193     nsCOMPtr<nsIRDFLiteral> literal;
04194     if (NS_FAILED(rv = gRDF->GetLiteral(litStr.get(), getter_AddRefs(literal))))
04195     {
04196         return rv;
04197     }
04198     *aTarget = literal;
04199     NS_IF_ADDREF(*aTarget);
04200     return NS_OK;
04201 }
04202 
04203 void
04204 nsBookmarksService::AnnotateBookmarkSchedule(nsIRDFResource* aSource, PRBool scheduleFlag)
04205 {
04206     if (scheduleFlag)
04207     {
04208         PRBool exists = PR_FALSE;
04209         if (NS_SUCCEEDED(mInner->HasAssertion(aSource, kWEB_ScheduleActive,
04210                                               kTrueLiteral, PR_TRUE, &exists)) && (!exists))
04211         {
04212             (void)mInner->Assert(aSource, kWEB_ScheduleActive, kTrueLiteral, PR_TRUE);
04213         }
04214     }
04215     else
04216     {
04217         (void)mInner->Unassert(aSource, kWEB_ScheduleActive, kTrueLiteral);
04218     }
04219 }
04220 
04221 NS_IMETHODIMP
04222 nsBookmarksService::Assert(nsIRDFResource* aSource,
04223                            nsIRDFResource* aProperty,
04224                            nsIRDFNode* aTarget,
04225                            PRBool aTruthValue)
04226 {
04227     nsresult rv = NS_RDF_ASSERTION_REJECTED;
04228 
04229     if (CanAccept(aSource, aProperty, aTarget))
04230     {
04231         rv = mInner->Assert(aSource, aProperty, aTarget, aTruthValue);
04232         if (NS_FAILED(rv))
04233             return rv;
04234 
04235         UpdateBookmarkLastModifiedDate(aSource);
04236             
04237         if (aProperty == kWEB_Schedule) {
04238               AnnotateBookmarkSchedule(aSource, PR_TRUE);
04239         }
04240     }
04241 
04242     return rv;
04243 }
04244 
04245 NS_IMETHODIMP
04246 nsBookmarksService::Unassert(nsIRDFResource* aSource,
04247                              nsIRDFResource* aProperty,
04248                              nsIRDFNode* aTarget)
04249 {
04250     nsresult rv = NS_RDF_ASSERTION_REJECTED;
04251 
04252     if (CanAccept(aSource, aProperty, aTarget)) {
04253         rv = mInner->Unassert(aSource, aProperty, aTarget);
04254         if (NS_FAILED(rv))
04255             return rv;
04256 
04257         UpdateBookmarkLastModifiedDate(aSource);
04258 
04259         if (aProperty == kWEB_Schedule) {
04260             AnnotateBookmarkSchedule(aSource, PR_FALSE);
04261         }
04262     }
04263 
04264     return rv;
04265 }
04266 
04267 NS_IMETHODIMP
04268 nsBookmarksService::Change(nsIRDFResource* aSource,
04269                            nsIRDFResource* aProperty,
04270                            nsIRDFNode* aOldTarget,
04271                            nsIRDFNode* aNewTarget)
04272 {
04273     nsresult rv = NS_RDF_ASSERTION_REJECTED;
04274 
04275     if (CanAccept(aSource, aProperty, aNewTarget)) {
04276         rv = mInner->Change(aSource, aProperty, aOldTarget, aNewTarget);
04277         if (NS_FAILED(rv))
04278             return rv;
04279 
04280         UpdateBookmarkLastModifiedDate(aSource);
04281 
04282         if (aProperty == kWEB_Schedule) {
04283             AnnotateBookmarkSchedule(aSource, PR_TRUE);
04284         }
04285     }
04286 
04287     return rv;
04288 }
04289 
04290 NS_IMETHODIMP
04291 nsBookmarksService::Move(nsIRDFResource* aOldSource,
04292                          nsIRDFResource* aNewSource,
04293                          nsIRDFResource* aProperty,
04294                          nsIRDFNode* aTarget)
04295 {
04296     nsresult    rv = NS_RDF_ASSERTION_REJECTED;
04297 
04298     if (CanAccept(aNewSource, aProperty, aTarget))
04299     {
04300         rv = mInner->Move(aOldSource, aNewSource, aProperty, aTarget);
04301         if (NS_FAILED(rv))
04302             return rv;
04303 
04304         UpdateBookmarkLastModifiedDate(aOldSource);
04305         UpdateBookmarkLastModifiedDate(aNewSource);
04306     }
04307     return rv;
04308 }
04309 
04310 NS_IMETHODIMP
04311 nsBookmarksService::HasAssertion(nsIRDFResource* source,
04312                 nsIRDFResource* property,
04313                 nsIRDFNode* target,
04314                 PRBool tv,
04315                 PRBool* hasAssertion)
04316 {
04317 #if defined(XP_WIN) || defined(XP_MAC) || defined(XP_MACOSX)
04318     HandleSystemBookmarks(source);
04319 #endif
04320 
04321     return mInner->HasAssertion(source, property, target, tv, hasAssertion);
04322 }
04323 
04324 NS_IMETHODIMP
04325 nsBookmarksService::AddObserver(nsIRDFObserver* aObserver)
04326 {
04327     if (! aObserver)
04328         return NS_ERROR_NULL_POINTER;
04329 
04330     if (! mObservers.AppendObject(aObserver)) {
04331         return NS_ERROR_FAILURE;
04332     }
04333     
04334     return NS_OK;
04335 }
04336 
04337 NS_IMETHODIMP
04338 nsBookmarksService::RemoveObserver(nsIRDFObserver* aObserver)
04339 {
04340     if (! aObserver)
04341         return NS_ERROR_NULL_POINTER;
04342 
04343     mObservers.RemoveObject(aObserver);
04344 
04345     return NS_OK;
04346 }
04347 
04348 NS_IMETHODIMP
04349 nsBookmarksService::HasArcIn(nsIRDFNode *aNode, nsIRDFResource *aArc, PRBool *_retval)
04350 {
04351 #if defined(XP_WIN) || defined(XP_MAC) || defined(XP_MACOSX)
04352     HandleSystemBookmarks(aNode);
04353 #endif
04354 
04355     return mInner->HasArcIn(aNode, aArc, _retval);
04356 }
04357 
04358 NS_IMETHODIMP
04359 nsBookmarksService::HasArcOut(nsIRDFResource *aSource, nsIRDFResource *aArc, PRBool *_retval)
04360 {
04361 #if defined(XP_WIN) || defined(XP_MAC) || defined(XP_MACOSX)
04362     HandleSystemBookmarks(aSource);
04363 #endif
04364   
04365     return mInner->HasArcOut(aSource, aArc, _retval);
04366 }
04367 
04368 NS_IMETHODIMP
04369 nsBookmarksService::ArcLabelsOut(nsIRDFResource* source,
04370                 nsISimpleEnumerator** labels)
04371 {
04372 #if defined(XP_WIN) || defined(XP_MAC) || defined(XP_MACOSX)
04373     HandleSystemBookmarks(source);
04374 #endif
04375 
04376     return mInner->ArcLabelsOut(source, labels);
04377 }
04378 
04379 NS_IMETHODIMP
04380 nsBookmarksService::GetAllResources(nsISimpleEnumerator** aResult)
04381 {
04382 #if defined(XP_WIN) || defined(XP_MAC) || defined(XP_MACOSX)
04383     HandleSystemBookmarks(kNC_SystemBookmarksStaticRoot);
04384 #endif
04385   
04386     return mInner->GetAllResources(aResult);
04387 }
04388 
04389 NS_IMETHODIMP
04390 nsBookmarksService::GetAllCmds(nsIRDFResource* source,
04391                    nsISimpleEnumerator/*<nsIRDFResource>*/** commands)
04392 {
04393     nsCOMPtr<nsISupportsArray>  cmdArray;
04394     nsresult            rv;
04395     rv = NS_NewISupportsArray(getter_AddRefs(cmdArray));
04396     if (NS_FAILED(rv))  return rv;
04397 
04398     // determine type
04399     nsCOMPtr<nsIRDFNode> nodeType;
04400     GetSynthesizedType(source, getter_AddRefs(nodeType));
04401 
04402     PRBool  isBookmark, isBookmarkFolder, isBookmarkSeparator;
04403     isBookmark = (nodeType == kNC_Bookmark) ? PR_TRUE : PR_FALSE;
04404     isBookmarkFolder = (nodeType == kNC_Folder) ? PR_TRUE : PR_FALSE;
04405     isBookmarkSeparator = (nodeType == kNC_BookmarkSeparator) ? PR_TRUE : PR_FALSE;
04406 
04407     if (isBookmark || isBookmarkFolder || isBookmarkSeparator)
04408     {
04409         cmdArray->AppendElement(kNC_BookmarkCommand_NewBookmark);
04410         cmdArray->AppendElement(kNC_BookmarkCommand_NewFolder);
04411         cmdArray->AppendElement(kNC_BookmarkCommand_NewSeparator);
04412         cmdArray->AppendElement(kNC_BookmarkSeparator);
04413     }
04414     if (isBookmark)
04415     {
04416         cmdArray->AppendElement(kNC_BookmarkCommand_DeleteBookmark);
04417     }
04418     if (isBookmarkFolder && (source != kNC_BookmarksRoot) && (source != kNC_IEFavoritesRoot))
04419     {
04420         cmdArray->AppendElement(kNC_BookmarkCommand_DeleteBookmarkFolder);
04421     }
04422     if (isBookmarkSeparator)
04423     {
04424         cmdArray->AppendElement(kNC_BookmarkCommand_DeleteBookmarkSeparator);
04425     }
04426     if (isBookmarkFolder)
04427     {
04428         nsCOMPtr<nsIRDFResource>    newBookmarkFolder, personalToolbarFolder, newSearchFolder;
04429         getFolderViaHint(kNC_NewBookmarkFolder, PR_FALSE, getter_AddRefs(newBookmarkFolder));
04430         getFolderViaHint(kNC_PersonalToolbarFolder, PR_FALSE, getter_AddRefs(personalToolbarFolder));
04431         getFolderViaHint(kNC_NewSearchFolder, PR_FALSE, getter_AddRefs(newSearchFolder));
04432 
04433         cmdArray->AppendElement(kNC_BookmarkSeparator);
04434         if (source != newBookmarkFolder.get())      cmdArray->AppendElement(kNC_BookmarkCommand_SetNewBookmarkFolder);
04435         if (source != newSearchFolder.get())        cmdArray->AppendElement(kNC_BookmarkCommand_SetNewSearchFolder);
04436         if (source != personalToolbarFolder.get())  cmdArray->AppendElement(kNC_BookmarkCommand_SetPersonalToolbarFolder);
04437     }
04438 
04439     // always append a separator last (due to aggregation of commands from multiple datasources)
04440     cmdArray->AppendElement(kNC_BookmarkSeparator);
04441 
04442     nsISimpleEnumerator     *result = new nsArrayEnumerator(cmdArray);
04443     if (!result)
04444         return NS_ERROR_OUT_OF_MEMORY;
04445 
04446     NS_ADDREF(result);
04447     *commands = result;
04448     return NS_OK;
04449 }
04450 
04451 NS_IMETHODIMP
04452 nsBookmarksService::IsCommandEnabled(nsISupportsArray/*<nsIRDFResource>*/* aSources,
04453                                          nsIRDFResource*   aCommand,
04454                                          nsISupportsArray/*<nsIRDFResource>*/* aArguments,
04455                                          PRBool* aResult)
04456 {
04457     return NS_ERROR_NOT_IMPLEMENTED;
04458 }
04459 
04460 nsresult
04461 nsBookmarksService::getArgumentN(nsISupportsArray *arguments, nsIRDFResource *res,
04462                 PRInt32 offset, nsIRDFNode **argValue)
04463 {
04464     nsresult        rv;
04465     PRUint32        loop, numArguments;
04466 
04467     *argValue = nsnull;
04468 
04469     if (NS_FAILED(rv = arguments->Count(&numArguments)))    return rv;
04470 
04471     // format is argument, value, argument, value, ... [you get the idea]
04472     // multiple arguments can be the same, by the way, thus the "offset"
04473     for (loop = 0; loop < numArguments; loop += 2)
04474     {
04475         nsCOMPtr<nsIRDFResource> src = do_QueryElementAt(arguments, loop, &rv);
04476         if (!src) return rv;
04477         
04478         if (src == res)
04479         {
04480             if (offset > 0)
04481             {
04482                 --offset;
04483                 continue;
04484             }
04485 
04486             nsCOMPtr<nsIRDFNode> val = do_QueryElementAt(arguments, loop + 1,
04487                                                          &rv);
04488             if (!val) return rv;
04489 
04490             *argValue = val;
04491             NS_ADDREF(*argValue);
04492             return NS_OK;
04493         }
04494     }
04495     return NS_ERROR_INVALID_ARG;
04496 }
04497 
04502 nsresult
04503 nsBookmarksService::insertBookmarkItem(nsIRDFResource *aRelativeNode, 
04504                                        nsISupportsArray *aArguments, 
04505                                        nsIRDFResource *aItemType)
04506 {
04507     nsresult rv;
04508     const PRInt32 kParentArgumentIndex = 0;
04509   
04510     nsCOMPtr<nsIRDFResource> rParent;
04511 
04512     if (aRelativeNode == kNC_BookmarksRoot)
04513         rParent = aRelativeNode;
04514     else
04515     {
04516         nsCOMPtr<nsIRDFNode> parentNode;
04517         rv = getArgumentN(aArguments, kNC_Parent, kParentArgumentIndex, getter_AddRefs(parentNode));
04518         if (NS_FAILED(rv)) return rv;
04519         rParent = do_QueryInterface(parentNode, &rv);
04520         if (NS_FAILED(rv)) return rv;
04521     }
04522 
04523     nsCOMPtr<nsIRDFContainer> container(do_CreateInstance("@mozilla.org/rdf/container;1", &rv));
04524     if (NS_FAILED(rv)) return rv;
04525 
04526     rv = container->Init(this, rParent);
04527     if (NS_FAILED(rv)) return rv;
04528 
04529     PRInt32 relNodeIdx = 0;
04530     if (aRelativeNode != kNC_BookmarksRoot) {
04531         // Find the position of the relative node, so we can create this item 
04532         // adjacent to it.
04533         rv = container->IndexOf(aRelativeNode, &relNodeIdx);
04534         if (NS_FAILED(rv)) return rv;
04535 
04536         // If the item isn't in the container, just append to the container.
04537         if (relNodeIdx == -1) {
04538             rv = container->GetCount(&relNodeIdx);
04539             if (NS_FAILED(rv)) return rv;
04540         }
04541     }
04542 
04543     nsAutoString itemName;
04544   
04545     // If a creation name was supplied, use it. 
04546     if (aItemType == kNC_Bookmark || aItemType == kNC_Folder) {
04547         nsCOMPtr<nsIRDFNode> nameNode;
04548         getArgumentN(aArguments, kNC_Name, kParentArgumentIndex, getter_AddRefs(nameNode));
04549         nsCOMPtr<nsIRDFLiteral> nameLiteral;
04550         nameLiteral = do_QueryInterface(nameNode);
04551         if (nameLiteral) {
04552             const PRUnichar* uName = nsnull;
04553             nameLiteral->GetValueConst(&uName);
04554             if (uName) 
04555                 itemName = uName;
04556         }
04557     }
04558 
04559     if (itemName.IsEmpty())
04560     {
04561         // Retrieve a default name from the bookmark properties file.
04562         if (aItemType == kNC_Bookmark) 
04563             getLocaleString("NewBookmark", itemName);
04564         else if (aItemType == kNC_Folder) 
04565             getLocaleString("NewFolder", itemName);
04566     }
04567 
04568     nsCOMPtr<nsIRDFResource> newResource;
04569   
04570     // Retrieve the URL from the arguments list. 
04571     if (aItemType == kNC_Bookmark || aItemType == kNC_Folder)
04572     {
04573         nsCOMPtr<nsIRDFNode> urlNode;
04574         getArgumentN(aArguments, kNC_URL, kParentArgumentIndex, getter_AddRefs(urlNode));
04575         nsCOMPtr<nsIRDFLiteral> bookmarkURILiteral(do_QueryInterface(urlNode));
04576         if (bookmarkURILiteral)
04577         {
04578             const PRUnichar* uURL = nsnull;
04579             bookmarkURILiteral->GetValueConst(&uURL);
04580             if (uURL)
04581             {
04582                 rv = gRDF->GetUnicodeResource(nsDependentString(uURL), getter_AddRefs(newResource));
04583                 if (NS_FAILED(rv)) return rv;
04584             }
04585         }
04586     }
04587 
04588     if (!newResource)
04589     {
04590         // We're a folder, or some other type of anonymous resource.
04591         rv = gRDF->GetAnonymousResource(getter_AddRefs(newResource));
04592         if (NS_FAILED(rv)) return rv;
04593     }
04594 
04595     if (aItemType == kNC_Folder)
04596     {
04597         // Make Sequences for Folders.
04598         rv = gRDFC->MakeSeq(mInner, newResource, nsnull);
04599         if (NS_FAILED(rv)) return rv;
04600     }
04601 
04602     // Assert Name arc
04603     if (!itemName.IsEmpty())
04604     {
04605         nsCOMPtr<nsIRDFLiteral> nameLiteral;
04606         rv = gRDF->GetLiteral(itemName.get(), getter_AddRefs(nameLiteral));
04607         if (NS_FAILED(rv)) return rv;
04608         rv = mInner->Assert(newResource, kNC_Name, nameLiteral, PR_TRUE);
04609         if (NS_FAILED(rv)) return rv;
04610     }
04611 
04612     // Assert type arc
04613     rv = mInner->Assert(newResource, kRDF_type, aItemType, PR_TRUE);
04614     if (NS_FAILED(rv)) return rv;
04615 
04616     // XXX - investigate asserting date as a resource with a raw date value for
04617     //       lookup, and a Name arc with a pretty display name, e.g. "Saturday"
04618   
04619     // Convert the current date/time from microseconds (PRTime) to seconds.
04620     nsCOMPtr<nsIRDFDate> dateLiteral;
04621     rv = gRDF->GetDateLiteral(PR_Now(), getter_AddRefs(dateLiteral));
04622     if (NS_FAILED(rv)) return rv;
04623     rv = mInner->Assert(newResource, kNC_BookmarkAddDate, dateLiteral, PR_TRUE);
04624     if (NS_FAILED(rv)) return rv;
04625 
04626     // Add to container. 
04627     rv = container->InsertElementAt(newResource, !relNodeIdx ? 1 : relNodeIdx, PR_TRUE);
04628   
04629     return rv;
04630 }
04631 
04632 nsresult
04633 nsBookmarksService::deleteBookmarkItem(nsIRDFResource *src,
04634                                        nsISupportsArray *aArguments,
04635                                        PRInt32 parentArgIndex)
04636 {
04637     nsresult            rv;
04638 
04639     nsCOMPtr<nsIRDFNode>        aNode;
04640     if (NS_FAILED(rv = getArgumentN(aArguments, kNC_Parent,
04641             parentArgIndex, getter_AddRefs(aNode))))
04642         return rv;
04643     nsCOMPtr<nsIRDFResource>    argParent = do_QueryInterface(aNode);
04644     if (!argParent) return NS_ERROR_NO_INTERFACE;
04645 
04646     nsCOMPtr<nsIRDFContainer> container =
04647             do_CreateInstance(kRDFContainerCID, &rv);
04648     if (NS_FAILED(rv))
04649         return rv;
04650     if (NS_FAILED(rv = container->Init(this, argParent)))
04651         return rv;
04652 
04653     if (NS_FAILED(rv = container->RemoveElement(src, PR_TRUE)))
04654         return rv;
04655 
04656     return rv;
04657 }
04658 
04659 nsresult
04660 nsBookmarksService::setFolderHint(nsIRDFResource *newSource, nsIRDFResource *objType)
04661 {
04662     nsresult            rv;
04663     nsCOMPtr<nsISimpleEnumerator>   srcList;
04664     if (NS_FAILED(rv = GetSources(kNC_FolderType, objType, PR_TRUE, getter_AddRefs(srcList))))
04665         return rv;
04666 
04667     PRBool  hasMoreSrcs = PR_TRUE;
04668     while(NS_SUCCEEDED(rv = srcList->HasMoreElements(&hasMoreSrcs))
04669         && (hasMoreSrcs == PR_TRUE))
04670     {
04671         nsCOMPtr<nsISupports>   aSrc;
04672         if (NS_FAILED(rv = srcList->GetNext(getter_AddRefs(aSrc))))
04673             break;
04674         nsCOMPtr<nsIRDFResource>    aSource = do_QueryInterface(aSrc);
04675         if (!aSource)   continue;
04676 
04677         // if folder is already marked, nothing left to do
04678         if (aSource.get() == newSource)   return NS_OK;
04679 
04680         if (NS_FAILED(rv = mInner->Unassert(aSource, kNC_FolderType, objType)))
04681             continue;
04682     }
04683 
04684     // If not setting a new Personal Toolbar Folder, just assert new type, and
04685     // then done.
04686     if (objType != kNC_PersonalToolbarFolder) {
04687         rv = mInner->Assert(newSource, kNC_FolderType, objType, PR_TRUE);
04688 
04689         mDirty = PR_TRUE;
04690         return rv;
04691     }
04692 
04693     // If setting a new Personal Toolbar Folder, we need to work some magic!
04694     BeginUpdateBatch();
04695     rv = SetNewPersonalToolbarFolder(newSource);
04696     EndUpdateBatch();
04697     if (NS_FAILED(rv))
04698         return rv;
04699 
04700     rv = mInner->Assert(kNC_PersonalToolbarFolder, kNC_FolderType, objType, PR_TRUE);
04701     if (NS_FAILED(rv))
04702         return rv;
04703 
04704     mDirty = PR_TRUE;
04705 
04706     return NS_OK;
04707 }
04708 
04709 nsresult
04710 nsBookmarksService::getFolderViaHint(nsIRDFResource *objType, PRBool fallbackFlag, nsIRDFResource **folder)
04711 {
04712     if (!folder)    return NS_ERROR_UNEXPECTED;
04713     *folder = nsnull;
04714     if (!objType)   return NS_ERROR_UNEXPECTED;
04715 
04716     nsresult            rv;
04717     nsCOMPtr<nsIRDFResource>    oldSource;
04718     if (NS_FAILED(rv = mInner->GetSource(kNC_FolderType, objType, PR_TRUE, getter_AddRefs(oldSource))))
04719         return rv;
04720 
04721     if ((rv != NS_RDF_NO_VALUE) && (oldSource))
04722     {
04723         PRBool isBookmarkedFlag = PR_FALSE;
04724         if (NS_SUCCEEDED(rv = IsBookmarkedResource(oldSource, &isBookmarkedFlag)) &&
04725             isBookmarkedFlag) {
04726             *folder = oldSource;
04727         }
04728     }
04729 
04730     // if we couldn't find a real "New Internet Search Folder", fallback to looking for
04731     // a "New Bookmark Folder", and if can't find that, then default to the bookmarks root
04732     if ((!(*folder)) && (fallbackFlag == PR_TRUE) && (objType == kNC_NewSearchFolder))
04733     {
04734         rv = getFolderViaHint(kNC_NewBookmarkFolder, fallbackFlag, folder);
04735     }
04736 
04737     if (!(*folder))
04738     {
04739         // fallback to some well-known defaults
04740         if (objType == kNC_NewBookmarkFolder || objType == kNC_NewSearchFolder)
04741         {
04742             *folder = kNC_BookmarksRoot;
04743         }
04744         else if (objType == kNC_PersonalToolbarFolder)
04745         {
04746             *folder = kNC_PersonalToolbarFolder;
04747         }
04748     }
04749 
04750     NS_IF_ADDREF(*folder);
04751 
04752     return NS_OK;
04753 }
04754 
04755 nsresult
04756 nsBookmarksService::importBookmarks(nsISupportsArray *aArguments)
04757 {
04758     // look for #URL which is the file path to import
04759     nsresult rv;
04760     nsCOMPtr<nsIRDFNode> aNode;
04761     rv = getArgumentN(aArguments, kNC_URL, 0, getter_AddRefs(aNode));
04762     NS_ENSURE_SUCCESS(rv, rv);
04763     nsCOMPtr<nsIRDFLiteral> pathLiteral = do_QueryInterface(aNode, &rv);
04764     NS_ENSURE_SUCCESS(rv, NS_ERROR_NO_INTERFACE);
04765     const PRUnichar *pathUni = nsnull;
04766     pathLiteral->GetValueConst(&pathUni);
04767     NS_ENSURE_TRUE(pathUni, NS_ERROR_NULL_POINTER);
04768 
04769     nsCOMPtr<nsILocalFile> file;
04770     rv = NS_NewLocalFile(nsDependentString(pathUni), PR_TRUE, getter_AddRefs(file));
04771     NS_ENSURE_SUCCESS(rv, rv);
04772     PRBool isFile;
04773     rv = file->IsFile(&isFile);
04774     NS_ENSURE_TRUE(NS_SUCCEEDED(rv) && isFile, NS_ERROR_UNEXPECTED);
04775 
04776     // figure out where to add the imported bookmarks
04777     nsCOMPtr<nsIRDFResource> newBookmarkFolder;
04778     rv = getFolderViaHint(kNC_NewBookmarkFolder, PR_TRUE, 
04779                           getter_AddRefs(newBookmarkFolder));
04780     NS_ENSURE_SUCCESS(rv, rv);
04781 
04782     // read 'em in
04783     BookmarkParser parser;
04784     parser.Init(file, mInner, PR_TRUE);
04785 
04786     // Note: can't Begin|EndUpdateBatch() this as notifications are required
04787     parser.Parse(newBookmarkFolder, kNC_Bookmark);
04788 
04789     return NS_OK;
04790 }
04791 
04792 nsresult
04793 nsBookmarksService::exportBookmarks(nsISupportsArray *aArguments)
04794 {
04795     // look for #URL which is the file path to export
04796     nsCOMPtr<nsIRDFNode> node;
04797     nsresult rv = getArgumentN(aArguments, kNC_URL, 0, getter_AddRefs(node));
04798     NS_ENSURE_SUCCESS(rv, rv);
04799     nsCOMPtr<nsIRDFLiteral> literal = do_QueryInterface(node, &rv);
04800     NS_ENSURE_SUCCESS(rv, NS_ERROR_NO_INTERFACE);
04801     const PRUnichar* pathUni = nsnull;
04802     literal->GetValueConst(&pathUni);
04803     NS_ENSURE_TRUE(pathUni, NS_ERROR_NULL_POINTER);
04804 
04805     // determine file type to export; default to HTML unless told otherwise
04806     const PRUnichar* format = EmptyString().get();
04807     rv = getArgumentN(aArguments, kRDF_type, 0, getter_AddRefs(node));
04808     if (NS_SUCCEEDED(rv))
04809     {
04810         literal = do_QueryInterface(node, &rv);
04811         NS_ENSURE_SUCCESS(rv, NS_ERROR_NO_INTERFACE);
04812         literal->GetValueConst(&format);
04813         NS_ENSURE_TRUE(format, NS_ERROR_NULL_POINTER);
04814     }
04815 
04816     nsCOMPtr<nsILocalFile> file;
04817     rv = NS_NewLocalFile(nsDependentString(pathUni), PR_TRUE, getter_AddRefs(file));
04818     NS_ENSURE_SUCCESS(rv, rv);
04819 
04820     if (NS_LITERAL_STRING("RDF").Equals(format, nsCaseInsensitiveStringComparator()))
04821     {
04822         nsCOMPtr<nsIURI> uri;
04823         nsresult rv = NS_NewFileURI(getter_AddRefs(uri), file);
04824         NS_ENSURE_SUCCESS(rv, rv);
04825 
04826         rv = SerializeBookmarks(uri);
04827     }
04828     else
04829     {
04830         // write 'em out
04831         rv = WriteBookmarks(file, mInner, kNC_BookmarksRoot);
04832     }
04833 
04834     return rv;
04835 }
04836 
04837 NS_IMETHODIMP
04838 nsBookmarksService::DoCommand(nsISupportsArray *aSources, nsIRDFResource *aCommand,
04839                 nsISupportsArray *aArguments)
04840 {
04841     nsresult        rv = NS_OK;
04842     PRInt32         loop;
04843     PRUint32        numSources;
04844     if (NS_FAILED(rv = aSources->Count(&numSources)))   return rv;
04845     if (numSources < 1)
04846     {
04847         return NS_ERROR_ILLEGAL_VALUE;
04848     }
04849 
04850     // Note: some commands only run once (instead of looping over selection);
04851     //       if that's the case, be sure to "break" (if success) so that "mDirty"
04852     //       is set (and "bookmarks.html" will be flushed out shortly afterwards)
04853 
04854     for (loop=((PRInt32)numSources)-1; loop>=0; loop--)
04855     {
04856         nsCOMPtr<nsIRDFResource> src = do_QueryElementAt(aSources, loop, &rv);
04857         if (!src) return rv;
04858 
04859         if (aCommand == kNC_BookmarkCommand_NewBookmark)
04860         {
04861             rv = insertBookmarkItem(src, aArguments, kNC_Bookmark);
04862             if (NS_FAILED(rv))  return rv;
04863             break;
04864         }
04865         else if (aCommand == kNC_BookmarkCommand_NewFolder)
04866         {
04867             rv = insertBookmarkItem(src, aArguments, kNC_Folder);
04868             if (NS_FAILED(rv))  return rv;
04869             break;
04870         }
04871         else if (aCommand == kNC_BookmarkCommand_NewSeparator)
04872         {
04873             rv = insertBookmarkItem(src, aArguments, kNC_BookmarkSeparator);
04874             if (NS_FAILED(rv))  return rv;
04875             break;
04876         }
04877         else if (aCommand == kNC_BookmarkCommand_DeleteBookmark ||
04878             aCommand == kNC_BookmarkCommand_DeleteBookmarkFolder ||
04879             aCommand == kNC_BookmarkCommand_DeleteBookmarkSeparator)
04880         {
04881             if (NS_FAILED(rv = deleteBookmarkItem(src, aArguments, loop)))
04882                 return rv;
04883         }
04884         else if (aCommand == kNC_BookmarkCommand_SetNewBookmarkFolder)
04885         {
04886             rv = setFolderHint(src, kNC_NewBookmarkFolder);
04887             if (NS_FAILED(rv))  return rv;
04888             break;
04889         }
04890         else if (aCommand == kNC_BookmarkCommand_SetPersonalToolbarFolder)
04891         {
04892             rv = setFolderHint(src, kNC_PersonalToolbarFolder);
04893             if (NS_FAILED(rv))  return rv;
04894             break;
04895         }
04896         else if (aCommand == kNC_BookmarkCommand_SetNewSearchFolder)
04897         {
04898             rv = setFolderHint(src, kNC_NewSearchFolder);
04899             if (NS_FAILED(rv))  return rv;
04900             break;
04901         }
04902         else if (aCommand == kNC_BookmarkCommand_Import)
04903         {
04904             rv = importBookmarks(aArguments);
04905             if (NS_FAILED(rv))  return rv;
04906             break;
04907         }
04908         else if (aCommand == kNC_BookmarkCommand_Export)
04909         {
04910             rv = exportBookmarks(aArguments);
04911             if (NS_FAILED(rv))  return rv;
04912             break;
04913         }
04914     }
04915 
04916     mDirty = PR_TRUE;
04917 
04918     return NS_OK;
04919 }
04920 
04921 
04923 // nsIRDFRemoteDataSource
04924 
04925 NS_IMETHODIMP
04926 nsBookmarksService::GetLoaded(PRBool* _result)
04927 {
04928     *_result = PR_TRUE;
04929     return NS_OK;
04930 }
04931 
04932 NS_IMETHODIMP
04933 nsBookmarksService::Init(const char* aURI)
04934 {
04935     return NS_OK;
04936 }
04937 
04938 NS_IMETHODIMP
04939 nsBookmarksService::Refresh(PRBool aBlocking)
04940 {
04941     // XXX re-sync with the bookmarks file, if necessary.
04942     return NS_OK;
04943 }
04944 
04945 NS_IMETHODIMP
04946 nsBookmarksService::Flush()
04947 {
04948     nsresult    rv = NS_OK;
04949 
04950     // Note: we cannot check mDirty here because consumers from script
04951     // (e.g. the BookmarksTransaction code in bookmarks.js) rely on
04952     // flushing us directly, as it has no way to set our mDirty flag.
04953     if (mBookmarksFile)
04954     {
04955         rv = WriteBookmarks(mBookmarksFile, mInner, kNC_BookmarksRoot);
04956     }
04957 
04958     return rv;
04959 }
04960 
04961 NS_IMETHODIMP
04962 nsBookmarksService::FlushTo(const char *aURI)
04963 {
04964   // Do not ever implement this (security)
04965   return NS_ERROR_NOT_IMPLEMENTED;
04966 }
04967 
04968 
04970 // nsIRDFPropagatableDataSource
04971 
04972 NS_IMETHODIMP
04973 nsBookmarksService::GetPropagateChanges(PRBool* aPropagateChanges)
04974 {
04975     nsCOMPtr<nsIRDFPropagatableDataSource> propagatable = do_QueryInterface(mInner);
04976     return propagatable->GetPropagateChanges(aPropagateChanges);
04977 }
04978 
04979 NS_IMETHODIMP
04980 nsBookmarksService::SetPropagateChanges(PRBool aPropagateChanges)
04981 {
04982     nsCOMPtr<nsIRDFPropagatableDataSource> propagatable = do_QueryInterface(mInner);
04983     return propagatable->SetPropagateChanges(aPropagateChanges);
04984 }
04985 
04986 
04988 // Implementation methods
04989 
04990 nsresult
04991 nsBookmarksService::EnsureBookmarksFile()
04992 {
04993     nsresult rv;
04994 
04995     // First we see if the user has set a pref for the location of the 
04996     // bookmarks file.
04997     nsCOMPtr<nsIPrefBranch> prefBranch(do_GetService(NS_PREFSERVICE_CONTRACTID, &rv));
04998     if (NS_SUCCEEDED(rv))
04999     {
05000         nsCOMPtr<nsISupportsString> prefVal;
05001         rv = prefBranch->GetComplexValue("browser.bookmarks.file",
05002                                          NS_GET_IID(nsISupportsString),
05003                                          getter_AddRefs(prefVal));      
05004         if (NS_SUCCEEDED(rv))
05005         {
05006             nsAutoString bookmarksFile;
05007             prefVal->GetData(bookmarksFile); // more efficient than ToString
05008             rv = NS_NewLocalFile(bookmarksFile, PR_TRUE,
05009                                  getter_AddRefs(mBookmarksFile));
05010 
05011             if (NS_SUCCEEDED(rv))
05012             {
05013                 return NS_OK;
05014             }
05015         }
05016     }
05017 
05018 
05019     // Otherwise, we look for bookmarks.html in the current profile
05020     // directory using the magic directory service.
05021     rv = NS_GetSpecialDirectory(NS_APP_BOOKMARKS_50_FILE, (nsIFile **)(nsILocalFile **)getter_AddRefs(mBookmarksFile));
05022     NS_ENSURE_SUCCESS(rv, rv);
05023 
05024     return NS_OK;
05025 }
05026 
05027 
05028 #if defined(XP_MAC) || defined(XP_MACOSX)
05029 
05030 nsresult
05031 nsBookmarksService::ReadFavorites()
05032 {
05033     mIEFavoritesAvailable = PR_TRUE;
05034     nsresult rv;
05035             
05036 #ifdef DEBUG_varga
05037     PRTime      now;
05038 #if defined(XP_MAC)
05039     Microseconds((UnsignedWide *)&now);
05040 #else
05041     now = PR_Now();
05042 #endif
05043     printf("Start reading in IE Favorites.html\n");
05044 #endif
05045 
05046     // look for and import any IE Favorites
05047     nsAutoString    ieTitle;
05048     getLocaleString("ImportedIEFavorites", ieTitle);
05049 
05050     nsCOMPtr<nsIFile> ieFavoritesFile;
05051     rv = NS_GetSpecialDirectory(NS_MAC_PREFS_DIR, getter_AddRefs(ieFavoritesFile));
05052     NS_ENSURE_SUCCESS(rv, rv);
05053 
05054     ieFavoritesFile->Append(NS_LITERAL_STRING("Explorer"));
05055     ieFavoritesFile->Append(NS_LITERAL_STRING("Favorites.html"));
05056 
05057     if (NS_SUCCEEDED(rv = gRDFC->MakeSeq(mInner, kNC_IEFavoritesRoot, nsnull)))
05058     {
05059         BookmarkParser parser;
05060         parser.Init(ieFavoritesFile, mInner);
05061         BeginUpdateBatch();
05062         parser.Parse(kNC_IEFavoritesRoot, kNC_IEFavorite);
05063         EndUpdateBatch();
05064             
05065         nsCOMPtr<nsIRDFLiteral> ieTitleLiteral;
05066         rv = gRDF->GetLiteral(ieTitle.get(), getter_AddRefs(ieTitleLiteral));
05067         if (NS_SUCCEEDED(rv) && ieTitleLiteral)
05068         {
05069             rv = mInner->Assert(kNC_IEFavoritesRoot, kNC_Name, ieTitleLiteral, PR_TRUE);
05070         }
05071     }
05072 #ifdef DEBUG_varga
05073     PRTime      now2;
05074 #if defined(XP_MAC)
05075     Microseconds((UnsignedWide *)&now2);
05076 #else
05077     now = PR_Now();
05078 #endif
05079     PRUint64    loadTime64;
05080     LL_SUB(loadTime64, now2, now);
05081     PRUint32    loadTime32;
05082     LL_L2UI(loadTime32, loadTime64);
05083     printf("Finished reading in IE Favorites.html  (%u microseconds)\n", loadTime32);
05084 #endif
05085     return rv;
05086 }
05087 
05088 #endif
05089 
05090 NS_IMETHODIMP
05091 nsBookmarksService::ReadBookmarks(PRBool *didLoadBookmarks)
05092 {
05093     *didLoadBookmarks = PR_FALSE;
05094     if (!mBookmarksFile)
05095     {
05096         LoadBookmarks();
05097         if (mBookmarksFile) {
05098             *didLoadBookmarks = PR_TRUE;
05099             nsCOMPtr<nsIPrefBranch2> prefBranchInt(do_GetService(NS_PREFSERVICE_CONTRACTID));
05100             if (prefBranchInt)
05101                 prefBranchInt->AddObserver("browser.bookmarks.file", this, true);
05102         }
05103     }
05104     return NS_OK;
05105 }
05106 
05107 nsresult
05108 nsBookmarksService::initDatasource()
05109 {
05110     // the profile manager might call Readbookmarks() in certain circumstances
05111     // so we need to forget about any previous bookmarks
05112     NS_IF_RELEASE(mInner);
05113 
05114     // don't change this to an xml-ds, it will cause serious perf problems
05115     nsresult rv = CallCreateInstance(kRDFInMemoryDataSourceCID, &mInner);
05116     if (NS_FAILED(rv)) return rv;
05117 
05118     rv = mInner->AddObserver(this);
05119     if (NS_FAILED(rv)) return rv;
05120 
05121     rv = gRDFC->MakeSeq(mInner, kNC_BookmarksTopRoot, nsnull);
05122     NS_ASSERTION(NS_SUCCEEDED(rv), "Unable to make NC:BookmarksTopRoot a sequence");
05123     if (NS_FAILED(rv)) return rv;
05124 
05125     rv = gRDFC->MakeSeq(mInner, kNC_BookmarksRoot, nsnull);
05126     NS_ASSERTION(NS_SUCCEEDED(rv), "Unable to make NC:BookmarksRoot a sequence");
05127     if (NS_FAILED(rv)) return rv;
05128 
05129     // Make sure bookmark's root has the correct type
05130     rv = mInner->Assert(kNC_BookmarksTopRoot, kRDF_type, kNC_Folder, PR_TRUE);
05131     if (NS_FAILED(rv)) return rv;
05132 
05133     rv = mInner->Assert(kNC_BookmarksRoot, kRDF_type, kNC_Folder, PR_TRUE);
05134     if (NS_FAILED(rv)) return rv;
05135 
05136     // Insert NC:BookmarksRoot in NC:BookmarksTopRoot
05137     nsCOMPtr<nsIRDFContainer> container(do_CreateInstance(kRDFContainerCID, &rv));
05138     if (NS_FAILED(rv)) return rv;
05139     rv = container->Init(mInner, kNC_BookmarksTopRoot);
05140     if (NS_FAILED(rv)) return rv;
05141     rv = container->AppendElement(kNC_BookmarksRoot);
05142 
05143     return rv;
05144 }
05145 
05146 nsresult
05147 nsBookmarksService::LoadBookmarks()
05148 {
05149     nsresult    rv;
05150 
05151     rv = initDatasource();
05152     if (NS_FAILED(rv)) return NS_OK;
05153 
05154     rv = EnsureBookmarksFile();
05155 
05156     // Lack of Bookmarks file is non-fatal
05157     if (NS_FAILED(rv)) return NS_OK;
05158 
05159     PRBool foundIERoot = PR_FALSE;
05160 
05161     nsCOMPtr<nsIPrefService> prefSvc(do_GetService(NS_PREFSERVICE_CONTRACTID));
05162     nsCOMPtr<nsIPrefBranch> bookmarksPrefs;
05163     if (prefSvc)
05164         prefSvc->GetBranch("browser.bookmarks.", getter_AddRefs(bookmarksPrefs));
05165 
05166 #ifdef DEBUG_varga
05167     PRTime now;
05168 #if defined(XP_MAC)
05169     Microseconds((UnsignedWide *)&now);
05170 #else
05171     now = PR_Now();
05172 #endif
05173     printf("Start reading in bookmarks.html\n");
05174 #endif
05175   
05176     // System Bookmarks Strategy
05177     //
05178     // * By default, we do a one-off import of system bookmarks when the browser 
05179     //   is run for the first time. This creates a hierarchy of bona-fide Mozilla
05180     //   bookmarks that is fully manipulable by the user but is not a live view.
05181     //
05182     // * As an option, the user can enable a "live view" of his or her system
05183     //   bookmarks which are not user manipulable but does update automatically.
05184 
05185     // Determine whether or not the user wishes to see the live view of system
05186     // bookmarks. This is controlled by the 
05187     //
05188     //   browser.bookmarks.import_system_favorites
05189     //
05190     // pref. See bug 22642 for details. 
05191     //
05192     PRBool useDynamicSystemBookmarks;
05193 #ifdef XP_BEOS
05194     // always dynamic in BeOS
05195     useDynamicSystemBookmarks = PR_TRUE;
05196 #else
05197     useDynamicSystemBookmarks = PR_FALSE;
05198     if (bookmarksPrefs)
05199         bookmarksPrefs->GetBoolPref("import_system_favorites", &useDynamicSystemBookmarks);
05200 #endif
05201 
05202     nsCAutoString bookmarksURICString;
05203 
05204 #if defined(XP_WIN) || defined(XP_BEOS)
05205     nsCOMPtr<nsIFile> systemBookmarksFolder;
05206 
05207 #if defined(XP_WIN)
05208     rv = NS_GetSpecialDirectory(NS_WIN_FAVORITES_DIR, getter_AddRefs(systemBookmarksFolder));
05209 #elif defined(XP_BEOS)
05210     rv = NS_GetSpecialDirectory(NS_BEOS_SETTINGS_DIR, getter_AddRefs(systemBookmarksFolder));
05211 
05212     if (NS_SUCCEEDED(rv))
05213         rv = systemBookmarksFolder->AppendNative(NS_LITERAL_CSTRING("NetPositive"));
05214    
05215     if (NS_SUCCEEDED(rv))
05216         rv = systemBookmarksFolder->AppendNative(NS_LITERAL_CSTRING("Bookmarks"));
05217 #endif
05218 
05219     if (NS_SUCCEEDED(rv))
05220     {
05221         nsCOMPtr<nsIURI> bookmarksURI;
05222         rv = NS_NewFileURI(getter_AddRefs(bookmarksURI), systemBookmarksFolder);
05223 
05224         if (NS_SUCCEEDED(rv))
05225             rv = bookmarksURI->GetSpec(bookmarksURICString);
05226     }
05227 #elif defined(XP_MAC) || defined(XP_MACOSX)
05228     bookmarksURICString.AssignLiteral(kURINC_IEFavoritesRoot);
05229 #endif
05230 
05231     nsCOMPtr<nsIRDFResource> systemFolderResource;
05232     if (!bookmarksURICString.IsEmpty())
05233         gRDF->GetResource(bookmarksURICString,
05234                           getter_AddRefs(systemFolderResource));
05235 
05236     // scope the stream to get the open/close automatically.
05237     {
05238         BookmarkParser parser;
05239         parser.Init(mBookmarksFile, mInner);
05240         if (useDynamicSystemBookmarks && !bookmarksURICString.IsEmpty())
05241         {
05242             parser.SetIEFavoritesRoot(bookmarksURICString);
05243             parser.ParserFoundIEFavoritesRoot(&foundIERoot);
05244         }
05245 
05246         BeginUpdateBatch();
05247         parser.Parse(kNC_BookmarksRoot, kNC_Bookmark);
05248         EndUpdateBatch();
05249         
05250         PRBool foundPTFolder = PR_FALSE;
05251         parser.ParserFoundPersonalToolbarFolder(&foundPTFolder);
05252         // try to ensure that we end up with a personal toolbar folder
05253         if ((foundPTFolder == PR_FALSE) && (!mPersonalToolbarName.IsEmpty()))
05254         {
05255             nsCOMPtr<nsIRDFLiteral>   ptNameLiteral;
05256             rv = gRDF->GetLiteral(mPersonalToolbarName.get(), getter_AddRefs(ptNameLiteral));
05257             if (NS_SUCCEEDED(rv))
05258             {
05259                 nsCOMPtr<nsIRDFResource>    ptSource;
05260                 rv = mInner->GetSource(kNC_Name, ptNameLiteral, PR_TRUE, getter_AddRefs(ptSource));
05261                 if (NS_FAILED(rv)) return rv;
05262         
05263                 if ((rv != NS_RDF_NO_VALUE) && (ptSource))
05264                     setFolderHint(ptSource, kNC_PersonalToolbarFolder);
05265             }
05266         }
05267 
05268       // Sets the default bookmarks root name.
05269       nsCOMPtr<nsIRDFLiteral> brNameLiteral;
05270       rv = gRDF->GetLiteral(mBookmarksRootName.get(), getter_AddRefs(brNameLiteral));
05271       if (NS_SUCCEEDED(rv))
05272           mInner->Assert(kNC_BookmarksRoot, kNC_Name, brNameLiteral, PR_TRUE);
05273 
05274     } // <-- scope the stream to get the open/close automatically.
05275 
05276     // Now append the one-time-per-profile empty "Full" System Bookmarks Root. 
05277     // When the user opens this folder for the first time, system bookmarks are 
05278     // imported into this folder. A pref is used to keep track of whether or 
05279     // not to perform this operation. 
05280 #if defined(XP_WIN) || defined(XP_MAC) || defined(XP_MACOSX)
05281     PRBool addedStaticRoot = PR_FALSE;
05282     if (bookmarksPrefs)
05283         bookmarksPrefs->GetBoolPref("added_static_root", 
05284                                     &addedStaticRoot);
05285 
05286     // Add the root that System bookmarks are imported into as real bookmarks. This is 
05287     // only done once. 
05288     if (!addedStaticRoot && systemFolderResource)
05289     {
05290         nsCOMPtr<nsIRDFContainer> rootContainer(do_CreateInstance(kRDFContainerCID, &rv));
05291         if (NS_FAILED(rv)) return rv;
05292 
05293         rv = rootContainer->Init(this, kNC_BookmarksRoot);
05294         if (NS_FAILED(rv)) return rv;
05295 
05296         rv = mInner->Assert(kNC_SystemBookmarksStaticRoot, kRDF_type, kNC_Folder, PR_TRUE);
05297         if (NS_FAILED(rv)) return rv;
05298 
05299         nsAutoString importedStaticTitle;
05300         getLocaleString("ImportedIEStaticFavorites", importedStaticTitle);
05301 
05302         nsCOMPtr<nsIRDFLiteral> staticTitleLiteral;
05303         rv = gRDF->GetLiteral(importedStaticTitle.get(), getter_AddRefs(staticTitleLiteral));
05304         if (NS_FAILED(rv)) return rv;
05305 
05306         rv = mInner->Assert(kNC_SystemBookmarksStaticRoot, kNC_Name, staticTitleLiteral, PR_TRUE);
05307         if (NS_FAILED(rv)) return rv;
05308 
05309         rv = rootContainer->AppendElement(kNC_SystemBookmarksStaticRoot);
05310         if (NS_FAILED(rv)) return rv;
05311 
05312         // If the user has not specifically asked for the Dynamic bookmarks root 
05313         // via the pref, remove it from an existing bookmarks file as it serves
05314         // only to add confusion. 
05315         if (!useDynamicSystemBookmarks)
05316         {
05317             nsCOMPtr<nsIRDFContainer> container(do_CreateInstance(kRDFContainerCID, &rv));
05318             if (NS_FAILED(rv)) return rv;
05319 
05320             rv = container->Init(this, kNC_BookmarksRoot);
05321             if (NS_FAILED(rv)) return rv;
05322       
05323             rv = container->RemoveElement(systemFolderResource, PR_TRUE);
05324             if (NS_FAILED(rv)) return rv;
05325         }
05326 
05327         bookmarksPrefs->SetBoolPref("added_static_root", PR_TRUE);
05328     }
05329 #endif
05330 
05331     // Add the dynamic system bookmarks root if the user has asked for it
05332     // by setting the pref. 
05333     if (useDynamicSystemBookmarks)
05334     {
05335 #if defined(XP_MAC) || defined(XP_MACOSX)
05336         // if the IE Favorites root isn't somewhere in bookmarks.html, add it
05337         if (!foundIERoot)
05338         {
05339             nsCOMPtr<nsIRDFContainer> bookmarksRoot(do_CreateInstance(kRDFContainerCID, &rv));
05340             if (NS_FAILED(rv)) return rv;
05341 
05342             rv = bookmarksRoot->Init(this, kNC_BookmarksRoot);
05343             if (NS_FAILED(rv)) return rv;
05344 
05345             rv = bookmarksRoot->AppendElement(kNC_IEFavoritesRoot);
05346             if (NS_FAILED(rv)) return rv;
05347 
05348             // make sure IE Favorites root folder has the proper type     
05349             rv = mInner->Assert(kNC_IEFavoritesRoot, kRDF_type, kNC_IEFavoriteFolder, PR_TRUE);
05350             if (NS_FAILED(rv)) return rv;
05351         }
05352 #elif defined(XP_WIN) || defined(XP_BEOS)
05353         if (systemFolderResource)
05354         {
05355             nsAutoString systemBookmarksFolderTitle;
05356 #ifdef XP_BEOS
05357             getLocaleString("ImportedNetPositiveBookmarks", systemBookmarksFolderTitle);
05358 #else
05359             getLocaleString("ImportedIEFavorites", systemBookmarksFolderTitle);
05360 #endif
05361 
05362             nsCOMPtr<nsIRDFLiteral>   systemFolderTitleLiteral;
05363             rv = gRDF->GetLiteral(systemBookmarksFolderTitle.get(), 
05364                                   getter_AddRefs(systemFolderTitleLiteral));
05365             if (NS_SUCCEEDED(rv) && systemFolderTitleLiteral)
05366                 rv = mInner->Assert(systemFolderResource, kNC_Name, 
05367                                     systemFolderTitleLiteral, PR_TRUE);
05368     
05369             // if the IE Favorites root isn't somewhere in bookmarks.html, add it
05370             if (!foundIERoot)
05371             {
05372                 nsCOMPtr<nsIRDFContainer> container(do_CreateInstance(kRDFContainerCID, &rv));
05373                 if (NS_FAILED(rv)) return rv;
05374 
05375                 rv = container->Init(this, kNC_BookmarksRoot);
05376                 if (NS_FAILED(rv)) return rv;
05377 
05378                 rv = container->AppendElement(systemFolderResource);
05379                 if (NS_FAILED(rv)) return rv;
05380             }
05381         }
05382 #endif
05383     }
05384 
05385 #ifdef DEBUG_varga
05386     PRTime      now2;
05387 #if defined(XP_MAC)
05388     Microseconds((UnsignedWide *)&now2);
05389 #else
05390     now2 = PR_Now();
05391 #endif
05392     PRUint64    loadTime64;
05393     LL_SUB(loadTime64, now2, now);
05394     PRUint32    loadTime32;
05395     LL_L2UI(loadTime32, loadTime64);
05396     printf("Finished reading in bookmarks.html  (%u microseconds)\n", loadTime32);
05397 #endif
05398 
05399     return NS_OK;
05400 }
05401 
05402 static char kFileIntro[] = 
05403     "<!DOCTYPE NETSCAPE-Bookmark-file-1>" NS_LINEBREAK
05404     "<!-- This is an automatically generated file." NS_LINEBREAK
05405     "     It will be read and overwritten." NS_LINEBREAK
05406     "     DO NOT EDIT! -->" NS_LINEBREAK
05407     // Note: we write bookmarks in UTF-8
05408     "<META HTTP-EQUIV=\"Content-Type\" CONTENT=\"text/html; charset=UTF-8\">" NS_LINEBREAK
05409     "<TITLE>Bookmarks</TITLE>" NS_LINEBREAK
05410     "<H1>Bookmarks</H1>" NS_LINEBREAK NS_LINEBREAK;
05411 
05412 nsresult
05413 nsBookmarksService::WriteBookmarks(nsIFile* aBookmarksFile,
05414                                    nsIRDFDataSource* aDataSource,
05415                                    nsIRDFResource *aRoot)
05416 {
05417     if (!aBookmarksFile || !aDataSource || !aRoot)
05418         return NS_ERROR_NULL_POINTER;
05419 
05420     // get a safe output stream, so we don't clobber the bookmarks file unless
05421     // all the writes succeeded.
05422     nsCOMPtr<nsIOutputStream> out;
05423     nsresult rv = NS_NewSafeLocalFileOutputStream(getter_AddRefs(out),
05424                                                   aBookmarksFile,
05425                                                   -1,
05426                                                   /*octal*/ 0600);
05427     if (NS_FAILED(rv)) return rv;
05428 
05429     // We need a buffered output stream for performance.
05430     // See bug 202477.
05431     nsCOMPtr<nsIOutputStream> strm;
05432     rv = NS_NewBufferedOutputStream(getter_AddRefs(strm), out, 4096);
05433     if (NS_FAILED(rv)) return rv;
05434 
05435     PRUint32 dummy;
05436     strm->Write(kFileIntro, sizeof(kFileIntro)-1, &dummy);
05437 
05438     nsCOMArray<nsIRDFResource> parentArray;
05439     rv = WriteBookmarksContainer(aDataSource, strm, aRoot, 0, parentArray);
05440 
05441     // All went ok. Maybe except for problems in Write(), but the stream detects
05442     // that for us
05443     nsCOMPtr<nsISafeOutputStream> safeStream = do_QueryInterface(strm);
05444     NS_ASSERTION(safeStream, "expected a safe output stream!");
05445     if (NS_SUCCEEDED(rv) && safeStream)
05446         rv = safeStream->Finish();
05447 
05448     if (NS_FAILED(rv)) {
05449         NS_WARNING("failed to save bookmarks file! possible dataloss");
05450         return rv;
05451     }
05452 
05453     mDirty = PR_FALSE;
05454     return NS_OK;
05455 }
05456 
05457 static const char kBookmarkIntro[] = "<DL><p>" NS_LINEBREAK;
05458 static const char kIndent[] = "    ";
05459 static const char kContainerIntro[] = "<DT><H3";
05460 static const char kSpaceStr[] = " ";
05461 static const char kTrueEnd[] = "true\"";
05462 static const char kQuoteStr[] = "\"";
05463 static const char kCloseAngle[] = ">";
05464 static const char kCloseH3[] = "</H3>" NS_LINEBREAK;
05465 static const char kHROpen[] = "<HR";
05466 static const char kAngleNL[] = ">" NS_LINEBREAK;
05467 static const char kDTOpen[] = "<DT><A";
05468 static const char kAClose[] = "</A>" NS_LINEBREAK;
05469 static const char kBookmarkClose[] = "</DL><p>" NS_LINEBREAK;
05470 static const char kNL[] = NS_LINEBREAK;
05471 
05472 nsresult
05473 nsBookmarksService::WriteBookmarksContainer(nsIRDFDataSource *ds,
05474                                             nsIOutputStream* strm,
05475                                             nsIRDFResource *parent, PRInt32 level,
05476                                             nsCOMArray<nsIRDFResource>& parentArray)
05477 {
05478     // rv is used for various functions
05479     nsresult rv;
05480 
05481     nsCOMPtr<nsIRDFContainer> container =
05482             do_CreateInstance(kRDFContainerCID, &rv);
05483     NS_ENSURE_SUCCESS(rv, rv);
05484 
05485     nsCAutoString   indentation;
05486 
05487     for (PRInt32 loop=0; loop<level; loop++)
05488         indentation.Append(kIndent, sizeof(kIndent)-1);
05489 
05490     PRUint32 dummy;
05491     rv = strm->Write(indentation.get(), indentation.Length(), &dummy);
05492     rv |= strm->Write(kBookmarkIntro, sizeof(kBookmarkIntro)-1, &dummy);
05493     NS_ENSURE_SUCCESS(rv, NS_ERROR_UNEXPECTED);
05494 
05495     rv = container->Init(ds, parent);
05496     if (NS_SUCCEEDED(rv) && (parentArray.IndexOfObject(parent) < 0))
05497     {
05498         // Note: once we've added something into the parentArray, don't "return" out
05499         //       of this function without removing it from the parentArray!
05500         parentArray.InsertObjectAt(parent, 0);
05501 
05502         nsCOMPtr<nsISimpleEnumerator>   children;
05503         if (NS_SUCCEEDED(rv = container->GetElements(getter_AddRefs(children))))
05504         {
05505             PRBool  more = PR_TRUE;
05506             while (more == PR_TRUE)
05507             {
05508                 if (NS_FAILED(rv = children->HasMoreElements(&more)))   break;
05509                 if (more != PR_TRUE)    break;
05510 
05511                 nsCOMPtr<nsISupports>   iSupports;                  
05512                 if (NS_FAILED(rv = children->GetNext(getter_AddRefs(iSupports))))   break;
05513 
05514                 nsCOMPtr<nsIRDFResource>    child = do_QueryInterface(iSupports);
05515                 if (!child) break;
05516 
05517                 PRBool  isContainer = PR_FALSE;
05518                 if (child.get() != kNC_IEFavoritesRoot)
05519                 {
05520                     rv = gRDFC->IsContainer(ds, child, &isContainer);
05521                     if (NS_FAILED(rv)) break;
05522                 }
05523 
05524                 nsCOMPtr<nsIRDFNode>    nameNode;
05525                 nsAutoString        nameString;
05526                 nsCAutoString       name;
05527                 rv = ds->GetTarget(child, kNC_Name, PR_TRUE, getter_AddRefs(nameNode));
05528                 if (NS_SUCCEEDED(rv) && nameNode)
05529                 {
05530                     nsCOMPtr<nsIRDFLiteral> nameLiteral = do_QueryInterface(nameNode);
05531                     if (nameLiteral)
05532                     {
05533                         const PRUnichar *title = nsnull;
05534                         if (NS_SUCCEEDED(rv = nameLiteral->GetValueConst(&title)))
05535                         {
05536                             nameString = title;
05537                             AppendUTF16toUTF8(nameString, name);
05538                         }
05539                     }
05540                 }
05541 
05542                 rv = strm->Write(indentation.get(), indentation.Length(), &dummy);
05543                 rv |= strm->Write(kIndent, sizeof(kIndent)-1, &dummy);
05544                 if (NS_FAILED(rv)) break;
05545 
05546                 if (isContainer == PR_TRUE)
05547                 {
05548                     rv = strm->Write(kContainerIntro, sizeof(kContainerIntro)-1, &dummy);
05549                     // output ADD_DATE
05550                     rv |= WriteBookmarkProperties(ds, strm, child, kNC_BookmarkAddDate, kAddDateEquals, PR_FALSE);
05551 
05552                     // output LAST_MODIFIED
05553                     rv |= WriteBookmarkProperties(ds, strm, child, kWEB_LastModifiedDate, kLastModifiedEquals, PR_FALSE);
05554                     if (NS_FAILED(rv)) break;
05555 
05556                     // output various special folder hints
05557                     PRBool  hasType = PR_FALSE;
05558                     if (NS_SUCCEEDED(rv = mInner->HasAssertion(child, kNC_FolderType, kNC_NewBookmarkFolder,
05559                                                                PR_TRUE, &hasType)) && (hasType == PR_TRUE))
05560                     {
05561                         rv = strm->Write(kSpaceStr, sizeof(kSpaceStr)-1, &dummy);
05562                         rv |= strm->Write(kNewBookmarkFolderEquals, sizeof(kNewBookmarkFolderEquals)-1, &dummy);
05563                         rv |= strm->Write(kTrueEnd, sizeof(kTrueEnd)-1, &dummy);
05564                         if (NS_FAILED(rv)) break;
05565                     }
05566                     if (NS_SUCCEEDED(rv = mInner->HasAssertion(child, kNC_FolderType, kNC_NewSearchFolder,
05567                                                                PR_TRUE, &hasType)) && (hasType == PR_TRUE))
05568                     {
05569                         rv = strm->Write(kSpaceStr, sizeof(kSpaceStr)-1, &dummy);
05570                         rv |= strm->Write(kNewSearchFolderEquals, sizeof(kNewSearchFolderEquals)-1, &dummy);
05571                         rv |= strm->Write(kTrueEnd, sizeof(kTrueEnd)-1, &dummy);
05572                         if (NS_FAILED(rv)) break;
05573                     }
05574                     if (NS_SUCCEEDED(rv = mInner->HasAssertion(child, kNC_FolderType, kNC_PersonalToolbarFolder,
05575                                                                PR_TRUE, &hasType)) && (hasType == PR_TRUE))
05576                     {
05577                         rv = strm->Write(kSpaceStr, sizeof(kSpaceStr)-1, &dummy);
05578                         rv |= strm->Write(kPersonalToolbarFolderEquals, sizeof(kPersonalToolbarFolderEquals)-1, &dummy);
05579                         rv |= strm->Write(kTrueEnd, sizeof(kTrueEnd)-1, &dummy);
05580                         if (NS_FAILED(rv)) break;
05581                     }
05582 
05583                     if (NS_SUCCEEDED(rv = mInner->HasArcOut(child, kNC_FolderGroup, &hasType)) && 
05584                         (hasType == PR_TRUE))
05585                     {
05586                         rv = strm->Write(kSpaceStr, sizeof(kSpaceStr)-1, &dummy);
05587                         rv |= strm->Write(kFolderGroupEquals, sizeof(kFolderGroupEquals)-1, &dummy);
05588                         rv |= strm->Write(kTrueEnd, sizeof(kTrueEnd)-1, &dummy);
05589                         if (NS_FAILED(rv)) break;
05590                     }
05591 
05592                     // output ID
05593                     const char  *id = nsnull;
05594                     rv = child->GetValueConst(&id);
05595                     if (NS_SUCCEEDED(rv) && (id))
05596                     {
05597                         rv = strm->Write(kSpaceStr, sizeof(kSpaceStr)-1, &dummy);
05598                         rv |= strm->Write(kIDEquals, sizeof(kIDEquals)-1, &dummy);
05599                         rv |= strm->Write(id, strlen(id), &dummy);
05600                         rv |= strm->Write(kQuoteStr, sizeof(kQuoteStr)-1, &dummy);
05601                         if (NS_FAILED(rv)) break;
05602                     }
05603           
05604                     rv = strm->Write(kCloseAngle, sizeof(kCloseAngle)-1, &dummy);
05605           
05606                     // output title
05607                     if (!name.IsEmpty())
05608                     {
05609                         // see bug #65098
05610                         char *escapedAttrib = nsEscapeHTML(name.get());
05611                         if (escapedAttrib)
05612                         {
05613                             rv |= strm->Write(escapedAttrib, strlen(escapedAttrib), &dummy);
05614                             NS_Free(escapedAttrib);
05615                         }
05616                     }
05617                     rv |= strm->Write(kCloseH3, sizeof(kCloseH3)-1, &dummy);
05618 
05619                     // output description (if one exists)
05620                     rv |= WriteBookmarkProperties(ds, strm, child, kNC_Description, kOpenDD, PR_TRUE);
05621 
05622                     rv |= WriteBookmarksContainer(ds, strm, child, level+1, parentArray);
05623                 }
05624                 else
05625                 {
05626                     const char  *url = nsnull;
05627                     if (NS_SUCCEEDED(rv = child->GetValueConst(&url)) && (url))
05628                     {
05629                         nsCAutoString   uri(url);
05630 
05631                         PRBool      isBookmarkSeparator = PR_FALSE;
05632                         if (NS_SUCCEEDED(mInner->HasAssertion(child, kRDF_type,
05633                                                               kNC_BookmarkSeparator, PR_TRUE, &isBookmarkSeparator)) &&
05634                             (isBookmarkSeparator == PR_TRUE) )
05635                         {
05636                             // its a separator
05637                             rv = strm->Write(kHROpen, sizeof(kHROpen)-1, &dummy);
05638 
05639                             // output NAME
05640                             rv |= WriteBookmarkProperties(ds, strm, child, kNC_Name, kNameEquals, PR_FALSE);
05641 
05642                             rv |= strm->Write(kAngleNL, sizeof(kAngleNL)-1, &dummy);
05643                             if (NS_FAILED(rv)) break;
05644                         }
05645                         else
05646                         {
05647                             rv = strm->Write(kDTOpen, sizeof(kDTOpen)-1, &dummy);
05648 
05649                             // output URL
05650                             rv |= WriteBookmarkProperties(ds, strm, child, kNC_URL, kHREFEquals, PR_FALSE);
05651 
05652                             // output ADD_DATE
05653                             rv |= WriteBookmarkProperties(ds, strm, child, kNC_BookmarkAddDate, kAddDateEquals, PR_FALSE);
05654 
05655                             // output LAST_VISIT
05656                             rv |= WriteBookmarkProperties(ds, strm, child, kWEB_LastVisitDate, kLastVisitEquals, PR_FALSE);
05657 
05658                             // output LAST_MODIFIED
05659                             rv |= WriteBookmarkProperties(ds, strm, child, kWEB_LastModifiedDate, kLastModifiedEquals, PR_FALSE);
05660 
05661                             // output SHORTCUTURL
05662                             rv |= WriteBookmarkProperties(ds, strm, child, kNC_ShortcutURL, kShortcutURLEquals, PR_FALSE);
05663 
05664                             // output kNC_Icon
05665                             rv |= WriteBookmarkProperties(ds, strm, child, kNC_Icon, kIconEquals, PR_FALSE);
05666 
05667                             // output SCHEDULE
05668                             rv |= WriteBookmarkProperties(ds, strm, child, kWEB_Schedule, kScheduleEquals, PR_FALSE);
05669 
05670                             // output LAST_PING
05671                             rv |= WriteBookmarkProperties(ds, strm, child, kWEB_LastPingDate, kLastPingEquals, PR_FALSE);
05672 
05673                             // output PING_ETAG
05674                             rv |= WriteBookmarkProperties(ds, strm, child, kWEB_LastPingETag, kPingETagEquals, PR_FALSE);
05675 
05676                             // output PING_LAST_MODIFIED
05677                             rv |= WriteBookmarkProperties(ds, strm, child, kWEB_LastPingModDate, kPingLastModEquals, PR_FALSE);
05678 
05679                             // output LAST_CHARSET
05680                             rv |= WriteBookmarkProperties(ds, strm, child, kWEB_LastCharset, kLastCharsetEquals, PR_FALSE);
05681 
05682                             // output PING_CONTENT_LEN
05683                             rv |= WriteBookmarkProperties(ds, strm, child, kWEB_LastPingContentLen, kPingContentLenEquals, PR_FALSE);
05684 
05685                             // output PING_STATUS
05686                             rv |= WriteBookmarkProperties(ds, strm, child, kWEB_Status, kPingStatusEquals, PR_FALSE);
05687                             if (NS_FAILED(rv)) break;
05688                             
05689                             // output ID
05690                             const char  *id = nsnull;
05691                             rv = child->GetValueConst(&id);
05692                             if (NS_SUCCEEDED(rv) && (id))
05693                             {
05694                                 rv = strm->Write(kSpaceStr, sizeof(kSpaceStr)-1, &dummy);
05695                                 rv |= strm->Write(kIDEquals, sizeof(kIDEquals)-1, &dummy);
05696                                 rv |= strm->Write(id, strlen(id), &dummy);
05697                                 rv |= strm->Write(kQuoteStr, sizeof(kQuoteStr)-1, &dummy);
05698                                 if (NS_FAILED(rv)) break;
05699                             }
05700 
05701                             rv = strm->Write(kCloseAngle, sizeof(kCloseAngle)-1, &dummy);
05702 
05703                             // output title
05704                             if (!name.IsEmpty())
05705                             {
05706                                 // Note: we escape the title due to security issues;
05707                                 //       see bug # 13197 for details
05708                                 char *escapedAttrib = nsEscapeHTML(name.get());
05709                                 if (escapedAttrib)
05710                                 {
05711                                     rv |= strm->Write(escapedAttrib,
05712                                                        strlen(escapedAttrib),
05713                                                        &dummy);
05714                                     NS_Free(escapedAttrib);
05715                                     escapedAttrib = nsnull;
05716                                 }
05717                             }
05718 
05719                             rv |= strm->Write(kAClose, sizeof(kAClose)-1, &dummy);
05720                             
05721                             // output description (if one exists)
05722                             rv |= WriteBookmarkProperties(ds, strm, child, kNC_Description, kOpenDD, PR_TRUE);
05723                         }
05724                     }
05725                 }
05726 
05727                 if (NS_FAILED(rv))  break;
05728             }
05729         }
05730 
05731         // cleanup: remove current parent element from parentArray
05732         parentArray.RemoveObjectAt(0);
05733     }
05734 
05735     rv |= strm->Write(indentation.get(), indentation.Length(), &dummy);
05736     rv |= strm->Write(kBookmarkClose, sizeof(kBookmarkClose)-1, &dummy);
05737 
05738     NS_ENSURE_SUCCESS(rv, NS_ERROR_UNEXPECTED);
05739 
05740     return NS_OK;
05741 }
05742 
05743 nsresult
05744 nsBookmarksService::SerializeBookmarks(nsIURI* aURI)
05745 {
05746     NS_ASSERTION(aURI, "null ptr");
05747 
05748     nsresult rv;
05749     nsCOMPtr<nsIFileURL> fileURL = do_QueryInterface(aURI, &rv);
05750     if (NS_FAILED(rv)) return rv;
05751 
05752     nsCOMPtr<nsIFile> file;
05753     rv = fileURL->GetFile(getter_AddRefs(file));
05754     if (NS_FAILED(rv)) return rv;
05755 
05756     // if file doesn't exist, create it
05757     (void)file->Create(nsIFile::NORMAL_FILE_TYPE, 0666);
05758 
05759     nsCOMPtr<nsIOutputStream> out;
05760     rv = NS_NewLocalFileOutputStream(getter_AddRefs(out), file);
05761     if (NS_FAILED(rv)) return rv;
05762 
05763     nsCOMPtr<nsIOutputStream> bufferedOut;
05764     rv = NS_NewBufferedOutputStream(getter_AddRefs(bufferedOut), out, 4096);
05765     if (NS_FAILED(rv)) return rv;
05766 
05767     nsCOMPtr<nsIRDFXMLSerializer> serializer =
05768         do_CreateInstance("@mozilla.org/rdf/xml-serializer;1", &rv);
05769     if (NS_FAILED(rv)) return rv;
05770 
05771     rv = serializer->Init(this);
05772     if (NS_FAILED(rv)) return rv;
05773 
05774     nsCOMPtr<nsIRDFXMLSource> source = do_QueryInterface(serializer);
05775     if (! source)
05776         return NS_ERROR_FAILURE;
05777 
05778     return source->Serialize(bufferedOut);
05779 }
05780 
05781 /*
05782     Note: this routine is similar, yet distinctly different from, nsRDFContentUtils::GetTextForNode
05783 */
05784 
05785 nsresult
05786 nsBookmarksService::GetTextForNode(nsIRDFNode* aNode, nsString& aResult)
05787 {
05788     nsresult        rv;
05789     nsIRDFResource  *resource;
05790     nsIRDFLiteral   *literal;
05791     nsIRDFDate      *dateLiteral;
05792     nsIRDFInt       *intLiteral;
05793 
05794     if (! aNode)
05795     {
05796         aResult.Truncate();
05797         rv = NS_OK;
05798     }
05799     else if (NS_SUCCEEDED(rv = aNode->QueryInterface(NS_GET_IID(nsIRDFResource), (void**) &resource)))
05800     {
05801         const char  *p = nsnull;
05802         if (NS_SUCCEEDED(rv = resource->GetValueConst( &p )) && (p))
05803         {
05804             aResult.AssignWithConversion(p);
05805         }
05806         NS_RELEASE(resource);
05807     }
05808     else if (NS_SUCCEEDED(rv = aNode->QueryInterface(NS_GET_IID(nsIRDFDate), (void**) &dateLiteral)))
05809     {
05810     PRInt64     theDate, million;
05811         if (NS_SUCCEEDED(rv = dateLiteral->GetValue( &theDate )))
05812         {
05813             LL_I2L(million, PR_USEC_PER_SEC);
05814             LL_DIV(theDate, theDate, million);          // convert from microseconds (PRTime) to seconds
05815             PRInt32     now32;
05816             LL_L2I(now32, theDate);
05817             aResult.Truncate();
05818             aResult.AppendInt(now32, 10);
05819         }
05820         NS_RELEASE(dateLiteral);
05821     }
05822     else if (NS_SUCCEEDED(rv = aNode->QueryInterface(NS_GET_IID(nsIRDFInt), (void**) &intLiteral)))
05823     {
05824         PRInt32     theInt;
05825         aResult.Truncate();
05826         if (NS_SUCCEEDED(rv = intLiteral->GetValue( &theInt )))
05827         {
05828             aResult.AppendInt(theInt, 10);
05829         }
05830         NS_RELEASE(intLiteral);
05831     }
05832     else if (NS_SUCCEEDED(rv = aNode->QueryInterface(NS_GET_IID(nsIRDFLiteral), (void**) &literal)))
05833     {
05834         const PRUnichar     *p = nsnull;
05835         if (NS_SUCCEEDED(rv = literal->GetValueConst( &p )) && (p))
05836         {
05837             aResult = p;
05838         }
05839         NS_RELEASE(literal);
05840     }
05841     else
05842     {
05843         NS_ERROR("not a resource or a literal");
05844         rv = NS_ERROR_UNEXPECTED;
05845     }
05846 
05847     return rv;
05848 }
05849 
05850 nsresult
05851 nsBookmarksService::WriteBookmarkProperties(nsIRDFDataSource *ds,
05852     nsIOutputStream* strm, nsIRDFResource *child, nsIRDFResource *property,
05853     const char *htmlAttrib, PRBool isFirst)
05854 {
05855     nsresult  rv;
05856     PRUint32  dummy;
05857 
05858     nsCOMPtr<nsIRDFNode>    node;
05859     if (NS_SUCCEEDED(rv = ds->GetTarget(child, property, PR_TRUE, getter_AddRefs(node)))
05860         && (rv != NS_RDF_NO_VALUE))
05861     {
05862         nsAutoString    literalString;
05863         if (NS_SUCCEEDED(rv = GetTextForNode(node, literalString)))
05864         {
05865             if (property == kNC_URL) {
05866                 // Now do properly replace %22's; this is particularly important for javascript: URLs
05867                 PRInt32 offset;
05868                 while ((offset = literalString.FindChar('\"')) >= 0) {
05869                     literalString.Cut(offset, 1);
05870                     literalString.Insert(NS_LITERAL_STRING("%22"), offset);
05871                 }
05872             }
05873 
05874             char        *attribute = ToNewUTF8String(literalString);
05875             if (nsnull != attribute)
05876             {
05877                 if (isFirst == PR_FALSE)
05878                 {
05879                     rv |= strm->Write(kSpaceStr, sizeof(kSpaceStr)-1, &dummy);
05880                 }
05881 
05882                 if (property == kNC_Description)
05883                 {
05884                     if (!literalString.IsEmpty())
05885                     {
05886                         char *escapedAttrib = nsEscapeHTML(attribute);
05887                         if (escapedAttrib)
05888                         {
05889                             rv |= strm->Write(htmlAttrib, strlen(htmlAttrib), &dummy);
05890                             rv |= strm->Write(escapedAttrib, strlen(escapedAttrib), &dummy);
05891                             rv |= strm->Write(kNL, sizeof(kNL)-1, &dummy);
05892 
05893                             NS_Free(escapedAttrib);
05894                             escapedAttrib = nsnull;
05895                         }
05896                     }
05897                 }
05898                 else
05899                 {
05900                     rv |= strm->Write(htmlAttrib, strlen(htmlAttrib), &dummy);
05901                     rv |= strm->Write(attribute, strlen(attribute), &dummy);
05902                     rv |= strm->Write(kQuoteStr, sizeof(kQuoteStr)-1, &dummy);
05903                 }
05904                 NS_Free(attribute);
05905                 attribute = nsnull;
05906             }
05907         }
05908     }
05909     if (NS_FAILED(rv))
05910         return NS_ERROR_UNEXPECTED;
05911     
05912     return NS_OK;
05913 }
05914 
05915 PRBool
05916 nsBookmarksService::CanAccept(nsIRDFResource* aSource,
05917                   nsIRDFResource* aProperty,
05918                   nsIRDFNode* aTarget)
05919 {
05920     nsresult    rv;
05921     PRBool      isBookmarkedFlag = PR_FALSE, canAcceptFlag = PR_FALSE, isOrdinal;
05922 
05923     if (NS_SUCCEEDED(rv = IsBookmarkedResource(aSource, &isBookmarkedFlag)) &&
05924         (isBookmarkedFlag == PR_TRUE) &&
05925         (NS_SUCCEEDED(rv = gRDFC->IsOrdinalProperty(aProperty, &isOrdinal))))
05926     {
05927         if (isOrdinal == PR_TRUE)
05928         {
05929             canAcceptFlag = PR_TRUE;
05930         }
05931         else if ((aProperty == kNC_Description) ||
05932              (aProperty == kNC_Name) ||
05933              (aProperty == kNC_ShortcutURL) ||
05934              (aProperty == kNC_URL) ||
05935              (aProperty == kWEB_LastModifiedDate) ||
05936              (aProperty == kWEB_LastVisitDate) ||
05937              (aProperty == kNC_BookmarkAddDate) ||
05938              (aProperty == kRDF_nextVal) ||
05939              (aProperty == kRDF_type) ||
05940              (aProperty == kWEB_Schedule))
05941         {
05942             canAcceptFlag = PR_TRUE;
05943         }
05944     }
05945     return canAcceptFlag;
05946 }
05947 
05948 
05949 //----------------------------------------------------------------------
05950 //
05951 // nsIRDFObserver interface
05952 //
05953 
05954 NS_IMETHODIMP
05955 nsBookmarksService::OnAssert(nsIRDFDataSource* aDataSource,
05956                  nsIRDFResource* aSource,
05957                  nsIRDFResource* aProperty,
05958                  nsIRDFNode* aTarget)
05959 {
05960     if (mUpdateBatchNest != 0)  return NS_OK;
05961 
05962     PRInt32 count = mObservers.Count();
05963     for (PRInt32 i = 0; i < count; ++i)
05964     {
05965         (void) mObservers[i]->OnAssert(this, aSource, aProperty, aTarget);
05966     }
05967 
05968     return NS_OK;
05969 }
05970 
05971 NS_IMETHODIMP
05972 nsBookmarksService::OnUnassert(nsIRDFDataSource* aDataSource,
05973                    nsIRDFResource* aSource,
05974                    nsIRDFResource* aProperty,
05975                    nsIRDFNode* aTarget)
05976 {
05977     if (mUpdateBatchNest != 0)  return NS_OK;
05978 
05979     PRInt32 count = mObservers.Count();
05980     for (PRInt32 i = 0; i < count; ++i)
05981     {
05982         (void) mObservers[i]->OnUnassert(this, aSource, aProperty, aTarget);
05983     }
05984 
05985     return NS_OK;
05986 }
05987 
05988 NS_IMETHODIMP
05989 nsBookmarksService::OnChange(nsIRDFDataSource* aDataSource,
05990                  nsIRDFResource* aSource,
05991                  nsIRDFResource* aProperty,
05992                  nsIRDFNode* aOldTarget,
05993                  nsIRDFNode* aNewTarget)
05994 {
05995     if (mUpdateBatchNest != 0)  return NS_OK;
05996 
05997     PRInt32 count = mObservers.Count();
05998     for (PRInt32 i = 0; i < count; ++i)
05999     {
06000         (void) mObservers[i]->OnChange(this, aSource, aProperty, aOldTarget, aNewTarget);
06001     }
06002 
06003     return NS_OK;
06004 }
06005 
06006 NS_IMETHODIMP
06007 nsBookmarksService::OnMove(nsIRDFDataSource* aDataSource,
06008                nsIRDFResource* aOldSource,
06009                nsIRDFResource* aNewSource,
06010                nsIRDFResource* aProperty,
06011                nsIRDFNode* aTarget)
06012 {
06013     if (mUpdateBatchNest != 0)  return NS_OK;
06014 
06015     PRInt32 count = mObservers.Count();
06016     for (PRInt32 i = 0; i < count; ++i)
06017     {
06018         (void) mObservers[i]->OnMove(this, aOldSource, aNewSource, aProperty, aTarget);
06019     }
06020 
06021     return NS_OK;
06022 }
06023 
06024 NS_IMETHODIMP
06025 nsBookmarksService::OnBeginUpdateBatch(nsIRDFDataSource* aDataSource)
06026 {
06027     if (mUpdateBatchNest++ == 0)
06028     {
06029         PRInt32 count = mObservers.Count();
06030         for (PRInt32 i = 0; i < count; ++i) {
06031             (void) mObservers[i]->OnBeginUpdateBatch(this);
06032         }
06033     }
06034 
06035     return NS_OK;
06036 }
06037 
06038 NS_IMETHODIMP
06039 nsBookmarksService::OnEndUpdateBatch(nsIRDFDataSource* aDataSource)
06040 {
06041     NS_ASSERTION(mUpdateBatchNest > 0, "badly nested update batch");
06042 
06043     if (--mUpdateBatchNest == 0)
06044     {
06045         PRInt32 count = mObservers.Count();
06046         for (PRInt32 i = 0; i < count; ++i) {
06047             (void) mObservers[i]->OnEndUpdateBatch(this);
06048         }
06049     }
06050 
06051     return NS_OK;
06052 }