diff --git a/mozilla/network/main/singsign.c b/mozilla/network/main/singsign.c new file mode 100644 index 00000000000..9a9baecb336 --- /dev/null +++ b/mozilla/network/main/singsign.c @@ -0,0 +1,1828 @@ +/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- + * + * The contents of this file are subject to the Netscape Public License + * Version 1.0 (the "License"); you may not use this file except in + * compliance with the License. You may obtain a copy of the License at + * http://www.mozilla.org/NPL/ + * + * Software distributed under the License is distributed on an "AS IS" + * basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See + * the License for the specific language governing rights and limitations + * under the License. + * + * The Original Code is Mozilla Communicator client code. + * + * The Initial Developer of the Original Code is Netscape Communications + * Corporation. Portions created by Netscape are Copyright (C) 1998 + * Netscape Communications Corporation. All Rights Reserved. + */ + +#if defined(SingleSignon) + +#include "libi18n.h" /* For the character code set conversions */ +#include "secnav.h" /* For SECNAV_UnMungeString and SECNAV_MungeString */ + +#ifdef XP_MAC +#include "prpriv.h" /* for NewNamedMonitor */ +#include "prinrval.h" /* for PR_IntervalNow */ +#else +#include "private/prpriv.h" /* for NewNamedMonitor */ +#endif + +#ifdef PROFILE +#pragma profile on +#endif + +#include "prefapi.h" +#include "xpgetstr.h" +#include "xpgetstr.h" + +extern int MK_SIGNON_PASSWORDS_GENERATE; +extern int MK_SIGNON_PASSWORDS_REMEMBER; +extern int MK_SIGNON_PASSWORDS_FETCH; +extern int MK_SIGNON_YOUR_SIGNONS; + +/* locks for signon cache */ + +static PRMonitor * signon_lock_monitor = NULL; +static PRThread * signon_lock_owner = NULL; +static int signon_lock_count = 0; + +PRIVATE void +si_lock_signon_list(void) +{ + if(!signon_lock_monitor) + signon_lock_monitor = + PR_NewNamedMonitor("signon-lock"); + + PR_EnterMonitor(signon_lock_monitor); + + while(TRUE) { + + /* no current owner or owned by this thread */ + PRThread * t = PR_CurrentThread(); + if(signon_lock_owner == NULL || signon_lock_owner == t) { + signon_lock_owner = t; + signon_lock_count++; + + PR_ExitMonitor(signon_lock_monitor); + return; + } + + /* owned by someone else -- wait till we can get it */ + PR_Wait(signon_lock_monitor, PR_INTERVAL_NO_TIMEOUT); + + } +} + +PRIVATE void +si_unlock_signon_list(void) +{ + PR_EnterMonitor(signon_lock_monitor); + +#ifdef DEBUG + /* make sure someone doesn't try to free a lock they don't own */ + PR_ASSERT(signon_lock_owner == PR_CurrentThread()); +#endif + + signon_lock_count--; + + if(signon_lock_count == 0) { + signon_lock_owner = NULL; + PR_Notify(signon_lock_monitor); + } + PR_ExitMonitor(signon_lock_monitor); + +} + + +/* + * Data and procedures for rememberSignons preference + */ + +static const char *pref_rememberSignons = + "network.signon.rememberSignons"; +PRIVATE Bool si_RememberSignons = FALSE; + +PRIVATE int +si_SaveSignonDataLocked(char * filename); + +PRIVATE void +si_SetSignonRememberingPref(Bool x) +{ + /* do nothing if new value of pref is same as current value */ + if (x == si_RememberSignons) { + return; + } + + /* if pref is being turned off, save the current signons to a file */ + if (x == 0) { + si_lock_signon_list(); + si_SaveSignonDataLocked(NULL); + si_unlock_signon_list(); + } + + /* change the pref */ + si_RememberSignons = x; + + /* if pref is being turned on, load the signon file into memory */ + if (x == 1) { + SI_RemoveAllSignonData(); + SI_LoadSignonData(NULL); + } +} + +MODULE_PRIVATE int PR_CALLBACK +si_SignonRememberingPrefChanged(const char * newpref, void * data) +{ + Bool x; + PREF_GetBoolPref(pref_rememberSignons, &x); + si_SetSignonRememberingPref(x); + return PREF_NOERROR; +} + +void +si_RegisterSignonPrefCallbacks(void) +{ + Bool x; + static Bool first_time = TRUE; + + if(first_time) + { + first_time = FALSE; + PREF_GetBoolPref(pref_rememberSignons, &x); + si_SetSignonRememberingPref(x); + PREF_RegisterCallback(pref_rememberSignons, si_SignonRememberingPrefChanged, NULL); + } +} + +PRIVATE Bool +si_GetSignonRememberingPref(void) +{ + si_RegisterSignonPrefCallbacks(); + return si_RememberSignons; +} + +/* + * Data and procedures for signon cache + */ + +typedef struct _SignonDataStruct { + char * name; + char * value; + Bool isPassword; +} si_SignonDataStruct; + +typedef struct _SignonUserStruct { + XP_List * signonData_list; +} si_SignonUserStruct; + +typedef struct _SignonURLStruct { + char * URLName; + si_SignonUserStruct* chosen_user; /* this is a state variable */ + Bool firsttime_chosing_user; /* this too is a state variable */ + XP_List * signonUser_list; +} si_SignonURLStruct; + +PRIVATE XP_List * si_signon_list=0; +PRIVATE Bool si_signon_list_changed = FALSE; +PRIVATE int si_TagCount = 0; +PRIVATE si_SignonURLStruct * lastURL = NULL; + + +/* Remove misleading portions from URL name */ +PRIVATE char* +si_StrippedURL (char* URLName) { + char *result = 0; + char *s, *t; + + /* check for null URLName */ + if (URLName == NULL || XP_STRLEN(URLName) == 0) { + return NULL; + } + + /* check for protocol at head of URL */ + s = URLName; + s = (char*) XP_STRCHR(s+1, '/'); + if (s) { + if (*(s+1) == '/') { + /* looks good, let's parse it */ + return NET_ParseURL(URLName, GET_HOST_PART); + } else { + /* not a valid URL, leave it as is */ + StrAllocCopy(result, URLName); + return result; + } + } + + /* initialize result */ + StrAllocCopy(result, URLName); + + /* remove everything in result after and including first question mark */ + s = result; + s = (char*) XP_STRCHR(s, '?'); + if (s) { + *s = '\0'; + } + + /* remove everything in result after last slash (e.g., index.html) */ + s = result; + t = 0; + while ((s = (char*) XP_STRCHR(s+1, '/'))) { + t = s; + } + if (t) { + *(t+1) = '\0'; + } + + /* remove socket number from result */ + s = result; + while ((s = (char*) XP_STRCHR(s+1, ':'))) { + /* s is at next colon */ + if (*(s+1) != '/') { + /* and it's not :// so it must be start of socket number */ + if ((t = (char*) XP_STRCHR(s+1, '/'))) { + /* t is at slash terminating the socket number */ + do { + /* copy remainder of t to s */ + *s++ = *t; + } while (*(t++)); + } + break; + } + } + + /* alll done */ + return result; +} + +/* remove terminating CRs or LFs */ +PRIVATE void +si_StripLF(char* buffer) { + while ((buffer[XP_STRLEN(buffer)-1] == '\n') || + (buffer[XP_STRLEN(buffer)-1] == '\r')) { + buffer[XP_STRLEN(buffer)-1] = '\0'; + } +} + +/* If user-entered password is "generate", then generate a random password */ +PRIVATE void +si_Randomize(char * password) { + PRIntervalTime random; + int i; + const char * hexDigits = "0123456789AbCdEf"; + if (XP_STRCMP(password, XP_GetString(MK_SIGNON_PASSWORDS_GENERATE)) == 0) { + random = PR_IntervalNow(); + for (i=0; i<8; i++) { + password[i] = hexDigits[random%16]; + random = random/16; + } + } +} + +/* + * Get the URL node for a given URL name + * + * This routine is called only when holding the signon lock!!! + */ +PRIVATE si_SignonURLStruct * +si_GetURL(char * URLName) { + XP_List * list_ptr = si_signon_list; + si_SignonURLStruct * URL; + char *strippedURLName = 0; + + if (!URLName) { + /* no URLName specified, return first URL (returns NULL if not URLs) */ + return (si_SignonURLStruct *) XP_ListNextObject(list_ptr); + } + + strippedURLName = si_StrippedURL(URLName); + while((URL = (si_SignonURLStruct *) XP_ListNextObject(list_ptr))!=0) { + if(URL->URLName && !XP_STRCMP(strippedURLName, URL->URLName)) { + XP_FREE(strippedURLName); + return URL; + } + } + XP_FREE(strippedURLName); + return (NULL); +} + +/* Remove a user node from a given URL node */ +PUBLIC Bool +SI_RemoveUser(char *URLName, char *userName, Bool save) { + si_SignonURLStruct * URL; + si_SignonUserStruct * user; + si_SignonDataStruct * data; + XP_List * user_ptr; + XP_List * data_ptr; + + /* do nothing if signon preference is not enabled */ + if (!si_GetSignonRememberingPref()) { + return FALSE; + } + + si_lock_signon_list(); + + /* get URL corresponding to URLName (or first URL if URLName is NULL) */ + URL = si_GetURL(URLName); + if (!URL) { + /* URL not found */ + si_unlock_signon_list(); + return FALSE; + } + + /* free the data in each node of the specified user node for this URL */ + user_ptr = URL->signonUser_list; + + if (userName == NULL) { + + /* no user specified so remove the first one */ + user = (si_SignonUserStruct *) XP_ListNextObject(user_ptr); + + } else { + + /* find the specified user */ + while((user = (si_SignonUserStruct *) XP_ListNextObject(user_ptr))!=0) { + data_ptr = user->signonData_list; + while((data = (si_SignonDataStruct *) XP_ListNextObject(data_ptr))!=0) { + if (XP_STRCMP(data->value, userName)==0) { + goto foundUser; + } + } + } + si_unlock_signon_list(); + return FALSE; /* user not found so nothing to remove */ + foundUser: ; + } + + /* free the items in the data list */ + data_ptr = user->signonData_list; + while((data = (si_SignonDataStruct *) XP_ListNextObject(data_ptr))!=0) { + XP_FREE(data->name); + XP_FREE(data->value); + } + + /* free the data list */ + XP_ListDestroy(user->signonData_list); + + /* free the user node */ + XP_ListRemoveObject(URL->signonUser_list, user); + + /* remove this URL if it contains no more users */ + if (XP_ListCount(URL->signonUser_list) == 0) { + XP_FREE(URL->URLName); + XP_ListRemoveObject(si_signon_list, URL); + } + + /* write out the change to disk */ + if (save) { + si_signon_list_changed = TRUE; + si_SaveSignonDataLocked(NULL); + } + + si_unlock_signon_list(); + return TRUE; +} + +/* + * temporary UI until FE implements this function as a single dialog box + */ +XP_Bool FE_Select( + MWContext* pContext, + char* pMessage, + char** pList, + int* pCount) { + int i; + char *message = 0; + for (i = 0; i < *pCount; i++) { + StrAllocCopy(message, pMessage); + StrAllocCat(message, " = "); + StrAllocCat(message, pList[i]); + if (FE_Confirm(pContext, message)) { + /* user selected this one */ + XP_FREE(message); + *pCount = i; + return TRUE; + } + } + + /* user rejected all */ + XP_FREE(message); + return FALSE; +} + +/* + * Get the user node for a given URL + * + * This routine is called only when holding the signon lock!!! + * + * This routine is called only if signon pref is enabled!!! + */ +PRIVATE si_SignonUserStruct* +si_GetUser(MWContext *context, char* URLName, Bool pickFirstUser) { + si_SignonURLStruct* URL; + si_SignonUserStruct* user; + si_SignonDataStruct* data; + XP_List * data_ptr=0; + XP_List * user_ptr=0; + XP_List * old_user_ptr=0; + + /* get to node for this URL */ + URL = si_GetURL(URLName); + if (URL != NULL) { + + /* node for this URL was found */ + int user_count; + user_ptr = URL->signonUser_list; + if ((user_count = XP_ListCount(user_ptr)) == 1) { + + /* only one set of data exists for this URL so select it */ + user = (si_SignonUserStruct *) XP_ListNextObject(user_ptr); + URL->firsttime_chosing_user = FALSE; + URL->chosen_user = user; + + } else if (pickFirstUser) { + user = (si_SignonUserStruct *) XP_ListNextObject(user_ptr); + + } else if (URL->firsttime_chosing_user) { + /* multiple users for this URL so a choice needs to be made */ + char ** list; + char ** list2; + si_SignonUserStruct** users; + si_SignonUserStruct** users2; + char * caption = 0; + + list = XP_ALLOC(user_count*sizeof(char*)); + users = XP_ALLOC(user_count*sizeof(si_SignonUserStruct*)); + list2 = list; + users2 = users; + + /* step through set of user nodes for this URL and create list of + * first data node of each (presumably that is the user name). + * Note that the user nodes are already ordered by + * most-recently-used so the first one in the list is the most + * likely one to be chosen. + */ + URL->firsttime_chosing_user = FALSE; + old_user_ptr = user_ptr; + + while((user = (si_SignonUserStruct *) XP_ListNextObject(user_ptr))!=0) { + data_ptr = user->signonData_list; + /* consider first item in data list to be the identifying item */ + data = (si_SignonDataStruct *) XP_ListNextObject(data_ptr); + if (!caption) { + caption = data->name; + } + *(list2++) = data->value; + *(users2++) = user; + } + + /* have user select an item from the list */ + if (FE_Select(context, caption, list, &user_count)) { + /* user selected an item */ + user = users[user_count]; /* this is the selected item */ + URL->chosen_user = user; + /* item selected is now most-recently used, put at head of list */ + XP_ListRemoveObject(URL->signonUser_list, user); + XP_ListAddObject(URL->signonUser_list, user); + } else { + user = NULL; + } + XP_FREE(list); + XP_FREE(users); + + } else { + user = URL->chosen_user; + } + } else { + user = NULL; + } + return user; +} + +/* + * Get the user for which a change-of-password is to be applied + * + * This routine is called only when holding the signon lock!!! + * + * This routine is called only if signon pref is enabled!!! + */ +PRIVATE si_SignonUserStruct* +si_GetUserForChangeForm(MWContext *context, char* URLName, int messageNumber) +{ + si_SignonURLStruct* URL; + si_SignonUserStruct* user; + si_SignonDataStruct * data; + XP_List * user_ptr = 0; + XP_List * data_ptr = 0; + char *message = 0; + + /* get to node for this URL */ + URL = si_GetURL(URLName); + if (URL != NULL) { + + /* node for this URL was found */ + user_ptr = URL->signonUser_list; + while((user = (si_SignonUserStruct *) XP_ListNextObject(user_ptr))!=0) { + char *strippedURLName = si_StrippedURL(URLName); + data_ptr = user->signonData_list; + data = (si_SignonDataStruct *) XP_ListNextObject(data_ptr); + message = PR_smprintf(XP_GetString(messageNumber), + data->value, + strippedURLName); + XP_FREE(strippedURLName); + if (FE_Confirm(context, message)) { + /* + * since this user node is now the most-recently-used one, move it + * to the head of the user list so that it can be favored for + * re-use the next time this form is encountered + */ + XP_ListRemoveObject(URL->signonUser_list, user); + XP_ListAddObject(URL->signonUser_list, user); + si_signon_list_changed = TRUE; + si_SaveSignonDataLocked(NULL); + XP_FREE(message); + return user; + } + } + } + XP_FREE(message); + return NULL; +} + +/* + * Put data obtained from a submit form into the data structure for + * the specified URL + * + * See comments below about state of signon lock when routine is called!!! + * + * This routine is called only if signon pref is enabled!!! + */ +PRIVATE void +si_PutData(char * URLName, LO_FormSubmitData * submit, Bool save) { + char * name; + char * value; + Bool added_to_list = FALSE; + si_SignonURLStruct * URL; + si_SignonUserStruct * user; + si_SignonDataStruct * data; + int j; + XP_List * list_ptr; + XP_List * user_ptr; + XP_List * data_ptr; + si_SignonURLStruct * tmp_URL_ptr; + size_t new_len; + Bool mismatch; + int i; + + /* discard this if the password is empty */ + for (i=0; ivalue_cnt; i++) { + if ((((uint8*)submit->type_array)[i] == FORM_TYPE_PASSWORD) && + (XP_STRLEN( ((char **)(submit->value_array)) [i])) == 0) { + return; + } + } + + /* + * lock the signon list + * Note that, for efficiency, SI_LoadSignonData already sets the lock + * before calling this routine whereas none of the other callers do. + * So we need to determine whether or not we were called from + * SI_LoadSignonData before setting or clearing the lock. We can + * determine this by testing "save" since only SI_LoadSignonData passes + * in a value of FALSE for "save". + */ + if (save) { + si_lock_signon_list(); + } + + /* make sure the signon list exists */ + if(!si_signon_list) { + si_signon_list = XP_ListNew(); + if(!si_signon_list) { + if (save) { + si_unlock_signon_list(); + } + return; + } + } + + /* find node in signon list having the same URL */ + if ((URL = si_GetURL(URLName)) == NULL) { + + /* doesn't exist so allocate new node to be put into signon list */ + URL = XP_NEW(si_SignonURLStruct); + if (!URL) { + if (save) { + si_unlock_signon_list(); + } + return; + } + + /* fill in fields of new node */ + URL->URLName = si_StrippedURL(URLName); + if (!URL->URLName) { + XP_FREE(URL); + if (save) { + si_unlock_signon_list(); + } + return; + } + + URL->signonUser_list = XP_ListNew(); + if(!URL->signonUser_list) { + XP_FREE(URL->URLName); + XP_FREE(URL); + } + + /* put new node into signon list so that it is before any + * strings of smaller length + */ + new_len = XP_STRLEN(URL->URLName); + list_ptr = si_signon_list; + while((tmp_URL_ptr = + (si_SignonURLStruct *) XP_ListNextObject(list_ptr))!=0) { + if(new_len > XP_STRLEN (tmp_URL_ptr->URLName)) { + XP_ListInsertObject + (si_signon_list, tmp_URL_ptr, URL); + added_to_list = TRUE; + break; + } + } + /* no shorter strings found in list */ + if (!added_to_list) { + XP_ListAddObjectToEnd (si_signon_list, URL); + } + } + + /* initialize state variables in URL node */ + URL->chosen_user = NULL; + URL->firsttime_chosing_user = TRUE; + + /* + * see if a user node with data list matching new data already exists + * (password fields will not be checked for in this matching) + */ + user_ptr = URL->signonUser_list; + while((user = (si_SignonUserStruct *) XP_ListNextObject(user_ptr))!=0) { + data_ptr = user->signonData_list; + j = 0; + while((data = (si_SignonDataStruct *) XP_ListNextObject(data_ptr))!=0) { + mismatch = FALSE; + + /* skip non text/password fields */ + while ((j < submit->value_cnt) && + (((uint8*)submit->type_array)[j]!=FORM_TYPE_TEXT) && + (((uint8*)submit->type_array)[j]!=FORM_TYPE_PASSWORD)) { + j++; + } + + /* check for match on name field and type field */ + if ((j < submit->value_cnt) && + (data->isPassword == + (((uint8*)submit->type_array)[j]==FORM_TYPE_PASSWORD)) && + (!XP_STRCMP(data->name, ((char **)submit->name_array)[j]))) { + + /* success, now check for match on value field if not password */ + if (!submit->value_array[j]) { + /* special case a null value */ + if (!XP_STRCMP(data->value, "")) { + j++; /* success */ + } else { + mismatch = TRUE; + break; /* value mismatch, try next user */ + } + } else if (!XP_STRCMP(data->value, ((char **)submit->value_array)[j]) + || data->isPassword) { + j++; /* success */ + } else { + mismatch = TRUE; + break; /* value mismatch, try next user */ + } + } else { + mismatch = TRUE; + break; /* name or type mismatch, try next user */ + } + + } + if (!mismatch) { + + /* all names and types matched and all non-password values matched */ + + /* + * note: it is ok for password values not to match; it means + * that the user has either changed his password behind our + * back or that he previously mis-entered the password + */ + + /* update the saved password values */ + data_ptr = user->signonData_list; + j = 0; + while((data = (si_SignonDataStruct *) XP_ListNextObject(data_ptr))!=0) { + + /* skip non text/password fields */ + while ((j < submit->value_cnt) && + (((uint8*)submit->type_array)[j]!=FORM_TYPE_TEXT) && + (((uint8*)submit->type_array)[j]!=FORM_TYPE_PASSWORD)) { + j++; + } + + /* update saved password */ + if ((j < submit->value_cnt) && data->isPassword) { + if (XP_STRCMP(data->value, ((char **)submit->value_array)[j])) { + si_signon_list_changed = TRUE; + StrAllocCopy(data->value, ((char **)submit->value_array)[j]); + si_Randomize(data->value); + } + } + j++; + } + + /* + * since this user node is now the most-recently-used one, move it + * to the head of the user list so that it can be favored for + * re-use the next time this form is encountered + */ + XP_ListRemoveObject(URL->signonUser_list, user); + XP_ListAddObject(URL->signonUser_list, user); + + /* return */ + if (save) { + si_signon_list_changed = TRUE; + si_SaveSignonDataLocked(NULL); + si_unlock_signon_list(); + } + return; /* nothing more to do since data already exists */ + } + } + + /* user node with current data not found so create one */ + user = XP_NEW(si_SignonUserStruct); + if (!user) { + if (save) { + si_unlock_signon_list(); + } + return; + } + user->signonData_list = XP_ListNew(); + if(!user->signonData_list) { + XP_FREE(user); + if (save) { + si_unlock_signon_list(); + } + return; + } + + + /* create and fill in data nodes for new user node */ + for (j=0; jvalue_cnt; j++) { + + /* skip non text/password fields */ + if((((uint8*)submit->type_array)[j]!=FORM_TYPE_TEXT) && + (((uint8*)submit->type_array)[j]!=FORM_TYPE_PASSWORD)) { + continue; + } + + /* create signon data node */ + data = XP_NEW(si_SignonDataStruct); + if (!data) { + XP_ListDestroy(user->signonData_list); + XP_FREE(user); + } + data->isPassword = + (((uint8 *)submit->type_array)[j] == FORM_TYPE_PASSWORD); + name = 0; /* so that StrAllocCopy doesn't free previous name */ + StrAllocCopy(name, ((char **)submit->name_array)[j]); + data->name = name; + value = 0; /* so that StrAllocCopy doesn't free previous name */ + if (submit->value_array[j]) { + StrAllocCopy(value, ((char **)submit->value_array)[j]); + } else { + StrAllocCopy(value, ""); /* insures that value is not null */ + } + data->value = value; + if (data->isPassword) { + si_Randomize(data->value); + } + + /* append new data node to end of data list */ + XP_ListAddObjectToEnd (user->signonData_list, data); + } + + /* append new user node to front of user list for matching URL */ + /* + * Note that by appending to the front, we assure that if there are + * several users, the most recently used one will be favored for + * reuse the next time this form is encountered. But don't do this + * when reading in signons.txt (i.e., when save is false), otherwise + * we will be reversing the order when reading in. + */ + if (save) { + XP_ListAddObject(URL->signonUser_list, user); + si_signon_list_changed = TRUE; + si_SaveSignonDataLocked(NULL); + si_unlock_signon_list(); + } else { + XP_ListAddObjectToEnd(URL->signonUser_list, user); + } +} + +/* + * When a new form is started, set the tagCount to 0 + */ +PUBLIC void +SI_StartOfForm() { + si_TagCount = 0; +} + +/* + * Load signon data from disk file + * The parameter passed in on entry is ignored + */ + +#define BUFFER_SIZE 4096 +#define MAX_ARRAY_SIZE 50 +PUBLIC int +SI_LoadSignonData(char * filename) { + char * URLName; + LO_FormSubmitData submit; + char* name_array[MAX_ARRAY_SIZE]; + char* value_array[MAX_ARRAY_SIZE]; + uint8 type_array[MAX_ARRAY_SIZE]; + char buffer[BUFFER_SIZE]; + XP_File fp; + Bool badInput; + int i; + char* unmungedValue; + + /* do nothing if signon preference is not enabled */ + if (!si_GetSignonRememberingPref()) { + return 0; + } + + /* open the signon file */ + if(!(fp = XP_FileOpen(filename, xpHTTPSingleSignon, XP_FILE_READ))) + return(-1); + + /* initialize the submit structure */ + submit.name_array = (PA_Block)name_array; + submit.value_array = (PA_Block)value_array; + submit.type_array = (PA_Block)type_array; + + /* read the URL line */ + si_lock_signon_list(); + while(XP_FileReadLine(buffer, BUFFER_SIZE, fp)) { + + /* ignore comment lines at head of file */ + if (buffer[0] == '#') { + continue; + } + si_StripLF(buffer); + URLName = NULL; + StrAllocCopy(URLName, buffer); + + /* preparre to read the name/value pairs */ + submit.value_cnt = 0; + badInput = FALSE; + + while(XP_FileReadLine(buffer, BUFFER_SIZE, fp)) { + + /* line starting with . terminates the pairs for this URL entry */ + if (buffer[0] == '.') { + break; /* end of URL entry */ + } + + /* line just read is the name part */ + + /* save the name part and determine if it is a password */ + si_StripLF(buffer); + name_array[submit.value_cnt] = NULL; + if (buffer[0] == '*') { + type_array[submit.value_cnt] = FORM_TYPE_PASSWORD; + StrAllocCopy(name_array[submit.value_cnt], buffer+1); + } else { + type_array[submit.value_cnt] = FORM_TYPE_TEXT; + StrAllocCopy(name_array[submit.value_cnt], buffer); + } + + /* read in and save the value part */ + if(!XP_FileReadLine(buffer, BUFFER_SIZE, fp)) { + /* error in input file so give up */ + badInput = TRUE; + break; + } + si_StripLF(buffer); + value_array[submit.value_cnt] = NULL; + /* note that we need to skip over leading '=' of value */ + if (type_array[submit.value_cnt] == FORM_TYPE_PASSWORD) { + if ((unmungedValue=SECNAV_UnMungeString(buffer+1)) == NULL) { + /* this is the free source and there is no obscuring of passwords */ + unmungedValue = buffer+1; + } + StrAllocCopy(value_array[submit.value_cnt++], unmungedValue); + XP_FREE(unmungedValue); + } else { + StrAllocCopy(value_array[submit.value_cnt++], buffer+1); + } + + /* check for overruning of the arrays */ + if (submit.value_cnt >= MAX_ARRAY_SIZE) { + break; + } + } + + /* store the info for this URL into memory-resident data structure */ + if (!URLName || XP_STRLEN(URLName) == 0) { + badInput = TRUE; + } + if (!badInput) { + si_PutData(URLName, &submit, FALSE); + } + + /* free up all the allocations done for processing this URL */ + XP_FREE(URLName); + for (i=0; isignonUser_list; + + /* write out each user node of the URL node */ + while((user = (si_SignonUserStruct *) + XP_ListNextObject(user_ptr)) != NULL) { + XP_FileWrite(URL->URLName, -1, fp); + XP_FileWrite(LINEBREAK, -1, fp); + + data_ptr = user->signonData_list; + + /* write out each data node of the user node */ + while((data = (si_SignonDataStruct *) + XP_ListNextObject(data_ptr)) != NULL) { + char* mungedValue; + if (data->isPassword) { + XP_FileWrite("*", -1, fp); + } + XP_FileWrite(data->name, -1, fp); + XP_FileWrite(LINEBREAK, -1, fp); + XP_FileWrite("=", -1, fp); /* precede values with '=' */ + if (data->isPassword) { + if ((mungedValue = SECNAV_MungeString(data->value))) { + XP_FileWrite(mungedValue, -1, fp); + XP_FREE(mungedValue); + } else { + /* this is the free source and passwords are not obscured */ + XP_FileWrite(data->value, -1, fp); + } + } else { + XP_FileWrite(data->value, -1, fp); + } + XP_FileWrite(LINEBREAK, -1, fp); + } + XP_FileWrite(".", -1, fp); + XP_FileWrite(LINEBREAK, -1, fp); + } + } + + si_signon_list_changed = FALSE; + XP_FileClose(fp); + return(0); +} + + +/* + * Save signon data to disk file + * The parameter passed in on entry is ignored + */ + +PUBLIC int +SI_SaveSignonData(char * filename) { + int retval; + + /* do nothing if signon preference is not enabled */ + if (!si_GetSignonRememberingPref()) { + return FALSE; + } + + /* lock and call common save routine */ + si_lock_signon_list(); + retval = si_SaveSignonDataLocked(filename); + si_unlock_signon_list(); + return retval; +} + +/* + * Remove all the signons and free everything + */ + +PUBLIC void +SI_RemoveAllSignonData() { + + /* do nothing if signon preference is not enabled */ + if (!si_GetSignonRememberingPref()) { + return; + } + + /* repeatedly remove first user node of first URL node */ + while (SI_RemoveUser(NULL, NULL, FALSE)) { + } +} + +/* + * Check for a signon submission and remember the data if so + */ +PUBLIC void +SI_RememberSignonData(MWContext *context, LO_FormSubmitData * submit) +{ + int i; + int passwordCount = 0; + int pswd[3]; + + /* do nothing if signon preference is not enabled */ + if (!si_GetSignonRememberingPref()){ + return; + } + + /* determine how many passwords are in the form and where they are */ + for (i=0; ivalue_cnt; i++) { + if (((uint8 *)submit->type_array)[i] == FORM_TYPE_PASSWORD) { + if (passwordCount < 3 ) { + pswd[passwordCount] = i; + } + passwordCount++; + } + } + + /* process the form according to how many passwords it has */ + if (passwordCount == 1) { + /* one-password form is a log-in so remember it */ + si_PutData(context->hist.cur_doc_ptr->address, submit, TRUE); + + } else if (passwordCount == 2) { + /* two-password form is a registration */ + + } else if (passwordCount == 3) { + /* three-password form is a change-of-password request */ + + XP_List * data_ptr; + si_SignonDataStruct* data; + si_SignonUserStruct* user; + + /* make sure all passwords are non-null and 2nd and 3rd are identical */ + if (!submit->value_array[pswd[0]] || ! submit->value_array[pswd[1]] || + !submit->value_array[pswd[2]] || + XP_STRCMP(((char **)submit->value_array)[pswd[1]], + ((char **)submit->value_array)[pswd[2]])) { + return; + } + + /* ask user if this is a password change */ + si_lock_signon_list(); + user = si_GetUserForChangeForm( + context, + context->hist.cur_doc_ptr->address, + MK_SIGNON_PASSWORDS_REMEMBER); + + /* return if user said no */ + if (!user) { + si_unlock_signon_list(); + return; + } + + /* get to password being saved */ + data_ptr = user->signonData_list; + while((data = (si_SignonDataStruct *) XP_ListNextObject(data_ptr))!=0) { + if (data->isPassword) { + break; + } + } + + /* + * if second password is "generate" then generate a random + * password for it and use same random value for third password + * as well (Note: this all works because we already know that + * second and third passwords are identical so third password + * must also be "generate". Furthermore si_Randomize() will + * create a random password of exactly eight characters -- the + * same length as "generate".) + */ + si_Randomize(((char **)submit->value_array)[pswd[1]]); + XP_STRCPY(((char **)submit->value_array)[pswd[2]], + ((char **)submit->value_array)[pswd[1]]); + StrAllocCopy(data->value, ((char **)submit->value_array)[pswd[1]]); + si_signon_list_changed = TRUE; + si_SaveSignonDataLocked(NULL); + si_unlock_signon_list(); + } +} + +/* + * Check for remembered data from a previous signon submission and + * restore it if so + */ +PUBLIC void +SI_RestoreOldSignonData + (MWContext *context, LO_FormElementStruct *form_element, char* URLName) +{ + si_SignonURLStruct* URL; + si_SignonUserStruct* user; + si_SignonDataStruct* data; + XP_List * data_ptr=0; + + /* do nothing if signon preference is not enabled */ + if (!si_GetSignonRememberingPref()){ + return; + } + + /* get URL */ + si_lock_signon_list(); + URL = si_GetURL(URLName); + + /* + * if we leave a form without submitting it, the URL's state variable + * will not get reset. So we test for that case now and reset the + * state variables if necessary + */ + if (lastURL != URL) { + if (lastURL) { + lastURL -> chosen_user = NULL; + lastURL -> firsttime_chosing_user = TRUE; + } + } + lastURL = URL; + + /* see if this is first item in form and is a password */ + si_TagCount++; + if ((si_TagCount == 1) && + (form_element->element_data->type == FORM_TYPE_PASSWORD)) { + /* + * it is so there's a good change its a password change form, + * let's ask user to confirm this + */ + user = si_GetUserForChangeForm(context, URLName, MK_SIGNON_PASSWORDS_FETCH); + if (user) { + /* user has confirmed it's a change-of-password form */ + data_ptr = user->signonData_list; + data = (si_SignonDataStruct *) XP_ListNextObject(data_ptr); + while((data = (si_SignonDataStruct *) XP_ListNextObject(data_ptr))!=0) { + if (data->isPassword) { +#ifdef XP_MAC + StrAllocCopy( + (char *)form_element->element_data->ele_text.default_text, + data->value); +#else + char* default_text = + (char*)(form_element->element_data->ele_text.default_text); + StrAllocCopy(default_text, data->value); + form_element->element_data->ele_text.default_text = + (unsigned long *)default_text; +#endif + si_unlock_signon_list(); + return; + } + } + } + } + + /* get the data from previous time this URL was visited */ + + if (si_TagCount == 1) { + user = si_GetUser(context, URLName, FALSE); + } else { + if (URL) { + user = URL->chosen_user; + } else { + user = NULL; + } + } + if (user) { + + /* restore the data from previous time this URL was visited */ + data_ptr = user->signonData_list; + while((data = (si_SignonDataStruct *) XP_ListNextObject(data_ptr))!=0) { + if(XP_STRCMP(data->name, + (char *)form_element->element_data->ele_text.name)==0) { +#ifdef XP_MAC + StrAllocCopy( + (char *)form_element->element_data->ele_text.default_text, + data->value); +#else + char* default_text = + (char*)(form_element->element_data->ele_text.default_text); + StrAllocCopy(default_text, data->value); + form_element->element_data->ele_text.default_text = + (unsigned long *)default_text; +#endif + si_unlock_signon_list(); + return; + } + } + } + si_unlock_signon_list(); +} + +/* + * Remember signon data from a browser-generated password dialog + */ +PRIVATE void +si_RememberSignonDataFromBrowser(char* URLName, char* username, char* password) +{ + LO_FormSubmitData submit; + char* USERNAME = "username"; + char* PASSWORD = "password"; + char* name_array[2]; + char* value_array[2]; + uint8 type_array[2]; + + /* do nothing if signon preference is not enabled */ + if (!si_GetSignonRememberingPref()){ + return; + } + + /* initialize a temporary submit structure */ + submit.name_array = (PA_Block)name_array; + submit.value_array = (PA_Block)value_array; + submit.type_array = (PA_Block)type_array; + submit.value_cnt = 2; + + name_array[0] = USERNAME; + value_array[0] = NULL; + StrAllocCopy(value_array[0], username); + type_array[0] = FORM_TYPE_TEXT; + + name_array[1] = PASSWORD; + value_array[1] = NULL; + StrAllocCopy(value_array[1], password); + type_array[1] = FORM_TYPE_PASSWORD; + + /* Save the signon data */ + si_PutData(URLName, &submit, TRUE); + + /* Free up the data memory just allocated */ + XP_FREE(value_array[0]); + XP_FREE(value_array[1]); +} + +/* + * Check for remembered data from a previous browser-generated password dialog + * restore it if so + */ +PRIVATE void +si_RestoreOldSignonDataFromBrowser + (MWContext *context, char* URLName, Bool pickFirstUser, + char** username, char** password) +{ + si_SignonUserStruct* user; + si_SignonDataStruct* data; + XP_List * data_ptr=0; + + /* get the data from previous time this URL was visited */ + si_lock_signon_list(); + user = si_GetUser(context, URLName, pickFirstUser); + if (!user) { + *username = 0; + *password = 0; + si_unlock_signon_list(); + return; + } + + /* restore the data from previous time this URL was visited */ + data_ptr = user->signonData_list; + while((data = (si_SignonDataStruct *) XP_ListNextObject(data_ptr))!=0) { + if(XP_STRCMP(data->name, "username")==0) { + StrAllocCopy(*username, data->value); + } else if(XP_STRCMP(data->name, "password")==0) { + StrAllocCopy(*password, data->value); + } + } + si_unlock_signon_list(); +} + +/* Browser-generated prompt for user-name and password */ +PUBLIC int +SI_PromptUsernameAndPassword + (MWContext *context, char *prompt, + char **username, char **password, char *URLName) +{ + int status; + char *copyOfPrompt=0; + + /* just for safety -- really is a problem in SI_Prompt */ + StrAllocCopy(copyOfPrompt, prompt); + + /* do the FE thing if signon preference is not enabled */ + if (!si_GetSignonRememberingPref()){ + status = FE_PromptUsernameAndPassword + (context, copyOfPrompt, username, password); + XP_FREEIF(copyOfPrompt); + return status; + } + + /* prefill with previous username/password if any */ + si_RestoreOldSignonDataFromBrowser + (context, URLName, FALSE, username, password); + + /* get new username/password from user */ + status = FE_PromptUsernameAndPassword + (context, copyOfPrompt, username, password); + + /* remember these values for next time */ + if (status) { + si_RememberSignonDataFromBrowser (URLName, *username, *password); + } + + /* cleanup and return */ + XP_FREEIF(copyOfPrompt); + return status; +} + +/* Browser-generated prompt for password */ +PUBLIC char * +SI_PromptPassword + (MWContext *context, char *prompt, char *URLName, + Bool pickFirstUser, Bool useLastPassword) +{ + char *password=0, *username=0, *copyOfPrompt=0, *result; + + /* just for safety -- really is a problem in SI_Prompt */ + StrAllocCopy(copyOfPrompt, prompt); + + /* do the FE thing if signon preference is not enabled */ + if (!si_GetSignonRememberingPref()){ + result = FE_PromptPassword(context, copyOfPrompt); + XP_FREEIF(copyOfPrompt); + return result; + } + + /* get previous password used with this username */ + if (useLastPassword) { + si_RestoreOldSignonDataFromBrowser + (context, URLName, pickFirstUser, &username, &password); + } + if (password && XP_STRLEN(password)) { + XP_FREEIF(copyOfPrompt); + return password; + } + + /* if no password found, get new password from user */ + password = FE_PromptPassword(context, copyOfPrompt); + + /* if username wasn't even found, substitute the URLName */ + if (!username) { + StrAllocCopy(username, URLName); + } + + /* remember these values for next time */ + if (password && XP_STRLEN(password)) { + si_RememberSignonDataFromBrowser (URLName, username, password); + } + + /* cleanup and return */ + XP_FREEIF(username); + XP_FREEIF(copyOfPrompt); + return password; +} + +/* Browser-generated prompt for username */ +PUBLIC char * +SI_Prompt (MWContext *context, char *prompt, + char* defaultUsername, char *URLName) +{ + char *password=0, *username=0, *copyOfPrompt=0, *result; + + /* + * make copy of prompt because it is currently in a temporary buffer in + * the caller (from GetString) which will be destroyed if GetString() is + * called again before prompt is used + */ + StrAllocCopy(copyOfPrompt, prompt); + + /* do the FE thing if signon preference is not enabled */ + if (!si_GetSignonRememberingPref()){ + result = FE_Prompt(context, copyOfPrompt, defaultUsername); + XP_FREEIF(copyOfPrompt); + return result; + } + + /* get previous username used */ + if (!defaultUsername || !XP_STRLEN(defaultUsername)) { + si_RestoreOldSignonDataFromBrowser + (context, URLName, FALSE, &username, &password); + } else { + StrAllocCopy(username, defaultUsername); + } + + /* prompt for new username */ + result = FE_Prompt(context, copyOfPrompt, username); + + /* remember this username for next time */ + if (result && XP_STRLEN(result)) { + si_RememberSignonDataFromBrowser (URLName, result, ""); + } + + /* cleanup and return */ + XP_FREEIF(username); + XP_FREEIF(password); + XP_FREEIF(copyOfPrompt); + return result; +} + +#include "mkgeturl.h" +#include "htmldlgs.h" +extern int XP_EMPTY_STRINGS; +extern int SA_VIEW_BUTTON_LABEL; +extern int SA_REMOVE_BUTTON_LABEL; + +#define BUFLEN 5000 + +#define FLUSH_BUFFER \ + if (buffer) { \ + StrAllocCat(buffer2, buffer); \ + g = 0; \ + } + +/* return TRUE if "number" is in sequence of comma-separated numbers */ +Bool si_InSequence(char* sequence, int number) { + char* ptr; + char* endptr; + char* undo = NULL; + Bool retval = FALSE; + int i; + + /* not necessary -- routine will work even with null sequence */ + if (!*sequence) { + return FALSE; + } + + for (ptr = sequence ; ptr ; ptr = endptr) { + + /* get to next comma */ + endptr = XP_STRCHR(ptr, ','); + + /* if comma found, set it to null */ + if (endptr) { + + /* restore last comma-to-null back to comma */ + if (undo) { + *undo = ','; + } + undo = endptr; + *endptr++ = '\0'; + } + + /* if there is a number before the comma, compare it with "number" */ + if (*ptr) { + i = atoi(ptr); + if (i == number) { + + /* "number" was in the sequence so return TRUE */ + retval = TRUE; + break; + } + } + } + + if (undo) { + *undo = ','; + } + return retval; +} + +MODULE_PRIVATE void +si_DisplayUserInfoAsHTML + (MWContext *context, si_SignonUserStruct* user, char* URLName) +{ + char *buffer = (char*)XP_ALLOC(BUFLEN); + char *buffer2 = 0; + int g = 0; + XP_List *data_ptr = user->signonData_list; + si_SignonDataStruct* data; + int dataNum; + + static XPDialogInfo dialogInfo = { + XP_DIALOG_OK_BUTTON, + NULL, + 250, + 200 + }; + + XPDialogStrings* strings; + StrAllocCopy(buffer2, ""); + + g += PR_snprintf(buffer+g, BUFLEN-g, +"
\n" +"
%s
\n" + ); + FLUSH_BUFFER + + /* free buffer since it is no longer needed */ + if (buffer) { + XP_FREE(buffer); + } + + /* do html dialog */ + strings = XP_GetDialogStrings(XP_EMPTY_STRINGS); + if (!strings) { + if (buffer2) { + XP_FREE(buffer2); + } + return; + } + if (buffer2) { + XP_CopyDialogString(strings, 0, buffer2); + XP_FREE(buffer2); + buffer2 = NULL; + } + XP_MakeHTMLDialog(context, &dialogInfo, MK_SIGNON_YOUR_SIGNONS, + strings, context, PR_FALSE); + + return; +} + +PR_STATIC_CALLBACK(PRBool) +si_SignonInfoDialogDone + (XPDialogState* state, char** argv, int argc, unsigned int button) +{ + XP_List *URL_ptr; + XP_List *user_ptr; + XP_List *data_ptr; + si_SignonURLStruct *URL; + si_SignonURLStruct *URLToDelete = 0; + si_SignonUserStruct* user; + si_SignonUserStruct* userToDelete = 0; + si_SignonDataStruct* data; + + char *buttonName, *userNumberAsString; + int userNumber; + char* gone; + + /* test for cancel */ + if (button == XP_DIALOG_CANCEL_BUTTON) { + /* CANCEL button was pressed */ + return PR_FALSE; + } + + /* get button name */ + buttonName = XP_FindValueInArgs("button", argv, argc); + if (buttonName && + !XP_STRCASECMP(buttonName, XP_GetString(SA_VIEW_BUTTON_LABEL))) { + + /* view button was pressed */ + + /* get "selname" value in argv list */ + if (!(userNumberAsString = XP_FindValueInArgs("selname", argv, argc))) { + /* no selname in argv list */ + return(PR_TRUE); + } + + /* convert "selname" value from string to an integer */ + userNumber = atoi(userNumberAsString); + + /* step to the user corresponding to that integer */ + si_lock_signon_list(); + URL_ptr = si_signon_list; + while ((URL = (si_SignonURLStruct *) XP_ListNextObject(URL_ptr))) { + user_ptr = URL->signonUser_list; + while ((user = (si_SignonUserStruct *) + XP_ListNextObject(user_ptr))) { + if (--userNumber < 0) { + si_DisplayUserInfoAsHTML + ((MWContext *)(state->arg), user, URL->URLName); + return(PR_TRUE); + } + } + } + si_unlock_signon_list(); + } + + /* OK was pressed, do the deletions */ + + /* first get the comma-separated sequence of signons to be deleted */ + gone = XP_FindValueInArgs("goneC", argv, argc); + XP_ASSERT(gone); + if (!gone) { + return PR_FALSE; + } + + /* step through all users and delete those that are in the sequence + * Note: we can't delete user while "user_ptr" is pointing to it because + * that would destroy "user_ptr". So we do a lazy deletion + */ + userNumber = 0; + URL_ptr = si_signon_list; + while ((URL = (si_SignonURLStruct *) XP_ListNextObject(URL_ptr))) { + user_ptr = URL->signonUser_list; + while ((user = (si_SignonUserStruct *) + XP_ListNextObject(user_ptr))) { + if (si_InSequence(gone, userNumber)) { + if (userToDelete) { + + /* get to first data item -- that's the user name */ + data_ptr = userToDelete->signonData_list; + data = (si_SignonDataStruct *) XP_ListNextObject(data_ptr); + /* do the deletion */ + SI_RemoveUser(URLToDelete->URLName, data->value, TRUE); + } + userToDelete = user; + URLToDelete = URL; + } + userNumber++; + } + } + if (userToDelete) { + /* get to first data item -- that's the user name */ + data_ptr = userToDelete->signonData_list; + data = (si_SignonDataStruct *) XP_ListNextObject(data_ptr); + + /* do the deletion */ + SI_RemoveUser(URLToDelete->URLName, data->value, TRUE); + si_signon_list_changed = TRUE; + si_SaveSignonDataLocked(NULL); + } + + return PR_FALSE; +} + +PUBLIC void +SI_DisplaySignonInfoAsHTML(MWContext *context) +{ + char *buffer = (char*)XP_ALLOC(BUFLEN); + char *buffer2 = 0; + int g = 0, userNumber; + XP_List *URL_ptr; + XP_List *user_ptr; + XP_List *data_ptr; + si_SignonURLStruct *URL; + si_SignonUserStruct * user; + si_SignonDataStruct* data; + + static XPDialogInfo dialogInfo = { + XP_DIALOG_OK_BUTTON | XP_DIALOG_CANCEL_BUTTON, + si_SignonInfoDialogDone, + 300, + 420 + }; + + XPDialogStrings* strings; + StrAllocCopy(buffer2, ""); + + /* Write out the javascript */ + g += PR_snprintf(buffer+g, BUFLEN-g, +"\n" + ); + FLUSH_BUFFER + + /* force loading of the signons file */ + si_RegisterSignonPrefCallbacks(); + si_lock_signon_list(); + URL_ptr = si_signon_list; + + /* Write out each URL */ + g += PR_snprintf(buffer+g, BUFLEN-g, +"
\n" +"\n" +"
%s
\n" + ); + FLUSH_BUFFER + + g += PR_snprintf(buffer+g, BUFLEN-g, +"
\n" +"\n" +"\n" +"\n" +"
\n", + XP_GetString(SA_REMOVE_BUTTON_LABEL), + XP_GetString(SA_VIEW_BUTTON_LABEL) ); + FLUSH_BUFFER + + /* free buffer since it is no longer needed */ + if (buffer) { + XP_FREE(buffer); + } + + /* do html dialog */ + strings = XP_GetDialogStrings(XP_EMPTY_STRINGS); + if (!strings) { + if (buffer2) { + XP_FREE(buffer2); + } + return; + } + if (buffer2) { + XP_CopyDialogString(strings, 0, buffer2); + XP_FREE(buffer2); + buffer2 = NULL; + } + XP_MakeHTMLDialog(context, &dialogInfo, MK_SIGNON_YOUR_SIGNONS, + strings, context, PR_FALSE); + + return; +} + +#endif