4 * Rewrites XMLHttpRequest to automatically send CSRF token with it. In theory
5 * plays nice with other JavaScript libraries, needs testing though.
7 // Here are the basic overloaded method definitions
8 // The wrapper must be set BEFORE onreadystatechange is written to, since
9 // a bug in ActiveXObject prevents us from properly testing for it.
10 var CsrfMagic = function (real) {
11 // try to make it ourselves, if you didn't pass it
12 if (!real) try { real = new XMLHttpRequest; } catch (e) {;}
13 if (!real) try { real = new ActiveXObject('Msxml2.XMLHTTP'); } catch (e) {;}
14 if (!real) try { real = new ActiveXObject('Microsoft.XMLHTTP'); } catch (e) {;}
15 if (!real) try { real = new ActiveXObject('Msxml2.XMLHTTP.4.0'); } catch (e) {;}
19 real.onreadystatechange = function() {
20 csrfMagic._updateProps();
21 return csrfMagic.onreadystatechange ? csrfMagic.onreadystatechange() : null;
23 csrfMagic._updateProps();
26 CsrfMagic.prototype.open = function(method, url, async, username, password) {
27 if (method == 'POST') this.csrf_isPost = true;
28 // deal with Opera bug, thanks jQuery
29 if (username) return this.csrf_open(method, url, async, username, password);
30 else return this.csrf_open(method, url, async);
32 CsrfMagic.prototype.csrf_open = function(method, url, async, username, password) {
33 if (username) return this.csrf.open(method, url, async, username, password);
34 else return this.csrf.open(method, url, async);
37 CsrfMagic.prototype.send = function(data) {
38 if (!this.csrf_isPost) this.csrf_send(data);
39 prepend = csrfMagicName + '=' + csrfMagicToken + '&';
40 if (this.csrf_purportedLength === undefined) {
41 this.csrf_setRequestHeader("Content-length", this.csrf_purportedLength + prepend.length);
42 delete this.csrf_purportedLength;
44 delete this.csrf_isPost;
45 return this.csrf_send(prepend + data);
47 CsrfMagic.prototype.csrf_send = function(data) {
48 return this.csrf.send(data);
51 CsrfMagic.prototype.setRequestHeader = function(header, value) {
52 // We have to auto-set this at the end, since we don't know how long the
53 // nonce is when added to the data.
54 if (this.csrf_isPost && header == "Content-length") {
55 this.csrf_purportedLength = value;
58 return this.csrf_setRequestHeader(header, value);
60 CsrfMagic.prototype.csrf_setRequestHeader = function(header, value) {
61 return this.csrf.setRequestHeader(header, value);
64 CsrfMagic.prototype.abort = function () {
65 return this.csrf.abort();
67 CsrfMagic.prototype.getAllResponseHeaders = function() {
68 return this.csrf.getAllResponseHeaders();
70 CsrfMagic.prototype.getResponseHeader = function(header) {
71 return this.csrf.getResponseHeader(header);
75 CsrfMagic.prototype._updateProps = function() {
76 this.readyState = this.csrf.readyState;
77 if (this.readyState == 4) {
78 this.responseText = this.csrf.responseText;
79 this.responseXML = this.csrf.responseXML;
80 this.status = this.csrf.status;
81 this.statusText = this.csrf.statusText;
84 CsrfMagic.process = function(base) {
85 var prepend = csrfMagicName + '=' + csrfMagicToken;
86 if (base) return prepend + '&' + base;
90 // Sets things up for Mozilla/Opera/nice browsers
91 if (window.XMLHttpRequest && window.XMLHttpRequest.prototype) {
92 XMLHttpRequest.prototype.csrf_open = XMLHttpRequest.prototype.open;
93 XMLHttpRequest.prototype.csrf_send = XMLHttpRequest.prototype.send;
94 XMLHttpRequest.prototype.csrf_setRequestHeader = XMLHttpRequest.prototype.setRequestHeader;
96 // Notice that CsrfMagic is itself an instantiatable object, but only
97 // open, send and setRequestHeader are necessary as decorators.
98 XMLHttpRequest.prototype.open = CsrfMagic.prototype.open;
99 XMLHttpRequest.prototype.send = CsrfMagic.prototype.send;
100 XMLHttpRequest.prototype.setRequestHeader = CsrfMagic.prototype.setRequestHeader;
102 // The only way we can do this is by modifying a library you have been
103 // using. We plan to support YUI, script.aculo.us, prototype, MooTools,
104 // jQuery, Ext and Dojo.
106 // jQuery didn't implement a new XMLHttpRequest function, so we have
107 // to do this the hard way.
108 jQuery.csrf_ajax = jQuery.ajax;
109 jQuery.ajax = function( s ) {
110 if (s.type && s.type.toUpperCase() == 'POST') {
111 s = jQuery.extend(true, s, jQuery.extend(true, {}, jQuery.ajaxSettings, s));
112 if ( s.data && s.processData && typeof s.data != "string" ) {
113 s.data = jQuery.param(s.data);
115 s.data = CsrfMagic.process(s.data);
117 return jQuery.csrf_ajax( s );
119 } else if (window.Prototype) {
120 // This works for script.aculo.us too
121 Ajax.csrf_getTransport = Ajax.getTransport;
122 Ajax.getTransport = function() {
123 return new CsrfMagic(Ajax.csrf_getTransport());
125 } else if (window.MooTools) {
126 Browser.csrf_Request = Browser.Request;
127 Browser.Request = function () {
128 return new CsrfMagic(Browser.csrf_Request());
130 } else if (window.YAHOO) {
131 YAHOO.util.Connect.csrf_createXhrObject = YAHOO.util.Connect.createXhrObject;
132 YAHOO.util.Connect.createXhrObject = function (transaction) {
133 obj = YAHOO.util.Connect.csrf_createXhrObject(transaction);
135 obj.conn = new CsrfMagic(old);
138 } else if (window.Ext) {
139 // Ext can use other js libraries as loaders, so it has to come last
140 // Ext's implementation is pretty identical to Yahoo's, but we duplicate
141 // it for comprehensiveness's sake.
142 Ext.lib.Ajax.csrf_createXhrObject = Ext.lib.Ajax.createXhrObject;
143 Ext.lib.Ajax.createXhrObject = function (transaction) {
144 obj = Ext.lib.Ajax.csrf_createXhrObject(transaction);
146 obj.conn = new CsrfMagic(old);
149 } else if (window.dojo) {
150 dojo.csrf__xhrObj = dojo._xhrObj;
151 dojo._xhrObj = function () {
152 return new CsrfMagic(dojo.csrf__xhrObj());