(* ***** BEGIN LICENSE BLOCK ***** * Version: MPL 1.1/GPL 2.0/LGPL 2.1 * * The contents of this file are subject to the Mozilla Public License Version * 1.1 (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * http://www.mozilla.org/MPL/ * * Software distributed under the License is distributed on an "AS IS" basis, * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License * for the specific language governing rights and limitations under the * License. * * The Original Code is mozilla.org. * * The Initial Developer of the Original Code is * Netscape Communications Corpotation. * Portions created by the Initial Developer are Copyright (C) 1998 * the Initial Developer. All Rights Reserved. * * Contributor(s): * Takanori Itou * * Alternatively, the contents of this file may be used under the terms of * either the GNU General Public License Version 2 or later (the "GPL"), or * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), * in which case the provisions of the GPL or the LGPL are applicable instead * of those above. If you wish to allow use of your version of this file only * under the terms of either the GPL or the LGPL, and not to allow others to * use your version of this file under the terms of the MPL, indicate your * decision by deleting the provisions above and replace them with the notice * and other provisions required by the GPL or the LGPL. If you do not delete * the provisions above, a recipient may use your version of this file under * the terms of any one of the MPL, the GPL or the LGPL. * * ***** END LICENSE BLOCK ***** *) unit nsProfile; interface uses nsXPCOM, nsTypes, nsXPCOMGlue; type nsProfileDirServiceProvider = interface(nsIDirectoryServiceProvider) procedure SetProfileDir(AProfileDir: nsIFile); procedure Register; procedure Shutdown; end; EGeckoProfileError = class(EGeckoError); function NS_NewProfileDirServiceProvider(aNotifyObservers: PRBool): nsProfileDirServiceProvider; resourcestring SProfileRegisterError = 'Failed to register profile.'; SProfileShutdownError = 'Failed to shutdown profile.'; SSetProfileDirError = 'Failed to register profile directory.'; SNotADirectory = 'The specified file is not a directory.'; SProfileInitError = 'Failed to initialize profile.'; SEnsureProfileDirError = 'Cannot ensure profile directory.'; implementation uses Windows, SysUtils, nsConsts, nsError, nsGeckoStrings, nsCID; type TProfileDirLock = class(TObject) FHaveLock: Boolean; FLockFileHandle: THandle; constructor Create; overload; constructor Create(src: TProfileDirLock); overload; destructor Destroy; override; procedure Assign(rhs: TProfileDirLock); function Lock(aFile: nsILocalFile): nsresult; function Unlock: nsresult; end; TProfileDirServiceProvider = class(TInterfacedObject, nsProfileDirServiceProvider, nsIDirectoryServiceProvider) FProfileDir: nsIFile; FProfileDirLock: TProfileDirLock; FNotifyObservers: PRBool; FSharingEnabled: Boolean; FNonSharedDirName: IInterfacedString; FNonSharedProfileDir: nsIFile; function GetFile(const prop: PAnsiChar; out persistent: PRBool): nsIFile; safecall; procedure SetProfileDir(AProfileDir: nsIFile); procedure Register; procedure Shutdown; constructor Create(aNotifyObservers: PRBool = True); destructor Destroy; override; procedure Initialize; procedure InitProfileDir(profileDir: nsIFile); procedure InitNonSharedProfileDir; procedure EnsureProfileFileExists(aFile: nsIFile; destDir: nsIFile); procedure UndefineFileLocations; function SafeCallException(ExceptObject: TObject; ExceptAddr: Pointer): HResult; override; end; constructor TProfileDirLock.Create; begin inherited Create; FHaveLock := False; FLockFileHandle := INVALID_HANDLE_VALUE; end; constructor TProfileDirLock.Create(src: TProfileDirLock); begin inherited Create; Assign(src); end; procedure TProfileDirLock.Assign(rhs: TProfileDirLock); begin Unlock(); FHaveLock := rhs.FHaveLock; rhs.FHaveLock := False; FLockFileHandle := rhs.FLockFileHandle; rhs.FLockFileHandle := INVALID_HANDLE_VALUE; end; destructor TProfileDirLock.Destroy; begin Unlock(); inherited; end; function TProfileDirLock.Lock(aFile: nsILocalFile): nsresult; const LOCKFILE_NAME = 'parent.lock'; var isDir: PRBool; lockFile: nsIFile; lockFileName: IInterfacedString; filePath: IInterfacedCString; begin try lockFileName := NewString(LOCKFILE_NAME); filePath := NewCString; if not FHaveLock then begin Result := NS_ERROR_UNEXPECTED; Exit; end; isDir := aFile.IsDirectory(); if not isDir then begin Result := NS_ERROR_FILE_NOT_DIRECTORY; Exit; end; lockFile := aFile.Clone(); lockFile.Append(lockFileName.AString); lockFile.GetNativePath(filePath.ACString); FLockFileHandle := CreateFileA(PAnsiChar(filePath.ToString), GENERIC_READ or GENERIC_WRITE, 0, nil, OPEN_ALWAYS, FILE_FLAG_DELETE_ON_CLOSE, 0); if FLockFileHandle = INVALID_HANDLE_VALUE then begin Result := NS_ERROR_FILE_ACCESS_DENIED; Exit; end; FHaveLock := True; Result := NS_OK; except on EOutOfMemory do Result := NS_ERROR_OUT_OF_MEMORY; on ESafeCallException do Result := NS_ERROR_FAILURE; else Result := NS_ERROR_UNEXPECTED; end; end; function TProfileDirLock.Unlock: nsresult; begin Result := NS_OK; if FHaveLock then begin if FLockFileHandle <> INVALID_HANDLE_VALUE then begin CloseHandle(FLockFileHandle); FLockFileHandle := INVALID_HANDLE_VALUE; end; FHaveLock := False; end; end; constructor TProfileDirServiceProvider.Create(aNotifyObservers: PRBool); begin inherited Create; FNotifyObservers := aNotifyObservers; end; destructor TProfileDirServiceProvider.Destroy; begin FProfileDirLock.Free; inherited; end; procedure TProfileDirServiceProvider.SetProfileDir(aProfileDir: nsIFile); var {$IFDEF MOZ_PROFILELOCKING} dirToLock: nsILocalFile; {$ENDIF} observerService: nsIObserverService; begin try if Assigned(FProfileDir) then begin if Assigned(aProfileDir) and aProfileDir.Equals(FProfileDir) then begin Exit; end; {$IFDEF MOZ_PROFILELOCKING} FProfileDirLock.Unlock(); {$ENDIF} UndefineFileLocations; end; FProfileDir := aProfileDir; if not Assigned(FProfileDir) then begin Exit; end; {$IFDEF MOZ_PROFILELOCKING} Result := InitProfileDir(FProfileDir); if NS_FAILED(Result) then Exit; if FSharingEnabled then Result := FNonSharedProfileDir.QueryInterface(nsILocalFile, dirToLock) else Result := FNonSharedProfileDir.QueryInterface(nsILocalFile, dirToLock); if NS_FAILED(Result) then Exit; Result := FProfileDirLock.Lock(dirToLock); if NS_FAILED(Result) then Exit; {$ENDIF} if FNotifyObservers then begin NS_GetService('@mozilla.org/observer-service;1', nsIObserverService, observerService); observerService.NotifyObservers(nil, 'profile-do-change', 'startup'); observerService.NotifyObservers(nil, 'profile-after-change', 'startup'); end; except raise EGeckoProfileError.CreateRes(PResStringRec(@SSetProfileDirError)); end; end; procedure TProfileDirServiceProvider.Register; var directoryService: nsIDirectoryService; begin NS_GetService(NS_DIRECTORY_SERVICE_CONTRACTID, nsIDirectoryService, directoryService); try directoryService.RegisterProvider(Self); except raise EGeckoProfileError.CreateRes(PResStringRec(@SProfileRegisterError)); end; end; procedure TProfileDirServiceProvider.Shutdown; var observerService: nsIObserverService; begin NS_GetService('@mozilla.org/observer-service;1', nsIObserverService, observerService); try observerService.NotifyObservers(nil, 'profile-before-change', 'shutdown-persist'); except raise EGeckoProfileError.CreateRes(PResStringRec(@SProfileShutdownError)); end; end; function TProfileDirServiceProvider.GetFile(const prop: PAnsiChar; out persistent: PRBool): nsIFile; var localFile: nsIFile; domainDir: nsIFile; appendStr: IInterfacedCString; const PREFS_FILE_50_NAME =' prefs.js'; USER_CHROME_DIR_50_NAME = 'chrome'; LOCAL_STORE_FILE_50_NAME = 'localstore.rdf'; HISTORY_FILE_50_NAME = 'history.dat'; PANELS_FILE_50_NAME = 'panels.rdf'; MIME_TYPES_FILE_50_NAME = 'mimeTypes.rdf'; BOOKMARKS_FILE_50_NAME = 'bookmark.html'; DOWNLOADS_FILE_50_NAME = 'downloads.rdf'; SEARCH_FILE_50_NAME = 'search.rdf'; MAIL_DIR_50_NAME = 'Mail'; IMAP_MAIL_DIR_50_NAME = 'ImapMail'; NEWS_DIR_50_NAME = 'News'; MSG_FOLDER_CACHE_DIR_50_NAME = 'panacea.dat'; begin appendStr := NewCString; persistent := True; domainDir := FProfileDir; Assert(Assigned(domainDir)); if prop = NS_APP_PREFS_50_DIR then begin localFile := domainDir.Clone(); end else if prop = NS_APP_PREFS_50_FILE then begin localFile := domainDir.Clone(); appendStr.Assign(PREFS_FILE_50_NAME); localFile.AppendNative(appendStr.ACString); end else if prop = NS_APP_USER_PROFILE_50_DIR then begin localFile := domainDir.Clone(); end else if prop = NS_APP_USER_CHROME_DIR then begin localFile := domainDir.Clone(); appendStr.Assign(USER_CHROME_DIR_50_NAME); localFile.AppendNative(appendStr.ACString); end else if prop = NS_APP_LOCALSTORE_50_FILE then begin localFile := domainDir.Clone(); appendStr.Assign(LOCAL_STORE_FILE_50_NAME); localFile.AppendNative(appendStr.ACString); EnsureProfileFileExists(localFile, domainDir); end else if prop = NS_APP_HISTORY_50_FILE then begin localFile := domainDir.Clone(); appendStr.Assign(HISTORY_FILE_50_NAME); localFile.AppendNative(appendStr.ACString); end else if prop = NS_APP_USER_PANELS_50_FILE then begin localFile := domainDir.Clone(); appendStr.Assign(PANELS_FILE_50_NAME); localFile.AppendNative(appendStr.ACString); EnsureProfileFileExists(localFile, domainDir); end else if prop = NS_APP_USER_MIMETYPES_50_FILE then begin localFile := domainDir.Clone(); appendStr.Assign(MIME_TYPES_FILE_50_NAME); localFile.AppendNative(appendStr.ACString); EnsureProfileFileExists(localFile, domainDir); end else if prop = NS_APP_BOOKMARKS_50_FILE then begin localFile := domainDir.Clone(); appendStr.Assign(BOOKMARKS_FILE_50_NAME); localFile.AppendNative(appendStr.ACString); end else if prop = NS_APP_DOWNLOADS_50_FILE then begin localFile := domainDir.Clone(); appendStr.Assign(DOWNLOADS_FILE_50_NAME); localFile.AppendNative(appendStr.ACString); end else if prop = NS_APP_SEARCH_50_FILE then begin localFile := domainDir.Clone(); appendStr.Assign(SEARCH_FILE_50_NAME); localFile.AppendNative(appendStr.ACString); EnsureProfileFileExists(localFile, domainDir); end else if prop = NS_APP_MAIL_50_DIR then begin localFile := domainDir.Clone(); appendStr.Assign(MAIL_DIR_50_NAME); localFile.AppendNative(appendStr.ACString); end else if prop = NS_APP_IMAP_MAIL_50_DIR then begin localFile := domainDir.Clone(); appendStr.Assign(IMAP_MAIL_DIR_50_NAME); localFile.AppendNative(appendStr.ACString); end else if prop = NS_APP_NEWS_50_DIR then begin localFile := domainDir.Clone(); appendStr.Assign(NEWS_DIR_50_NAME); localFile.AppendNative(appendStr.ACString); end else if prop = NS_APP_MESSENGER_FOLDER_CACHE_50_DIR then begin localFile := domainDir.Clone(); appendStr.Assign(MSG_FOLDER_CACHE_DIR_50_NAME); localFile.AppendNative(appendStr.ACString); end; if Assigned(localFile) then Result := localFile as nsIFile; end; procedure TProfileDirServiceProvider.Initialize; begin {$IFDEF MOZ_PROFILELOCKING} FProfileDir := TProfileDirServiceProvider.Create(FNotifyObservers); {$ENDIF} end; procedure TProfileDirServiceProvider.InitProfileDir(profileDir: nsIFile); var exists: PRBool; profileDefaultsDir: nsIFile; profileDirParent: nsIFile; profileDirName: IInterfacedCString; isDir: PRBool; begin try profileDirName := NewCString; exists := profileDir.Exists(); if not exists then begin profileDirParent := profileDir.Parent; profileDir.GetNativeLeafName(profileDirName.ACString); try profileDefaultsDir := NS_GetSpecialDirectory(NS_APP_PROFILE_DEFAULTS_50_DIR); except profileDefaultsDir := NS_GetSpecialDirectory(NS_APP_PROFILE_DEFAULTS_NLOC_50_DIR); end; try profileDefaultsDir.CopyToNative(profileDirParent, profileDirName.ACString); except profileDirParent.AppendNative(profileDirName.ACString); profileDirParent.Create(NS_IFILE_DIRECTORY_TYPE, 7 shl 6); end; end else begin isDir := profileDir.IsDirectory(); if not isDir then raise EGeckoProfileError.CreateRes(PResStringRec(@SNotADirectory)); end; if FNonSharedDirName.Length > 0 then InitNonSharedProfileDir; except on EGeckoError do raise else raise EGeckoProfileError.CreateRes(PResStringRec(@SProfileInitError)); end; end; procedure TProfileDirServiceProvider.InitNonSharedProfileDir; var localDir: nsIFile; exists: PRBool; isDir: PRBool; begin try localDir := FProfileDir.Clone(); localDir.Append(FNonSharedDirName.AString); exists := localDir.Exists(); if not exists then begin localDir.Create(NS_IFILE_DIRECTORY_TYPE, 7 shl 6); end else begin isDir := localDir.IsDirectory(); if not isDir then raise EGeckoProfileError.CreateRes(PResStringRec(@SNotADirectory)); end; FNonSharedProfileDir := localDir; except on EGeckoError do raise else raise EGeckoProfileError.CreateRes(PResStringRec(@SProfileInitError)); end; end; procedure TProfileDirServiceProvider.EnsureProfileFileExists(aFile: nsIFile; destDir: nsIFile); var exists: PRBool; defaultsFile: nsIFile; leafName: IInterfacedCString; begin try exists := aFile.Exists; if exists then begin Exit; end; try defaultsFile := NS_GetSpecialDirectory(NS_APP_PROFILE_DEFAULTS_50_DIR); except defaultsFile := NS_GetSpecialDirectory(NS_APP_PROFILE_DEFAULTS_NLOC_50_DIR); end; leafName := NewCString; aFile.GetNativeLeafName(leafName.ACString); defaultsFile.AppendNative(leafName.ACString); leafName.Assign(''); defaultsFile.CopyToNative(destDir, leafName.ACString); except on EGeckoError do raise else raise EGeckoProfileError.CreateRes(PResStringRec(@SEnsureProfileDirError)); end; end; procedure TProfileDirServiceProvider.UndefineFileLocations; var directoryService : nsIProperties; i: Integer; const NUM_OF_DIRS = 15; Dirs: array [1..15] of PAnsiChar = ( NS_APP_PREFS_50_DIR, NS_APP_PREFS_50_FILE, NS_APP_USER_PROFILE_50_DIR, NS_APP_USER_CHROME_DIR, NS_APP_LOCALSTORE_50_FILE, NS_APP_HISTORY_50_FILE, NS_APP_USER_PANELS_50_FILE, NS_APP_USER_MIMETYPES_50_FILE, NS_APP_BOOKMARKS_50_FILE, NS_APP_DOWNLOADS_50_FILE, NS_APP_SEARCH_50_FILE, NS_APP_MAIL_50_DIR, NS_APP_IMAP_MAIL_50_DIR, NS_APP_NEWS_50_DIR, NS_APP_MESSENGER_FOLDER_CACHE_50_DIR ); begin NS_GetService(NS_DIRECTORY_SERVICE_CONTRACTID, nsIProperties, directoryService); for I:=1 to NUM_OF_DIRS do try directoryService.Undefine(Dirs[I]); except end; end; function TProfileDirServiceProvider.SafeCallException(ExceptObject: TObject; ExceptAddr: Pointer): HResult; begin Result := HResult(NS_ERROR_FAILURE); end; function NS_NewProfileDirServiceProvider(aNotifyObservers: PRBool): nsProfileDirServiceProvider; var prov: TProfileDirServiceProvider; begin prov := TProfileDirServiceProvider.Create(aNotifyObservers); prov.Initialize; Result := prov; prov.FNotifyObservers := aNotifyObservers; end; end.