Chris Pollett > Students >
Yun

    ( Print View )

    [Bio]

    [Project Blog]

    [CS297Proposal]

    [Del1-PDF]

    [Del2-PDF]

    [SecuritySlides-PDF]

    [Del4]

    [CS297Report-PDF]

    [CS298Proposal]

    [CS298Final Source-ZIP]

    [CS297Presentation-PDF]

    [CS298Report-PDF]

                          

























Implementation of the Profile Loading Feature

Code: 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:

All the profiles are on the local drive.

Example:After the USB Profiles are loaded:

Profiles on the USB drive are also 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)