Back to index

lightning-sunbird  0.9+nobinonly
singsign.cpp
Go to the documentation of this file.
00001 /* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
00002 /* ***** BEGIN LICENSE BLOCK *****
00003  * Version: MPL 1.1/GPL 2.0/LGPL 2.1
00004  *
00005  * The contents of this file are subject to the Mozilla Public License Version
00006  * 1.1 (the "License"); you may not use this file except in compliance with
00007  * the License. You may obtain a copy of the License at
00008  * http://www.mozilla.org/MPL/
00009  *
00010  * Software distributed under the License is distributed on an "AS IS" basis,
00011  * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
00012  * for the specific language governing rights and limitations under the
00013  * License.
00014  *
00015  * The Original Code is Mozilla Communicator client code.
00016  *
00017  * The Initial Developer of the Original Code is
00018  * Netscape Communications Corporation.
00019  * Portions created by the Initial Developer are Copyright (C) 1998
00020  * the Initial Developer. All Rights Reserved.
00021  *
00022  * Contributor(s):
00023  *   Pierre Phaneuf <pp@ludusdesign.com>
00024  *   Mike Calmus
00025  *
00026  * Alternatively, the contents of this file may be used under the terms of
00027  * either the GNU General Public License Version 2 or later (the "GPL"), or
00028  * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
00029  * in which case the provisions of the GPL or the LGPL are applicable instead
00030  * of those above. If you wish to allow use of your version of this file only
00031  * under the terms of either the GPL or the LGPL, and not to allow others to
00032  * use your version of this file under the terms of the MPL, indicate your
00033  * decision by deleting the provisions above and replace them with the notice
00034  * and other provisions required by the GPL or the LGPL. If you do not delete
00035  * the provisions above, a recipient may use your version of this file under
00036  * the terms of any one of the MPL, the GPL or the LGPL.
00037  *
00038  * ***** END LICENSE BLOCK ***** */
00039 
00040 #define alphabetize 1
00041 
00042 #include "singsign.h"
00043 #include "wallet.h"
00044 #include "nsNetUtil.h"
00045 
00046 #ifdef XP_MAC
00047 #include "prpriv.h"             /* for NewNamedMonitor */
00048 #include "prinrval.h"           /* for PR_IntervalNow */
00049 #ifdef APPLE_KEYCHAIN                   /* APPLE */
00050 #include "Keychain.h"                   /* APPLE */
00051 #define kNetscapeProtocolType   'form'  /* APPLE */
00052 #endif                                  /* APPLE */
00053 #else
00054 #include "private/prpriv.h"     /* for NewNamedMonitor */
00055 #endif
00056 
00057 #include "nsIPref.h"
00058 #include "nsIServiceManager.h"
00059 #include "nsIIOService.h"
00060 #include "nsIURL.h"
00061 #include "nsIDOMHTMLDocument.h"
00062 #include "prmem.h"
00063 #include "prprf.h"
00064 #include "nsXPIDLString.h"
00065 #include "nsReadableUtils.h"
00066 #include "nsIObserverService.h"
00067 #include "nsIObserver.h"
00068 #include "nsCRT.h"
00069 
00070 //#define SINGSIGN_LOGGING
00071 #ifdef SINGSIGN_LOGGING
00072 #define LOG(args) printf args
00073 #else
00074 #define LOG(args)
00075 #endif
00076 
00077 // Currently the default is on, so we don't need the code to prompt the
00078 // user if the default is off.
00079 #undef WALLET_PASSWORDMANAGER_DEFAULT_IS_OFF
00080 
00081 /********************
00082  * Global Variables *
00083  ********************/
00084 
00085 /* locks for signon cache */
00086 
00087 static PRMonitor * signon_lock_monitor = NULL;
00088 static PRThread  * signon_lock_owner = NULL;
00089 static int signon_lock_count = 0;
00090 
00091 /* load states */
00092 
00093 static PRBool si_PartiallyLoaded = PR_FALSE;
00094 static PRInt32 si_LastFormForWhichUserHasBeenSelected = -1;
00095 
00096 /* apple keychain stuff */
00097 
00098 #ifdef APPLE_KEYCHAIN
00099 static PRBool     si_list_invalid = PR_FALSE;
00100 static KCCallbackUPP si_kcUPP = NULL;
00101 static int
00102 si_SaveSignonDataInKeychain();
00103 #endif
00104 
00105 #define USERNAMEFIELD "\\=username=\\"
00106 #define PASSWORDFIELD "\\=password=\\"
00107 
00108 /******************
00109  * Key Management *
00110  ******************/
00111 
00112 char* signonFileName = nsnull;
00113 static PRBool gLoadedUserData = PR_FALSE;
00114 static PRUint32 gSelectUserDialogCount = 0;
00115 
00116 
00117 /***************************
00118  * Locking the Signon List *
00119  ***************************/
00120 
00121 static void
00122 si_lock_signon_list(void) {
00123   if(!signon_lock_monitor) {
00124     signon_lock_monitor = PR_NewNamedMonitor("signon-lock");
00125   }
00126   PR_EnterMonitor(signon_lock_monitor);
00127   while(PR_TRUE) {
00128 
00129     /* no current owner or owned by this thread */
00130     PRThread * t = PR_GetCurrentThread();
00131     if(signon_lock_owner == NULL || signon_lock_owner == t) {
00132       signon_lock_owner = t;
00133       signon_lock_count++;
00134       PR_ExitMonitor(signon_lock_monitor);
00135       return;
00136     }
00137 
00138     /* owned by someone else -- wait till we can get it */
00139     PR_Wait(signon_lock_monitor, PR_INTERVAL_NO_TIMEOUT);
00140   }
00141 }
00142 
00143 static void
00144 si_unlock_signon_list(void) {
00145     PR_EnterMonitor(signon_lock_monitor);
00146 
00147 #ifdef DEBUG
00148     /* make sure someone doesn't try to free a lock they don't own */
00149     PR_ASSERT(signon_lock_owner == PR_GetCurrentThread());
00150 #endif
00151 
00152     signon_lock_count--;
00153     if(signon_lock_count == 0) {
00154         signon_lock_owner = NULL;
00155         PR_Notify(signon_lock_monitor);
00156     }
00157     PR_ExitMonitor(signon_lock_monitor);
00158 }
00159 
00160 
00161 /********************************
00162  * Preference Utility Functions *
00163  ********************************/
00164 
00165 void
00166 SI_RegisterCallback(const char* domain, PrefChangedFunc callback, void* instance_data) {
00167   nsresult ret;
00168   nsCOMPtr<nsIPref> pPrefService = do_GetService(NS_PREF_CONTRACTID, &ret);
00169   if (NS_SUCCEEDED(ret)) {
00170     ret = pPrefService->RegisterCallback(domain, callback, instance_data);
00171   }
00172 }
00173 
00174 void
00175 SI_UnregisterCallback(const char* domain, PrefChangedFunc callback, void* instance_data) {
00176   nsresult ret;
00177   nsCOMPtr<nsIPref> pPrefService = do_GetService(NS_PREF_CONTRACTID, &ret);
00178   if (NS_SUCCEEDED(ret)) {
00179     ret = pPrefService->UnregisterCallback(domain, callback, instance_data);
00180   }
00181 }
00182 
00183 void
00184 SI_SetBoolPref(const char * prefname, PRBool prefvalue) {
00185   nsresult ret;
00186   nsCOMPtr<nsIPref> pPrefService = do_GetService(NS_PREF_CONTRACTID, &ret);
00187   if (NS_SUCCEEDED(ret)) {
00188     ret = pPrefService->SetBoolPref(prefname, prefvalue);
00189     if (NS_SUCCEEDED(ret)) {
00190       ret = pPrefService->SavePrefFile(nsnull); 
00191     }
00192   }
00193 }
00194 
00195 PRBool
00196 SI_GetBoolPref(const char * prefname, PRBool defaultvalue) {
00197   nsresult ret;
00198   PRBool prefvalue = defaultvalue;
00199   nsCOMPtr<nsIPref> pPrefService = do_GetService(NS_PREF_CONTRACTID, &ret);
00200   if (NS_SUCCEEDED(ret)) {
00201     ret = pPrefService->GetBoolPref(prefname, &prefvalue);
00202   }
00203   return prefvalue;
00204 }
00205 
00206 void
00207 SI_SetCharPref(const char * prefname, const char * prefvalue) {
00208   if (!prefvalue) {
00209     return; /* otherwise the SetCharPref routine called below will crash */
00210   }
00211   nsresult ret;
00212   nsCOMPtr<nsIPref> pPrefService = do_GetService(NS_PREF_CONTRACTID, &ret);
00213   if (NS_SUCCEEDED(ret)) {
00214     ret = pPrefService->SetCharPref(prefname, prefvalue);
00215     if (NS_SUCCEEDED(ret)) {
00216       ret = pPrefService->SavePrefFile(nsnull); 
00217     }
00218   }
00219 }
00220 
00221 void
00222 SI_GetCharPref(const char * prefname, char** aPrefvalue) {
00223   nsresult ret;
00224   nsCOMPtr<nsIPref> pPrefService = do_GetService(NS_PREF_CONTRACTID, &ret);
00225   if (NS_SUCCEEDED(ret)) {
00226     ret = pPrefService->CopyCharPref(prefname, aPrefvalue);
00227     if (NS_FAILED(ret)) {
00228       *aPrefvalue = nsnull;
00229     }
00230   } else {
00231     *aPrefvalue = nsnull;
00232   }
00233 }
00234 
00235 void
00236 SI_GetLocalizedUnicharPref(const char * prefname, PRUnichar** aPrefvalue) {
00237   nsresult ret;
00238   nsCOMPtr<nsIPref> pPrefService = do_GetService(NS_PREF_CONTRACTID, &ret);
00239   if (NS_SUCCEEDED(ret)) {
00240     ret = pPrefService->GetLocalizedUnicharPref(prefname, aPrefvalue);
00241     if (NS_FAILED(ret)) {
00242       *aPrefvalue = nsnull;
00243     }
00244   } else {
00245       *aPrefvalue = nsnull;
00246   }
00247 }
00248 
00249 
00250 /*********************************
00251  * Preferences for Single Signon *
00252  *********************************/
00253 
00254 static const char *pref_rememberSignons = "signon.rememberSignons";
00255 #ifdef WALLET_PASSWORDMANAGER_DEFAULT_IS_OFF
00256 static const char *pref_Notified = "signon.Notified";
00257 #endif
00258 static const char *pref_SignonFileName = "signon.SignonFileName";
00259 
00260 static PRBool si_RememberSignons = PR_FALSE;
00261 #ifdef WALLET_PASSWORDMANAGER_DEFAULT_IS_OFF
00262 static PRBool si_Notified = PR_FALSE;
00263 #endif
00264 
00265 static int
00266 si_SaveSignonDataLocked(char * state, PRBool notify);
00267 
00268 int
00269 SI_LoadSignonData();
00270 
00271 void
00272 SI_RemoveAllSignonData();
00273 
00274 #ifdef WALLET_PASSWORDMANAGER_DEFAULT_IS_OFF
00275 static PRBool
00276 si_GetNotificationPref(void) {
00277   return si_Notified;
00278 }
00279 
00280 static void
00281 si_SetNotificationPref(PRBool x) {
00282   SI_SetBoolPref(pref_Notified, x);
00283   si_Notified = x;
00284 }
00285 #endif
00286 
00287 static void
00288 si_SetSignonRememberingPref(PRBool x) {
00289 #ifdef APPLE_KEYCHAIN
00290   if (x == 0) {
00291     /* We no longer need the Keychain callback installed */
00292     KCRemoveCallback( si_kcUPP );
00293     DisposeRoutineDescriptor( si_kcUPP );
00294     si_kcUPP = NULL;
00295   }
00296 #endif
00297   si_RememberSignons = x;
00298 }
00299 
00300 int PR_CALLBACK
00301 si_SignonRememberingPrefChanged(const char * newpref, void * data) {
00302     PRBool x;
00303     x = SI_GetBoolPref(pref_rememberSignons, PR_TRUE);
00304     si_SetSignonRememberingPref(x);
00305     return 0; /* this is PREF_NOERROR but we no longer include prefapi.h */
00306 }
00307 
00308 static void
00309 si_RegisterSignonPrefCallbacks(void) {
00310   PRBool x;
00311   static PRBool first_time = PR_TRUE;
00312   if(first_time) {
00313     first_time = PR_FALSE;
00314     SI_RegisterCallback(pref_rememberSignons, si_SignonRememberingPrefChanged, NULL);
00315   }
00316   
00317   if (!gLoadedUserData) {
00318     gLoadedUserData = PR_TRUE;
00319     SI_LoadSignonData();
00320 #ifdef WALLET_PASSWORDMANAGER_DEFAULT_IS_OFF
00321     x = SI_GetBoolPref(pref_Notified, PR_FALSE);
00322     si_SetNotificationPref(x);        
00323 #endif
00324     x = SI_GetBoolPref(pref_rememberSignons, PR_FALSE);
00325     si_SetSignonRememberingPref(x);
00326   }
00327 }
00328 
00329 static PRBool
00330 si_GetSignonRememberingPref(void) {
00331 #ifdef APPLE_KEYCHAIN
00332   /* If the Keychain has been locked or an item deleted or updated,
00333    * we need to reload the signon data
00334    */
00335   if (si_list_invalid) {
00336     /*
00337      * set si_list_invalid to PR_FALSE first because SI_RemoveAllSignonData
00338      * calls si_GetSignonRememberingPref
00339      */
00340     si_list_invalid = PR_FALSE;
00341     SI_LoadSignonData();
00342   }
00343 #endif
00344 
00345   si_RegisterSignonPrefCallbacks();
00346 
00347 #ifdef WALLET_PASSWORDMANAGER_DEFAULT_IS_OFF
00348   /*
00349    * We initially want the rememberSignons pref to be PR_FALSE.  But this will
00350    * prevent the notification message from ever occurring.  To get around
00351    * this problem, if the signon pref is PR_FALSE and no notification has
00352    * ever been given, we will treat this as if the signon pref were PR_TRUE.
00353    */
00354   if (!si_RememberSignons && !si_GetNotificationPref()) {
00355     return PR_TRUE;
00356   } else {
00357     return si_RememberSignons;
00358   }
00359 #else
00360   return si_RememberSignons;
00361 #endif
00362 }
00363 
00364 void
00365 SI_InitSignonFileName() {
00366   SI_GetCharPref(pref_SignonFileName, &signonFileName);
00367   if (!signonFileName) {
00368     signonFileName = Wallet_RandomName("s");
00369     SI_SetCharPref(pref_SignonFileName, signonFileName);
00370   }
00371 }
00372 
00373 
00374 /***********
00375  * Dialogs *
00376  ***********/
00377 
00378 #ifdef WALLET_PASSWORDMANAGER_DEFAULT_IS_OFF
00379 static PRBool
00380 si_ConfirmYN(PRUnichar * szMessage, nsIDOMWindowInternal* window) {
00381   return Wallet_ConfirmYN(szMessage, window);
00382 }
00383 #endif
00384 
00385 static PRInt32
00386 si_3ButtonConfirm(PRUnichar * szMessage, nsIDOMWindowInternal* window) {
00387   return Wallet_3ButtonConfirm(szMessage, window);
00388 }
00389 
00390 static PRBool
00391 si_SelectDialog(const PRUnichar* szMessage, nsIPrompt* dialog, PRUnichar** pList, PRInt32* pCount, PRUint32 formNumber) {
00392   if (si_LastFormForWhichUserHasBeenSelected == (PRInt32)formNumber) {
00393     /* a user was already selected for this form, use same one again */
00394     *pCount = 0; /* last user selected is now at head of list */
00395     return PR_TRUE;
00396   }
00397   nsresult rv;
00398   PRInt32 selectedIndex;
00399   PRBool rtnValue;
00400   PRUnichar * title_string = Wallet_Localize("SelectUserTitleLine");
00401 
00402   /* Notify signon manager dialog to update its display */
00403   nsCOMPtr<nsIObserverService> os(do_GetService("@mozilla.org/observer-service;1"));
00404   gSelectUserDialogCount++;
00405   if (os) {
00406     os->NotifyObservers(nsnull, "signonSelectUser", NS_LITERAL_STRING("suspend").get());
00407   }
00408 
00409   rv = dialog->Select( title_string, szMessage, *pCount, NS_CONST_CAST(const PRUnichar**, pList), &selectedIndex, &rtnValue );
00410 
00411   gSelectUserDialogCount--;
00412   if (os) {
00413     os->NotifyObservers(nsnull, "signonSelectUser", NS_LITERAL_STRING("resume").get());
00414   }
00415 
00416   Recycle(title_string);
00417   if (selectedIndex >= *pCount) {
00418     return PR_FALSE; // out-of-range selection
00419   }
00420   *pCount = selectedIndex;
00421   if (rtnValue) {
00422     si_LastFormForWhichUserHasBeenSelected = formNumber;
00423   }
00424   return rtnValue;  
00425 }
00426 
00427 static nsresult
00428 si_CheckGetPassword
00429   (PRUnichar ** password,
00430    const PRUnichar* dialogTitle,
00431    const PRUnichar * szMessage,
00432    nsIPrompt* dialog,
00433    PRUint32 savePassword,
00434    PRBool* checkValue)
00435 {
00436   nsresult res;
00437 
00438   PRUnichar * prompt_string = (PRUnichar*)dialogTitle;
00439   if (dialogTitle == nsnull || dialogTitle[0] == 0)
00440     prompt_string = Wallet_Localize("PromptForPassword");
00441   PRUnichar * check_string;
00442   
00443   // According to nsIPrompt spec, the checkbox is shown or not
00444   // depending on whether or not checkValue == nsnull, not checkMsg.
00445   PRBool * check_value = checkValue;
00446   if (savePassword != SINGSIGN_SAVE_PASSWORD_PERMANENTLY) {
00447     check_string = nsnull;
00448     check_value = nsnull;
00449   } else if (SI_GetBoolPref(pref_Crypto, PR_FALSE)) {
00450     check_string = Wallet_Localize("SaveThisPasswordEncrypted");
00451   } else {
00452     check_string = Wallet_Localize("SaveThisPasswordObscured");
00453   }
00454 
00455   PRBool confirmed = PR_FALSE;  
00456   res = dialog->PromptPassword(prompt_string,
00457                                szMessage,
00458                                password,
00459                                check_string,
00460                                check_value,
00461                                &confirmed);
00462 
00463   if (dialogTitle == nsnull)
00464     Recycle(prompt_string);
00465   if (check_string)
00466     Recycle(check_string);
00467 
00468   if (NS_FAILED(res)) {
00469     return res;
00470   }
00471   if (confirmed) {
00472     return NS_OK;
00473   } else {
00474     return NS_ERROR_FAILURE; /* user pressed cancel */
00475   }
00476 }
00477 
00478 static nsresult
00479 si_CheckGetData
00480   (PRUnichar ** data,
00481    const PRUnichar* dialogTitle,
00482    const PRUnichar * szMessage,
00483    nsIPrompt* dialog,
00484    PRUint32 savePassword,
00485    PRBool* checkValue)
00486 {
00487   nsresult res;  
00488 
00489   PRUnichar * prompt_string = (PRUnichar*)dialogTitle;
00490   if (dialogTitle == nsnull || dialogTitle[0] == 0)
00491     prompt_string = Wallet_Localize("PromptForData");
00492   PRUnichar * check_string;
00493 
00494   // According to nsIPrompt spec, the checkbox is shown or not
00495   // depending on whether or not checkValue == nsnull, not checkMsg.
00496   PRBool * check_value = checkValue;
00497   if (savePassword != SINGSIGN_SAVE_PASSWORD_PERMANENTLY) {
00498     check_string = nsnull;
00499     check_value = nsnull;
00500   } else if (SI_GetBoolPref(pref_Crypto, PR_FALSE)) {
00501     check_string = Wallet_Localize("SaveThisValueEncrypted");
00502   } else {
00503     check_string = Wallet_Localize("SaveThisValueObscured");
00504   }
00505 
00506   PRBool confirmed = PR_FALSE;  
00507   res = dialog->Prompt(prompt_string,
00508                        szMessage,
00509                        data,
00510                        check_string,
00511                        check_value,
00512                        &confirmed);
00513 
00514   if (dialogTitle == nsnull || dialogTitle[0] == 0)
00515     Recycle(prompt_string);
00516   if (check_string)
00517     Recycle(check_string);
00518 
00519   if (NS_FAILED(res)) {
00520     return res;
00521   }
00522   if (confirmed) {
00523     return NS_OK;
00524   } else {
00525     return NS_ERROR_FAILURE; /* user pressed cancel */
00526   }
00527 }
00528 
00529 static nsresult
00530 si_CheckGetUsernamePassword
00531   (PRUnichar ** username,
00532    PRUnichar ** password,
00533    const PRUnichar* dialogTitle,
00534    const PRUnichar * szMessage,
00535    nsIPrompt* dialog,
00536    PRUint32 savePassword,
00537    PRBool* checkValue)
00538 {
00539   nsresult res;  
00540   PRUnichar * check_string;
00541   PRUnichar * prompt_string = (PRUnichar*)dialogTitle;
00542   if (dialogTitle == nsnull || dialogTitle[0] == 0)
00543     prompt_string = Wallet_Localize("PromptForPassword");
00544   
00545   // According to nsIPrompt spec, the checkbox is shown or not
00546   // depending on whether or not checkValue == nsnull, not checkMsg.
00547   PRBool * check_value = checkValue;
00548   if (savePassword != SINGSIGN_SAVE_PASSWORD_PERMANENTLY) {
00549     check_string = nsnull;
00550     check_value = nsnull;
00551   } else if (SI_GetBoolPref(pref_Crypto, PR_FALSE)) {
00552     check_string = Wallet_Localize("SaveTheseValuesEncrypted");
00553   } else {
00554     check_string = Wallet_Localize("SaveTheseValuesObscured");
00555   }
00556 
00557   PRBool confirmed = PR_FALSE;  
00558   res = dialog->PromptUsernameAndPassword(prompt_string,
00559                                           szMessage,
00560                                           username, password,
00561                                           check_string,
00562                                           check_value,
00563                                           &confirmed);
00564 
00565   if (dialogTitle == nsnull || dialogTitle[0] == 0)
00566     Recycle(prompt_string);
00567   if (check_string)
00568     Recycle(check_string);
00569   
00570   if (NS_FAILED(res)) {
00571     return res;
00572   }
00573   if (confirmed) {
00574     return NS_OK;
00575   } else {
00576     return NS_ERROR_FAILURE; /* user pressed cancel */
00577   }
00578 }
00579 
00580 
00581 /********************
00582  * Utility Routines *
00583  ********************/
00584 
00585 /* StrAllocCopy should really be defined elsewhere */
00586 #include "plstr.h"
00587 #include "prmem.h"
00588 
00589 #undef StrAllocCopy
00590 #define StrAllocCopy(dest, src) Local_SACopy (&(dest), src)
00591 static char *
00592 Local_SACopy(char **destination, const char *source) {
00593   if(*destination) {
00594     PL_strfree(*destination);
00595   }
00596   *destination = PL_strdup(source);
00597   return *destination;
00598 }
00599 
00600 #ifdef WALLET_PASSWORDMANAGER_DEFAULT_IS_OFF
00601 /* If user-entered password is "********", then generate a random password */
00602 static void
00603 si_Randomize(nsString& password) {
00604   PRIntervalTime randomNumber;
00605   int i;
00606   const char * hexDigits = "0123456789AbCdEf";
00607   if (password.EqualsLiteral("********")) {
00608     randomNumber = PR_IntervalNow();
00609     for (i=0; i<8; i++) {
00610       password.SetCharAt(hexDigits[randomNumber%16], i);
00611       randomNumber = randomNumber/16;
00612     }
00613   }
00614 }
00615 #endif
00616 
00617 
00618 /***********************
00619  * Encryption Routines *
00620  ***********************/
00621 
00622 static PRBool
00623 si_CompareEncryptedToCleartext(const nsString& crypt, const nsString& text) {
00624   nsAutoString decrypted;
00625   if (NS_FAILED(Wallet_Decrypt(crypt, decrypted))) {
00626     return PR_FALSE;
00627   }
00628   return (decrypted == text);
00629 }
00630 
00631 static PRBool
00632 si_CompareEncryptedToEncrypted(const nsString& crypt1, const nsString& crypt2) {
00633   nsAutoString decrypted1;
00634   nsAutoString decrypted2;
00635   if (NS_FAILED(Wallet_Decrypt(crypt1, decrypted1))) {
00636     return PR_FALSE;
00637   }
00638   if (NS_FAILED(Wallet_Decrypt(crypt2, decrypted2))) {
00639     return PR_FALSE;
00640   }
00641   return (decrypted1 == decrypted2);
00642 }
00643 
00644 
00645 /************************
00646  * Managing Signon List *
00647  ************************/
00648 
00649 static PRUint32
00650 SecondsFromPRTime(PRTime prTime) {
00651   PRInt64 microSecondsPerSecond, intermediateResult;
00652   PRUint32 seconds;
00653   
00654   LL_I2L(microSecondsPerSecond, PR_USEC_PER_SEC);
00655   LL_DIV(intermediateResult, prTime, microSecondsPerSecond);
00656   LL_L2UI(seconds, intermediateResult);
00657   return seconds;
00658 }
00659 
00660 MOZ_DECL_CTOR_COUNTER(si_SignonDataStruct)
00661 
00662 si_SignonDataStruct::si_SignonDataStruct()
00663   : isPassword(PR_FALSE)
00664 {
00665   MOZ_COUNT_CTOR(si_SignonDataStruct);
00666 }
00667 si_SignonDataStruct::~si_SignonDataStruct()
00668 {
00669   MOZ_COUNT_DTOR(si_SignonDataStruct);
00670 }
00671 
00672 MOZ_DECL_CTOR_COUNTER(si_SignonUserStruct)
00673 
00674 class si_SignonUserStruct {
00675 public:
00676   si_SignonUserStruct()
00677   {
00678     MOZ_COUNT_CTOR(si_SignonUserStruct);
00679   }
00680   ~si_SignonUserStruct()
00681   {
00682     for (PRInt32 i = signonData_list.Count() - 1; i >= 0; i--) {
00683       delete NS_STATIC_CAST(si_SignonDataStruct*, signonData_list.ElementAt(i));
00684     }
00685     MOZ_COUNT_DTOR(si_SignonUserStruct);
00686   }
00687   PRUint32 time;
00688   nsVoidArray signonData_list; // elements are si_SignonDataStruct
00689 };
00690 
00691 MOZ_DECL_CTOR_COUNTER(si_SignonURLStruct)
00692 
00693 class si_SignonURLStruct {
00694 public:
00695   si_SignonURLStruct() : passwordRealm(NULL), chosen_user(NULL)
00696   {
00697     MOZ_COUNT_CTOR(si_SignonURLStruct);
00698   }
00699   ~si_SignonURLStruct()
00700   {
00701     MOZ_COUNT_DTOR(si_SignonURLStruct);
00702   }
00703   char * passwordRealm;
00704   si_SignonUserStruct* chosen_user; /* this is a state variable */
00705   nsVoidArray signonUser_list;
00706 };
00707 
00708 MOZ_DECL_CTOR_COUNTER(si_Reject)
00709 
00710 class si_Reject {
00711 public:
00712   si_Reject() : passwordRealm(NULL)
00713   {
00714     MOZ_COUNT_CTOR(si_Reject);
00715   }
00716   ~si_Reject()
00717   {
00718     MOZ_COUNT_DTOR(si_Reject);
00719   }
00720   char * passwordRealm;
00721   nsString userName;
00722 };
00723 
00724 static nsVoidArray * si_signon_list=0;
00725 static nsVoidArray * si_reject_list=0;
00726 #define LIST_COUNT(list) (list ? list->Count() : 0)
00727 static PRBool si_signon_list_changed = PR_FALSE;
00728 
00729 /*
00730  * Get the URL node for a given URL name
00731  *
00732  * This routine is called only when holding the signon lock!!!
00733  */
00734 static si_SignonURLStruct *
00735 si_GetURL(const char * passwordRealm) {
00736   si_SignonURLStruct * url;
00737   if (!passwordRealm) {
00738     /* no passwordRealm specified, return first URL (returns NULL if not URLs) */
00739     if (LIST_COUNT(si_signon_list)==0) {
00740       return NULL;
00741     }
00742     /* XXX how can this be right -- seems wrong to give back a random password */
00743     LOG(("  returning first element in the signon list\n"));
00744     return (si_SignonURLStruct *) (si_signon_list->ElementAt(0));
00745   }
00746 
00747   PRInt32 urlCount = LIST_COUNT(si_signon_list);
00748   if (urlCount) {
00749     // If the last char of passwordRealm is '/' then strip it before making comparison.
00750     nsCAutoString realmWithoutTrailingSlash(passwordRealm);
00751     if (!realmWithoutTrailingSlash.IsEmpty() && realmWithoutTrailingSlash.Last() == '/')
00752       realmWithoutTrailingSlash.Truncate(realmWithoutTrailingSlash.Length()-1);
00753 
00754     for (PRInt32 i=0; i<urlCount; i++) {
00755       url = NS_STATIC_CAST(si_SignonURLStruct*, si_signon_list->ElementAt(i));
00756       if(url->passwordRealm && !PL_strcmp(realmWithoutTrailingSlash.get(), url->passwordRealm)) {
00757         return url;
00758       }
00759     }
00760   }
00761   return (NULL);
00762 }
00763 
00768 class si_SignonCompositeURLStruct : public si_SignonURLStruct {
00769 public:
00770   si_SignonURLStruct *primaryUrl;
00771   si_SignonURLStruct *legacyUrl;
00772 };
00773 
00774 static si_SignonCompositeURLStruct * si_composite_url=0;
00775 
00776 #if defined(SINGSIGN_LOGGING)
00777 static void
00778 si_DumpUserList(nsVoidArray &list)
00779 {
00780   LOG(("dumping user list:\n"));
00781   PRInt32 i, j, user_count = list.Count(), data_count;
00782   for (i=0; i<user_count; ++i) {
00783     si_SignonUserStruct *user = (si_SignonUserStruct *) list[i];
00784     LOG((" user[%d]\n", i));
00785     data_count = user->signonData_list.Count();
00786     for (j=0; j<data_count; ++j) {
00787       si_SignonDataStruct *data = (si_SignonDataStruct *) user->signonData_list[j];
00788       LOG(("  (%s,%s)\n",
00789           NS_ConvertUCS2toUTF8(data->name).get(),
00790           NS_ConvertUCS2toUTF8(data->value).get()));
00791     }
00792   }
00793 }
00794 #endif
00795 
00796 static si_SignonURLStruct *
00797 si_GetCompositeURL(const char *primaryRealm, const char *legacyRealm)
00798 {
00799   si_SignonURLStruct *primaryUrl, *legacyUrl;
00800 
00801   primaryUrl = si_GetURL(primaryRealm);
00802 
00803   if (legacyRealm)
00804     legacyUrl = si_GetURL(legacyRealm);
00805   else
00806     legacyUrl = nsnull;
00807 
00808   if (primaryUrl && legacyUrl) {
00809     LOG((">>> building composite URL struct\n"));
00810     if (si_composite_url) {
00811       NS_ERROR("si_composite_url already in use");
00812       return NULL;
00813     }
00814     si_composite_url = new si_SignonCompositeURLStruct;
00815     if (!si_composite_url)
00816       return NULL;
00817 
00818     si_composite_url->primaryUrl = primaryUrl;
00819     si_composite_url->legacyUrl = legacyUrl;
00820 
00821     si_composite_url->signonUser_list.AppendElements(primaryUrl->signonUser_list);
00822     si_composite_url->signonUser_list.AppendElements(legacyUrl->signonUser_list);
00823 
00824 #if defined(SINGSIGN_LOGGING)
00825     si_DumpUserList(si_composite_url->signonUser_list);
00826 #endif
00827 
00828     /* need to transfer the chosen_user state variable */
00829     if (primaryUrl->chosen_user)
00830       si_composite_url->chosen_user = primaryUrl->chosen_user;
00831     else if (legacyUrl->chosen_user) {
00832       si_SignonUserStruct *chosen_user = legacyUrl->chosen_user;
00833       PRInt32 index;
00834       /* XXX fixup chosen_user -- THIS SHOULD NOT BE NECESSARY */
00835       index = legacyUrl->signonUser_list.IndexOf(chosen_user);
00836       if (index < 0) {
00837         index = primaryUrl->signonUser_list.IndexOf(chosen_user);
00838         if (index >= 0)
00839           primaryUrl->chosen_user = chosen_user;
00840         legacyUrl->chosen_user = NULL;
00841       }
00842       /* move first element of legacy user list to front */
00843       index = si_composite_url->signonUser_list.IndexOf(chosen_user);
00844       if (index > 0)
00845         si_composite_url->signonUser_list.MoveElement(index, 0);
00846       si_composite_url->chosen_user = chosen_user;
00847     }
00848     else
00849       si_composite_url->chosen_user = NULL;
00850 
00851 #if defined(SINGSIGN_LOGGING)
00852     LOG(("after chosen_user fixup [chosen_user=%x]:\n", si_composite_url->chosen_user));
00853     si_DumpUserList(si_composite_url->signonUser_list);
00854 #endif
00855 
00856     return si_composite_url;
00857   }
00858 
00859   if (primaryUrl)
00860     return primaryUrl;
00861 
00862   return legacyUrl;
00863 }
00864 
00865 static PRInt32
00866 si_SetChosenUser(si_SignonURLStruct *url, si_SignonUserStruct *chosen_user)
00867 {
00868   PRInt32 index;
00869 
00870   index = url->signonUser_list.IndexOf(chosen_user);
00871   if (index < 0) {
00872       url->chosen_user = NULL;
00873       return -1;
00874   }
00875 
00876   url->chosen_user = chosen_user;
00877   return index; 
00878 }
00879 
00880 static void
00881 si_ReleaseCompositeURL(si_SignonURLStruct *url)
00882 {
00883   if (url == si_composite_url) {
00884     si_SignonUserStruct *chosen_user = url->chosen_user;
00885     /* need to transfer the chosen_user state variable */
00886     if (chosen_user) {
00887       PRInt32 index;
00888 
00889       /* store chosen_user */
00890       index = si_SetChosenUser(url = si_composite_url->primaryUrl, chosen_user);
00891       if (index >= 0)
00892         si_composite_url->legacyUrl->chosen_user = NULL;
00893       else
00894         index = si_SetChosenUser(url = si_composite_url->legacyUrl, chosen_user);
00895       NS_ASSERTION(index >= 0, "chosen_user not found");
00896 
00897       /* need to move chosen_user to front of list */
00898       url->signonUser_list.MoveElement(index, 0);
00899     }
00900     else {
00901       si_composite_url->primaryUrl->chosen_user = NULL;
00902       si_composite_url->legacyUrl->chosen_user = NULL;
00903     }
00904     si_composite_url->primaryUrl = NULL;
00905     si_composite_url->legacyUrl = NULL;
00906     si_composite_url->chosen_user = NULL;
00907     si_composite_url->signonUser_list.Clear();
00908 
00909     delete si_composite_url;
00910     si_composite_url = NULL;
00911   }
00912 }
00913 
00914 /* Remove a user node from a given URL node */
00915 static PRBool
00916 si_RemoveUser(const char *passwordRealm, const nsString& userName, PRBool save, PRBool loginFailure, PRBool notify, PRBool first = PR_FALSE) {
00917   si_SignonURLStruct * url;
00918   si_SignonUserStruct * user;
00919   si_SignonDataStruct * data;
00920 
00921   si_lock_signon_list();
00922 
00923   /* get URL corresponding to host */
00924   url = si_GetURL(passwordRealm);
00925   if (!url) {
00926     /* URL not found */
00927     si_unlock_signon_list();
00928     return PR_FALSE;
00929   }
00930 
00931   /* free the data in each node of the specified user node for this URL */
00932   if (first) {
00933 
00934     /* remove the first user */
00935     user = NS_STATIC_CAST(si_SignonUserStruct *,
00936                           url->signonUser_list.ElementAt(0));
00937 
00938   } else {
00939 
00940     /* find the specified user */
00941     PRInt32 userCount = url->signonUser_list.Count();
00942     for (PRInt32 i=0; i<userCount; i++) {
00943       user = NS_STATIC_CAST(si_SignonUserStruct*, url->signonUser_list.ElementAt(i));
00944       PRInt32 dataCount = user->signonData_list.Count();
00945       for (PRInt32 ii=0; ii<dataCount; ii++) {
00946         data = NS_STATIC_CAST(si_SignonDataStruct*, user->signonData_list.ElementAt(ii));
00947         if (si_CompareEncryptedToCleartext(data->value, userName)) {
00948           goto foundUser;
00949         }
00950       }
00951     }
00952     si_unlock_signon_list();
00953     return PR_FALSE; /* user not found so nothing to remove */
00954     foundUser: ;
00955   }
00956 
00957   /* free the user node */
00958   url->signonUser_list.RemoveElement(user);
00959   delete user;
00960 
00961   /* remove this URL if it contains no more users */
00962   if (url->signonUser_list.Count() == 0) {
00963     PR_Free(url->passwordRealm);
00964     si_signon_list->RemoveElement(url);
00965     delete url;
00966   }
00967 
00968   /* write out the change to disk */
00969   if (save) {
00970     si_signon_list_changed = PR_TRUE;
00971     si_SaveSignonDataLocked("signons", notify);
00972   }
00973 
00974   si_unlock_signon_list();
00975   return PR_TRUE;
00976 }
00977 
00978 nsresult
00979 SINGSIGN_RemoveUser(const char *host, const PRUnichar *user, PRBool notify) {
00980   PRBool rv = si_RemoveUser(host, nsDependentString(user), PR_TRUE, PR_FALSE, notify);
00981   return rv ? NS_OK : NS_ERROR_FAILURE;
00982 }
00983 
00984 nsresult
00985 SINGSIGN_RemoveUserAfterLoginFailure(const char *host, const PRUnichar *user, PRBool notify) {
00986   PRBool rv = si_RemoveUser(host, nsDependentString(user), PR_TRUE, PR_TRUE, notify);
00987   return rv ? NS_OK : NS_ERROR_FAILURE;
00988 }
00989 
00990 static void
00991 si_FreeReject(si_Reject * reject);
00992 
00993 nsresult
00994 SINGSIGN_RemoveReject(const char *host) {
00995   si_Reject* reject;
00996   nsresult rv = NS_ERROR_FAILURE;
00997 
00998   /* step backwards through all rejects */
00999   si_lock_signon_list();
01000   PRInt32 rejectCount = LIST_COUNT(si_reject_list);
01001   while (rejectCount>0) {
01002     rejectCount--;
01003     reject = NS_STATIC_CAST(si_Reject*, si_reject_list->ElementAt(rejectCount));
01004     if (reject && !PL_strcmp(reject->passwordRealm, host)) {
01005       si_FreeReject(reject);
01006       si_signon_list_changed = PR_TRUE;
01007       rv = NS_OK;
01008     }
01009   }
01010   si_SaveSignonDataLocked("rejects", PR_FALSE);
01011   si_unlock_signon_list();
01012   return rv;
01013 }
01014 
01015 static void
01016 si_PutReject(const char * passwordRealm, const nsString& userName, PRBool save);
01017 
01018 nsresult
01019 SINGSIGN_AddReject(const char *host /*, const char *userName*/) {
01020   si_PutReject(host, nsString(/*thisParameter_isObsolete*/), PR_TRUE);
01021 // @see http://bonsai.mozilla.org/cvsblame.cgi?file=mozilla/extensions/wallet/src/singsign.cpp&rev=1.212&mark=1693#1650
01022   return NS_OK;
01023 }
01024 
01025 /* Determine if a specified url/user exists */
01026 static PRBool
01027 si_CheckForUser(const char *passwordRealm, const nsString& userName) {
01028   si_SignonURLStruct * url;
01029   si_SignonUserStruct * user;
01030   si_SignonDataStruct * data;
01031 
01032   /* do nothing if signon preference is not enabled */
01033   if (!si_GetSignonRememberingPref()) {
01034     return PR_FALSE;
01035   }
01036 
01037   si_lock_signon_list();
01038 
01039   /* get URL corresponding to passwordRealm */
01040   url = si_GetURL(passwordRealm);
01041   if (!url) {
01042     /* URL not found */
01043     si_unlock_signon_list();
01044     return PR_FALSE;
01045   }
01046 
01047   /* find the specified user */
01048   PRInt32 userCount = url->signonUser_list.Count();
01049   for (PRInt32 i=0; i<userCount; i++) {
01050     user = NS_STATIC_CAST(si_SignonUserStruct*, url->signonUser_list.ElementAt(i));
01051     PRInt32 dataCount = user->signonData_list.Count();
01052     for (PRInt32 ii=0; ii<dataCount; ii++) {
01053       data = NS_STATIC_CAST(si_SignonDataStruct*, user->signonData_list.ElementAt(ii));
01054       if (si_CompareEncryptedToCleartext(data->value, userName)) {
01055         si_unlock_signon_list();
01056         return PR_TRUE;
01057       }
01058     }
01059   }
01060   si_unlock_signon_list();
01061   return PR_FALSE; /* user not found */
01062 }
01063 
01064 /*
01065  * Get first data node that is not a password
01066  */
01067 
01068 static si_SignonDataStruct *
01069 si_GetFirstNonPasswordData(si_SignonUserStruct* user) {
01070   PRInt32 dataCount = user->signonData_list.Count();
01071   for (PRInt32 j=0; j<dataCount; j++) {
01072     si_SignonDataStruct * data =
01073       NS_STATIC_CAST(si_SignonDataStruct *, user->signonData_list.ElementAt(j));
01074     if (!data->isPassword) {
01075       return data;
01076     }
01077   }
01078   return nsnull;
01079 }
01080 
01081 /*
01082  * Get the user node for a given URL
01083  *
01084  * This routine is called only when holding the signon lock!!!
01085  *
01086  * This routine is called only if signon pref is enabled!!!
01087  */
01088 static si_SignonUserStruct*
01089 si_GetUser(nsIPrompt* dialog, const char* passwordRealm, const char *legacyRealm,
01090            PRBool pickFirstUser, const nsString& userText, PRUint32 formNumber) {
01091   si_SignonURLStruct* url;
01092   si_SignonUserStruct* user = nsnull;
01093   si_SignonDataStruct* data;
01094 
01095   /* get to node for this URL */
01096   url = si_GetCompositeURL(passwordRealm, legacyRealm);
01097 
01098   if (url != NULL) {
01099 
01100     /* node for this URL was found */
01101     PRInt32 user_count;
01102     if ((user_count = url->signonUser_list.Count()) == 1) {
01103 
01104       /* only one set of data exists for this URL so select it */
01105       user = NS_STATIC_CAST(si_SignonUserStruct *,
01106                             url->signonUser_list.ElementAt(0));
01107       url->chosen_user = user;
01108 
01109     } else if (pickFirstUser) {
01110       PRInt32 userCount = url->signonUser_list.Count();
01111       for (PRInt32 i=0; i<userCount; i++) {
01112         user = NS_STATIC_CAST(si_SignonUserStruct*, url->signonUser_list.ElementAt(i));
01113         /* consider first data node to be the identifying item */
01114         data = NS_STATIC_CAST(si_SignonDataStruct *,
01115                               user->signonData_list.ElementAt(0));
01116         if (data->name != userText) {
01117           /* name of current data item does not match name in data node */
01118           continue;
01119         }
01120         break;
01121       }
01122       url->chosen_user = user;
01123 
01124     } else {
01125       /* multiple users for this URL so a choice needs to be made */
01126       PRUnichar ** list;
01127       PRUnichar ** list2;
01128       si_SignonUserStruct** users;
01129       si_SignonUserStruct** users2;
01130       list = (PRUnichar**)PR_Malloc(user_count*sizeof(PRUnichar*));
01131       users = (si_SignonUserStruct **) PR_Malloc(user_count*sizeof(si_SignonUserStruct*));
01132       list2 = list;
01133       users2 = users;
01134 
01135       /* step through set of user nodes for this URL and create list of
01136        * first data node of each (presumably that is the user name).
01137        * Note that the user nodes are already ordered by
01138        * most-recently-used so the first one in the list is the most
01139        * likely one to be chosen.
01140        */
01141       user_count = 0;
01142       PRInt32 userCount = url->signonUser_list.Count();
01143       for (PRInt32 i=0; i<userCount; i++) {
01144         user = NS_STATIC_CAST(si_SignonUserStruct*, url->signonUser_list.ElementAt(i));
01145         /* consider first data node to be the identifying item */
01146         data = NS_STATIC_CAST(si_SignonDataStruct *,
01147                               user->signonData_list.ElementAt(0));
01148         if (data->name != userText) {
01149           /* name of current data item does not match name in data node */
01150           continue;
01151         }
01152         nsAutoString userName;
01153         data = si_GetFirstNonPasswordData(user);
01154         if (NS_SUCCEEDED(Wallet_Decrypt (data->value, userName))) {
01155           *(list2++) = ToNewUnicode(userName);
01156           *(users2++) = user;
01157           user_count++;
01158         } else {
01159           break;
01160         }
01161       }
01162 
01163       /* have user select a username from the list */
01164       PRUnichar * selectUser = Wallet_Localize("SelectUser");
01165       if (user_count == 0) {
01166         /* not first data node for any saved user, so simply pick first user */
01167         if (url->chosen_user) {
01168           user = url->chosen_user;
01169         } else {
01170           /* no user selection had been made for first data node */
01171           user = NULL;
01172         } 
01173       } else if (user_count == 1) {
01174         /* only one user for this form at this url, so select it */
01175         user = users[0];
01176       } else if ((user_count > 1) && si_SelectDialog(selectUser, dialog, list, &user_count, formNumber)) {
01177         /* user pressed OK */
01178         if (user_count == -1) {
01179           user_count = 0; /* user didn't select, so use first one */
01180         }
01181         user = users[user_count]; /* this is the selected item */
01182         /* item selected is now most-recently used, put at head of list */
01183         url->signonUser_list.RemoveElement(user);
01184         url->signonUser_list.InsertElementAt(user, 0);
01185       } else {
01186         user = NULL;
01187       }
01188       Recycle(selectUser);
01189       url->chosen_user = user;
01190       while (--list2 > list) {
01191         Recycle(*list2);
01192       }
01193       PR_Free(list);
01194       PR_Free(users);
01195 
01196       /* if we don't remove the URL from the cache at this point, the
01197        * cached copy will be brought containing the last-used username
01198        * rather than the username that was just selected
01199        */
01200 
01201 #ifdef junk
01202       NET_RemoveURLFromCache(NET_CreateURLStruct((char *)passwordRealm, NET_DONT_RELOAD));
01203 #endif
01204 
01205     }
01206     si_ReleaseCompositeURL(url);
01207   } else {
01208     user = NULL;
01209   }
01210   return user;
01211 }
01212 
01213 /*
01214  * Get a specific user node for a given URL
01215  *
01216  * This routine is called only when holding the signon lock!!!
01217  *
01218  * This routine is called only if signon pref is enabled!!!
01219  */
01220 static si_SignonUserStruct*
01221 si_GetSpecificUser(const char* passwordRealm, const nsString& userName, const nsString& userText) {
01222   si_SignonURLStruct* url;
01223   si_SignonUserStruct* user;
01224   si_SignonDataStruct* data;
01225 
01226   /* get to node for this URL */
01227   url = si_GetURL(passwordRealm);
01228   if (url != NULL) {
01229 
01230     /* step through set of user nodes for this URL looking for specified username */
01231     PRInt32 userCount2 = url->signonUser_list.Count();
01232     for (PRInt32 i2=0; i2<userCount2; i2++) {
01233       user = NS_STATIC_CAST(si_SignonUserStruct*, url->signonUser_list.ElementAt(i2));
01234       /* consider first data node to be the identifying item */
01235       data = NS_STATIC_CAST(si_SignonDataStruct *,
01236                             user->signonData_list.ElementAt(0));
01237       if (data->name != userText) {
01238         /* desired username text does not match name in data node */
01239         continue;
01240       }
01241       if (!si_CompareEncryptedToCleartext(data->value, userName)) {
01242         /* desired username value does not match value in data node */
01243         continue;
01244       }
01245       return user;
01246     }
01247 
01248     /* if we don't remove the URL from the cache at this point, the
01249      * cached copy will be brought containing the last-used username
01250      * rather than the username that was just selected
01251      */
01252 
01253 #ifdef junk
01254     NET_RemoveURLFromCache(NET_CreateURLStruct((char *)passwordRealm, NET_DONT_RELOAD));
01255 #endif
01256 
01257   }
01258   return NULL;
01259 }
01260 
01261 /*
01262  * Get the url and user for which a change-of-password is to be applied
01263  *
01264  * This routine is called only when holding the signon lock!!!
01265  *
01266  * This routine is called only if signon pref is enabled!!!
01267  */
01268 static si_SignonUserStruct*
01269 si_GetURLAndUserForChangeForm(nsIPrompt* dialog, const nsString& password)
01270 {
01271   si_SignonURLStruct* url;
01272   si_SignonUserStruct* user;
01273   si_SignonDataStruct * data;
01274   PRInt32 user_count;
01275 
01276   PRUnichar ** list;
01277   PRUnichar ** list2;
01278   si_SignonUserStruct** users;
01279   si_SignonUserStruct** users2;
01280   si_SignonURLStruct** urls;
01281   si_SignonURLStruct** urls2;
01282 
01283   /* get count of total number of user nodes at all url nodes */
01284   user_count = 0;
01285   PRInt32 urlCount = LIST_COUNT(si_signon_list);
01286   for (PRInt32 i=0; i<urlCount; i++) {
01287     url = NS_STATIC_CAST(si_SignonURLStruct*, si_signon_list->ElementAt(i));
01288     PRInt32 userCount = url->signonUser_list.Count();
01289     for (PRInt32 ii=0; ii<userCount; ii++) {
01290       user = NS_STATIC_CAST(si_SignonUserStruct*, url->signonUser_list.ElementAt(ii));
01291       user_count++;
01292     }
01293   }
01294 
01295   /* avoid malloc of zero */
01296   if( user_count == 0 )
01297   {
01298     return NULL;
01299   }
01300   
01301   /* allocate lists for maximumum possible url and user names */
01302   list = (PRUnichar**)PR_Malloc(user_count*sizeof(PRUnichar*));
01303   users = (si_SignonUserStruct **) PR_Malloc(user_count*sizeof(si_SignonUserStruct*));
01304   urls = (si_SignonURLStruct **)PR_Malloc(user_count*sizeof(si_SignonUserStruct*));
01305   list2 = list;
01306   users2 = users;
01307   urls2 = urls;
01308     
01309   /* step through set of URLs and users and create list of each */
01310   user_count = 0;
01311   PRInt32 urlCount2 = LIST_COUNT(si_signon_list);
01312   for (PRInt32 i2=0; i2<urlCount2; i2++) {
01313     url = NS_STATIC_CAST(si_SignonURLStruct*, si_signon_list->ElementAt(i2));
01314     PRInt32 userCount = url->signonUser_list.Count();
01315     for (PRInt32 i3=0; i3<userCount; i3++) {
01316       user = NS_STATIC_CAST(si_SignonUserStruct*, url->signonUser_list.ElementAt(i3));
01317       /* find saved password and see if it matches password user just entered */
01318       PRInt32 dataCount = user->signonData_list.Count();
01319       for (PRInt32 i4=0; i4<dataCount; i4++) {
01320         data = NS_STATIC_CAST(si_SignonDataStruct*, user->signonData_list.ElementAt(i4));
01321         if (data->isPassword && si_CompareEncryptedToCleartext(data->value, password)) {
01322           /* passwords match so add entry to list */
01323           /* consider first data node to be the identifying item */
01324           data = NS_STATIC_CAST(si_SignonDataStruct *,
01325                                 user->signonData_list.ElementAt(0));
01326 
01327           nsAutoString userName;
01328           if (NS_SUCCEEDED(Wallet_Decrypt (data->value, userName))) {
01329             nsAutoString temp; temp.AssignASCII(url->passwordRealm); // XXX non-ascii realms?
01330             temp.AppendLiteral(":");
01331             temp.Append(userName);
01332 
01333             *list2 = ToNewUnicode(temp);
01334             list2++;
01335             *(users2++) = user;
01336             *(urls2++) = url;
01337             user_count++;
01338           }
01339           break;
01340         }
01341       }
01342     }
01343   }
01344 
01345   /* query user */
01346   PRUnichar * msg = Wallet_Localize("SelectUserWhosePasswordIsBeingChanged");
01347 //@@@@ is 0 correct?
01348   if (user_count && si_SelectDialog(msg, dialog, list, &user_count, 0)) {
01349     user = users[user_count];
01350     url = urls[user_count];
01351     /*
01352      * since this user node is now the most-recently-used one, move it
01353      * to the head of the user list so that it can be favored for
01354      * re-use the next time this form is encountered
01355      */
01356     url->signonUser_list.RemoveElement(user);
01357     url->signonUser_list.InsertElementAt(user, 0);
01358     si_signon_list_changed = PR_TRUE;
01359     si_SaveSignonDataLocked("signons", PR_TRUE);
01360   } else {
01361     user = NULL;
01362   }
01363   Recycle(msg);
01364 
01365   /* free allocated strings */
01366   while (--list2 > list) {
01367     Recycle(*list2);
01368   }
01369   PR_Free(list);
01370   PR_Free(users);
01371   PR_Free(urls);
01372   return user;
01373 }
01374 
01375 /*
01376  * Remove all the signons and free everything
01377  */
01378 
01379 void
01380 SI_RemoveAllSignonData() {
01381   if (si_PartiallyLoaded) {
01382     /* repeatedly remove first user node of first URL node */
01383     while (si_RemoveUser(NULL, EmptyString(), PR_FALSE, PR_FALSE, PR_FALSE, PR_TRUE)) {
01384     }
01385   }
01386   si_PartiallyLoaded = PR_FALSE;
01387 
01388   if (si_reject_list) {
01389     si_Reject * reject;
01390     while (LIST_COUNT(si_reject_list)>0) {
01391       reject = NS_STATIC_CAST(si_Reject*, si_reject_list->ElementAt(0));
01392       if (reject) {
01393         si_FreeReject(reject);
01394         si_signon_list_changed = PR_TRUE;
01395       }
01396     }
01397     delete si_reject_list;
01398     si_reject_list = nsnull;
01399   }
01400   delete si_signon_list;
01401   si_signon_list = nsnull;
01402 }
01403 
01404 void
01405 SI_DeleteAll() {
01406   if (si_PartiallyLoaded) {
01407     /* repeatedly remove first user node of first URL node */
01408     while (si_RemoveUser(NULL, EmptyString(), PR_FALSE, PR_FALSE, PR_TRUE, PR_TRUE)) {
01409     }
01410   }
01411   si_PartiallyLoaded = PR_FALSE;
01412   si_signon_list_changed = PR_TRUE;
01413   si_SaveSignonDataLocked("signons", PR_TRUE);
01414 }
01415 
01416 void
01417 SI_ClearUserData() {
01418   SI_RemoveAllSignonData();
01419   gLoadedUserData = PR_FALSE;
01420 }
01421 
01422 void
01423 SI_DeletePersistentUserData() {
01424 
01425   if (signonFileName && signonFileName[0]) {
01426     nsCOMPtr<nsIFile> file;
01427     nsresult rv = Wallet_ProfileDirectory(getter_AddRefs(file));
01428     if (NS_SUCCEEDED(rv)) {
01429       rv = file->AppendNative(nsDependentCString(signonFileName));
01430       if (NS_SUCCEEDED(rv))
01431         file->Remove(PR_FALSE);
01432     }
01433   }
01434 }
01435 
01436 /****************************
01437  * Managing the Reject List *
01438  ****************************/
01439 
01440 static void
01441 si_FreeReject(si_Reject * reject) {
01442 
01443   /*
01444    * This routine should only be called while holding the
01445    * signon list lock
01446    */
01447 
01448   if(!reject) {
01449       return;
01450   }
01451   si_reject_list->RemoveElement(reject);
01452   PR_FREEIF(reject->passwordRealm);
01453   delete reject;
01454 }
01455 
01456 static PRBool
01457 si_CheckForReject(const char * passwordRealm, const nsString& userName) {
01458   si_Reject * reject;
01459 
01460   si_lock_signon_list();
01461   if (si_reject_list) {
01462     PRInt32 rejectCount = LIST_COUNT(si_reject_list);
01463     for (PRInt32 i=0; i<rejectCount; i++) {
01464       reject = NS_STATIC_CAST(si_Reject*, si_reject_list->ElementAt(i));
01465       if(!PL_strcmp(passwordRealm, reject->passwordRealm)) {
01466 // No need for username check on a rejectlist entry.  URL check is sufficient
01467 //    if(!PL_strcmp(userName, reject->userName) && !PL_strcmp(passwordRealm, reject->passwordRealm)) {
01468         si_unlock_signon_list();
01469         return PR_TRUE;
01470       }
01471     }
01472   }
01473   si_unlock_signon_list();
01474   return PR_FALSE;
01475 }
01476 
01477 static void
01478 si_PutReject(const char * passwordRealm, const nsString& userName, PRBool save) {
01479   char * passwordRealm2=NULL;
01480   nsAutoString userName2;
01481   si_Reject * reject = new si_Reject;
01482 
01483   if (reject) {
01484     if(!si_reject_list) {
01485       si_reject_list = new nsVoidArray();
01486       if(!si_reject_list) {
01487         delete reject;
01488         return;
01489       }
01490     }
01491 
01492     /*
01493      * lock the signon list
01494      *  Note that, for efficiency, SI_LoadSignonData already sets the lock
01495      *  before calling this routine whereas none of the other callers do.
01496      *  So we need to determine whether or not we were called from
01497      *  SI_LoadSignonData before setting or clearing the lock.  We can
01498      *  determine this by testing "save" since only SI_LoadSignonData
01499      *  passes in a value of PR_FALSE for "save".
01500      */
01501     if (save) {
01502       si_lock_signon_list();
01503     }
01504 
01505     StrAllocCopy(passwordRealm2, passwordRealm);
01506     userName2 = userName;
01507     reject->passwordRealm = passwordRealm2;
01508     reject->userName = userName2;
01509 
01510 #ifdef alphabetize
01511     /* add it to the list in alphabetical order */
01512     si_Reject * tmp_reject;
01513     PRBool rejectAdded = PR_FALSE;
01514     PRInt32 rejectCount = LIST_COUNT(si_reject_list);
01515     for (PRInt32 i = 0; i<rejectCount; ++i) {
01516       tmp_reject = NS_STATIC_CAST(si_Reject *, si_reject_list->ElementAt(i));
01517       if (tmp_reject) {
01518         if (PL_strcasecmp(reject->passwordRealm, tmp_reject->passwordRealm)<0) {
01519           si_reject_list->InsertElementAt(reject, i);
01520           rejectAdded = PR_TRUE;
01521           break;
01522         }
01523       }
01524     }
01525     if (!rejectAdded) {
01526       si_reject_list->AppendElement(reject);
01527     }
01528 #else
01529     /* add it to the end of the list */
01530     si_reject_list->AppendElement(reject);
01531 #endif
01532 
01533     if (save) {
01534       si_signon_list_changed = PR_TRUE;
01535       si_lock_signon_list();
01536       si_SaveSignonDataLocked("rejects", PR_TRUE);
01537       si_unlock_signon_list();
01538     }
01539   }
01540 }
01541 
01542 /*
01543  * Put data obtained from a submit form into the data structure for
01544  * the specified URL
01545  *
01546  * See comments below about state of signon lock when routine is called!!!
01547  *
01548  * This routine is called only if signon pref is enabled!!!
01549  */
01550 static void
01551 si_PutData(const char *passwordRealm, nsVoidArray *signonData, PRBool save) {
01552   PRBool added_to_list = PR_FALSE;
01553   si_SignonURLStruct * url;
01554   si_SignonUserStruct * user;
01555   si_SignonDataStruct * data;
01556   si_SignonDataStruct * data2;
01557   PRBool mismatch = PR_FALSE;
01558 
01559   /* discard this if the password is empty */
01560   PRInt32 count = signonData->Count();
01561   for (PRInt32 i=0; i<count; i++) {
01562     data2 = NS_STATIC_CAST(si_SignonDataStruct*, signonData->ElementAt(i));
01563     if (data2->isPassword && data2->value.IsEmpty()) {
01564       return;
01565     }
01566   }
01567 
01568   /* make sure the signon list exists */
01569   if (!si_signon_list) {
01570     si_signon_list = new nsVoidArray();
01571     if (!si_signon_list) {
01572       return;
01573     }
01574   }
01575 
01576   /*
01577    * lock the signon list
01578    *   Note that, for efficiency, SI_LoadSignonData already sets the lock
01579    *   before calling this routine whereas none of the other callers do.
01580    *   So we need to determine whether or not we were called from
01581    *   SI_LoadSignonData before setting or clearing the lock.  We can
01582    *   determine this by testing "save" since only SI_LoadSignonData passes
01583    *   in a value of PR_FALSE for "save".
01584    */
01585   if (save) {
01586     si_lock_signon_list();
01587   }
01588 
01589   /* find node in signon list having the same URL */
01590   if ((url = si_GetURL(passwordRealm)) == NULL) {
01591 
01592     /* doesn't exist so allocate new node to be put into signon list */
01593     url = new si_SignonURLStruct;
01594     if (!url) {
01595       if (save) {
01596         si_unlock_signon_list();
01597       }
01598       return;
01599     }
01600 
01601     /* fill in fields of new node */
01602        url->passwordRealm = nsnull;
01603        if (passwordRealm) {
01604        url->passwordRealm = PL_strdup(passwordRealm);
01605        }
01606 
01607     if (!url->passwordRealm) {
01608       if (save) {
01609         si_unlock_signon_list();
01610       }
01611       return;
01612     }
01613 
01614     /* put new node into signon list */
01615 
01616 #ifdef alphabetize
01617     /* add it to the list in alphabetical order */
01618     si_SignonURLStruct * tmp_URL;
01619     PRInt32 urlCount = LIST_COUNT(si_signon_list);
01620     for (PRInt32 ii = 0; ii<urlCount; ++ii) {
01621       tmp_URL = NS_STATIC_CAST(si_SignonURLStruct *, si_signon_list->ElementAt(ii));
01622       if (tmp_URL) {
01623         if (PL_strcasecmp(url->passwordRealm, tmp_URL->passwordRealm)<0) {
01624           si_signon_list->InsertElementAt(url, ii);
01625           added_to_list = PR_TRUE;
01626           break;
01627         }
01628       }
01629     }
01630     if (!added_to_list) {
01631       si_signon_list->AppendElement(url);
01632     }
01633 #else
01634     /* add it to the end of the list */
01635     si_signon_list->AppendElement(url);
01636 #endif
01637   }
01638 
01639   /* initialize state variables in URL node */
01640   url->chosen_user = NULL;
01641 
01642   /*
01643    * see if a user node with data list matching new data already exists
01644    * (password fields will not be checked for in this matching)
01645    */
01646   PRInt32 userCount = url->signonUser_list.Count();
01647   for (PRInt32 i2=0; i2<userCount; i2++) {
01648     if (!save) {
01649       break; /* otherwise we could be asked for master password when loading signon data */
01650     }
01651     user = NS_STATIC_CAST(si_SignonUserStruct*, url->signonUser_list.ElementAt(i2));
01652     PRInt32 j = 0;
01653     PRInt32 dataCount = user->signonData_list.Count();
01654     for (PRInt32 i3=0; i3<dataCount; i3++) {
01655       data = NS_STATIC_CAST(si_SignonDataStruct*, user->signonData_list.ElementAt(i3));
01656       mismatch = PR_FALSE;
01657 
01658       /* check for match on name field and type field */
01659       if (j < signonData->Count()) {
01660         data2 = NS_STATIC_CAST(si_SignonDataStruct*, signonData->ElementAt(j));
01661         if ((data->isPassword == data2->isPassword) &&
01662             (data->name == data2->name)) {
01663 
01664           /* success, now check for match on value field if not password */
01665           if (si_CompareEncryptedToEncrypted(data->value, data2->value) || data->isPassword) {
01666             j++; /* success */
01667           } else {
01668             mismatch = PR_TRUE;
01669             break; /* value mismatch, try next user */
01670           }
01671         } else {
01672           mismatch = PR_TRUE;
01673           break; /* name or type mismatch, try next user */
01674         }
01675       }
01676     }
01677     if (!mismatch) {
01678 
01679       /* all names and types matched and all non-password values matched */
01680 
01681       /*
01682        * note: it is ok for password values not to match; it means
01683        * that the user has either changed his password behind our
01684        * back or that he previously mis-entered the password
01685        */
01686 
01687       /* update the saved password values */
01688       j = 0;
01689       PRInt32 dataCount2 = user->signonData_list.Count();
01690       for (PRInt32 i4=0; i4<dataCount2; i4++) {
01691         data = NS_STATIC_CAST(si_SignonDataStruct*, user->signonData_list.ElementAt(i4));
01692 
01693         /* update saved password */
01694         if ((j < signonData->Count()) && data->isPassword) {
01695           data2 = NS_STATIC_CAST(si_SignonDataStruct*, signonData->ElementAt(j));
01696           if (!si_CompareEncryptedToEncrypted(data->value, data2->value)) {
01697             si_signon_list_changed = PR_TRUE;
01698             data->value = data2->value;
01699             user->time = SecondsFromPRTime(PR_Now()); 
01700 /* commenting out because I don't see how such randomizing could ever have worked. */
01701 //          si_Randomize(data->value);
01702           }
01703         }
01704         j++;
01705       }
01706 
01707       /*
01708        * since this user node is now the most-recently-used one, move it
01709        * to the head of the user list so that it can be favored for
01710        * re-use the next time this form is encountered
01711        */
01712       url->signonUser_list.RemoveElement(user);
01713       url->signonUser_list.InsertElementAt(user, 0);
01714 
01715       /* return */
01716       if (save) {
01717         si_signon_list_changed = PR_TRUE;
01718         si_SaveSignonDataLocked("signons", PR_TRUE);
01719         si_unlock_signon_list();
01720       }
01721       return; /* nothing more to do since data already exists */
01722     }
01723   }
01724 
01725   /* user node with current data not found so create one */
01726   user = new si_SignonUserStruct;
01727   if (!user) {
01728     if (save) {
01729       si_unlock_signon_list();
01730     }
01731     return;
01732   }
01733 
01734   /* create and fill in data nodes for new user node */
01735   for (PRInt32 k=0; k<signonData->Count(); k++) {
01736 
01737     /* create signon data node */
01738     data = new si_SignonDataStruct;
01739     if (!data) {
01740       delete user;
01741       if (save) {
01742         si_unlock_signon_list();
01743       }
01744       return;
01745     }
01746     data2 = NS_STATIC_CAST(si_SignonDataStruct*, signonData->ElementAt(k));
01747     data->isPassword = data2->isPassword;
01748     data->name = data2->name;
01749     data->value = data2->value;
01750 /* commenting out because I don't see how such randomizing could ever have worked. */
01751 //  if (data->isPassword) {
01752 //    si_Randomize(data->value);
01753 //  }
01754     /* append new data node to end of data list */
01755     user->signonData_list.AppendElement(data);
01756   }
01757 
01758   /* append new user node to front of user list for matching URL */
01759     /*
01760      * Note that by appending to the front, we assure that if there are
01761      * several users, the most recently used one will be favored for
01762      * reuse the next time this form is encountered.  But don't do this
01763      * when reading in the saved signons (i.e., when save is PR_FALSE), otherwise
01764      * we will be reversing the order when reading in.
01765      */
01766   if (save) {
01767     user->time = SecondsFromPRTime(PR_Now());
01768     url->signonUser_list.InsertElementAt(user, 0);
01769     si_signon_list_changed = PR_TRUE;
01770     si_SaveSignonDataLocked("signons", PR_TRUE);
01771     si_unlock_signon_list();
01772   } else {
01773     user->time = 0;
01774     url->signonUser_list.AppendElement(user);
01775   }
01776 }
01777 
01778 
01779 /*****************************
01780  * Managing the Signon Files *
01781  *****************************/
01782  
01784 // nsSingleSignOnProfileObserver
01785 // This observer is a global object and is registered the first time any consumer
01786 // touches signon profile data. That is, when SI_LoadSignonData() is called.
01787 
01788 class nsSingleSignOnProfileObserver : public nsIObserver
01789 {
01790 public:
01791     nsSingleSignOnProfileObserver() { }
01792     virtual ~nsSingleSignOnProfileObserver() {}
01793     
01794     NS_DECL_ISUPPORTS
01795     
01796     NS_IMETHODIMP Observe(nsISupports*, const char *aTopic, const PRUnichar *someData) 
01797     {
01798         if (!strcmp(aTopic, "profile-before-change")) {
01799             SI_ClearUserData();
01800         if (!nsCRT::strcmp(someData, NS_LITERAL_STRING("shutdown-cleanse").get()))
01801             SI_DeletePersistentUserData();
01802         }
01803         return NS_OK;
01804     }
01805 };
01806 NS_IMPL_THREADSAFE_ISUPPORTS1(nsSingleSignOnProfileObserver, nsIObserver)
01807 
01808 static nsresult EnsureSingleSignOnProfileObserver()
01809 {
01810   static nsSingleSignOnProfileObserver *gSignOnProfileObserver;
01811   
01812   if (!gSignOnProfileObserver) {      
01813     nsCOMPtr<nsIObserverService> observerService(do_GetService("@mozilla.org/observer-service;1"));
01814     if (!observerService)
01815       return NS_ERROR_FAILURE;
01816       
01817     gSignOnProfileObserver = new nsSingleSignOnProfileObserver;
01818     if (!gSignOnProfileObserver)
01819       return NS_ERROR_OUT_OF_MEMORY;
01820 
01821     // The observer service holds the only ref to the observer
01822     // It thus has the lifespan of the observer service
01823     nsresult rv = observerService->AddObserver(gSignOnProfileObserver, "profile-before-change", PR_FALSE);
01824     if (NS_FAILED(rv)) {
01825       delete gSignOnProfileObserver;
01826       gSignOnProfileObserver = nsnull; 
01827       return rv;
01828     }
01829   }
01830   return NS_OK;
01831 }
01832 
01833 #define BUFFER_SIZE 4096
01834 
01835 /*
01836  * get a line from a file
01837  * return -1 if end of file reached
01838  * strip carriage returns and line feeds from end of line
01839  */
01840 static PRInt32
01841 si_ReadLine(nsIInputStream* strm, nsString& lineBuffer)
01842 {
01843   nsCAutoString line;
01844   nsresult rv = wallet_GetLine(strm, line);
01845   if (NS_FAILED(rv))
01846     return -1;
01847   
01848   CopyUTF8toUTF16(line, lineBuffer);
01849   return NS_OK;
01850 }
01851 
01852 /*
01853  * Load signon data from disk file
01854  * Return value is:
01855  *   -1: fatal error
01856  *    0: successfully load
01857  *   +1: user aborted the load (by failing to open the database)
01858  */
01859 int
01860 SI_LoadSignonData() {
01861   char * passwordRealm;
01862   nsAutoString buffer;
01863   PRBool badInput = PR_FALSE;
01864 
01865 #ifdef APPLE_KEYCHAIN
01866   if (KeychainManagerAvailable()) {
01867     SI_RemoveAllSignonData();
01868     return si_LoadSignonDataFromKeychain();
01869   }
01870 #endif
01871   
01872   /* open the signon file */
01873   nsCOMPtr<nsIFile> file;
01874   nsresult rv = Wallet_ProfileDirectory(getter_AddRefs(file));
01875   if (NS_FAILED(rv)) {
01876     return -1;
01877   }
01878 
01879 
01880   rv = EnsureSingleSignOnProfileObserver();
01881   NS_ASSERTION(NS_SUCCEEDED(rv), "Failed to register profile change observer");
01882 
01883   SI_InitSignonFileName();
01884   file->AppendNative(nsDependentCString(signonFileName));
01885 
01886   nsCOMPtr<nsIInputStream> strm;
01887   rv = NS_NewLocalFileInputStream(getter_AddRefs(strm), file);
01888   if (NS_FAILED(rv)) {
01889     si_PartiallyLoaded = PR_TRUE;
01890     return 0;
01891   }
01892 
01893   SI_RemoveAllSignonData();
01894 
01895   /* read the format information */
01896   nsAutoString format;
01897   if (NS_FAILED(si_ReadLine(strm, format))) {
01898     return -1;
01899   }
01900   if (!format.EqualsLiteral(HEADER_VERSION)) {
01901     /* something's wrong */
01902     return -1;
01903   }
01904 
01905   /* read the reject list */
01906   si_lock_signon_list();
01907   while (NS_SUCCEEDED(si_ReadLine(strm, buffer))) {
01908     /* a blank line is perfectly valid here -- corresponds to a local file */
01909     if (!buffer.IsEmpty() && buffer.CharAt(0) == '.') {
01910       break; /* end of reject list */
01911     }
01912     passwordRealm = ToNewCString(buffer);
01913     si_PutReject(passwordRealm, buffer, PR_FALSE); /* middle parameter is obsolete */
01914     Recycle (passwordRealm);
01915   }
01916 
01917   /* read the URL line */
01918   while (NS_SUCCEEDED(si_ReadLine(strm, buffer))) {
01919     /* a blank line is perfectly valid here -- corresponds to a local file */
01920     passwordRealm = ToNewCString(buffer);
01921     if (!passwordRealm) {
01922       si_unlock_signon_list();
01923       return -1;
01924     }
01925 
01926     /* prepare to read the name/value pairs */
01927     badInput = PR_FALSE;
01928 
01929     nsVoidArray signonData;
01930     si_SignonDataStruct * data;
01931     while(NS_SUCCEEDED(si_ReadLine(strm, buffer))) {
01932 
01933       /* line starting with . terminates the pairs for this URL entry */
01934       if (buffer.CharAt(0) == '.') {
01935         break; /* end of URL entry */
01936       }
01937 
01938       /* line just read is the name part */
01939 
01940       /* save the name part and determine if it is a password */
01941       PRBool ret;
01942       nsAutoString name;
01943       nsAutoString value;
01944       PRBool isPassword;
01945       if (buffer.CharAt(0) == '*') {
01946         isPassword = PR_TRUE;
01947         buffer.Mid(name, 1, buffer.Length()-1);
01948         ret = si_ReadLine(strm, buffer);
01949       } else {
01950         isPassword = PR_FALSE;
01951         name = buffer;
01952         ret = si_ReadLine(strm, buffer);
01953       }
01954 
01955       /* read in and save the value part */
01956       if(NS_FAILED(ret)) {
01957         /* error in input file so give up */
01958         badInput = PR_TRUE;
01959         break;
01960       }
01961       value = buffer;
01962 
01963       data = new si_SignonDataStruct;
01964       data->name = name;
01965       data->value = value;
01966       data->isPassword = isPassword;
01967       signonData.AppendElement(data);
01968     }
01969 
01970     /* store the info for this URL into memory-resident data structure */
01971     PRInt32 count = signonData.Count();
01972     if (count) {
01973       si_PutData(passwordRealm, &signonData, PR_FALSE);
01974     }
01975 
01976     /* free up all the allocations done for processing this URL */
01977     Recycle(passwordRealm);
01978     for (PRInt32 i=count-1; i>=0; i--) {
01979       data = NS_STATIC_CAST(si_SignonDataStruct*, signonData.ElementAt(i));
01980       delete data;
01981     }
01982   }
01983 
01984   si_unlock_signon_list();
01985   si_PartiallyLoaded = PR_TRUE;
01986   return 0;
01987 }
01988 
01989 /*
01990  * Save signon data to disk file
01991  * The parameter passed in on entry is ignored
01992  *
01993  * This routine is called only when holding the signon lock!!!
01994  *
01995  * This routine is called only if signon pref is enabled!!!
01996  */
01997 
01998 static int
01999 si_SaveSignonDataLocked(char * state, PRBool notify) {
02000   si_SignonURLStruct * url;
02001   si_SignonUserStruct * user;
02002   si_SignonDataStruct * data;
02003   si_Reject * reject;
02004 
02005   /* do nothing if signon list has not changed */
02006   if(!si_signon_list_changed) {
02007     return(-1);
02008   }
02009 
02010 #ifdef APPLE_KEYCHAIN
02011   if (KeychainManagerAvailable()) {
02012     return si_SaveSignonDataInKeychain();
02013   }
02014 #endif
02015 
02016   /* do nothing if we are unable to open file that contains signon list */
02017   nsCOMPtr<nsIFile> file;
02018   nsresult rv = Wallet_ProfileDirectory(getter_AddRefs(file));
02019   if (NS_FAILED(rv)) {
02020     return 0;
02021   }
02022 
02023   file->AppendNative(nsDependentCString(signonFileName));
02024 
02025   nsCOMPtr<nsIOutputStream> fileOutputStream;
02026   rv = NS_NewSafeLocalFileOutputStream(getter_AddRefs(fileOutputStream),
02027                                        file,
02028                                        -1,
02029                                        0600);
02030   if (NS_FAILED(rv))
02031     return 0;
02032 
02033   nsCOMPtr<nsIOutputStream> strm;
02034   rv = NS_NewBufferedOutputStream(getter_AddRefs(strm), fileOutputStream, 4096);
02035   if (NS_FAILED(rv))
02036     return 0;
02037 
02038   /* write out the format revision number */
02039 
02040   wallet_PutLine(strm, HEADER_VERSION);
02041 
02042   /* format for next part of file shall be:
02043    * passwordRealm -- first url/username on reject list
02044    * userName
02045    * passwordRealm -- second url/username on reject list
02046    * userName
02047    * ...     -- etc.
02048    * .       -- end of list
02049    */
02050 
02051   /* write out reject list */
02052   if (si_reject_list) {
02053     PRInt32 rejectCount = LIST_COUNT(si_reject_list);
02054     for (PRInt32 i=0; i<rejectCount; i++) {
02055       reject = NS_STATIC_CAST(si_Reject*, si_reject_list->ElementAt(i));
02056       wallet_PutLine(strm, reject->passwordRealm);
02057     }
02058   }
02059   wallet_PutLine(strm, ".");
02060 
02061   /* format for cached logins shall be:
02062    * url LINEBREAK {name LINEBREAK value LINEBREAK}*  . LINEBREAK
02063    * if type is password, name is preceded by an asterisk (*)
02064    */
02065 
02066   /* write out each URL node */
02067   if((si_signon_list)) {
02068     PRInt32 urlCount = LIST_COUNT(si_signon_list);
02069     for (PRInt32 i2=0; i2<urlCount; i2++) {
02070       url = NS_STATIC_CAST(si_SignonURLStruct*, si_signon_list->ElementAt(i2));
02071 
02072       /* write out each user node of the URL node */
02073       PRInt32 userCount = url->signonUser_list.Count();
02074       for (PRInt32 i3=0; i3<userCount; i3++) {
02075         user = NS_STATIC_CAST(si_SignonUserStruct*, url->signonUser_list.ElementAt(i3));
02076         wallet_PutLine(strm, url->passwordRealm);
02077 
02078         /* write out each data node of the user node */
02079         PRInt32 dataCount = user->signonData_list.Count();
02080         for (PRInt32 i4=0; i4<dataCount; i4++) {
02081           data = NS_STATIC_CAST(si_SignonDataStruct*, user->signonData_list.ElementAt(i4));
02082           if (data->isPassword) {
02083             static const char asterisk = '*';
02084             PRUint32 dummy;
02085             strm->Write(&asterisk, 1, &dummy);
02086           }
02087           wallet_PutLine(strm, NS_ConvertUTF16toUTF8(data->name).get());
02088           wallet_PutLine(strm, NS_ConvertUTF16toUTF8(data->value).get());
02089         }
02090         wallet_PutLine(strm, ".");
02091       }
02092     }
02093   }
02094   si_signon_list_changed = PR_FALSE;
02095 
02096   // All went ok. Maybe except for problems in Write(), but the stream detects
02097   // that for us
02098   nsCOMPtr<nsISafeOutputStream> safeStream = do_QueryInterface(strm);
02099   NS_ASSERTION(safeStream, "expected a safe output stream!");
02100   if (safeStream) {
02101     rv = safeStream->Finish();
02102     if (NS_FAILED(rv)) {
02103       NS_WARNING("failed to save wallet file! possible dataloss");
02104       return 0;
02105     }
02106   }
02107   strm = nsnull;
02108   fileOutputStream = nsnull;
02109 
02110 
02111   /* Notify signon manager dialog to update its display */
02112   if (notify) {
02113     nsCOMPtr<nsIObserverService> os(do_GetService("@mozilla.org/observer-service;1"));
02114     if (os) {
02115       os->NotifyObservers(nsnull, "signonChanged", NS_ConvertASCIItoUCS2(state).get());
02116     }
02117   }
02118 
02119   return 0;
02120 }
02121 
02122 
02123 /***************************
02124  * Processing Signon Forms *
02125  ***************************/
02126 
02127 static PRBool
02128 si_ExtractRealm(nsIURI *uri, nsCString &realm)
02129 {
02130   nsCAutoString hostPort;
02131 
02132   /* Security check: if URI is of a scheme that doesn't support hostnames,
02133    * we have no host to get the signon data from, so we must not attempt to
02134    * build a valid realm from the URI (bug 159484) */
02135   nsresult rv = uri->GetHostPort(hostPort);
02136   if (NS_FAILED(rv) || hostPort.IsEmpty())
02137     return PR_FALSE;
02138 
02139   nsCAutoString scheme;
02140   rv = uri->GetScheme(scheme);
02141   if (NS_FAILED(rv) || scheme.IsEmpty())
02142     return PR_FALSE;
02143 
02144   realm = scheme + NS_LITERAL_CSTRING("://") + hostPort;
02145   return PR_TRUE;
02146 }
02147 
02148 /* Ask user if it is ok to save the signon data */
02149 static PRBool
02150 si_OkToSave(const char *passwordRealm, const char *legacyRealm,
02151             const nsString& userName, nsIDOMWindowInternal* window) {
02152 
02153   /* if url/user already exists, then it is safe to save it again */
02154   if (si_CheckForUser(passwordRealm, userName)) {
02155     return PR_TRUE;
02156   }
02157   if (legacyRealm && si_CheckForUser(legacyRealm, userName)) {
02158     return PR_TRUE;
02159   }
02160 
02161 #ifdef WALLET_PASSWORDMANAGER_DEFAULT_IS_OFF
02162   if (!si_RememberSignons && !si_GetNotificationPref()) {
02163     PRUnichar * notification = Wallet_Localize("PasswordNotification");
02164     si_SetNotificationPref(PR_TRUE);
02165     if (!si_ConfirmYN(notification, window)) {
02166       Recycle(notification);
02167       SI_SetBoolPref(pref_rememberSignons, PR_FALSE);
02168       return PR_FALSE;
02169     }
02170     Recycle(notification);
02171     SI_SetBoolPref(pref_rememberSignons, PR_TRUE);
02172   }
02173 #endif
02174 
02175   if (si_CheckForReject(passwordRealm, userName)) {
02176     return PR_FALSE;
02177   }
02178   if (legacyRealm && si_CheckForReject(legacyRealm, userName)) {
02179     return PR_FALSE;
02180   }
02181 
02182   PRUnichar * message;
02183   if (SI_GetBoolPref(pref_Crypto, PR_FALSE)) {
02184     message = Wallet_Localize("WantToSavePasswordEncrypted?");
02185   } else {
02186     message = Wallet_Localize("WantToSavePasswordObscured?");
02187   }
02188 
02189   PRInt32 button = si_3ButtonConfirm(message, window);
02190   if (button == NEVER_BUTTON) {
02191     si_PutReject(passwordRealm, userName, PR_TRUE);
02192   }
02193   Recycle(message);
02194   return (button == YES_BUTTON);
02195 }
02196 
02197 /*
02198  * Check for a signon submission and remember the data if so
02199  */
02200 static void
02201 si_RememberSignonData
02202     (nsIPrompt* dialog, const char* passwordRealm, const char* legacyRealm,
02203      nsVoidArray * signonData, nsIDOMWindowInternal* window)
02204 {
02205   int passwordCount = 0;
02206   int pswd[3];
02207   si_SignonDataStruct * data = nsnull;  // initialization only to avoid warning message
02208   si_SignonDataStruct * data0;
02209   si_SignonDataStruct * data1;
02210   si_SignonDataStruct * data2;
02211 
02212   /* do nothing if signon preference is not enabled */
02213   if (!si_GetSignonRememberingPref()){
02214     return;
02215   }
02216 
02217   /* determine how many passwords are in the form and where they are */
02218   for (PRInt32 i=0; i<signonData->Count(); i++) {
02219     data = NS_STATIC_CAST(si_SignonDataStruct*, signonData->ElementAt(i));
02220     if (data->isPassword) {
02221       if (passwordCount < 3 ) {
02222         pswd[passwordCount] = i;
02223       }
02224       passwordCount++;
02225     }
02226   }
02227 
02228   /* process the form according to how many passwords it has */
02229   if (passwordCount == 1) {
02230     /* one-password form is a log-in so remember it */
02231 
02232     /* obtain the index of the first input field (that is the username) */
02233     PRInt32 j;
02234     for (j=0; j<signonData->Count(); j++) {
02235       data = NS_STATIC_CAST(si_SignonDataStruct*, signonData->ElementAt(j));
02236       if (!data->isPassword) {
02237         break;
02238       }
02239     }
02240 
02241     if (j<signonData->Count()) {
02242       data2 = NS_STATIC_CAST(si_SignonDataStruct*, signonData->ElementAt(j));
02243 
02244       if (si_OkToSave(passwordRealm, legacyRealm, data2->value, window)) {
02245         // remove legacy password entry if found
02246         if (legacyRealm && si_CheckForUser(legacyRealm, data2->value)) {
02247           si_RemoveUser(legacyRealm, data2->value, PR_TRUE, PR_FALSE, PR_TRUE);
02248         }
02249         Wallet_GiveCaveat(window, nsnull);
02250         for (j=0; j<signonData->Count(); j++) {
02251           data2 = NS_STATIC_CAST(si_SignonDataStruct*, signonData->ElementAt(j));
02252           nsAutoString value(data2->value);
02253           if (NS_FAILED(Wallet_Encrypt(value, data2->value))) {
02254             return;
02255           }
02256         }
02257         si_PutData(passwordRealm, signonData, PR_TRUE);
02258       }
02259     }
02260   } else if (passwordCount == 2) {
02261     /* two-password form is a registration */
02262 
02263   } else if (passwordCount == 3) {
02264     /* three-password form is a change-of-password request */
02265 
02266     si_SignonUserStruct* user;
02267 
02268     /* make sure all passwords are non-null and 2nd and 3rd are identical */
02269     data0 = NS_STATIC_CAST(si_SignonDataStruct*, signonData->ElementAt(pswd[0]));
02270     data1 = NS_STATIC_CAST(si_SignonDataStruct*, signonData->ElementAt(pswd[1]));
02271     data2 = NS_STATIC_CAST(si_SignonDataStruct*, signonData->ElementAt(pswd[2]));
02272     if (data0->value.IsEmpty() || data1->value.IsEmpty() ||
02273         data2->value.IsEmpty() || data1->value != data2->value) {
02274       return;
02275     }
02276 
02277     /* ask user if this is a password change */
02278     si_lock_signon_list();
02279     user = si_GetURLAndUserForChangeForm(dialog, data0->value);
02280 
02281     /* return if user said no */
02282     if (!user) {
02283       si_unlock_signon_list();
02284       return;
02285     }
02286 
02287     /* get to password being saved */
02288     PRInt32 dataCount = user->signonData_list.Count();
02289     for (PRInt32 k=0; k<dataCount; k++) {
02290       data = NS_STATIC_CAST(si_SignonDataStruct*, user->signonData_list.ElementAt(k));
02291       if (data->isPassword) {
02292         break;
02293       }
02294     }
02295 
02296     /*
02297      * if second password is "********" then generate a random
02298      * password for it and use same random value for third password
02299      * as well (Note: this all works because we already know that
02300      * second and third passwords are identical so third password
02301      * must also be "********".  Furthermore si_Randomize() will
02302      * create a random password of exactly eight characters -- the
02303      * same length as "********".)
02304      */
02305 
02306 /* commenting out because I don't see how such randomizing could ever have worked. */
02307 //    si_Randomize(data1->value);
02308 //    data2->value = data1->value;
02309 
02310     if (NS_SUCCEEDED(Wallet_Encrypt(data1->value, data->value))) {
02311       user->time = SecondsFromPRTime(PR_Now()); 
02312       si_signon_list_changed = PR_TRUE;
02313       si_SaveSignonDataLocked("signons", PR_TRUE);
02314       si_unlock_signon_list();
02315     }
02316   }
02317 }
02318 
02319 void
02320 SINGSIGN_RememberSignonData 
02321     (nsIPrompt* dialog, nsIURI* passwordRealm, nsVoidArray * signonData,
02322      nsIDOMWindowInternal* window)
02323 {
02324   if (!passwordRealm)
02325     return;      
02326 
02327   nsCAutoString realm, legacyRealm;
02328   if (!si_ExtractRealm(passwordRealm, realm))
02329     return;
02330 
02331   if (NS_FAILED(passwordRealm->GetHost(legacyRealm)))
02332     return;
02333 
02334   if (!realm.IsEmpty()) {
02335     si_RememberSignonData(dialog, realm.get(), legacyRealm.get(), signonData, window);
02336   }
02337 }
02338 
02339 static void
02340 si_RestoreSignonData(nsIPrompt* dialog,
02341                      const char* passwordRealm, const char* legacyRealm,
02342                      const PRUnichar* name, PRUnichar** value,
02343                      PRUint32 formNumber, PRUint32 elementNumber) {
02344   si_SignonUserStruct* user;
02345   si_SignonDataStruct* data;
02346   nsAutoString correctedName;
02347 
02348   /* do nothing if signon preference is not enabled */
02349   if (!si_GetSignonRememberingPref()){
02350     return;
02351   }
02352 
02353   si_lock_signon_list();
02354   if (elementNumber == 0) {
02355     si_LastFormForWhichUserHasBeenSelected = -1;
02356   }
02357 
02358   /* Correct the field name to avoid mistaking for fields in browser-generated form
02359    *
02360    *   Note that data saved for browser-generated logins (e.g. http authentication)
02361    *   use artificial field names starting with * \= (see USERNAMEFIELD and PASSWORDFIELD.
02362    *   To avoid mistakes whereby saved logins for http authentication is then prefilled
02363    *   into a field on the html form at the same URL, we will prevent html field names
02364    *   from starting with \=.  We do that by doubling up a backslash if it appears in the
02365    *   first character position
02366    */
02367   if (*name == '\\') {
02368     correctedName = NS_LITERAL_STRING("\\") + nsDependentString(name);
02369   } else {
02370     correctedName = name;
02371   }
02372 
02373   /* determine if name has been saved (avoids unlocking the database if not) */
02374   PRBool nameFound = PR_FALSE;
02375   user = si_GetUser(dialog, passwordRealm, legacyRealm, PR_FALSE, correctedName, formNumber);
02376   if (user) {
02377     PRInt32 dataCount = user->signonData_list.Count();
02378     for (PRInt32 i=0; i<dataCount; i++) {
02379       data = NS_STATIC_CAST(si_SignonDataStruct*, user->signonData_list.ElementAt(i));
02380       LOG(("  got [name=%s value=%s]\n",
02381               NS_LossyConvertUCS2toASCII(data->name).get(),
02382               NS_LossyConvertUCS2toASCII(data->value).get()));
02383       if(!correctedName.IsEmpty() && (data->name == correctedName)) {
02384         nameFound = PR_TRUE;
02385       }
02386     }
02387   }
02388   if (!nameFound) {
02389     si_unlock_signon_list();
02390     return;
02391   }
02392 
02393 #ifdef xxx
02394   /*
02395    * determine if it is a change-of-password field
02396    *    the heuristic that we will use is that if this is the first
02397    *    item on the form and it is a password, this is probably a
02398    *    change-of-password form
02399    */
02400   /* see if this is first item in form and is a password */
02401   /* get first saved user just so we can see the name of the first item on the form */
02402   user = si_GetUser(passwordRealm, PR_TRUE, NULL, formNumber); /* this is the first saved user */
02403   if (user) {
02404     data = NS_STATIC_CAST(si_SignonDataStruct *,
02405                     user->signonData_list.ElementAt(0)); /* 1st item on form */
02406     if(data->isPassword && !correctedName.IsEmpty() && (data->name == correctedName)) {
02407       /* current item is first item on form and is a password */
02408       user = (passwordRealm, MK_SIGNON_PASSWORDS_FETCH);
02409       if (user) {
02410         /* user has confirmed it's a change-of-password form */
02411         PRInt32 dataCount = user->signonData_list.Count();
02412         for (PRInt32 i=1; i<dataCount; i++) {
02413           data = NS_STATIC_CAST(si_SignonDataStruct*, user->signonData_list.ElementAt(i));
02414           if (data->isPassword) {
02415             nsAutoString password;
02416             if (NS_SUCCEEDED(Wallet_Decrypt(data->value, password))) {
02417               *value = ToNewUnicode(password);
02418             }
02419             si_unlock_signon_list();
02420             return;
02421           }
02422         }
02423       }
02424     }
02425   }
02426 #endif
02427 
02428   /* restore the data from previous time this URL was visited */
02429 
02430   user = si_GetUser(dialog, passwordRealm, legacyRealm, PR_FALSE, correctedName, formNumber);
02431   if (user) {
02432     PRInt32 dataCount = user->signonData_list.Count();
02433     for (PRInt32 i=0; i<dataCount; i++) {
02434       data = NS_STATIC_CAST(si_SignonDataStruct*, user->signonData_list.ElementAt(i));
02435       LOG(("  got [name=%s value=%s]\n",
02436               NS_LossyConvertUCS2toASCII(data->name).get(),
02437               NS_LossyConvertUCS2toASCII(data->value).get()));
02438       if(!correctedName.IsEmpty() && (data->name == correctedName)) {
02439         nsAutoString password;
02440         if (NS_SUCCEEDED(Wallet_Decrypt(data->value, password))) {
02441           *value = ToNewUnicode(password);
02442         }
02443         si_unlock_signon_list();
02444         return;
02445       }
02446     }
02447   }
02448   si_unlock_signon_list();
02449 }
02450 
02451 void
02452 SINGSIGN_RestoreSignonData(nsIPrompt* dialog, nsIURI* passwordRealm, const PRUnichar* name, PRUnichar** value, PRUint32 formNumber, PRUint32 elementNumber) {
02453   LOG(("enter SINGSIGN_RestoreSignonData\n"));
02454 
02455   if (!passwordRealm)
02456     return;  
02457 
02458   nsCAutoString realm;
02459   if (!si_ExtractRealm(passwordRealm, realm))
02460     return;
02461 
02462   nsCAutoString legacyRealm;
02463   if (NS_FAILED(passwordRealm->GetHost(legacyRealm)))
02464     return;
02465 
02466   si_RestoreSignonData(dialog, realm.get(), legacyRealm.get(), name, value, formNumber, elementNumber);
02467 
02468   LOG(("exit SINGSIGN_RestoreSignonData [value=%s]\n",
02469       *value ? NS_LossyConvertUCS2toASCII(*value).get() : "(null)"));
02470 }
02471 
02472 /*
02473  * Remember signon data from a browser-generated password dialog
02474  */
02475 static void
02476 si_RememberSignonDataFromBrowser(const char* passwordRealm, const nsString& username, const nsString& password) {
02477   /* do nothing if signon preference is not enabled */
02478   if (!si_GetSignonRememberingPref()){
02479     return;
02480   }
02481 
02482   nsVoidArray signonData;
02483   si_SignonDataStruct data1;
02484   data1.name.AssignLiteral(USERNAMEFIELD);
02485   if (NS_FAILED(Wallet_Encrypt(username, data1.value))) {
02486     return;
02487   }
02488   data1.isPassword = PR_FALSE;
02489   signonData.AppendElement(&data1);
02490   si_SignonDataStruct data2;
02491   data2.name.AssignLiteral(PASSWORDFIELD);
02492   if (NS_FAILED(Wallet_Encrypt(password, data2.value))) {
02493     return;
02494   }
02495   data2.isPassword = PR_TRUE;
02496   signonData.AppendElement(&data2);
02497 
02498   /* Save the signon data */
02499   si_PutData(passwordRealm, &signonData, PR_TRUE);
02500 }
02501 
02502 /*
02503  * Check for remembered data from a previous browser-generated password dialog
02504  * restore it if so
02505  */
02506 static void
02507 si_RestoreOldSignonDataFromBrowser
02508     (nsIPrompt* dialog, const char* passwordRealm, PRBool pickFirstUser, nsString& username, nsString& password) {
02509   si_SignonUserStruct* user;
02510   si_SignonDataStruct* data;
02511 
02512   /* get the data from previous time this URL was visited */
02513   si_lock_signon_list();
02514   if (!username.IsEmpty()) {
02515     user = si_GetSpecificUser(passwordRealm, username, NS_ConvertASCIItoUCS2(USERNAMEFIELD));
02516   } else {
02517     si_LastFormForWhichUserHasBeenSelected = -1;
02518     user = si_GetUser(dialog, passwordRealm, nsnull, pickFirstUser, NS_ConvertASCIItoUCS2(USERNAMEFIELD), 0);
02519   }
02520   if (!user) {
02521     /* leave original username and password from caller unchanged */
02522     /* username = 0; */
02523     /* *password = 0; */
02524     si_unlock_signon_list();
02525     return;
02526   }
02527 
02528   /* restore the data from previous time this URL was visited */
02529   PRInt32 dataCount = user->signonData_list.Count();
02530   for (PRInt32 i=0; i<dataCount; i++) {
02531     data = NS_STATIC_CAST(si_SignonDataStruct*, user->signonData_list.ElementAt(i));
02532     nsAutoString decrypted;
02533     if (NS_SUCCEEDED(Wallet_Decrypt(data->value, decrypted))) {
02534       if(data->name.EqualsLiteral(USERNAMEFIELD)) {
02535         username = decrypted;
02536       } else if(data->name.EqualsLiteral(PASSWORDFIELD)) {
02537         password = decrypted;
02538       }
02539     }
02540   }
02541   si_unlock_signon_list();
02542 }
02543 
02544 PRBool
02545 SINGSIGN_StorePassword(const char *passwordRealm, const PRUnichar *user, const PRUnichar *password)
02546 {
02547 //  Wallet_GiveCaveat(nsnull, dialog); ??? what value to use for dialog?
02548   si_RememberSignonDataFromBrowser(passwordRealm, nsDependentString(user), nsDependentString(password));
02549   return PR_TRUE;
02550 }
02551 
02552 enum DialogType {promptUsernameAndPassword, promptPassword, prompt};
02553 
02554 static nsresult
02555 si_DoDialogIfPrefIsOff(
02556     const PRUnichar *dialogTitle,
02557     const PRUnichar *text,
02558     PRUnichar **user,
02559     PRUnichar **pwd,
02560     const PRUnichar *defaultText,
02561     PRUnichar **resultText,
02562     const char *passwordRealm,
02563     nsIPrompt* dialog,
02564     PRBool *pressedOK, 
02565     PRUint32 savePassword,
02566     DialogType dlg) {
02567 
02568   nsresult res = NS_ERROR_FAILURE;
02569   const PRUnichar * prompt_string = dialogTitle;
02570   if (dialogTitle == nsnull || !dialogTitle[0]) {
02571     prompt_string = Wallet_Localize("PromptForData");
02572   }
02573 
02574   nsAutoString data(defaultText);
02575   switch (dlg) {
02576     case promptUsernameAndPassword:
02577       res = dialog->PromptUsernameAndPassword(prompt_string,
02578                                               text,
02579                                               user,
02580                                               pwd,
02581                                               nsnull, nsnull,
02582                                               pressedOK);
02583       break;
02584     case promptPassword:
02585       res = dialog->PromptPassword(prompt_string,
02586                                    text,
02587                                    pwd,
02588                                    nsnull, nsnull,
02589                                    pressedOK);
02590       break;
02591     case prompt:
02592       *resultText = ToNewUnicode(data);
02593       res = dialog->Prompt(prompt_string,
02594                            text,
02595                            resultText,
02596                            nsnull, nsnull,
02597                            pressedOK);
02598 #ifdef DEBUG
02599       break;
02600     default:
02601       NS_ERROR("Undefined DialogType in si_DoDialogIfPrefIsOff");
02602 #endif
02603   }
02604 
02605   if (dialogTitle != prompt_string) {
02606     Recycle(NS_CONST_CAST(PRUnichar*, prompt_string));
02607   }
02608   return res;
02609 }
02610 
02611 /* The following comments apply to the three prompt routines that follow
02612  *
02613  * If a password was successfully obtain (either from the single-signon
02614  * database or from a dialog with the user), we return NS_OK for the
02615  * function value and PR_TRUE for the boolean argument "pressedOK".
02616  *
02617  * If the user presses cancel from the dialog, we return NS_OK for the
02618  * function value and PR_FALSE for the boolean argument "pressedOK".
02619  *
02620  * If a password is not collected for any other reason, we return the
02621  * failure code for the function value and the boolean argument
02622  * "pressedOK" is undefined.
02623  */
02624 
02625 nsresult
02626 SINGSIGN_PromptUsernameAndPassword
02627     (const PRUnichar *dialogTitle, const PRUnichar *text, PRUnichar **user, PRUnichar **pwd,
02628      const char *passwordRealm, nsIPrompt* dialog, PRBool *pressedOK, PRUint32 savePassword) {
02629 
02630   nsresult res;
02631 
02632   /* do only the dialog if signon preference is not enabled */
02633   if (!si_GetSignonRememberingPref()){
02634     return si_DoDialogIfPrefIsOff(dialogTitle,
02635                                   text,
02636                                   user,
02637                                   pwd,
02638                                   nsnull,
02639                                   nsnull,
02640                                   passwordRealm,
02641                                   dialog,
02642                                   pressedOK, 
02643                                   savePassword,
02644                                   promptUsernameAndPassword);
02645   }
02646 
02647   /* prefill with previous username/password if any */
02648   nsAutoString username, password;
02649   si_RestoreOldSignonDataFromBrowser(dialog, passwordRealm, PR_FALSE, username, password);
02650 
02651   /* get new username/password from user */
02652   if (!(*user = ToNewUnicode(username))) {
02653     return NS_ERROR_OUT_OF_MEMORY;
02654   }
02655   if (!(*pwd = ToNewUnicode(password))) {
02656     PR_Free(*user);
02657     return NS_ERROR_OUT_OF_MEMORY;
02658   }
02659 
02660   PRBool checked = (**user != 0);
02661   PRBool remembered = checked;
02662   res = si_CheckGetUsernamePassword(user, pwd, dialogTitle, text, dialog, savePassword, &checked);
02663   if (NS_FAILED(res)) {
02664     /* user pressed Cancel */
02665     PR_FREEIF(*user);
02666     PR_FREEIF(*pwd);
02667     *pressedOK = PR_FALSE;
02668     return NS_OK;
02669   }
02670   if (checked) {
02671     Wallet_GiveCaveat(nsnull, dialog);
02672     si_RememberSignonDataFromBrowser (passwordRealm, nsDependentString(*user), nsDependentString(*pwd));
02673   } else if (remembered) {
02674     /* a login was remembered but user unchecked the box; we forget the remembered login */
02675     si_RemoveUser(passwordRealm, username, PR_TRUE, PR_FALSE, PR_TRUE);  
02676   }
02677 
02678   /* cleanup and return */
02679   *pressedOK = PR_TRUE;
02680   return NS_OK;
02681 }
02682 
02683 nsresult
02684 SINGSIGN_PromptPassword
02685     (const PRUnichar *dialogTitle, const PRUnichar *text, PRUnichar **pwd, const char *passwordRealm,
02686      nsIPrompt* dialog, PRBool *pressedOK, PRUint32 savePassword) 
02687 {
02688 
02689   nsresult res;
02690   nsAutoString password, username;
02691 
02692   /* do only the dialog if signon preference is not enabled */
02693   if (!si_GetSignonRememberingPref()){
02694     return si_DoDialogIfPrefIsOff(dialogTitle,
02695                                   text,
02696                                   nsnull,
02697                                   pwd,
02698                                   nsnull,
02699                                   nsnull,
02700                                   passwordRealm,
02701                                   dialog,
02702                                   pressedOK, 
02703                                   savePassword,
02704                                   promptPassword);
02705   }
02706 
02707   /* get previous password used with this username, pick first user if no username found */
02708   si_RestoreOldSignonDataFromBrowser(dialog, passwordRealm, username.IsEmpty(), username, password);
02709 
02710   /* return if a password was found */
02711   if (!password.IsEmpty()) {
02712     *pwd = ToNewUnicode(password);
02713     *pressedOK = PR_TRUE;
02714     return NS_OK;
02715   }
02716 
02717   /* no password found, get new password from user */
02718   PRBool checked = PR_FALSE;
02719   res = si_CheckGetPassword(pwd, dialogTitle, text, dialog, savePassword, &checked);
02720   if (NS_FAILED(res)) {
02721     /* user pressed Cancel */
02722     PR_FREEIF(*pwd);
02723     *pressedOK = PR_FALSE;
02724     return NS_OK;
02725   }
02726   if (checked) {
02727     Wallet_GiveCaveat(nsnull, dialog);
02728     si_RememberSignonDataFromBrowser(passwordRealm, username, nsDependentString(*pwd));
02729   }
02730 
02731   /* cleanup and return */
02732   *pressedOK = PR_TRUE;
02733   return NS_OK;
02734 }
02735 
02736 nsresult
02737 SINGSIGN_Prompt
02738     (const PRUnichar *dialogTitle, const PRUnichar *text, const PRUnichar *defaultText, PRUnichar **resultText,
02739      const char *passwordRealm, nsIPrompt* dialog, PRBool *pressedOK, PRUint32 savePassword) 
02740 {
02741   nsresult res;
02742   nsAutoString data, emptyUsername;
02743 
02744   /* do only the dialog if signon preference is not enabled */
02745   if (!si_GetSignonRememberingPref()){
02746     return si_DoDialogIfPrefIsOff(dialogTitle,
02747                                   text,
02748                                   nsnull,
02749                                   nsnull,
02750                                   defaultText,
02751                                   resultText,
02752                                   passwordRealm,
02753                                   dialog,
02754                                   pressedOK, 
02755                                   savePassword,
02756                                   prompt);
02757   }
02758 
02759   /* get previous data used with this hostname */
02760   si_RestoreOldSignonDataFromBrowser(dialog, passwordRealm, PR_TRUE, emptyUsername, data);
02761 
02762   /* return if data was found */
02763   if (!data.IsEmpty()) {
02764     *resultText = ToNewUnicode(data);
02765     *pressedOK = PR_TRUE;
02766     return NS_OK;
02767   }
02768 
02769   /* no data found, get new data from user */
02770   data = defaultText;
02771   *resultText = ToNewUnicode(data);
02772   PRBool checked = PR_FALSE;
02773   res = si_CheckGetData(resultText, dialogTitle, text, dialog, savePassword, &checked);
02774   if (NS_FAILED(res)) {
02775     /* user pressed Cancel */
02776     PR_FREEIF(*resultText);
02777     *pressedOK = PR_FALSE;
02778     return NS_OK;
02779   }
02780   if (checked) {
02781     Wallet_GiveCaveat(nsnull, dialog);
02782     si_RememberSignonDataFromBrowser(passwordRealm, emptyUsername, nsDependentString(*resultText));
02783   }
02784 
02785   /* cleanup and return */
02786   *pressedOK = PR_TRUE;
02787   return NS_OK;
02788 }
02789 
02790 /*****************
02791  * Signon Viewer *
02792  *****************/
02793 
02794 /* return PR_TRUE if "number" is in sequence of comma-separated numbers */
02795 PRBool
02796 SI_InSequence(const nsString& sequence, PRInt32 number)
02797 {
02798   nsAutoString tail( sequence );
02799   nsAutoString head, temp;
02800   PRInt32 separator;
02801 
02802   for (;;) {
02803     /* get next item in list */
02804     separator = tail.FindChar(',');
02805     if (-1 == separator) {
02806       return PR_FALSE;
02807     }
02808     tail.Left(head, separator);
02809     tail.Mid(temp, separator+1, tail.Length() - (separator+1));
02810     tail = temp;
02811 
02812     /* test item to see if it equals our number */
02813     PRInt32 error;
02814     PRInt32 numberInList = head.ToInteger(&error);
02815     if (!error && numberInList == number) {
02816       return PR_TRUE;
02817     }
02818   }
02819   /* NOTREACHED */
02820   return PR_FALSE;
02821 }
02822 
02823 void
02824 SI_FindValueInArgs(const nsAString& results, const nsAString& name, nsAString& value)
02825 {
02826   /* note: name must start and end with a vertical bar */
02827   nsReadingIterator<PRUnichar> start, end, barPos;
02828   results.BeginReading(start);
02829   results.EndReading(end);
02830 
02831   FindInReadable(name, start, end);
02832   if (start == end) {
02833     return;
02834   }
02835   start.advance(name.Length()); /* get past the |name| part */
02836   barPos = start;
02837   results.EndReading(end);
02838   FindCharInReadable(PRUnichar('|'), barPos, end);
02839   value = Substring(start, barPos);
02840 }
02841 
02842 PRBool
02843 SINGSIGN_ReencryptAll()
02844 {
02845   /* force loading of the signons file */
02846   si_RegisterSignonPrefCallbacks();
02847 
02848   nsAutoString buffer;
02849   si_SignonURLStruct *url;
02850   si_SignonUserStruct * user;
02851   si_SignonDataStruct* data = nsnull;
02852 
02853   si_lock_signon_list();
02854   PRInt32 urlCount = LIST_COUNT(si_signon_list);
02855   for (PRInt32 i=0; i<urlCount; i++) {
02856     url = NS_STATIC_CAST(si_SignonURLStruct*, si_signon_list->ElementAt(i));
02857     PRInt32 userCount = url->signonUser_list.Count();
02858     for (PRInt32 j=0; j<userCount; j++) {
02859       user = NS_STATIC_CAST(si_SignonUserStruct*, url->signonUser_list.ElementAt(j));
02860 
02861       PRInt32 dataCount = user->signonData_list.Count();
02862       for (PRInt32 k=0; k<dataCount; k++) {
02863         data = NS_STATIC_CAST(si_SignonDataStruct *,
02864                               user->signonData_list.ElementAt(k));
02865         nsAutoString userName;
02866         if (NS_FAILED(Wallet_Decrypt(data->value, userName))) {
02867           //Don't try to re-encrypt. Just go to the next one.
02868           continue;
02869         }
02870         if (NS_FAILED(Wallet_Encrypt(userName, data->value))) {
02871           return PR_FALSE;
02872         }
02873       }
02874     }
02875   }
02876   si_signon_list_changed = PR_TRUE;
02877   si_SaveSignonDataLocked("signons", PR_TRUE);
02878   si_unlock_signon_list();
02879   return PR_TRUE;
02880 }
02881 
02882 nsresult
02883 SINGSIGN_HaveData(nsIPrompt* dialog, const char *passwordRealm, const PRUnichar *userName, PRBool *retval)
02884 {
02885   nsAutoString data, usernameForLookup;
02886 
02887   *retval = PR_FALSE;
02888 
02889   if (!si_GetSignonRememberingPref()) {
02890     return NS_OK;
02891   }
02892 
02893   /* get previous data used with this username, pick first user if no username found */
02894   si_RestoreOldSignonDataFromBrowser(dialog, passwordRealm, usernameForLookup.IsEmpty(), usernameForLookup, data);
02895 
02896   if (!data.IsEmpty()) {
02897     *retval = PR_TRUE;
02898   }
02899 
02900   return NS_OK;
02901 }
02902 
02903 PRInt32
02904 SINGSIGN_HostCount() {
02905   /* force loading of the signons file */
02906   si_RegisterSignonPrefCallbacks();
02907 
02908   if (!si_signon_list) {
02909     return 0;
02910   }
02911   return si_signon_list->Count();
02912 }
02913 
02914 PRInt32
02915 SINGSIGN_UserCount(PRInt32 host) {
02916   if (!si_signon_list) {
02917     return 0;
02918   }
02919 
02920   si_SignonURLStruct *hostStruct;
02921   hostStruct = NS_STATIC_CAST(si_SignonURLStruct*, si_signon_list->ElementAt(host));
02922   return hostStruct->signonUser_list.Count();
02923 }
02924 
02925 nsresult
02926 SINGSIGN_Enumerate
02927     (PRInt32 hostNumber, PRInt32 userNumber, PRBool decrypt, char **host,
02928      PRUnichar ** user, PRUnichar ** pswd) {
02929 
02930   if (gSelectUserDialogCount>0 && hostNumber==0 && userNumber==0) {
02931     // starting to enumerate over all saved logins
02932     // notify recipients if login list is in use by SelectUserDialog
02933     nsCOMPtr<nsIObserverService> os(do_GetService("@mozilla.org/observer-service;1"));
02934     if (os) {
02935       os->NotifyObservers(nsnull, "signonSelectUser", NS_LITERAL_STRING("inUse").get());
02936     }
02937   }
02938 
02939   if (hostNumber > SINGSIGN_HostCount() || userNumber > SINGSIGN_UserCount(hostNumber)) {
02940     return NS_ERROR_FAILURE;
02941   }
02942   si_SignonURLStruct *hostStruct;
02943   si_SignonUserStruct * userStruct;
02944   si_SignonDataStruct* data = nsnull;
02945 
02946   hostStruct = NS_STATIC_CAST(si_SignonURLStruct*, si_signon_list->ElementAt(hostNumber));
02947   NS_ASSERTION(hostStruct, "corrupt singlesignon list");
02948   *host = (char *) nsMemory::Clone
02949     (hostStruct->passwordRealm, strlen(hostStruct->passwordRealm) + 1);
02950   NS_ENSURE_ARG_POINTER(host);
02951   userStruct = NS_STATIC_CAST
02952     (si_SignonUserStruct*, hostStruct->signonUser_list.ElementAt(userNumber));
02953 
02954   /* first non-password data item for user is the username */
02955   PRInt32 dataCount = userStruct->signonData_list.Count();
02956   PRInt32 k;
02957   for (k=0; k<dataCount; k++) {
02958     data = NS_STATIC_CAST(si_SignonDataStruct *, userStruct->signonData_list.ElementAt(k));
02959     if (!(data->isPassword)) {
02960       break;
02961     }
02962   }
02963 
02964   nsresult rv;
02965   nsAutoString userName;
02966   if (decrypt) {
02967     rv = Wallet_Decrypt(data->value, userName);
02968     if (NS_FAILED(rv)) {
02969       /* don't display saved signons if user couldn't unlock the database */
02970     return rv;
02971     }
02972   } else {
02973     userName = data->value;
02974   }
02975   if (!(*user = ToNewUnicode(userName))) {
02976     return NS_ERROR_OUT_OF_MEMORY;
02977   }
02978 
02979   /* first password data item for user is the password */
02980   for (k=0; k<dataCount; k++) {
02981     data = NS_STATIC_CAST(si_SignonDataStruct *, userStruct->signonData_list.ElementAt(k));
02982     if ((data->isPassword)) {
02983       break;
02984     }
02985   }
02986 
02987   nsAutoString passWord;
02988   if (decrypt) {
02989     rv = Wallet_Decrypt(data->value, passWord);
02990     if (NS_FAILED(rv)) {
02991       /* don't display saved signons if user couldn't unlock the database */
02992       Recycle(*user);
02993       return rv;
02994     }
02995   } else {
02996     passWord = data->value;
02997   }  
02998   if (!(*pswd = ToNewUnicode(passWord))) {
02999     Recycle(*user);
03000     return NS_ERROR_OUT_OF_MEMORY;
03001   }
03002   return NS_OK;
03003 }
03004 
03005 PRInt32
03006 SINGSIGN_RejectCount() {
03007   if (!si_reject_list) {
03008     return 0;
03009   }
03010   return si_reject_list->Count();
03011 }
03012 
03013 nsresult
03014 SINGSIGN_RejectEnumerate
03015     (PRInt32 rejectNumber, char **host) {
03016 
03017   si_Reject *reject;
03018   reject = NS_STATIC_CAST(si_Reject*, si_reject_list->ElementAt(rejectNumber));
03019   NS_ASSERTION(reject, "corrupt reject list");
03020 
03021   *host = (char *) nsMemory::Clone
03022     (reject->passwordRealm, strlen(reject->passwordRealm) + 1);
03023   NS_ENSURE_ARG_POINTER(host);
03024   return NS_OK;
03025 }
03026 
03027 #ifdef APPLE_KEYCHAIN
03028 /************************************
03029  * Apple Keychain Specific Routines *
03030  ************************************/
03031 
03032 /*
03033  * APPLE
03034  * The Keychain callback.  This routine will be called whenever a lock,
03035  * delete, or update event occurs in the Keychain.  The only action taken
03036  * is to make the signon list invalid, so it will be read in again the
03037  * next time it is accessed.
03038  */
03039 OSStatus PR_CALLBACK
03040 si_KeychainCallback( KCEvent keychainEvent, KCCallbackInfo *info, void *userContext) {
03041   PRBool    *listInvalid = (PRBool*)userContext;
03042   *listInvalid = PR_TRUE;
03043 }
03044 
03045 /*
03046  * APPLE
03047  * Get the signon data from the keychain
03048  *
03049  * This routine is called only if signon pref is enabled!!!
03050  */
03051 static int
03052 si_LoadSignonDataFromKeychain() {
03053   char * passwordRealm;
03054   si_FormSubmitData submit;
03055   nsAutoString name_array[MAX_ARRAY_SIZE];
03056   nsAutoString value_array[MAX_ARRAY_SIZE];
03057   uint8 type_array[MAX_ARRAY_SIZE];
03058   char buffer[BUFFER_SIZE];
03059   PRBool badInput = PR_FALSE;
03060   int i;
03061   KCItemRef   itemRef;
03062   KCAttributeList attrList;
03063   KCAttribute attr[2];
03064   KCItemClass itemClass = kInternetPasswordKCItemClass;
03065   KCProtocolType protocol = kNetscapeProtocolType;
03066   OSStatus status = noErr;
03067   KCSearchRef searchRef = NULL;
03068   /* initialize the submit structure */
03069   submit.name_array = name_array;
03070   submit.value_array = value_array;
03071   submit.type_array = (PRUnichar *)type_array;
03072 
03073   /* set up the attribute list */
03074   attrList.count = 2;
03075   attrList.attr = attr;
03076   attr[0].tag = kClassKCItemAttr;
03077   attr[0].data = &itemClass;
03078   attr[0].length = sizeof(itemClass);
03079 
03080   attr[1].tag = kProtocolKCItemAttr;
03081   attr[1].data = &protocol;
03082   attr[1].length = sizeof(protocol);
03083 
03084   status = KCFindFirstItem( &attrList, &searchRef, &itemRef );
03085 
03086 #if 0
03087   if (status == noErr) {
03088     /* if we found a Netscape item, let's assume notice has been given */
03089     si_SetNotificationPref(PR_TRUE);
03090   } else {
03091     si_SetNotificationPref(PR_FALSE);
03092   }
03093 #endif
03094 
03095   si_lock_signon_list();
03096   while(status == noErr) {
03097     char *value;
03098     uint16 i = 0;
03099     uint32 actualSize;
03100     KCItemFlags flags;
03101     PRBool reject = PR_FALSE;
03102     submit.value_cnt = 0;
03103 
03104     /* first find out if it is a reject entry */
03105     attr[0].tag = kFlagsKCItemAttr;
03106     attr[0].length = sizeof(KCItemFlags);
03107     attr[0].data = &flags;
03108     status = KCGetAttribute( itemRef, attr, nil );
03109     if (status != noErr) {
03110       break;
03111     }
03112     if (flags & kNegativeKCItemFlag) {
03113       reject = PR_TRUE;
03114     }
03115 
03116     /* get the server name */
03117     attr[0].tag = kServerKCItemAttr;
03118     attr[0].length = BUFFER_SIZE;
03119     attr[0].data = buffer;
03120     status = KCGetAttribute( itemRef, attr, &actualSize );
03121     if (status != noErr) {
03122       break;
03123     }
03124 
03125     /* null terminate */
03126     buffer[actualSize] = 0;
03127     passwordRealm = NULL;
03128     StrAllocCopy(passwordRealm, buffer);
03129     if (!reject) {
03130       /* get the password data */
03131       status = KCGetData(itemRef, BUFFER_SIZE, buffer, &actualSize);
03132       if (status != noErr) {
03133         break;
03134       }
03135 
03136       /* null terminate */
03137       buffer[actualSize] = 0;
03138 
03139       /* parse for '=' which separates the name and value */
03140       uint16 bufferlen = PL_strlen(buffer);
03141       for (i = 0; i < bufferlen; i++) {
03142         if (buffer[i] == '=') {
03143           value = &buffer[i+1];
03144           buffer[i] = 0;
03145           break;
03146         }
03147       }
03148       name_array[submit.value_cnt] = NULL;
03149       value_array[submit.value_cnt] = NULL;
03150       type_array[submit.value_cnt] = FORM_TYPE_PASSWORD;
03151       StrAllocCopy(name_array[submit.value_cnt], buffer);
03152       StrAllocCopy(value_array[submit.value_cnt], value);
03153     }
03154 
03155     /* get the account attribute */
03156     attr[0].tag = kAccountKCItemAttr;
03157     attr[0].length = BUFFER_SIZE;
03158     attr[0].data = buffer;
03159     status = KCGetAttribute( itemRef, attr, &actualSize );
03160     if (status != noErr) {
03161       break;
03162     }
03163 
03164     /* null terminate */
03165     buffer[actualSize] = 0;
03166     if (!reject) {
03167       /* parse for '=' which separates the name and value */
03168       uint16 bufferlen = PL_strlen(buffer);
03169       for (i = 0; i < bufferlen; i++) {
03170         if (buffer[i] == '=') {
03171           value = &buffer[i+1];
03172           buffer[i] = 0;
03173           break;
03174         }
03175       }
03176       submit.value_cnt++;
03177       name_array[submit.value_cnt] = NULL;
03178       value_array[submit.value_cnt] = NULL;
03179       type_array[submit.value_cnt] = FORM_TYPE_TEXT;
03180       StrAllocCopy(name_array[submit.value_cnt], buffer);
03181       StrAllocCopy(value_array[submit.value_cnt], value);
03182 
03183       /* check for overruning of the arrays */
03184       if (submit.value_cnt >= MAX_ARRAY_SIZE) {
03185         break;
03186       }
03187       submit.value_cnt++;
03188       /* store the info for this URL into memory-resident data structure */
03189       if (!passwordRealm || passwordRealm[0] == 0) {
03190         badInput = PR_TRUE;
03191       }
03192       if (!badInput) {
03193         si_PutData(passwordRealm, &submit, PR_FALSE);
03194       }
03195 
03196     } else {
03197       /* reject */
03198       si_PutReject(passwordRealm, nsDependentString(buffer), PR_FALSE);
03199     }
03200     reject = PR_FALSE; /* reset reject flag */
03201     PR_Free(passwordRealm);
03202     KCReleaseItemRef( &itemRef );
03203     status = KCFindNextItem( searchRef, &itemRef);
03204   }
03205   si_unlock_signon_list();
03206 
03207   if (searchRef) {
03208     KCReleaseSearchRef( &searchRef );
03209   }
03210 
03211   /* Register a callback with the Keychain if we haven't already done so. */
03212 
03213   if (si_kcUPP == NULL) {
03214     si_kcUPP = NewKCCallbackProc( si_KeychainCallback );
03215     if (!si_kcUPP) {
03216       return memFullErr;
03217     }
03218 
03219     KCAddCallback( si_kcUPP, kLockKCEventMask + kDeleteKCEventMask + kUpdateKCEventMask, &si_list_invalid );
03220     /*
03221      * Note that the callback is not necessarily removed.  We take advantage
03222      * of the fact that the Keychain will clean up the callback when the app
03223      * goes away. It is explicitly removed when the signon preference is turned off.
03224      */
03225   }
03226   if (status == errKCItemNotFound) {
03227     status = 0;
03228   }
03229   return (status);
03230 }
03231 
03232 /*
03233  * APPLE
03234  * Save signon data to Apple Keychain
03235  *
03236  * This routine is called only if signon pref is enabled!!!
03237  */
03238 static int
03239 si_SaveSignonDataInKeychain() {
03240   char* account = nil;
03241   char* password = nil;
03242   si_SignonURLStruct * URL;
03243   si_SignonUserStruct * user;
03244   si_SignonDataStruct * data;
03245   si_Reject * reject;
03246   OSStatus status;
03247   KCItemRef itemRef;
03248   KCAttribute attr;
03249   KCItemFlags flags = kInvisibleKCItemFlag + kNegativeKCItemFlag;
03250   uint32 actualLength;
03251 
03252   /* save off the reject list */
03253   if (si_reject_list) {
03254     PRInt32 rejectCount = LIST_COUNT(si_reject_list);
03255     for (PRInt32 i=0; i<rejectCount; i++) {
03256       reject = NS_STATIC_CAST(si_Reject*, si_reject_list->ElementAt(i));
03257       status = kcaddinternetpassword
03258         (reject->passwordRealm, nil,
03259          reject->userName,
03260          kAnyPort,
03261          kNetscapeProtocolType,
03262          kAnyAuthType,
03263          0,
03264          nil,
03265          &itemRef);
03266       if (status != noErr && status != errKCDuplicateItem) {
03267         return(status);
03268       }
03269       if (status == noErr) {
03270         /*
03271          * make the item invisible so the user doesn't see it and
03272          * negative so we know that it is a reject entry
03273          */
03274         attr.tag = kFlagsKCItemAttr;
03275         attr.data = &flags;
03276         attr.length = sizeof( flags );
03277 
03278         status = KCSetAttribute( itemRef, &attr );
03279         if (status != noErr) {
03280           return(status);
03281         }
03282         status = KCUpdateItem(itemRef);
03283         if (status != noErr) {
03284           return(status);
03285         }
03286         KCReleaseItemRef(&itemRef);
03287       }
03288     }
03289   }
03290 
03291   /* save off the passwords */
03292   if((si_signon_list)) {
03293     PRInt32 urlCount = LIST_COUNT(si_signon_list);
03294     for (PRInt32 i=0; i<urlCount; i++) {
03295       URL = NS_STATIC_CAST(si_SignonURLStruct*, si_signon_list->ElementAt(i));
03296 
03297       /* add each user node of the URL node */
03298       PRInt32 userCount = URL->signonUser_list.Count();
03299       for (PRInt32 i=0; i<userCount; i++) {
03300         user = NS_STATIC_CAST(si_SignonUserStruct*, URL->signonUser_list.ElementAt(i));
03301 
03302         /* write out each data node of the user node */
03303         PRInt32 count = user->signonData_list.Count();
03304         for (PRInt32 i=0; i<count; i++) {
03305           data = NS_STATIC_CAST(si_SignonDataStruct*, user->signonData_list.ElementAt(i));
03306           char* attribute = nil;
03307           if (data->isPassword) {
03308             password = PR_Malloc(PL_strlen(data->value) + PL_strlen(data->name) + 2);
03309             if (!password) {
03310               return (-1);
03311             }
03312             attribute = password;
03313           } else {
03314             account = PR_Malloc( PL_strlen(data->value) + PL_strlen(data->name) + 2);
03315             if (!account) {
03316               PR_Free(password);
03317               return (-1);
03318             }
03319             attribute = account;
03320           }
03321           PL_strcpy(attribute, data->name);
03322           PL_strcat(attribute, "=");
03323           PL_strcat(attribute, data->value);
03324         }
03325         /* if it's already there, we just want to change the password */
03326         status = kcfindinternetpassword
03327             (URL->passwordRealm,
03328              nil,
03329              account,
03330              kAnyPort,
03331              kNetscapeProtocolType,
03332              kAnyAuthType,
03333              0,
03334              nil,
03335              &actualLength,
03336              &itemRef);
03337         if (status == noErr) {
03338           status = KCSetData(itemRef, PL_strlen(password), password);
03339           if (status != noErr) {
03340             return(status);
03341           }
03342           status = KCUpdateItem(itemRef);
03343           KCReleaseItemRef(&itemRef);
03344         } else {
03345           /* wasn't there, let's add it */
03346           status = kcaddinternetpassword
03347             (URL->passwordRealm,
03348              nil,
03349              account,
03350              kAnyPort,
03351              kNetscapeProtocolType,
03352              kAnyAuthType,
03353              PL_strlen(password),
03354              password,
03355              nil);
03356         }
03357         if (account) {
03358           PR_Free(account);
03359         }
03360         if (password) {
03361           PR_Free(password);
03362         } 
03363         account = password = nil;
03364         if (status != noErr) {
03365           return(status);
03366         }
03367       }
03368     }
03369   }
03370   si_signon_list_changed = PR_FALSE;
03371   return (0);
03372 }
03373 
03374 #endif