HTTPPost.js

Summary

This module is used to save documents via HTTP POST. You must have a server which is configured to accept the POST data and take action. Examples for catching the POST data are provided in PHP and JSP format. The PHP examples include tests for handling saving errors.

To configure Mozile to use HTTPPost for saving, add the following line to the "modules" array in your mozile.js configuration file: "HTTPPost". You can add more options, for example: "HTTPPost: url='http://somewhere.com/save.php', default=true".

Version: 0.7.0

Author: James A. Overton


/* ***** BEGIN LICENSE BLOCK *****
 * Licensed under Version: MPL 1.1/GPL 2.0/LGPL 2.1
 * Full Terms at http://mozile.mozdev.org/license.html
 *
 * 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 was written by Max d'Ayala (www.dAyala.co.uk).
 *
 * The Initial Developer of the Original Code is Max d'Ayala
 * Portions created by the Initial Developer are Copyright (C) 2002-2003
 * the Initial Developer. All Rights Reserved.
 *
 * Contributor(s):
 *	Max d'Ayala (www.dAyala.co.uk)
 *	Tobias Minich
 *	James A. Overton <james@overton.ca>
 *
 * ***** END LICENSE BLOCK ***** */
 

/** HTTP POST
 * @fileoverview This module is used to save documents via HTTP POST. You must have a server which is configured to accept the POST data and take action. Examples for catching the POST data are provided in PHP and JSP format. The PHP examples include tests for handling saving errors.
 * <p>To configure Mozile to use HTTPPost for saving, add the following line to the "modules" array in your mozile.js configuration file: "HTTPPost". You can add more options, for example: "HTTPPost: url='http://somewhere.com/save.php', default=true".
 * 
 * @link http://mozile.mozdev.org 
 * @author James A. Overton <james@overton.ca>
 * @version 0.7.0
 */

// Define a new Mozile.saveList entry.
mozile.saveList["HTTPPost"] = new Array();
mozile.saveList["HTTPPost"]["value"] ="HTTPPost";
mozile.saveList["HTTPPost"]["label"] ="Save via HTTP POST";
mozile.saveList["HTTPPost"]["function"] = "mozileSaveViaPOST";
mozile.saveList["HTTPPost"]["showURL"] = true;
mozile.saveList["HTTPPost"]["url"] = "";

// If the configuration of this modules gives a URL, then set it
if(mozile.moduleList["HTTPPost"]["url"]) {
	mozile.saveList["HTTPPost"]["url"] = mozile.moduleList["HTTPPost"]["url"];
}

// If the configuration of this modules says it's the default, then set it as the default.
if(mozile.moduleList["HTTPPost"]["default"] && mozile.moduleList["HTTPPost"]["default"] == "true") {
	mozile.saveList["default"] = mozile.saveList["HTTPPost"];
}

/*
var save = mozile.commandList["Mozile-SaveToDialog"];
save.command = function(event) {
	var f = new Array();
	f["File"] = "HTTPPost/HTTPPost.js";
	f["Function"] = "save.command";
	
	mozile.status(f,1,"Starting to save document.");
	PostOnload();
}
*/








var fileObj = new Object();
var content;

function mozileSaveViaPOST() {
	var f = new Array();
	f["File"] = "HTTPPost/HTTPPost.js";
	f["Function"] = "mozileSaveViaPOST()";
	mozile.status(f,1,"Saving via POST...");
	
	fileObj.httpSavePath = mozile.saveConfig["url"];
	fileObj.documentHref =  mozile.saveConfig["url"];
	fileObj.charset = document.characterSet;
	fileObj.characterSet = document.characterSet;
	fileObj.contentType = "text/plain";
	
	var content = mozile.content();
	content = CR + LF + content + CR + LF;
	
	POST_HttpRequest(fileObj, content);
}




const CR = '\x0D';
const LF = '\x0A';

var reqObj = new Object();


/* === Global variables === */
var XHR = new Object();
var POSTinfo = new Object();
POSTinfo.isError=true;
POSTinfo.isAbort=false;
POSTinfo.status="0";
POSTinfo.statusText="Undefined";
POSTinfo.msgText="Undefined";
POSTinfo.display=null;
POSTinfo.document=null;
POSTinfo.documentText=null;
POSTinfo.location=null;
POSTinfo.replace=null;





/* === WINDOW Events === */

/** POST - On Load  -
 * Starts the HTTPRequest, and focuses the abort button.
 *
 * @return True if successful.
 */
function PostOnload() {
	var content = mozile.documentToXML();
	POST_HttpRequest( fileObj, content );
	//document.getElementById("abortButton").focus();
	return true;
}

/** POST - Stop  -
 * This function is called by the abort button, and it tyried to gracefully shut down the HTTPRequest.
 *
 * @return True if successful.
 */
function PostStop() {
	var f = ["savepost.js","PostStop"];

	if ( XHR.readyState != 4 ) {
		XHR.abort();
	}
	/* Possibility of locking window open if this try block fails */
	try {
		var n = document.getElementById("saveText2").firstChild;
		n.data = "Save aborted.";
		n = document.getElementById("acceptButton");
		n.setAttribute( "hidden", "false" );
		n.focus();
		document.getElementById("abortButton").setAttribute( "hidden", "true" );
		document.getElementById("saveMeter").setAttribute( "mode", "determined" );
		document.getElementById("saveMeter").setAttribute( "value", "65" );
	}
	catch(e) {
		mozile.debug(f,3,"Exception: "+e);
	}
	reqObj.isAbort = true;
	mozile.debug(f,1,"XMLHttpRequest aborted");
	return true;
}


/** POST - Accept  -
 * This function performs the final cleanup after a successful save.
 *
 * @return True if successful.
 */
function PostAccept() {
	var f = new Array();
	f["File"] = "HTTPPost/HTTPPost.js";
	f["Function"] = "save.command";
	mozile.status(f,1,"Save complete!");
	return true;
}



/* === XMLHttpRequest Events === */

/** POST - Complete  -
 * If the HTTPRequest is successful, then this function completes the save operation.
 *
 * @return True if successful.
 */
function PostComplete()
{
	var f = new Array();
	f["File"] = "HTTPPost/HTTPPost.js";
	f["Function"] = "PostComplete";

	//mozile.insertString(XHR.responseText);

	if ( XHR.readyState == 4 ) {
		try {
			//document.getElementById("abortButton").setAttribute( "disabled", "true" );
		} catch (e) {
			mozile.debug(f,3,"Exception: "+e);
		}
		POST_Response();
	}

	/*Set up info to be returned to opener.*/
	reqObj.isError = POSTinfo.isError;
	reqObj.status = POSTinfo.status;
	reqObj.statusText = POSTinfo.statusText;
	reqObj.msgText = POSTinfo.msgText;
	reqObj.display = POSTinfo.display;
	if (POSTinfo.document) {
		reqObj.document = POSTinfo.document;
	}
	if (POSTinfo.documentText) {
		reqObj.documentText = POSTinfo.documentText;
	}
	if (POSTinfo.location != null) {
		reqObj.location = POSTinfo.location;
		if (POSTinfo.replace === true)
			reqObj.replace = POSTinfo.replace;
	}
	mozile.debug(f,1,"POST completed");

	

	if ( "status" in reqObj ) {mozile.debug(f,1,"status: "+reqObj.status);}
	if ( "statusText" in reqObj ) {mozile.debug(f,1,"statusText: "+reqObj.statusText);}
	if ( "msgText" in reqObj ) {mozile.debug(f,2,"msgText: "+reqObj.msgText);}
	if ( "originalStatus" in reqObj ) {mozile.debug(f,1,"originalStatus: "+reqObj.originalStatus);}
	if ( "originalStatusText" in reqObj ) {mozile.debug(f,1,"originalStatusText: "+reqObj.originalStatusText);}

	if ( ("isAbort" in reqObj) && reqObj.isAbort ) {
		mozile.debug(f,3,"Save aborted");
	}
	else if ( ("isError" in reqObj) ) {
		if ( !reqObj.isError ) {
			mozileTarget.document.MOZILE_CURRENT_CONFIG.saved = 'true';
			mozile.debug(f,3,"Save successful");
		}
		else {
			mozile.debug(f,3,"Save error.");
			if ( !("msgText" in reqObj) ) {
				/*This is the old error function*/
			  alert ("Couldn't save document \n" + reqObj.statusText);
			}
		}
	}
	
	// Currently message display only implemented for POST method.
	// if ( ( ("display" in reqObj) && reqObj.display == "yes" ) 
	//   || ( ("isError" in reqObj) && reqObj.isError===true && ("display" in reqObj) && reqObj.display != "no" ) ) {
	// 	try {
	// 		var SaveMsgWindow = window.open(
	// 			"savemsg.xml",
	// 			"savemsg",
	// 			"modal,dialog,centerscreen,chrome,resizable=yes");
	// 	}catch (e){
	// 		mozile.debug(f,3,"Save message window open exception: "+e);
	// 	}
	// }
	mozile.status(f,2, "Document saved.", 100, "window.open(mozile.moduleList['HTTPPost']['path']+'savemsg.xml', 'savemsg', 'centerscreen,chrome,resizable=yes')");

	/*Close window. window.close() doesn't work directly from here.*/
	/*Don't like this but can't get dispatchEvent() to work.*/
	try {
		//var b = document.getElementById("acceptButton");
		//b.doCommand( PostAccept );
		/*problem handling errors here because window has closed.*/
	} catch (e) {
		mozile.debug(f,3,"Exception2: "+e);
	}
	
	return true;
}



/** POST - Change  -
 * This function is called when the HTTPRequest status changes. It sets the message for the statusbar. 
 * 0 (initial text), 1 and 4 probably won't show, 2 may be too quick as well!
 *
 * @return True if successful.
 */
function PostChange() {
	var f = new Array();
	f["File"] = "HTTPPost/HTTPPost.js";
	f["Function"] = "PostChange";
	
	try {
		switch ( XHR.readyState )
		{
		case 1 :
			mozile.status(f,3,"1 : Initialising...", 10);
			break;
		case 2 :
			mozile.status(f,3,"2 : Sending...", 50);
			break;
		case 3 :
			mozile.status(f,3,"3 : Receiving acknowledgement...", 90);
			break;
		case 4 :
			mozile.status(f,3,"4 : Completed", 100);
			break;
		}
	}
	catch(e) {
		mozile.debug(f,4,"Exception: "+e);
	}
	return true;
}


/** POST - On Error  -
 * This function is called when the HTTPRequest signals an error.
 *
 * @return True if successful.
 */
function PostOnerror()
{
	/*What to do here??? Will the onload event still fire to terminate the process???*/
	var f = new Array();
	f["File"] = "HTTPPost/HTTPPost.js";
	f["Function"] = "PostOnerror";
	
	mozile.debug(f,3,"POST XMLHttpRequest Error");
	mozile.status(f,3,"Save Failed! There was some kind of error in the HTTP POST.");
	return true;
}



/* === FUNCTIONS === */

/** POST - HTTP Request  -
 * This function starts and HTTP Request.
 *
 * @param id The id object for this request.
 * @param content The content string to be saved.
 * @return True if successful.
 */
function POST_HttpRequest(id, content)
{
	var f = new Array();
	f["File"] = "HTTPPost/HTTPPost.js";
	f["Function"] = "POST_HttpRequest";
	var contentLength;
	
	//alert(id.httpSavePath +" "+ id.documentHref);

	//Length doesn't include CRs and LFs, data only.
	contentLength = content.length;

	//alert(contentLength);
	
	try {
		XHR = new XMLHttpRequest();
		XHR.open("POST", id.httpSavePath, true, null, null);
		XHR.setRequestHeader( 'Content-Type', id.contentType + "; " + id.characterSet );
		XHR.setRequestHeader( 'Content-Length',  contentLength );
		XHR.setRequestHeader( 'Content-Location', id.documentHref );
		XHR.onerror = PostOnerror;
		XHR.onreadystatechange = PostChange;
		XHR.onload = PostComplete;
		XHR.send(content);
		mozile.debug(f,1,"POST sent");
	}catch (e){
		mozile.debug(f,4,"Exception: "+e);
		mozile.status(f,3,"Save Failed! Permission denied to save to URL \"" + mozile.saveConfig["url"] +"\"");
	}
}



/** POST - Prepare Data  -
 * Prepares the data by formatting it into an input stream? Requires privileges.
 *
 * @param data The data to be prepared.
 * @return True if successful.
 */
function POST_PrepareData( data )
{
	var f = new Array();
	f["File"] = "HTTPPost/HTTPPost.js";
	f["Function"] = "POST_PrepareData";
	var SP;
	
	const CR = '\x0D';
	const LF = '\x0A';
	
	var postData = new String();
	postData = CR + LF + data + CR + LF;
	try {
		SP = Components.classes["@mozilla.org/io/string-input-stream;1"].createInstance();
		if (SP) {
			SP.QueryInterface(Components.interfaces.nsIStringInputStream);
			SP.setData( postData, postData.length ); 
		}
		else {
			SP = null;
			mozile.debug(f,3,"Problem creating Stream!!!");
		}
	} catch(e) {
		SP = null;
		mozile.debug(f,3,"Exception: "+e);
	}
	return SP;
}


/** POST - Response  -
 * This function is called when there is a response to the HTTPRequest. It is really just a switch that calls another function as appropriate.
 *
 * @return True if successful.
 */
function POST_Response()
{
  switch ( XHR.status ) {
	case 200 :
		POST_200();
		break;
	case 204 :
		POST_204();
		break;
	case 201 :
		POST_201();
		break;
	default :
		POST_Error( "Unexpected server response" );
	}
	return true;
}


/** POST - 204  -
 * This function handles the 204: No Content response to the HTTPRequest.
 *
 * @return True if successful.
 */
function POST_204() {
	POSTinfo.isError = false;
	POSTinfo.status = "204";
	POSTinfo.statusText = XHR.statusText;
}

/** POST - 201  -
 * This function handles the 201: Created response to the HTTPRequest.
 *
 * @return True if successful.
 */
function POST_201() {
	POSTinfo.isError = false;
	POSTinfo.status = "201";
	POSTinfo.statusText = XHR.statusText;
}

/** POST - 200  -
 * This function handles the 200: Ok response to the HTTPRequest.
 *
 * @return True if successful.
 */
function POST_200() {
	if ( XHR.responseXML ) {
		POST_200XML();
	}
	else {
		POST_Error( "XML response not received from server." );
	}
}

/** POST - 200 XML  -
 * This function handles the 200: Ok response to the HTTPRequest, and parses the XML which is returned.
 *
 * @return True if successful.
 */
function POST_200XML() {
	var f = new Array();
	f["File"] = "HTTPPost/HTTPPost.js";
	f["Function"] = "POST_200XML";
	var postNode, psNode, ptNode, pdNode, plNode, postStatus="", postStatusText="";
	var postLocation=null, postReplace=false, postDisplay=null;

	try {
		postNode = XHR.responseXML.getElementsByTagNameNS("http://www.mozile.mozdev.org/ns/save/", "post");
		if (postNode.length > 0) {
			psNode = postNode[0].getElementsByTagName("status");
			if ( (psNode.length > 0) && psNode[0].hasChildNodes() && (psNode[0].firstChild.nodeType == Node.TEXT_NODE) ) {
				postStatus = psNode[0].firstChild.data;
			}
			ptNode = postNode[0].getElementsByTagName("statustext");
			if ( (ptNode.length > 0) && ptNode[0].hasChildNodes() && (ptNode[0].firstChild.nodeType == Node.TEXT_NODE) ) {
				postStatusText = ptNode[0].firstChild.data;
			}
			pdNode = postNode[0].getElementsByTagName("display");
			if ( (pdNode.length > 0) && pdNode[0].hasChildNodes() && (pdNode[0].firstChild.nodeType == Node.TEXT_NODE) ) {
				postDisplay = pdNode[0].firstChild.data.toLowerCase();
			}
			plNode = postNode[0].getElementsByTagName("location");
			if (plNode.length > 0) {
				if ( plNode[0].hasChildNodes() && (plNode[0].firstChild.nodeType == Node.TEXT_NODE) ) {
					postLocation = plNode[0].firstChild.data;
				}
				else {
					postLocation = "";
				}
				if ( plNode[0].hasAttributes() ) {
					var plAttr = plNode[0].attributes.getNamedItem("replace");
					if ( plAttr && plAttr.value.toLowerCase() == "true" )
						postReplace = true;
				}
			}
		}
		if ( (postStatus == "") || (postStatusText == "") ) {
			var s = "Something is wrong with the XML response."
			if (postNode.length == 0) {s+="\n<post> tag is missing or namespace is incorrect.\n  Namespace should be;\nhttp://www.mozile.mozdev.org/ns/save/";}
			else if (psNode.length == 0) {s+="\n<status> tag is missing.";}
			else if (postStatus == "") {s+="\nEmpty <status></status> tags.";}
			else if (ptNode.length == 0) {s+="\n<statustext> tag is missing.";}
			else if (postStatusText == "") {s+="\nEmpty <statustext></statustext> tags.";}
			POST_Error( s );
		} else {
			POSTinfo.status = "200";
			POSTinfo.statusText = XHR.statusText;
			POSTinfo.msgText = "[" + postStatus + "] " + postStatusText;
			if ( postStatus == "1" ) {
				POSTinfo.isError = false;
			}
			else {
				POSTinfo.isError = true;
			}
			switch (postDisplay) {
			case "yes" :
			case "no" :
				POSTinfo.display = postDisplay;
				break;
			default :
				POSTinfo.display = null;
			}
			if ( postLocation !== null ) {
				POSTinfo.location = postLocation;
				if ( postReplace )
					POSTinfo.replace = true;
			}
			if ( XHR.responseText )
				POSTinfo.documentText = XHR.responseText;
			if ( XHR.responseXML )
				POSTinfo.document = XHR.responseXML;
		}
	}
	catch(e) {
		mozile.debug(f,2,"Exception: "+e);
		POST_Error( "Exception" );
	}
}

/** POST - Error  -
 * General error handling function.	Optional first argument is a short string describing the error.
 *
 * @param message Optional A descriptive error message.
 * @return True if successful.
 */
/*
  
*/
function POST_Error() {
	var m = "Error";
	if ( arguments[0] ) {
		m += ": " + arguments[0] + "\n";
	}
	if (XHR.status && XHR.responseXML) {
		POSTinfo.status = XHR.status;
		POSTinfo.statusText = XHR.statusText;
		POSTinfo.document = XHR.responseXML;
		var x = parseXMLerror(XHR.responseXML);
		if ( x ) {
			m += "\n" + x;
		}
		POSTinfo.msgText = m;
		if ( XHR.responseText ) {
			POSTinfo.documentText = XHR.responseText;
		}
	}
	else if ( XHR.status && XHR.responseText ) {
		POSTinfo.status = XHR.status;
		POSTinfo.statusText = XHR.statusText;
		POSTinfo.msgText = m;
		POSTinfo.documentText = XHR.responseText;
	}
	else if ( XHR.status ) {
		POSTinfo.status = XHR.status;
		POSTinfo.statusText = XHR.statusText;
		POSTinfo.msgText = m;
	}
	else {
		POSTinfo.status = 999;
		POSTinfo.statusText = "Undefined";
		POSTinfo.msgText = m;
	}
	POSTinfo.isError = true;
}


/** POST - Parse XML Error  -
 * Checks for parsing errors in the XML response.
 *
 * @param responseXML The XML provided by the 200XML response to the HTTPRequest.
 * @return True if successful.
 */
function parseXMLerror( responseXML ) {
	var f = new Array();
	f["File"] = "HTTPPost/HTTPPost.js";
	f["Function"] = "parseXMLerror";
	var errtext="";
  
	try {
		// look to see if there is a parse error
		var parserErrorNode = responseXML.getElementsByTagNameNS("http://www.mozilla.org/newlayout/xml/parsererror.xml","parsererror")[0];

		if (parserErrorNode) {
			errtext += parserErrorNode.firstChild.data;
			var sourceNode = parserErrorNode.getElementsByTagName("sourcetext")[0];
			if (sourceNode) {
				errtext += "\n" + sourceNode.firstChild.data;
			}
		}
	} catch (e) {
		  mozile.debug(f,2,"Exception: "+e);
	}
	return errtext;
}



















mozile.registerModule("HTTPPost","1.0.0");


Documentation generated by JSDoc on Thu Feb 2 14:36:27 2006