document.write( "<script language=\"JavaScript\" src=\"sha1.js\"></script>" );
if ( navigator.platform == "Win32" )
{
  document.write( "<script language=\"JavaScript\" src=\"largeint.js\"></script>" );
}
else
{
  document.write( "<script language=\"JavaScript\" src=\"largeint2.js\"></script>" );
}
document.write( "<script language=\"JavaScript\" src=\"arc4.js\"></script>" );
document.write( "<script language=\"JavaScript\" src=\"keyhandler.js\"></script>" );

  //******************************************************************
  //The following five lines are applicable only to Response-O-Matic.
  //If you use a different server, you'll need different HTML.
  //******************************************************************
  document.write( "<form action=\"http://www.response-o-matic.com/cgi-bin/rom.pl\" method=\"POST\" name=\"hiddenform\">" );
  document.write( "<input type=\"hidden\" name=\"your_email_address\" value=\"nospam@nospam.org\">" );
  document.write( "<input type=\"hidden\" name=\"visitor_name\" value=\"Anonymous\">" );
  document.write( "<input type=\"hidden\" name=\"visitor_email_address\" value=\"nemo@nowhere.org\">" );
  document.write( "<input type=\"hidden\" name=\"email_subject_line\" value=\"MeringueCGI\">" );

  document.write( "<input type=\"hidden\" name=\"Your form has been encrypted for privacy using MeringueMail\" value=\" \">" );
  document.write( "<input type=\"hidden\" name=\"MERINGUE_MAIL_VERSION\" value=\"1.2\">" );
  document.write( "<input type=\"hidden\" name=\"KEY_WRAPPING_MECHANISM\" value=\"pkcs1_v1.5\">" );
  document.write( "<input type=\"hidden\" name=\"PUBLIC_KEY_ID\" value>" );
  document.write( "<input type=\"hidden\" name=\"WRAPPED_KEY\" value>" );
  document.write( "<input type=\"hidden\" name=\"ENCRYPTION_ALGORITHM\" value=\"arc4\">" );
  document.write( "<input type=\"hidden\" name=\"FORM_CHAR_SET\" value=\"latin1\">" );
  document.write( "<input type=\"hidden\" name=\"FORM_NAME\" value>" );
  document.write( "<input type=\"hidden\" name=\"ENCRYPTED_FORM_DATA\" value>" );
document.write( "</form>" );

var sendAllCheckBoxesFlag = false;
var sendCheckBoxValueFlag = false;

//Checkboxes can be checked or unchecked.  The default behaviour of
//MeringueMail is to send only the checked checkboxes.
//If you want to send all checkboxes (checked and unchecked),
//call the sendAllCheckBoxes function with the value 'true'.
//Checkboxes may sent as "name=value" pairs.  The "value" is sent
//as "yes" or "no" rather than the common but less descriptive
//"on" for "yes".  If only checked boxes are to be sent, there is
//little point in sending the value field; however you can do that
//by calling the sendCheckBoxValue function with the argument true.
function sendAllCheckBoxes( flag ) {
  sendAllCheckBoxesFlag = flag;
}

function sendCheckBoxValue( flag ) {
  sendCheckBoxValueFlag = flag;
}

//Check box and radio button values may have spaces appended so
//that all values are the same length.  This value must be
//false if you don't want this behaviour. Call the doPadding
//function with the argument false.
var doPaddingFlag = true;

function doPadding( flag ) {
  doPaddingFlag = flag;
}


//A restriction of MeringueMail is that form field names may not
//contain '=' characters or begin with an '&' character. This function
//returns false if a form field name is unacceptable.
function checkFormFieldName( formFieldName ) {
  if ( formFieldName.length < 1 ) {
    return false;
  }
  if ( formFieldName.charAt(0) == '&' ) {
    return false;
  }
  if (formFieldName.indexOf('=') != -1) {
    return false;
  }
  if ( formFieldName.indexOf( "Meringue" ) == 0 ) {
    return false;
  }
  return true;
}

//Some form elements may have different lengths.  To ensure that one
//can't determine which form element has been selected on the basis of
//the length of the encrypted data, the following two routines will
//determine how much padding must be added.
function getNumberPaddingCharacters( element, elementArray ) {
  var numPads = 0;
  for ( var i=0; i<elementArray.length; i++) {
    while ( (element.length+numPads) < elementArray[i].value.length ) {
      numPads++;
    }
  }

  return numPads;
}

function getNumberPaddingCharacters2( element, elementArray ) {
  var numPads = 0;
  for ( var i=0; i<elementArray.length; i++) {
    while ( (element.length+numPads) < elementArray[i].text.length ) {
      numPads++;
    }
  }

  return numPads;
}

//When a radio button has been selected, one will need to encrypt
//the value of the radio button.  Since the values may have
//different lengths, it's necessary to find all the radio buttons
//in a radio button group so that padding can be added to values
//which are shorter than the maximum possible length.
function getRadioArray( form, radioElement ) {
  var radioArray = new Array();
  for (var i=0; i<form.elements.length; i++) {
    if ( form.elements[i].name != radioElement.name ) {
      continue;
    }
    radioArray[ radioArray.length ] = form.elements[i];
  }

  return radioArray;
}

//This code adds new line characters after every 96 characters.
function formatRomValue(str,prependLength) {
  var SPACER=96;

  var str2 = "";
  var i;

  for (i=0; i<prependLength; i++) {
    str = " " + str;
  }

  for (i=0; i<Math.floor(str.length/SPACER); i++) {
    str2 += str.substring(SPACER*i, SPACER*i+96 );
    str2 += '\n';
  }
  i = Math.floor(str.length/SPACER);
  str2 += str.substring(SPACER*i, str.length );

  return str2.substring( prependLength, str2.length);
}

//MeringueMail form value fields must contain only lower case letters,
//digits, underscores and periods.  Form fields that need to contain
//additional characters are encoded so that each character is replaced
//by 2 hex digits specifying the ASCII character code.
function convertToCharCodes( s ) {
  var s2="";
  for (var i=0; i<s.length; i++) {
    var c=s.charCodeAt(i);
    c &= 0xFF;
    s2 += hexDigits[ Math.floor(c/16) ];  //hexDigits defined in arc4.js
    s2 += hexDigits[ c & 0x0F ];
  }
  return s2;
}

//This function checks how many bytes are needed to store a character;
//i.e. whether we can use UTF-8 or whether we need to switch to UTF-16.
function getCharWidth(text) {
  for (var i=0; i<text.length; i++) {
    if ( text.charCodeAt(i) > 0xFF ) {
      return 2;
    }
  }

  return 1;
}


//This code encrypts and submits the form passed as a parameter
function submitForm(parentForm) {
  submitFormWithName( parentForm, parentForm.name );
}

//This code encrypts and submits a form passed as a parameter.  The
//formName is sent unencrypted.  Pass "" for the form name if you
//don't want outsiders to identify which form is being submitted.
function submitFormWithName(parentForm,formName) {
  if ( recipients.length == 0 ) {
    alert( "There is no known recipient.\n\n" +
           "Possibly the 'keys.js' file is missing.\n" );
    return;
  }
  submitFormWithNameToRecipient(parentForm,formName,recipients[0]);
}

//The keys.js contains an array recipients specifying one or more
//MeringueMail recipients.  Normally the array contains only one entry
//but if multiple recipients are to be allowed it is possible
//to specify which recipient is intended.  The default is to submit
//to the first name in the recipients array.  Use this function if
//you want to submit to another recipient.
function submitFormWithNameToRecipient(parentForm,formName,recipient) {
  //Get a random key
  var key = getRandomArc4Key();
  initializeArc4SBox( key );

  var rsaPublicModulus;
  var rsaPublicKeyId;

  rsaPublicModulus = getPublicKey( recipient );
  rsaPublicKeyId = getKeyId( recipient );
  document.hiddenform.PUBLIC_KEY_ID.value = rsaPublicKeyId;
  //The form name can contain weird characters.  Format it so that the
  //client can make sense of it.
  document.hiddenform.FORM_NAME.value = convertToCharCodes(formName);

  //******************************************************************
  //The following lines are applicable only to Response-O-Matic
  //******************************************************************
  document.hiddenform.email_subject_line.value = "MeringueCGI";
  document.hiddenform.your_email_address.value = getEmailAddress( recipient );

  //Encrypt the key under the recipient's public key
  var enc = encryptKeyPkcs1( key, rsaPublicModulus );
  var encKeyString = convertLargeIntToHexString( enc );
  document.hiddenform.WRAPPED_KEY.value = encKeyString;

  //Now start encrypting all the elements of the form
  var plainText = "";
  var cipherText = "";
  var numPaddingCharacters = 0;

  for (var i=0; i<parentForm.elements.length; i++) {
    var elementType = parentForm.elements[i].type;

    //No point sending buttons
    if ( elementType == "button" || elementType == "reset" || elementType == "submit" ) {
      continue;
    }

    var elementName = parentForm.elements[i].name;
    if (!checkFormFieldName(elementName) ) {
      alert( "This form element: " + elementName + "\n" +
             "is unacceptable.\n\nA form element's name must have\n" +
             "non-zero length and cannot contain\n" +
             "'=' characters, begin with an '&' or\n" +
             "start with 'Meringue'." );
      return;
    }

    var elementValue;
    var formElement;

    if ( elementType == "radio" ) {
      if ( parentForm.elements[i].checked ) {
        var radioButton = parentForm.elements[i];
        elementValue = radioButton.value;
        var elementArray = getRadioArray( parentForm, radioButton );
        numPaddingCharacters += getNumberPaddingCharacters( elementValue, elementArray );
        formElement = elementName + '=' + elementValue;
      }
      else {
        continue;
      }
    }
    else if ( elementType == "checkbox" ) {
      if ( parentForm.elements[i].checked ) {
        elementValue = "Yes";
        if ( sendAllCheckBoxesFlag || sendCheckBoxValueFlag ) {
          formElement = elementName + '=' + elementValue;
        }
        else {
          formElement = elementName;
        }
      }
      else {
        if ( !sendAllCheckBoxesFlag ) {
          if ( sendCheckBoxValueFlag ) {
            numPaddingCharacters += elementName.length+4;
          }
          else {
            numPaddingCharacters += elementName.length;
          }
          continue;
        }
        elementValue = "No";
        formElement = elementName + '=' + elementValue;
        numPaddingCharacters++;
      }
    }
    else if ( elementType == "select-one" ) {
      var index = parentForm.elements[i].selectedIndex;
      if ( index < 0 ) {
        continue;
      }
      var optionsArray = parentForm.elements[i].options;
      elementValue = optionsArray[index].text;
      numPaddingCharacters += getNumberPaddingCharacters2( elementValue, optionsArray );
      formElement = elementName + '=' + elementValue;
    }
    else if ( elementType == "select-multiple" ) {
      var optionsArray = parentForm.elements[i].options;
      elementValue = "";
      for (var j=optionsArray.length-1; j>=0; j-- ) {
        if ( optionsArray[j].selected ) {
          var ev2 = optionsArray[j].text;
          numPaddingCharacters += getNumberPaddingCharacters2( ev2, optionsArray );
          ev2 += '\n';
          elementValue = ev2+elementValue;
        }
        else {
          var ev2 = "";
          numPaddingCharacters += getNumberPaddingCharacters2( ev2, optionsArray );
          ev2 += '\n';
          elementValue = elementValue+ev2;
        }
      }
      formElement = elementName + '=' + elementValue;
    }
    else {
      elementValue = parentForm.elements[i].value;
      formElement = elementName + '=' + elementValue;
    }

    //Replace occurrences of '&' with '&&'
    var formElement2="";
    for (var j=0; j<formElement.length; j++) {
      formElement2 += formElement.charAt(j);
      if ( formElement.charAt(j) == '&' ) {
        formElement2 += "&";
      }
    }

    //Add the element to the plainText
    plainText += "&" + formElement2;
  }

  //Prepend the padding
  for (var i=0; i<numPaddingCharacters; i++) {
    plainText = "." + plainText;
  }
  plainText = "MeringuePadding=" + plainText;

  //Check whether the plaintext is UTF-16 or Unicode.
  var charWidth = getCharWidth( plainText );
  if (charWidth == 1) {
    document.hiddenform.FORM_CHAR_SET.value = "latin1";
  }
  else {
    document.hiddenform.FORM_CHAR_SET.value = "unicode";
  }

  //Encrypt the plaintext
  for (var i=0; i<plainText.length; i++) {
    if ( charWidth == 2 ) {
      cipherText += arc4EncryptByte( Math.floor( plainText.charCodeAt(i)/0x100 )  );
    }
    cipherText += arc4EncryptByte( plainText.charCodeAt(i)%0x100 );
  }

  //Store the encrypted in the hidden form
  document.hiddenform.ENCRYPTED_FORM_DATA.value = cipherText;

  //Response-O-Matic battles if the encrypted form data doesn't have
  //some white space in very long strings.  This adds some white
  //space padding.
  document.hiddenform.WRAPPED_KEY.value =
      formatRomValue( document.hiddenform.WRAPPED_KEY.value, 13 );
  document.hiddenform.ENCRYPTED_FORM_DATA.value =
      formatRomValue( document.hiddenform.ENCRYPTED_FORM_DATA.value, 21 );

  document.hiddenform.submit();
  //parentForm.reset();
}

