/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- * * 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 Netscape are * Copyright (C) 1998 Netscape Communications Corporation. All * Rights Reserved. * * Contributor(s): * Pierre Phaneuf */ #define alphabetize 1 #include "singsign.h" #ifdef XP_MAC #include "prpriv.h" /* for NewNamedMonitor */ #include "prinrval.h" /* for PR_IntervalNow */ #ifdef APPLE_KEYCHAIN /* APPLE */ #include "Keychain.h" /* APPLE */ #define kNetscapeProtocolType 'form' /* APPLE */ #endif /* APPLE */ #else #include "private/prpriv.h" /* for NewNamedMonitor */ #endif #include "nsIPref.h" #include "nsFileStream.h" #include "nsSpecialSystemDirectory.h" #include "nsINetSupportDialogService.h" #include "nsIServiceManager.h" #include "nsIIOService.h" #include "nsIURL.h" #include "nsIDOMHTMLDocument.h" #include "prmem.h" #include "prprf.h" #include "nsVoidArray.h" #include "nsXPIDLString.h" static NS_DEFINE_IID(kIPrefServiceIID, NS_IPREF_IID); static NS_DEFINE_IID(kPrefServiceCID, NS_PREF_CID); static NS_DEFINE_CID(kNetSupportDialogCID, NS_NETSUPPORTDIALOG_CID); static NS_DEFINE_CID(kIOServiceCID, NS_IOSERVICE_CID); static NS_DEFINE_CID(kStandardUrlCID, NS_STANDARDURL_CID); /******************** * Global Variables * ********************/ /* locks for signon cache */ static PRMonitor * signon_lock_monitor = NULL; static PRThread * signon_lock_owner = NULL; static int signon_lock_count = 0; /* load states */ static PRBool si_PartiallyLoaded = PR_FALSE; static PRBool si_UserHasBeenSelected = PR_FALSE; /* apple keychain stuff */ #ifdef APPLE_KEYCHAIN static PRBool si_list_invalid = PR_FALSE; static KCCallbackUPP si_kcUPP = NULL; PRIVATE int si_SaveSignonDataInKeychain(); #endif #define USERNAMEFIELD "\\=username=\\" #define PASSWORDFIELD "\\=password=\\" /************************************* * Externs to Routines in Wallet.cpp * *************************************/ extern nsresult Wallet_ProfileDirectory(nsFileSpec& dirSpec); extern PRUnichar * Wallet_Localize(char * genericString); /*********** * Dialogs * ***********/ extern PRBool Wallet_ConfirmYN(PRUnichar * szMessage); PRIVATE PRBool si_ConfirmYN(PRUnichar * szMessage) { return Wallet_ConfirmYN(szMessage); } extern PRInt32 Wallet_3ButtonConfirm(PRUnichar * szMessage); PRIVATE PRInt32 si_3ButtonConfirm(PRUnichar * szMessage) { return Wallet_3ButtonConfirm(szMessage); } // This will go away once select is passed a prompter interface #include "nsAppShellCIDs.h" // TODO remove later #include "nsIAppShellService.h" // TODO remove later #include "nsIXULWindow.h" // TODO remove later static NS_DEFINE_CID(kAppShellServiceCID, NS_APPSHELL_SERVICE_CID); PRIVATE PRBool si_SelectDialog(const PRUnichar* szMessage, PRUnichar** pList, PRInt32* pCount) { if (si_UserHasBeenSelected) { /* a user was already selected for this form, use same one again */ *pCount = 0; /* last user selected is now at head of list */ return PR_TRUE; } nsresult rv; NS_WITH_SERVICE(nsIAppShellService, appshellservice, kAppShellServiceCID, &rv); if(NS_FAILED(rv)) { return PR_FALSE; } nsCOMPtr xulWindow; appshellservice->GetHiddenWindow(getter_AddRefs(xulWindow)); nsCOMPtr prompter(do_QueryInterface(xulWindow)); PRInt32 selectedIndex; PRBool rtnValue; PRUnichar * title_string = Wallet_Localize("SelectUserTitleLine"); rv = prompter->Select( title_string, szMessage, *pCount, NS_CONST_CAST(const PRUnichar**, pList), &selectedIndex, &rtnValue ); Recycle(title_string); *pCount = selectedIndex; si_UserHasBeenSelected = PR_TRUE; return rtnValue; } nsresult si_CheckGetPassword (PRUnichar ** password, const PRUnichar * szMessage, PRBool* checkValue) { nsresult res; NS_WITH_SERVICE(nsIPrompt, dialog, kNetSupportDialogCID, &res); if (NS_FAILED(res)) { return res; } PRInt32 buttonPressed = 1; /* in case user exits dialog by clickin X */ PRUnichar * prompt_string = Wallet_Localize("PromptForPassword"); PRUnichar * check_string = Wallet_Localize("SaveThisValue"); res = dialog->UniversalDialog( NULL, /* title message */ prompt_string, /* title text in top line of window */ szMessage, /* this is the main message */ check_string, /* This is the checkbox message */ NULL, /* first button text, becomes OK by default */ NULL, /* second button text, becomes CANCEL by default */ NULL, /* third button text */ NULL, /* fourth button text */ NULL, /* first edit field label */ NULL, /* second edit field label */ password, /* first edit field initial and final value */ NULL, /* second edit field initial and final value */ NULL, /* icon: question mark by default */ checkValue, /* initial and final value of checkbox */ 2, /* number of buttons */ 1, /* number of edit fields */ 1, /* is first edit field a password field */ &buttonPressed); Recycle(prompt_string); Recycle(check_string); if (NS_FAILED(res)) { return res; } if (buttonPressed == 0) { return NS_OK; } else { return NS_ERROR_FAILURE; /* user pressed cancel */ } } nsresult si_CheckGetData (PRUnichar ** data, const PRUnichar * szMessage, PRBool* checkValue) { nsresult res; NS_WITH_SERVICE(nsIPrompt, dialog, kNetSupportDialogCID, &res); if (NS_FAILED(res)) { return res; } PRInt32 buttonPressed = 1; /* in case user exits dialog by clickin X */ PRUnichar * prompt_string = Wallet_Localize("PromptForData"); PRUnichar * check_string = Wallet_Localize("SaveThisValue"); res = dialog->UniversalDialog( NULL, /* title message */ prompt_string, /* title text in top line of window */ szMessage, /* this is the main message */ check_string, /* This is the checkbox message */ NULL, /* first button text, becomes OK by default */ NULL, /* second button text, becomes CANCEL by default */ NULL, /* third button text */ NULL, /* fourth button text */ NULL, /* first edit field label */ NULL, /* second edit field label */ data, /* first edit field initial and final value */ NULL, /* second edit field initial and final value */ NULL, /* icon: question mark by default */ checkValue, /* initial and final value of checkbox */ 2, /* number of buttons */ 1, /* number of edit fields */ 0, /* is first edit field a password field */ &buttonPressed); Recycle(prompt_string); Recycle(check_string); if (NS_FAILED(res)) { return res; } if (buttonPressed == 0) { return NS_OK; } else { return NS_ERROR_FAILURE; /* user pressed cancel */ } } nsresult si_CheckGetUsernamePassword (PRUnichar ** username, PRUnichar ** password, const PRUnichar * szMessage, PRBool* checkValue) { nsresult res; NS_WITH_SERVICE(nsIPrompt, dialog, kNetSupportDialogCID, &res); if (NS_FAILED(res)) { return res; } PRInt32 buttonPressed = 1; /* in case user exits dialog by clickin X */ PRUnichar * prompt_string = Wallet_Localize("PromptForPassword"); PRUnichar * check_string = Wallet_Localize("SaveTheseValues"); PRUnichar * user_string = Wallet_Localize("UserName"); PRUnichar * password_string = Wallet_Localize("Password"); res = dialog->UniversalDialog( NULL, /* title message */ prompt_string, /* title text in top line of window */ szMessage, /* this is the main message */ check_string, /* This is the checkbox message */ NULL, /* first button text, becomes OK by default */ NULL, /* second button text, becomes CANCEL by default */ NULL, /* third button text */ NULL, /* fourth button text */ user_string, /* first edit field label */ password_string, /* second edit field label */ username, /* first edit field initial and final value */ password, /* second edit field initial and final value */ NULL, /* icon: question mark by default */ checkValue, /* initial and final value of checkbox */ 2, /* number of buttons */ 2, /* number of edit fields */ 0, /* is first edit field a password field */ &buttonPressed); Recycle(prompt_string); Recycle(check_string); Recycle(user_string); Recycle(password_string); if (NS_FAILED(res)) { return res; } if (buttonPressed == 0) { return NS_OK; } else { return NS_ERROR_FAILURE; /* user pressed cancel */ } } /****************** * Key Management * ******************/ char* signonFileName = nsnull; /*************************** * Locking the Signon List * ***************************/ PRIVATE void si_lock_signon_list(void) { if(!signon_lock_monitor) { signon_lock_monitor = PR_NewNamedMonitor("signon-lock"); } PR_EnterMonitor(signon_lock_monitor); while(PR_TRUE) { /* no current owner or owned by this thread */ PRThread * t = PR_CurrentThread(); if(signon_lock_owner == NULL || signon_lock_owner == t) { signon_lock_owner = t; signon_lock_count++; PR_ExitMonitor(signon_lock_monitor); return; } /* owned by someone else -- wait till we can get it */ PR_Wait(signon_lock_monitor, PR_INTERVAL_NO_TIMEOUT); } } PRIVATE void si_unlock_signon_list(void) { PR_EnterMonitor(signon_lock_monitor); #ifdef DEBUG /* make sure someone doesn't try to free a lock they don't own */ PR_ASSERT(signon_lock_owner == PR_CurrentThread()); #endif signon_lock_count--; if(signon_lock_count == 0) { signon_lock_owner = NULL; PR_Notify(signon_lock_monitor); } PR_ExitMonitor(signon_lock_monitor); } /******************************** * Preference Utility Functions * ********************************/ typedef int (*PrefChangedFunc) (const char *, void *); PUBLIC void SI_RegisterCallback(const char* domain, PrefChangedFunc callback, void* instance_data) { nsresult ret; nsCOMPtr pPrefService = do_GetService(kPrefServiceCID, &ret); if (!NS_FAILED(ret)) { ret = pPrefService->RegisterCallback(domain, callback, instance_data); } } PUBLIC void SI_SetBoolPref(const char * prefname, PRBool prefvalue) { nsresult ret; nsCOMPtr pPrefService = do_GetService(kPrefServiceCID, &ret); if (!NS_FAILED(ret)) { ret = pPrefService->SetBoolPref(prefname, prefvalue); if (!NS_FAILED(ret)) { ret = pPrefService->SavePrefFile(); } } } PUBLIC PRBool SI_GetBoolPref(const char * prefname, PRBool defaultvalue) { nsresult ret; PRBool prefvalue = defaultvalue; nsCOMPtr pPrefService = do_GetService(kPrefServiceCID, &ret); if (!NS_FAILED(ret)) { ret = pPrefService->GetBoolPref(prefname, &prefvalue); } return prefvalue; } PUBLIC void SI_SetCharPref(const char * prefname, const char * prefvalue) { nsresult ret; nsCOMPtr pPrefService = do_GetService(kPrefServiceCID, &ret); if (!NS_FAILED(ret)) { ret = pPrefService->SetCharPref(prefname, prefvalue); if (!NS_FAILED(ret)) { ret = pPrefService->SavePrefFile(); } } } PUBLIC void SI_GetCharPref(const char * prefname, char** aPrefvalue) { nsresult ret; nsCOMPtr pPrefService = do_GetService(kPrefServiceCID, &ret); if (!NS_FAILED(ret)) { ret = pPrefService->CopyCharPref(prefname, aPrefvalue); if (NS_FAILED(ret)) { *aPrefvalue = nsnull; } } else { *aPrefvalue = nsnull; } } /********************************* * Preferences for Single Signon * *********************************/ static const char *pref_rememberSignons = "signon.rememberSignons"; #ifdef DefaultIsOff static const char *pref_Notified = "signon.Notified"; #endif static const char *pref_SignonFileName = "signon.SignonFileName"; PRIVATE PRBool si_RememberSignons = PR_FALSE; #ifdef DefaultIsOff PRIVATE PRBool si_Notified = PR_FALSE; #endif PRIVATE int si_SaveSignonDataLocked(); PUBLIC int SI_LoadSignonData(); PUBLIC void SI_RemoveAllSignonData(); #ifdef DefaultIsOff PRIVATE PRBool si_GetNotificationPref(void) { return si_Notified; } PRIVATE void si_SetNotificationPref(PRBool x) { SI_SetBoolPref(pref_Notified, x); si_Notified = x; } #endif PRIVATE void si_SetSignonRememberingPref(PRBool x) { #ifdef APPLE_KEYCHAIN if (x == 0) { /* We no longer need the Keychain callback installed */ KCRemoveCallback( si_kcUPP ); DisposeRoutineDescriptor( si_kcUPP ); si_kcUPP = NULL; } #endif si_RememberSignons = x; } MODULE_PRIVATE int PR_CALLBACK si_SignonRememberingPrefChanged(const char * newpref, void * data) { PRBool x; x = SI_GetBoolPref(pref_rememberSignons, PR_TRUE); si_SetSignonRememberingPref(x); return 0; /* this is PREF_NOERROR but we no longer include prefapi.h */ } PRIVATE void si_RegisterSignonPrefCallbacks(void) { PRBool x; static PRBool first_time = PR_TRUE; if(first_time) { first_time = PR_FALSE; SI_LoadSignonData(); #ifdef DefaultIsOff x = SI_GetBoolPref(pref_Notified, PR_FALSE); si_SetNotificationPref(x); #endif x = SI_GetBoolPref(pref_rememberSignons, PR_FALSE); si_SetSignonRememberingPref(x); SI_RegisterCallback(pref_rememberSignons, si_SignonRememberingPrefChanged, NULL); } } PRIVATE PRBool si_GetSignonRememberingPref(void) { #ifdef APPLE_KEYCHAIN /* If the Keychain has been locked or an item deleted or updated, * we need to reload the signon data */ if (si_list_invalid) { /* * set si_list_invalid to PR_FALSE first because SI_RemoveAllSignonData * calls si_GetSignonRememberingPref */ si_list_invalid = PR_FALSE; SI_LoadSignonData(); } #endif si_RegisterSignonPrefCallbacks(); #ifdef DefaultIsOff /* * We initially want the rememberSignons pref to be PR_FALSE. But this will * prevent the notification message from ever occurring. To get around * this problem, if the signon pref is PR_FALSE and no notification has * ever been given, we will treat this as if the signon pref were PR_TRUE. */ if (!si_RememberSignons && !si_GetNotificationPref()) { return PR_TRUE; } else { return si_RememberSignons; } #else return si_RememberSignons; #endif } extern char* Wallet_RandomName(char* suffix); PUBLIC void SI_InitSignonFileName() { SI_GetCharPref(pref_SignonFileName, &signonFileName); if (!signonFileName) { signonFileName = Wallet_RandomName("s"); SI_SetCharPref(pref_SignonFileName, signonFileName); } } /******************** * Utility Routines * ********************/ /* StrAllocCopy should really be defined elsewhere */ #include "plstr.h" #include "prmem.h" #undef StrAllocCopy #define StrAllocCopy(dest, src) Local_SACopy (&(dest), src) PRIVATE char * Local_SACopy(char **destination, const char *source) { if(*destination) { PL_strfree(*destination); *destination = 0; } *destination = PL_strdup(source); return *destination; } /* Remove misleading portions from URL name */ const char* empty = "empty"; PRIVATE char* si_StrippedURL (const char* URLName) { char *result = 0; char *s, *t; /* check for null URLName */ if (URLName == NULL || PL_strlen(URLName) == 0) { return NULL; } /* remove protocol */ s = (char*) PL_strchr(URLName +1, '/'); if (s && *s == '/' && *(s+1) == '/') { s += 2; } if (s) { StrAllocCopy(result, s); } else { StrAllocCopy(result, URLName); } /* remove everything after hostname */ s = result; s = (char*) PL_strchr(s, '/'); if (s) { *s = '\0'; } /* remove everything after and including first question mark */ s = result; s = (char*) PL_strchr(s, '?'); if (s) { *s = '\0'; } /* remove socket number from result */ s = result; while ((s = (char*) PL_strchr(s+1, ':'))) { /* s is at next colon */ if (*(s+1) != '/') { /* and it's not :// so it must be start of socket number */ if ((t = (char*) PL_strchr(s+1, '/'))) { /* t is at slash terminating the socket number */ do { /* copy remainder of t to s */ *s++ = *t; } while (*(t++)); } break; } } /* all done */ if (PL_strlen(result)) { return result; } else { return PL_strdup(empty); } } /* remove terminating CRs or LFs */ PRIVATE void si_StripLF(nsAutoString buffer) { PRUnichar c; while ((c=buffer.CharAt(buffer.Length())-1) == '\n' || c == '\r' ) { buffer.SetLength(buffer.Length()-1); } } /* If user-entered password is "********", then generate a random password */ PRIVATE void si_Randomize(nsAutoString& password) { PRIntervalTime randomNumber; int i; const char * hexDigits = "0123456789AbCdEf"; if (password == NS_ConvertToString("********")) { randomNumber = PR_IntervalNow(); for (i=0; i<8; i++) { password.SetCharAt(hexDigits[randomNumber%16], i); randomNumber = randomNumber/16; } } } /*********************** * Encryption Routines * ***********************/ // don't understand why linker doesn't let me call Wallet_Encrypt and Wallet_Decrypt // directly but it doesn't. Need to introduce Wallet_Enctrypt2 and Wallet_Decrypt2 instead. extern nsresult Wallet_Encrypt2 (nsAutoString text, nsAutoString& crypt); extern nsresult Wallet_Decrypt2 (nsAutoString crypt, nsAutoString& text); nsresult si_Encrypt (nsAutoString text, nsAutoString& crypt) { return Wallet_Encrypt2(text, crypt); } nsresult si_Decrypt (nsAutoString crypt, nsAutoString& text) { return Wallet_Decrypt2(crypt, text); } PRBool si_CompareEncryptedToCleartext(const nsAutoString crypt, const nsAutoString text) { nsAutoString decrypted; if (NS_FAILED(si_Decrypt(crypt, decrypted))) { return PR_FALSE; } return (decrypted == text); } PRBool si_CompareEncryptedToEncrypted(const nsAutoString crypt1, const nsAutoString crypt2) { nsAutoString decrypted1; nsAutoString decrypted2; if (NS_FAILED(si_Decrypt(crypt1, decrypted1))) { return PR_FALSE; } if (NS_FAILED(si_Decrypt(crypt2, decrypted2))) { return PR_FALSE; } return (decrypted1 == decrypted2); } /************************ * Managing Signon List * ************************/ class si_SignonDataStruct { public: si_SignonDataStruct() : isPassword(PR_FALSE) {} nsAutoString name; nsAutoString value; PRBool isPassword; }; class si_SignonUserStruct { public: si_SignonUserStruct() : signonData_list(NULL) {} nsVoidArray * signonData_list; }; class si_SignonURLStruct { public: si_SignonURLStruct() : URLName(NULL), chosen_user(NULL), signonUser_list(NULL) {} char * URLName; si_SignonUserStruct* chosen_user; /* this is a state variable */ nsVoidArray * signonUser_list; }; class si_Reject { public: si_Reject() : URLName(NULL) {} char * URLName; nsAutoString userName; }; //typedef struct _SignonDataStruct { // nsAutoString name; // nsAutoString value; // PRBool isPassword; //} si_SignonDataStruct; //typedef struct _SignonUserStruct { // nsVoidArray * signonData_list; //} si_SignonUserStruct; //typedef struct _SignonURLStruct { // char * URLName; // si_SignonUserStruct* chosen_user; /* this is a state variable */ // nsVoidArray * signonUser_list; //} si_SignonURLStruct; //typedef struct _RejectStruct { // char * URLName; // nsAutoString userName; //} si_Reject; PRIVATE nsVoidArray * si_signon_list=0; PRIVATE nsVoidArray * si_reject_list=0; #define LIST_COUNT(list) (list ? list->Count() : 0) PRIVATE PRBool si_signon_list_changed = PR_FALSE; /* * Get the URL node for a given URL name * * This routine is called only when holding the signon lock!!! */ PRIVATE si_SignonURLStruct * si_GetURL(const char * URLName) { si_SignonURLStruct * url; char *strippedURLName = 0; if (!URLName) { /* no URLName specified, return first URL (returns NULL if not URLs) */ if (LIST_COUNT(si_signon_list)==0) { return NULL; } return (si_SignonURLStruct *) (si_signon_list->ElementAt(0)); } strippedURLName = si_StrippedURL(URLName); PRInt32 urlCount = LIST_COUNT(si_signon_list); for (PRInt32 i=0; iElementAt(i)); if(url->URLName && !PL_strcmp(strippedURLName, url->URLName)) { PR_Free(strippedURLName); return url; } } PR_Free(strippedURLName); return (NULL); } static nsresult MangleUrl(const char *url, char **result) { if (!url || !result) return NS_ERROR_FAILURE; nsCAutoString temp(url); temp.ReplaceSubstring("@","-"); temp.ReplaceSubstring("/","-"); temp.ReplaceSubstring(":","-"); temp.ReplaceSubstring("#","-"); temp.ReplaceSubstring("&","-"); temp.ReplaceSubstring("?","-"); temp.ReplaceSubstring(".","-"); *result = PL_strdup((const char *)temp); #ifdef DEBUG_sspitzer printf("mangling %s into %s\n", url, *result); #endif return NS_OK; } /* Remove a user node from a given URL node */ PRIVATE PRBool si_RemoveUser(const char *URLName, nsAutoString userName, PRBool save, PRBool strip) { nsresult res; si_SignonURLStruct * url; si_SignonUserStruct * user; si_SignonDataStruct * data; /* do nothing if signon preference is not enabled */ if (!si_GetSignonRememberingPref()) { return PR_FALSE; } /* convert URLName to a uri so we can parse out the username and hostname */ nsXPIDLCString host; if (strip) { if (URLName) { nsCOMPtr uri; nsComponentManager::CreateInstance(kStandardUrlCID, nsnull, NS_GET_IID(nsIURL), (void **) getter_AddRefs(uri)); res = uri->SetSpec(URLName); if (NS_FAILED(res)) return PR_FALSE; /* uri is of the form ://:@:/) */ /* get host part of the uri */ res = uri->GetHost(getter_Copies(host)); if (NS_FAILED(res)) { return PR_FALSE; } /* if no username given, extract it from uri -- note: prehost is : */ if (userName.Length() == 0) { nsXPIDLCString userName2; res = uri->GetPreHost(getter_Copies(userName2)); if (NS_FAILED(res)) { return PR_FALSE; } if ((const char *)userName2 && (PL_strlen((const char *)userName2))) { userName = NS_ConvertToString((const char *)userName2); PRInt32 colon = userName.FindChar(':'); if (colon != -1) { userName.Truncate(colon); } } } } } else { res = MangleUrl(URLName, getter_Copies(host)); if (NS_FAILED(res)) return PR_FALSE; } si_lock_signon_list(); /* get URL corresponding to host */ url = si_GetURL((const char *)host); if (!url) { /* URL not found */ si_unlock_signon_list(); return PR_FALSE; } /* free the data in each node of the specified user node for this URL */ if (userName.Length() == 0) { /* no user specified so remove the first one */ user = (si_SignonUserStruct *) (url->signonUser_list->ElementAt(0)); } else { /* find the specified user */ PRInt32 userCount = LIST_COUNT(url->signonUser_list); for (PRInt32 i=0; isignonUser_list->ElementAt(i)); PRInt32 dataCount = LIST_COUNT(user->signonData_list); for (PRInt32 ii=0; iisignonData_list->ElementAt(ii)); if (si_CompareEncryptedToCleartext(data->value, userName)) { goto foundUser; } } } si_unlock_signon_list(); return PR_FALSE; /* user not found so nothing to remove */ foundUser: ; } /* free the data list */ delete user->signonData_list; /* free the user node */ url->signonUser_list->RemoveElement(user); /* remove this URL if it contains no more users */ if (LIST_COUNT(url->signonUser_list) == 0) { PR_Free(url->URLName); si_signon_list->RemoveElement(url); } /* write out the change to disk */ if (save) { si_signon_list_changed = PR_TRUE; si_SaveSignonDataLocked(); } si_unlock_signon_list(); return PR_TRUE; } PUBLIC PRBool SINGSIGN_RemoveUser(const char *URLName, const PRUnichar *userName, PRBool strip) { return si_RemoveUser(URLName, nsAutoString(userName), PR_TRUE, strip); } /* Determine if a specified url/user exists */ PRIVATE PRBool si_CheckForUser(char *URLName, nsAutoString userName) { si_SignonURLStruct * url; si_SignonUserStruct * user; si_SignonDataStruct * data; /* do nothing if signon preference is not enabled */ if (!si_GetSignonRememberingPref()) { return PR_FALSE; } si_lock_signon_list(); /* get URL corresponding to URLName */ url = si_GetURL(URLName); if (!url) { /* URL not found */ si_unlock_signon_list(); return PR_FALSE; } /* find the specified user */ PRInt32 userCount = LIST_COUNT(url->signonUser_list); for (PRInt32 i=0; isignonUser_list->ElementAt(i)); PRInt32 dataCount = LIST_COUNT(user->signonData_list); for (PRInt32 ii=0; iisignonData_list->ElementAt(ii)); if (si_CompareEncryptedToCleartext(data->value, userName)) { si_unlock_signon_list(); return PR_TRUE; } } } si_unlock_signon_list(); return PR_FALSE; /* user not found */ } /* * Get the user node for a given URL * * This routine is called only when holding the signon lock!!! * * This routine is called only if signon pref is enabled!!! */ PRIVATE si_SignonUserStruct* si_GetUser(const char* URLName, PRBool pickFirstUser, nsAutoString userText) { si_SignonURLStruct* url; si_SignonUserStruct* user = nsnull; si_SignonDataStruct* data; /* get to node for this URL */ url = si_GetURL(URLName); if (url != NULL) { /* node for this URL was found */ PRInt32 user_count; if ((user_count = LIST_COUNT(url->signonUser_list)) == 1) { /* only one set of data exists for this URL so select it */ user = (si_SignonUserStruct *) (url->signonUser_list->ElementAt(0)); url->chosen_user = user; } else if (pickFirstUser) { PRInt32 userCount = LIST_COUNT(url->signonUser_list); for (PRInt32 i=0; isignonUser_list->ElementAt(i)); /* consider first data node to be the identifying item */ data = (si_SignonDataStruct *) (user->signonData_list->ElementAt(0)); if (data->name != userText) { /* name of current data item does not match name in data node */ continue; } break; } url->chosen_user = user; } else { /* multiple users for this URL so a choice needs to be made */ PRUnichar ** list; PRUnichar ** list2; si_SignonUserStruct** users; si_SignonUserStruct** users2; list = (PRUnichar**)PR_Malloc(user_count*sizeof(PRUnichar*)); users = (si_SignonUserStruct **) PR_Malloc(user_count*sizeof(si_SignonUserStruct*)); list2 = list; users2 = users; /* step through set of user nodes for this URL and create list of * first data node of each (presumably that is the user name). * Note that the user nodes are already ordered by * most-recently-used so the first one in the list is the most * likely one to be chosen. */ user_count = 0; PRInt32 userCount = LIST_COUNT(url->signonUser_list); for (PRInt32 i=0; isignonUser_list->ElementAt(i)); /* consider first data node to be the identifying item */ data = (si_SignonDataStruct *) (user->signonData_list->ElementAt(0)); if (data->name != userText) { /* name of current data item does not match name in data node */ continue; } nsAutoString userName; if (NS_SUCCEEDED(si_Decrypt (data->value, userName))) { *(list2++) = userName.ToNewUnicode(); *(users2++) = user; user_count++; } else { break; } } /* have user select a username from the list */ PRUnichar * selectUser = Wallet_Localize("SelectUser"); if (user_count == 0) { /* not first data node for any saved user, so simply pick first user */ if (url->chosen_user) { user = url->chosen_user; } else { /* no user selection had been made for first data node */ user = NULL; } } else if (user_count == 1) { /* only one user for this form at this url, so select it */ user = users[0]; } else if ((user_count > 1) && si_SelectDialog(selectUser, list, &user_count)) { /* user pressed OK */ if (user_count == -1) { user_count = 0; /* user didn't select, so use first one */ } user = users[user_count]; /* this is the selected item */ /* item selected is now most-recently used, put at head of list */ url->signonUser_list->RemoveElement(user); url->signonUser_list->InsertElementAt(user, 0); } else { user = NULL; } Recycle(selectUser); url->chosen_user = user; while (--list2 > list) { Recycle(*list2); } PR_Free(list); PR_Free(users); /* if we don't remove the URL from the cache at this point, the * cached copy will be brought containing the last-used username * rather than the username that was just selected */ #ifdef junk NET_RemoveURLFromCache(NET_CreateURLStruct((char *)URLName, NET_DONT_RELOAD)); #endif } } else { user = NULL; } return user; } /* * Get a specific user node for a given URL * * This routine is called only when holding the signon lock!!! * * This routine is called only if signon pref is enabled!!! */ PRIVATE si_SignonUserStruct* si_GetSpecificUser(const char* URLName, nsAutoString userName, nsAutoString userText) { si_SignonURLStruct* url; si_SignonUserStruct* user; si_SignonDataStruct* data; /* get to node for this URL */ url = si_GetURL(URLName); if (url != NULL) { /* step through set of user nodes for this URL looking for specified username */ PRInt32 userCount2 = LIST_COUNT(url->signonUser_list); for (PRInt32 i2=0; i2signonUser_list->ElementAt(i2)); /* consider first data node to be the identifying item */ data = (si_SignonDataStruct *) (user->signonData_list->ElementAt(0)); if (data->name != userText) { /* desired username text does not match name in data node */ continue; } if (!si_CompareEncryptedToCleartext(data->value, userName)) { /* desired username value does not match value in data node */ continue; } return user; } /* if we don't remove the URL from the cache at this point, the * cached copy will be brought containing the last-used username * rather than the username that was just selected */ #ifdef junk NET_RemoveURLFromCache(NET_CreateURLStruct((char *)URLName, NET_DONT_RELOAD)); #endif } return NULL; } /* * Get the url and user for which a change-of-password is to be applied * * This routine is called only when holding the signon lock!!! * * This routine is called only if signon pref is enabled!!! */ PRIVATE si_SignonUserStruct* si_GetURLAndUserForChangeForm(nsAutoString password) { si_SignonURLStruct* url; si_SignonUserStruct* user; si_SignonDataStruct * data; PRInt32 user_count; PRUnichar ** list; PRUnichar ** list2; si_SignonUserStruct** users; si_SignonUserStruct** users2; si_SignonURLStruct** urls; si_SignonURLStruct** urls2; /* get count of total number of user nodes at all url nodes */ user_count = 0; PRInt32 urlCount = LIST_COUNT(si_signon_list); for (PRInt32 i=0; iElementAt(i)); PRInt32 userCount = LIST_COUNT(url->signonUser_list); for (PRInt32 ii=0; iisignonUser_list->ElementAt(ii)); user_count++; } } /* allocate lists for maximumum possible url and user names */ list = (PRUnichar**)PR_Malloc(user_count*sizeof(PRUnichar*)); users = (si_SignonUserStruct **) PR_Malloc(user_count*sizeof(si_SignonUserStruct*)); urls = (si_SignonURLStruct **)PR_Malloc(user_count*sizeof(si_SignonUserStruct*)); list2 = list; users2 = users; urls2 = urls; /* step through set of URLs and users and create list of each */ user_count = 0; PRInt32 urlCount2 = LIST_COUNT(si_signon_list); for (PRInt32 i2=0; i2ElementAt(i2)); PRInt32 userCount = LIST_COUNT(url->signonUser_list); for (PRInt32 i3=0; i3signonUser_list->ElementAt(i3)); /* find saved password and see if it matches password user just entered */ PRInt32 dataCount = LIST_COUNT(user->signonData_list); for (PRInt32 i4=0; i4signonData_list->ElementAt(i4)); if (data->isPassword && si_CompareEncryptedToCleartext(data->value, password)) { /* passwords match so add entry to list */ /* consider first data node to be the identifying item */ data = (si_SignonDataStruct *) (user->signonData_list->ElementAt(0)); nsAutoString userName; if (NS_SUCCEEDED(si_Decrypt (data->value, userName))) { nsAutoString temp; temp.AssignWithConversion(url->URLName); temp.AppendWithConversion(":"); temp.Append(userName); *list2 = temp.ToNewUnicode(); list2++; *(users2++) = user; *(urls2++) = url; user_count++; } break; } } } } /* query user */ PRUnichar * msg = Wallet_Localize("SelectUserWhosePasswordIsBeingChanged"); if (user_count && si_SelectDialog(msg, list, &user_count)) { user = users[user_count]; url = urls[user_count]; /* * since this user node is now the most-recently-used one, move it * to the head of the user list so that it can be favored for * re-use the next time this form is encountered */ url->signonUser_list->RemoveElement(user); url->signonUser_list->InsertElementAt(user, 0); si_signon_list_changed = PR_TRUE; si_SaveSignonDataLocked(); } else { user = NULL; } Recycle(msg); /* free allocated strings */ while (--list2 > list) { Recycle(*list2); } PR_Free(list); PR_Free(users); PR_Free(urls); return user; } PRIVATE void si_FreeReject(si_Reject * reject); /* * Remove all the signons and free everything */ PUBLIC void SI_RemoveAllSignonData() { if (si_PartiallyLoaded) { /* repeatedly remove first user node of first URL node */ while (si_RemoveUser(NULL, nsAutoString(), PR_FALSE, PR_TRUE)) { } } si_PartiallyLoaded = PR_FALSE; si_Reject * reject; while (LIST_COUNT(si_reject_list)>0) { reject = NS_STATIC_CAST(si_Reject*, si_reject_list->ElementAt(0)); if (reject) { si_FreeReject(reject); si_signon_list_changed = PR_TRUE; } } } /**************************** * Managing the Reject List * ****************************/ PRIVATE void si_FreeReject(si_Reject * reject) { /* * This routine should only be called while holding the * signon list lock */ if(!reject) { return; } si_reject_list->RemoveElement(reject); PR_FREEIF(reject->URLName); PR_Free(reject); } PRIVATE PRBool si_CheckForReject(char * URLName, nsAutoString userName) { si_Reject * reject; si_lock_signon_list(); if (si_reject_list) { PRInt32 rejectCount = LIST_COUNT(si_reject_list); for (PRInt32 i=0; iElementAt(i)); if(!PL_strcmp(URLName, reject->URLName)) { // No need for username check on a rejectlist entry. URL check is sufficient // if(!PL_strcmp(userName, reject->userName) && !PL_strcmp(URLName, reject->URLName)) { si_unlock_signon_list(); return PR_TRUE; } } } si_unlock_signon_list(); return PR_FALSE; } PRIVATE void si_PutReject(char * URLName, nsAutoString userName, PRBool save) { char * URLName2=NULL; nsAutoString userName2; si_Reject * reject = new si_Reject; if (reject) { /* * lock the signon list * Note that, for efficiency, SI_LoadSignonData already sets the lock * before calling this routine whereas none of the other callers do. * So we need to determine whether or not we were called from * SI_LoadSignonData before setting or clearing the lock. We can * determine this by testing "save" since only SI_LoadSignonData * passes in a value of PR_FALSE for "save". */ if (save) { si_lock_signon_list(); } StrAllocCopy(URLName2, URLName); userName2 = userName; reject->URLName = URLName2; reject->userName = userName2; if(!si_reject_list) { si_reject_list = new nsVoidArray(); if(!si_reject_list) { PR_Free(reject->URLName); PR_Free(reject); if (save) { si_unlock_signon_list(); } return; } } #ifdef alphabetize /* add it to the list in alphabetical order */ si_Reject * tmp_reject; PRBool rejectAdded = PR_FALSE; PRInt32 rejectCount = LIST_COUNT(si_reject_list); for (PRInt32 i = 0; iElementAt(i)); if (tmp_reject) { if (PL_strcasecmp(reject->URLName, tmp_reject->URLName)<0) { si_reject_list->InsertElementAt(reject, i); rejectAdded = PR_TRUE; break; } } } if (!rejectAdded) { si_reject_list->AppendElement(reject); } #else /* add it to the end of the list */ si_reject_list->AppendElement(reject); #endif if (save) { si_signon_list_changed = PR_TRUE; si_lock_signon_list(); si_SaveSignonDataLocked(); si_unlock_signon_list(); } } } /* * Put data obtained from a submit form into the data structure for * the specified URL * * See comments below about state of signon lock when routine is called!!! * * This routine is called only if signon pref is enabled!!! */ PRIVATE void si_PutData(const char * URLName, nsVoidArray * signonData, PRBool save) { PRBool added_to_list = PR_FALSE; si_SignonURLStruct * url; si_SignonUserStruct * user; si_SignonDataStruct * data; si_SignonDataStruct * data2; PRBool mismatch = PR_FALSE; /* discard this if the password is empty */ PRInt32 count = signonData->Count(); for (PRInt32 i=0; iElementAt(i)); if (data2->isPassword && data2->value.Length()==0) { return; } } /* * lock the signon list * Note that, for efficiency, SI_LoadSignonData already sets the lock * before calling this routine whereas none of the other callers do. * So we need to determine whether or not we were called from * SI_LoadSignonData before setting or clearing the lock. We can * determine this by testing "save" since only SI_LoadSignonData passes * in a value of PR_FALSE for "save". */ if (save) { si_lock_signon_list(); } /* make sure the signon list exists */ if(!si_signon_list) { si_signon_list = new nsVoidArray(); if(!si_signon_list) { if (save) { si_unlock_signon_list(); } return; } } /* find node in signon list having the same URL */ if ((url = si_GetURL(URLName)) == NULL) { /* doesn't exist so allocate new node to be put into signon list */ url = new si_SignonURLStruct; if (!url) { if (save) { si_unlock_signon_list(); } return; } /* fill in fields of new node */ url->URLName = si_StrippedURL(URLName); if (!url->URLName) { PR_Free(url); if (save) { si_unlock_signon_list(); } return; } url->signonUser_list = new nsVoidArray(); if(!url->signonUser_list) { PR_Free(url->URLName); PR_Free(url); } /* put new node into signon list */ #ifdef alphabetize /* add it to the list in alphabetical order */ si_SignonURLStruct * tmp_URL; PRInt32 urlCount = LIST_COUNT(si_signon_list); for (PRInt32 ii = 0; iiElementAt(ii)); if (tmp_URL) { if (PL_strcasecmp(url->URLName, tmp_URL->URLName)<0) { si_signon_list->InsertElementAt(url, ii); added_to_list = PR_TRUE; break; } } } if (!added_to_list) { si_signon_list->AppendElement(url); } #else /* add it to the end of the list */ si_signon_list->AppendElement(url); #endif } /* initialize state variables in URL node */ url->chosen_user = NULL; /* * see if a user node with data list matching new data already exists * (password fields will not be checked for in this matching) */ PRInt32 userCount = LIST_COUNT(url->signonUser_list); for (PRInt32 i2=0; i2signonUser_list->ElementAt(i2)); PRInt32 j = 0; PRInt32 dataCount = LIST_COUNT(user->signonData_list); for (PRInt32 i3=0; i3signonData_list->ElementAt(i3)); mismatch = PR_FALSE; /* check for match on name field and type field */ if (j < signonData->Count()) { data2 = NS_STATIC_CAST(si_SignonDataStruct*, signonData->ElementAt(j)); if ((data->isPassword == data2->isPassword) && (data->name == data2->name)) { /* success, now check for match on value field if not password */ if (si_CompareEncryptedToEncrypted(data->value, data2->value) || data->isPassword) { j++; /* success */ } else { mismatch = PR_TRUE; break; /* value mismatch, try next user */ } } else { mismatch = PR_TRUE; break; /* name or type mismatch, try next user */ } } } if (!mismatch) { /* all names and types matched and all non-password values matched */ /* * note: it is ok for password values not to match; it means * that the user has either changed his password behind our * back or that he previously mis-entered the password */ /* update the saved password values */ j = 0; PRInt32 dataCount2 = LIST_COUNT(user->signonData_list); for (PRInt32 i4=0; i4signonData_list->ElementAt(i4)); /* update saved password */ if ((j < signonData->Count()) && data->isPassword) { data2 = NS_STATIC_CAST(si_SignonDataStruct*, signonData->ElementAt(j)); if (!si_CompareEncryptedToEncrypted(data->value, data2->value)) { si_signon_list_changed = PR_TRUE; data->value = data2->value; /* commenting out because I don't see how such randomizing could ever have worked. */ // si_Randomize(data->value); } } j++; } /* * since this user node is now the most-recently-used one, move it * to the head of the user list so that it can be favored for * re-use the next time this form is encountered */ url->signonUser_list->RemoveElement(user); url->signonUser_list->InsertElementAt(user, 0); /* return */ if (save) { si_signon_list_changed = PR_TRUE; si_SaveSignonDataLocked(); si_unlock_signon_list(); } return; /* nothing more to do since data already exists */ } } /* user node with current data not found so create one */ user = new si_SignonUserStruct; if (!user) { if (save) { si_unlock_signon_list(); } return; } user->signonData_list = new nsVoidArray(); if(!user->signonData_list) { PR_Free(user); if (save) { si_unlock_signon_list(); } return; } /* create and fill in data nodes for new user node */ for (PRInt32 k=0; kCount(); k++) { /* create signon data node */ data = new si_SignonDataStruct; if (!data) { delete user->signonData_list; PR_Free(user); if (save) { si_unlock_signon_list(); } return; } data2 = NS_STATIC_CAST(si_SignonDataStruct*, signonData->ElementAt(k)); data->isPassword = data2->isPassword; data->name = data2->name; data->value = data2->value; /* commenting out because I don't see how such randomizing could ever have worked. */ // if (data->isPassword) { // si_Randomize(data->value); // } /* append new data node to end of data list */ user->signonData_list->AppendElement(data); } /* append new user node to front of user list for matching URL */ /* * Note that by appending to the front, we assure that if there are * several users, the most recently used one will be favored for * reuse the next time this form is encountered. But don't do this * when reading in the saved signons (i.e., when save is PR_FALSE), otherwise * we will be reversing the order when reading in. */ if (save) { url->signonUser_list->InsertElementAt(user, 0); si_signon_list_changed = PR_TRUE; si_SaveSignonDataLocked(); si_unlock_signon_list(); } else { url->signonUser_list->AppendElement(user); } } /***************************** * Managing the Signon Files * *****************************/ #define HEADER_VERSION_2a "#2a" extern void Wallet_UTF8Put(nsOutputFileStream strm, PRUnichar c); extern PRUnichar Wallet_UTF8Get(nsInputFileStream strm); #define BUFFER_SIZE 4096 /* * get a line from a file * return -1 if end of file reached * strip carriage returns and line feeds from end of line */ PRIVATE PRInt32 si_ReadLine (nsInputFileStream strm, nsAutoString& lineBuffer) { lineBuffer.SetLength(0); /* read the line */ PRUnichar c; for (;;) { c = Wallet_UTF8Get(strm); /* note that eof is not set until we read past the end of the file */ if (strm.eof()) { return -1; } if (c == '\n') { break; } if (c != '\r') { lineBuffer += c; } } return 0; } /* * Load signon data from disk file * Return value is: * -1: fatal error * 0: successfully load * +1: user aborted the load (by failing to open the database) */ PUBLIC int SI_LoadSignonData() { char * URLName; nsAutoString buffer; PRBool badInput = PR_FALSE; #ifdef APPLE_KEYCHAIN if (KeychainManagerAvailable()) { SI_RemoveAllSignonData(); return si_LoadSignonDataFromKeychain(); } #endif /* open the signon file */ nsFileSpec dirSpec; nsresult rv = Wallet_ProfileDirectory(dirSpec); if (NS_FAILED(rv)) { return -1; } SI_InitSignonFileName(); nsInputFileStream strm(dirSpec+signonFileName); if (!strm.is_open()) { return 0; } SI_RemoveAllSignonData(); /* read the format information */ nsAutoString format; if (NS_FAILED(si_ReadLine(strm, format))) { return -1; } if (!format.EqualsWithConversion(HEADER_VERSION_2a)) { /* something's wrong */ return -1; } /* read the reject list */ si_lock_signon_list(); while (!NS_FAILED(si_ReadLine(strm, buffer))) { if (buffer.CharAt(0) == '.') { break; /* end of reject list */ } si_StripLF(buffer); URLName = buffer.ToNewCString(); si_PutReject(URLName, buffer, PR_FALSE); /* middle parameter is obsolete */ Recycle (URLName); } /* read the URL line */ while(!NS_FAILED(si_ReadLine(strm, buffer))) { si_StripLF(buffer); URLName = buffer.ToNewCString(); /* prepare to read the name/value pairs */ badInput = PR_FALSE; nsVoidArray * signonData = new nsVoidArray(); si_SignonDataStruct * data; while(!NS_FAILED(si_ReadLine(strm, buffer))) { /* line starting with . terminates the pairs for this URL entry */ if (buffer.CharAt(0) == '.') { break; /* end of URL entry */ } /* line just read is the name part */ /* save the name part and determine if it is a password */ PRBool ret; si_StripLF(buffer); nsAutoString name; nsAutoString value; PRBool isPassword; if (buffer.CharAt(0) == '*') { isPassword = PR_TRUE; buffer.Mid(name, 1, buffer.Length()-1); ret = si_ReadLine(strm, buffer); } else { isPassword = PR_FALSE; name = buffer; ret = si_ReadLine(strm, buffer); } /* read in and save the value part */ if(NS_FAILED(ret)) { /* error in input file so give up */ badInput = PR_TRUE; break; } si_StripLF(buffer); value = buffer; data = new si_SignonDataStruct; data->name = name; data->value = value; data->isPassword = isPassword; signonData->AppendElement(data); } /* store the info for this URL into memory-resident data structure */ if (!URLName || PL_strlen(URLName) == 0) { badInput = PR_TRUE; } if (!badInput) { si_PutData(URLName, signonData, PR_FALSE); } /* free up all the allocations done for processing this URL */ Recycle(URLName); if (badInput) { si_unlock_signon_list(); return -1; } PRInt32 count = signonData->Count(); for (PRInt32 i=count-1; i>=0; i--) { data = NS_STATIC_CAST(si_SignonDataStruct*, signonData->ElementAt(i)); delete data; } delete signonData; } si_unlock_signon_list(); si_PartiallyLoaded = PR_TRUE; return 0; } /* * Save signon data to disk file * The parameter passed in on entry is ignored * * This routine is called only when holding the signon lock!!! * * This routine is called only if signon pref is enabled!!! */ PRIVATE void si_WriteChar(nsOutputFileStream strm, PRUnichar c) { Wallet_UTF8Put(strm, c); } PRIVATE void si_WriteLine(nsOutputFileStream strm, nsAutoString lineBuffer) { for (PRUint32 i=0; iElementAt(i)); si_WriteLine(strm, NS_ConvertToString(reject->URLName)); } } si_WriteLine(strm, NS_ConvertToString(".")); /* format for cached logins shall be: * url LINEBREAK {name LINEBREAK value LINEBREAK}* . LINEBREAK * if type is password, name is preceded by an asterisk (*) */ /* write out each URL node */ if((si_signon_list)) { PRInt32 urlCount = LIST_COUNT(si_signon_list); for (PRInt32 i2=0; i2ElementAt(i2)); /* write out each user node of the URL node */ PRInt32 userCount = LIST_COUNT(url->signonUser_list); for (PRInt32 i3=0; i3signonUser_list->ElementAt(i3)); si_WriteLine (strm, NS_ConvertToString(url->URLName)); /* write out each data node of the user node */ PRInt32 dataCount = LIST_COUNT(user->signonData_list); for (PRInt32 i4=0; i4signonData_list->ElementAt(i4)); if (data->isPassword) { si_WriteChar(strm, '*'); } si_WriteLine(strm, nsAutoString(data->name)); si_WriteLine(strm, nsAutoString(data->value)); } si_WriteLine(strm, NS_ConvertToString(".")); } } } si_signon_list_changed = PR_FALSE; strm.flush(); strm.close(); return 0; } /*************************** * Processing Signon Forms * ***************************/ /* Ask user if it is ok to save the signon data */ PRIVATE PRBool si_OkToSave(char *URLName, nsAutoString userName) { char *strippedURLName = 0; /* if url/user already exists, then it is safe to save it again */ if (si_CheckForUser(URLName, userName)) { return PR_TRUE; } #ifdef DefaultIsOff if (!si_RememberSignons && !si_GetNotificationPref()) { PRUnichar * notification = Wallet_Localize("PasswordNotification"); si_SetNotificationPref(PR_TRUE); if (!si_ConfirmYN(notification)) { Recycle(notification); SI_SetBoolPref(pref_rememberSignons, PR_FALSE); return PR_FALSE; } Recycle(notification); SI_SetBoolPref(pref_rememberSignons, PR_TRUE); } #endif strippedURLName = si_StrippedURL(URLName); if (si_CheckForReject(strippedURLName, userName)) { PR_Free(strippedURLName); return PR_FALSE; } PRUnichar * message = Wallet_Localize("WantToSavePassword?"); PRInt32 button; if ((button = si_3ButtonConfirm(message)) != 1) { if (button == -1) { si_PutReject(strippedURLName, userName, PR_TRUE); } PR_Free(strippedURLName); Recycle(message); return PR_FALSE; } Recycle(message); PR_Free(strippedURLName); return PR_TRUE; } /* * Check for a signon submission and remember the data if so */ PUBLIC void SINGSIGN_RememberSignonData (char* URLName, nsVoidArray * signonData) { int passwordCount = 0; int pswd[3]; si_SignonDataStruct * data; si_SignonDataStruct * data0; si_SignonDataStruct * data1; si_SignonDataStruct * data2; /* do nothing if signon preference is not enabled */ if (!si_GetSignonRememberingPref()){ return; } /* determine how many passwords are in the form and where they are */ for (PRInt32 i=0; iCount(); i++) { data = NS_STATIC_CAST(si_SignonDataStruct*, signonData->ElementAt(i)); if (data->isPassword) { if (passwordCount < 3 ) { pswd[passwordCount] = i; } passwordCount++; } } /* process the form according to how many passwords it has */ if (passwordCount == 1) { /* one-password form is a log-in so remember it */ /* obtain the index of the first input field (that is the username) */ PRInt32 j; for (j=0; jCount(); j++) { data = NS_STATIC_CAST(si_SignonDataStruct*, signonData->ElementAt(j)); if (!data->isPassword) { break; } } if (jCount()) { data2 = NS_STATIC_CAST(si_SignonDataStruct*, signonData->ElementAt(j)); nsAutoString userName; if (NS_SUCCEEDED(si_Decrypt (data->value, userName))) { if (si_OkToSave(URLName, userName)) { si_PutData(URLName, signonData, PR_TRUE); } } } } else if (passwordCount == 2) { /* two-password form is a registration */ } else if (passwordCount == 3) { /* three-password form is a change-of-password request */ si_SignonUserStruct* user; /* make sure all passwords are non-null and 2nd and 3rd are identical */ data0 = NS_STATIC_CAST(si_SignonDataStruct*, signonData->ElementAt(pswd[0])); data1 = NS_STATIC_CAST(si_SignonDataStruct*, signonData->ElementAt(pswd[1])); data2 = NS_STATIC_CAST(si_SignonDataStruct*, signonData->ElementAt(pswd[2])); if (data0->value.Length() == 0 || data1->value.Length() == 0 || data2->value.Length() == 0 || !si_CompareEncryptedToEncrypted(data1->value, data2->value)) { return; } /* ask user if this is a password change */ nsAutoString password; if (NS_FAILED(si_Decrypt (data0->value, password))) { return; } si_lock_signon_list(); user = si_GetURLAndUserForChangeForm(password); /* return if user said no */ if (!user) { si_unlock_signon_list(); return; } /* get to password being saved */ PRInt32 dataCount = LIST_COUNT(user->signonData_list); for (PRInt32 k=0; ksignonData_list->ElementAt(k)); if (data->isPassword) { break; } } /* * if second password is "********" then generate a random * password for it and use same random value for third password * as well (Note: this all works because we already know that * second and third passwords are identical so third password * must also be "********". Furthermore si_Randomize() will * create a random password of exactly eight characters -- the * same length as "********".) */ /* commenting out because I don't see how such randomizing could ever have worked. */ // si_Randomize(data1->value); // data2->value = data1->value; data->value = data1->value; si_signon_list_changed = PR_TRUE; si_SaveSignonDataLocked(); si_unlock_signon_list(); } } PUBLIC void SINGSIGN_RestoreSignonData (char* URLName, PRUnichar* name, PRUnichar** value, PRUint32 elementNumber) { si_SignonUserStruct* user; si_SignonDataStruct* data; nsAutoString correctedName; /* do nothing if signon preference is not enabled */ if (!si_GetSignonRememberingPref()){ return; } si_lock_signon_list(); if (elementNumber == 0) { si_UserHasBeenSelected = PR_FALSE; } /* Correct the field name to avoid mistaking for fields in browser-generated form * * Note that data saved for browser-generated logins (e.g. http authentication) * use artificial field names starting with * \= (see USERNAMEFIELD and PASSWORDFIELD. * To avoid mistakes whereby saved logins for http authentication is then prefilled * into a field on the html form at the same URL, we will prevent html field names * from starting with \=. We do that by doubling up a backslash if it appears in the * first character position */ if (*name == '\\') { correctedName = nsAutoString('\\'); correctedName.Append(name); } else { correctedName = name; } /* determine if name has been saved (avoids unlocking the database if not) */ PRBool nameFound = PR_FALSE; user = si_GetUser(URLName, PR_FALSE, correctedName); if (user) { PRInt32 dataCount = LIST_COUNT(user->signonData_list); for (PRInt32 i=0; isignonData_list->ElementAt(i)); if(correctedName.Length() && (data->name == correctedName)) { nameFound = PR_TRUE; } } } if (!nameFound) { si_unlock_signon_list(); return; } #ifdef xxx /* * determine if it is a change-of-password field * the heuristic that we will use is that if this is the first * item on the form and it is a password, this is probably a * change-of-password form */ /* see if this is first item in form and is a password */ /* get first saved user just so we can see the name of the first item on the form */ user = si_GetUser(URLName, PR_TRUE, NULL); /* this is the first saved user */ if (user) { data = (si_SignonDataStruct *) (user->signonData_list->ElementAt(0)); /* 1st item on form */ if(data->isPassword && correctedName.Length() && (data->name == correctedName)) { /* current item is first item on form and is a password */ user = (URLName, MK_SIGNON_PASSWORDS_FETCH); if (user) { /* user has confirmed it's a change-of-password form */ PRInt32 dataCount = LIST_COUNT(user->signonData_list); for (PRInt32 i=1; isignonData_list->ElementAt(i)); if (data->isPassword) { nsAutoString password; if (NS_SUCCEEDED(si_Decrypt(data->value, password))) { *value = password.ToNewUnicode(); } si_unlock_signon_list(); return; } } } } } #endif /* restore the data from previous time this URL was visited */ user = si_GetUser(URLName, PR_FALSE, correctedName); if (user) { PRInt32 dataCount = LIST_COUNT(user->signonData_list); for (PRInt32 i=0; isignonData_list->ElementAt(i)); if(correctedName.Length() && (data->name == correctedName)) { nsAutoString password; if (NS_SUCCEEDED(si_Decrypt(data->value, password))) { *value = password.ToNewUnicode(); } si_unlock_signon_list(); return; } } } si_unlock_signon_list(); } /* * Remember signon data from a browser-generated password dialog */ PRIVATE void si_RememberSignonDataFromBrowser(const char* URLName, nsAutoString username, nsAutoString password) { /* do nothing if signon preference is not enabled */ if (!si_GetSignonRememberingPref()){ return; } nsVoidArray * signonData = new nsVoidArray(); si_SignonDataStruct * data1 = new si_SignonDataStruct; data1->name.AssignWithConversion(USERNAMEFIELD); if (NS_FAILED(si_Encrypt(nsAutoString(username), data1->value))) { return; } data1->isPassword = PR_FALSE; signonData->AppendElement(data1); si_SignonDataStruct * data2 = new si_SignonDataStruct; data2->name.AssignWithConversion(PASSWORDFIELD); if (NS_FAILED(si_Encrypt(nsAutoString(password), data2->value))) { return; } data2->isPassword = PR_TRUE; signonData->AppendElement(data2); /* Save the signon data */ si_PutData(URLName, signonData, PR_TRUE); /* Deallocate */ delete data1; delete data2; delete signonData; } /* * Check for remembered data from a previous browser-generated password dialog * restore it if so */ PRIVATE void si_RestoreOldSignonDataFromBrowser (const char* URLName, PRBool pickFirstUser, nsAutoString& username, nsAutoString& password) { si_SignonUserStruct* user; si_SignonDataStruct* data; /* get the data from previous time this URL was visited */ si_lock_signon_list(); if (username.Length() != 0) { user = si_GetSpecificUser(URLName, username, NS_ConvertToString(USERNAMEFIELD)); } else { user = si_GetUser(URLName, pickFirstUser, NS_ConvertToString(USERNAMEFIELD)); } if (!user) { /* leave original username and password from caller unchanged */ /* username = 0; */ /* *password = 0; */ si_unlock_signon_list(); return; } /* restore the data from previous time this URL was visited */ PRInt32 dataCount = LIST_COUNT(user->signonData_list); for (PRInt32 i=0; isignonData_list->ElementAt(i)); nsAutoString decrypted; if (NS_SUCCEEDED(si_Decrypt(data->value, decrypted))) { if(data->name.EqualsWithConversion(USERNAMEFIELD)) { username = decrypted; } else if(data->name.EqualsWithConversion(PASSWORDFIELD)) { password = decrypted; } } } si_unlock_signon_list(); } PUBLIC PRBool SINGSIGN_StorePassword(const char *URLName, const PRUnichar *user, const PRUnichar *password, PRBool strip) { nsresult res; nsAutoString userName(user); /* convert URLName to a uri so we can parse out the username and hostname */ nsXPIDLCString host; if (strip) { if (URLName) { nsCOMPtr uri; nsComponentManager::CreateInstance(kStandardUrlCID, nsnull, NS_GET_IID(nsIURL), (void **) getter_AddRefs(uri)); res = uri->SetSpec(URLName); if (NS_FAILED(res)) return PR_FALSE; /* uri is of the form ://:@:/) */ /* get host part of the uri */ res = uri->GetHost(getter_Copies(host)); if (NS_FAILED(res)) { return PR_FALSE; } /* if no username given, extract it from uri -- note: prehost is : */ if (userName.Length() == 0) { nsXPIDLCString userName2; res = uri->GetPreHost(getter_Copies(userName2)); if (NS_FAILED(res)) { return PR_FALSE; } if ((const char *)userName2 && (PL_strlen((const char *)userName2))) { userName.AssignWithConversion(userName2); PRInt32 colon = userName.FindChar(':'); if (colon != -1) { userName.Truncate(colon); } } } } } else { res = MangleUrl(URLName, getter_Copies(host)); if (NS_FAILED(res)) return PR_FALSE; } si_RememberSignonDataFromBrowser ((const char *)host, userName, nsAutoString(password)); return PR_TRUE; } /* The following comments apply to the three prompt routines that follow * * If a password was successfully obtain (either from the single-signon * database or from a dialog with the user), we return NS_OK for the * function value and PR_TRUE for the boolean argument "pressedOK". * * If the user presses cancel from the dialog, we return NS_OK for the * function value and PR_FALSE for the boolean argument "pressedOK". * * If a password is not collected for any other reason, we return the * failure code for the function value and the boolean argument * "pressedOK" is undefined. */ PUBLIC nsresult SINGSIGN_PromptUsernameAndPassword (const PRUnichar *text, PRUnichar **user, PRUnichar **pwd, const char *urlname, nsIPrompt* dialog, PRBool *pressedOK, PRBool strip) { nsresult res; /* do only the dialog if signon preference is not enabled */ if (!si_GetSignonRememberingPref()){ return dialog->PromptUsernameAndPassword(text, user, pwd, pressedOK); } /* convert to a uri so we can parse out the hostname */ nsCOMPtr uri; nsComponentManager::CreateInstance(kStandardUrlCID, nsnull, NS_GET_IID(nsIURL), (void **) getter_AddRefs(uri)); res = uri->SetSpec(urlname); if (NS_FAILED(res)) { return res; } /* uri is of the form ://:@:/) */ /* get host part of the uri */ nsXPIDLCString host; if (strip) { res = uri->GetHost(getter_Copies(host)); if (NS_FAILED(res)) { return res; } } else { res = MangleUrl(urlname, getter_Copies(host)); if (NS_FAILED(res)) { return res; } } /* prefill with previous username/password if any */ nsAutoString username, password; si_RestoreOldSignonDataFromBrowser((const char*)host, PR_FALSE, username, password); /* get new username/password from user */ *user = username.ToNewUnicode(); *pwd = password.ToNewUnicode(); PRBool checked = PR_FALSE; res = si_CheckGetUsernamePassword(user, pwd, text, &checked); if (NS_FAILED(res)) { /* user pressed Cancel */ PR_FREEIF(*user); PR_FREEIF(*pwd); *pressedOK = PR_FALSE; return NS_OK; } if (checked) { si_RememberSignonDataFromBrowser ((const char*)host, nsAutoString(*user), nsAutoString(*pwd)); } /* cleanup and return */ *pressedOK = PR_TRUE; return NS_OK; } PUBLIC nsresult SINGSIGN_PromptPassword (const PRUnichar *text, PRUnichar **pwd, const char *urlname, nsIPrompt* dialog, PRBool *pressedOK, PRBool strip) { nsresult res; nsAutoString password, username; /* do only the dialog if signon preference is not enabled */ if (!si_GetSignonRememberingPref()){ PRUnichar * prompt_string = Wallet_Localize("PromptForPassword"); res = dialog->PromptPassword(text, prompt_string, pwd, pressedOK); Recycle(prompt_string); return res; } /* convert to a uri so we can parse out the username and hostname */ nsCOMPtr uri; nsComponentManager::CreateInstance(kStandardUrlCID, nsnull, NS_GET_IID(nsIURL), (void **) getter_AddRefs(uri)); res = uri->SetSpec(urlname); if (NS_FAILED(res)) { return res; } /* uri is of the form ://:@:/) */ /* get host part of the uri */ nsXPIDLCString host; if (strip) { res = uri->GetHost(getter_Copies(host)); if (NS_FAILED(res)) { return res; } } else { res = MangleUrl(urlname, getter_Copies(host)); if (NS_FAILED(res)) { return res; } } /* extract username from uri -- note: prehost is : */ if (strip) { nsXPIDLCString prehostCString; res = uri->GetPreHost(getter_Copies(prehostCString)); if (NS_FAILED(res)) { return res; } nsAutoString prehost; prehost.AssignWithConversion((const char *)prehostCString); PRInt32 colon = prehost.FindChar(':'); if (colon == -1) { username = prehost; } else { prehost.Left(username, colon); } } /* get previous password used with this username, pick first user if no username found */ si_RestoreOldSignonDataFromBrowser((const char *)host, (username.Length() == 0), username, password); /* return if a password was found */ if (password.Length() != 0) { *pwd = password.ToNewUnicode(); *pressedOK = PR_TRUE; return NS_OK; } /* no password found, get new password from user */ *pwd = password.ToNewUnicode(); PRBool checked = PR_FALSE; res = si_CheckGetPassword(pwd, text, &checked); if (NS_FAILED(res)) { /* user pressed Cancel */ PR_FREEIF(*pwd); *pressedOK = PR_FALSE; return NS_OK; } if (checked) { si_RememberSignonDataFromBrowser ((const char *)host, username, nsAutoString(*pwd)); } /* cleanup and return */ *pressedOK = PR_TRUE; return NS_OK; } PUBLIC nsresult SINGSIGN_Prompt (const PRUnichar *text, const PRUnichar *defaultText, PRUnichar **resultText, const char *urlname, nsIPrompt* dialog, PRBool *pressedOK, PRBool strip) { nsresult res; nsAutoString data, emptyUsername; /* do only the dialog if signon preference is not enabled */ if (!si_GetSignonRememberingPref()){ PRUnichar * prompt_string = Wallet_Localize("PromptForData"); res = dialog->Prompt(text, prompt_string, resultText, pressedOK); Recycle(prompt_string); return res; } /* convert to a uri so we can parse out the hostname */ nsCOMPtr uri; nsComponentManager::CreateInstance(kStandardUrlCID, nsnull, NS_GET_IID(nsIURL), (void **) getter_AddRefs(uri)); res = uri->SetSpec(urlname); if (NS_FAILED(res)) { return res; } /* get host part of the uri */ nsXPIDLCString host; if (strip) { res = uri->GetHost(getter_Copies(host)); if (NS_FAILED(res)) { return res; } } else { res = MangleUrl(urlname, getter_Copies(host)); if (NS_FAILED(res)) { return res; } } /* get previous data used with this hostname */ si_RestoreOldSignonDataFromBrowser((const char *)host, PR_TRUE, emptyUsername, data); /* return if data was found */ if (data.Length() != 0) { *resultText = data.ToNewUnicode(); *pressedOK = PR_TRUE; return NS_OK; } /* no data found, get new data from user */ *resultText = data.ToNewUnicode(); PRBool checked = PR_FALSE; res = si_CheckGetData(resultText, text, &checked); if (NS_FAILED(res)) { /* user pressed Cancel */ PR_FREEIF(*resultText); *pressedOK = PR_FALSE; return NS_OK; } if (checked) { si_RememberSignonDataFromBrowser ((const char *)host, emptyUsername, nsAutoString(*resultText)); } /* cleanup and return */ *pressedOK = PR_TRUE; return NS_OK; } /***************** * Signon Viewer * *****************/ /* return PR_TRUE if "number" is in sequence of comma-separated numbers */ PUBLIC PRBool SI_InSequence(nsAutoString sequence, PRInt32 number) { nsAutoString tail = sequence; nsAutoString head, temp; PRInt32 separator; for (;;) { /* get next item in list */ separator = tail.FindChar(','); if (-1 == separator) { return PR_FALSE; } tail.Left(head, separator); tail.Mid(temp, separator+1, tail.Length() - (separator+1)); tail = temp; /* test item to see if it equals our number */ PRInt32 error; PRInt32 numberInList = head.ToInteger(&error); if (!error && numberInList == number) { return PR_TRUE; } } } PUBLIC PRUnichar* SI_FindValueInArgs(nsAutoString results, nsAutoString name) { /* note: name must start and end with a vertical bar */ nsAutoString value; PRInt32 start, length; start = results.Find(name); PR_ASSERT(start >= 0); if (start < 0) { return nsAutoString().ToNewUnicode(); } start += name.Length(); /* get passed the |name| part */ length = results.FindChar('|', PR_FALSE,start) - start; results.Mid(value, start, length); return value.ToNewUnicode(); } extern void Wallet_SignonViewerReturn (nsAutoString results); PUBLIC void SINGSIGN_SignonViewerReturn (nsAutoString results) { si_SignonURLStruct *url; si_SignonUserStruct* user; si_SignonDataStruct* data; si_Reject* reject; PRInt32 signonNum; /* get total number of signons so we can go through list backwards */ signonNum = 0; PRInt32 count = LIST_COUNT(si_signon_list); for (PRInt32 i=0; iElementAt(i)); signonNum += LIST_COUNT(url->signonUser_list); } /* * step backwards through all users and delete those that are in the sequence */ nsAutoString gone; gone = SI_FindValueInArgs(results, NS_ConvertToString("|goneS|")); PRInt32 urlCount = LIST_COUNT(si_signon_list); while (urlCount>0) { urlCount--; url = NS_STATIC_CAST(si_SignonURLStruct*, si_signon_list->ElementAt(urlCount)); PRInt32 userCount = LIST_COUNT(url->signonUser_list); while (userCount>0) { userCount--; signonNum--; user = NS_STATIC_CAST(si_SignonUserStruct*, url->signonUser_list->ElementAt(userCount)); if (user && SI_InSequence(gone, signonNum)) { /* get to first data item -- that's the user name */ data = (si_SignonDataStruct *) (user->signonData_list->ElementAt(0)); /* do the deletion */ nsAutoString userName; if (NS_SUCCEEDED(si_Decrypt(data->value, userName))) { si_RemoveUser(url->URLName, userName, PR_TRUE, PR_TRUE); si_signon_list_changed = PR_TRUE; } } } } si_SaveSignonDataLocked(); /* step backwards through all rejects and delete those that are in the sequence */ gone = SI_FindValueInArgs(results, NS_ConvertToString("|goneR|")); si_lock_signon_list(); PRInt32 rejectCount = LIST_COUNT(si_reject_list); while (rejectCount>0) { rejectCount--; reject = NS_STATIC_CAST(si_Reject*, si_reject_list->ElementAt(rejectCount)); if (reject && SI_InSequence(gone, rejectCount)) { si_FreeReject(reject); si_signon_list_changed = PR_TRUE; } } si_SaveSignonDataLocked(); si_unlock_signon_list(); /* now give wallet a chance to do its deletions */ Wallet_SignonViewerReturn(results); } #define BUFLEN2 5000 #define BREAK '\001' PUBLIC void SINGSIGN_GetSignonListForViewer(nsAutoString& aSignonList) { /* force loading of the signons file */ si_RegisterSignonPrefCallbacks(); nsAutoString buffer; int signonNum = 0; si_SignonURLStruct *url; si_SignonUserStruct * user; si_SignonDataStruct* data = nsnull; PRInt32 urlCount = LIST_COUNT(si_signon_list); for (PRInt32 i=0; iElementAt(i)); PRInt32 userCount = LIST_COUNT(url->signonUser_list); for (PRInt32 j=0; jsignonUser_list->ElementAt(j)); /* first non-password data item for user is the username */ PRInt32 dataCount = LIST_COUNT(user->signonData_list); for (PRInt32 k=0; ksignonData_list->ElementAt(k)); if (!(data->isPassword)) { break; } } nsAutoString userName; if (NS_SUCCEEDED(si_Decrypt(data->value, userName))) { buffer.AppendWithConversion(BREAK); buffer.AppendWithConversion("\n"); signonNum++; } else { /* don't display saved signons if user couldn't unlock the database */ aSignonList.AssignWithConversion("."); /* a list of length 1 tells viewer that database was not unlocked */ return; } } } aSignonList = buffer; } PUBLIC void SINGSIGN_GetRejectListForViewer(nsAutoString& aRejectList) { nsAutoString buffer; int rejectNum = 0; si_Reject *reject; /* force loading of the signons file */ si_RegisterSignonPrefCallbacks(); PRInt32 rejectCount = LIST_COUNT(si_reject_list); for (PRInt32 i=0; iElementAt(i)); buffer.AppendWithConversion(BREAK); buffer.AppendWithConversion("\n"); rejectNum++; } aRejectList = buffer; } PUBLIC nsresult SINGSIGN_HaveData(const char *url, const PRUnichar *userName, PRBool strip, PRBool *retval) { nsresult res; nsAutoString data, usernameForLookup; *retval = PR_FALSE; if (!si_GetSignonRememberingPref()) { return NS_OK; } NS_ASSERTION(strip == PR_FALSE, "this code needs to be finished for the strip case"); /* convert to a uri so we can parse out the username and hostname */ nsCOMPtr uri; nsComponentManager::CreateInstance(kStandardUrlCID, nsnull, NS_GET_IID(nsIURL), (void **) getter_AddRefs(uri)); res = uri->SetSpec(url); if (NS_FAILED(res)) return res; /* get host part of the uri */ nsXPIDLCString host; if (strip) { res = uri->GetHost(getter_Copies(host)); if (NS_FAILED(res)) { return res; } } else { res = MangleUrl(url, getter_Copies(host)); if (NS_FAILED(res)) return res; } if (strip) { /* convert url to a uri so we can parse out the username and hostname */ /* if no username given, extract it from uri -- note: prehost is : */ return NS_ERROR_NOT_IMPLEMENTED; } /* get previous data used with this username, pick first user if no username found */ si_RestoreOldSignonDataFromBrowser((const char *)host, (usernameForLookup.Length() == 0), usernameForLookup, data); if (data.Length()) { *retval = PR_TRUE; } return NS_OK; } #ifdef APPLE_KEYCHAIN /************************************ * Apple Keychain Specific Routines * ************************************/ /* * APPLE * The Keychain callback. This routine will be called whenever a lock, * delete, or update event occurs in the Keychain. The only action taken * is to make the signon list invalid, so it will be read in again the * next time it is accessed. */ OSStatus PR_CALLBACK si_KeychainCallback( KCEvent keychainEvent, KCCallbackInfo *info, void *userContext) { PRBool *listInvalid = (PRBool*)userContext; *listInvalid = PR_TRUE; } /* * APPLE * Get the signon data from the keychain * * This routine is called only if signon pref is enabled!!! */ PRIVATE int si_LoadSignonDataFromKeychain() { char * URLName; si_FormSubmitData submit; nsAutoString name_array[MAX_ARRAY_SIZE]; nsAutoString value_array[MAX_ARRAY_SIZE]; uint8 type_array[MAX_ARRAY_SIZE]; char buffer[BUFFER_SIZE]; PRBool badInput = PR_FALSE; int i; KCItemRef itemRef; KCAttributeList attrList; KCAttribute attr[2]; KCItemClass itemClass = kInternetPasswordKCItemClass; KCProtocolType protocol = kNetscapeProtocolType; OSStatus status = noErr; KCSearchRef searchRef = NULL; /* initialize the submit structure */ submit.name_array = name_array; submit.value_array = value_array; submit.type_array = (PRUnichar *)type_array; /* set up the attribute list */ attrList.count = 2; attrList.attr = attr; attr[0].tag = kClassKCItemAttr; attr[0].data = &itemClass; attr[0].length = sizeof(itemClass); attr[1].tag = kProtocolKCItemAttr; attr[1].data = &protocol; attr[1].length = sizeof(protocol); status = KCFindFirstItem( &attrList, &searchRef, &itemRef ); #ifdef DefaultIsOff if (status == noErr) { /* if we found a Netscape item, let's assume notice has been given */ si_SetNotificationPref(PR_TRUE); } else { si_SetNotificationPref(PR_FALSE); } #endif si_lock_signon_list(); while(status == noErr) { char *value; uint16 i = 0; uint32 actualSize; KCItemFlags flags; PRBool reject = PR_FALSE; submit.value_cnt = 0; /* first find out if it is a reject entry */ attr[0].tag = kFlagsKCItemAttr; attr[0].length = sizeof(KCItemFlags); attr[0].data = &flags; status = KCGetAttribute( itemRef, attr, nil ); if (status != noErr) { break; } if (flags & kNegativeKCItemFlag) { reject = PR_TRUE; } /* get the server name */ attr[0].tag = kServerKCItemAttr; attr[0].length = BUFFER_SIZE; attr[0].data = buffer; status = KCGetAttribute( itemRef, attr, &actualSize ); if (status != noErr) { break; } /* null terminate */ buffer[actualSize] = 0; URLName = NULL; StrAllocCopy(URLName, buffer); if (!reject) { /* get the password data */ status = KCGetData(itemRef, BUFFER_SIZE, buffer, &actualSize); if (status != noErr) { break; } /* null terminate */ buffer[actualSize] = 0; /* parse for '=' which separates the name and value */ for (i = 0; i < PL_strlen(buffer); i++) { if (buffer[i] == '=') { value = &buffer[i+1]; buffer[i] = 0; break; } } name_array[submit.value_cnt] = NULL; value_array[submit.value_cnt] = NULL; type_array[submit.value_cnt] = FORM_TYPE_PASSWORD; StrAllocCopy(name_array[submit.value_cnt], buffer); StrAllocCopy(value_array[submit.value_cnt], value); } /* get the account attribute */ attr[0].tag = kAccountKCItemAttr; attr[0].length = BUFFER_SIZE; attr[0].data = buffer; status = KCGetAttribute( itemRef, attr, &actualSize ); if (status != noErr) { break; } /* null terminate */ buffer[actualSize] = 0; if (!reject) { /* parse for '=' which separates the name and value */ for (i = 0; i < PL_strlen(buffer); i++) { if (buffer[i] == '=') { value = &buffer[i+1]; buffer[i] = 0; break; } } submit.value_cnt++; name_array[submit.value_cnt] = NULL; value_array[submit.value_cnt] = NULL; type_array[submit.value_cnt] = FORM_TYPE_TEXT; StrAllocCopy(name_array[submit.value_cnt], buffer); StrAllocCopy(value_array[submit.value_cnt], value); /* check for overruning of the arrays */ if (submit.value_cnt >= MAX_ARRAY_SIZE) { break; } submit.value_cnt++; /* store the info for this URL into memory-resident data structure */ if (!URLName || PL_strlen(URLName) == 0) { badInput = PR_TRUE; } if (!badInput) { si_PutData(URLName, &submit, PR_FALSE); } } else { /* reject */ si_PutReject(URLName, nsAutoString(buffer), PR_FALSE); } reject = PR_FALSE; /* reset reject flag */ PR_Free(URLName); KCReleaseItemRef( &itemRef ); status = KCFindNextItem( searchRef, &itemRef); } si_unlock_signon_list(); if (searchRef) { KCReleaseSearchRef( &searchRef ); } /* Register a callback with the Keychain if we haven't already done so. */ if (si_kcUPP == NULL) { si_kcUPP = NewKCCallbackProc( si_KeychainCallback ); if (!si_kcUPP) { return memFullErr; } KCAddCallback( si_kcUPP, kLockKCEventMask + kDeleteKCEventMask + kUpdateKCEventMask, &si_list_invalid ); /* * Note that the callback is not necessarily removed. We take advantage * of the fact that the Keychain will clean up the callback when the app * goes away. It is explicitly removed when the signon preference is turned off. */ } if (status == errKCItemNotFound) { status = 0; } return (status); } /* * APPLE * Save signon data to Apple Keychain * * This routine is called only if signon pref is enabled!!! */ PRIVATE int si_SaveSignonDataInKeychain() { char* account = nil; char* password = nil; si_SignonURLStruct * URL; si_SignonUserStruct * user; si_SignonDataStruct * data; si_Reject * reject; OSStatus status; KCItemRef itemRef; KCAttribute attr; KCItemFlags flags = kInvisibleKCItemFlag + kNegativeKCItemFlag; uint32 actualLength; /* save off the reject list */ if (si_reject_list) { PRInt32 rejectCount = LIST_COUNT(si_reject_list); for (PRInt32 i=0; iElementAt(i)); status = kcaddinternetpassword (reject->URLName, nil, reject->userName, kAnyPort, kNetscapeProtocolType, kAnyAuthType, 0, nil, &itemRef); if (status != noErr && status != errKCDuplicateItem) { return(status); } if (status == noErr) { /* * make the item invisible so the user doesn't see it and * negative so we know that it is a reject entry */ attr.tag = kFlagsKCItemAttr; attr.data = &flags; attr.length = sizeof( flags ); status = KCSetAttribute( itemRef, &attr ); if (status != noErr) { return(status); } status = KCUpdateItem(itemRef); if (status != noErr) { return(status); } KCReleaseItemRef(&itemRef); } } } /* save off the passwords */ if((si_signon_list)) { PRInt32 urlCount = LIST_COUNT(si_signon_list); for (PRInt32 i=0; iElementAt(i)); /* add each user node of the URL node */ PRInt32 userCount = LIST_COUNT(URL->signonUser_list); for (PRInt32 i=0; isignonUser_list->ElementAt(i)); /* write out each data node of the user node */ PRInt32 count = LIST_COUNT(user->signonData_list); for (PRInt32 i=0; isignonData_list->ElementAt(i)); char* attribute = nil; if (data->isPassword) { password = PR_Malloc(PL_strlen(data->value) + PL_strlen(data->name) + 2); if (!password) { return (-1); } attribute = password; } else { account = PR_Malloc( PL_strlen(data->value) + PL_strlen(data->name) + 2); if (!account) { PR_Free(password); return (-1); } attribute = account; } PL_strcpy(attribute, data->name); PL_strcat(attribute, "="); PL_strcat(attribute, data->value); } /* if it's already there, we just want to change the password */ status = kcfindinternetpassword (URL->URLName, nil, account, kAnyPort, kNetscapeProtocolType, kAnyAuthType, 0, nil, &actualLength, &itemRef); if (status == noErr) { status = KCSetData(itemRef, PL_strlen(password), password); if (status != noErr) { return(status); } status = KCUpdateItem(itemRef); KCReleaseItemRef(&itemRef); } else { /* wasn't there, let's add it */ status = kcaddinternetpassword (URL->URLName, nil, account, kAnyPort, kNetscapeProtocolType, kAnyAuthType, PL_strlen(password), password, nil); } if (account) { PR_Free(account); } if (password) { PR_Free(password); } account = password = nil; if (status != noErr) { return(status); } } } } si_signon_list_changed = PR_FALSE; return (0); } #endif