/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ /* ***** BEGIN LICENSE BLOCK ***** * Version: NPL 1.1/GPL 2.0/LGPL 2.1 * * The contents of this file are subject to the Netscape 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/NPL/ * * 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 Communicator client code. * * The Initial Developer of the Original Code is * Netscape Communications Corporation. * Portions created by the Initial Developer are Copyright (C) 1998 * the Initial Developer. All Rights Reserved. * * Contributor(s): * * * 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 NPL, 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 NPL, the GPL or the LGPL. * * ***** END LICENSE BLOCK ***** */ /* wallet.cpp */ #define AutoCapture //#define IgnoreFieldNames #include "wallet.h" #include "singsign.h" #include "nsNetUtil.h" #include "nsReadableUtils.h" #include "nsUnicharUtils.h" #include "nsILocalFile.h" #include "nsIServiceManager.h" #include "nsIDocument.h" #include "nsIDOMHTMLDocument.h" #include "nsIDOMHTMLCollection.h" #include "nsIDOMHTMLFormElement.h" #include "nsIDOMHTMLInputElement.h" #include "nsIDOMHTMLSelectElement.h" #include "nsIDOMHTMLOptionElement.h" #include "nsIURL.h" #include "nsIDOMWindowCollection.h" #include "nsIPrompt.h" #include "nsIWindowWatcher.h" #include "nsFileStream.h" #include "nsAppDirectoryServiceDefs.h" #include "nsIStringBundle.h" #include "nsIFileSpec.h" #include "prmem.h" #include "prprf.h" #include "nsIContent.h" #include "nsIObserverService.h" #include "nsIWalletService.h" #include "nsComObsolete.h" #include #ifdef DEBUG_morse #define morseAssert NS_ASSERTION #else #define morseAssert(x,y) 0 #endif #include "prlong.h" #include "prinrval.h" #include "prlog.h" // // To enable logging (see prlog.h for full details): // // set NSPR_LOG_MODULES=nsWallet:5 // set NSPR_LOG_FILE=nspr.log // PRLogModuleInfo* gWalletLog = nsnull; /********************************************************/ /* The following data and procedures are for preference */ /********************************************************/ static const char *pref_Caveat = "wallet.caveat"; #ifdef AutoCapture static const char *pref_captureForms = "wallet.captureForms"; static const char *pref_enabled = "wallet.enabled"; #else static const char *pref_WalletNotified = "wallet.Notified"; #endif /* AutoCapture */ static const char *pref_WalletSchemaValueFileName = "wallet.SchemaValueFileName"; #ifdef AutoCapture PRIVATE PRBool wallet_captureForms = PR_FALSE; #else PRIVATE PRBool wallet_Notified = PR_FALSE; #endif #ifdef AutoCapture PRIVATE void wallet_SetFormsCapturingPref(PRBool x) { /* do nothing if new value of pref is same as current value */ if (x == wallet_captureForms) { return; } /* change the pref */ wallet_captureForms = x; } MODULE_PRIVATE int PR_CALLBACK wallet_FormsCapturingPrefChanged(const char * newpref, void * data) { PRBool x; x = SI_GetBoolPref(pref_captureForms, PR_TRUE); wallet_SetFormsCapturingPref(x); return 0; /* this is PREF_NOERROR but we no longer include prefapi.h */ } static void wallet_RegisterCapturePrefCallbacks(void) { PRBool x; static PRBool first_time = PR_TRUE; if(first_time) { first_time = PR_FALSE; x = SI_GetBoolPref(pref_captureForms, PR_TRUE); wallet_SetFormsCapturingPref(x); SI_RegisterCallback(pref_captureForms, wallet_FormsCapturingPrefChanged, NULL); } } PRIVATE PRBool wallet_GetFormsCapturingPref(void) { wallet_RegisterCapturePrefCallbacks(); return wallet_captureForms; } PRIVATE PRBool wallet_GetEnabledPref(void) { /* This pref is not in the prefs panel. It's purpose is to remove wallet from all UI */ static PRBool first_time = PR_TRUE; static PRBool enabled = PR_TRUE; if (first_time) { first_time = PR_FALSE; PRBool x = SI_GetBoolPref(pref_enabled, PR_TRUE); enabled = x; } return enabled; } #else PRIVATE void wallet_SetWalletNotificationPref(PRBool x) { SI_SetBoolPref(pref_WalletNotified, x); wallet_Notified = x; } PRIVATE PRBool wallet_GetWalletNotificationPref(void) { static PRBool first_time = PR_TRUE; if (first_time) { PRBool x = SI_GetBoolPref(pref_WalletNotified, PR_FALSE); wallet_Notified = x; } return wallet_Notified; } #endif /* ! AutoCapture */ /***************************************************/ /* The following declarations define the data base */ /***************************************************/ #define WALLET_FREE(_ptr) { nsMemory::Free((void*)_ptr); (_ptr) = nsnull; } #define WALLET_FREEIF(_ptr) if (_ptr) WALLET_FREE(_ptr) enum PlacementType {DUP_IGNORE, DUP_OVERWRITE, DUP_BEFORE, DUP_AFTER, AT_END, BY_LENGTH}; #define LIST_COUNT(list) ((list) ? (list)->Count() : 0) MOZ_DECL_CTOR_COUNTER(wallet_Sublist) class wallet_Sublist { public: wallet_Sublist() { MOZ_COUNT_CTOR(wallet_Sublist); } ~wallet_Sublist() { WALLET_FREEIF(item); MOZ_COUNT_DTOR(wallet_Sublist); } const char* item; }; /* * The data structure below consists of mapping tables that map one item into another. * The actual interpretation of the items depend on which table we are in. For * example, if in the field-to-schema table, item1 is a field name and item2 is a * schema name. Whereas in the schema-to-value table, item1 is a schema name and * item2 is a value. Therefore this generic data structure refers to them simply as * item1 and item2. */ MOZ_DECL_CTOR_COUNTER(wallet_MapElement) class wallet_MapElement { public: wallet_MapElement() : itemList(nsnull) { MOZ_COUNT_CTOR(wallet_MapElement); } ~wallet_MapElement() { WALLET_FREEIF(item1); WALLET_FREEIF(item2); if (itemList) { PRInt32 count = LIST_COUNT(itemList); wallet_Sublist * sublistPtr; for (PRInt32 i=0; iElementAt(i)); delete sublistPtr; } delete itemList; } MOZ_COUNT_DTOR(wallet_MapElement); } const char* item1; const char* item2; nsVoidArray * itemList; }; /* Purpose of this class is to speed up startup time on the mac * * These strings are used over and over again inside an inner loop. Rather * then allocating them and then deallocating them, they will be allocated * only once and left sitting on the heap */ MOZ_DECL_CTOR_COUNTER(wallet_HelpMac) class wallet_HelpMac { public: wallet_HelpMac() { item1 = nsnull; item2 = nsnull; item3 = nsnull; MOZ_COUNT_CTOR(wallet_HelpMac); } ~wallet_HelpMac() { WALLET_FREEIF(item1); WALLET_FREEIF(item2); WALLET_FREEIF(item3); MOZ_COUNT_DTOR(wallet_HelpMac); } const char* item1; const char* item2; const char* item3; }; wallet_HelpMac * helpMac; PRIVATE nsVoidArray * wallet_FieldToSchema_list = 0; PRIVATE nsVoidArray * wallet_VcardToSchema_list = 0; PRIVATE nsVoidArray * wallet_SchemaToValue_list = 0; PRIVATE nsVoidArray * wallet_SchemaConcat_list = 0; PRIVATE nsVoidArray * wallet_SchemaStrings_list = 0; PRIVATE nsVoidArray * wallet_PositionalSchema_list = 0; PRIVATE nsVoidArray * wallet_StateSchema_list = 0; PRIVATE nsVoidArray * wallet_URL_list = 0; #ifdef AutoCapture PRIVATE nsVoidArray * wallet_DistinguishedSchema_list = 0; #endif #define NO_CAPTURE(x) x[0] #define NO_PREVIEW(x) x[1] MOZ_DECL_CTOR_COUNTER(wallet_PrefillElement) class wallet_PrefillElement { public: wallet_PrefillElement() : inputElement(nsnull), selectElement(nsnull) { schema = nsnull; MOZ_COUNT_CTOR(wallet_PrefillElement); } ~wallet_PrefillElement() { WALLET_FREEIF(schema); NS_IF_RELEASE(inputElement); NS_IF_RELEASE(selectElement); MOZ_COUNT_DTOR(wallet_PrefillElement); } nsIDOMHTMLInputElement* inputElement; nsIDOMHTMLSelectElement* selectElement; char* schema; nsString value; PRInt32 selectIndex; PRUint32 count; }; nsIURI * wallet_lastUrl = NULL; /***********************************************************/ /* The following routines are for diagnostic purposes only */ /***********************************************************/ #ifdef DEBUG_morse static void wallet_Pause(){ fprintf(stdout,"%cpress y to continue\n", '\007'); char c; for (;;) { c = getchar(); if (tolower(c) == 'y') { fprintf(stdout,"OK\n"); break; } } while (c != '\n') { c = getchar(); } } static void wallet_DumpAutoString(const nsString& as){ fprintf(stdout, "%s\n", NS_LossyConvertUCS2toASCII(as).get()); } static void wallet_Dump(nsVoidArray * list) { wallet_MapElement * mapElementPtr; PRInt32 count = LIST_COUNT(list); for (PRInt32 i=0; iElementAt(i)); fprintf(stdout, "%s %s \n", (mapElementPtr->item1), (mapElementPtr->item2)); wallet_Sublist * sublistPtr; PRInt32 count2 = LIST_COUNT(mapElementPtr->itemList); for (PRInt32 i2=0; i2itemList->ElementAt(i2)); fprintf(stdout, " %s \n", (sublistPtr->item)); } } wallet_Pause(); } /******************************************************************/ /* The following diagnostic routines are for timing purposes only */ /******************************************************************/ const PRInt32 timing_max = 1000; PRInt64 timings [timing_max]; char timingID [timing_max]; PRInt32 timing_index = 0; PRInt64 stopwatch = LL_Zero(); PRInt64 stopwatchBase; PRBool stopwatchRunning = PR_FALSE; static void wallet_ClearTiming() { timing_index = 0; LL_I2L(timings[timing_index++], PR_IntervalNow()); } static void wallet_DumpTiming() { PRInt32 i, r4; PRInt64 r1, r2, r3; for (i=1; i pStringService = do_GetService(NS_STRINGBUNDLE_CONTRACTID, &ret); if (NS_FAILED(ret)) { #ifdef DEBUG printf("cannot get string service\n"); #endif return ToNewUnicode(v); } nsCOMPtr bundle; ret = pStringService->CreateBundle(PROPERTIES_URL, getter_AddRefs(bundle)); if (NS_FAILED(ret)) { #ifdef DEBUG printf("cannot create instance\n"); #endif return ToNewUnicode(v); } /* localize the given string */ nsAutoString strtmp; strtmp.AssignWithConversion(genericString); const PRUnichar *ptrtmp = strtmp.get(); PRUnichar *ptrv = nsnull; ret = bundle->GetStringFromName(ptrtmp, &ptrv); if (NS_FAILED(ret)) { #ifdef DEBUG printf("cannot get string from name\n"); #endif return ToNewUnicode(v); } v = ptrv; nsCRT::free(ptrv); /* convert # to newlines */ PRUint32 i; for (i=0; i dialog; window->GetPrompter(getter_AddRefs(dialog)); if (!dialog) { return retval; } const nsAutoString message( szMessage ); retval = PR_FALSE; /* in case user exits dialog by clicking X */ res = dialog->Confirm(nsnull, message.get(), &retval); return retval; } PUBLIC PRBool Wallet_ConfirmYN(PRUnichar * szMessage, nsIDOMWindowInternal* window) { nsresult res; nsCOMPtr dialog; window->GetPrompter(getter_AddRefs(dialog)); if (!dialog) { return PR_FALSE; } PRInt32 buttonPressed = 1; /* in case user exits dialog by clickin X */ PRUnichar * confirm_string = Wallet_Localize("Confirm"); res = dialog->ConfirmEx(confirm_string, szMessage, (nsIPrompt::BUTTON_TITLE_YES * nsIPrompt::BUTTON_POS_0) + (nsIPrompt::BUTTON_TITLE_NO * nsIPrompt::BUTTON_POS_1), nsnull, nsnull, nsnull, nsnull, nsnull, &buttonPressed); WALLET_FREE(confirm_string); return (buttonPressed == 0); } PUBLIC PRInt32 Wallet_3ButtonConfirm(PRUnichar * szMessage, nsIDOMWindowInternal* window) { nsresult res; nsCOMPtr dialog; window->GetPrompter(getter_AddRefs(dialog)); if (!dialog) { return 0; /* default value is NO */ } PRInt32 buttonPressed = 1; /* default of NO if user exits dialog by clickin X */ PRUnichar * never_string = Wallet_Localize("Never"); PRUnichar * confirm_string = Wallet_Localize("Confirm"); res = dialog->ConfirmEx(confirm_string, szMessage, (nsIPrompt::BUTTON_TITLE_YES * nsIPrompt::BUTTON_POS_0) + (nsIPrompt::BUTTON_TITLE_NO * nsIPrompt::BUTTON_POS_1) + (nsIPrompt::BUTTON_TITLE_IS_STRING * nsIPrompt::BUTTON_POS_2), nsnull, nsnull, never_string, nsnull, nsnull, &buttonPressed); WALLET_FREE(never_string); WALLET_FREE(confirm_string); return buttonPressed; } PRIVATE void wallet_Alert(PRUnichar * szMessage, nsIDOMWindowInternal* window) { nsresult res; nsCOMPtr dialog; window->GetPrompter(getter_AddRefs(dialog)); if (!dialog) { return; // XXX should return the error } const nsAutoString message( szMessage ); PRUnichar * title = Wallet_Localize("CaveatTitle"); res = dialog->Alert(title, message.get()); WALLET_FREE(title); return; // XXX should return the error } PRIVATE void wallet_Alert(PRUnichar * szMessage, nsIPrompt* dialog) { nsresult res; const nsAutoString message( szMessage ); PRUnichar * title = Wallet_Localize("CaveatTitle"); res = dialog->Alert(title, message.get()); WALLET_FREE(title); return; // XXX should return the error } PUBLIC PRBool Wallet_CheckConfirmYN (PRUnichar * szMessage, PRUnichar * szCheckMessage, PRBool* checkValue, nsIDOMWindowInternal* window) { nsresult res; nsCOMPtr dialog; window->GetPrompter(getter_AddRefs(dialog)); if (!dialog) { return PR_FALSE; } PRInt32 buttonPressed = 1; /* in case user exits dialog by clickin X */ PRUnichar * confirm_string = Wallet_Localize("Confirm"); res = dialog->ConfirmEx(confirm_string, szMessage, (nsIPrompt::BUTTON_TITLE_YES * nsIPrompt::BUTTON_POS_0) + (nsIPrompt::BUTTON_TITLE_NO * nsIPrompt::BUTTON_POS_1), nsnull, nsnull, nsnull, szCheckMessage, checkValue, &buttonPressed); if (NS_FAILED(res)) { *checkValue = 0; } if (*checkValue!=0 && *checkValue!=1) { NS_ASSERTION(PR_FALSE, "Bad result from checkbox"); *checkValue = 0; /* this should never happen but it is happening!!! */ } WALLET_FREE(confirm_string); return (buttonPressed == 0); } /*******************************************************/ /* The following routines are for Encyption/Decryption */ /*******************************************************/ #include "nsISecretDecoderRing.h" nsISecretDecoderRing* gSecretDecoderRing; PRBool gEncryptionFailure = PR_FALSE; PRInt32 gReencryptionLevel = 0; PRIVATE nsresult wallet_CryptSetup() { if (!gSecretDecoderRing) { /* Get a secret decoder ring */ nsresult rv = NS_OK; nsCOMPtr secretDecoderRing = do_CreateInstance("@mozilla.org/security/sdr;1", &rv); if (NS_FAILED(rv)) { return NS_ERROR_FAILURE; } gSecretDecoderRing = secretDecoderRing.get(); NS_ADDREF(gSecretDecoderRing); } return NS_OK; } #define PREFIX "~" #include "plbase64.h" PRIVATE nsresult EncryptString (const char * text, char *& crypt) { /* use SecretDecoderRing if encryption pref is set */ nsresult rv; if (SI_GetBoolPref(pref_Crypto, PR_FALSE)) { rv = wallet_CryptSetup(); if (NS_SUCCEEDED(rv)) { rv = gSecretDecoderRing->EncryptString(text, &crypt); } if (NS_FAILED(rv)) { gEncryptionFailure = PR_TRUE; } return rv; } /* otherwise do our own obscuring using Base64 encoding */ char * crypt0 = PL_Base64Encode(text, 0, NULL); if (!crypt0) { return NS_ERROR_FAILURE; } PRUint32 PREFIX_len = sizeof (PREFIX) - 1; PRUint32 crypt0_len = PL_strlen(crypt0); crypt = (char *)PR_Malloc(PREFIX_len + crypt0_len + 1); PRUint32 i; for (i=0; iDecryptString(crypt, &text); } if (NS_FAILED(rv)) { gEncryptionFailure = PR_TRUE; } return rv; } /* otherwise do our own de-obscuring */ PRUint32 PREFIX_len = sizeof(PREFIX) - 1; if (PL_strlen(crypt) == PREFIX_len) { text = (char *)PR_Malloc(1); text[0] = '\0'; return NS_OK; } text = PL_Base64Decode(&crypt[PREFIX_len], 0, NULL); if (!text) { return NS_ERROR_FAILURE; } return NS_OK; } PUBLIC void WLLT_ExpirePassword(PRBool* status) { nsresult rv = wallet_CryptSetup(); if (NS_SUCCEEDED(rv)) { rv = gSecretDecoderRing->LogoutAndTeardown(); } *status = NS_SUCCEEDED(rv); } PUBLIC void WLLT_ExpirePasswordOnly(PRBool* status) { nsresult rv = wallet_CryptSetup(); if (NS_SUCCEEDED(rv)) { rv = gSecretDecoderRing->Logout(); } *status = NS_SUCCEEDED(rv); } PRBool changingPassword = PR_FALSE; PUBLIC void WLLT_ChangePassword(PRBool* status) { nsresult rv = wallet_CryptSetup(); if (NS_SUCCEEDED(rv)) { changingPassword = PR_TRUE; rv = gSecretDecoderRing->ChangePassword(); changingPassword = PR_FALSE; } *status = NS_SUCCEEDED(rv); } nsresult wallet_Encrypt(const nsCString& text, nsCString& crypt) { /* encrypt text to crypt */ char * cryptCString = nsnull; nsresult rv = EncryptString(text.get(), cryptCString); if NS_FAILED(rv) { return rv; } crypt = cryptCString; WALLET_FREE(cryptCString); return NS_OK; } nsresult wallet_Decrypt(const nsCString& crypt, nsCString& text) { /* decrypt crypt to text */ char * textCString = nsnull; nsresult rv = DecryptString(crypt.get(), textCString); if NS_FAILED(rv) { return rv; } text = textCString; WALLET_FREE(textCString); return NS_OK; } PUBLIC nsresult Wallet_Encrypt (const nsString& textUCS2, nsString& cryptUCS2) { nsCAutoString cryptUTF8; nsresult rv = wallet_Encrypt(NS_ConvertUCS2toUTF8(textUCS2), cryptUTF8); cryptUCS2 = NS_ConvertUTF8toUCS2(cryptUTF8); return rv; } PUBLIC nsresult Wallet_Decrypt(const nsString& cryptUCS2, nsString& textUCS2) { nsCAutoString textUTF8; nsresult rv = wallet_Decrypt(NS_ConvertUCS2toUTF8(cryptUCS2), textUTF8); textUCS2 = NS_ConvertUTF8toUCS2(textUTF8); return rv; } PUBLIC nsresult Wallet_Encrypt2(const nsString& text, nsString& crypt) { return Wallet_Encrypt (text, crypt); } PUBLIC nsresult Wallet_Decrypt2 (const nsString& crypt, nsString& text) { return Wallet_Decrypt (crypt, text); } /**********************************************************/ /* The following routines are for accessing the data base */ /**********************************************************/ /* * clear out the designated list */ static void wallet_Clear(nsVoidArray ** list) { if (*list == wallet_SchemaToValue_list || *list == wallet_URL_list) { /* the other lists were allocated in blocks and need to be deallocated the same way */ wallet_MapElement * mapElementPtr; PRInt32 count = LIST_COUNT((*list)); for (PRInt32 i=count-1; i>=0; i--) { mapElementPtr = NS_STATIC_CAST(wallet_MapElement*, (*list)->ElementAt(i)); delete mapElementPtr; } } delete (*list); *list = nsnull; } /* * allocate another mapElement * We are going to buffer up allocations because it was found that alocating one * element at a time was very inefficient on the mac */ PRIVATE nsVoidArray * wallet_MapElementAllocations_list = 0; const PRInt32 kAllocBlockElems = 500; static PRInt32 wallet_NextAllocSlot = kAllocBlockElems; static nsresult wallet_AllocateMapElement(wallet_MapElement*& mapElement) { static wallet_MapElement* mapElementTable; if (wallet_NextAllocSlot >= kAllocBlockElems) { mapElementTable = new wallet_MapElement[kAllocBlockElems]; if (!mapElementTable) { return NS_ERROR_OUT_OF_MEMORY; } if(!wallet_MapElementAllocations_list) { wallet_MapElementAllocations_list = new nsVoidArray(); } if(wallet_MapElementAllocations_list) { wallet_MapElementAllocations_list->AppendElement(mapElementTable); } wallet_NextAllocSlot = 0; } mapElement = &mapElementTable[wallet_NextAllocSlot++]; return NS_OK; } static void wallet_DeallocateMapElements() { wallet_MapElement * mapElementPtr; PRInt32 count = LIST_COUNT(wallet_MapElementAllocations_list); // initialize remainder of last allocated block so we don't crash on []delete for (PRInt32 j=wallet_NextAllocSlot; jElementAt(count-1)); mapElementPtr[j].item1 = nsnull; mapElementPtr[j].item2 = nsnull; mapElementPtr[j].itemList = nsnull; } for (PRInt32 i=count-1; i>=0; i--) { mapElementPtr = NS_STATIC_CAST(wallet_MapElement*, (wallet_MapElementAllocations_list)->ElementAt(i)); delete [] mapElementPtr; } delete wallet_MapElementAllocations_list; wallet_MapElementAllocations_list = nsnull; wallet_NextAllocSlot = kAllocBlockElems; } /* * add an entry to the designated list */ static PRBool wallet_WriteToList( const char* item1, const char* item2, nsVoidArray* itemList, nsVoidArray*& list, PRBool obscure, PlacementType placement = DUP_BEFORE) { wallet_MapElement * mapElementPtr; PRBool added_to_list = PR_FALSE; wallet_MapElement * mapElement = nsnull; if (list == wallet_FieldToSchema_list || list == wallet_SchemaStrings_list || list == wallet_PositionalSchema_list || list == wallet_StateSchema_list || list == wallet_SchemaConcat_list || list == wallet_DistinguishedSchema_list || list == wallet_VcardToSchema_list) { wallet_AllocateMapElement(mapElement); } else { mapElement = new wallet_MapElement; } if (!mapElement) { return PR_FALSE; } nsCAutoString item1UTF8(item1); ToLowerCase(item1UTF8); mapElement->item1 = ToNewCString(item1UTF8); mapElement->item2 = PL_strdup(item2); if (obscure) { char * crypt = nsnull; if (NS_FAILED(EncryptString(mapElement->item2, crypt))) { delete mapElement; return PR_FALSE; } WALLET_FREEIF(mapElement->item2); mapElement->item2 = crypt; } /* make sure the list exists */ if(!list) { list = new nsVoidArray(); if(!list) { delete mapElement; return PR_FALSE; } } mapElement->itemList = itemList; // note: we didn't want to assign itemList sooner because if we delete mapElement // above, we would be wiping out the itemList input parameter /* * Add new entry to the list in alphabetical order by item1. * If identical value of item1 exists, use "placement" parameter to * determine what to do */ if (AT_END==placement) { list->AppendElement(mapElement); return PR_TRUE; } PRInt32 count = LIST_COUNT(list); for (PRInt32 i=0; iElementAt(i)); if (BY_LENGTH==placement) { if (LIST_COUNT(mapElementPtr->itemList) < LIST_COUNT(itemList)) { list->InsertElementAt(mapElement, i); added_to_list = PR_TRUE; break; } else if (LIST_COUNT(mapElementPtr->itemList) == LIST_COUNT(itemList)) { if (itemList) { wallet_Sublist * sublistPtr; wallet_Sublist * sublistPtr2; sublistPtr = NS_STATIC_CAST(wallet_Sublist*, mapElementPtr->itemList->ElementAt(0)); sublistPtr2 = NS_STATIC_CAST(wallet_Sublist*, itemList->ElementAt(0)); if(PL_strlen(sublistPtr->item) < PL_strlen(sublistPtr2->item)) { list->InsertElementAt(mapElement, i); added_to_list = PR_TRUE; break; } } else if (PL_strlen(mapElementPtr->item2) < PL_strlen(mapElement->item2)) { list->InsertElementAt(mapElement, i); added_to_list = PR_TRUE; break; } } } else if(!PL_strcmp(mapElementPtr->item1, mapElement->item1)) { if (DUP_OVERWRITE==placement) { mapElementPtr->item2 = PL_strdup(item2); mapElementPtr->itemList = itemList; mapElement->itemList = nsnull; // else delete might delete itemList input parameter delete mapElement; } else if (DUP_BEFORE==placement) { list->InsertElementAt(mapElement, i); } if (DUP_AFTER!=placement) { added_to_list = PR_TRUE; break; } } else if(PL_strcmp(mapElementPtr->item1, mapElement->item1)>=0) { list->InsertElementAt(mapElement, i); added_to_list = PR_TRUE; break; } } if (!added_to_list) { list->AppendElement(mapElement); } return PR_TRUE; } /* * fetch an entry from the designated list */ static PRBool wallet_ReadFromList( const nsACString& item1, nsACString& item2, nsVoidArray*& itemList, nsVoidArray*& list, PRBool obscure, PRInt32& index) { if (!list || (index == -1)) { return PR_FALSE; } /* find item1 in the list */ wallet_MapElement * mapElementPtr; PRInt32 count = LIST_COUNT(list); for (PRInt32 i=index; iElementAt(i)); if(item1.Equals(mapElementPtr->item1, nsCaseInsensitiveCStringComparator())) { if (obscure) { char * plaintext = nsnull; if (NS_FAILED(DecryptString(mapElementPtr->item2, plaintext))) { return PR_FALSE; } item2 = plaintext; } else { item2 = mapElementPtr->item2; } itemList = mapElementPtr->itemList; index = i+1; if (index == count) { index = -1; } return PR_TRUE; } } index = 0; return PR_FALSE; } PRBool wallet_ReadFromList( const nsACString& item1, nsACString& item2, nsVoidArray*& itemList, nsVoidArray*& list, PRBool obscure) { PRInt32 index = 0; return wallet_ReadFromList(item1, item2, itemList, list, obscure, index); } /*************************************************************/ /* The following routines are for reading/writing utf8 files */ /*************************************************************/ /* * For purposed of internationalization, characters are represented in memory as 16-bit * values (unicode, aka UCS-2) rather than 7 bit values (ascii). For simplicity, the * unicode representation of an ascii character has the upper 9 bits of zero and the * lower 7 bits equal to the 7-bit ascii value. * * These 16-bit unicode values could be stored directly in files. However such files would * not be readable by ascii editors even if they contained all ascii values. To solve * this problem, the 16-bit unicode values are first encoded into a sequence of 8-bit * characters before being written to the file -- the encoding is such that unicode * characters which have the upper 9 bits of zero are encoded into a single 8-bit character * of the same value whereas the remaining unicode characters are encoded into a sequence of * more than one 8-bit character. * * There is a standard 8-bit-encoding of bit strings and it is called UTF-8. The format of * UTF-8 is as follows: * * Up to 7 bits: 0xxxxxxx * Up to 11 bits: 110xxxxx 10xxxxxx * Up to 16 bits: 1110xxxx 10xxxxxx 10xxxxxx * Up to 21 bits: 11110xxx 10xxxxxx 10xxxxxx 10xxxxxx * Up to 26 bits: 111110xx 10xxxxxx 10xxxxxx 10xxxxxx 10xxxxxx * Up to 31 bits: 1111110x 10xxxxxx 10xxxxxx 10xxxxxx 10xxxxxx 10xxxxxx * * Since we are converting unicode (16-bit) values, we need only be concerned with the * first three lines above. * * There are conversion routines provided in intl/uconv which convert between unicode and * UTF-8. However these routines are extremely cumbersome to use. So I have a very simple * pair of encoding/decoding routines for converting between unicode characters and UTF-8 * sequences. Here are the encoding/decoding algorithms that I use: * * encoding 16-bit unicode to 8-bit utf8 stream: * if (unicodeChar <= 0x7F) { // up to 7 bits * utf8Char1 = 0xxxxxxx + lower 7 bits of unicodeChar * } else if (unicodeChar <= 0x7FF) { // up to 11 bits * utf8Char1 = 110xxxxx + upper 5 bits of unicodeChar * utf8Char2 = 10xxxxxx + lower 6 bits of unicodeChar * } else { // up to 16 bits * utf8Char1 = 1110xxxx + upper 4 bits of unicodeChar * utf8Char2 = 10xxxxxx + next 6 bits of unicodeChar * utf8Char3 = 10xxxxxx + lower 6 bits of unicodeChar * } * * decoding 8-bit utf8 stream to 16-bit unicode: * if (utf8Char1 starts with 0) { * unicodeChar = utf8Char1; * } else if (utf8Char1 starts with 110) { * unicodeChar = (lower 5 bits of utf8Char1)<<6 * + (lower 6 bits of utf8Char2); * } else if (utf8Char1 starts with 1110) { * unicodeChar = (lower 4 bits of utf8Char1)<<12 * + (lower 6 bits of utf8Char2)<<6 * + (lower 6 bits of utf8Char3); * } else { * error; * } * */ PUBLIC void Wallet_UTF8Put(nsOutputFileStream& strm, PRUnichar c) { if (c <= 0x7F) { strm.put((char)c); } else if (c <= 0x7FF) { strm.put(((PRUnichar)0xC0) | ((c>>6) & 0x1F)); strm.put(((PRUnichar)0x80) | (c & 0x3F)); } else { strm.put(((PRUnichar)0xE0) | ((c>>12) & 0xF)); strm.put(((PRUnichar)0x80) | ((c>>6) & 0x3F)); strm.put(((PRUnichar)0x80) | (c & 0x3F)); } } static char wallet_Get(nsInputFileStream& strm) { const PRUint32 buflen = 1000; static char buf[buflen+1]; static PRUint32 last = 0; static PRUint32 next = 0; if (next >= last) { next = 0; last = strm.read(buf, buflen); if (last <= 0 || strm.eof()) { /* note that eof is not set until we read past the end of the file */ return 0; } } return (buf[next++]); } PUBLIC PRUnichar Wallet_UTF8Get(nsInputFileStream& strm) { PRUnichar c = wallet_Get(strm); if ((c & 0x80) == 0x00) { return c; } else if ((c & 0xE0) == 0xC0) { return (((c & 0x1F)<<6) + (wallet_Get(strm) & 0x3F)); } else if ((c & 0xF0) == 0xE0) { return (((c & 0x0F)<<12) + ((wallet_Get(strm) & 0x3F)<<6) + (wallet_Get(strm) & 0x3F)); } else { return 0; /* this is an error, input was not utf8 */ } } /* * I have an even a simpler set of routines if you are not concerned about UTF-8. The * algorithms for those routines are as follows: * * encoding 16-bit unicode to 8-bit simple stream: * if (unicodeChar < 0xFF) { * simpleChar1 = unicodeChar * } else { * simpleChar1 = 0xFF * simpleChar2 = upper 8 bits of unicodeChar * simpleChar3 = lower 8 bits of unicodeChar * } * * decoding 8-bit simple stream to 16-bit unicode: * if (simpleChar1 < 0xFF) { * unicodeChar = simpleChar1; * } else { * unicodeChar = 256*simpleChar2 + simpleChar3; * } * */ PUBLIC void Wallet_SimplePut(nsOutputFileStream& strm, PRUnichar c) { if (c < 0xFF) { strm.put((char)c); } else { strm.put((PRUnichar)0xFF); strm.put((c>>8) & 0xFF); strm.put(c & 0xFF); } } PUBLIC PRUnichar Wallet_SimpleGet(nsInputFileStream& strm) { PRUnichar c = (strm.get() & 0xFF); if (c != 0xFF) { return c; } else { return ((strm.get() & 0xFF)<<8) + (strm.get() & 0xFF); } } /************************************************************/ /* The following routines are for unlocking the stored data */ /************************************************************/ char* schemaValueFileName = nsnull; const char URLFileName[] = "URL.tbl"; const char allFileName[] = "wallet.tbl"; const char fieldSchemaFileName[] = "FieldSchema.tbl"; const char vcardSchemaFileName[] = "VcardSchema.tbl"; const char schemaConcatFileName[] = "SchemaConcat.tbl"; const char schemaStringsFileName[] = "SchemaStrings.tbl"; const char positionalSchemaFileName[] = "PositionalSchema.tbl"; const char stateSchemaFileName[] = "StateSchema.tbl"; #ifdef AutoCapture const char distinguishedSchemaFileName[] = "DistinguishedSchema.tbl"; #endif /******************************************************/ /* The following routines are for accessing the files */ /******************************************************/ PUBLIC nsresult Wallet_ProfileDirectory(nsFileSpec& dirSpec) { /* return the profile */ nsresult res; nsCOMPtr aFile; nsCAutoString pathBuf; nsCOMPtr tempSpec; res = NS_GetSpecialDirectory(NS_APP_USER_PROFILE_50_DIR, getter_AddRefs(aFile)); if (NS_FAILED(res)) return res; res = NS_NewFileSpecFromIFile(aFile, getter_AddRefs(tempSpec)); if (NS_FAILED(res)) return res; // TODO: Change the function to return an nsIFile // and not do this conversion res = tempSpec->GetFileSpec(&dirSpec); return res; } PUBLIC nsresult Wallet_DefaultsDirectory(nsFileSpec& dirSpec) { nsresult res; nsCOMPtr aFile; nsCAutoString pathBuf; nsCOMPtr tempSpec; res = NS_GetSpecialDirectory(NS_APP_DEFAULTS_50_DIR, getter_AddRefs(aFile)); if (NS_FAILED(res)) return res; res = aFile->AppendNative(NS_LITERAL_CSTRING("wallet")); if (NS_FAILED(res)) return res; res = NS_NewFileSpecFromIFile(aFile, getter_AddRefs(tempSpec)); if (NS_FAILED(res)) return res; // TODO: Change the function to return an nsIFile // and not do this conversion res = tempSpec->GetFileSpec(&dirSpec); return res; } PUBLIC char * Wallet_RandomName(char* suffix) { /* pick the current time as the random number */ time_t curTime = time(NULL); /* take 8 least-significant digits + three-digit suffix as the file name */ char name[13]; PR_snprintf(name, 13, "%lu.%s", ((int)curTime%100000000), suffix); return PL_strdup(name); } /* * get a line from a file * return -1 if end of file reached * strip carriage returns and line feeds from end of line */ static nsresult wallet_GetLine(nsInputFileStream& strm, const char** lineCString) { const PRUint32 kInitialStringCapacity = 64; nsCAutoString line; /* read the line */ line.Truncate(0); PRInt32 stringLen = 0; PRInt32 stringCap = kInitialStringCapacity; line.SetCapacity(stringCap); char c; static char lastC = '\0'; for (;;) { c = wallet_Get(strm); /* check for eof */ if (c == 0) { return NS_ERROR_FAILURE; } /* check for line terminator (mac=CR, unix=LF, win32=CR+LF */ if (c == '\n' && lastC == '\r') { continue; /* ignore LF if preceded by a CR */ } lastC = c; if (c == '\n' || c == '\r') { break; } stringLen ++; // buffer string grows if (stringLen == stringCap) { stringCap += stringCap; // double buffer len line.SetCapacity(stringCap); } line += c; } WALLET_FREEIF(*lineCString); *lineCString = ToNewCString(line); return NS_OK; } static PRBool wallet_GetHeader(nsInputFileStream& strm) { const char* format = nsnull; /* format revision number */ if (NS_FAILED(wallet_GetLine(strm, &format))) { return PR_FALSE; } PRBool rv = !PL_strcmp(format, HEADER_VERSION); WALLET_FREEIF(format); return rv; } /* * Write a line-feed to a file */ static void wallet_EndLine(nsOutputFileStream& strm) { strm.put('\n'); } /* * Write a line to a file */ static void wallet_PutLine(nsOutputFileStream& strm, const char* line) { strm.write(line, PL_strlen(line)); wallet_EndLine(strm); } static void wallet_PutHeader(nsOutputFileStream& strm) { /* format revision number */ wallet_PutLine(strm, HEADER_VERSION); } #define WALLET_NULL(_ptr) (!(_ptr) || !(_ptr)[0]) /* * write contents of designated list into designated file */ static void wallet_WriteToFile(const char * filename, nsVoidArray* list) { wallet_MapElement * mapElementPtr; /* open output stream */ nsFileSpec dirSpec; nsresult rv = Wallet_ProfileDirectory(dirSpec); if (NS_FAILED(rv)) { return; } nsOutputFileStream strm(dirSpec + filename, nsOutputFileStream::kDefaultMode, 0600); if (!strm.is_open()) { NS_ERROR("unable to open file"); return; } /* make sure the list exists */ if(!list) { return; } /* put out the header */ if (!PL_strcmp(filename, schemaValueFileName)) { wallet_PutHeader(strm); } /* traverse the list */ PRInt32 count = LIST_COUNT(list); for (PRInt32 i=0; iElementAt(i)); wallet_PutLine(strm, (*mapElementPtr).item1); if (!WALLET_NULL((*mapElementPtr).item2)) { wallet_PutLine(strm, (*mapElementPtr).item2); } else { wallet_Sublist * sublistPtr; PRInt32 count2 = LIST_COUNT(mapElementPtr->itemList); for (PRInt32 j=0; jitemList->ElementAt(j)); wallet_PutLine(strm, (*sublistPtr).item); } } wallet_EndLine(strm); } /* close the stream */ strm.flush(); strm.close(); } /* * Read contents of designated file into designated list */ static void wallet_ReadFromFile (const char * filename, nsVoidArray*& list, PRBool localFile, PlacementType placement = AT_END) { /* open input stream */ nsFileSpec dirSpec; nsresult rv; if (localFile) { rv = Wallet_ProfileDirectory(dirSpec); } else { rv = Wallet_DefaultsDirectory(dirSpec); } if (NS_FAILED(rv)) { return; } nsInputFileStream strm(dirSpec + filename); if (!strm.is_open()) { return; } /* read in the header */ if (!PL_strcmp(filename, schemaValueFileName)) { if (!wallet_GetHeader(strm)) { /* something's wrong -- ignore the file */ strm.close(); return; } } for (;;) { if (NS_FAILED(wallet_GetLine(strm, &helpMac->item1))) { /* end of file reached */ break; } #ifdef AutoCapture /* Distinguished schema list is a list of single entries, not name/value pairs */ if (!PL_strcmp(filename, distinguishedSchemaFileName)) { nsVoidArray* dummy = NULL; wallet_WriteToList(helpMac->item1, helpMac->item1, dummy, list, PR_FALSE, placement); continue; } #endif if (NS_FAILED(wallet_GetLine(strm, &helpMac->item2))) { /* unexpected end of file reached */ break; } if (WALLET_NULL(helpMac->item2)) { /* the value must have been deleted */ nsVoidArray* dummy = NULL; wallet_WriteToList(helpMac->item1, helpMac->item2, dummy, list, PR_FALSE, placement); continue; } if (NS_FAILED(wallet_GetLine(strm, &helpMac->item3))) { /* end of file reached */ nsVoidArray* dummy = NULL; wallet_WriteToList(helpMac->item1, helpMac->item2, dummy, list, PR_FALSE, placement); strm.close(); return; } if (WALLET_NULL(helpMac->item3)) { /* just a pair of values, no need for a sublist */ nsVoidArray* dummy = NULL; wallet_WriteToList(helpMac->item1, helpMac->item2, dummy, list, PR_FALSE, placement); } else { /* need to create a sublist and put item2 and item3 onto it */ nsVoidArray * itemList = new nsVoidArray(); if (!itemList) { break; } wallet_Sublist * sublist = new wallet_Sublist; if (!sublist) { break; } sublist->item = PL_strdup(helpMac->item2); itemList->AppendElement(sublist); sublist = new wallet_Sublist; if (!sublist) { delete itemList; break; } sublist->item = PL_strdup(helpMac->item3); itemList->AppendElement(sublist); /* add any following items to sublist up to next blank line */ for (;;) { /* get next item for sublist */ if (NS_FAILED(wallet_GetLine(strm, &helpMac->item3))) { /* end of file reached */ wallet_WriteToList(helpMac->item1, nsnull, itemList, list, PR_FALSE, placement); strm.close(); return; } if (WALLET_NULL(helpMac->item3)) { /* blank line reached indicating end of sublist */ wallet_WriteToList(helpMac->item1, nsnull, itemList, list, PR_FALSE, placement); break; } /* add item to sublist */ sublist = new wallet_Sublist; if (!sublist) { delete itemList; break; } sublist->item = PL_strdup(helpMac->item3); itemList->AppendElement(sublist); } } } strm.close(); } /*********************************************************************/ /* The following are utility routines for the main wallet processing */ /*********************************************************************/ PUBLIC void Wallet_GiveCaveat(nsIDOMWindowInternal* window, nsIPrompt* dialog) { /* test for first capturing of data ever and give caveat if so */ if (!SI_GetBoolPref(pref_Caveat, PR_FALSE)) { SI_SetBoolPref(pref_Caveat, PR_TRUE); PRUnichar * message = Wallet_Localize("Caveat"); if (window) { wallet_Alert(message, window); } else { wallet_Alert(message, dialog); } WALLET_FREE(message); } } static void wallet_GetHostFile(nsIURI * url, nsString& outHostFile) { outHostFile.Truncate(0); nsCAutoString host; nsresult rv = url->GetHost(host); if (NS_FAILED(rv)) { return; } NS_ConvertUTF8toUCS2 urlName(host); nsCAutoString file; rv = url->GetPath(file); if (NS_FAILED(rv)) { return; } urlName.Append(NS_ConvertUTF8toUCS2(file)); PRInt32 queryPos = urlName.FindChar('?'); PRInt32 stringEnd = (queryPos == kNotFound) ? urlName.Length() : queryPos; urlName.Left(outHostFile, stringEnd); } static void Strip(const nsString& textUCS2, nsCString& stripText) { NS_ConvertUCS2toUTF8 textUTF8(textUCS2); // above line is equivalen to the following (who would have guessed it?) // nsCAutoString textUTF8 = NS_ConvertUCS2toUTF8(textUCS2); for (PRUint32 i=0; i'~') { stripText += c; } } } /* * given a displayable text, get the schema */ static void TextToSchema( const nsString& text, nsACString& schema) { /* return if no SchemaStrings list exists */ if (!wallet_SchemaStrings_list) { return; } /* try each schema entry in schemastring table to see if it's acceptable */ wallet_MapElement * mapElementPtr; PRInt32 count = LIST_COUNT(wallet_SchemaStrings_list); for (PRInt32 i=0; iElementAt(i)); wallet_Sublist * sublistPtr; PRInt32 count2 = LIST_COUNT(mapElementPtr->itemList); if (count2) { for (PRInt32 i2=0; i2itemList->ElementAt(i2)); if (text.Find(sublistPtr->item, PR_TRUE) == -1) { /* displayable text does not contain this string, reject this schema */ isSubstring = PR_FALSE; break; } } } else if (text.Find(mapElementPtr->item2, PR_TRUE) == -1) { /* displayable text does not contain this string, reject this schema */ isSubstring = PR_FALSE; } if (isSubstring) { /* all strings were contained in the displayable text, accept this schema */ schema.Assign(mapElementPtr->item1); return; } } } /* * given a field name, get the value */ static nsresult FieldToValue( const nsString& field, nsACString& schema, nsString& valueUCS2, nsVoidArray*& itemList, PRInt32& index) { /* return if no SchemaToValue list exists or if all values previous used */ if (!wallet_SchemaToValue_list || index == -1) { return NS_ERROR_FAILURE; } /* if no schema name is given, fetch schema name from field/schema tables */ nsVoidArray* dummy; nsCAutoString stripField; if (schema.IsEmpty()) { Strip(field, stripField); } if (!schema.IsEmpty() || wallet_ReadFromList(stripField, schema, dummy, wallet_FieldToSchema_list, PR_FALSE)) { /* schema name found, now attempt to fetch value from schema/value table */ nsCAutoString valueUTF8; PRInt32 index2 = index; if ((index >= 0) && wallet_ReadFromList (schema, valueUTF8, itemList, wallet_SchemaToValue_list, PR_TRUE, index2)) { /* value found, prefill it into form and return */ valueUCS2 = NS_ConvertUTF8toUCS2(valueUTF8); index = index2; return NS_OK; } else { /* value not found, see if concatenation rule exists */ nsVoidArray * itemList2; nsCAutoString valueUTF8b; if (index > 0) { index = 0; } PRInt32 index0 = index; PRInt32 index00 = index; PRInt32 index4 = 0; while (wallet_ReadFromList(schema, valueUTF8b, itemList2, wallet_SchemaConcat_list, PR_FALSE, index4)) { /* concatenation rules exist, generate value as a concatenation */ nsCAutoString concatenatedValueUTF8; wallet_Sublist * sublistPtr; concatenatedValueUTF8.SetLength(0); nsCAutoString valueUTF8c; PRInt32 index00max = index0; if (!valueUTF8b.IsEmpty()) { /* single item on rhs of concatenation rule */ PRInt32 index5 = 0; PRInt32 j; PRBool failed = PR_FALSE; for (j=0; j>index0; j -= 2) { if (!wallet_ReadFromList(valueUTF8b, valueUTF8c, dummy, wallet_SchemaToValue_list, PR_TRUE, index5)) { failed = PR_TRUE; break; } index00 += 2; } if (!failed && wallet_ReadFromList(valueUTF8b, valueUTF8c, dummy, wallet_SchemaToValue_list, PR_TRUE, index5)) { /* found an unused value for the single rhs item */ concatenatedValueUTF8 += valueUTF8c; index00 += 2; } index00max = index00; } /* process each item in a multi-rhs rule */ PRInt32 count = LIST_COUNT(itemList2); for (PRInt32 i=0; iElementAt(i)); /* skip over values found previously */ /* note: a returned index of -1 means not-found. So we will use the * negative even numbers (-2, -4, -6) to designate found as a concatenation * where -2 means first value of each concatenation, -4 means second value, etc. */ index00 = index0; PRInt32 index3 = 0; PRBool failed = PR_FALSE; nsCAutoString valueUTF8d; valueUTF8d.Assign(sublistPtr->item); for (PRInt32 j=0; j>index0; j -= 2) { if (!wallet_ReadFromList(valueUTF8d, valueUTF8, dummy, wallet_SchemaToValue_list, PR_TRUE, index3)) { /* all values of next multi-rhs item were used previously */ failed = PR_TRUE; break; } index00 += 2; } if (!failed && wallet_ReadFromList(valueUTF8d, valueUTF8, dummy, wallet_SchemaToValue_list, PR_TRUE, index3)) { if (!concatenatedValueUTF8.IsEmpty()) { concatenatedValueUTF8 += " "; } /* found an unused value for the multi-rhs item */ concatenatedValueUTF8 += valueUTF8; index00 += 2; } if (index00 > index00max) { index00max = index00; } } itemList = nsnull; if (!concatenatedValueUTF8.IsEmpty()) { /* a new value was found */ index -= 2; valueUCS2 = NS_ConvertUTF8toUCS2(concatenatedValueUTF8); return NS_OK; } /* all values from this concat rule were used, go on to next concat rule */ index0 = index00max; } /* no more concat rules, indicate failure */ index = -1; return NS_ERROR_FAILURE; } } else { /* schema name not found, use field name as schema name and fetch value */ PRInt32 index2 = index; nsAutoString localSchemaUCS2; wallet_GetHostFile(wallet_lastUrl, localSchemaUCS2); localSchemaUCS2.Append(NS_LITERAL_STRING(":")); localSchemaUCS2.Append(field); nsCAutoString localSchemaUTF8 = NS_ConvertUCS2toUTF8(localSchemaUCS2); nsCAutoString valueUTF8; if (wallet_ReadFromList (localSchemaUTF8, valueUTF8, itemList, wallet_SchemaToValue_list, PR_TRUE, index2)) { /* value found, prefill it into form */ schema = localSchemaUTF8; index = index2; valueUCS2 = NS_ConvertUTF8toUCS2(valueUTF8); return NS_OK; } } index = -1; return NS_ERROR_FAILURE; } static nsresult wallet_GetSelectIndex( nsIDOMHTMLSelectElement* selectElement, const nsString& value, PRInt32& index) { PRUint32 length; selectElement->GetLength(&length); nsCOMPtr options; selectElement->GetOptions(getter_AddRefs(options)); if (options) { PRUint32 numOptions; options->GetLength(&numOptions); for (PRUint32 optionX = 0; optionX < numOptions; optionX++) { nsCOMPtr optionNode; options->Item(optionX, getter_AddRefs(optionNode)); if (optionNode) { nsCOMPtr optionElement(do_QueryInterface(optionNode)); if (optionElement) { nsAutoString optionValue; nsAutoString optionText; optionElement->GetValue(optionValue); optionElement->GetText(optionText); nsAutoString valueLC( value ); ToLowerCase(valueLC); ToLowerCase(optionValue); ToLowerCase(optionText); optionText.Trim(" \n\t\r"); if (valueLC==optionValue || valueLC==optionText) { index = optionX; return NS_OK; } } } } } return NS_ERROR_FAILURE; } void wallet_StepForwardOrBack (nsIDOMNode*& elementNode, nsString& text, PRBool& atInputOrSelect, PRBool& atEnd, PRBool goForward) { nsresult result; atInputOrSelect = PR_FALSE; atEnd = PR_FALSE; /* try getting next/previous sibling */ nsCOMPtr sibling; if (goForward) { result = elementNode->GetNextSibling(getter_AddRefs(sibling)); } else { result = elementNode->GetPreviousSibling(getter_AddRefs(sibling)); } if ((NS_FAILED(result)) || !sibling) { /* no next/previous siblings, try getting parent */ nsCOMPtr parent; result = elementNode->GetParentNode(getter_AddRefs(parent)); if ((NS_FAILED(result)) || !parent) { /* no parent, we've reached the top of the tree */ atEnd = PR_TRUE; } else { /* parent obtained */ elementNode = parent; } return; } /* sibling obtained */ elementNode = sibling; while (PR_TRUE) { /* if we've reached a SELECT or non-hidden INPUT tag, we're done */ /* * There is a subtle difference here between going forward and going backwards. * * When going forward we are trying to find out how many consecutive elements are not separated * by displayed text. That is important for determing, for example, if we have a three-input phone-number * field. In that case, we want to consider only input tags have type="text" or no type ("text" by default). * * When going backwards we want to find the text between the current element and any preceding * visible element. That would include such things as type="button", type="submit" etc. The * only thing it would exclude is type="hidden". */ nsCOMPtr inputElement(do_QueryInterface(elementNode, &result)); if ((NS_SUCCEEDED(result)) && (inputElement)) { nsAutoString type; result = inputElement->GetType(type); if (goForward) { if (NS_SUCCEEDED(result) && (type.IsEmpty() || type.Equals(NS_LITERAL_STRING("text"), nsCaseInsensitiveStringComparator()))) { /* at element and it's type is either "text" or is missing ("text" by default) */ atInputOrSelect = PR_TRUE; return; } } else { if (NS_SUCCEEDED(result) && !type.Equals(NS_LITERAL_STRING("hidden"), nsCaseInsensitiveStringComparator())) { /* at element and it's type is not "hidden" */ atInputOrSelect = PR_TRUE; return; } } } else { nsCOMPtr selectElement(do_QueryInterface(elementNode)); if (selectElement) { atInputOrSelect = PR_TRUE; return; } } /* if we've reached a #text node, append it to accumulated text */ nsAutoString siblingNameUCS2; result = elementNode->GetNodeName(siblingNameUCS2); nsCAutoString siblingNameUTF8; siblingNameUTF8.AssignWithConversion(siblingNameUCS2); if (siblingNameUTF8.EqualsIgnoreCase("#text")) { nsAutoString siblingValue; result = elementNode->GetNodeValue(siblingValue); text.Append(siblingValue); } /* if we've reached a SCRIPT node, don't fetch its siblings */ if (siblingNameUTF8.EqualsIgnoreCase("SCRIPT")) { return; } /* try getting first/last child */ nsCOMPtr child; if (goForward) { result = elementNode->GetFirstChild(getter_AddRefs(child)); } else { result = elementNode->GetLastChild(getter_AddRefs(child)); } if ((NS_FAILED(result)) || !child) { /* no children, we're done with this node */ return; } /* child obtained */ elementNode = child; } return; } //#include "nsIUGenCategory.h" //#include "nsUnicharUtilCIID.h" //static NS_DEFINE_IID(kUnicharUtilCID, NS_UNICHARUTIL_CID); //static NS_DEFINE_IID(kIUGenCategoryIID, NS_IUGENCATEGORY_IID); //#include "nsICaseConversion.h" //static NS_DEFINE_IID(kICaseConversionIID, NS_ICASECONVERSION_IID); //static nsICaseConversion* gCaseConv = nsnull; static void wallet_ResolvePositionalSchema(nsIDOMNode* elementNode, nsACString& schema) { static PRInt32 numerator = 0; static PRInt32 denominator = 0; static nsCString lastPositionalSchema; /* return if no PositionalSchema list exists */ if (!wallet_PositionalSchema_list) { schema.SetLength(0); return; } if (!schema.IsEmpty()) { numerator = 0; denominator = 0; lastPositionalSchema.Assign(schema); } else if (numerator < denominator) { schema.Assign(lastPositionalSchema); } else { schema.SetLength(0); return; } /* search PositionalSchema list for our positional schema */ wallet_MapElement * mapElementPtr; PRInt32 count = LIST_COUNT(wallet_PositionalSchema_list); for (PRInt32 i=0; iElementAt(i)); if (schema.Equals(mapElementPtr->item1, nsCaseInsensitiveCStringComparator())) { /* found our positional schema in the list */ /* A "position set" is a set of continuous or