Chris Pollett >
Students > [Bio] [Del1-PDF] [Del2-PDF] [Del4] |
Implementation of the Profile Loading FeatureCode: Here is a ZIP file of source code with installation instructions. Description: The first major feature of the USB Key Profile Manager is to automatically detect and load profiles from a removable disk such as a USB drive. The current version of Mozilla allows profiles to be saved to both local or removable disks. However, Mozilla does not recognize these profiles unless they are registered. Registration can only be done when a profile is created. meaning that one cannot easily take a profile created on one computer and use it on another. My profile loading feature solves this problem and helps to achieve location independence without extra copying and pasting work from the user. Example:Before the USB Profiles are loaded:
Example:After the USB Profiles are loaded:
The USB profile manager is implemented as a XPCOM component for Mozilla. When the browser is started up, the component will register itself with Mozilla and start running. It first checks how many usb storage media are mounted to the system, then loops through each mounted disk searching for profiles. The identification of a profile for now is a simplified process, i.e., all directories of which the name ends with .slt are considered valid Mozilla profiles. Once a profile is found, it is added directly to Mozilla's profile registry. If no USB profile is found, Mozilla's profile manager will act as usual. If only one USB profile is located, the component will tell Mozilla to suppress the profile manager dialog at startup and use the USB profile by default. If multiple are located, the profile manager dialog will be shown with all the profiles available, both on the local drive or the USB drive. The example shows the third situation. //#define MOZILLA_STRICT_API #include "nsIGenericFactory.h" #include "nsCOMPtr.h" #include "nsXPCOM.h" #include "nsIServiceManager.h" #include "nsICategoryManager.h" #include "nsIComponentManager.h" #include "nsIObserver.h" #include "nsMemory.h" #include "nsIUSBProfileManager.h" #include "nsXPCOMCID.h" #include "nsISupportsPrimitives.h" #include "nsIIOService.h" #include "nsString.h" #include "nsIRegistry.h" #include "nsXPIDLString.h" #include "nsIFile.h" #include "nsILocalFile.h" #include "nsAppDirectoryServiceDefs.h" #include "nsDirectoryServiceUtils.h" #include "nsDirectoryServiceDefs.h" #include "nsIEnumerator.h" #include "nsIWindowWatcher.h" #include "nsIDialogParamBlock.h" #include "nsIDOMWindowInternal.h" #include <stdlib.h> #include <stdio.h> #include <sys/types.h> # include <mntent.h> static NS_DEFINE_CID(kSupportsCStringCID, NS_SUPPORTS_CSTRING_CID); #define USBPROFILE_CID \ { 0x777f7150, 0x4a2b, 0x4301, \ { 0xad, 0x10, 0x5e, 0xab, 0x25, 0xb3, 0x22, 0xaa}} #define USBPROFILE_ContractID "@azhou/USBProfile" // Registry Keys #define kRegistryYesString (NS_LITERAL_STRING("yes")) #define kRegistryNoString (NS_LITERAL_STRING("no")) #define kRegistryProfileSubtreeString (NS_LITERAL_STRING("Profiles")) #define kRegistryCurrentProfileString (NS_LITERAL_STRING("CurrentProfile")) #define kRegistryNCServiceDenialString (NS_LITERAL_STRING("NCServiceDenial")) #define kRegistryNCProfileNameString (NS_LITERAL_STRING("NCProfileName")) #define kRegistryNCUserEmailString (NS_LITERAL_STRING("NCEmailAddress")) #define kRegistryNCHavePREGInfoString (NS_LITERAL_STRING("NCHavePregInfo")) #define kRegistryHavePREGInfoString (NS_LITERAL_STRING("HavePregInfo")) #define kRegistryMigratedString (NS_LITERAL_STRING("migrated")) #define kRegistryDirectoryString (NS_LITERAL_STRING("directory")) #define kRegistryNeedMigrationString (NS_LITERAL_STRING("NeedMigration")) #define kRegistryMozRegDataMovedString (NS_LITERAL_STRING("OldRegDataMoved")) #define kRegistryCreationTimeString (NS_LITERAL_CSTRING("CreationTime")) #define kRegistryLastModTimeString (NS_LITERAL_CSTRING("LastModTime")) #define kRegistryMigratedFromString (NS_LITERAL_CSTRING("MigFromDir")) #define kRegistryVersionString (NS_LITERAL_STRING("Version")) #define kRegistryVersion_1_0 (NS_LITERAL_STRING("1.0")) #define kRegistryCurrentVersion (NS_LITERAL_STRING("1.0")) #define kRegistryStartWithLastString (NS_LITERAL_CSTRING("AutoStartWithLast")) #define PROFILE_MANAGER_URL "chrome://communicator/content/profile/profileSelection.xul?manage=true" const char* kDefaultOpenWindowParams = "centerscreen,chrome,modal,titlebar"; /* A mount table entry. */ struct mount_entry { char *me_devname; /* Device node pathname, including "/dev/". */ char *me_mountdir; /* Mount point directory pathname. */ char *me_type; /* "nfs", "4.2", etc. */ dev_t me_dev; /* Device number of me_mountdir. */ unsigned int me_dummy : 1; /* Nonzero for dummy filesystems. */ unsigned int me_remote : 1; /* Nonzero for remote fileystems. */ struct mount_entry *me_next; }; #ifndef ME_DUMMY # define ME_DUMMY(fs_name, fs_type) \ (!strcmp (fs_type, "auto") \ || !strcmp (fs_type, "autofs") \ /* for Irix 6.5 */ \ || !strcmp (fs_type, "ignore")) #endif #ifndef ME_REMOTE # define ME_REMOTE(fs_name, fs_type) (strchr (fs_name, ':') != 0) #endif ////////////////////// USBProfileManager //////////////////////////// class nsUSBProfileManager: public nsIObserver, public nsIUSBProfileManager { private: PRBool mLocked; public: nsUSBProfileManager(); virtual ~nsUSBProfileManager(); NS_DECL_ISUPPORTS // The Observe function serves as the main function. That's when the component is loaded // to Mozilla and started. NS_DECL_NSIOBSERVER NS_DECL_NSIUSBPROFILEMANAGER private: // helper methods NS_IMETHODIMP SearchForProfile(nsCOMPtr<nsIFile> baseDir); PRBool IsProfile(nsCOMPtr<nsIFile> dirEntry); NS_IMETHODIMP LoadProfile(const PRUnichar *profileName, const PRUnichar *profilePath); // record number of profiles int numOfUSBProfiles; // Mozilla's special registry file nsCOMPtr<nsIFile> mNewRegFile; // The profile to be used, needed when only one USB profile is located nsString mUSBProfileName; // the following fields are for resetting to the last state nsString prevProfileName; // record the previous startWithLastUsedProfile flag PRBool prevStartMode; }; nsUSBProfileManager::nsUSBProfileManager() { mLocked = PR_TRUE; numOfUSBProfiles = 0; NS_GetSpecialDirectory(NS_APP_APPLICATION_REGISTRY_FILE, getter_AddRefs(mNewRegFile)); mUSBProfileName = NS_LITERAL_STRING(""); NS_INIT_ISUPPORTS(); } nsUSBProfileManager::~nsUSBProfileManager() { } /** param dirEntry: A directory. return PR_TRUE if is a profile, PR_FALSE otherwise. More sophiscated validation methods can be implemented later */ PRBool nsUSBProfileManager::IsProfile(nsCOMPtr<nsIFile> dirEntry) { nsCAutoString newPathName, ext; nsresult rv = dirEntry->GetNativeLeafName(newPathName); if(NS_SUCCEEDED(rv)) { newPathName.Right(ext, 4); // If the directory name ends with ".slt", return true if(ext.Equals(NS_LITERAL_CSTRING(".slt"))) return PR_TRUE; else return PR_FALSE; } return PR_FALSE; } /** A recursive function for finding all the profiles under the baseDir param baseDir: the USB drive to search. For instance: /mnt/usbstick */ NS_IMETHODIMP nsUSBProfileManager::SearchForProfile(nsCOMPtr<nsIFile> baseDir) { PRBool hasMore = PR_FALSE; PRBool isDir; nsCOMPtr<nsISimpleEnumerator> dirIterator; nsresult rv = baseDir->GetDirectoryEntries(getter_AddRefs(dirIterator)); if (NS_FAILED(rv)) return rv; rv = dirIterator->HasMoreElements(&hasMore); if (NS_FAILED(rv)) return rv; nsCOMPtr<nsIFile> dirEntry, parentDir; nsAutoString profileName, profilePath; // loop through all the subfolders and files immediately under baseDir while (hasMore) { //printf("more..\n"); rv = dirIterator->GetNext((nsISupports**)getter_AddRefs(dirEntry)); if (NS_SUCCEEDED(rv)) { //only consider directories rv = dirEntry->IsDirectory(&isDir); if (NS_SUCCEEDED(rv)) { if (isDir) { //If is a profile folder, load it if(IsProfile(dirEntry)) { //The name of the parent folder will be the name of the profile rv = dirEntry->GetParent(getter_AddRefs(parentDir)); if (NS_SUCCEEDED(rv)) { //Increment the number of found USB profiles numOfUSBProfiles++; rv = parentDir->GetLeafName(profileName); rv = dirEntry->GetPath(profilePath); // load the profile to the registry rv = LoadProfile(profileName.get(), profilePath.get()); // record the profileName for auto launching if(NS_SUCCEEDED(rv) && mUSBProfileName.Equals(NS_LITERAL_STRING(""))) mUSBProfileName = profileName; } } //Recursively search the subdirectory for profiles else SearchForProfile(dirEntry); } } } rv = dirIterator->HasMoreElements(&hasMore); if (NS_FAILED(rv)) return rv; } return rv; } /** Set the directory value and add the entry to the registry tree. param profileName: The name of the profile, which is actually the name of the parent folder of the profile folder xxx.slt param profilePath: the full path name of the profile folder */ NS_IMETHODIMP nsUSBProfileManager::LoadProfile(const PRUnichar *profileName, const PRUnichar *profilePath) { nsresult rv; nsCOMPtr<nsIRegistry> registry(do_CreateInstance(NS_REGISTRY_CONTRACTID, &rv)); if (NS_FAILED(rv)) return rv; //Open the registry file rv = registry->Open(mNewRegFile); if (NS_FAILED(rv)) return rv; nsRegistryKey profilesTreeKey; // Get the major subtree rv = registry->GetKey(nsIRegistry::Common, kRegistryProfileSubtreeString.get(), &profilesTreeKey); if (NS_FAILED(rv)) { rv = registry->AddKey(nsIRegistry::Common, kRegistryProfileSubtreeString.get(), &profilesTreeKey); if (NS_FAILED(rv)) return rv; } // add the new profile to the registry nsRegistryKey profKey; rv = registry->AddKey(profilesTreeKey, profileName, &profKey); if (NS_FAILED(rv)) return rv; rv = registry->SetString(profKey, kRegistryMigratedString.get(), kRegistryYesString.get()); if (NS_FAILED(rv)) return rv; //Set the profile name registry->SetString(profKey, kRegistryNCProfileNameString.get(), profileName); // Externalize the location rv = registry->SetString(profKey, kRegistryDirectoryString.get(), profilePath); if (NS_FAILED(rv)) { NS_ASSERTION(PR_FALSE, "Could not update profile location"); return rv; } return NS_OK; } NS_IMPL_ISUPPORTS2(nsUSBProfileManager, nsIObserver, nsIUSBProfileManager); NS_GENERIC_FACTORY_CONSTRUCTOR(nsUSBProfileManager) ///////////////// inplement nsIObserver ///////////////// /** This function gets called when the component is registered. It serves as the main function of the USB Profile Manager. */ NS_IMETHODIMP nsUSBProfileManager::Observe(nsISupports *aSubject, const char *aTopic, const PRUnichar *aData) { nsresult rv; nsCOMPtr<nsIComponentManager> aCompMgr; NS_GetComponentManager(getter_AddRefs(aCompMgr)); if (!aCompMgr) return NS_ERROR_UNEXPECTED; //If the event is xpcom-startup, starts running. if(strcmp("xpcom-startup", aTopic) == 0) { printf("**********************************************************\n"); printf("**********************************************************\n"); printf("Observe xpcom-startup: Registered...\n"); printf("Topic: %s\n", aTopic); //Automatically detect mounted USB storage disk // Linked list of mounted filesystems. struct mount_entry *me; // Read filesystem list. struct mount_entry *mount_list; struct mount_entry **mtail = &mount_list; struct mntent *mnt; char *table = MOUNTED; FILE *fp; char *devopt; fp = setmntent (table, "r"); if (fp == NULL) return NS_FAILED; //Fetch all the information about a filesystem from the mounted table while ((mnt = getmntent (fp))) { me = (struct mount_entry *) malloc (sizeof (struct mount_entry)); me->me_devname = strdup (mnt->mnt_fsname); me->me_mountdir = strdup (mnt->mnt_dir); me->me_type = strdup (mnt->mnt_type); me->me_dummy = ME_DUMMY (me->me_devname, me->me_type); me->me_remote = ME_REMOTE (me->me_devname, me->me_type); devopt = strstr (mnt->mnt_opts, "dev="); if (devopt) { if (devopt[4] == '0' && (devopt[5] == 'x' || devopt[5] == 'X')) me->me_dev = atoi (devopt + 6); else me->me_dev = atoi (devopt + 4); } else me->me_dev = (dev_t) -1; // Magic; means not known yet. // Add to the linked list. *mtail = me; mtail = &me->me_next; } me = mount_list; if (endmntent (fp) == 0) { *mtail = NULL; while (mount_list) { me = mount_list->me_next; //free up the memory free (mount_list->me_devname); free (mount_list->me_mountdir); // FIXME: me_type is not always malloced. free (mount_list); mount_list = me; } me = NULL; } *mtail = NULL; // check if any /dev/sda devices, i.e. USB drives are mounted. char* usb_dev = "/dev/sda"; int len = strlen(usb_dev); for (; me; me = me->me_next) { if(strncmp(me->me_devname, usb_dev, len) == 0) { printf("%s %s\n", me->me_devname, me->me_mountdir); // look for the .slt folder which is the profile directory nsCOMPtr<nsIFile> baseDir; nsAutoString wideString; wideString.AssignWithConversion(me->me_mountdir); rv = NS_NewLocalFile(nsDependentString(wideString), PR_TRUE, (nsILocalFile **)((nsIFile **)getter_AddRefs(baseDir))); if (NS_FAILED(rv)) return rv; // search for profiles and load SearchForProfile(baseDir); } } printf("Usb Profiles: %d\n", numOfUSBProfiles); // get the profile registry nsCOMPtr<nsIRegistry> registry(do_CreateInstance(NS_REGISTRY_CONTRACTID, &rv)); if (NS_FAILED(rv)) return rv; rv = registry->Open(mNewRegFile); if (NS_FAILED(rv)) return rv; nsRegistryKey profilesTreeKey; // Get the major subtree rv = registry->GetKey(nsIRegistry::Common, kRegistryProfileSubtreeString.get(), &profilesTreeKey); if (NS_FAILED(rv)) { rv = registry->AddKey(nsIRegistry::Common, kRegistryProfileSubtreeString.get(), &profilesTreeKey); if (NS_FAILED(rv)) return rv; } //If only 1 USB profile is found, start it directly if(numOfUSBProfiles == 1) { //Get the current profile for reset nsXPIDLString tmpCurrentProfile; rv = registry->GetString(profilesTreeKey, kRegistryCurrentProfileString.get(), getter_Copies(tmpCurrentProfile)); if(tmpCurrentProfile) prevProfileName = NS_STATIC_CAST(const PRUnichar*, tmpCurrentProfile); // Set the current profile to be the USB profile rv = registry->SetString(profilesTreeKey, kRegistryCurrentProfileString.get(), mUSBProfileName.get()); if (NS_FAILED(rv)) return rv; // Get the StartWithLastProfile flag for reset PRInt32 tempLong; rv = registry->GetInt(profilesTreeKey, kRegistryStartWithLastString.get(), &tempLong); if (NS_SUCCEEDED(rv)) prevStartMode = tempLong; // Set the StartWithLastProfile flag to true rv = registry->SetInt(profilesTreeKey, kRegistryStartWithLastString.get(), PR_TRUE); if (NS_FAILED(rv)) return rv; } } return NS_OK; } //Autoregistration static NS_METHOD nsUSBProfileManagerRegistration( nsIComponentManager *aCompMgr, nsIFile *aPath, const char *registryLocation, const char *componentType, const nsModuleComponentInfo *info) { nsresult rv; nsCOMPtr<nsIServiceManager> servman = do_QueryInterface((nsISupports*)aCompMgr, &rv); if (NS_FAILED(rv)) return rv; nsCOMPtr<nsICategoryManager> catman; servman->GetServiceByContractID(NS_CATEGORYMANAGER_CONTRACTID, NS_GET_IID(nsICategoryManager), getter_AddRefs(catman)); if (NS_FAILED(rv)) return rv; char* previous = nsnull; rv = catman->AddCategoryEntry("profile-do-change", "USBProfileManager", USBPROFILE_ContractID, PR_TRUE, PR_TRUE, &previous); if (previous) nsMemory::Free(previous); return rv; } //Automatic unregistration static NS_METHOD nsUSBProfileManagerUnregistration(nsIComponentManager *aCompMgr, nsIFile *aPath, const char *registryLocation, const nsModuleComponentInfo *info) { nsresult rv; nsCOMPtr<nsIServiceManager> servman = do_QueryInterface((nsISupports*)aCompMgr, &rv); if (NS_FAILED(rv)) return rv; nsCOMPtr<nsICategoryManager> catman; servman->GetServiceByContractID(NS_CATEGORYMANAGER_CONTRACTID, NS_GET_IID(nsICategoryManager), getter_AddRefs(catman)); if (NS_FAILED(rv)) return rv; rv = catman->DeleteCategoryEntry("xpcom-startup", "USBProfileManager", PR_TRUE); return rv; } ///////////////// inplement nsIUSBProfileManager ///////////////// // Not implemented ////////////////// nsModuleComponentInfo ////////////////////// static const nsModuleComponentInfo components[] = { { "USBProfile", USBPROFILE_CID, USBPROFILE_ContractID, nsUSBProfileManagerConstructor, nsUSBProfileManagerRegistration, nsUSBProfileManagerUnregistration } }; NS_IMPL_NSGETMODULE(USBProfileModule, components) |