1 /* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2 /* ***** BEGIN LICENSE BLOCK *****
3 * Version: MPL 1.1/GPL 2.0/LGPL 2.1
5 * The contents of this file are subject to the Mozilla Public License Version
6 * 1.1 (the "License"); you may not use this file except in compliance with
7 * the License. You may obtain a copy of the License at
8 * http://www.mozilla.org/MPL/
10 * Software distributed under the License is distributed on an "AS IS" basis,
11 * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
12 * for the specific language governing rights and limitations under the
15 * The Original Code is Mozilla Communicator client code.
17 * The Initial Developer of the Original Code is
18 * Netscape Communications Corporation.
19 * Portions created by the Initial Developer are Copyright (C) 1998
20 * the Initial Developer. All Rights Reserved.
23 * Pierre Phaneuf <pp@ludusdesign.com>
26 * Alternatively, the contents of this file may be used under the terms of
27 * either the GNU General Public License Version 2 or later (the "GPL"), or
28 * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
29 * in which case the provisions of the GPL or the LGPL are applicable instead
30 * of those above. If you wish to allow use of your version of this file only
31 * under the terms of either the GPL or the LGPL, and not to allow others to
32 * use your version of this file under the terms of the MPL, indicate your
33 * decision by deleting the provisions above and replace them with the notice
34 * and other provisions required by the GPL or the LGPL. If you do not delete
35 * the provisions above, a recipient may use your version of this file under
36 * the terms of any one of the MPL, the GPL or the LGPL.
38 * ***** END LICENSE BLOCK ***** */
44 #include "nsNetUtil.h"
47 #include "prpriv.h" /* for NewNamedMonitor */
48 #include "prinrval.h" /* for PR_IntervalNow */
49 #ifdef APPLE_KEYCHAIN /* APPLE */
50 #include "Keychain.h" /* APPLE */
51 #define kNetscapeProtocolType 'form' /* APPLE */
54 #include "private/prpriv.h" /* for NewNamedMonitor */
58 #include "nsIServiceManager.h"
59 #include "nsIIOService.h"
61 #include "nsIDOMHTMLDocument.h"
64 #include "nsXPIDLString.h"
65 #include "nsReadableUtils.h"
66 #include "nsIObserverService.h"
67 #include "nsIObserver.h"
68 #include "nsIPromptService2.h"
69 #include "nsIWindowWatcher.h"
70 #include "nsIAuthInformation.h"
71 #include "nsIProxiedChannel.h"
72 #include "nsIProxyInfo.h"
73 #include "nsIIDNService.h"
76 #include "nsPromptUtils.h"
78 //#define SINGSIGN_LOGGING
79 #ifdef SINGSIGN_LOGGING
80 #define LOG(args) printf args
85 // Currently the default is on, so we don't need the code to prompt the
86 // user if the default is off.
87 #undef WALLET_PASSWORDMANAGER_DEFAULT_IS_OFF
93 /* locks for signon cache */
95 static PRMonitor
* signon_lock_monitor
= NULL
;
96 static PRThread
* signon_lock_owner
= NULL
;
97 static int signon_lock_count
= 0;
101 static PRBool si_PartiallyLoaded
= PR_FALSE
;
102 static PRInt32 si_LastFormForWhichUserHasBeenSelected
= -1;
104 /* apple keychain stuff */
106 #ifdef APPLE_KEYCHAIN
107 static PRBool si_list_invalid
= PR_FALSE
;
108 static KCCallbackUPP si_kcUPP
= NULL
;
110 si_SaveSignonDataInKeychain();
113 #define USERNAMEFIELD "\\=username=\\"
114 #define PASSWORDFIELD "\\=password=\\"
120 char* signonFileName
= nsnull
;
121 static PRBool gLoadedUserData
= PR_FALSE
;
122 static PRUint32 gSelectUserDialogCount
= 0;
125 /***************************
126 * Locking the Signon List *
127 ***************************/
130 si_lock_signon_list(void) {
131 if(!signon_lock_monitor
) {
132 signon_lock_monitor
= PR_NewNamedMonitor("signon-lock");
134 PR_EnterMonitor(signon_lock_monitor
);
137 /* no current owner or owned by this thread */
138 PRThread
* t
= PR_GetCurrentThread();
139 if(signon_lock_owner
== NULL
|| signon_lock_owner
== t
) {
140 signon_lock_owner
= t
;
142 PR_ExitMonitor(signon_lock_monitor
);
146 /* owned by someone else -- wait till we can get it */
147 PR_Wait(signon_lock_monitor
, PR_INTERVAL_NO_TIMEOUT
);
152 si_unlock_signon_list(void) {
153 PR_EnterMonitor(signon_lock_monitor
);
156 /* make sure someone doesn't try to free a lock they don't own */
157 PR_ASSERT(signon_lock_owner
== PR_GetCurrentThread());
161 if(signon_lock_count
== 0) {
162 signon_lock_owner
= NULL
;
163 PR_Notify(signon_lock_monitor
);
165 PR_ExitMonitor(signon_lock_monitor
);
169 /********************************
170 * Preference Utility Functions *
171 ********************************/
174 SI_RegisterCallback(const char* domain
, PrefChangedFunc callback
, void* instance_data
) {
176 nsCOMPtr
<nsIPref
> pPrefService
= do_GetService(NS_PREF_CONTRACTID
, &ret
);
177 if (NS_SUCCEEDED(ret
)) {
178 ret
= pPrefService
->RegisterCallback(domain
, callback
, instance_data
);
183 SI_UnregisterCallback(const char* domain
, PrefChangedFunc callback
, void* instance_data
) {
185 nsCOMPtr
<nsIPref
> pPrefService
= do_GetService(NS_PREF_CONTRACTID
, &ret
);
186 if (NS_SUCCEEDED(ret
)) {
187 ret
= pPrefService
->UnregisterCallback(domain
, callback
, instance_data
);
192 SI_SetBoolPref(const char * prefname
, PRBool prefvalue
) {
194 nsCOMPtr
<nsIPref
> pPrefService
= do_GetService(NS_PREF_CONTRACTID
, &ret
);
195 if (NS_SUCCEEDED(ret
)) {
196 ret
= pPrefService
->SetBoolPref(prefname
, prefvalue
);
197 if (NS_SUCCEEDED(ret
)) {
198 ret
= pPrefService
->SavePrefFile(nsnull
);
204 SI_GetBoolPref(const char * prefname
, PRBool defaultvalue
) {
206 PRBool prefvalue
= defaultvalue
;
207 nsCOMPtr
<nsIPref
> pPrefService
= do_GetService(NS_PREF_CONTRACTID
, &ret
);
208 if (NS_SUCCEEDED(ret
)) {
209 ret
= pPrefService
->GetBoolPref(prefname
, &prefvalue
);
215 SI_SetCharPref(const char * prefname
, const char * prefvalue
) {
217 return; /* otherwise the SetCharPref routine called below will crash */
220 nsCOMPtr
<nsIPref
> pPrefService
= do_GetService(NS_PREF_CONTRACTID
, &ret
);
221 if (NS_SUCCEEDED(ret
)) {
222 ret
= pPrefService
->SetCharPref(prefname
, prefvalue
);
223 if (NS_SUCCEEDED(ret
)) {
224 ret
= pPrefService
->SavePrefFile(nsnull
);
230 SI_GetCharPref(const char * prefname
, char** aPrefvalue
) {
232 nsCOMPtr
<nsIPref
> pPrefService
= do_GetService(NS_PREF_CONTRACTID
, &ret
);
233 if (NS_SUCCEEDED(ret
)) {
234 ret
= pPrefService
->CopyCharPref(prefname
, aPrefvalue
);
235 if (NS_FAILED(ret
)) {
236 *aPrefvalue
= nsnull
;
239 *aPrefvalue
= nsnull
;
244 SI_GetLocalizedUnicharPref(const char * prefname
, PRUnichar
** aPrefvalue
) {
246 nsCOMPtr
<nsIPref
> pPrefService
= do_GetService(NS_PREF_CONTRACTID
, &ret
);
247 if (NS_SUCCEEDED(ret
)) {
248 ret
= pPrefService
->GetLocalizedUnicharPref(prefname
, aPrefvalue
);
249 if (NS_FAILED(ret
)) {
250 *aPrefvalue
= nsnull
;
253 *aPrefvalue
= nsnull
;
258 /*********************************
259 * Preferences for Single Signon *
260 *********************************/
262 static const char *pref_rememberSignons
= "signon.rememberSignons";
263 #ifdef WALLET_PASSWORDMANAGER_DEFAULT_IS_OFF
264 static const char *pref_Notified
= "signon.Notified";
266 static const char *pref_SignonFileName
= "signon.SignonFileName";
268 static PRBool si_RememberSignons
= PR_FALSE
;
269 #ifdef WALLET_PASSWORDMANAGER_DEFAULT_IS_OFF
270 static PRBool si_Notified
= PR_FALSE
;
274 si_SaveSignonDataLocked(char * state
, PRBool notify
);
280 SI_RemoveAllSignonData();
282 #ifdef WALLET_PASSWORDMANAGER_DEFAULT_IS_OFF
284 si_GetNotificationPref(void) {
289 si_SetNotificationPref(PRBool x
) {
290 SI_SetBoolPref(pref_Notified
, x
);
296 si_SetSignonRememberingPref(PRBool x
) {
297 #ifdef APPLE_KEYCHAIN
299 /* We no longer need the Keychain callback installed */
300 KCRemoveCallback( si_kcUPP
);
301 DisposeRoutineDescriptor( si_kcUPP
);
305 si_RememberSignons
= x
;
309 si_SignonRememberingPrefChanged(const char * newpref
, void * data
) {
311 x
= SI_GetBoolPref(pref_rememberSignons
, PR_TRUE
);
312 si_SetSignonRememberingPref(x
);
313 return 0; /* this is PREF_NOERROR but we no longer include prefapi.h */
317 si_RegisterSignonPrefCallbacks(void) {
319 static PRBool first_time
= PR_TRUE
;
321 first_time
= PR_FALSE
;
322 SI_RegisterCallback(pref_rememberSignons
, si_SignonRememberingPrefChanged
, NULL
);
325 if (!gLoadedUserData
) {
326 gLoadedUserData
= PR_TRUE
;
328 #ifdef WALLET_PASSWORDMANAGER_DEFAULT_IS_OFF
329 x
= SI_GetBoolPref(pref_Notified
, PR_FALSE
);
330 si_SetNotificationPref(x
);
332 x
= SI_GetBoolPref(pref_rememberSignons
, PR_FALSE
);
333 si_SetSignonRememberingPref(x
);
338 si_GetSignonRememberingPref(void) {
339 #ifdef APPLE_KEYCHAIN
340 /* If the Keychain has been locked or an item deleted or updated,
341 * we need to reload the signon data
343 if (si_list_invalid
) {
345 * set si_list_invalid to PR_FALSE first because SI_RemoveAllSignonData
346 * calls si_GetSignonRememberingPref
348 si_list_invalid
= PR_FALSE
;
353 si_RegisterSignonPrefCallbacks();
355 #ifdef WALLET_PASSWORDMANAGER_DEFAULT_IS_OFF
357 * We initially want the rememberSignons pref to be PR_FALSE. But this will
358 * prevent the notification message from ever occurring. To get around
359 * this problem, if the signon pref is PR_FALSE and no notification has
360 * ever been given, we will treat this as if the signon pref were PR_TRUE.
362 if (!si_RememberSignons
&& !si_GetNotificationPref()) {
365 return si_RememberSignons
;
368 return si_RememberSignons
;
373 SI_InitSignonFileName() {
374 SI_GetCharPref(pref_SignonFileName
, &signonFileName
);
375 if (!signonFileName
) {
376 signonFileName
= Wallet_RandomName("s");
377 SI_SetCharPref(pref_SignonFileName
, signonFileName
);
386 #ifdef WALLET_PASSWORDMANAGER_DEFAULT_IS_OFF
388 si_ConfirmYN(PRUnichar
* szMessage
, nsIDOMWindowInternal
* window
) {
389 return Wallet_ConfirmYN(szMessage
, window
);
394 si_3ButtonConfirm(PRUnichar
* szMessage
, nsIDOMWindowInternal
* window
) {
395 return Wallet_3ButtonConfirm(szMessage
, window
);
399 si_SelectDialog(const PRUnichar
* szMessage
, nsIPrompt
* dialog
, PRUnichar
** pList
, PRInt32
* pCount
, PRUint32 formNumber
) {
400 if (si_LastFormForWhichUserHasBeenSelected
== (PRInt32
)formNumber
) {
401 /* a user was already selected for this form, use same one again */
402 *pCount
= 0; /* last user selected is now at head of list */
406 PRInt32 selectedIndex
;
408 PRUnichar
* title_string
= Wallet_Localize("SelectUserTitleLine");
410 /* Notify signon manager dialog to update its display */
411 nsCOMPtr
<nsIObserverService
> os(do_GetService("@mozilla.org/observer-service;1"));
412 gSelectUserDialogCount
++;
414 os
->NotifyObservers(nsnull
, "signonSelectUser", NS_LITERAL_STRING("suspend").get());
417 rv
= dialog
->Select( title_string
, szMessage
, *pCount
, const_cast<const PRUnichar
**>(pList
), &selectedIndex
, &rtnValue
);
419 gSelectUserDialogCount
--;
421 os
->NotifyObservers(nsnull
, "signonSelectUser", NS_LITERAL_STRING("resume").get());
424 Recycle(title_string
);
425 if (selectedIndex
>= *pCount
) {
426 return PR_FALSE
; // out-of-range selection
428 *pCount
= selectedIndex
;
430 si_LastFormForWhichUserHasBeenSelected
= formNumber
;
437 (PRUnichar
** password
,
438 const PRUnichar
* dialogTitle
,
439 const PRUnichar
* szMessage
,
441 PRUint32 savePassword
,
446 PRUnichar
* prompt_string
= (PRUnichar
*)dialogTitle
;
447 if (dialogTitle
== nsnull
|| dialogTitle
[0] == 0)
448 prompt_string
= Wallet_Localize("PromptForPassword");
449 PRUnichar
* check_string
;
451 // According to nsIPrompt spec, the checkbox is shown or not
452 // depending on whether or not checkValue == nsnull, not checkMsg.
453 PRBool
* check_value
= checkValue
;
454 if (savePassword
!= SINGSIGN_SAVE_PASSWORD_PERMANENTLY
) {
455 check_string
= nsnull
;
456 check_value
= nsnull
;
457 } else if (SI_GetBoolPref(pref_Crypto
, PR_FALSE
)) {
458 check_string
= Wallet_Localize("SaveThisPasswordEncrypted");
460 check_string
= Wallet_Localize("SaveThisPasswordObscured");
463 PRBool confirmed
= PR_FALSE
;
464 res
= dialog
->PromptPassword(prompt_string
,
471 if (dialogTitle
== nsnull
)
472 Recycle(prompt_string
);
474 Recycle(check_string
);
476 if (NS_FAILED(res
)) {
482 return NS_ERROR_FAILURE
; /* user pressed cancel */
489 const PRUnichar
* dialogTitle
,
490 const PRUnichar
* szMessage
,
492 PRUint32 savePassword
,
497 PRUnichar
* prompt_string
= (PRUnichar
*)dialogTitle
;
498 if (dialogTitle
== nsnull
|| dialogTitle
[0] == 0)
499 prompt_string
= Wallet_Localize("PromptForData");
500 PRUnichar
* check_string
;
502 // According to nsIPrompt spec, the checkbox is shown or not
503 // depending on whether or not checkValue == nsnull, not checkMsg.
504 PRBool
* check_value
= checkValue
;
505 if (savePassword
!= SINGSIGN_SAVE_PASSWORD_PERMANENTLY
) {
506 check_string
= nsnull
;
507 check_value
= nsnull
;
508 } else if (SI_GetBoolPref(pref_Crypto
, PR_FALSE
)) {
509 check_string
= Wallet_Localize("SaveThisValueEncrypted");
511 check_string
= Wallet_Localize("SaveThisValueObscured");
514 PRBool confirmed
= PR_FALSE
;
515 res
= dialog
->Prompt(prompt_string
,
522 if (dialogTitle
== nsnull
|| dialogTitle
[0] == 0)
523 Recycle(prompt_string
);
525 Recycle(check_string
);
527 if (NS_FAILED(res
)) {
533 return NS_ERROR_FAILURE
; /* user pressed cancel */
538 si_CheckGetUsernamePassword
539 (PRUnichar
** username
,
540 PRUnichar
** password
,
541 const PRUnichar
* dialogTitle
,
542 const PRUnichar
* szMessage
,
544 PRUint32 savePassword
,
548 PRUnichar
* check_string
;
549 PRUnichar
* prompt_string
= (PRUnichar
*)dialogTitle
;
550 if (dialogTitle
== nsnull
|| dialogTitle
[0] == 0)
551 prompt_string
= Wallet_Localize("PromptForPassword");
553 // According to nsIPrompt spec, the checkbox is shown or not
554 // depending on whether or not checkValue == nsnull, not checkMsg.
555 PRBool
* check_value
= checkValue
;
556 if (savePassword
!= SINGSIGN_SAVE_PASSWORD_PERMANENTLY
) {
557 check_string
= nsnull
;
558 check_value
= nsnull
;
559 } else if (SI_GetBoolPref(pref_Crypto
, PR_FALSE
)) {
560 check_string
= Wallet_Localize("SaveTheseValuesEncrypted");
562 check_string
= Wallet_Localize("SaveTheseValuesObscured");
565 PRBool confirmed
= PR_FALSE
;
566 res
= dialog
->PromptUsernameAndPassword(prompt_string
,
573 if (dialogTitle
== nsnull
|| dialogTitle
[0] == 0)
574 Recycle(prompt_string
);
576 Recycle(check_string
);
578 if (NS_FAILED(res
)) {
584 return NS_ERROR_FAILURE
; /* user pressed cancel */
590 (nsIPromptService2
* aService
, nsIDOMWindow
* aParent
, nsIChannel
* aChannel
,
591 PRUint32 aLevel
, nsIAuthInformation
* aAuthInfo
, PRBool
* remembered
)
593 PRUnichar
* check_string
;
594 if (SI_GetBoolPref(pref_Crypto
, PR_FALSE
)) {
595 check_string
= Wallet_Localize("SaveTheseValuesEncrypted");
597 check_string
= Wallet_Localize("SaveTheseValuesObscured");
600 PRBool confirmed
= PR_FALSE
;
601 nsresult rv
= aService
->PromptAuth(aParent
, aChannel
, aLevel
,
602 aAuthInfo
, check_string
,
603 remembered
, &confirmed
);
605 Recycle(check_string
);
613 return NS_ERROR_FAILURE
; /* user pressed cancel */
619 /********************
621 ********************/
623 /* StrAllocCopy should really be defined elsewhere */
628 #define StrAllocCopy(dest, src) Local_SACopy (&(dest), src)
630 Local_SACopy(char **destination
, const char *source
) {
632 PL_strfree(*destination
);
634 *destination
= PL_strdup(source
);
638 #ifdef WALLET_PASSWORDMANAGER_DEFAULT_IS_OFF
639 /* If user-entered password is "********", then generate a random password */
641 si_Randomize(nsString
& password
) {
642 PRIntervalTime randomNumber
;
644 const char * hexDigits
= "0123456789AbCdEf";
645 if (password
.EqualsLiteral("********")) {
646 randomNumber
= PR_IntervalNow();
647 for (i
=0; i
<8; i
++) {
648 password
.SetCharAt(hexDigits
[randomNumber
%16], i
);
649 randomNumber
= randomNumber
/16;
656 /***********************
657 * Encryption Routines *
658 ***********************/
661 si_CompareEncryptedToCleartext(const nsString
& crypt
, const nsString
& text
) {
662 nsAutoString decrypted
;
663 if (NS_FAILED(Wallet_Decrypt(crypt
, decrypted
))) {
666 return (decrypted
== text
);
670 si_CompareEncryptedToEncrypted(const nsString
& crypt1
, const nsString
& crypt2
) {
671 nsAutoString decrypted1
;
672 nsAutoString decrypted2
;
673 if (NS_FAILED(Wallet_Decrypt(crypt1
, decrypted1
))) {
676 if (NS_FAILED(Wallet_Decrypt(crypt2
, decrypted2
))) {
679 return (decrypted1
== decrypted2
);
683 /************************
684 * Managing Signon List *
685 ************************/
688 SecondsFromPRTime(PRTime prTime
) {
689 PRInt64 microSecondsPerSecond
, intermediateResult
;
692 LL_I2L(microSecondsPerSecond
, PR_USEC_PER_SEC
);
693 LL_DIV(intermediateResult
, prTime
, microSecondsPerSecond
);
694 LL_L2UI(seconds
, intermediateResult
);
698 si_SignonDataStruct::si_SignonDataStruct()
699 : isPassword(PR_FALSE
)
701 MOZ_COUNT_CTOR(si_SignonDataStruct
);
703 si_SignonDataStruct::~si_SignonDataStruct()
705 MOZ_COUNT_DTOR(si_SignonDataStruct
);
708 class si_SignonUserStruct
{
710 si_SignonUserStruct()
712 MOZ_COUNT_CTOR(si_SignonUserStruct
);
714 ~si_SignonUserStruct()
716 for (PRInt32 i
= signonData_list
.Count() - 1; i
>= 0; i
--) {
717 delete static_cast<si_SignonDataStruct
*>(signonData_list
.ElementAt(i
));
719 MOZ_COUNT_DTOR(si_SignonUserStruct
);
722 nsVoidArray signonData_list
; // elements are si_SignonDataStruct
725 class si_SignonURLStruct
{
727 si_SignonURLStruct() : passwordRealm(NULL
), chosen_user(NULL
)
729 MOZ_COUNT_CTOR(si_SignonURLStruct
);
731 ~si_SignonURLStruct()
733 MOZ_COUNT_DTOR(si_SignonURLStruct
);
735 char * passwordRealm
;
736 si_SignonUserStruct
* chosen_user
; /* this is a state variable */
737 nsVoidArray signonUser_list
;
742 si_Reject() : passwordRealm(NULL
)
744 MOZ_COUNT_CTOR(si_Reject
);
748 MOZ_COUNT_DTOR(si_Reject
);
750 char * passwordRealm
;
754 static nsVoidArray
* si_signon_list
=0;
755 static nsVoidArray
* si_reject_list
=0;
756 #define LIST_COUNT(list) (list ? list->Count() : 0)
757 static PRBool si_signon_list_changed
= PR_FALSE
;
760 * Get the URL node for a given URL name
762 * This routine is called only when holding the signon lock!!!
764 static si_SignonURLStruct
*
765 si_GetURL(const char * passwordRealm
) {
766 si_SignonURLStruct
* url
;
767 if (!passwordRealm
) {
768 /* no passwordRealm specified, return first URL (returns NULL if not URLs) */
769 if (LIST_COUNT(si_signon_list
)==0) {
772 /* XXX how can this be right -- seems wrong to give back a random password */
773 LOG((" returning first element in the signon list\n"));
774 return (si_SignonURLStruct
*) (si_signon_list
->ElementAt(0));
777 PRInt32 urlCount
= LIST_COUNT(si_signon_list
);
779 // If the last char of passwordRealm is '/' then strip it before making comparison.
780 nsCAutoString
realmWithoutTrailingSlash(passwordRealm
);
781 if (!realmWithoutTrailingSlash
.IsEmpty() && realmWithoutTrailingSlash
.Last() == '/')
782 realmWithoutTrailingSlash
.Truncate(realmWithoutTrailingSlash
.Length()-1);
784 for (PRInt32 i
=0; i
<urlCount
; i
++) {
785 url
= static_cast<si_SignonURLStruct
*>(si_signon_list
->ElementAt(i
));
786 if(url
->passwordRealm
&& !PL_strcmp(realmWithoutTrailingSlash
.get(), url
->passwordRealm
)) {
795 * composite URL struct for handling the migration of legacy password entries.
798 class si_SignonCompositeURLStruct
: public si_SignonURLStruct
{
800 si_SignonURLStruct
*primaryUrl
;
801 si_SignonURLStruct
*legacyUrl
;
804 static si_SignonCompositeURLStruct
* si_composite_url
=0;
806 #if defined(SINGSIGN_LOGGING)
808 si_DumpUserList(nsVoidArray
&list
)
810 LOG(("dumping user list:\n"));
811 PRInt32 i
, j
, user_count
= list
.Count(), data_count
;
812 for (i
=0; i
<user_count
; ++i
) {
813 si_SignonUserStruct
*user
= (si_SignonUserStruct
*) list
[i
];
814 LOG((" user[%d]\n", i
));
815 data_count
= user
->signonData_list
.Count();
816 for (j
=0; j
<data_count
; ++j
) {
817 si_SignonDataStruct
*data
= (si_SignonDataStruct
*) user
->signonData_list
[j
];
819 NS_ConvertUTF16toUTF8(data
->name
).get(),
820 NS_ConvertUTF16toUTF8(data
->value
).get()));
826 static si_SignonURLStruct
*
827 si_GetCompositeURL(const char *primaryRealm
, const char *legacyRealm
)
829 si_SignonURLStruct
*primaryUrl
, *legacyUrl
;
831 primaryUrl
= si_GetURL(primaryRealm
);
834 legacyUrl
= si_GetURL(legacyRealm
);
838 if (primaryUrl
&& legacyUrl
) {
839 LOG((">>> building composite URL struct\n"));
840 if (si_composite_url
) {
841 NS_ERROR("si_composite_url already in use");
844 si_composite_url
= new si_SignonCompositeURLStruct
;
845 if (!si_composite_url
)
848 si_composite_url
->primaryUrl
= primaryUrl
;
849 si_composite_url
->legacyUrl
= legacyUrl
;
851 si_composite_url
->signonUser_list
.AppendElements(primaryUrl
->signonUser_list
);
852 si_composite_url
->signonUser_list
.AppendElements(legacyUrl
->signonUser_list
);
854 #if defined(SINGSIGN_LOGGING)
855 si_DumpUserList(si_composite_url
->signonUser_list
);
858 /* need to transfer the chosen_user state variable */
859 if (primaryUrl
->chosen_user
)
860 si_composite_url
->chosen_user
= primaryUrl
->chosen_user
;
861 else if (legacyUrl
->chosen_user
) {
862 si_SignonUserStruct
*chosen_user
= legacyUrl
->chosen_user
;
864 /* XXX fixup chosen_user -- THIS SHOULD NOT BE NECESSARY */
865 index
= legacyUrl
->signonUser_list
.IndexOf(chosen_user
);
867 index
= primaryUrl
->signonUser_list
.IndexOf(chosen_user
);
869 primaryUrl
->chosen_user
= chosen_user
;
870 legacyUrl
->chosen_user
= NULL
;
872 /* move first element of legacy user list to front */
873 index
= si_composite_url
->signonUser_list
.IndexOf(chosen_user
);
875 si_composite_url
->signonUser_list
.MoveElement(index
, 0);
876 si_composite_url
->chosen_user
= chosen_user
;
879 si_composite_url
->chosen_user
= NULL
;
881 #if defined(SINGSIGN_LOGGING)
882 LOG(("after chosen_user fixup [chosen_user=%x]:\n", si_composite_url
->chosen_user
));
883 si_DumpUserList(si_composite_url
->signonUser_list
);
886 return si_composite_url
;
896 si_SetChosenUser(si_SignonURLStruct
*url
, si_SignonUserStruct
*chosen_user
)
900 index
= url
->signonUser_list
.IndexOf(chosen_user
);
902 url
->chosen_user
= NULL
;
906 url
->chosen_user
= chosen_user
;
911 si_ReleaseCompositeURL(si_SignonURLStruct
*url
)
913 if (url
== si_composite_url
) {
914 si_SignonUserStruct
*chosen_user
= url
->chosen_user
;
915 /* need to transfer the chosen_user state variable */
919 /* store chosen_user */
920 index
= si_SetChosenUser(url
= si_composite_url
->primaryUrl
, chosen_user
);
922 si_composite_url
->legacyUrl
->chosen_user
= NULL
;
924 index
= si_SetChosenUser(url
= si_composite_url
->legacyUrl
, chosen_user
);
925 NS_ASSERTION(index
>= 0, "chosen_user not found");
927 /* need to move chosen_user to front of list */
928 url
->signonUser_list
.MoveElement(index
, 0);
931 si_composite_url
->primaryUrl
->chosen_user
= NULL
;
932 si_composite_url
->legacyUrl
->chosen_user
= NULL
;
934 si_composite_url
->primaryUrl
= NULL
;
935 si_composite_url
->legacyUrl
= NULL
;
936 si_composite_url
->chosen_user
= NULL
;
937 si_composite_url
->signonUser_list
.Clear();
939 delete si_composite_url
;
940 si_composite_url
= NULL
;
944 /* Remove a user node from a given URL node */
946 si_RemoveUser(const char *passwordRealm
, const nsString
& userName
, PRBool save
, PRBool loginFailure
, PRBool notify
, PRBool first
= PR_FALSE
) {
947 si_SignonURLStruct
* url
;
948 si_SignonUserStruct
* user
;
949 si_SignonDataStruct
* data
;
951 si_lock_signon_list();
953 /* get URL corresponding to host */
954 url
= si_GetURL(passwordRealm
);
957 si_unlock_signon_list();
961 /* free the data in each node of the specified user node for this URL */
964 /* remove the first user */
965 user
= static_cast<si_SignonUserStruct
*>
966 (url
->signonUser_list
.ElementAt(0));
970 /* find the specified user */
971 PRInt32 userCount
= url
->signonUser_list
.Count();
972 for (PRInt32 i
=0; i
<userCount
; i
++) {
973 user
= static_cast<si_SignonUserStruct
*>(url
->signonUser_list
.ElementAt(i
));
974 PRInt32 dataCount
= user
->signonData_list
.Count();
975 for (PRInt32 ii
=0; ii
<dataCount
; ii
++) {
976 data
= static_cast<si_SignonDataStruct
*>(user
->signonData_list
.ElementAt(ii
));
977 if (si_CompareEncryptedToCleartext(data
->value
, userName
)) {
982 si_unlock_signon_list();
983 return PR_FALSE
; /* user not found so nothing to remove */
987 /* free the user node */
988 url
->signonUser_list
.RemoveElement(user
);
991 /* remove this URL if it contains no more users */
992 if (url
->signonUser_list
.Count() == 0) {
993 PR_Free(url
->passwordRealm
);
994 si_signon_list
->RemoveElement(url
);
998 /* write out the change to disk */
1000 si_signon_list_changed
= PR_TRUE
;
1001 si_SaveSignonDataLocked("signons", notify
);
1004 si_unlock_signon_list();
1009 SINGSIGN_RemoveUser(const char *host
, const PRUnichar
*user
, PRBool notify
) {
1010 PRBool rv
= si_RemoveUser(host
, nsDependentString(user
), PR_TRUE
, PR_FALSE
, notify
);
1011 return rv
? NS_OK
: NS_ERROR_FAILURE
;
1015 SINGSIGN_RemoveUserAfterLoginFailure(const char *host
, const PRUnichar
*user
, PRBool notify
) {
1016 PRBool rv
= si_RemoveUser(host
, nsDependentString(user
), PR_TRUE
, PR_TRUE
, notify
);
1017 return rv
? NS_OK
: NS_ERROR_FAILURE
;
1021 si_FreeReject(si_Reject
* reject
);
1024 SINGSIGN_RemoveReject(const char *host
) {
1026 nsresult rv
= NS_ERROR_FAILURE
;
1028 /* step backwards through all rejects */
1029 si_lock_signon_list();
1030 PRInt32 rejectCount
= LIST_COUNT(si_reject_list
);
1031 while (rejectCount
>0) {
1033 reject
= static_cast<si_Reject
*>(si_reject_list
->ElementAt(rejectCount
));
1034 if (reject
&& !PL_strcmp(reject
->passwordRealm
, host
)) {
1035 si_FreeReject(reject
);
1036 si_signon_list_changed
= PR_TRUE
;
1040 si_SaveSignonDataLocked("rejects", PR_FALSE
);
1041 si_unlock_signon_list();
1046 si_PutReject(const char * passwordRealm
, const nsString
& userName
, PRBool save
);
1049 SINGSIGN_AddReject(const char *host
/*, const char *userName*/) {
1050 si_PutReject(host
, nsString(/*thisParameter_isObsolete*/), PR_TRUE
);
1051 // @see http://bonsai.mozilla.org/cvsblame.cgi?file=mozilla/extensions/wallet/src/singsign.cpp&rev=1.212&mark=1693#1650
1055 /* Determine if a specified url/user exists */
1057 si_CheckForUser(const char *passwordRealm
, const nsString
& userName
) {
1058 si_SignonURLStruct
* url
;
1059 si_SignonUserStruct
* user
;
1060 si_SignonDataStruct
* data
;
1062 /* do nothing if signon preference is not enabled */
1063 if (!si_GetSignonRememberingPref()) {
1067 si_lock_signon_list();
1069 /* get URL corresponding to passwordRealm */
1070 url
= si_GetURL(passwordRealm
);
1073 si_unlock_signon_list();
1077 /* find the specified user */
1078 PRInt32 userCount
= url
->signonUser_list
.Count();
1079 for (PRInt32 i
=0; i
<userCount
; i
++) {
1080 user
= static_cast<si_SignonUserStruct
*>(url
->signonUser_list
.ElementAt(i
));
1081 PRInt32 dataCount
= user
->signonData_list
.Count();
1082 for (PRInt32 ii
=0; ii
<dataCount
; ii
++) {
1083 data
= static_cast<si_SignonDataStruct
*>(user
->signonData_list
.ElementAt(ii
));
1084 if (si_CompareEncryptedToCleartext(data
->value
, userName
)) {
1085 si_unlock_signon_list();
1090 si_unlock_signon_list();
1091 return PR_FALSE
; /* user not found */
1095 * Get first data node that is not a password
1098 static si_SignonDataStruct
*
1099 si_GetFirstNonPasswordData(si_SignonUserStruct
* user
) {
1100 PRInt32 dataCount
= user
->signonData_list
.Count();
1101 for (PRInt32 j
=0; j
<dataCount
; j
++) {
1102 si_SignonDataStruct
* data
=
1103 static_cast<si_SignonDataStruct
*>(user
->signonData_list
.ElementAt(j
));
1104 if (!data
->isPassword
) {
1112 * Get the user node for a given URL
1114 * This routine is called only when holding the signon lock!!!
1116 * This routine is called only if signon pref is enabled!!!
1118 static si_SignonUserStruct
*
1119 si_GetUser(nsIPrompt
* dialog
, const char* passwordRealm
, const char *legacyRealm
,
1120 PRBool pickFirstUser
, const nsString
& userText
, PRUint32 formNumber
) {
1121 si_SignonURLStruct
* url
;
1122 si_SignonUserStruct
* user
= nsnull
;
1123 si_SignonDataStruct
* data
;
1125 /* get to node for this URL */
1126 url
= si_GetCompositeURL(passwordRealm
, legacyRealm
);
1130 /* node for this URL was found */
1132 if ((user_count
= url
->signonUser_list
.Count()) == 1) {
1134 /* only one set of data exists for this URL so select it */
1135 user
= static_cast<si_SignonUserStruct
*>
1136 (url
->signonUser_list
.ElementAt(0));
1137 url
->chosen_user
= user
;
1139 } else if (pickFirstUser
) {
1140 PRInt32 userCount
= url
->signonUser_list
.Count();
1141 for (PRInt32 i
=0; i
<userCount
; i
++) {
1142 user
= static_cast<si_SignonUserStruct
*>(url
->signonUser_list
.ElementAt(i
));
1143 /* consider first data node to be the identifying item */
1144 data
= static_cast<si_SignonDataStruct
*>
1145 (user
->signonData_list
.ElementAt(0));
1146 if (data
->name
!= userText
) {
1147 /* name of current data item does not match name in data node */
1152 url
->chosen_user
= user
;
1155 /* multiple users for this URL so a choice needs to be made */
1158 si_SignonUserStruct
** users
;
1159 si_SignonUserStruct
** users2
;
1160 list
= (PRUnichar
**)PR_Malloc(user_count
*sizeof(PRUnichar
*));
1161 users
= (si_SignonUserStruct
**) PR_Malloc(user_count
*sizeof(si_SignonUserStruct
*));
1165 /* step through set of user nodes for this URL and create list of
1166 * first data node of each (presumably that is the user name).
1167 * Note that the user nodes are already ordered by
1168 * most-recently-used so the first one in the list is the most
1169 * likely one to be chosen.
1172 PRInt32 userCount
= url
->signonUser_list
.Count();
1173 for (PRInt32 i
=0; i
<userCount
; i
++) {
1174 user
= static_cast<si_SignonUserStruct
*>(url
->signonUser_list
.ElementAt(i
));
1175 /* consider first data node to be the identifying item */
1176 data
= static_cast<si_SignonDataStruct
*>
1177 (user
->signonData_list
.ElementAt(0));
1178 if (data
->name
!= userText
) {
1179 /* name of current data item does not match name in data node */
1182 nsAutoString userName
;
1183 data
= si_GetFirstNonPasswordData(user
);
1184 if (NS_SUCCEEDED(Wallet_Decrypt (data
->value
, userName
))) {
1185 *(list2
++) = ToNewUnicode(userName
);
1193 /* have user select a username from the list */
1194 PRUnichar
* selectUser
= Wallet_Localize("SelectUser");
1195 if (user_count
== 0) {
1196 /* not first data node for any saved user, so simply pick first user */
1197 if (url
->chosen_user
) {
1198 user
= url
->chosen_user
;
1200 /* no user selection had been made for first data node */
1203 } else if (user_count
== 1) {
1204 /* only one user for this form at this url, so select it */
1206 } else if ((user_count
> 1) && si_SelectDialog(selectUser
, dialog
, list
, &user_count
, formNumber
)) {
1207 /* user pressed OK */
1208 if (user_count
== -1) {
1209 user_count
= 0; /* user didn't select, so use first one */
1211 user
= users
[user_count
]; /* this is the selected item */
1212 /* item selected is now most-recently used, put at head of list */
1213 url
->signonUser_list
.RemoveElement(user
);
1214 url
->signonUser_list
.InsertElementAt(user
, 0);
1218 Recycle(selectUser
);
1219 url
->chosen_user
= user
;
1220 while (--list2
> list
) {
1226 /* if we don't remove the URL from the cache at this point, the
1227 * cached copy will be brought containing the last-used username
1228 * rather than the username that was just selected
1232 NET_RemoveURLFromCache(NET_CreateURLStruct((char *)passwordRealm
, NET_DONT_RELOAD
));
1236 si_ReleaseCompositeURL(url
);
1244 * Get a specific user node for a given URL
1246 * This routine is called only when holding the signon lock!!!
1248 * This routine is called only if signon pref is enabled!!!
1250 static si_SignonUserStruct
*
1251 si_GetSpecificUser(const char* passwordRealm
, const nsString
& userName
, const nsString
& userText
) {
1252 si_SignonURLStruct
* url
;
1253 si_SignonUserStruct
* user
;
1254 si_SignonDataStruct
* data
;
1256 /* get to node for this URL */
1257 url
= si_GetURL(passwordRealm
);
1260 /* step through set of user nodes for this URL looking for specified username */
1261 PRInt32 userCount2
= url
->signonUser_list
.Count();
1262 for (PRInt32 i2
=0; i2
<userCount2
; i2
++) {
1263 user
= static_cast<si_SignonUserStruct
*>(url
->signonUser_list
.ElementAt(i2
));
1264 /* consider first data node to be the identifying item */
1265 data
= static_cast<si_SignonDataStruct
*>
1266 (user
->signonData_list
.ElementAt(0));
1267 if (data
->name
!= userText
) {
1268 /* desired username text does not match name in data node */
1271 if (!si_CompareEncryptedToCleartext(data
->value
, userName
)) {
1272 /* desired username value does not match value in data node */
1278 /* if we don't remove the URL from the cache at this point, the
1279 * cached copy will be brought containing the last-used username
1280 * rather than the username that was just selected
1284 NET_RemoveURLFromCache(NET_CreateURLStruct((char *)passwordRealm
, NET_DONT_RELOAD
));
1292 * Get the url and user for which a change-of-password is to be applied
1294 * This routine is called only when holding the signon lock!!!
1296 * This routine is called only if signon pref is enabled!!!
1298 static si_SignonUserStruct
*
1299 si_GetURLAndUserForChangeForm(nsIPrompt
* dialog
, const nsString
& password
)
1301 si_SignonURLStruct
* url
;
1302 si_SignonUserStruct
* user
;
1303 si_SignonDataStruct
* data
;
1308 si_SignonUserStruct
** users
;
1309 si_SignonUserStruct
** users2
;
1310 si_SignonURLStruct
** urls
;
1311 si_SignonURLStruct
** urls2
;
1313 /* get count of total number of user nodes at all url nodes */
1315 PRInt32 urlCount
= LIST_COUNT(si_signon_list
);
1316 for (PRInt32 i
=0; i
<urlCount
; i
++) {
1317 url
= static_cast<si_SignonURLStruct
*>(si_signon_list
->ElementAt(i
));
1318 PRInt32 userCount
= url
->signonUser_list
.Count();
1319 for (PRInt32 ii
=0; ii
<userCount
; ii
++) {
1320 user
= static_cast<si_SignonUserStruct
*>(url
->signonUser_list
.ElementAt(ii
));
1325 /* avoid malloc of zero */
1326 if( user_count
== 0 )
1331 /* allocate lists for maximumum possible url and user names */
1332 list
= (PRUnichar
**)PR_Malloc(user_count
*sizeof(PRUnichar
*));
1333 users
= (si_SignonUserStruct
**) PR_Malloc(user_count
*sizeof(si_SignonUserStruct
*));
1334 urls
= (si_SignonURLStruct
**)PR_Malloc(user_count
*sizeof(si_SignonUserStruct
*));
1339 /* step through set of URLs and users and create list of each */
1341 PRInt32 urlCount2
= LIST_COUNT(si_signon_list
);
1342 for (PRInt32 i2
=0; i2
<urlCount2
; i2
++) {
1343 url
= static_cast<si_SignonURLStruct
*>(si_signon_list
->ElementAt(i2
));
1344 PRInt32 userCount
= url
->signonUser_list
.Count();
1345 for (PRInt32 i3
=0; i3
<userCount
; i3
++) {
1346 user
= static_cast<si_SignonUserStruct
*>(url
->signonUser_list
.ElementAt(i3
));
1347 /* find saved password and see if it matches password user just entered */
1348 PRInt32 dataCount
= user
->signonData_list
.Count();
1349 for (PRInt32 i4
=0; i4
<dataCount
; i4
++) {
1350 data
= static_cast<si_SignonDataStruct
*>(user
->signonData_list
.ElementAt(i4
));
1351 if (data
->isPassword
&& si_CompareEncryptedToCleartext(data
->value
, password
)) {
1352 /* passwords match so add entry to list */
1353 /* consider first data node to be the identifying item */
1354 data
= static_cast<si_SignonDataStruct
*>
1355 (user
->signonData_list
.ElementAt(0));
1357 nsAutoString userName
;
1358 if (NS_SUCCEEDED(Wallet_Decrypt (data
->value
, userName
))) {
1359 nsAutoString temp
; temp
.AssignASCII(url
->passwordRealm
); // XXX non-ascii realms?
1360 temp
.AppendLiteral(":");
1361 temp
.Append(userName
);
1363 *list2
= ToNewUnicode(temp
);
1376 PRUnichar
* msg
= Wallet_Localize("SelectUserWhosePasswordIsBeingChanged");
1377 //@@@@ is 0 correct?
1378 if (user_count
&& si_SelectDialog(msg
, dialog
, list
, &user_count
, 0)) {
1379 user
= users
[user_count
];
1380 url
= urls
[user_count
];
1382 * since this user node is now the most-recently-used one, move it
1383 * to the head of the user list so that it can be favored for
1384 * re-use the next time this form is encountered
1386 url
->signonUser_list
.RemoveElement(user
);
1387 url
->signonUser_list
.InsertElementAt(user
, 0);
1388 si_signon_list_changed
= PR_TRUE
;
1389 si_SaveSignonDataLocked("signons", PR_TRUE
);
1395 /* free allocated strings */
1396 while (--list2
> list
) {
1406 * Remove all the signons and free everything
1410 SI_RemoveAllSignonData() {
1411 if (si_PartiallyLoaded
) {
1412 /* repeatedly remove first user node of first URL node */
1413 while (si_RemoveUser(NULL
, EmptyString(), PR_FALSE
, PR_FALSE
, PR_FALSE
, PR_TRUE
)) {
1416 si_PartiallyLoaded
= PR_FALSE
;
1418 if (si_reject_list
) {
1420 while (LIST_COUNT(si_reject_list
)>0) {
1421 reject
= static_cast<si_Reject
*>(si_reject_list
->ElementAt(0));
1423 si_FreeReject(reject
);
1424 si_signon_list_changed
= PR_TRUE
;
1427 delete si_reject_list
;
1428 si_reject_list
= nsnull
;
1430 delete si_signon_list
;
1431 si_signon_list
= nsnull
;
1436 if (si_PartiallyLoaded
) {
1437 /* repeatedly remove first user node of first URL node */
1438 while (si_RemoveUser(NULL
, EmptyString(), PR_FALSE
, PR_FALSE
, PR_TRUE
, PR_TRUE
)) {
1441 si_PartiallyLoaded
= PR_FALSE
;
1442 si_signon_list_changed
= PR_TRUE
;
1443 si_SaveSignonDataLocked("signons", PR_TRUE
);
1447 SI_ClearUserData() {
1448 SI_RemoveAllSignonData();
1449 gLoadedUserData
= PR_FALSE
;
1453 SI_DeletePersistentUserData() {
1455 if (signonFileName
&& signonFileName
[0]) {
1456 nsCOMPtr
<nsIFile
> file
;
1457 nsresult rv
= Wallet_ProfileDirectory(getter_AddRefs(file
));
1458 if (NS_SUCCEEDED(rv
)) {
1459 rv
= file
->AppendNative(nsDependentCString(signonFileName
));
1460 if (NS_SUCCEEDED(rv
))
1461 file
->Remove(PR_FALSE
);
1466 /****************************
1467 * Managing the Reject List *
1468 ****************************/
1471 si_FreeReject(si_Reject
* reject
) {
1474 * This routine should only be called while holding the
1481 si_reject_list
->RemoveElement(reject
);
1482 PR_FREEIF(reject
->passwordRealm
);
1487 si_CheckForReject(const char * passwordRealm
, const nsString
& userName
) {
1490 si_lock_signon_list();
1491 if (si_reject_list
) {
1492 PRInt32 rejectCount
= LIST_COUNT(si_reject_list
);
1493 for (PRInt32 i
=0; i
<rejectCount
; i
++) {
1494 reject
= static_cast<si_Reject
*>(si_reject_list
->ElementAt(i
));
1495 if(!PL_strcmp(passwordRealm
, reject
->passwordRealm
)) {
1496 // No need for username check on a rejectlist entry. URL check is sufficient
1497 // if(!PL_strcmp(userName, reject->userName) && !PL_strcmp(passwordRealm, reject->passwordRealm)) {
1498 si_unlock_signon_list();
1503 si_unlock_signon_list();
1508 si_PutReject(const char * passwordRealm
, const nsString
& userName
, PRBool save
) {
1509 char * passwordRealm2
=NULL
;
1510 nsAutoString userName2
;
1511 si_Reject
* reject
= new si_Reject
;
1514 if(!si_reject_list
) {
1515 si_reject_list
= new nsVoidArray();
1516 if(!si_reject_list
) {
1523 * lock the signon list
1524 * Note that, for efficiency, SI_LoadSignonData already sets the lock
1525 * before calling this routine whereas none of the other callers do.
1526 * So we need to determine whether or not we were called from
1527 * SI_LoadSignonData before setting or clearing the lock. We can
1528 * determine this by testing "save" since only SI_LoadSignonData
1529 * passes in a value of PR_FALSE for "save".
1532 si_lock_signon_list();
1535 StrAllocCopy(passwordRealm2
, passwordRealm
);
1536 userName2
= userName
;
1537 reject
->passwordRealm
= passwordRealm2
;
1538 reject
->userName
= userName2
;
1541 /* add it to the list in alphabetical order */
1542 si_Reject
* tmp_reject
;
1543 PRBool rejectAdded
= PR_FALSE
;
1544 PRInt32 rejectCount
= LIST_COUNT(si_reject_list
);
1545 for (PRInt32 i
= 0; i
<rejectCount
; ++i
) {
1546 tmp_reject
= static_cast<si_Reject
*>(si_reject_list
->ElementAt(i
));
1548 if (PL_strcasecmp(reject
->passwordRealm
, tmp_reject
->passwordRealm
)<0) {
1549 si_reject_list
->InsertElementAt(reject
, i
);
1550 rejectAdded
= PR_TRUE
;
1556 si_reject_list
->AppendElement(reject
);
1559 /* add it to the end of the list */
1560 si_reject_list
->AppendElement(reject
);
1564 si_signon_list_changed
= PR_TRUE
;
1565 si_lock_signon_list();
1566 si_SaveSignonDataLocked("rejects", PR_TRUE
);
1567 si_unlock_signon_list();
1573 * Put data obtained from a submit form into the data structure for
1576 * See comments below about state of signon lock when routine is called!!!
1578 * This routine is called only if signon pref is enabled!!!
1581 si_PutData(const char *passwordRealm
, nsVoidArray
*signonData
, PRBool save
) {
1582 PRBool added_to_list
= PR_FALSE
;
1583 si_SignonURLStruct
* url
;
1584 si_SignonUserStruct
* user
;
1585 si_SignonDataStruct
* data
;
1586 si_SignonDataStruct
* data2
;
1587 PRBool mismatch
= PR_FALSE
;
1589 /* discard this if the password is empty */
1590 PRInt32 count
= signonData
->Count();
1591 for (PRInt32 i
=0; i
<count
; i
++) {
1592 data2
= static_cast<si_SignonDataStruct
*>(signonData
->ElementAt(i
));
1593 if (data2
->isPassword
&& data2
->value
.IsEmpty()) {
1598 /* make sure the signon list exists */
1599 if (!si_signon_list
) {
1600 si_signon_list
= new nsVoidArray();
1601 if (!si_signon_list
) {
1607 * lock the signon list
1608 * Note that, for efficiency, SI_LoadSignonData already sets the lock
1609 * before calling this routine whereas none of the other callers do.
1610 * So we need to determine whether or not we were called from
1611 * SI_LoadSignonData before setting or clearing the lock. We can
1612 * determine this by testing "save" since only SI_LoadSignonData passes
1613 * in a value of PR_FALSE for "save".
1616 si_lock_signon_list();
1619 /* find node in signon list having the same URL */
1620 if ((url
= si_GetURL(passwordRealm
)) == NULL
) {
1622 /* doesn't exist so allocate new node to be put into signon list */
1623 url
= new si_SignonURLStruct
;
1626 si_unlock_signon_list();
1631 /* fill in fields of new node */
1632 url
->passwordRealm
= nsnull
;
1633 if (passwordRealm
) {
1634 url
->passwordRealm
= PL_strdup(passwordRealm
);
1637 if (!url
->passwordRealm
) {
1639 si_unlock_signon_list();
1644 /* put new node into signon list */
1647 /* add it to the list in alphabetical order */
1648 si_SignonURLStruct
* tmp_URL
;
1649 PRInt32 urlCount
= LIST_COUNT(si_signon_list
);
1650 for (PRInt32 ii
= 0; ii
<urlCount
; ++ii
) {
1651 tmp_URL
= static_cast<si_SignonURLStruct
*>(si_signon_list
->ElementAt(ii
));
1653 if (PL_strcasecmp(url
->passwordRealm
, tmp_URL
->passwordRealm
)<0) {
1654 si_signon_list
->InsertElementAt(url
, ii
);
1655 added_to_list
= PR_TRUE
;
1660 if (!added_to_list
) {
1661 si_signon_list
->AppendElement(url
);
1664 /* add it to the end of the list */
1665 si_signon_list
->AppendElement(url
);
1669 /* initialize state variables in URL node */
1670 url
->chosen_user
= NULL
;
1673 * see if a user node with data list matching new data already exists
1674 * (password fields will not be checked for in this matching)
1676 PRInt32 userCount
= url
->signonUser_list
.Count();
1677 for (PRInt32 i2
=0; i2
<userCount
; i2
++) {
1679 break; /* otherwise we could be asked for master password when loading signon data */
1681 user
= static_cast<si_SignonUserStruct
*>(url
->signonUser_list
.ElementAt(i2
));
1683 PRInt32 dataCount
= user
->signonData_list
.Count();
1684 for (PRInt32 i3
=0; i3
<dataCount
; i3
++) {
1685 data
= static_cast<si_SignonDataStruct
*>(user
->signonData_list
.ElementAt(i3
));
1686 mismatch
= PR_FALSE
;
1688 /* check for match on name field and type field */
1689 if (j
< signonData
->Count()) {
1690 data2
= static_cast<si_SignonDataStruct
*>(signonData
->ElementAt(j
));
1691 if ((data
->isPassword
== data2
->isPassword
) &&
1692 (data
->name
== data2
->name
)) {
1694 /* success, now check for match on value field if not password */
1695 if (si_CompareEncryptedToEncrypted(data
->value
, data2
->value
) || data
->isPassword
) {
1699 break; /* value mismatch, try next user */
1703 break; /* name or type mismatch, try next user */
1709 /* all names and types matched and all non-password values matched */
1712 * note: it is ok for password values not to match; it means
1713 * that the user has either changed his password behind our
1714 * back or that he previously mis-entered the password
1717 /* update the saved password values */
1719 PRInt32 dataCount2
= user
->signonData_list
.Count();
1720 for (PRInt32 i4
=0; i4
<dataCount2
; i4
++) {
1721 data
= static_cast<si_SignonDataStruct
*>(user
->signonData_list
.ElementAt(i4
));
1723 /* update saved password */
1724 if ((j
< signonData
->Count()) && data
->isPassword
) {
1725 data2
= static_cast<si_SignonDataStruct
*>(signonData
->ElementAt(j
));
1726 if (!si_CompareEncryptedToEncrypted(data
->value
, data2
->value
)) {
1727 si_signon_list_changed
= PR_TRUE
;
1728 data
->value
= data2
->value
;
1729 user
->time
= SecondsFromPRTime(PR_Now());
1730 /* commenting out because I don't see how such randomizing could ever have worked. */
1731 // si_Randomize(data->value);
1738 * since this user node is now the most-recently-used one, move it
1739 * to the head of the user list so that it can be favored for
1740 * re-use the next time this form is encountered
1742 url
->signonUser_list
.RemoveElement(user
);
1743 url
->signonUser_list
.InsertElementAt(user
, 0);
1747 si_signon_list_changed
= PR_TRUE
;
1748 si_SaveSignonDataLocked("signons", PR_TRUE
);
1749 si_unlock_signon_list();
1751 return; /* nothing more to do since data already exists */
1755 /* user node with current data not found so create one */
1756 user
= new si_SignonUserStruct
;
1759 si_unlock_signon_list();
1764 /* create and fill in data nodes for new user node */
1765 for (PRInt32 k
=0; k
<signonData
->Count(); k
++) {
1767 /* create signon data node */
1768 data
= new si_SignonDataStruct
;
1772 si_unlock_signon_list();
1776 data2
= static_cast<si_SignonDataStruct
*>(signonData
->ElementAt(k
));
1777 data
->isPassword
= data2
->isPassword
;
1778 data
->name
= data2
->name
;
1779 data
->value
= data2
->value
;
1780 /* commenting out because I don't see how such randomizing could ever have worked. */
1781 // if (data->isPassword) {
1782 // si_Randomize(data->value);
1784 /* append new data node to end of data list */
1785 user
->signonData_list
.AppendElement(data
);
1788 /* append new user node to front of user list for matching URL */
1790 * Note that by appending to the front, we assure that if there are
1791 * several users, the most recently used one will be favored for
1792 * reuse the next time this form is encountered. But don't do this
1793 * when reading in the saved signons (i.e., when save is PR_FALSE), otherwise
1794 * we will be reversing the order when reading in.
1797 user
->time
= SecondsFromPRTime(PR_Now());
1798 url
->signonUser_list
.InsertElementAt(user
, 0);
1799 si_signon_list_changed
= PR_TRUE
;
1800 si_SaveSignonDataLocked("signons", PR_TRUE
);
1801 si_unlock_signon_list();
1804 url
->signonUser_list
.AppendElement(user
);
1809 /*****************************
1810 * Managing the Signon Files *
1811 *****************************/
1813 ////////////////////////////////////////////////////////////////////////////////
1814 // nsSingleSignOnProfileObserver
1815 // This observer is a global object and is registered the first time any consumer
1816 // touches signon profile data. That is, when SI_LoadSignonData() is called.
1818 class nsSingleSignOnProfileObserver
: public nsIObserver
1821 nsSingleSignOnProfileObserver() { }
1822 virtual ~nsSingleSignOnProfileObserver() {}
1826 NS_IMETHODIMP
Observe(nsISupports
*, const char *aTopic
, const PRUnichar
*someData
)
1828 if (!strcmp(aTopic
, "profile-before-change")) {
1830 if (!nsCRT::strcmp(someData
, NS_LITERAL_STRING("shutdown-cleanse").get()))
1831 SI_DeletePersistentUserData();
1836 NS_IMPL_THREADSAFE_ISUPPORTS1(nsSingleSignOnProfileObserver
, nsIObserver
)
1838 static nsresult
EnsureSingleSignOnProfileObserver()
1840 static nsSingleSignOnProfileObserver
*gSignOnProfileObserver
;
1842 if (!gSignOnProfileObserver
) {
1843 nsCOMPtr
<nsIObserverService
> observerService(do_GetService("@mozilla.org/observer-service;1"));
1844 if (!observerService
)
1845 return NS_ERROR_FAILURE
;
1847 gSignOnProfileObserver
= new nsSingleSignOnProfileObserver
;
1848 if (!gSignOnProfileObserver
)
1849 return NS_ERROR_OUT_OF_MEMORY
;
1851 // The observer service holds the only ref to the observer
1852 // It thus has the lifespan of the observer service
1853 nsresult rv
= observerService
->AddObserver(gSignOnProfileObserver
, "profile-before-change", PR_FALSE
);
1854 if (NS_FAILED(rv
)) {
1855 delete gSignOnProfileObserver
;
1856 gSignOnProfileObserver
= nsnull
;
1863 #define BUFFER_SIZE 4096
1866 * get a line from a file
1867 * return -1 if end of file reached
1868 * strip carriage returns and line feeds from end of line
1871 si_ReadLine(nsIInputStream
* strm
, nsString
& lineBuffer
)
1874 nsresult rv
= wallet_GetLine(strm
, line
);
1878 CopyUTF8toUTF16(line
, lineBuffer
);
1883 * Load signon data from disk file
1886 * 0: successfully load
1887 * +1: user aborted the load (by failing to open the database)
1890 SI_LoadSignonData() {
1891 char * passwordRealm
;
1892 nsAutoString buffer
;
1893 PRBool badInput
= PR_FALSE
;
1895 #ifdef APPLE_KEYCHAIN
1896 if (KeychainManagerAvailable()) {
1897 SI_RemoveAllSignonData();
1898 return si_LoadSignonDataFromKeychain();
1902 /* open the signon file */
1903 nsCOMPtr
<nsIFile
> file
;
1904 nsresult rv
= Wallet_ProfileDirectory(getter_AddRefs(file
));
1905 if (NS_FAILED(rv
)) {
1910 rv
= EnsureSingleSignOnProfileObserver();
1911 NS_ASSERTION(NS_SUCCEEDED(rv
), "Failed to register profile change observer");
1913 SI_InitSignonFileName();
1914 file
->AppendNative(nsDependentCString(signonFileName
));
1916 nsCOMPtr
<nsIInputStream
> strm
;
1917 rv
= NS_NewLocalFileInputStream(getter_AddRefs(strm
), file
);
1918 if (NS_FAILED(rv
)) {
1919 si_PartiallyLoaded
= PR_TRUE
;
1923 SI_RemoveAllSignonData();
1925 /* read the format information */
1926 nsAutoString format
;
1927 if (NS_FAILED(si_ReadLine(strm
, format
))) {
1930 if (!format
.EqualsLiteral(HEADER_VERSION
)) {
1931 /* something's wrong */
1935 /* read the reject list */
1936 si_lock_signon_list();
1937 while (NS_SUCCEEDED(si_ReadLine(strm
, buffer
))) {
1938 /* a blank line is perfectly valid here -- corresponds to a local file */
1939 if (!buffer
.IsEmpty() && buffer
.CharAt(0) == '.') {
1940 break; /* end of reject list */
1942 passwordRealm
= ToNewCString(buffer
);
1943 si_PutReject(passwordRealm
, buffer
, PR_FALSE
); /* middle parameter is obsolete */
1944 Recycle (passwordRealm
);
1947 /* read the URL line */
1948 while (NS_SUCCEEDED(si_ReadLine(strm
, buffer
))) {
1949 /* a blank line is perfectly valid here -- corresponds to a local file */
1950 passwordRealm
= ToNewCString(buffer
);
1951 if (!passwordRealm
) {
1952 si_unlock_signon_list();
1956 /* prepare to read the name/value pairs */
1957 badInput
= PR_FALSE
;
1959 nsVoidArray signonData
;
1960 si_SignonDataStruct
* data
;
1961 while(NS_SUCCEEDED(si_ReadLine(strm
, buffer
))) {
1963 /* line starting with . terminates the pairs for this URL entry */
1964 if (buffer
.CharAt(0) == '.') {
1965 break; /* end of URL entry */
1968 /* line just read is the name part */
1970 /* save the name part and determine if it is a password */
1975 if (buffer
.CharAt(0) == '*') {
1976 isPassword
= PR_TRUE
;
1977 buffer
.Mid(name
, 1, buffer
.Length()-1);
1978 ret
= si_ReadLine(strm
, buffer
);
1980 isPassword
= PR_FALSE
;
1982 ret
= si_ReadLine(strm
, buffer
);
1985 /* read in and save the value part */
1986 if(NS_FAILED(ret
)) {
1987 /* error in input file so give up */
1993 data
= new si_SignonDataStruct
;
1995 data
->value
= value
;
1996 data
->isPassword
= isPassword
;
1997 signonData
.AppendElement(data
);
2000 /* store the info for this URL into memory-resident data structure */
2001 PRInt32 count
= signonData
.Count();
2003 si_PutData(passwordRealm
, &signonData
, PR_FALSE
);
2006 /* free up all the allocations done for processing this URL */
2007 Recycle(passwordRealm
);
2008 for (PRInt32 i
=count
-1; i
>=0; i
--) {
2009 data
= static_cast<si_SignonDataStruct
*>(signonData
.ElementAt(i
));
2014 si_unlock_signon_list();
2015 si_PartiallyLoaded
= PR_TRUE
;
2020 * Save signon data to disk file
2021 * The parameter passed in on entry is ignored
2023 * This routine is called only when holding the signon lock!!!
2025 * This routine is called only if signon pref is enabled!!!
2029 si_SaveSignonDataLocked(char * state
, PRBool notify
) {
2030 si_SignonURLStruct
* url
;
2031 si_SignonUserStruct
* user
;
2032 si_SignonDataStruct
* data
;
2035 /* do nothing if signon list has not changed */
2036 if(!si_signon_list_changed
) {
2040 #ifdef APPLE_KEYCHAIN
2041 if (KeychainManagerAvailable()) {
2042 return si_SaveSignonDataInKeychain();
2046 /* do nothing if we are unable to open file that contains signon list */
2047 nsCOMPtr
<nsIFile
> file
;
2048 nsresult rv
= Wallet_ProfileDirectory(getter_AddRefs(file
));
2049 if (NS_FAILED(rv
)) {
2053 file
->AppendNative(nsDependentCString(signonFileName
));
2055 nsCOMPtr
<nsIOutputStream
> fileOutputStream
;
2056 rv
= NS_NewSafeLocalFileOutputStream(getter_AddRefs(fileOutputStream
),
2063 nsCOMPtr
<nsIOutputStream
> strm
;
2064 rv
= NS_NewBufferedOutputStream(getter_AddRefs(strm
), fileOutputStream
, 4096);
2068 /* write out the format revision number */
2070 wallet_PutLine(strm
, HEADER_VERSION
);
2072 /* format for next part of file shall be:
2073 * passwordRealm -- first url/username on reject list
2075 * passwordRealm -- second url/username on reject list
2081 /* write out reject list */
2082 if (si_reject_list
) {
2083 PRInt32 rejectCount
= LIST_COUNT(si_reject_list
);
2084 for (PRInt32 i
=0; i
<rejectCount
; i
++) {
2085 reject
= static_cast<si_Reject
*>(si_reject_list
->ElementAt(i
));
2086 wallet_PutLine(strm
, reject
->passwordRealm
);
2089 wallet_PutLine(strm
, ".");
2091 /* format for cached logins shall be:
2092 * url LINEBREAK {name LINEBREAK value LINEBREAK}* . LINEBREAK
2093 * if type is password, name is preceded by an asterisk (*)
2096 /* write out each URL node */
2097 if((si_signon_list
)) {
2098 PRInt32 urlCount
= LIST_COUNT(si_signon_list
);
2099 for (PRInt32 i2
=0; i2
<urlCount
; i2
++) {
2100 url
= static_cast<si_SignonURLStruct
*>(si_signon_list
->ElementAt(i2
));
2102 /* write out each user node of the URL node */
2103 PRInt32 userCount
= url
->signonUser_list
.Count();
2104 for (PRInt32 i3
=0; i3
<userCount
; i3
++) {
2105 user
= static_cast<si_SignonUserStruct
*>(url
->signonUser_list
.ElementAt(i3
));
2106 wallet_PutLine(strm
, url
->passwordRealm
);
2108 /* write out each data node of the user node */
2109 PRInt32 dataCount
= user
->signonData_list
.Count();
2110 for (PRInt32 i4
=0; i4
<dataCount
; i4
++) {
2111 data
= static_cast<si_SignonDataStruct
*>(user
->signonData_list
.ElementAt(i4
));
2112 if (data
->isPassword
) {
2113 static const char asterisk
= '*';
2115 strm
->Write(&asterisk
, 1, &dummy
);
2117 wallet_PutLine(strm
, NS_ConvertUTF16toUTF8(data
->name
).get());
2118 wallet_PutLine(strm
, NS_ConvertUTF16toUTF8(data
->value
).get());
2120 wallet_PutLine(strm
, ".");
2124 si_signon_list_changed
= PR_FALSE
;
2126 // All went ok. Maybe except for problems in Write(), but the stream detects
2128 nsCOMPtr
<nsISafeOutputStream
> safeStream
= do_QueryInterface(strm
);
2129 NS_ASSERTION(safeStream
, "expected a safe output stream!");
2131 rv
= safeStream
->Finish();
2132 if (NS_FAILED(rv
)) {
2133 NS_WARNING("failed to save wallet file! possible dataloss");
2138 fileOutputStream
= nsnull
;
2141 /* Notify signon manager dialog to update its display */
2143 nsCOMPtr
<nsIObserverService
> os(do_GetService("@mozilla.org/observer-service;1"));
2145 os
->NotifyObservers(nsnull
, "signonChanged", NS_ConvertASCIItoUTF16(state
).get());
2153 /***************************
2154 * Processing Signon Forms *
2155 ***************************/
2158 si_ExtractRealm(nsIURI
*uri
, nsCString
&realm
)
2160 nsCAutoString hostPort
;
2162 /* Security check: if URI is of a scheme that doesn't support hostnames,
2163 * we have no host to get the signon data from, so we must not attempt to
2164 * build a valid realm from the URI (bug 159484) */
2165 nsresult rv
= uri
->GetHostPort(hostPort
);
2166 if (NS_FAILED(rv
) || hostPort
.IsEmpty())
2169 nsCAutoString scheme
;
2170 rv
= uri
->GetScheme(scheme
);
2171 if (NS_FAILED(rv
) || scheme
.IsEmpty())
2174 realm
= scheme
+ NS_LITERAL_CSTRING("://") + hostPort
;
2178 /* Ask user if it is ok to save the signon data */
2180 si_OkToSave(const char *passwordRealm
, const char *legacyRealm
,
2181 const nsString
& userName
, nsIDOMWindowInternal
* window
) {
2183 /* if url/user already exists, then it is safe to save it again */
2184 if (si_CheckForUser(passwordRealm
, userName
)) {
2187 if (legacyRealm
&& si_CheckForUser(legacyRealm
, userName
)) {
2191 #ifdef WALLET_PASSWORDMANAGER_DEFAULT_IS_OFF
2192 if (!si_RememberSignons
&& !si_GetNotificationPref()) {
2193 PRUnichar
* notification
= Wallet_Localize("PasswordNotification");
2194 si_SetNotificationPref(PR_TRUE
);
2195 if (!si_ConfirmYN(notification
, window
)) {
2196 Recycle(notification
);
2197 SI_SetBoolPref(pref_rememberSignons
, PR_FALSE
);
2200 Recycle(notification
);
2201 SI_SetBoolPref(pref_rememberSignons
, PR_TRUE
);
2205 if (si_CheckForReject(passwordRealm
, userName
)) {
2208 if (legacyRealm
&& si_CheckForReject(legacyRealm
, userName
)) {
2212 PRUnichar
* message
;
2213 if (SI_GetBoolPref(pref_Crypto
, PR_FALSE
)) {
2214 message
= Wallet_Localize("WantToSavePasswordEncrypted?");
2216 message
= Wallet_Localize("WantToSavePasswordObscured?");
2219 PRInt32 button
= si_3ButtonConfirm(message
, window
);
2220 if (button
== NEVER_BUTTON
) {
2221 si_PutReject(passwordRealm
, userName
, PR_TRUE
);
2224 return (button
== YES_BUTTON
);
2228 * Check for a signon submission and remember the data if so
2231 si_RememberSignonData
2232 (nsIPrompt
* dialog
, const char* passwordRealm
, const char* legacyRealm
,
2233 nsVoidArray
* signonData
, nsIDOMWindowInternal
* window
)
2235 int passwordCount
= 0;
2237 si_SignonDataStruct
* data
= nsnull
; // initialization only to avoid warning message
2238 si_SignonDataStruct
* data0
;
2239 si_SignonDataStruct
* data1
;
2240 si_SignonDataStruct
* data2
;
2242 /* do nothing if signon preference is not enabled */
2243 if (!si_GetSignonRememberingPref()){
2247 /* determine how many passwords are in the form and where they are */
2248 for (PRInt32 i
=0; i
<signonData
->Count(); i
++) {
2249 data
= static_cast<si_SignonDataStruct
*>(signonData
->ElementAt(i
));
2250 if (data
->isPassword
) {
2251 if (passwordCount
< 3 ) {
2252 pswd
[passwordCount
] = i
;
2258 /* process the form according to how many passwords it has */
2259 if (passwordCount
== 1) {
2260 /* one-password form is a log-in so remember it */
2262 /* obtain the index of the first input field (that is the username) */
2264 for (j
=0; j
<signonData
->Count(); j
++) {
2265 data
= static_cast<si_SignonDataStruct
*>(signonData
->ElementAt(j
));
2266 if (!data
->isPassword
) {
2271 if (j
<signonData
->Count()) {
2272 if (si_OkToSave(passwordRealm
, legacyRealm
, data
->value
, window
)) {
2273 // remove legacy password entry if found
2274 if (legacyRealm
&& si_CheckForUser(legacyRealm
, data
->value
)) {
2275 si_RemoveUser(legacyRealm
, data
->value
, PR_TRUE
, PR_FALSE
, PR_TRUE
);
2277 Wallet_GiveCaveat(window
, nsnull
);
2278 for (j
=0; j
<signonData
->Count(); j
++) {
2279 data2
= static_cast<si_SignonDataStruct
*>(signonData
->ElementAt(j
));
2280 nsAutoString
value(data2
->value
);
2281 if (NS_FAILED(Wallet_Encrypt(value
, data2
->value
))) {
2285 si_PutData(passwordRealm
, signonData
, PR_TRUE
);
2288 } else if (passwordCount
== 2) {
2289 /* two-password form is a registration */
2291 } else if (passwordCount
== 3) {
2292 /* three-password form is a change-of-password request */
2294 si_SignonUserStruct
* user
;
2296 /* make sure all passwords are non-null and 2nd and 3rd are identical */
2297 data0
= static_cast<si_SignonDataStruct
*>(signonData
->ElementAt(pswd
[0]));
2298 data1
= static_cast<si_SignonDataStruct
*>(signonData
->ElementAt(pswd
[1]));
2299 data2
= static_cast<si_SignonDataStruct
*>(signonData
->ElementAt(pswd
[2]));
2300 if (data0
->value
.IsEmpty() || data1
->value
.IsEmpty() ||
2301 data2
->value
.IsEmpty() || data1
->value
!= data2
->value
) {
2305 /* ask user if this is a password change */
2306 si_lock_signon_list();
2307 user
= si_GetURLAndUserForChangeForm(dialog
, data0
->value
);
2309 /* return if user said no */
2311 si_unlock_signon_list();
2315 /* get to password being saved */
2316 PRInt32 dataCount
= user
->signonData_list
.Count();
2317 for (PRInt32 k
=0; k
<dataCount
; k
++) {
2318 data
= static_cast<si_SignonDataStruct
*>(user
->signonData_list
.ElementAt(k
));
2319 if (data
->isPassword
) {
2325 * if second password is "********" then generate a random
2326 * password for it and use same random value for third password
2327 * as well (Note: this all works because we already know that
2328 * second and third passwords are identical so third password
2329 * must also be "********". Furthermore si_Randomize() will
2330 * create a random password of exactly eight characters -- the
2331 * same length as "********".)
2334 /* commenting out because I don't see how such randomizing could ever have worked. */
2335 // si_Randomize(data1->value);
2336 // data2->value = data1->value;
2338 if (NS_SUCCEEDED(Wallet_Encrypt(data1
->value
, data
->value
))) {
2339 user
->time
= SecondsFromPRTime(PR_Now());
2340 si_signon_list_changed
= PR_TRUE
;
2341 si_SaveSignonDataLocked("signons", PR_TRUE
);
2342 si_unlock_signon_list();
2350 if(signon_lock_monitor
) {
2351 PR_DestroyMonitor(signon_lock_monitor
);
2352 signon_lock_monitor
= nsnull
;
2357 SINGSIGN_RememberSignonData
2358 (nsIPrompt
* dialog
, nsIURI
* passwordRealm
, nsVoidArray
* signonData
,
2359 nsIDOMWindowInternal
* window
)
2364 nsCAutoString realm
, legacyRealm
;
2365 if (!si_ExtractRealm(passwordRealm
, realm
))
2368 if (NS_FAILED(passwordRealm
->GetHost(legacyRealm
)))
2371 if (!realm
.IsEmpty()) {
2372 si_RememberSignonData(dialog
, realm
.get(), legacyRealm
.get(), signonData
, window
);
2377 si_RestoreSignonData(nsIPrompt
* dialog
,
2378 const char* passwordRealm
, const char* legacyRealm
,
2379 const PRUnichar
* name
, PRUnichar
** value
,
2380 PRUint32 formNumber
, PRUint32 elementNumber
) {
2381 si_SignonUserStruct
* user
;
2382 si_SignonDataStruct
* data
;
2383 nsAutoString correctedName
;
2385 /* do nothing if signon preference is not enabled */
2386 if (!si_GetSignonRememberingPref()){
2390 si_lock_signon_list();
2391 if (elementNumber
== 0) {
2392 si_LastFormForWhichUserHasBeenSelected
= -1;
2395 /* Correct the field name to avoid mistaking for fields in browser-generated form
2397 * Note that data saved for browser-generated logins (e.g. http authentication)
2398 * use artificial field names starting with * \= (see USERNAMEFIELD and PASSWORDFIELD.
2399 * To avoid mistakes whereby saved logins for http authentication is then prefilled
2400 * into a field on the html form at the same URL, we will prevent html field names
2401 * from starting with \=. We do that by doubling up a backslash if it appears in the
2402 * first character position
2404 if (*name
== '\\') {
2405 correctedName
= NS_LITERAL_STRING("\\") + nsDependentString(name
);
2407 correctedName
= name
;
2410 /* determine if name has been saved (avoids unlocking the database if not) */
2411 PRBool nameFound
= PR_FALSE
;
2412 user
= si_GetUser(dialog
, passwordRealm
, legacyRealm
, PR_FALSE
, correctedName
, formNumber
);
2414 PRInt32 dataCount
= user
->signonData_list
.Count();
2415 for (PRInt32 i
=0; i
<dataCount
; i
++) {
2416 data
= static_cast<si_SignonDataStruct
*>(user
->signonData_list
.ElementAt(i
));
2417 LOG((" got [name=%s value=%s]\n",
2418 NS_LossyConvertUTF16toASCII(data
->name
).get(),
2419 NS_LossyConvertUTF16toASCII(data
->value
).get()));
2420 if(!correctedName
.IsEmpty() && (data
->name
== correctedName
)) {
2421 nameFound
= PR_TRUE
;
2426 si_unlock_signon_list();
2432 * determine if it is a change-of-password field
2433 * the heuristic that we will use is that if this is the first
2434 * item on the form and it is a password, this is probably a
2435 * change-of-password form
2437 /* see if this is first item in form and is a password */
2438 /* get first saved user just so we can see the name of the first item on the form */
2439 user
= si_GetUser(passwordRealm
, PR_TRUE
, NULL
, formNumber
); /* this is the first saved user */
2441 data
= static_cast<si_SignonDataStruct
*>
2442 (user
->signonData_list
.ElementAt(0)); /* 1st item on form */
2443 if(data
->isPassword
&& !correctedName
.IsEmpty() && (data
->name
== correctedName
)) {
2444 /* current item is first item on form and is a password */
2445 user
= (passwordRealm
, MK_SIGNON_PASSWORDS_FETCH
);
2447 /* user has confirmed it's a change-of-password form */
2448 PRInt32 dataCount
= user
->signonData_list
.Count();
2449 for (PRInt32 i
=1; i
<dataCount
; i
++) {
2450 data
= static_cast<si_SignonDataStruct
*>(user
->signonData_list
.ElementAt(i
));
2451 if (data
->isPassword
) {
2452 nsAutoString password
;
2453 if (NS_SUCCEEDED(Wallet_Decrypt(data
->value
, password
))) {
2454 *value
= ToNewUnicode(password
);
2456 si_unlock_signon_list();
2465 /* restore the data from previous time this URL was visited */
2467 user
= si_GetUser(dialog
, passwordRealm
, legacyRealm
, PR_FALSE
, correctedName
, formNumber
);
2469 PRInt32 dataCount
= user
->signonData_list
.Count();
2470 for (PRInt32 i
=0; i
<dataCount
; i
++) {
2471 data
= static_cast<si_SignonDataStruct
*>(user
->signonData_list
.ElementAt(i
));
2472 LOG((" got [name=%s value=%s]\n",
2473 NS_LossyConvertUTF16toASCII(data
->name
).get(),
2474 NS_LossyConvertUTF16toASCII(data
->value
).get()));
2475 if(!correctedName
.IsEmpty() && (data
->name
== correctedName
)) {
2476 nsAutoString password
;
2477 if (NS_SUCCEEDED(Wallet_Decrypt(data
->value
, password
))) {
2478 *value
= ToNewUnicode(password
);
2480 si_unlock_signon_list();
2485 si_unlock_signon_list();
2489 SINGSIGN_RestoreSignonData(nsIPrompt
* dialog
, nsIURI
* passwordRealm
, const PRUnichar
* name
, PRUnichar
** value
, PRUint32 formNumber
, PRUint32 elementNumber
) {
2490 LOG(("enter SINGSIGN_RestoreSignonData\n"));
2495 nsCAutoString realm
;
2496 if (!si_ExtractRealm(passwordRealm
, realm
))
2499 nsCAutoString legacyRealm
;
2500 if (NS_FAILED(passwordRealm
->GetHost(legacyRealm
)))
2503 si_RestoreSignonData(dialog
, realm
.get(), legacyRealm
.get(), name
, value
, formNumber
, elementNumber
);
2505 LOG(("exit SINGSIGN_RestoreSignonData [value=%s]\n",
2506 *value
? NS_LossyConvertUTF16toASCII(*value
).get() : "(null)"));
2510 * Remember signon data from a browser-generated password dialog
2513 si_RememberSignonDataFromBrowser(const char* passwordRealm
, const nsString
& username
, const nsString
& password
) {
2514 /* do nothing if signon preference is not enabled */
2515 if (!si_GetSignonRememberingPref()){
2519 nsVoidArray signonData
;
2520 si_SignonDataStruct data1
;
2521 data1
.name
.AssignLiteral(USERNAMEFIELD
);
2522 if (NS_FAILED(Wallet_Encrypt(username
, data1
.value
))) {
2525 data1
.isPassword
= PR_FALSE
;
2526 signonData
.AppendElement(&data1
);
2527 si_SignonDataStruct data2
;
2528 data2
.name
.AssignLiteral(PASSWORDFIELD
);
2529 if (NS_FAILED(Wallet_Encrypt(password
, data2
.value
))) {
2532 data2
.isPassword
= PR_TRUE
;
2533 signonData
.AppendElement(&data2
);
2535 /* Save the signon data */
2536 si_PutData(passwordRealm
, &signonData
, PR_TRUE
);
2540 * Check for remembered data from a previous browser-generated password dialog
2544 si_RestoreOldSignonDataFromBrowser
2545 (nsIPrompt
* dialog
, const char* passwordRealm
, PRBool pickFirstUser
, nsString
& username
, nsString
& password
) {
2546 si_SignonUserStruct
* user
;
2547 si_SignonDataStruct
* data
;
2549 /* get the data from previous time this URL was visited */
2550 si_lock_signon_list();
2551 if (!username
.IsEmpty()) {
2552 user
= si_GetSpecificUser(passwordRealm
, username
, NS_ConvertASCIItoUTF16(USERNAMEFIELD
));
2554 si_LastFormForWhichUserHasBeenSelected
= -1;
2555 user
= si_GetUser(dialog
, passwordRealm
, nsnull
, pickFirstUser
, NS_ConvertASCIItoUTF16(USERNAMEFIELD
), 0);
2558 /* leave original username and password from caller unchanged */
2560 /* *password = 0; */
2561 si_unlock_signon_list();
2565 /* restore the data from previous time this URL was visited */
2566 PRInt32 dataCount
= user
->signonData_list
.Count();
2567 for (PRInt32 i
=0; i
<dataCount
; i
++) {
2568 data
= static_cast<si_SignonDataStruct
*>(user
->signonData_list
.ElementAt(i
));
2569 nsAutoString decrypted
;
2570 if (NS_SUCCEEDED(Wallet_Decrypt(data
->value
, decrypted
))) {
2571 if(data
->name
.EqualsLiteral(USERNAMEFIELD
)) {
2572 username
= decrypted
;
2573 } else if(data
->name
.EqualsLiteral(PASSWORDFIELD
)) {
2574 password
= decrypted
;
2578 si_unlock_signon_list();
2583 SINGSIGN_StorePassword(const char *passwordRealm
, const PRUnichar
*user
, const PRUnichar
*password
)
2585 // Wallet_GiveCaveat(nsnull, dialog); ??? what value to use for dialog?
2586 si_RememberSignonDataFromBrowser(passwordRealm
, nsDependentString(user
), nsDependentString(password
));
2590 enum DialogType
{promptUsernameAndPassword
, promptPassword
, prompt
};
2593 si_DoDialogIfPrefIsOff(
2594 const PRUnichar
*dialogTitle
,
2595 const PRUnichar
*text
,
2598 const PRUnichar
*defaultText
,
2599 PRUnichar
**resultText
,
2600 const char *passwordRealm
,
2603 PRUint32 savePassword
,
2606 nsresult res
= NS_ERROR_FAILURE
;
2607 const PRUnichar
* prompt_string
= dialogTitle
;
2608 if (dialogTitle
== nsnull
|| !dialogTitle
[0]) {
2609 prompt_string
= Wallet_Localize("PromptForData");
2612 nsAutoString
data(defaultText
);
2614 case promptUsernameAndPassword
:
2615 res
= dialog
->PromptUsernameAndPassword(prompt_string
,
2622 case promptPassword
:
2623 res
= dialog
->PromptPassword(prompt_string
,
2630 *resultText
= ToNewUnicode(data
);
2631 res
= dialog
->Prompt(prompt_string
,
2639 NS_ERROR("Undefined DialogType in si_DoDialogIfPrefIsOff");
2643 if (dialogTitle
!= prompt_string
) {
2644 Recycle(const_cast<PRUnichar
*>(prompt_string
));
2649 /* The following comments apply to the three prompt routines that follow
2651 * If a password was successfully obtain (either from the single-signon
2652 * database or from a dialog with the user), we return NS_OK for the
2653 * function value and PR_TRUE for the boolean argument "pressedOK".
2655 * If the user presses cancel from the dialog, we return NS_OK for the
2656 * function value and PR_FALSE for the boolean argument "pressedOK".
2658 * If a password is not collected for any other reason, we return the
2659 * failure code for the function value and the boolean argument
2660 * "pressedOK" is undefined.
2664 SINGSIGN_PromptUsernameAndPassword
2665 (const PRUnichar
*dialogTitle
, const PRUnichar
*text
, PRUnichar
**user
, PRUnichar
**pwd
,
2666 const char *passwordRealm
, nsIPrompt
* dialog
, PRBool
*pressedOK
, PRUint32 savePassword
) {
2670 /* do only the dialog if signon preference is not enabled */
2671 if (!si_GetSignonRememberingPref()){
2672 return si_DoDialogIfPrefIsOff(dialogTitle
,
2682 promptUsernameAndPassword
);
2685 /* prefill with previous username/password if any */
2686 nsAutoString username
, password
;
2687 si_RestoreOldSignonDataFromBrowser(dialog
, passwordRealm
, PR_FALSE
, username
, password
);
2689 /* get new username/password from user */
2690 if (!(*user
= ToNewUnicode(username
))) {
2691 return NS_ERROR_OUT_OF_MEMORY
;
2693 if (!(*pwd
= ToNewUnicode(password
))) {
2695 return NS_ERROR_OUT_OF_MEMORY
;
2698 PRBool checked
= (**user
!= 0);
2699 PRBool remembered
= checked
;
2700 res
= si_CheckGetUsernamePassword(user
, pwd
, dialogTitle
, text
, dialog
, savePassword
, &checked
);
2701 if (NS_FAILED(res
)) {
2702 /* user pressed Cancel */
2705 *pressedOK
= PR_FALSE
;
2709 Wallet_GiveCaveat(nsnull
, dialog
);
2710 si_RememberSignonDataFromBrowser (passwordRealm
, nsDependentString(*user
), nsDependentString(*pwd
));
2711 } else if (remembered
) {
2712 /* a login was remembered but user unchecked the box; we forget the remembered login */
2713 si_RemoveUser(passwordRealm
, username
, PR_TRUE
, PR_FALSE
, PR_TRUE
);
2716 /* cleanup and return */
2717 *pressedOK
= PR_TRUE
;
2722 SINGSIGN_PromptPassword
2723 (const PRUnichar
*dialogTitle
, const PRUnichar
*text
, PRUnichar
**pwd
, const char *passwordRealm
,
2724 nsIPrompt
* dialog
, PRBool
*pressedOK
, PRUint32 savePassword
)
2728 nsAutoString password
, username
;
2730 /* do only the dialog if signon preference is not enabled */
2731 if (!si_GetSignonRememberingPref()){
2732 return si_DoDialogIfPrefIsOff(dialogTitle
,
2745 /* get previous password used with this username, pick first user if no username found */
2746 si_RestoreOldSignonDataFromBrowser(dialog
, passwordRealm
, username
.IsEmpty(), username
, password
);
2748 /* return if a password was found */
2749 if (!password
.IsEmpty()) {
2750 *pwd
= ToNewUnicode(password
);
2751 *pressedOK
= PR_TRUE
;
2755 /* no password found, get new password from user */
2756 PRBool checked
= PR_FALSE
;
2757 res
= si_CheckGetPassword(pwd
, dialogTitle
, text
, dialog
, savePassword
, &checked
);
2758 if (NS_FAILED(res
)) {
2759 /* user pressed Cancel */
2761 *pressedOK
= PR_FALSE
;
2765 Wallet_GiveCaveat(nsnull
, dialog
);
2766 si_RememberSignonDataFromBrowser(passwordRealm
, username
, nsDependentString(*pwd
));
2769 /* cleanup and return */
2770 *pressedOK
= PR_TRUE
;
2776 (const PRUnichar
*dialogTitle
, const PRUnichar
*text
, const PRUnichar
*defaultText
, PRUnichar
**resultText
,
2777 const char *passwordRealm
, nsIPrompt
* dialog
, PRBool
*pressedOK
, PRUint32 savePassword
)
2780 nsAutoString data
, emptyUsername
;
2782 /* do only the dialog if signon preference is not enabled */
2783 if (!si_GetSignonRememberingPref()){
2784 return si_DoDialogIfPrefIsOff(dialogTitle
,
2797 /* get previous data used with this hostname */
2798 si_RestoreOldSignonDataFromBrowser(dialog
, passwordRealm
, PR_TRUE
, emptyUsername
, data
);
2800 /* return if data was found */
2801 if (!data
.IsEmpty()) {
2802 *resultText
= ToNewUnicode(data
);
2803 *pressedOK
= PR_TRUE
;
2807 /* no data found, get new data from user */
2809 *resultText
= ToNewUnicode(data
);
2810 PRBool checked
= PR_FALSE
;
2811 res
= si_CheckGetData(resultText
, dialogTitle
, text
, dialog
, savePassword
, &checked
);
2812 if (NS_FAILED(res
)) {
2813 /* user pressed Cancel */
2814 PR_FREEIF(*resultText
);
2815 *pressedOK
= PR_FALSE
;
2819 Wallet_GiveCaveat(nsnull
, dialog
);
2820 si_RememberSignonDataFromBrowser(passwordRealm
, emptyUsername
, nsDependentString(*resultText
));
2823 /* cleanup and return */
2824 *pressedOK
= PR_TRUE
;
2830 (nsIPromptService2
* aService
, nsIDOMWindow
* aParent
, nsIChannel
* aChannel
,
2831 PRUint32 aLevel
, nsIAuthInformation
* aAuthInfo
, PRBool
* retval
) {
2834 NS_GetAuthKey(aChannel
, aAuthInfo
, key
);
2836 /* do only the dialog if signon preference is not enabled */
2837 if (!si_GetSignonRememberingPref()){
2838 return aService
->PromptAuth(aParent
, aChannel
, aLevel
,
2839 aAuthInfo
, nsnull
, nsnull
,
2843 /* prefill with previous username/password if any */
2844 /* this needs a dialog to choose between multiple usernames */
2845 nsCOMPtr
<nsIPrompt
> prompt
;
2847 nsCOMPtr
<nsIWindowWatcher
> wwatcher
=
2848 do_GetService(NS_WINDOWWATCHER_CONTRACTID
, &rv
);
2849 wwatcher
->GetNewPrompter(aParent
, getter_AddRefs(prompt
));
2851 // If we have a domain, insert a "domain\" in front of the username
2853 nsAutoString domain
, username
, password
;
2854 aAuthInfo
->GetDomain(domain
);
2855 aAuthInfo
->GetUsername(username
);
2856 if (!domain
.IsEmpty()) {
2857 domain
.Append(PRUnichar('\\'));
2858 username
.Insert(domain
, 0);
2860 // Offer saving the data iff we had previously stored information
2861 PRBool checked
= si_RestoreOldSignonDataFromBrowser(prompt
,
2868 aAuthInfo
->GetFlags(&flags
);
2871 NS_SetAuthInfo(aAuthInfo
, username
, password
);
2872 // If we were only asked for a password, return immediately
2873 // (to match SINGSIGN_PromptPassword)
2874 if (flags
& nsIAuthInformation::ONLY_PASSWORD
)
2878 PRBool remembered
= checked
;
2879 rv
= si_CheckPromptAuth(aService
, aParent
, aChannel
, aLevel
,
2880 aAuthInfo
, &checked
);
2881 if (NS_FAILED(rv
)) {
2882 /* user pressed Cancel */
2887 /* Get the newly entered data back */
2888 aAuthInfo
->GetDomain(domain
);
2889 aAuthInfo
->GetUsername(username
);
2890 aAuthInfo
->GetPassword(password
);
2891 if (!domain
.IsEmpty()) {
2892 domain
.Append(PRUnichar('\\'));
2893 username
.Insert(domain
, 0);
2897 Wallet_GiveCaveat(nsnull
, prompt
);
2898 si_RememberSignonDataFromBrowser (key
.get(), username
, password
);
2899 } else if (remembered
) {
2900 /* a login was remembered but user unchecked the box; we forget the remembered login */
2901 si_RemoveUser(key
.get(), username
, PR_TRUE
, PR_FALSE
, PR_TRUE
);
2904 /* cleanup and return */
2914 /* return PR_TRUE if "number" is in sequence of comma-separated numbers */
2916 SI_InSequence(const nsString
& sequence
, PRInt32 number
)
2918 nsAutoString
tail( sequence
);
2919 nsAutoString head
, temp
;
2923 /* get next item in list */
2924 separator
= tail
.FindChar(',');
2925 if (-1 == separator
) {
2928 tail
.Left(head
, separator
);
2929 tail
.Mid(temp
, separator
+1, tail
.Length() - (separator
+1));
2932 /* test item to see if it equals our number */
2934 PRInt32 numberInList
= head
.ToInteger(&error
);
2935 if (!error
&& numberInList
== number
) {
2944 SI_FindValueInArgs(const nsAString
& results
, const nsAString
& name
, nsAString
& value
)
2946 /* note: name must start and end with a vertical bar */
2947 nsReadingIterator
<PRUnichar
> start
, end
, barPos
;
2948 results
.BeginReading(start
);
2949 results
.EndReading(end
);
2951 FindInReadable(name
, start
, end
);
2955 start
.advance(name
.Length()); /* get past the |name| part */
2957 results
.EndReading(end
);
2958 FindCharInReadable(PRUnichar('|'), barPos
, end
);
2959 value
= Substring(start
, barPos
);
2963 SINGSIGN_ReencryptAll()
2965 /* force loading of the signons file */
2966 si_RegisterSignonPrefCallbacks();
2968 nsAutoString buffer
;
2969 si_SignonURLStruct
*url
;
2970 si_SignonUserStruct
* user
;
2971 si_SignonDataStruct
* data
= nsnull
;
2973 si_lock_signon_list();
2974 PRInt32 urlCount
= LIST_COUNT(si_signon_list
);
2975 for (PRInt32 i
=0; i
<urlCount
; i
++) {
2976 url
= static_cast<si_SignonURLStruct
*>(si_signon_list
->ElementAt(i
));
2977 PRInt32 userCount
= url
->signonUser_list
.Count();
2978 for (PRInt32 j
=0; j
<userCount
; j
++) {
2979 user
= static_cast<si_SignonUserStruct
*>(url
->signonUser_list
.ElementAt(j
));
2981 PRInt32 dataCount
= user
->signonData_list
.Count();
2982 for (PRInt32 k
=0; k
<dataCount
; k
++) {
2983 data
= static_cast<si_SignonDataStruct
*>
2984 (user
->signonData_list
.ElementAt(k
));
2985 nsAutoString userName
;
2986 if (NS_FAILED(Wallet_Decrypt(data
->value
, userName
))) {
2987 //Don't try to re-encrypt. Just go to the next one.
2990 if (NS_FAILED(Wallet_Encrypt(userName
, data
->value
))) {
2996 si_signon_list_changed
= PR_TRUE
;
2997 si_SaveSignonDataLocked("signons", PR_TRUE
);
2998 si_unlock_signon_list();
3003 SINGSIGN_HaveData(nsIPrompt
* dialog
, const char *passwordRealm
, const PRUnichar
*userName
, PRBool
*retval
)
3005 nsAutoString data
, usernameForLookup
;
3009 if (!si_GetSignonRememberingPref()) {
3013 /* get previous data used with this username, pick first user if no username found */
3014 si_RestoreOldSignonDataFromBrowser(dialog
, passwordRealm
, usernameForLookup
.IsEmpty(), usernameForLookup
, data
);
3016 if (!data
.IsEmpty()) {
3024 SINGSIGN_HostCount() {
3025 /* force loading of the signons file */
3026 si_RegisterSignonPrefCallbacks();
3028 if (!si_signon_list
) {
3031 return si_signon_list
->Count();
3035 SINGSIGN_UserCount(PRInt32 host
) {
3036 if (!si_signon_list
) {
3040 si_SignonURLStruct
*hostStruct
;
3041 hostStruct
= static_cast<si_SignonURLStruct
*>(si_signon_list
->ElementAt(host
));
3042 return hostStruct
->signonUser_list
.Count();
3047 (PRInt32 hostNumber
, PRInt32 userNumber
, PRBool decrypt
, char **host
,
3048 PRUnichar
** user
, PRUnichar
** pswd
) {
3050 if (gSelectUserDialogCount
>0 && hostNumber
==0 && userNumber
==0) {
3051 // starting to enumerate over all saved logins
3052 // notify recipients if login list is in use by SelectUserDialog
3053 nsCOMPtr
<nsIObserverService
> os(do_GetService("@mozilla.org/observer-service;1"));
3055 os
->NotifyObservers(nsnull
, "signonSelectUser", NS_LITERAL_STRING("inUse").get());
3059 if (hostNumber
> SINGSIGN_HostCount() || userNumber
> SINGSIGN_UserCount(hostNumber
)) {
3060 return NS_ERROR_FAILURE
;
3062 si_SignonURLStruct
*hostStruct
;
3063 si_SignonUserStruct
* userStruct
;
3064 si_SignonDataStruct
* data
= nsnull
;
3066 hostStruct
= static_cast<si_SignonURLStruct
*>(si_signon_list
->ElementAt(hostNumber
));
3067 NS_ASSERTION(hostStruct
, "corrupt singlesignon list");
3068 *host
= (char *) nsMemory::Clone
3069 (hostStruct
->passwordRealm
, strlen(hostStruct
->passwordRealm
) + 1);
3070 NS_ENSURE_ARG_POINTER(host
);
3072 static_cast<si_SignonUserStruct
*>(hostStruct
->signonUser_list
.ElementAt(userNumber
));
3074 /* first non-password data item for user is the username */
3075 PRInt32 dataCount
= userStruct
->signonData_list
.Count();
3077 for (k
=0; k
<dataCount
; k
++) {
3078 data
= static_cast<si_SignonDataStruct
*>(userStruct
->signonData_list
.ElementAt(k
));
3079 if (!(data
->isPassword
)) {
3085 nsAutoString userName
;
3087 rv
= Wallet_Decrypt(data
->value
, userName
);
3088 if (NS_FAILED(rv
)) {
3089 /* don't display saved signons if user couldn't unlock the database */
3093 userName
= data
->value
;
3095 if (!(*user
= ToNewUnicode(userName
))) {
3096 return NS_ERROR_OUT_OF_MEMORY
;
3099 /* first password data item for user is the password */
3100 for (k
=0; k
<dataCount
; k
++) {
3101 data
= static_cast<si_SignonDataStruct
*>(userStruct
->signonData_list
.ElementAt(k
));
3102 if ((data
->isPassword
)) {
3107 nsAutoString passWord
;
3109 rv
= Wallet_Decrypt(data
->value
, passWord
);
3110 if (NS_FAILED(rv
)) {
3111 /* don't display saved signons if user couldn't unlock the database */
3116 passWord
= data
->value
;
3118 if (!(*pswd
= ToNewUnicode(passWord
))) {
3120 return NS_ERROR_OUT_OF_MEMORY
;
3126 SINGSIGN_RejectCount() {
3127 if (!si_reject_list
) {
3130 return si_reject_list
->Count();
3134 SINGSIGN_RejectEnumerate
3135 (PRInt32 rejectNumber
, char **host
) {
3138 reject
= static_cast<si_Reject
*>(si_reject_list
->ElementAt(rejectNumber
));
3139 NS_ASSERTION(reject
, "corrupt reject list");
3141 *host
= (char *) nsMemory::Clone
3142 (reject
->passwordRealm
, strlen(reject
->passwordRealm
) + 1);
3143 NS_ENSURE_ARG_POINTER(host
);
3147 #ifdef APPLE_KEYCHAIN
3148 /************************************
3149 * Apple Keychain Specific Routines *
3150 ************************************/
3154 * The Keychain callback. This routine will be called whenever a lock,
3155 * delete, or update event occurs in the Keychain. The only action taken
3156 * is to make the signon list invalid, so it will be read in again the
3157 * next time it is accessed.
3159 OSStatus PR_CALLBACK
3160 si_KeychainCallback( KCEvent keychainEvent
, KCCallbackInfo
*info
, void *userContext
) {
3161 PRBool
*listInvalid
= (PRBool
*)userContext
;
3162 *listInvalid
= PR_TRUE
;
3167 * Get the signon data from the keychain
3169 * This routine is called only if signon pref is enabled!!!
3172 si_LoadSignonDataFromKeychain() {
3173 char * passwordRealm
;
3174 si_FormSubmitData submit
;
3175 nsAutoString name_array
[MAX_ARRAY_SIZE
];
3176 nsAutoString value_array
[MAX_ARRAY_SIZE
];
3177 uint8 type_array
[MAX_ARRAY_SIZE
];
3178 char buffer
[BUFFER_SIZE
];
3179 PRBool badInput
= PR_FALSE
;
3182 KCAttributeList attrList
;
3183 KCAttribute attr
[2];
3184 KCItemClass itemClass
= kInternetPasswordKCItemClass
;
3185 KCProtocolType protocol
= kNetscapeProtocolType
;
3186 OSStatus status
= noErr
;
3187 KCSearchRef searchRef
= NULL
;
3188 /* initialize the submit structure */
3189 submit
.name_array
= name_array
;
3190 submit
.value_array
= value_array
;
3191 submit
.type_array
= (PRUnichar
*)type_array
;
3193 /* set up the attribute list */
3195 attrList
.attr
= attr
;
3196 attr
[0].tag
= kClassKCItemAttr
;
3197 attr
[0].data
= &itemClass
;
3198 attr
[0].length
= sizeof(itemClass
);
3200 attr
[1].tag
= kProtocolKCItemAttr
;
3201 attr
[1].data
= &protocol
;
3202 attr
[1].length
= sizeof(protocol
);
3204 status
= KCFindFirstItem( &attrList
, &searchRef
, &itemRef
);
3207 if (status
== noErr
) {
3208 /* if we found a Netscape item, let's assume notice has been given */
3209 si_SetNotificationPref(PR_TRUE
);
3211 si_SetNotificationPref(PR_FALSE
);
3215 si_lock_signon_list();
3216 while(status
== noErr
) {
3221 PRBool reject
= PR_FALSE
;
3222 submit
.value_cnt
= 0;
3224 /* first find out if it is a reject entry */
3225 attr
[0].tag
= kFlagsKCItemAttr
;
3226 attr
[0].length
= sizeof(KCItemFlags
);
3227 attr
[0].data
= &flags
;
3228 status
= KCGetAttribute( itemRef
, attr
, nil
);
3229 if (status
!= noErr
) {
3232 if (flags
& kNegativeKCItemFlag
) {
3236 /* get the server name */
3237 attr
[0].tag
= kServerKCItemAttr
;
3238 attr
[0].length
= BUFFER_SIZE
;
3239 attr
[0].data
= buffer
;
3240 status
= KCGetAttribute( itemRef
, attr
, &actualSize
);
3241 if (status
!= noErr
) {
3245 /* null terminate */
3246 buffer
[actualSize
] = 0;
3247 passwordRealm
= NULL
;
3248 StrAllocCopy(passwordRealm
, buffer
);
3250 /* get the password data */
3251 status
= KCGetData(itemRef
, BUFFER_SIZE
, buffer
, &actualSize
);
3252 if (status
!= noErr
) {
3256 /* null terminate */
3257 buffer
[actualSize
] = 0;
3259 /* parse for '=' which separates the name and value */
3260 uint16 bufferlen
= PL_strlen(buffer
);
3261 for (i
= 0; i
< bufferlen
; i
++) {
3262 if (buffer
[i
] == '=') {
3263 value
= &buffer
[i
+1];
3268 name_array
[submit
.value_cnt
] = NULL
;
3269 value_array
[submit
.value_cnt
] = NULL
;
3270 type_array
[submit
.value_cnt
] = FORM_TYPE_PASSWORD
;
3271 StrAllocCopy(name_array
[submit
.value_cnt
], buffer
);
3272 StrAllocCopy(value_array
[submit
.value_cnt
], value
);
3275 /* get the account attribute */
3276 attr
[0].tag
= kAccountKCItemAttr
;
3277 attr
[0].length
= BUFFER_SIZE
;
3278 attr
[0].data
= buffer
;
3279 status
= KCGetAttribute( itemRef
, attr
, &actualSize
);
3280 if (status
!= noErr
) {
3284 /* null terminate */
3285 buffer
[actualSize
] = 0;
3287 /* parse for '=' which separates the name and value */
3288 uint16 bufferlen
= PL_strlen(buffer
);
3289 for (i
= 0; i
< bufferlen
; i
++) {
3290 if (buffer
[i
] == '=') {
3291 value
= &buffer
[i
+1];
3297 name_array
[submit
.value_cnt
] = NULL
;
3298 value_array
[submit
.value_cnt
] = NULL
;
3299 type_array
[submit
.value_cnt
] = FORM_TYPE_TEXT
;
3300 StrAllocCopy(name_array
[submit
.value_cnt
], buffer
);
3301 StrAllocCopy(value_array
[submit
.value_cnt
], value
);
3303 /* check for overruning of the arrays */
3304 if (submit
.value_cnt
>= MAX_ARRAY_SIZE
) {
3308 /* store the info for this URL into memory-resident data structure */
3309 if (!passwordRealm
|| passwordRealm
[0] == 0) {
3313 si_PutData(passwordRealm
, &submit
, PR_FALSE
);
3318 si_PutReject(passwordRealm
, nsDependentString(buffer
), PR_FALSE
);
3320 reject
= PR_FALSE
; /* reset reject flag */
3321 PR_Free(passwordRealm
);
3322 KCReleaseItemRef( &itemRef
);
3323 status
= KCFindNextItem( searchRef
, &itemRef
);
3325 si_unlock_signon_list();
3328 KCReleaseSearchRef( &searchRef
);
3331 /* Register a callback with the Keychain if we haven't already done so. */
3333 if (si_kcUPP
== NULL
) {
3334 si_kcUPP
= NewKCCallbackProc( si_KeychainCallback
);
3339 KCAddCallback( si_kcUPP
, kLockKCEventMask
+ kDeleteKCEventMask
+ kUpdateKCEventMask
, &si_list_invalid
);
3341 * Note that the callback is not necessarily removed. We take advantage
3342 * of the fact that the Keychain will clean up the callback when the app
3343 * goes away. It is explicitly removed when the signon preference is turned off.
3346 if (status
== errKCItemNotFound
) {
3354 * Save signon data to Apple Keychain
3356 * This routine is called only if signon pref is enabled!!!
3359 si_SaveSignonDataInKeychain() {
3360 char* account
= nil
;
3361 char* password
= nil
;
3362 si_SignonURLStruct
* URL
;
3363 si_SignonUserStruct
* user
;
3364 si_SignonDataStruct
* data
;
3369 KCItemFlags flags
= kInvisibleKCItemFlag
+ kNegativeKCItemFlag
;
3370 uint32 actualLength
;
3372 /* save off the reject list */
3373 if (si_reject_list
) {
3374 PRInt32 rejectCount
= LIST_COUNT(si_reject_list
);
3375 for (PRInt32 i
=0; i
<rejectCount
; i
++) {
3376 reject
= static_cast<si_Reject
*>(si_reject_list
->ElementAt(i
));
3377 status
= kcaddinternetpassword
3378 (reject
->passwordRealm
, nil
,
3381 kNetscapeProtocolType
,
3386 if (status
!= noErr
&& status
!= errKCDuplicateItem
) {
3389 if (status
== noErr
) {
3391 * make the item invisible so the user doesn't see it and
3392 * negative so we know that it is a reject entry
3394 attr
.tag
= kFlagsKCItemAttr
;
3396 attr
.length
= sizeof( flags
);
3398 status
= KCSetAttribute( itemRef
, &attr
);
3399 if (status
!= noErr
) {
3402 status
= KCUpdateItem(itemRef
);
3403 if (status
!= noErr
) {
3406 KCReleaseItemRef(&itemRef
);
3411 /* save off the passwords */
3412 if((si_signon_list
)) {
3413 PRInt32 urlCount
= LIST_COUNT(si_signon_list
);
3414 for (PRInt32 i
=0; i
<urlCount
; i
++) {
3415 URL
= static_cast<si_SignonURLStruct
*>(si_signon_list
->ElementAt(i
));
3417 /* add each user node of the URL node */
3418 PRInt32 userCount
= URL
->signonUser_list
.Count();
3419 for (PRInt32 i
=0; i
<userCount
; i
++) {
3420 user
= static_cast<si_SignonUserStruct
*>(URL
->signonUser_list
.ElementAt(i
));
3422 /* write out each data node of the user node */
3423 PRInt32 count
= user
->signonData_list
.Count();
3424 for (PRInt32 i
=0; i
<count
; i
++) {
3425 data
= static_cast<si_SignonDataStruct
*>(user
->signonData_list
.ElementAt(i
));
3426 char* attribute
= nil
;
3427 if (data
->isPassword
) {
3428 password
= PR_Malloc(PL_strlen(data
->value
) + PL_strlen(data
->name
) + 2);
3432 attribute
= password
;
3434 account
= PR_Malloc( PL_strlen(data
->value
) + PL_strlen(data
->name
) + 2);
3439 attribute
= account
;
3441 PL_strcpy(attribute
, data
->name
);
3442 PL_strcat(attribute
, "=");
3443 PL_strcat(attribute
, data
->value
);
3445 /* if it's already there, we just want to change the password */
3446 status
= kcfindinternetpassword
3447 (URL
->passwordRealm
,
3451 kNetscapeProtocolType
,
3457 if (status
== noErr
) {
3458 status
= KCSetData(itemRef
, PL_strlen(password
), password
);
3459 if (status
!= noErr
) {
3462 status
= KCUpdateItem(itemRef
);
3463 KCReleaseItemRef(&itemRef
);
3465 /* wasn't there, let's add it */
3466 status
= kcaddinternetpassword
3467 (URL
->passwordRealm
,
3471 kNetscapeProtocolType
,
3473 PL_strlen(password
),
3483 account
= password
= nil
;
3484 if (status
!= noErr
) {
3490 si_signon_list_changed
= PR_FALSE
;