1 Components.utils.import("resource://gre/modules/XPCOMUtils.jsm");
3 // These components are intended to handle remote XUL requests
5 // FIRST, if we don't have bind, add a workaroundish thing.
6 // If we stop caring about firefox/xulrunner < 4 we can ditch this.
7 if (!Function.prototype.bind) {
8 Function.prototype.bind = function (oThis) {
9 if (typeof this !== "function") {
10 // closest thing possible to the ECMAScript 5 internal IsCallable function
11 throw new TypeError("Function.prototype.bind - what is trying to be bound is not callable");
14 var aArgs = Array.prototype.slice.call(arguments, 1),
16 fNOP = function () {},
17 fBound = function () {
18 return fToBind.apply(this instanceof fNOP && oThis
21 aArgs.concat(Array.prototype.slice.call(arguments)));
24 fNOP.prototype = this.prototype;
25 fBound.prototype = new fNOP();
31 // First we define a channel wrapper.
32 // We need this to handle redirects properly.
33 // Things we care about:
34 // Intercepting the URI
35 // Intercepting things that get a channel in callbacks
37 // We define what we need for those.
38 // wrap_channel(_mode) handles all of the other fun we then have to worry about.
40 function oilsChannel() {
43 oilsChannel.prototype = {
44 QueryInterface: XPCOMUtils.generateQI([
45 Components.interfaces.nsIChannel,
46 Components.interfaces.nsIHttpChannel,
47 Components.interfaces.nsIHttpChannelInternal,
48 Components.interfaces.nsIRequest,
49 Components.interfaces.nsIInterfaceRequestor,
50 Components.interfaces.nsIChannelEventSink,
51 Components.interfaces.nsIProgressEventSink,
52 Components.interfaces.nsIHttpEventSink,
53 Components.interfaces.nsIStreamListener,
54 Components.interfaces.nsIAuthPrompt2,
55 Components.interfaces.nsIRequestObserver,
56 Components.interfaces.nsIUploadChannel
58 _internal_channel: null,
60 _redirect_notificationCallbacks: null,
61 _redirect_streamListener: null,
62 wrap_channel: function(channel, uri) {
63 this._internal_channel = channel;
64 this._internal_uri = uri;
65 this.wrap_channel_mode(channel.QueryInterface(Components.interfaces.nsIRequest)); // Basic request stuff
66 this.wrap_channel_mode(channel.QueryInterface(Components.interfaces.nsIChannel)); // Basic channel stuff
67 this.wrap_channel_mode(channel.QueryInterface(Components.interfaces.nsIHttpChannel)); // Basic HTTP stuff
68 this.wrap_channel_mode(channel.QueryInterface(Components.interfaces.nsIHttpChannelInternal)); // To pretend we are internal-ish
69 this.wrap_channel_mode(channel.QueryInterface(Components.interfaces.nsIUploadChannel)); // To make POST work
71 wrap_channel_mode: function(channel) {
72 for( var item in channel ) {
74 if(this[item] || typeof this[item] != 'undefined')
76 } catch (E) { continue; }
80 isfunc = (/[Ff]unction/.test(typeof channel[item])) || typeof channel[item].bind != 'undefined';
84 this[item] = (function(thisItem){ return channel[thisItem].bind(channel); })(item);
88 this.__defineGetter__(item, (function(thisItem) { return function() { return channel[thisItem]; } })(item));
91 this.__defineSetter__(item, (function(thisItem) { return function(val) { return channel[thisItem] = val; } })(item));
97 get notificationCallbacks() {
98 // for a number of reasons we don't admit to re-writing these things here
99 return this._redirect_notificationCallbacks;
101 set notificationCallbacks(val) {
103 this._internal_channel.notificationCallbacks = this.QueryInterface(Components.interfaces.nsIInterfaceRequestor);
104 this._redirect_notificationCallbacks = val;
106 this._internal_channel.notificationCallbacks = null;
107 this._redirect_notificationCallbacks = null;
109 this._internal_channel.notificationCallbacks = val;
112 return this._internal_uri;
114 asyncOpen: function(aListener, aContext) {
115 this._redirect_streamListener = aListener;
116 this._internal_channel.asyncOpen(this.QueryInterface(Components.interfaces.nsIStreamListener), aContext);
119 return this._internal_channel.open();
121 getInterface: function(aIID) {
123 if (this.QueryInterface(aIID) && this._redirect_notificationCallbacks.getInterface(aIID)) {
124 return this.QueryInterface(aIID);
127 // Pass onto the forwarding target as a last resort.
128 return this._redirect_notificationCallbacks.getInterface(aIID);
130 onChannelRedirect: function(oldChannel, newChannel, flags) {
131 var redirect = this._redirect_notificationCallbacks.getInterface(Components.interfaces.nsIChannelEventSink);
132 return redirect.onChannelRedirect(this.QueryInterface(Components.interfaces.nsIChannel), newChannel, flags);
134 asyncOnChannelRedirect: function(oldChannel, newChannel, flags, callback) {
135 var redirect = this._redirect_notificationCallbacks.getInterface(Components.interfaces.nsIChannelEventSink);
136 return redirect.asyncOnChannelRedirect(this.QueryInterface(Components.interfaces.nsIChannel), newChannel, flags, callback);
138 onProgress: function(aRequest, aContext, aProgress, aProgressMax) {
139 var redirect = this._redirect_notificationCallbacks.getInterface(Components.interfaces.nsIProgressEventSink);
140 return redirect.onProgress(this.QueryInterface(Components.interfaces.nsIRequest), aContext, aProgress, aProgressMax);
142 onStatus: function(aRequest, aContext, aStatus, aStatusArg) {
143 var redirect = this._redirect_notificationCallbacks.getInterface(Components.interfaces.nsIProgressEventSink);
144 return redirect.onStatus(this.QueryInterface(Components.interfaces.nsIRequest), aContext, aStatus, aStatusArg);
146 onRedirect: function(httpChannel, newChannel) {
147 var redirect = this._redirect_notificationCallbacks.getInterface(Components.interfaces.nsIHttpEventSink);
148 return redirect.onRedirect(this.QueryInterface(Components.interfaces.nsIHttpChannel), newChannel);
150 asyncPromptAuth: function(aChannel, aCallback, aContext, level, authInfo) {
151 var redirect = this._redirect_notificationCallbacks.getInterface(Components.interfaces.nsIAuthPrompt2);
152 return redirect.asyncPromptAuth(this.QueryInterface(Components.interfaces.nsIChannel), aCallback, aContext, level, authInfo);
154 promptAuth: function(aChannel, level, authInfo) {
155 var redirect = this._redirect_notificationCallbacks.getInterface(Components.interfaces.nsIAuthPrompt2);
156 return redirect.promptAuth(this.QueryInterface(Components.interfaces.nsIChannel), level, authInfo);
158 onStartRequest: function(aRequest, aContext) {
159 if ( aRequest == this._internal_channel )
160 this._redirect_streamListener.onStartRequest(this.QueryInterface(Components.interfaces.nsIRequest), aContext);
162 this._redirect_streamListener.onStartRequest(aRequest, aContext);
164 onStopRequest: function(aRequest, aContext, aStatusCode) {
165 if ( aRequest == this._internal_channel )
166 this._redirect_streamListener.onStopRequest(this.QueryInterface(Components.interfaces.nsIRequest), aContext, aStatusCode);
168 this._redirect_streamListener.onStopRequest(aRequest, aContext, aStatusCode);
170 onDataAvailable: function(aRequest, aContext, aInputStream, aOffset, aCount) {
171 if ( aRequest == this._internal_channel )
172 this._redirect_streamListener.onDataAvailable(this.QueryInterface(Components.interfaces.nsIRequest), aContext, aInputStream, aOffset, aCount);
174 this._redirect_streamListener.onDataAvailable(aRequest, aContext, aInputStream, aOffset, aCount);
178 // This handles the actual security-related elements of the protocol wrapper
180 function oilsProtocol() {}
182 oilsProtocol.prototype = {
183 _system_principal: null,
185 protocolflags: Components.interfaces.nsIProtocolHandler.URI_DANGEROUS_TO_LOAD |
186 Components.interfaces.nsIProtocolHandler.URI_INHERITS_SECURITY_CONTEXT,
187 newURI: function(aSpec, aOriginCharset, aBaseURI) {
188 var new_url = Components.classes["@mozilla.org/network/standard-url;1"].createInstance(Components.interfaces.nsIStandardURL);
189 new_url.init(1, -1, aSpec, aOriginCharset, aBaseURI);
190 return new_url.QueryInterface(Components.interfaces.nsIURI);
192 newChannel: function(aURI) {
193 var ios = Components.classes["@mozilla.org/network/io-service;1"].getService(Components.interfaces.nsIIOService);
195 var data_cache = Components.classes["@open-ils.org/openils_data_cache;1"].getService().wrappedJSObject.data;
198 host = data_cache.server_unadorned;
201 // To allow elevated permissions on a specific host for selfcheck purposes change this from null.
202 // This is intended for installing an extension on Firefox specifically for selfcheck purposes
203 // NOTE: I honestly don't know how dangerous this might be, but I can't imagine it is worse than the previous "grant the domain permissions to do anything" model.
208 return ios.newChannel("about:blank", null, null); // Bad input. Not really sure what to do. Returning a dummy channel does prevent a crash, though!
209 var chunk = aURI.spec.replace(/^oils:\/\/[^\/]*\//,'');
210 var channel = ios.newChannel("https://" + host + "/" + chunk, null, null).QueryInterface(Components.interfaces.nsIHttpChannel);
211 channel.setRequestHeader("OILS-Wrapper", "true", false);
212 // If we have a search/pref lib, set them too!
213 if (data_cache.search_lib && data_cache.search_lib != null)
214 channel.setRequestHeader("OILS-Search-Lib", data_cache.search_lib, false);
215 if (data_cache.pref_lib && data_cache.pref_lib != null)
216 channel.setRequestHeader("OILS-Pref-Lib", data_cache.pref_lib, false);
217 if(this._system_principal == null) {
218 // We don't have the owner?
219 var chrome_service = Components.classesByID['{61ba33c0-3031-11d3-8cd0-0060b0fc14a3}'].getService().QueryInterface(Components.interfaces.nsIProtocolHandler);
220 var chrome_uri = chrome_service.newURI("chrome://open_ils_staff_client/content/main/main.xul", null, null);
221 var chrome_channel = chrome_service.newChannel(chrome_uri);
222 this._system_principal = chrome_channel.owner;
223 var chrome_request = chrome_channel.QueryInterface(Components.interfaces.nsIRequest);
224 chrome_request.cancel(0x804b0002);
226 if (this._system_principal) channel.owner = this._system_principal;
227 // This is a workaround.
228 // We can't wrap all the time because XMLHttpRequests are busted by us doing so.
229 // If we don't wrap, redirects in the Template Toolkit OPAC break out of the protocol.
230 // So wrap only if we are in the catalog!
231 if (aURI.path.match(/^\/eg\/[ok]pac/)) {
232 var outChannel = new oilsChannel();
233 outChannel.wrap_channel(channel, aURI);
238 allowPort: function(aPort, aScheme) {
241 classDescription: "OILS Protocol Handler",
242 contractID: "@mozilla.org/network/protocol;1?name=oils",
243 classID: Components.ID('{51d35450-5e59-11e1-b86c-0800200c9a66}'),
244 QueryInterface: XPCOMUtils.generateQI([Components.interfaces.nsIProtocolHandler]),
247 if (XPCOMUtils.generateNSGetFactory)
248 var NSGetFactory = XPCOMUtils.generateNSGetFactory([oilsProtocol]);
250 var NSGetModule = XPCOMUtils.generateNSGetModule([oilsProtocol]);