4 * Rewrites XMLHttpRequest to automatically send CSRF token with it. In theory
5 * plays nice with other JavaScript libraries, needs testing though.
8 // Here are the basic overloaded method definitions
9 // The wrapper must be set BEFORE onreadystatechange is written to, since
10 // a bug in ActiveXObject prevents us from properly testing for it.
11 CsrfMagic = function(real
) {
12 // try to make it ourselves, if you didn't pass it
13 if (!real
) try { real
= new XMLHttpRequest
; } catch (e
) {;}
14 if (!real
) try { real
= new ActiveXObject('Msxml2.XMLHTTP'); } catch (e
) {;}
15 if (!real
) try { real
= new ActiveXObject('Microsoft.XMLHTTP'); } catch (e
) {;}
16 if (!real
) try { real
= new ActiveXObject('Msxml2.XMLHTTP.4.0'); } catch (e
) {;}
20 real
.onreadystatechange = function() {
21 csrfMagic
._updateProps();
22 return csrfMagic
.onreadystatechange
? csrfMagic
.onreadystatechange() : null;
24 csrfMagic
._updateProps();
27 CsrfMagic
.prototype = {
29 open: function(method
, url
, async
, username
, password
) {
30 if (method
== 'POST') this.csrf_isPost
= true;
31 // deal with Opera bug, thanks jQuery
32 if (username
) return this.csrf_open(method
, url
, async
, username
, password
);
33 else return this.csrf_open(method
, url
, async
);
35 csrf_open: function(method
, url
, async
, username
, password
) {
36 if (username
) return this.csrf
.open(method
, url
, async
, username
, password
);
37 else return this.csrf
.open(method
, url
, async
);
40 send: function(data
) {
41 if (!this.csrf_isPost
) return this.csrf_send(data
);
42 prepend
= csrfMagicName
+ '=' + csrfMagicToken
+ '&';
43 if (this.csrf_purportedLength
=== undefined) {
44 this.csrf_setRequestHeader("Content-length", this.csrf_purportedLength
+ prepend
.length
);
45 delete this.csrf_purportedLength
;
47 delete this.csrf_isPost
;
48 return this.csrf_send(prepend
+ data
);
50 csrf_send: function(data
) {
51 return this.csrf
.send(data
);
54 setRequestHeader: function(header
, value
) {
55 // We have to auto-set this at the end, since we don't know how long the
56 // nonce is when added to the data.
57 if (this.csrf_isPost
&& header
== "Content-length") {
58 this.csrf_purportedLength
= value
;
61 return this.csrf_setRequestHeader(header
, value
);
63 csrf_setRequestHeader: function(header
, value
) {
64 return this.csrf
.setRequestHeader(header
, value
);
68 return this.csrf
.abort();
70 getAllResponseHeaders: function() {
71 return this.csrf
.getAllResponseHeaders();
73 getResponseHeader: function(header
) {
74 return this.csrf
.getResponseHeader(header
);
79 CsrfMagic
.prototype._updateProps = function() {
80 this.readyState
= this.csrf
.readyState
;
81 if (this.readyState
== 4) {
82 this.responseText
= this.csrf
.responseText
;
83 this.responseXML
= this.csrf
.responseXML
;
84 this.status
= this.csrf
.status
;
85 this.statusText
= this.csrf
.statusText
;
88 CsrfMagic
.process = function(base
) {
89 var prepend
= csrfMagicName
+ '=' + csrfMagicToken
;
90 if (base
) return prepend
+ '&' + base
;
93 // callback function for when everything on the page has loaded
94 CsrfMagic
.end = function() {
95 // This rewrites forms AGAIN, so in case buffering didn't work this
97 forms
= document
.getElementsByTagName('form');
98 for (var i
= 0; i
< forms
.length
; i
++) {
100 if (form
.method
.toUpperCase() !== 'POST') continue;
101 if (form
.elements
[csrfMagicName
]) continue;
102 var input
= document
.createElement('input');
103 input
.setAttribute('name', csrfMagicName
);
104 input
.setAttribute('value', csrfMagicToken
);
105 input
.setAttribute('type', 'hidden');
106 form
.appendChild(input
);
110 // Sets things up for Mozilla/Opera/nice browsers
111 // We very specifically match against Internet Explorer, since they haven't
112 // implemented prototypes correctly yet.
113 if (window
.XMLHttpRequest
&& window
.XMLHttpRequest
.prototype && '\v' != 'v') {
114 var x
= XMLHttpRequest
.prototype;
115 var c
= CsrfMagic
.prototype;
117 // Save the original functions
118 x
.csrf_open
= x
.open
;
119 x
.csrf_send
= x
.send
;
120 x
.csrf_setRequestHeader
= x
.setRequestHeader
;
122 // Notice that CsrfMagic is itself an instantiatable object, but only
123 // open, send and setRequestHeader are necessary as decorators.
126 x
.setRequestHeader
= c
.setRequestHeader
;
128 // The only way we can do this is by modifying a library you have been
129 // using. We support YUI, script.aculo.us, prototype, MooTools,
130 // jQuery, Ext and Dojo.
132 // jQuery didn't implement a new XMLHttpRequest function, so we have
133 // to do this the hard way.
134 jQuery
.csrf_ajax
= jQuery
.ajax
;
135 jQuery
.ajax = function( s
) {
136 if (s
.type
&& s
.type
.toUpperCase() == 'POST') {
137 s
= jQuery
.extend(true, s
, jQuery
.extend(true, {}, jQuery
.ajaxSettings
, s
));
138 if ( s
.data
&& s
.processData
&& typeof s
.data
!= "string" ) {
139 s
.data
= jQuery
.param(s
.data
);
141 s
.data
= CsrfMagic
.process(s
.data
);
143 return jQuery
.csrf_ajax( s
);
146 if (window
.Prototype
) {
147 // This works for script.aculo.us too
148 Ajax
.csrf_getTransport
= Ajax
.getTransport
;
149 Ajax
.getTransport = function() {
150 return new CsrfMagic(Ajax
.csrf_getTransport());
153 if (window
.MooTools
) {
154 Browser
.csrf_Request
= Browser
.Request
;
155 Browser
.Request = function () {
156 return new CsrfMagic(Browser
.csrf_Request());
161 YAHOO
.util
.Connect
.csrf_createXhrObject
= YAHOO
.util
.Connect
.createXhrObject
;
162 YAHOO
.util
.Connect
.createXhrObject = function (transaction
) {
163 obj
= YAHOO
.util
.Connect
.csrf_createXhrObject(transaction
);
164 obj
.conn
= new CsrfMagic(obj
.conn
);
169 // Ext can use other js libraries as loaders, so it has to come last
170 // Ext's implementation is pretty identical to Yahoo's, but we duplicate
171 // it for comprehensiveness's sake.
172 Ext
.lib
.Ajax
.csrf_createXhrObject
= Ext
.lib
.Ajax
.createXhrObject
;
173 Ext
.lib
.Ajax
.createXhrObject = function (transaction
) {
174 obj
= Ext
.lib
.Ajax
.csrf_createXhrObject(transaction
);
175 obj
.conn
= new CsrfMagic(obj
.conn
);
180 // NOTE: this doesn't work with latest dojo
181 dojo
.csrf__xhrObj
= dojo
._xhrObj
;
182 dojo
._xhrObj = function () {
183 return new CsrfMagic(dojo
.csrf__xhrObj());