/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ /* ***** 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 The Browser Profile Migrator. * * The Initial Developer of the Original Code is Ben Goodger. * Portions created by the Initial Developer are Copyright (C) 2004 * the Initial Developer. All Rights Reserved. * * Contributor(s): * Ben Goodger * * 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 ***** */ #include "nsBrowserProfileMigratorUtils.h" #include "nsCRT.h" #include "nsDirectoryServiceDefs.h" #include "nsIObserverService.h" #include "nsIPrefService.h" #include "nsIRegistry.h" #include "nsIServiceManager.h" #include "nsISupportsArray.h" #include "nsISupportsPrimitives.h" #include "nsNetCID.h" #include "nsNetUtil.h" #include "nsFirefoxProfileMigrator.h" #include "nsIProfileMigrator.h" #include "nsILineInputStream.h" #include "nsINIParser.h" /////////////////////////////////////////////////////////////////////////////// // nsFirefoxProfileMigrator #define FILE_NAME_BOOKMARKS NS_LITERAL_STRING("bookmarks.html") #define FILE_NAME_COOKIES NS_LITERAL_STRING("cookies.txt") #define FILE_NAME_SITEPERM_OLD NS_LITERAL_STRING("cookperm.txt") #define FILE_NAME_SITEPERM_NEW NS_LITERAL_STRING("hostperm.1") #define FILE_NAME_CERT8DB NS_LITERAL_STRING("cert8.db") #define FILE_NAME_KEY3DB NS_LITERAL_STRING("key3.db") #define FILE_NAME_KEY3DB2 NS_LITERAL_STRING("key3.db2") #define FILE_NAME_SECMODDB NS_LITERAL_STRING("secmod.db") #define FILE_NAME_HISTORY NS_LITERAL_STRING("history.dat") #define FILE_NAME_FORMHISTORY NS_LITERAL_STRING("formhistory.dat") #define FILE_NAME_LOCALSTORE NS_LITERAL_STRING("localstore.rdf") #define FILE_NAME_MIMETYPES NS_LITERAL_STRING("mimeTypes.rdf") #define FILE_NAME_DOWNLOADS NS_LITERAL_STRING("downloads.rdf") #define FILE_NAME_PREFS NS_LITERAL_STRING("prefs.js") #define FILE_NAME_USER_PREFS NS_LITERAL_STRING("user.js") #define FILE_NAME_SEARCH NS_LITERAL_STRING("search.rdf") #define FILE_NAME_USERCHROME NS_LITERAL_STRING("userChrome.css") #define FILE_NAME_USERCONTENT NS_LITERAL_STRING("userContent.css") #define DIR_NAME_CHROME NS_LITERAL_STRING("chrome") NS_IMPL_ISUPPORTS1(nsFirefoxProfileMigrator, nsIBrowserProfileMigrator) nsFirefoxProfileMigrator::nsFirefoxProfileMigrator() { mObserverService = do_GetService("@mozilla.org/observer-service;1"); } nsFirefoxProfileMigrator::~nsFirefoxProfileMigrator() { } /////////////////////////////////////////////////////////////////////////////// // nsIBrowserProfileMigrator NS_IMETHODIMP nsFirefoxProfileMigrator::Migrate(PRUint16 aItems, nsIProfileStartup* aStartup, const PRUnichar* aProfile) { nsresult rv = NS_OK; // At this time the only reason for this migrator is to get data across from the // Firefox profile directory on initial run, so we don't need to support after-the-fact // importing. NS_ASSERTION(aStartup, "Can't migrate from Firefox/Firebird/Firefox profiles once Firefox is running!"); if (!aStartup) return NS_ERROR_FAILURE; if (!mTargetProfile) { GetProfilePath(aStartup, mTargetProfile); if (!mTargetProfile) return NS_ERROR_FAILURE; } nsAutoString tmp; mTargetProfile->GetPath(tmp); if (!mSourceProfile) GetSourceProfile(aProfile); NOTIFY_OBSERVERS(MIGRATION_STARTED, nsnull); COPY_DATA(CopyHistory, PR_TRUE, nsIBrowserProfileMigrator::HISTORY); COPY_DATA(CopyPreferences, PR_TRUE, nsIBrowserProfileMigrator::SETTINGS); rv = NS_OK;// prefs already exist! COPY_DATA(CopyCookies, PR_TRUE, nsIBrowserProfileMigrator::COOKIES); COPY_DATA(CopyPasswords, PR_TRUE, nsIBrowserProfileMigrator::PASSWORDS); COPY_DATA(CopyOtherData, PR_TRUE, nsIBrowserProfileMigrator::OTHERDATA); if (aItems & nsIBrowserProfileMigrator::SETTINGS || aItems & nsIBrowserProfileMigrator::COOKIES || aItems & nsIBrowserProfileMigrator::PASSWORDS || !aItems) { // Permissions (Images, Cookies, Popups) rv |= CopyFile(FILE_NAME_SITEPERM_NEW, FILE_NAME_SITEPERM_NEW); rv |= CopyFile(FILE_NAME_SITEPERM_OLD, FILE_NAME_SITEPERM_OLD); } if(aStartup) aStartup->DoStartup();//enable access to ProfD for dependent services (ie favorites) COPY_DATA(CopyBookmarks, PR_FALSE, nsIBrowserProfileMigrator::BOOKMARKS); NOTIFY_OBSERVERS(MIGRATION_ENDED, nsnull); return rv; } NS_IMETHODIMP nsFirefoxProfileMigrator::GetMigrateData(const PRUnichar* aProfile, PRBool aReplace, PRUint16* aResult) { *aResult = 0; if (!mSourceProfile) { GetSourceProfile(aProfile); if (!mSourceProfile) return NS_ERROR_FILE_NOT_FOUND; } MigrationData data[] = { /* { ToNewUnicode(FILE_NAME_PREFS), nsIBrowserProfileMigrator::SETTINGS, PR_TRUE }, { ToNewUnicode(FILE_NAME_USER_PREFS), nsIBrowserProfileMigrator::SETTINGS, PR_TRUE }, */ { ToNewUnicode(FILE_NAME_COOKIES), nsIBrowserProfileMigrator::COOKIES, PR_TRUE }, { ToNewUnicode(FILE_NAME_HISTORY), nsIBrowserProfileMigrator::HISTORY, PR_TRUE }, { ToNewUnicode(FILE_NAME_BOOKMARKS), nsIBrowserProfileMigrator::BOOKMARKS, PR_TRUE }, { ToNewUnicode(FILE_NAME_DOWNLOADS), nsIBrowserProfileMigrator::OTHERDATA, PR_TRUE }, { ToNewUnicode(FILE_NAME_MIMETYPES), nsIBrowserProfileMigrator::OTHERDATA, PR_TRUE }, { ToNewUnicode(FILE_NAME_USERCHROME), nsIBrowserProfileMigrator::OTHERDATA, PR_TRUE }, { ToNewUnicode(FILE_NAME_USERCONTENT), nsIBrowserProfileMigrator::OTHERDATA, PR_TRUE }, { ToNewUnicode(FILE_NAME_FORMHISTORY), nsIBrowserProfileMigrator::OTHERDATA, PR_TRUE } }; // Frees file name strings allocated above. GetMigrateDataFromArray(data, sizeof(data)/sizeof(MigrationData), aReplace, mSourceProfile, aResult); // Now locate passwords nsXPIDLCString signonsFileName; GetSignonFileName(aReplace, getter_Copies(signonsFileName)); if (!signonsFileName.IsEmpty()) { nsAutoString fileName; fileName.AssignWithConversion(signonsFileName); nsCOMPtr sourcePasswordsFile; mSourceProfile->Clone(getter_AddRefs(sourcePasswordsFile)); sourcePasswordsFile->Append(fileName); PRBool exists; sourcePasswordsFile->Exists(&exists); if (exists) {//hold off on this until we actually support password importing *aResult |= nsIBrowserProfileMigrator::PASSWORDS; } } return NS_OK; } NS_IMETHODIMP nsFirefoxProfileMigrator::GetSourceExists(PRBool* aResult) { nsCOMPtr profiles; GetSourceProfiles(getter_AddRefs(profiles)); if (profiles) { PRUint32 count; profiles->Count(&count); *aResult = count > 0; } else *aResult = PR_FALSE; return NS_OK; } NS_IMETHODIMP nsFirefoxProfileMigrator::GetSourceHasMultipleProfiles(PRBool* aResult) { nsCOMPtr profiles; GetSourceProfiles(getter_AddRefs(profiles)); if (profiles) { PRUint32 count; profiles->Count(&count); *aResult = count > 1; } else *aResult = PR_FALSE; return NS_OK; } NS_IMETHODIMP nsFirefoxProfileMigrator::GetSourceProfiles(nsISupportsArray** aResult) { if (!mProfileNames && !mProfileLocations) { nsresult rv = NS_NewISupportsArray(getter_AddRefs(mProfileNames)); if (NS_FAILED(rv)) return rv; rv = NS_NewISupportsArray(getter_AddRefs(mProfileLocations)); if (NS_FAILED(rv)) return rv; // Fills mProfileNames and mProfileLocations FillProfileDataFromFirefoxRegistry(); } NS_IF_ADDREF(*aResult = mProfileNames); return NS_OK; } nsresult nsFirefoxProfileMigrator::HackGetStringPref(char *aStr, nsACString& aResult) { nsCOMPtr sourceFile; mSourceProfile->Clone(getter_AddRefs(sourceFile)); sourceFile->Append(FILE_NAME_PREFS); PRBool exists = PR_FALSE; sourceFile->Exists(&exists); if (!exists) return NS_OK; nsCOMPtr fileStream; NS_NewLocalFileInputStream(getter_AddRefs(fileStream), sourceFile); if (!fileStream) return NS_ERROR_OUT_OF_MEMORY; nsCOMPtr lineStream = do_QueryInterface(fileStream); nsCAutoString buffer; nsCAutoString homePage; nsAutoString tmp; PRBool moreData = PR_FALSE; //NS_NAMED_LITERAL_STRING(valuePrefix, "browser.startup.homepage\", \""); nsAutoString valuePrefix; buffer.Append(aStr); AppendASCIItoUTF16(buffer, valuePrefix); // NS_LITERAL_STRING(valuePrefix, aStr); do { nsresult rv = lineStream->ReadLine(buffer, &moreData); if (NS_FAILED(rv)) return rv; tmp.Truncate(); AppendASCIItoUTF16(buffer, tmp); nsAString::const_iterator start, end; tmp.BeginReading(start); tmp.EndReading(end); if (FindInReadable(valuePrefix, start, end)) { start = end; tmp.EndReading(end); while (start != end) { if(*start=='"') break; homePage.Append(*start); ++start; } break; } } while (moreData); aResult.Assign(homePage); return NS_OK; } NS_IMETHODIMP nsFirefoxProfileMigrator::GetSourceHomePageURL(nsACString& aResult) { //return HackGetStringPref("browser.startup.homepage\", \"", aResult); nsCOMPtr psvc(do_GetService(NS_PREFSERVICE_CONTRACTID)); psvc->ResetPrefs(); nsCOMPtr seamonkeyPrefsFile; mSourceProfile->Clone(getter_AddRefs(seamonkeyPrefsFile)); seamonkeyPrefsFile->Append(FILE_NAME_PREFS); psvc->ReadUserPrefs(seamonkeyPrefsFile); nsXPIDLCString homePage; nsCOMPtr branch(do_QueryInterface(psvc)); nsresult rv = branch->GetCharPref("browser.startup.homepage", getter_Copies(homePage)); if(!NS_SUCCEEDED(rv)) return rv; aResult.Assign(homePage); return NS_OK; } /////////////////////////////////////////////////////////////////////////////// // nsFirefoxProfileMigrator nsresult nsFirefoxProfileMigrator::GetSourceProfile(const PRUnichar* aProfile) { PRUint32 count; mProfileNames->Count(&count); for (PRUint32 i = 0; i < count; ++i) { nsCOMPtr str(do_QueryElementAt(mProfileNames, i)); nsXPIDLString profileName; str->GetData(profileName); if (profileName.Equals(aProfile)) { mSourceProfile = do_QueryElementAt(mProfileLocations, i); break; } nsAString::const_iterator start, end; } return NS_OK; } nsresult nsFirefoxProfileMigrator::AppendNativeProfilePath(nsILocalFile *aFile) { #ifdef XP_WIN aFile->Append(NS_LITERAL_STRING("Mozilla")); aFile->Append(NS_LITERAL_STRING("Firefox")); #elif defined(XP_MACOSX) aFile->Append(NS_LITERAL_STRING("Application Support")); aFile->Append(NS_LITERAL_STRING("Firefox")); #elif defined(XP_UNIX) aFile->Append(NS_LITERAL_STRING(".mozilla")); aFile->Append(NS_LITERAL_STRING("firefox")); #endif return NS_OK; } nsresult nsFirefoxProfileMigrator::FillProfileDataFromFirefoxRegistry() { nsCOMPtr fileLocator(do_GetService("@mozilla.org/file/directory_service;1")); nsCOMPtr firefoxRegistry; nsCOMPtr profileRoot; char *APPDATADIR; #ifdef XP_WIN APPDATADIR = NS_WIN_APPDATA_DIR; #elif defined(XP_MACOSX) APPDATADIR = NS_MAC_USER_LIB_DIR; #elif defined(XP_UNIX) APPDATADIR = NS_UNIX_HOME_DIR; #endif fileLocator->Get(APPDATADIR, NS_GET_IID(nsILocalFile), getter_AddRefs(firefoxRegistry)); fileLocator->Get(APPDATADIR, NS_GET_IID(nsILocalFile), getter_AddRefs(profileRoot)); AppendNativeProfilePath(profileRoot); AppendNativeProfilePath(firefoxRegistry); nsCAutoString test; firefoxRegistry->AppendNative(NS_LITERAL_CSTRING("profiles.ini")); //firefoxRegistry->GetNativePath(test); //printf("YOO %s\n", test.get()); nsINIParser parser; nsresult rv = parser.Init(firefoxRegistry); if (NS_FAILED(rv)) return rv; unsigned int c = 0; nsCAutoString buffer; for (c = 0; PR_TRUE; ++c) { nsCAutoString profileID("Profile"); profileID.AppendInt(c); rv = parser.GetString(profileID.get(), "IsRelative", buffer); if (NS_FAILED(rv)) break; PRBool isRelative = buffer.EqualsLiteral("1"); nsCAutoString filePath; nsCAutoString profileName; rv = parser.GetString(profileID.get(), "Path", filePath); if (NS_FAILED(rv)) { NS_ERROR("Malformed profiles.ini: Path= not found"); continue; } rv = parser.GetString(profileID.get(), "Name", profileName); if (NS_FAILED(rv)) { NS_ERROR("Malformed profiles.ini: Name= not found"); continue; } nsCOMPtr firefoxProfileDir; rv = NS_NewNativeLocalFile(EmptyCString(), PR_TRUE, getter_AddRefs(firefoxProfileDir)); NS_ENSURE_SUCCESS(rv, rv); if (isRelative) { rv = firefoxProfileDir->SetRelativeDescriptor(profileRoot, filePath); } else { rv = firefoxProfileDir->SetPersistentDescriptor(filePath); } if (NS_FAILED(rv)) continue; PRBool exists; firefoxProfileDir->Exists(&exists); nsAutoString test; rv = firefoxProfileDir->GetPath(test); if (exists) { nsCOMPtr profileNameString(do_CreateInstance("@mozilla.org/supports-string;1")); profileNameString->SetData(NS_ConvertUTF8toUTF16(profileName)); mProfileNames->AppendElement(profileNameString); mProfileLocations->AppendElement(firefoxProfileDir); } /* nsCOMPtr rootDir; rv = NS_NewNativeLocalFile(EmptyCString(), PR_TRUE, getter_AddRefs(rootDir)); NS_ENSURE_SUCCESS(rv, rv); if (isRelative) { rv = rootDir->SetRelativeDescriptor(mAppData, filePath); } else { rv = rootDir->SetPersistentDescriptor(filePath); } if (NS_FAILED(rv)) continue; nsCOMPtr localDir; if (isRelative) { rv = NS_NewNativeLocalFile(EmptyCString(), PR_TRUE, getter_AddRefs(localDir)); NS_ENSURE_SUCCESS(rv, rv); rv = localDir->SetRelativeDescriptor(mTempData, filePath); } else { localDir = rootDir; } nsCOMPtr dir; currentProfile = new nsToolkitProfile(buffer, rootDir, localDir, currentProfile); NS_ENSURE_TRUE(currentProfile, NS_ERROR_OUT_OF_MEMORY); rv = parser.GetString(profileID.get(), "Default", buffer); if (NS_SUCCEEDED(rv) && buffer.EqualsLiteral("1")) mChosen = currentProfile; */ } return NS_OK; /* // Find the Firefox Registry nsCOMPtr fileLocator(do_GetService("@mozilla.org/file/directory_service;1")); nsCOMPtr firefoxRegistry; #ifdef XP_WIN fileLocator->Get(NS_WIN_APPDATA_DIR, NS_GET_IID(nsILocalFile), getter_AddRefs(firefoxRegistry)); firefoxRegistry->Append(NS_LITERAL_STRING("Firefox")); firefoxRegistry->Append(NS_LITERAL_STRING("registry.dat")); #elif defined(XP_MACOSX) fileLocator->Get(NS_MAC_USER_LIB_DIR, NS_GET_IID(nsILocalFile), getter_AddRefs(firefoxRegistry)); firefoxRegistry->Append(NS_LITERAL_STRING("Firefox")); firefoxRegistry->Append(NS_LITERAL_STRING("Application Registry")); #elif defined(XP_UNIX) fileLocator->Get(NS_UNIX_HOME_DIR, NS_GET_IID(nsILocalFile), getter_AddRefs(firefoxRegistry)); firefoxRegistry->Append(NS_LITERAL_STRING(".firefox")); firefoxRegistry->Append(NS_LITERAL_STRING("appreg")); #elif defined(XP_OS2) fileLocator->Get(NS_OS2_HOME_DIR, NS_GET_IID(nsILocalFile), getter_AddRefs(firefoxRegistry)); firefoxRegistry->Append(NS_LITERAL_STRING("Firefox")); firefoxRegistry->Append(NS_LITERAL_STRING("registry.dat")); #endif return GetProfileDataFromRegistry(firefoxRegistry, mProfileNames, mProfileLocations); */ } nsresult nsFirefoxProfileMigrator::CopyPreferences(PRBool aReplace) { nsresult rv = NS_OK; if (!aReplace) return rv; // Prefs files //rv |= CopyFile(FILE_NAME_PREFS, FILE_NAME_PREFS); //rv |= CopyFile(FILE_NAME_USER_PREFS, FILE_NAME_USER_PREFS); // Security Stuff rv |= CopyFile(FILE_NAME_CERT8DB, FILE_NAME_CERT8DB); rv |= CopyFile(FILE_NAME_KEY3DB, FILE_NAME_KEY3DB); rv |= CopyFile(FILE_NAME_SECMODDB, FILE_NAME_SECMODDB); // User MIME Type overrides rv |= CopyFile(FILE_NAME_MIMETYPES, FILE_NAME_MIMETYPES); // rv |= CopyUserStyleSheets(); return rv; } nsresult nsFirefoxProfileMigrator::CopyUserStyleSheets() { nsresult rv = NS_OK; nsCOMPtr sourceUserContent; mSourceProfile->Clone(getter_AddRefs(sourceUserContent)); sourceUserContent->Append(DIR_NAME_CHROME); sourceUserContent->Append(FILE_NAME_USERCONTENT); PRBool exists = PR_FALSE; sourceUserContent->Exists(&exists); if (exists) { nsCOMPtr targetUserContent; mTargetProfile->Clone(getter_AddRefs(targetUserContent)); targetUserContent->Append(DIR_NAME_CHROME); nsCOMPtr targetChromeDir; targetUserContent->Clone(getter_AddRefs(targetChromeDir)); targetUserContent->Append(FILE_NAME_USERCONTENT); targetUserContent->Exists(&exists); if (exists) targetUserContent->Remove(PR_FALSE); rv |= sourceUserContent->CopyTo(targetChromeDir, FILE_NAME_USERCONTENT); } nsCOMPtr sourceUserChrome; mSourceProfile->Clone(getter_AddRefs(sourceUserChrome)); sourceUserChrome->Append(DIR_NAME_CHROME); sourceUserChrome->Append(FILE_NAME_USERCHROME); sourceUserChrome->Exists(&exists); if (exists) { nsCOMPtr targetUserChrome; mTargetProfile->Clone(getter_AddRefs(targetUserChrome)); targetUserChrome->Append(DIR_NAME_CHROME); nsCOMPtr targetChromeDir; targetUserChrome->Clone(getter_AddRefs(targetChromeDir)); targetUserChrome->Append(FILE_NAME_USERCHROME); targetUserChrome->Exists(&exists); if (exists) targetUserChrome->Remove(PR_FALSE); rv |= sourceUserChrome->CopyTo(targetChromeDir, FILE_NAME_USERCHROME); } return rv; } nsresult nsFirefoxProfileMigrator::CopyCookies(PRBool aReplace) { return aReplace ? CopyFile(FILE_NAME_COOKIES, FILE_NAME_COOKIES) : NS_OK; } nsresult nsFirefoxProfileMigrator::CopyHistory(PRBool aReplace) { return aReplace ? CopyFile(FILE_NAME_HISTORY, FILE_NAME_HISTORY) : NS_OK; } nsresult nsFirefoxProfileMigrator::CopyPasswords(PRBool aReplace) { CopyPreferences(aReplace);// this only copies security dbs nsresult rv; nsXPIDLCString signonsFileName; if (!aReplace) return NS_OK; // Find out what the signons file was called, this is stored in a pref // in Seamonkey. nsCOMPtr psvc(do_GetService(NS_PREFSERVICE_CONTRACTID)); psvc->ResetPrefs(); nsCOMPtr seamonkeyPrefsFile; mSourceProfile->Clone(getter_AddRefs(seamonkeyPrefsFile)); seamonkeyPrefsFile->Append(FILE_NAME_PREFS); psvc->ReadUserPrefs(seamonkeyPrefsFile); nsCOMPtr branch(do_QueryInterface(psvc)); rv = branch->GetCharPref("signon.SignonFileName", getter_Copies(signonsFileName)); if (signonsFileName.IsEmpty()) return NS_ERROR_FILE_NOT_FOUND; nsCOMPtr targetPrefFile; mTargetProfile->Clone(getter_AddRefs(targetPrefFile)); targetPrefFile->Append(FILE_NAME_PREFS); psvc->ReadUserPrefs(targetPrefFile); nsAutoString fileName; fileName.AssignWithConversion(signonsFileName); return aReplace ? CopyFile(fileName, fileName) : NS_OK; } nsresult nsFirefoxProfileMigrator::CopyBookmarks(PRBool aReplace) { if (aReplace) return CopyFile(FILE_NAME_BOOKMARKS, FILE_NAME_BOOKMARKS); return ImportNetscapeBookmarks(FILE_NAME_BOOKMARKS, NS_LITERAL_STRING("sourceNameFirefox").get()); } nsresult nsFirefoxProfileMigrator::CopyOtherData(PRBool aReplace) { if (!aReplace) return NS_OK; nsresult rv = NS_OK; rv |= CopyFile(FILE_NAME_DOWNLOADS, FILE_NAME_DOWNLOADS); // rv |= CopyFile(FILE_NAME_SEARCH, FILE_NAME_SEARCH); bad on osx // rv |= CopyFile(FILE_NAME_LOCALSTORE, FILE_NAME_LOCALSTORE);// avoid the // buttonset problem ;) rv |= CopyFile(FILE_NAME_FORMHISTORY, FILE_NAME_FORMHISTORY); return rv; }