/* -*- 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 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 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 ***** */ /* wallet.cpp */ #include "wallet.h" #include "singsign.h" #include "nsNetUtil.h" #include "nsILineInputStream.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 "nsAppDirectoryServiceDefs.h" #include "nsIStringBundle.h" #include "prmem.h" #include "prprf.h" #include "nsIContent.h" #include "nsIObserverService.h" #include "nsIWalletService.h" #include #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"; static const char pref_captureForms[] = "wallet.captureForms"; static const char pref_enabled[] = "wallet.enabled"; static const char pref_WalletSchemaValueFileName[] = "wallet.SchemaValueFileName"; static PRBool wallet_captureForms = PR_FALSE; static 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; } 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); } } static PRBool wallet_GetFormsCapturingPref(void) { wallet_RegisterCapturePrefCallbacks(); return wallet_captureForms; } static 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; } /***************************************************/ /* 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) 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. */ 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; i(itemList->ElementAt(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 */ class wallet_HelpMac { public: wallet_HelpMac() { MOZ_COUNT_CTOR(wallet_HelpMac); } ~wallet_HelpMac() { MOZ_COUNT_DTOR(wallet_HelpMac); } nsCString item1; nsCString item2; nsCString item3; }; wallet_HelpMac * helpMac; static nsVoidArray * wallet_FieldToSchema_list = 0; static nsVoidArray * wallet_VcardToSchema_list = 0; static nsVoidArray * wallet_SchemaToValue_list = 0; static nsVoidArray * wallet_SchemaConcat_list = 0; static nsVoidArray * wallet_SchemaStrings_list = 0; static nsVoidArray * wallet_PositionalSchema_list = 0; static nsVoidArray * wallet_StateSchema_list = 0; static nsVoidArray * wallet_URL_list = 0; static nsVoidArray * wallet_DistinguishedSchema_list = 0; #define NO_CAPTURE(x) x[0] #define NO_PREVIEW(x) x[1] 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_LossyConvertUTF16toASCII(as).get()); } static void wallet_Dump(nsVoidArray * list) { wallet_MapElement * mapElementPtr; PRInt32 count = LIST_COUNT(list); for (PRInt32 i=0; i(list->ElementAt(i)); fprintf(stdout, "%s %s \n", (mapElementPtr->item1), (mapElementPtr->item2)); wallet_Sublist * sublistPtr; PRInt32 count2 = LIST_COUNT(mapElementPtr->itemList); for (PRInt32 i2=0; i2(mapElementPtr->itemList->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 */ NS_ConvertASCIItoUTF16 strtmp(genericString); PRUnichar *ptrv = nsnull; ret = bundle->GetStringFromName(strtmp.get(), &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; } 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::STD_YES_NO_BUTTONS, nsnull, nsnull, nsnull, nsnull, nsnull, &buttonPressed); WALLET_FREE(confirm_string); return (buttonPressed == 0); } 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_POS_1_DEFAULT + nsIPrompt::STD_YES_NO_BUTTONS + (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; } static 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 } static 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 } 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::STD_YES_NO_BUTTONS, 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 = nsnull; PRBool gEncryptionFailure = PR_FALSE; PRInt32 gReencryptionLevel = 0; static 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" static 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; } void WLLT_ExpirePassword(PRBool* status) { if (gSecretDecoderRing) { gSecretDecoderRing->LogoutAndTeardown(); } *status = PR_TRUE; } void WLLT_ExpirePasswordOnly(PRBool* status) { nsresult rv = wallet_CryptSetup(); if (NS_SUCCEEDED(rv)) { rv = gSecretDecoderRing->Logout(); } *status = NS_SUCCEEDED(rv); } PRBool changingPassword = PR_FALSE; 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; } nsresult Wallet_Encrypt (const nsAString& textUCS2, nsAString& cryptUCS2) { nsCAutoString cryptUTF8; nsresult rv = wallet_Encrypt(NS_ConvertUTF16toUTF8(textUCS2), cryptUTF8); CopyUTF8toUTF16(cryptUTF8, cryptUCS2); return rv; } nsresult Wallet_Decrypt(const nsAString& cryptUCS2, nsAString& textUCS2) { nsCAutoString textUTF8; nsresult rv = wallet_Decrypt(NS_ConvertUTF16toUTF8(cryptUCS2), textUTF8); CopyUTF8toUTF16(textUTF8, textUCS2); return rv; } /**********************************************************/ /* 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 = static_cast((*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 */ static 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; j ((wallet_MapElementAllocations_list)->ElementAt(count-1)); mapElementPtr[j].item1 = nsnull; mapElementPtr[j].item2 = nsnull; mapElementPtr[j].itemList = nsnull; } for (PRInt32 i=count-1; i>=0; i--) { mapElementPtr = static_cast((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; i(list->ElementAt(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 = static_cast(mapElementPtr->itemList->ElementAt(0)); sublistPtr2 = static_cast(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; i(list->ElementAt(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 unlocking the stored data */ /************************************************************/ char* schemaValueFileName = nsnull; static const char URLFileName[] = "URL.tbl"; static const char allFileName[] = "wallet.tbl"; static const char fieldSchemaFileName[] = "FieldSchema.tbl"; static const char vcardSchemaFileName[] = "VcardSchema.tbl"; static const char schemaConcatFileName[] = "SchemaConcat.tbl"; static const char schemaStringsFileName[] = "SchemaStrings.tbl"; static const char positionalSchemaFileName[] = "PositionalSchema.tbl"; static const char stateSchemaFileName[] = "StateSchema.tbl"; static const char distinguishedSchemaFileName[] = "DistinguishedSchema.tbl"; /******************************************************/ /* The following routines are for accessing the files */ /******************************************************/ nsresult Wallet_ProfileDirectory(nsIFile** aFile) { /* return the profile */ return NS_GetSpecialDirectory(NS_APP_USER_PROFILE_50_DIR, aFile); } nsresult Wallet_DefaultsDirectory(nsIFile** aFile) { nsresult res; nsCOMPtr file; res = NS_GetSpecialDirectory(NS_APP_DEFAULTS_50_DIR, getter_AddRefs(file)); if (NS_FAILED(res)) return res; res = file->AppendNative(NS_LITERAL_CSTRING("wallet")); if (NS_FAILED(res)) return res; NS_ADDREF(*aFile = file); return NS_OK; } 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. stream must implement nsILineInputStream. * return error if end of file reached * strip carriage returns and line feeds from end of line * free with nsMemory::Free */ nsresult wallet_GetLine(nsIInputStream* strm, nsACString &line) { line.Truncate(); nsCOMPtr lis(do_QueryInterface(strm)); NS_ENSURE_TRUE(lis, NS_ERROR_UNEXPECTED); PRBool more; nsresult rv = lis->ReadLine(line, &more); if (NS_FAILED(rv)) return rv; // Assume that we are past EOF if more==FALSE and line is empty // this may be wrong if the file ends with an empty line, though if (!more && line.IsEmpty()) return NS_ERROR_FAILURE; return NS_OK; } static PRBool wallet_GetHeader(nsIInputStream* strm) { nsCAutoString format; /* format revision number */ if (NS_FAILED(wallet_GetLine(strm, format))) { return PR_FALSE; } return format.EqualsLiteral(HEADER_VERSION); } /* * Write a line-feed to a file */ static void wallet_EndLine(nsIOutputStream* strm) { static const char nl = '\n'; PRUint32 dummy; strm->Write(&nl, 1, &dummy); } /* * Write a line to a file */ void wallet_PutLine(nsIOutputStream* strm, const char* line) { PRUint32 dummy; strm->Write(line, strlen(line), &dummy); wallet_EndLine(strm); } static void wallet_PutHeader(nsIOutputStream* 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; /* make sure the list exists */ if(!list) { return; } /* open output stream */ nsCOMPtr file; nsresult rv = Wallet_ProfileDirectory(getter_AddRefs(file)); if (NS_FAILED(rv)) { return; } file->AppendNative(nsDependentCString(filename)); nsCOMPtr fileOutputStream; rv = NS_NewSafeLocalFileOutputStream(getter_AddRefs(fileOutputStream), file, -1, 0600); if (NS_FAILED(rv)) return; nsCOMPtr strm; rv = NS_NewBufferedOutputStream(getter_AddRefs(strm), fileOutputStream, 4096); if (NS_FAILED(rv)) 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; i(list->ElementAt(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; j(mapElementPtr->itemList->ElementAt(j)); wallet_PutLine(strm, (*sublistPtr).item); } } wallet_EndLine(strm); } // All went ok. Maybe except for problems in Write(), but the stream detects // that for us nsCOMPtr safeStream = do_QueryInterface(strm); NS_ASSERTION(safeStream, "expected a safe output stream!"); if (safeStream) { rv = safeStream->Finish(); if (NS_FAILED(rv)) { NS_WARNING("failed to save wallet file! possible dataloss"); return; } } } /* * 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 */ nsCOMPtr file; nsresult rv; if (localFile) { rv = Wallet_ProfileDirectory(getter_AddRefs(file)); } else { rv = Wallet_DefaultsDirectory(getter_AddRefs(file)); } if (NS_FAILED(rv)) { return; } file->AppendNative(nsDependentCString(filename)); nsCOMPtr strm; rv = NS_NewLocalFileInputStream(getter_AddRefs(strm), file); if (NS_FAILED(rv)) return; /* read in the header */ if (!PL_strcmp(filename, schemaValueFileName)) { if (!wallet_GetHeader(strm)) { /* something's wrong -- ignore the file */ return; } } for (;;) { if (NS_FAILED(wallet_GetLine(strm, helpMac->item1))) { /* end of file reached */ break; } /* 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.get(), helpMac->item1.get(), dummy, list, PR_FALSE, placement); continue; } if (NS_FAILED(wallet_GetLine(strm, helpMac->item2))) { /* unexpected end of file reached */ break; } if (helpMac->item2.IsEmpty()) { /* the value must have been deleted */ nsVoidArray* dummy = NULL; wallet_WriteToList(helpMac->item1.get(), helpMac->item2.get(), 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.get(), helpMac->item2.get(), dummy, list, PR_FALSE, placement); return; } if (helpMac->item3.IsEmpty()) { /* just a pair of values, no need for a sublist */ nsVoidArray* dummy = NULL; wallet_WriteToList(helpMac->item1.get(), helpMac->item2.get(), 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 = ToNewCString(helpMac->item2); itemList->AppendElement(sublist); sublist = new wallet_Sublist; if (!sublist) { delete itemList; break; } sublist->item = ToNewCString(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.get(), nsnull, itemList, list, PR_FALSE, placement); return; } if (helpMac->item3.IsEmpty()) { /* blank line reached indicating end of sublist */ wallet_WriteToList(helpMac->item1.get(), nsnull, itemList, list, PR_FALSE, placement); break; } /* add item to sublist */ sublist = new wallet_Sublist; if (!sublist) { delete itemList; break; } sublist->item = ToNewCString(helpMac->item3); itemList->AppendElement(sublist); } } } } /*********************************************************************/ /* The following are utility routines for the main wallet processing */ /*********************************************************************/ 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_ConvertUTF8toUTF16 urlName(host); nsCAutoString file; rv = url->GetPath(file); if (NS_FAILED(rv)) { return; } AppendUTF8toUTF16(file, urlName); PRInt32 queryPos = urlName.FindChar('?'); PRInt32 stringEnd = (queryPos == kNotFound) ? urlName.Length() : queryPos; urlName.Left(outHostFile, stringEnd); } static void Strip(const nsString& textUCS2, nsCString& stripText) { NS_ConvertUTF16toUTF8 textUTF8(textUCS2); // above line is equivalen to the following (who would have guessed it?) // nsCAutoString textUTF8 = NS_ConvertUTF16toUTF8(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; i(wallet_SchemaStrings_list->ElementAt(i)); wallet_Sublist * sublistPtr; PRInt32 count2 = LIST_COUNT(mapElementPtr->itemList); if (count2) { for (PRInt32 i2=0; i2(mapElementPtr->itemList->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 */ CopyUTF8toUTF16(valueUTF8, valueUCS2); 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; i(itemList2->ElementAt(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; CopyUTF8toUTF16(concatenatedValueUTF8, valueUCS2); 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.AppendLiteral(":"); localSchemaUCS2.Append(field); NS_ConvertUTF16toUTF8 localSchemaUTF8(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; CopyUTF8toUTF16(valueUTF8, valueUCS2); 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.LowerCaseEqualsLiteral("text"))) { /* 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.LowerCaseEqualsLiteral("hidden")) { /* 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); if (siblingNameUCS2.LowerCaseEqualsLiteral("#text")) { nsAutoString siblingValue; result = elementNode->GetNodeValue(siblingValue); text.Append(siblingValue); } /* if we've reached a SCRIPT node, don't fetch its siblings */ if (siblingNameUCS2.LowerCaseEqualsLiteral("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); //#include "nsICaseConversion.h" //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; i(wallet_PositionalSchema_list->ElementAt(i)); if (schema.Equals(mapElementPtr->item1, nsCaseInsensitiveCStringComparator())) { /* found our positional schema in the list */ /* A "position set" is a set of continuous or