si_SaveSignonDataLocked();
si_SaveSignonDataLocked("signons", PR_TRUE);
si_PutData(const char * passwordRealm, nsVoidArray * signonData, PRBool save) {  PRBool added_to_list = PR_FALSE;  si_SignonURLStruct * url;  si_SignonUserStruct * user;  si_SignonDataStruct * data;  si_SignonDataStruct * data2;  PRBool mismatch = PR_FALSE;  /* discard this if the password is empty */  PRInt32 count = signonData->Count();  for (PRInt32 i=0; i<count; i++) {    data2 = NS_STATIC_CAST(si_SignonDataStruct*, signonData->ElementAt(i));    if (data2->isPassword && data2->value.Length()==0) {      return;    }  }  /* make sure the signon list exists */  if (!si_signon_list) {    si_signon_list = new nsVoidArray();    if (!si_signon_list) {      return;    }  }  /*   * lock the signon list   *   Note that, for efficiency, SI_LoadSignonData already sets the lock   *   before calling this routine whereas none of the other callers do.   *   So we need to determine whether or not we were called from   *   SI_LoadSignonData before setting or clearing the lock.  We can   *   determine this by testing "save" since only SI_LoadSignonData passes   *   in a value of PR_FALSE for "save".   */  if (save) {    si_lock_signon_list();  }  /* find node in signon list having the same URL */  if ((url = si_GetURL(passwordRealm)) == NULL) {    /* doesn't exist so allocate new node to be put into signon list */    url = new si_SignonURLStruct;    if (!url) {      if (save) {        si_unlock_signon_list();      }      return;    }    /* fill in fields of new node */	url->passwordRealm = nsnull;	if (passwordRealm) {    	url->passwordRealm = PL_strdup(passwordRealm);	}    if (!url->passwordRealm) {      if (save) {        si_unlock_signon_list();      }      return;    }    /* put new node into signon list */#ifdef alphabetize    /* add it to the list in alphabetical order */    si_SignonURLStruct * tmp_URL;    PRInt32 urlCount = LIST_COUNT(si_signon_list);    for (PRInt32 ii = 0; ii<urlCount; ++ii) {      tmp_URL = NS_STATIC_CAST(si_SignonURLStruct *, si_signon_list->ElementAt(ii));      if (tmp_URL) {        if (PL_strcasecmp(url->passwordRealm, tmp_URL->passwordRealm)<0) {          si_signon_list->InsertElementAt(url, ii);          added_to_list = PR_TRUE;          break;        }      }    }    if (!added_to_list) {      si_signon_list->AppendElement(url);    }#else    /* add it to the end of the list */    si_signon_list->AppendElement(url);#endif  }  /* initialize state variables in URL node */  url->chosen_user = NULL;  /*   * see if a user node with data list matching new data already exists   * (password fields will not be checked for in this matching)   */  PRInt32 userCount = url->signonUser_list.Count();  for (PRInt32 i2=0; i2<userCount; i2++) {    if (!save) {      break; /* otherwise we could be asked for master password when loading signon data */    }    user = NS_STATIC_CAST(si_SignonUserStruct*, url->signonUser_list.ElementAt(i2));    PRInt32 j = 0;    PRInt32 dataCount = user->signonData_list.Count();    for (PRInt32 i3=0; i3<dataCount; i3++) {      data = NS_STATIC_CAST(si_SignonDataStruct*, user->signonData_list.ElementAt(i3));      mismatch = PR_FALSE;      /* check for match on name field and type field */      if (j < signonData->Count()) {        data2 = NS_STATIC_CAST(si_SignonDataStruct*, signonData->ElementAt(j));        if ((data->isPassword == data2->isPassword) &&            (data->name == data2->name)) {          /* success, now check for match on value field if not password */          if (si_CompareEncryptedToEncrypted(data->value, data2->value) || data->isPassword) {            j++; /* success */          } else {            mismatch = PR_TRUE;            break; /* value mismatch, try next user */          }        } else {          mismatch = PR_TRUE;          break; /* name or type mismatch, try next user */        }      }    }    if (!mismatch) {      /* all names and types matched and all non-password values matched */      /*       * note: it is ok for password values not to match; it means       * that the user has either changed his password behind our       * back or that he previously mis-entered the password       */      /* update the saved password values */      j = 0;      PRInt32 dataCount2 = user->signonData_list.Count();      for (PRInt32 i4=0; i4<dataCount2; i4++) {        data = NS_STATIC_CAST(si_SignonDataStruct*, user->signonData_list.ElementAt(i4));        /* update saved password */        if ((j < signonData->Count()) && data->isPassword) {          data2 = NS_STATIC_CAST(si_SignonDataStruct*, signonData->ElementAt(j));          if (!si_CompareEncryptedToEncrypted(data->value, data2->value)) {            si_signon_list_changed = PR_TRUE;            data->value = data2->value;/* commenting out because I don't see how such randomizing could ever have worked. *///          si_Randomize(data->value);          }        }        j++;      }      /*       * since this user node is now the most-recently-used one, move it       * to the head of the user list so that it can be favored for       * re-use the next time this form is encountered       */      url->signonUser_list.RemoveElement(user);      url->signonUser_list.InsertElementAt(user, 0);      /* return */      if (save) {        si_signon_list_changed = PR_TRUE;        si_SaveSignonDataLocked();        si_unlock_signon_list();      }      return; /* nothing more to do since data already exists */    }  }  /* user node with current data not found so create one */  user = new si_SignonUserStruct;  if (!user) {    if (save) {      si_unlock_signon_list();    }    return;  }  /* create and fill in data nodes for new user node */  for (PRInt32 k=0; k<signonData->Count(); k++) {    /* create signon data node */    data = new si_SignonDataStruct;    if (!data) {      delete user;      if (save) {        si_unlock_signon_list();      }      return;    }    data2 = NS_STATIC_CAST(si_SignonDataStruct*, signonData->ElementAt(k));    data->isPassword = data2->isPassword;    data->name = data2->name;    data->value = data2->value;/* commenting out because I don't see how such randomizing could ever have worked. *///  if (data->isPassword) {//    si_Randomize(data->value);//  }    /* append new data node to end of data list */    user->signonData_list.AppendElement(data);  }  /* append new user node to front of user list for matching URL */    /*     * Note that by appending to the front, we assure that if there are     * several users, the most recently used one will be favored for     * reuse the next time this form is encountered.  But don't do this     * when reading in the saved signons (i.e., when save is PR_FALSE), otherwise     * we will be reversing the order when reading in.     */  if (save) {    url->signonUser_list.InsertElementAt(user, 0);    si_signon_list_changed = PR_TRUE;    si_SaveSignonDataLocked();    si_unlock_signon_list();  } else {    url->signonUser_list.AppendElement(user);  }}