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
12 * Domain A (lets say en.wiki)
13 * invokes add-media-wizard and wants to upload to domain B ( commons.wiki )
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
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)
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."
48 * Base API Proxy object
54 * The client setup function:
56 $.proxy.client = function( pConf, conf ) {
59 if ( pConf.server_frame )
60 $.proxy.server_frame = pConf.server_frame;
62 if ( pConf.client_frame_path ) {
63 $.proxy.client_frame_path = pConf.client_frame_path;
65 // Set to default mwEmbed iframe path:
66 $.proxy.client_frame_path = wgScriptPath + '/js2/mwEmbed/libMwApi/NestedCallbackIframe.html';
69 if ( mw.parseUri( $.proxy.server_frame ).host == mw.parseUri( document.URL ).host ) {
70 js_log( "Error: trying to proxy local domain? " );
75 // Set the frameProxy Flag:
76 var frameProxyOk = false;
80 * Writes an iframe with a hashed value of the requestQuery
82 $.proxy.doFrameProxy = function( requestQuery ) {
84 'cd': mw.parseUri( document.URL ).host,
85 'cfp': $.proxy.client_frame_path,
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 ) ) +
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();
110 var lastApiReq = { };
111 $.proxy.proxyNotReadyDialog = function() {
113 buttons[ gM( 'mwe-re-try' ) ] = function() {
114 $j.addLoaderDialog( gM( 'mwe-re-trying' ) );
115 $.proxy.doFrameProxy( lastApiReq );
117 buttons[ gM( 'mwe-cancel' ) ] = function() {
118 $j.closeLoaderDialog();
120 var pUri = mw.parseUri( $.proxy.server_frame );
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
125 var login_url = pUri.protocol + '://' + pUri.host;
126 login_url += pUri.path.replace( 'MediaWiki:ApiProxy', 'Special:UserLogin' );
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' ) +
139 * Takes a requestQuery, executes the query and then calls the callback
141 $.proxy.doRequest = function( requestQuery, callback ) {
142 js_log( "doRequest:: " + JSON.stringify( reqObj ) );
144 // setup the callback:
145 $.proxy.callback = callback;
147 $.proxy.doFrameProxy( requestQuery );
150 * The nested iframe action that passes its result back up to the top frame instance
152 $.proxy.nested = function( hashResult ) {
153 // Close the loader if present:
154 $j.closeLoaderDialog();
155 js_log( '$.proxy.nested callback :: ' + unescape( hashResult ) );
158 // Try to parse the hash result:
160 var rObj = JSON.parse( unescape( hashResult ) );
162 js_log( "Error could not parse hashResult" );
165 // Special callback to frameProxyOk flag
166 // (only used to test the proxy connection)
167 if ( rObj.state == 'ok' )
170 // Pass the callback:
171 $.proxy.callback( rObj );
174 * The server handles the actual proxy
175 * it adds child frames pointing to the parent "blank" frames
177 * This is (Domain B) in the above described setup
179 $.proxy.server = function( pConf, callback ) {
180 /* clear the body of any html */
181 $j( 'body' ).html( 'proxy setup' );
183 // read the anchor action from the requesting url
184 var jmsg = unescape( mw.parseUri( document.URL ).anchor );
186 var aObj = JSON.parse( jmsg );
188 js_log( "ProxyServer:: could not parse anchor" );
191 js_log( "Error: no client domain provided " );
195 js_log( "Setup server on: " + mw.parseUri( document.URL ).host +
196 ' client from: ' + aObj.cd +
197 ' to nested target: ' + aObj.cfp );
199 // Make sure we are logged in
200 // (its a normal mediaWiki page so all site vars should be defined)
202 js_log( 'Error Not logged in' );
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 ] ) {
212 return doNestedProxy( aObj.req );
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' );
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 ){
230 // if still not found:
231 js_log( "domain " + domain + " not approved" );
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' } );
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,
252 // Put it into the nested frame hash string:
253 doNestedFrame( 'nested_push', JSON.parse( data ) );
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 ) ) +