removed output-disable in dbms-output fetching procedure
[mediawiki.git] / js2 / mwEmbed / libMwApi / mw.proxy.js
blobc3cd6da032713a013ac23946e4c4585c615e1e32
1 /*
2
3 * Api proxy system
5 * Supports cross domain uploading, and api actions for a approved set of domains.
7 * The framework /will/ support a request approval system for per-user domain approval
8 * and a central blacklisting of domains controlled by the site 
10 *  Flow outline below:  
11
12 * Domain A (lets say en.wiki)  
13 *       invokes add-media-wizard and wants to upload to domain B ( commons.wiki )
14
15 *       Domain A loads iframe to domain B ? with request param to to insert from Domain A
16 *               Domain B checks list of approved domains for (Domain A) & checks the user is logged in ( and if the same account name ) 
17 *                       if user is not logged in 
18 *                               a _link_ to Domain B to new window login page is given
19 *                       if user has not approved domain and (Domain A) is not pre-approved 
20 *                               a new page link is generated with a approve domain request
21 *                                       if user approves domain it goes into their User:{username}/apiProxyDomains.js config
22 *               If Domain A is approved we then: 
23 *                       loads a "push" and "pull" iframe back to Domain A 
24                                 (Domain B can change the #hash values of these children thereby proxy the data)  
25 *       Domain A now gets the iframe "loaded" callback a does a initial echo to confirm cross domain proxy
26 *               echo sends "echo" to push and (Domain A) js passes the echo onto the "pull"
27 *       Domain A now sends api requests to the iframe "push" child and gets results from the iframe "pull"
28 *               api actions happen with status updates and everything and we can reuse existing api interface code  
29
30 * if the browser supports it we can pass msgs with the postMessage  API
31 * http://ejohn.org/blog/cross-window-messaging/
33 * @@todo it would be nice if this supported multiple proxy targets (ie to a bright widgets future) 
37 loadGM( {
38         "mwe-setting-up-proxy" : "Setting up proxy...",
39         "mwe-re-try" : "Retry API request",
40         "mwe-re-trying" : "Retrying API request...",
41         "mwe-proxy-not-ready" : "Proxy is not configured",
42         "mwe-please-login" : "You are not <a target=\"_new\" href=\"$1\">logged in<\/a> on $2 or mwEmbed has not been enabled. Resolve the issue, and then retry the request.",
43         "mwe-remember-loging" : "General security reminder: Only login to web sites when your address bar displays that site's address."
44 } );
46 ( function( $ ) {
47         /**
48          * Base API Proxy object
49          *       
50          */
51         $.proxy = { };
52         
53         /**
54          * The client setup function: 
55          */
56         $.proxy.client = function( pConf, conf ) {
57                 var _this = this;
58                 // Do client setup: 
59                 if ( pConf.server_frame )
60                         $.proxy.server_frame = pConf.server_frame;
61                 
62                 if ( pConf.client_frame_path ) {
63                         $.proxy.client_frame_path = pConf.client_frame_path;
64                 } else {
65                         // Set to default mwEmbed iframe path:
66                         $.proxy.client_frame_path =  wgScriptPath + '/js2/mwEmbed/libMwApi/NestedCallbackIframe.html';
67                 }
68                                 
69                 if ( mw.parseUri( $.proxy.server_frame ).host ==  mw.parseUri( document.URL ).host ) {
70                         js_log( "Error: trying to proxy local domain? " );
71                         return false;
72                 }
73                 return true;
74         }
75         // Set the frameProxy Flag: 
76         var frameProxyOk = false;
77         
78         /** 
79         * doFrameProxy
80         *       Writes an iframe with a hashed value of the requestQuery
81         */
82         $.proxy.doFrameProxy = function( requestQuery ) {
83                 var hashPack = {
84                         'cd': mw.parseUri( document.URL ).host,
85                         'cfp': $.proxy.client_frame_path,
86                         'req': requestQuery
87                 }
88                 js_log( "Do frame proxy request on src: \n" + $.proxy.server_frame + "\n" +
89                                         JSON.stringify(  requestQuery ) );
90                 // we can't update src's so we have to remove and add all the time :(
91                 // @@todo we should support frame msg system 
92                 $j( '#frame_proxy' ).remove();
93                 $j( 'body' ).append( '<iframe style="display:none" id="frame_proxy" name="frame_proxy" ' +
94                                 'src="' + $.proxy.server_frame +
95                                  '#' + escape( JSON.stringify( hashPack ) ) +
96                                  '"></iframe>' );
97                                  
98                 // add an onLoad hook: 
99                 $j( '#frame_proxy' ).get( 0 ).onload = function() {
100                         // add a 5 second timeout for setting up the nested child callback (after page load) 
101                         setTimeout( function() {
102                                 if ( !frameProxyOk ) {
103                                         // we timmed out no api proxy (should make sure the user is "logged in")
104                                         js_log( "Error:: api proxy timeout are we logged in? mwEmbed is on?" );
105                                         $.proxy.proxyNotReadyDialog();
106                                 }
107                         }, 5000 );
108                 }
109         }
110         var lastApiReq = { };
111         $.proxy.proxyNotReadyDialog = function() {
112                 var buttons = { };
113                 buttons[ gM( 'mwe-re-try' ) ] = function() {
114                         $j.addLoaderDialog( gM( 'mwe-re-trying' ) );
115                         $.proxy.doFrameProxy( lastApiReq );
116                 }
117                 buttons[ gM( 'mwe-cancel' ) ] = function() {
118                         $j.closeLoaderDialog();
119                 }
120                 var pUri =  mw.parseUri( $.proxy.server_frame );
121                 
122                 // FIXME we should have a Hosted iframe once we deploy mwEmbed on the servers.
123                 // A hosted iframe would be much faster since than a normal page view 
124                 
125                 var login_url = pUri.protocol + '://' + pUri.host;
126                 login_url += pUri.path.replace( 'MediaWiki:ApiProxy', 'Special:UserLogin' );
127                 
128                 $j.addDialog( 
129                         gM( 'mwe-proxy-not-ready' ), 
130                         gM( 'mwe-please-login', [ login_url, pUri.host] ) +
131                                 '<p style="font-size:small">' + 
132                                         gM( 'mwe-remember-loging' ) + 
133                                 '</p>',
134                         buttons
135                 )
136         }
137         /* 
138         * doRequest 
139         * Takes a requestQuery, executes the query and then calls the callback
140         */
141         $.proxy.doRequest = function( requestQuery, callback ) {
142                 js_log( "doRequest:: " + JSON.stringify( reqObj ) );
143                 lastApiReq = reqObj;
144                 // setup the callback:
145                 $.proxy.callback = callback;
146                 // do the proxy req:
147                 $.proxy.doFrameProxy( requestQuery );
148         }
149         /**
150          * The nested iframe action that passes its result back up to the top frame instance     
151          */
152         $.proxy.nested = function( hashResult ) {
153                 // Close the loader if present: 
154                 $j.closeLoaderDialog();
155                 js_log( '$.proxy.nested callback :: ' + unescape( hashResult ) );
156                 frameProxyOk = true;
157                 
158                 // Try to parse the hash result: 
159                 try {
160                         var rObj = JSON.parse( unescape( hashResult ) );
161                 } catch ( e ) {
162                         js_log( "Error could not parse hashResult" );
163                 }
164                 
165                 // Special callback to frameProxyOk flag 
166                 // (only used to test the proxy connection)   
167                 if ( rObj.state == 'ok' )
168                         return ;
169                 
170                 // Pass the callback:
171                 $.proxy.callback( rObj );
172         }
173         /**
174          * The server handles the actual proxy 
175          * it adds child frames pointing to the parent "blank" frames
176          * 
177          * This is (Domain B) in the above described setup
178          */
179         $.proxy.server = function( pConf, callback ) {
180                 /* clear the body of any html */
181                 $j( 'body' ).html( 'proxy setup' );
182                 
183                 // read the anchor action from the requesting url
184                 var jmsg = unescape( mw.parseUri( document.URL ).anchor );
185                 try {
186                         var aObj = JSON.parse( jmsg );
187                 } catch ( e ) {
188                         js_log( "ProxyServer:: could not parse anchor" );
189                 }
190                 if ( !aObj.cd ) {
191                         js_log( "Error: no client domain provided " );
192                         return false;
193                 }
194                 
195                 js_log( "Setup server on: "  + mw.parseUri( document.URL ).host +
196                         ' client from: ' + aObj.cd +
197                         ' to nested target: ' + aObj.cfp );
198                 
199                 // Make sure we are logged in 
200                 // (its a normal mediaWiki page so all site vars should be defined)             
201                 if ( !wgUserName ) {
202                         js_log( 'Error Not logged in' );
203                         return false;
204                 }
205                         
206                 var domain =  aObj.cd;
207                 var nested_frame_src = 'http://' + aObj.cd + aObj.cfp;
208                 // Check the master whitelist
209                 for ( var i in pConf.master_whitelist ) {
210                         if ( domain ==  pConf.master_whitelist[ i ] ) {
211                                 // Do the request:                      
212                                 return doNestedProxy( aObj.req );
213                         }
214                 }
215                 // Check master blacklist
216                 for ( var i in pConf.master_blacklist ) {
217                         if ( domain == pConf.master_blacklist ) {
218                                 js_log( 'domain: ' + domain + ' is blacklisted' );
219                                 return false;
220                         }
221                 }
222                 // FIXME grab the users whitelist for our current domain                                
223                 /*var local_api = wgScriptPath + '/index' + wgScriptExtension + '?title=' +
224                                 'User:' + wgUserName + '/apiProxyDomainList.js' +
225                                 '&action=raw&smaxage=0&gen=js';
226                 $j.get( local_api, function( data ){
227                         debugger;
228                 });*/
229                 
230                 // if still not found: 
231                 js_log( "domain " + domain + " not approved" );
232                 
233                 // FIXME :: offer the user the ability to "approve" requested domain save to
234                 // their user/ apiProxyDomainList.js page
236                 function doNestedProxy( reqObj ) {
237                         js_log( "doNestedProxy to: " + nested_frame_src );
239                         // Do a quick response to establish the proxy is working
240                         // ( before we actually run the api-request )  
241                         doNestedFrame ( 'nested_ok' , { 'state':'ok' } );
242                         
243                         var outputhash = escape( JSON.stringify( reqObj ) );
245                         // Add some api stuff: 
246                         reqObj[ 'format' ] = 'json';
248                         // Process the api request
249                         $j.post( wgScriptPath + '/api' + wgScriptExtension,
250                                 reqObj,
251                                 function( data ) {                                      
252                                         // Put it into the nested frame hash string: 
253                                         doNestedFrame( 'nested_push', JSON.parse( data ) );
254                                 }
255                         );
256                 }
257                 // Add the doNestedFrame iframe: 
258                 function doNestedFrame( nestname, resultObj ) {
259                         $j( '#nested_push' ).remove();
260                         // Setup the nested proxy that points back to top domain:                       
261                         $j( 'body' ).append( '<iframe id="nested_push" name="nested_push" ' +
262                                 'src="' + nested_frame_src +
263                                 '#' + escape( JSON.stringify( resultObj ) ) +
264                                 '"></iframe>' );
265                 }
266         }
267         
268 } )( window.mw );