!import
1 //@line 37 "/home/visbrero/mnt/roisin/rev_control/hg/mozilla/toolkit/components/url-classifier/src/nsUrlClassifierLib.js"
2
3 // We wastefully reload the same JS files across components. This puts all
4 // the common JS files used by safebrowsing and url-classifier into a
5 // single component.
6
7 const Cc = Components.classes;
8 const Ci = Components.interfaces;
9 const G_GDEBUG = false;
10
11 //@line 36 "/home/visbrero/mnt/roisin/rev_control/hg/mozilla/toolkit/components/url-classifier/content/moz/lang.js"
12
13
14 /**
15 * lang.js - Some missing JavaScript language features
16 */
17
18 /**
19 * Partially applies a function to a particular "this object" and zero or
20 * more arguments. The result is a new function with some arguments of the first
21 * function pre-filled and the value of |this| "pre-specified".
22 *
23 * Remaining arguments specified at call-time are appended to the pre-
24 * specified ones.
25 *
26 * Usage:
27 * var barMethBound = BindToObject(myFunction, myObj, "arg1", "arg2");
28 * barMethBound("arg3", "arg4");
29 *
30 * @param fn {string} Reference to the function to be bound
31 *
32 * @param self {object} Specifies the object which |this| should point to
33 * when the function is run. If the value is null or undefined, it will default
34 * to the global object.
35 *
36 * @returns {function} A partially-applied form of the speficied function.
37 */
BindToObject
38 function BindToObject(fn, self, opt_args) {
39 var boundargs = fn.boundArgs_ || [];
40 boundargs = boundargs.concat(Array.slice(arguments, 2, arguments.length));
41
42 if (fn.boundSelf_)
43 self = fn.boundSelf_;
44 if (fn.boundFn_)
45 fn = fn.boundFn_;
46
anon:47:14
47 var newfn = function() {
48 // Combine the static args and the new args into one big array
49 var args = boundargs.concat(Array.slice(arguments));
50 return fn.apply(self, args);
51 }
52
53 newfn.boundArgs_ = boundargs;
54 newfn.boundSelf_ = self;
55 newfn.boundFn_ = fn;
56
57 return newfn;
58 }
59
60 /**
61 * Inherit the prototype methods from one constructor into another.
62 *
63 * Usage:
64 *
65 * function ParentClass(a, b) { }
66 * ParentClass.prototype.foo = function(a) { }
67 *
68 * function ChildClass(a, b, c) {
69 * ParentClass.call(this, a, b);
70 * }
71 *
72 * ChildClass.inherits(ParentClass);
73 *
74 * var child = new ChildClass("a", "b", "see");
75 * child.foo(); // works
76 *
77 * In addition, a superclass' implementation of a method can be invoked
78 * as follows:
79 *
80 * ChildClass.prototype.foo = function(a) {
81 * ChildClass.superClass_.foo.call(this, a);
82 * // other code
83 * };
84 */
inherits
85 Function.prototype.inherits = function(parentCtor) {
anon:86:17
86 var tempCtor = function(){};
87 tempCtor.prototype = parentCtor.prototype;
88 this.superClass_ = parentCtor.prototype;
89 this.prototype = new tempCtor();
90 }
91 //@line 36 "/home/visbrero/mnt/roisin/rev_control/hg/mozilla/toolkit/components/url-classifier/content/moz/preferences.js"
92
93
94 // Class for manipulating preferences. Aside from wrapping the pref
95 // service, useful functionality includes:
96 //
97 // - abstracting prefobserving so that you can observe preferences
98 // without implementing nsIObserver
99 //
100 // - getters that return a default value when the pref doesn't exist
101 // (instead of throwing)
102 //
103 // - get-and-set getters
104 //
105 // Example:
106 //
107 // var p = new PROT_Preferences();
108 // dump(p.getPref("some-true-pref")); // shows true
109 // dump(p.getPref("no-such-pref", true)); // shows true
110 // dump(p.getPref("no-such-pref", null)); // shows null
111 //
112 // function observe(prefThatChanged) {
113 // dump("Pref changed: " + prefThatChanged);
114 // };
115 //
116 // p.addObserver("somepref", observe);
117 // p.setPref("somepref", true); // dumps
118 // p.removeObserver("somepref", observe);
119 //
120 // TODO: should probably have the prefobserver pass in the new and old
121 // values
122
123 // TODO(tc): Maybe remove this class and just call natively since we're no
124 // longer an extension.
125
126 /**
127 * A class that wraps the preferences service.
128 *
129 * @param opt_startPoint A starting point on the prefs tree to resolve
130 * names passed to setPref and getPref.
131 *
132 * @param opt_useDefaultPranch Set to true to work against the default
133 * preferences tree instead of the profile one.
134 *
135 * @constructor
136 */
G_Preferences
137 function G_Preferences(opt_startPoint, opt_getDefaultBranch) {
138 this.debugZone = "prefs";
139 this.observers_ = {};
140 this.getDefaultBranch_ = !!opt_getDefaultBranch;
141
142 this.startPoint_ = opt_startPoint || null;
143 }
144
145 G_Preferences.setterMap_ = { "string": "setCharPref",
146 "boolean": "setBoolPref",
147 "number": "setIntPref" };
148
149 G_Preferences.getterMap_ = {};
150 G_Preferences.getterMap_[Ci.nsIPrefBranch.PREF_STRING] = "getCharPref";
151 G_Preferences.getterMap_[Ci.nsIPrefBranch.PREF_BOOL] = "getBoolPref";
152 G_Preferences.getterMap_[Ci.nsIPrefBranch.PREF_INT] = "getIntPref";
153
get_prefs_
154 G_Preferences.prototype.__defineGetter__('prefs_', function() {
155 var prefs;
156 var prefSvc = Cc["@mozilla.org/preferences-service;1"]
157 .getService(Ci.nsIPrefService);
158
159 if (this.getDefaultBranch_) {
160 prefs = prefSvc.getDefaultBranch(this.startPoint_);
161 } else {
162 prefs = prefSvc.getBranch(this.startPoint_);
163 }
164
165 // QI to prefs in case we want to add observers
166 prefs.QueryInterface(Ci.nsIPrefBranchInternal);
167 return prefs;
168 });
169
170 /**
171 * Stores a key/value in a user preference. Valid types for val are string,
172 * boolean, and number. Complex values are not yet supported (but feel free to
173 * add them!).
174 */
setPref
175 G_Preferences.prototype.setPref = function(key, val) {
176 var datatype = typeof(val);
177
178 if (datatype == "number" && (val % 1 != 0)) {
179 throw new Error("Cannot store non-integer numbers in preferences.");
180 }
181
182 var meth = G_Preferences.setterMap_[datatype];
183
184 if (!meth) {
185 throw new Error("Pref datatype {" + datatype + "} not supported.");
186 }
187
188 return this.prefs_[meth](key, val);
189 }
190
191 /**
192 * Retrieves a user preference. Valid types for the value are the same as for
193 * setPref. If the preference is not found, opt_default will be returned
194 * instead.
195 */
getPref
196 G_Preferences.prototype.getPref = function(key, opt_default) {
197 var type = this.prefs_.getPrefType(key);
198
199 // zero means that the specified pref didn't exist
200 if (type == Ci.nsIPrefBranch.PREF_INVALID) {
201 return opt_default;
202 }
203
204 var meth = G_Preferences.getterMap_[type];
205
206 if (!meth) {
207 throw new Error("Pref datatype {" + type + "} not supported.");
208 }
209
210 // If a pref has been cleared, it will have a valid type but won't
211 // be gettable, so this will throw.
212 try {
213 return this.prefs_[meth](key);
214 } catch(e) {
215 return opt_default;
216 }
217 }
218
219 /**
220 * Delete a preference.
221 *
222 * @param which Name of preference to obliterate
223 */
clearPref
224 G_Preferences.prototype.clearPref = function(which) {
225 try {
226 // This throws if the pref doesn't exist, which is fine because a
227 // non-existent pref is cleared
228 this.prefs_.clearUserPref(which);
229 } catch(e) {}
230 }
231
232 /**
233 * Add an observer for a given pref.
234 *
235 * @param which String containing the pref to listen to
236 * @param callback Function to be called when the pref changes. This
237 * function will receive a single argument, a string
238 * holding the preference name that changed
239 */
addObserver
240 G_Preferences.prototype.addObserver = function(which, callback) {
241 // Need to store the observer we create so we can eventually unregister it
242 if (!this.observers_[which])
243 this.observers_[which] = { callbacks: [], observers: [] };
244
245 /* only add an observer if the callback hasn't been registered yet */
246 if (this.observers_[which].callbacks.indexOf(callback) == -1) {
247 var observer = new G_PreferenceObserver(callback);
248 this.observers_[which].callbacks.push(callback);
249 this.observers_[which].observers.push(observer);
250 this.prefs_.addObserver(which, observer, false /* strong reference */);
251 }
252 }
253
254 /**
255 * Remove an observer for a given pref.
256 *
257 * @param which String containing the pref to stop listening to
258 * @param callback Function to remove as an observer
259 */
removeObserver
260 G_Preferences.prototype.removeObserver = function(which, callback) {
261 var ix = this.observers_[which].callbacks.indexOf(callback);
262 G_Assert(this, ix != -1, "Tried to unregister a nonexistant observer");
263 this.observers_[which].callbacks.splice(ix, 1);
264 var observer = this.observers_[which].observers.splice(ix, 1)[0];
265 this.prefs_.removeObserver(which, observer);
266 }
267
268 /**
269 * Remove all preference observers registered through this object.
270 */
removeAllObservers
271 G_Preferences.prototype.removeAllObservers = function() {
272 for (var which in this.observers_) {
273 for each (var observer in this.observers_[which].observers) {
274 this.prefs_.removeObserver(which, observer);
275 }
276 }
277 this.observers_ = {};
278 }
279
280 /**
281 * Helper class that knows how to observe preference changes and
282 * invoke a callback when they do
283 *
284 * @constructor
285 * @param callback Function to call when the preference changes
286 */
G_PreferenceObserver
287 function G_PreferenceObserver(callback) {
288 this.debugZone = "prefobserver";
289 this.callback_ = callback;
290 }
291
292 /**
293 * Invoked by the pref system when a preference changes. Passes the
294 * message along to the callback.
295 *
296 * @param subject The nsIPrefBranch that changed
297 * @param topic String "nsPref:changed" (aka
298 * NS_PREFBRANCH_PREFCHANGE_OBSERVER_ID -- but where does it
299 * live???)
300 * @param data Name of the pref that changed
301 */
observe
302 G_PreferenceObserver.prototype.observe = function(subject, topic, data) {
303 G_Debug(this, "Observed pref change: " + data);
304 this.callback_(data);
305 }
306
307 /**
308 * XPCOM cruft
309 *
310 * @param iid Interface id of the interface the caller wants
311 */
QueryInterface
312 G_PreferenceObserver.prototype.QueryInterface = function(iid) {
313 if (iid.equals(Ci.nsISupports) ||
314 iid.equals(Ci.nsIObserver) ||
315 iid.equals(Ci.nsISupportsWeakReference))
316 return this;
317 throw Components.results.NS_ERROR_NO_INTERFACE;
318 }
319
320 //@line 38 "/home/visbrero/mnt/roisin/rev_control/hg/mozilla/toolkit/components/url-classifier/content/moz/debug.js"
321
322 //@line 868 "/home/visbrero/mnt/roisin/rev_control/hg/mozilla/toolkit/components/url-classifier/content/moz/debug.js"
323
324 // Stubs for the debugging aids scattered through this component.
325 // They will be expanded if you compile yourself a debug build.
326
G_Debug
327 function G_Debug(who, msg) { }
G_Assert
328 function G_Assert(who, condition, msg) { }
G_Error
329 function G_Error(who, msg) { }
__noSuchMethod__
330 var G_debugService = { __noSuchMethod__: function() { } };
331
332 //@line 36 "/home/visbrero/mnt/roisin/rev_control/hg/mozilla/toolkit/components/url-classifier/content/moz/alarm.js"
333
334
335 // An Alarm fires a callback after a certain amount of time, or at
336 // regular intervals. It's a convenient replacement for
337 // setTimeout/Interval when you don't want to bind to a specific
338 // window.
339 //
340 // The ConditionalAlarm is an Alarm that cancels itself if its callback
341 // returns a value that type-converts to true.
342 //
343 // Example:
344 //
345 // function foo() { dump('hi'); };
346 // new G_Alarm(foo, 10*1000); // Fire foo in 10 seconds
347 // new G_Alarm(foo, 10*1000, true /*repeat*/); // Fire foo every 10 seconds
348 // new G_Alarm(foo, 10*1000, true, 7); // Fire foo every 10 seconds
349 // // seven times
350 // new G_ConditionalAlarm(foo, 1000, true); // Fire every sec until foo()==true
351 //
352 // // Fire foo every 10 seconds until foo returns true or until it fires seven
353 // // times, whichever happens first.
354 // new G_ConditionalAlarm(foo, 10*1000, true /*repeating*/, 7);
355 //
356 // TODO: maybe pass an isFinal flag to the callback if they opted to
357 // set maxTimes and this is the last iteration?
358
359
360 /**
361 * Set an alarm to fire after a given amount of time, or at specific
362 * intervals.
363 *
364 * @param callback Function to call when the alarm fires
365 * @param delayMS Number indicating the length of the alarm period in ms
366 * @param opt_repeating Boolean indicating whether this should fire
367 * periodically
368 * @param opt_maxTimes Number indicating a maximum number of times to
369 * repeat (obviously only useful when opt_repeating==true)
370 */
G_Alarm
371 function G_Alarm(callback, delayMS, opt_repeating, opt_maxTimes) {
372 this.debugZone = "alarm";
373 this.callback_ = callback;
374 this.repeating_ = !!opt_repeating;
375 this.timer_ = Cc["@mozilla.org/timer;1"].createInstance(Ci.nsITimer);
376 var type = opt_repeating ?
377 this.timer_.TYPE_REPEATING_SLACK :
378 this.timer_.TYPE_ONE_SHOT;
379 this.maxTimes_ = opt_maxTimes ? opt_maxTimes : null;
380 this.nTimes_ = 0;
381
382 this.observerServiceObserver_ = new G_ObserverServiceObserver(
383 'xpcom-shutdown',
384 BindToObject(this.cancel, this));
385
386 // Ask the timer to use nsITimerCallback (.notify()) when ready
387 this.timer_.initWithCallback(this, delayMS, type);
388 }
389
390 /**
391 * Cancel this timer
392 */
cancel
393 G_Alarm.prototype.cancel = function() {
394 if (!this.timer_) {
395 return;
396 }
397
398 this.timer_.cancel();
399 // Break circular reference created between this.timer_ and the G_Alarm
400 // instance (this)
401 this.timer_ = null;
402 this.callback_ = null;
403
404 // We don't need the shutdown observer anymore
405 this.observerServiceObserver_.unregister();
406 }
407
408 /**
409 * Invoked by the timer when it fires
410 *
411 * @param timer Reference to the nsITimer which fired (not currently
412 * passed along)
413 */
notify
414 G_Alarm.prototype.notify = function(timer) {
415 // fire callback and save results
416 var ret = this.callback_();
417
418 // If they've given us a max number of times to fire, enforce it
419 this.nTimes_++;
420 if (this.repeating_ &&
421 typeof this.maxTimes_ == "number"
422 && this.nTimes_ >= this.maxTimes_) {
423 this.cancel();
424 } else if (!this.repeating_) {
425 // Clear out the callback closure for TYPE_ONE_SHOT timers
426 this.cancel();
427 }
428 // We don't cancel/cleanup timers that repeat forever until either
429 // xpcom-shutdown occurs or cancel() is called explicitly.
430
431 return ret;
432 }
433
setDelay
434 G_Alarm.prototype.setDelay = function(delay) {
435 this.timer_.delay = delay;
436 }
437
438 /**
439 * XPCOM cruft
440 */
QueryInterface
441 G_Alarm.prototype.QueryInterface = function(iid) {
442 if (iid.equals(Components.interfaces.nsISupports) ||
443 iid.equals(Components.interfaces.nsITimerCallback))
444 return this;
445
446 throw Components.results.NS_ERROR_NO_INTERFACE;
447 }
448
449
450 /**
451 * An alarm with the additional property that it cancels itself if its
452 * callback returns true.
453 *
454 * For parameter documentation, see G_Alarm
455 */
G_ConditionalAlarm
456 function G_ConditionalAlarm(callback, delayMS, opt_repeating, opt_maxTimes) {
457 G_Alarm.call(this, callback, delayMS, opt_repeating, opt_maxTimes);
458 this.debugZone = "conditionalalarm";
459 }
460
461 G_ConditionalAlarm.inherits(G_Alarm);
462
463 /**
464 * Invoked by the timer when it fires
465 *
466 * @param timer Reference to the nsITimer which fired (not currently
467 * passed along)
468 */
notify
469 G_ConditionalAlarm.prototype.notify = function(timer) {
470 // Call G_Alarm::notify
471 var rv = G_Alarm.prototype.notify.call(this, timer);
472
473 if (this.repeating_ && rv) {
474 G_Debug(this, "Callback of a repeating alarm returned true; cancelling.");
475 this.cancel();
476 }
477 }
478 //@line 36 "/home/visbrero/mnt/roisin/rev_control/hg/mozilla/toolkit/components/url-classifier/content/moz/cryptohasher.js"
479
480
481 // A very thin wrapper around nsICryptoHash. It's not strictly
482 // necessary, but makes the code a bit cleaner and gives us the
483 // opportunity to verify that our implementations give the results that
484 // we expect, for example if we have to interoperate with a server.
485 //
486 // The digest* methods reset the state of the hasher, so it's
487 // necessary to call init() explicitly after them.
488 //
489 // Works only in Firefox 1.5+.
490 //
491 // IMPORTANT NOTE: Due to https://bugzilla.mozilla.org/show_bug.cgi?id=321024
492 // you cannot use the cryptohasher before app-startup. The symptom of doing
493 // so is a segfault in NSS.
494
495 /**
496 * Instantiate a new hasher. You must explicitly call init() before use!
497 */
G_CryptoHasher
498 function G_CryptoHasher() {
499 this.debugZone = "cryptohasher";
500 this.hasher_ = null;
501 }
502
503 G_CryptoHasher.algorithms = {
504 MD2: Ci.nsICryptoHash.MD2,
505 MD5: Ci.nsICryptoHash.MD5,
506 SHA1: Ci.nsICryptoHash.SHA1,
507 SHA256: Ci.nsICryptoHash.SHA256,
508 SHA384: Ci.nsICryptoHash.SHA384,
509 SHA512: Ci.nsICryptoHash.SHA512,
510 };
511
512 /**
513 * Initialize the hasher. This function must be called after every call
514 * to one of the digest* methods.
515 *
516 * @param algorithm Constant from G_CryptoHasher.algorithms specifying the
517 * algorithm this hasher will use
518 */
init
519 G_CryptoHasher.prototype.init = function(algorithm) {
520 var validAlgorithm = false;
521 for (var alg in G_CryptoHasher.algorithms)
522 if (algorithm == G_CryptoHasher.algorithms[alg])
523 validAlgorithm = true;
524
525 if (!validAlgorithm)
526 throw new Error("Invalid algorithm: " + algorithm);
527
528 this.hasher_ = Cc["@mozilla.org/security/hash;1"]
529 .createInstance(Ci.nsICryptoHash);
530 this.hasher_.init(algorithm);
531 }
532
533 /**
534 * Update the hash's internal state with input given in a string. Can be
535 * called multiple times for incrementeal hash updates.
536 *
537 * @param input String containing data to hash.
538 */
updateFromString
539 G_CryptoHasher.prototype.updateFromString = function(input) {
540 if (!this.hasher_)
541 throw new Error("You must initialize the hasher first!");
542
543 var stream = Cc['@mozilla.org/io/string-input-stream;1']
544 .createInstance(Ci.nsIStringInputStream);
545 stream.setData(input, input.length);
546 this.updateFromStream(stream);
547 }
548
549 /**
550 * Update the hash's internal state with input given in an array. Can be
551 * called multiple times for incremental hash updates.
552 *
553 * @param input Array containing data to hash.
554 */
updateFromArray
555 G_CryptoHasher.prototype.updateFromArray = function(input) {
556 if (!this.hasher_)
557 throw new Error("You must initialize the hasher first!");
558
559 this.hasher_.update(input, input.length);
560 }
561
562 /**
563 * Update the hash's internal state with input given in a stream. Can be
564 * called multiple times from incremental hash updates.
565 */
updateFromStream
566 G_CryptoHasher.prototype.updateFromStream = function(stream) {
567 if (!this.hasher_)
568 throw new Error("You must initialize the hasher first!");
569
570 if (stream.available())
571 this.hasher_.updateFromStream(stream, stream.available());
572 }
573
574 /**
575 * @returns The hash value as a string (sequence of 8-bit values)
576 */
digestRaw
577 G_CryptoHasher.prototype.digestRaw = function() {
578 var digest = this.hasher_.finish(false /* not b64 encoded */);
579 this.hasher_ = null;
580 return digest;
581 }
582
583 /**
584 * @returns The hash value as a base64-encoded string
585 */
digestBase64
586 G_CryptoHasher.prototype.digestBase64 = function() {
587 var digest = this.hasher_.finish(true /* b64 encoded */);
588 this.hasher_ = null;
589 return digest;
590 }
591
592 /**
593 * @returns The hash value as a hex-encoded string
594 */
digestHex
595 G_CryptoHasher.prototype.digestHex = function() {
596 var raw = this.digestRaw();
597 return this.toHex_(raw);
598 }
599
600 /**
601 * Converts a sequence of values to a hex-encoded string. The input is a
602 * a string, so you can stick 16-bit values in each character.
603 *
604 * @param str String to conver to hex. (Often this is just a sequence of
605 * 16-bit values)
606 *
607 * @returns String containing the hex representation of the input
608 */
toHex_
609 G_CryptoHasher.prototype.toHex_ = function(str) {
610 var hexchars = '0123456789ABCDEF';
611 var hexrep = new Array(str.length * 2);
612
613 for (var i = 0; i < str.length; ++i) {
614 hexrep[i * 2] = hexchars.charAt((str.charCodeAt(i) >> 4) & 15);
615 hexrep[i * 2 + 1] = hexchars.charAt(str.charCodeAt(i) & 15);
616 }
617 return hexrep.join('');
618 }
619
620 //@line 36 "/home/visbrero/mnt/roisin/rev_control/hg/mozilla/toolkit/components/url-classifier/content/moz/observer.js"
621
622
623 // A couple of classes to simplify creating observers.
624 //
625 // // Example1:
626 //
627 // function doSomething() { ... }
628 // var observer = new G_ObserverWrapper(topic, doSomething);
629 // someObj.addObserver(topic, observer);
630 //
631 // // Example2:
632 //
633 // function doSomething() { ... }
634 // new G_ObserverServiceObserver("profile-after-change",
635 // doSomething,
636 // true /* run only once */);
637
638
639 /**
640 * This class abstracts the admittedly simple boilerplate required of
641 * an nsIObserver. It saves you the trouble of implementing the
642 * indirection of your own observe() function.
643 *
644 * @param topic String containing the topic the observer will filter for
645 *
646 * @param observeFunction Reference to the function to call when the
647 * observer fires
648 *
649 * @constructor
650 */
G_ObserverWrapper
651 function G_ObserverWrapper(topic, observeFunction) {
652 this.debugZone = "observer";
653 this.topic_ = topic;
654 this.observeFunction_ = observeFunction;
655 }
656
657 /**
658 * XPCOM
659 */
QueryInterface
660 G_ObserverWrapper.prototype.QueryInterface = function(iid) {
661 if (iid.equals(Ci.nsISupports) || iid.equals(Ci.nsIObserver))
662 return this;
663 throw Components.results.NS_ERROR_NO_INTERFACE;
664 }
665
666 /**
667 * Invoked by the thingy being observed
668 */
observe
669 G_ObserverWrapper.prototype.observe = function(subject, topic, data) {
670 if (topic == this.topic_)
671 this.observeFunction_(subject, topic, data);
672 }
673
674
675 /**
676 * This class abstracts the admittedly simple boilerplate required of
677 * observing an observerservice topic. It implements the indirection
678 * required, and automatically registers to hear the topic.
679 *
680 * @param topic String containing the topic the observer will filter for
681 *
682 * @param observeFunction Reference to the function to call when the
683 * observer fires
684 *
685 * @param opt_onlyOnce Boolean indicating if the observer should unregister
686 * after it has fired
687 *
688 * @constructor
689 */
G_ObserverServiceObserver
690 function G_ObserverServiceObserver(topic, observeFunction, opt_onlyOnce) {
691 this.debugZone = "observerserviceobserver";
692 this.topic_ = topic;
693 this.observeFunction_ = observeFunction;
694 this.onlyOnce_ = !!opt_onlyOnce;
695
696 this.observer_ = new G_ObserverWrapper(this.topic_,
697 BindToObject(this.observe_, this));
698 this.observerService_ = Cc["@mozilla.org/observer-service;1"]
699 .getService(Ci.nsIObserverService);
700 this.observerService_.addObserver(this.observer_, this.topic_, false);
701 }
702
703 /**
704 * Unregister the observer from the observerservice
705 */
unregister
706 G_ObserverServiceObserver.prototype.unregister = function() {
707 this.observerService_.removeObserver(this.observer_, this.topic_);
708 this.observerService_ = null;
709 }
710
711 /**
712 * Invoked by the observerservice
713 */
observe_
714 G_ObserverServiceObserver.prototype.observe_ = function(subject, topic, data) {
715 this.observeFunction_(subject, topic, data);
716 if (this.onlyOnce_)
717 this.unregister();
718 }
719
720 //@line 36 "/home/visbrero/mnt/roisin/rev_control/hg/mozilla/toolkit/components/url-classifier/content/moz/protocol4.js"
721
722
723 // A helper class that knows how to parse from and serialize to
724 // protocol4. This is a simple, historical format used by some Google
725 // interfaces, for example the Toolbar (i.e., ancient services).
726 //
727 // Protocol4 consists of a newline-separated sequence of name/value
728 // pairs (strings). Each line consists of the name, the value length,
729 // and the value itself, all separated by colons. Example:
730 //
731 // foo:6:barbaz\n
732 // fritz:33:issickofdynamicallytypedlanguages\n
733
734
735 /**
736 * This class knows how to serialize/deserialize maps to/from their
737 * protocol4 representation.
738 *
739 * @constructor
740 */
G_Protocol4Parser
741 function G_Protocol4Parser() {
742 this.debugZone = "protocol4";
743
744 this.protocol4RegExp_ = new RegExp("([^:]+):\\d+:(.*)$");
745 this.newlineRegExp_ = new RegExp("(\\r)?\\n");
746 }
747
748 /**
749 * Create a map from a protocol4 string. Silently skips invalid lines.
750 *
751 * @param text String holding the protocol4 representation
752 *
753 * @returns Object as an associative array with keys and values
754 * given in text. The empty object is returned if none
755 * are parsed.
756 */
parse
757 G_Protocol4Parser.prototype.parse = function(text) {
758
759 var response = {};
760 if (!text)
761 return response;
762
763 // Responses are protocol4: (repeated) name:numcontentbytes:content\n
764 var lines = text.split(this.newlineRegExp_);
765 for (var i = 0; i < lines.length; i++)
766 if (this.protocol4RegExp_.exec(lines[i]))
767 response[RegExp.$1] = RegExp.$2;
768
769 return response;
770 }
771
772 /**
773 * Create a protocol4 string from a map (object). Throws an error on
774 * an invalid input.
775 *
776 * @param map Object as an associative array with keys and values
777 * given as strings.
778 *
779 * @returns text String holding the protocol4 representation
780 */
serialize
781 G_Protocol4Parser.prototype.serialize = function(map) {
782 if (typeof map != "object")
783 throw new Error("map must be an object");
784
785 var text = "";
786 for (var key in map) {
787 if (typeof map[key] != "string")
788 throw new Error("Keys and values must be strings");
789
790 text += key + ":" + map[key].length + ":" + map[key] + "\n";
791 }
792
793 return text;
794 }
795
796 //@line 53 "/home/visbrero/mnt/roisin/rev_control/hg/mozilla/toolkit/components/url-classifier/src/nsUrlClassifierLib.js"
797
798 /* ***** BEGIN LICENSE BLOCK *****
799 * Version: MPL 1.1/GPL 2.0/LGPL 2.1
800 *
801 * The contents of this file are subject to the Mozilla Public License Version
802 * 1.1 (the "License"); you may not use this file except in compliance with
803 * the License. You may obtain a copy of the License at
804 * http://www.mozilla.org/MPL/
805 *
806 * Software distributed under the License is distributed on an "AS IS" basis,
807 * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
808 * for the specific language governing rights and limitations under the
809 * License.
810 *
811 * The Original Code is Google Safe Browsing.
812 *
813 * The Initial Developer of the Original Code is Google Inc.
814 * Portions created by the Initial Developer are Copyright (C) 2006
815 * the Initial Developer. All Rights Reserved.
816 *
817 * Contributor(s):
818 * Tony Chang <tc@google.com> (original author)
819 *
820 * Alternatively, the contents of this file may be used under the terms of
821 * either the GNU General Public License Version 2 or later (the "GPL"), or
822 * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
823 * in which case the provisions of the GPL or the LGPL are applicable instead
824 * of those above. If you wish to allow use of your version of this file only
825 * under the terms of either the GPL or the LGPL, and not to allow others to
826 * use your version of this file under the terms of the MPL, indicate your
827 * decision by deleting the provisions above and replace them with the notice
828 * and other provisions required by the GPL or the LGPL. If you do not delete
829 * the provisions above, a recipient may use your version of this file under
830 * the terms of any one of the MPL, the GPL or the LGPL.
831 *
832 * ***** END LICENSE BLOCK ***** */
833
834 // This implements logic for stopping requests if the server starts to return
835 // too many errors. If we get MAX_ERRORS errors in ERROR_PERIOD minutes, we
836 // back off for TIMEOUT_INCREMENT minutes. If we get another error
837 // immediately after we restart, we double the timeout and add
838 // TIMEOUT_INCREMENT minutes, etc.
839 //
840 // This is similar to the logic used by the search suggestion service.
841
842 // HTTP responses that count as an error. We also include any 5xx response
843 // as an error.
844 const HTTP_FOUND = 302;
845 const HTTP_SEE_OTHER = 303;
846 const HTTP_TEMPORARY_REDIRECT = 307;
847
848 /**
849 * @param maxErrors Number the number of errors needed to trigger backoff
850 * @param errorPeriod Number time (ms) in which maxErros have to occur to
851 * trigger the backoff behavior
852 * @param maxRequests Number the number of requests needed to trigger backoff
853 * @param requestPeriod Number time (ms) in which maxRequests have to occur to
854 * trigger the backoff behavior
855 * @param timeoutIncrement Number time (ms) the starting timeout period
856 * we double this time for consecutive errors
857 * @param maxTimeout Number time (ms) maximum timeout period
858 */
RequestBackoff
859 function RequestBackoff(maxErrors, errorPeriod,
860 maxRequests, requestPeriod,
861 timeoutIncrement, maxTimeout) {
862 this.MAX_ERRORS_ = maxErrors;
863 this.ERROR_PERIOD_ = errorPeriod;
864 this.MAX_REQUESTS_ = maxRequests;
865 this.REQUEST_PERIOD_ = requestPeriod;
866 this.TIMEOUT_INCREMENT_ = timeoutIncrement;
867 this.MAX_TIMEOUT_ = maxTimeout;
868
869 // Queue of ints keeping the time of all requests
870 this.requestTimes_ = [];
871
872 // Queue of ints keeping the time of errors.
873 this.errorTimes_ = [];
874 this.errorTimeout_ = 0;
875 this.nextRequestTime_ = 0;
876 this.backoffTriggered_ = false;
877 }
878
879 /**
880 * Reset the object for reuse.
881 */
reset
882 RequestBackoff.prototype.reset = function() {
883 this.errorTimes_ = [];
884 this.errorTimeout_ = 0;
885 this.nextRequestTime_ = 0;
886 this.backoffTriggered_ = false;
887 }
888
889 /**
890 * Check to see if we can make a request.
891 */
canMakeRequest
892 RequestBackoff.prototype.canMakeRequest = function() {
893 var now = Date.now();
894 if (now <= this.nextRequestTime_) {
895 return false;
896 }
897
898 return (this.requestTimes_.length < this.MAX_REQUESTS_ ||
899 (now - this.requestTimes_[0]) > this.REQUEST_PERIOD_);
900 }
901
noteRequest
902 RequestBackoff.prototype.noteRequest = function() {
903 var now = Date.now();
904 this.requestTimes_.push(now);
905
906 // We only care about keeping track of MAX_REQUESTS
907 if (this.requestTimes_.length > this.MAX_REQUESTS_)
908 this.requestTimes_.shift();
909 }
910
911 /**
912 * Notify this object of the last server response. If it's an error,
913 */
noteServerResponse
914 RequestBackoff.prototype.noteServerResponse = function(status) {
915 if (this.isErrorStatus(status)) {
916 var now = Date.now();
917 this.errorTimes_.push(now);
918
919 // We only care about keeping track of MAX_ERRORS
920 if (this.errorTimes_.length > this.MAX_ERRORS_)
921 this.errorTimes_.shift();
922
923 // See if we hit the backoff case
924 // This either means we hit MAX_ERRORS in ERROR_PERIOD
925 // *or* we were already in a backoff state, in which case we
926 // increase our timeout.
927 if ((this.errorTimes_.length == this.MAX_ERRORS_ &&
928 now - this.errorTimes_[0] < this.ERROR_PERIOD_)
929 || this.backoffTriggered_) {
930 this.errorTimeout_ = (this.errorTimeout_ * 2) + this.TIMEOUT_INCREMENT_;
931 this.errorTimeout_ = Math.min(this.errorTimeout_, this.MAX_TIMEOUT_);
932 this.nextRequestTime_ = now + this.errorTimeout_;
933 this.backoffTriggered_ = true;
934 }
935 } else {
936 // Reset error timeout, allow requests to go through, and switch out
937 // of backoff state.
938 this.errorTimeout_ = 0;
939 this.nextRequestTime_ = 0;
940 this.backoffTriggered_ = false;
941 }
942 }
943
944 /**
945 * We consider 302, 303, 307, 4xx, and 5xx http responses to be errors.
946 * @param status Number http status
947 * @return Boolean true if we consider this http status an error
948 */
isErrorStatus
949 RequestBackoff.prototype.isErrorStatus = function(status) {
950 return ((400 <= status && status <= 599) ||
951 HTTP_FOUND == status ||
952 HTTP_SEE_OTHER == status ||
953 HTTP_TEMPORARY_REDIRECT == status);
954 }
955
956 //@line 36 "/home/visbrero/mnt/roisin/rev_control/hg/mozilla/toolkit/components/url-classifier/content/url-crypto-key-manager.js"
957
958
959 // This file implements the tricky business of managing the keys for our
960 // URL encryption. The protocol is:
961 //
962 // - Server generates secret key K_S
963 // - Client starts up and requests a new key K_C from the server via HTTPS
964 // - Server generates K_C and WrappedKey, which is K_C encrypted with K_S
965 // - Server resonse with K_C and WrappedKey
966 // - When client wants to encrypt a URL, it encrypts it with K_C and sends
967 // the encrypted URL along with WrappedKey
968 // - Server decrypts WrappedKey with K_S to get K_C, and the URL with K_C
969 //
970 // This is, however, trickier than it sounds for two reasons. First,
971 // we want to keep the number of HTTPS requests to an aboslute minimum
972 // (like 1 or 2 per browser session). Second, the HTTPS request at
973 // startup might fail, for example the user might be offline or a URL
974 // fetch might need to be issued before the HTTPS request has
975 // completed.
976 //
977 // We implement the following policy:
978 //
979 // - Firefox will issue at most two HTTPS getkey requests per session
980 // - Firefox will issue one HTTPS getkey request at startup if more than 24
981 // hours has passed since the last getkey request.
982 // - Firefox will serialize to disk any key it gets
983 // - Firefox will fall back on this serialized key until it has a
984 // fresh key
985 // - The front-end can respond with a flag in a lookup request that tells
986 // the client to re-key. Firefox will issue a new HTTPS getkey request
987 // at this time if it has only issued one before
988
989 // We store the user key in this file. The key can be used to verify signed
990 // server updates.
991 const kKeyFilename = "urlclassifierkey3.txt";
992
993 /**
994 * A key manager for UrlCrypto. There should be exactly one of these
995 * per appplication, and all UrlCrypto's should share it. This is
996 * currently implemented by having the manager attach itself to the
997 * UrlCrypto's prototype at startup. We could've opted for a global
998 * instead, but I like this better, even though it is spooky action
999 * at a distance.
1000 * XXX: Should be an XPCOM service
1001 *
1002 * @param opt_keyFilename String containing the name of the
1003 * file we should serialize keys to/from. Used
1004 * mostly for testing.
1005 *
1006 * @param opt_testing Boolean indicating whether we are testing. If we
1007 * are, then we skip trying to read the old key from
1008 * file and automatically trying to rekey; presumably
1009 * the tester will drive these manually.
1010 *
1011 * @constructor
1012 */
PROT_UrlCryptoKeyManager
1013 function PROT_UrlCryptoKeyManager(opt_keyFilename, opt_testing) {
1014 this.debugZone = "urlcryptokeymanager";
1015 this.testing_ = !!opt_testing;
1016 this.clientKey_ = null; // Base64-encoded, as fetched from server
1017 this.clientKeyArray_ = null; // Base64-decoded into an array of numbers
1018 this.wrappedKey_ = null; // Opaque websafe base64-encoded server key
1019 this.rekeyTries_ = 0;
1020 this.updating_ = false;
1021
1022 // Don't do anything until keyUrl_ is set.
1023 this.keyUrl_ = null;
1024
1025 this.keyFilename_ = opt_keyFilename ?
1026 opt_keyFilename : kKeyFilename;
1027
1028 this.onNewKey_ = null;
1029
1030 // Convenience properties
1031 this.MAX_REKEY_TRIES = PROT_UrlCryptoKeyManager.MAX_REKEY_TRIES;
1032 this.CLIENT_KEY_NAME = PROT_UrlCryptoKeyManager.CLIENT_KEY_NAME;
1033 this.WRAPPED_KEY_NAME = PROT_UrlCryptoKeyManager.WRAPPED_KEY_NAME;
1034
1035 if (!this.testing_) {
1036 this.maybeLoadOldKey();
1037 }
1038 }
1039
1040 // Do ***** NOT ***** set this higher; HTTPS is expensive
1041 PROT_UrlCryptoKeyManager.MAX_REKEY_TRIES = 2;
1042
1043 // Base pref for keeping track of when we updated our key.
1044 // We store the time as seconds since the epoch.
1045 PROT_UrlCryptoKeyManager.NEXT_REKEY_PREF = "urlclassifier.keyupdatetime.";
1046
1047 // Once every 30 days (interval in seconds)
1048 PROT_UrlCryptoKeyManager.KEY_MIN_UPDATE_TIME = 30 * 24 * 60 * 60;
1049
1050 // These are the names the server will respond with in protocol4 format
1051 PROT_UrlCryptoKeyManager.CLIENT_KEY_NAME = "clientkey";
1052 PROT_UrlCryptoKeyManager.WRAPPED_KEY_NAME = "wrappedkey";
1053
1054 /**
1055 * Called to get ClientKey
1056 * @returns urlsafe-base64-encoded client key or null if we haven't gotten one.
1057 */
getClientKey
1058 PROT_UrlCryptoKeyManager.prototype.getClientKey = function() {
1059 return this.clientKey_;
1060 }
1061
1062 /**
1063 * Called by a UrlCrypto to get the current K_C
1064 *
1065 * @returns Array of numbers making up the client key or null if we
1066 * have no key
1067 */
getClientKeyArray
1068 PROT_UrlCryptoKeyManager.prototype.getClientKeyArray = function() {
1069 return this.clientKeyArray_;
1070 }
1071
1072 /**
1073 * Called by a UrlCrypto to get WrappedKey
1074 *
1075 * @returns Opaque base64-encoded WrappedKey or null if we haven't
1076 * gotten one
1077 */
getWrappedKey
1078 PROT_UrlCryptoKeyManager.prototype.getWrappedKey = function() {
1079 return this.wrappedKey_;
1080 }
1081
1082 /**
1083 * Change the key url. When we do this, we go ahead and rekey.
1084 * @param keyUrl String
1085 */
setKeyUrl
1086 PROT_UrlCryptoKeyManager.prototype.setKeyUrl = function(keyUrl) {
1087 // If it's the same key url, do nothing.
1088 if (keyUrl == this.keyUrl_)
1089 return;
1090
1091 this.keyUrl_ = keyUrl;
1092 this.rekeyTries_ = 0;
1093
1094 // Check to see if we should make a new getkey request.
1095 var prefs = new G_Preferences(PROT_UrlCryptoKeyManager.NEXT_REKEY_PREF);
1096 var nextRekey = prefs.getPref(this.getPrefName_(this.keyUrl_), 0);
1097 if (nextRekey < parseInt(Date.now() / 1000, 10)) {
1098 this.reKey();
1099 }
1100 }
1101
1102 /**
1103 * Given a url, return the pref value to use (pref contains last update time).
1104 * We basically use the url up until query parameters. This avoids duplicate
1105 * pref entries as version number changes over time.
1106 * @param url String getkey URL
1107 */
getPrefName_
1108 PROT_UrlCryptoKeyManager.prototype.getPrefName_ = function(url) {
1109 var queryParam = url.indexOf("?");
1110 if (queryParam != -1) {
1111 return url.substring(0, queryParam);
1112 }
1113 return url;
1114 }
1115
1116 /**
1117 * Tell the manager to re-key. For safety, this method still obeys the
1118 * max-tries limit. Clients should generally use maybeReKey() if they
1119 * want to try a re-keying: it's an error to call reKey() after we've
1120 * hit max-tries, but not an error to call maybeReKey().
1121 */
reKey
1122 PROT_UrlCryptoKeyManager.prototype.reKey = function() {
1123 if (this.updating_) {
1124 G_Debug(this, "Already re-keying, ignoring this request");
1125 return true;
1126 }
1127
1128 if (this.rekeyTries_ > this.MAX_REKEY_TRIES)
1129 throw new Error("Have already rekeyed " + this.rekeyTries_ + " times");
1130
1131 this.rekeyTries_++;
1132
1133 G_Debug(this, "Attempting to re-key");
1134 // If the keyUrl isn't set, we don't do anything.
1135 if (!this.testing_ && this.keyUrl_) {
1136 (new PROT_XMLFetcher()).get(this.keyUrl_,
1137 BindToObject(this.onGetKeyResponse, this));
1138 this.updating_ = true;
1139
1140 // Calculate the next time we're allowed to re-key.
1141 var prefs = new G_Preferences(PROT_UrlCryptoKeyManager.NEXT_REKEY_PREF);
1142 var nextRekey = parseInt(Date.now() / 1000, 10)
1143 + PROT_UrlCryptoKeyManager.KEY_MIN_UPDATE_TIME;
1144 prefs.setPref(this.getPrefName_(this.keyUrl_), nextRekey);
1145 }
1146 }
1147
1148 /**
1149 * Try to re-key if we haven't already hit our limit. It's OK to call
1150 * this method multiple times, even if we've already tried to rekey
1151 * more than the max. It will simply refuse to do so.
1152 *
1153 * @returns Boolean indicating if it actually issued a rekey request (that
1154 * is, if we haven' already hit the max)
1155 */
maybeReKey
1156 PROT_UrlCryptoKeyManager.prototype.maybeReKey = function() {
1157 if (this.rekeyTries_ > this.MAX_REKEY_TRIES) {
1158 G_Debug(this, "Not re-keying; already at max");
1159 return false;
1160 }
1161
1162 this.reKey();
1163 return true;
1164 }
1165
1166 /**
1167 * Drop the existing set of keys. Resets the rekeyTries variable to
1168 * allow a rekey to succeed.
1169 */
dropKey
1170 PROT_UrlCryptoKeyManager.prototype.dropKey = function() {
1171 this.rekeyTries_ = 0;
1172 this.replaceKey_(null, null);
1173 }
1174
1175 /**
1176 * @returns Boolean indicating if we have a key we can use
1177 */
hasKey
1178 PROT_UrlCryptoKeyManager.prototype.hasKey = function() {
1179 return this.clientKey_ != null && this.wrappedKey_ != null;
1180 }
1181
1182 /**
1183 * Set a new key and serialize it to disk.
1184 *
1185 * @param clientKey String containing the base64-encoded client key
1186 * we wish to use
1187 *
1188 * @param wrappedKey String containing the opaque base64-encoded WrappedKey
1189 * the server gave us (i.e., K_C encrypted with K_S)
1190 */
replaceKey_
1191 PROT_UrlCryptoKeyManager.prototype.replaceKey_ = function(clientKey,
1192 wrappedKey) {
1193 if (this.clientKey_)
1194 G_Debug(this, "Replacing " + this.clientKey_ + " with " + clientKey);
1195
1196 this.clientKey_ = clientKey;
1197 this.clientKeyArray_ = Array.map(atob(clientKey),
anon:1198:35
1198 function(c) { return c.charCodeAt(0); });
1199 this.wrappedKey_ = wrappedKey;
1200
1201 this.serializeKey_(this.clientKey_, this.wrappedKey_);
1202
1203 if (this.onNewKey_) {
1204 this.onNewKey_();
1205 }
1206 }
1207
1208 /**
1209 * Try to write the key to disk so we can fall back on it. Fail
1210 * silently if we cannot. The keys are serialized in protocol4 format.
1211 *
1212 * @returns Boolean indicating whether we succeeded in serializing
1213 */
serializeKey_
1214 PROT_UrlCryptoKeyManager.prototype.serializeKey_ = function() {
1215
1216 var map = {};
1217 map[this.CLIENT_KEY_NAME] = this.clientKey_;
1218 map[this.WRAPPED_KEY_NAME] = this.wrappedKey_;
1219
1220 try {
1221
1222 var keyfile = Cc["@mozilla.org/file/directory_service;1"]
1223 .getService(Ci.nsIProperties)
1224 .get("ProfD", Ci.nsILocalFile); /* profile directory */
1225 keyfile.append(this.keyFilename_);
1226
1227 if (!this.clientKey_ || !this.wrappedKey_) {
1228 keyfile.remove(true);
1229 return;
1230 }
1231
1232 var data = (new G_Protocol4Parser()).serialize(map);
1233
1234 try {
1235 var stream = Cc["@mozilla.org/network/file-output-stream;1"]
1236 .createInstance(Ci.nsIFileOutputStream);
1237 stream.init(keyfile,
1238 0x02 | 0x08 | 0x20 /* PR_WRONLY | PR_CREATE_FILE | PR_TRUNCATE */,
1239 -1 /* default perms */, 0 /* no special behavior */);
1240 stream.write(data, data.length);
1241 } finally {
1242 stream.close();
1243 }
1244 return true;
1245
1246 } catch(e) {
1247
1248 G_Error(this, "Failed to serialize new key: " + e);
1249 return false;
1250
1251 }
1252 }
1253
1254 /**
1255 * Invoked when we've received a protocol4 response to our getkey
1256 * request. Try to parse it and set this key as the new one if we can.
1257 *
1258 * @param responseText String containing the protocol4 getkey response
1259 */
onGetKeyResponse
1260 PROT_UrlCryptoKeyManager.prototype.onGetKeyResponse = function(responseText) {
1261
1262 var response = (new G_Protocol4Parser).parse(responseText);
1263 var clientKey = response[this.CLIENT_KEY_NAME];
1264 var wrappedKey = response[this.WRAPPED_KEY_NAME];
1265
1266 this.updating_ = false;
1267
1268 if (response && clientKey && wrappedKey) {
1269 G_Debug(this, "Got new key from: " + responseText);
1270 this.replaceKey_(clientKey, wrappedKey);
1271 } else {
1272 G_Debug(this, "Not a valid response for /newkey");
1273 }
1274 }
1275
1276 /**
1277 * Set the callback to be called whenever we get a new key.
1278 *
1279 * @param callback The callback.
1280 */
onNewKey
1281 PROT_UrlCryptoKeyManager.prototype.onNewKey = function(callback)
1282 {
1283 this.onNewKey_ = callback;
1284 }
1285
1286 /**
1287 * Attempt to read a key we've previously serialized from disk, so
1288 * that we can fall back on it in case we can't get one from the
1289 * server. If we get a key, only use it if we don't already have one
1290 * (i.e., if our startup HTTPS request died or hasn't yet completed).
1291 *
1292 * This method should be invoked early, like when the user's profile
1293 * becomes available.
1294 */
maybeLoadOldKey
1295 PROT_UrlCryptoKeyManager.prototype.maybeLoadOldKey = function() {
1296
1297 var oldKey = null;
1298 try {
1299 var keyfile = Cc["@mozilla.org/file/directory_service;1"]
1300 .getService(Ci.nsIProperties)
1301 .get("ProfD", Ci.nsILocalFile); /* profile directory */
1302 keyfile.append(this.keyFilename_);
1303 if (keyfile.exists()) {
1304 try {
1305 var fis = Cc["@mozilla.org/network/file-input-stream;1"]
1306 .createInstance(Ci.nsIFileInputStream);
1307 fis.init(keyfile, 0x01 /* PR_RDONLY */, 0444, 0);
1308 var stream = Cc["@mozilla.org/scriptableinputstream;1"]
1309 .createInstance(Ci.nsIScriptableInputStream);
1310 stream.init(fis);
1311 oldKey = stream.read(stream.available());
1312 } finally {
1313 if (stream)
1314 stream.close();
1315 }
1316 }
1317 } catch(e) {
1318 G_Debug(this, "Caught " + e + " trying to read keyfile");
1319 return;
1320 }
1321
1322 if (!oldKey) {
1323 G_Debug(this, "Couldn't find old key.");
1324 return;
1325 }
1326
1327 oldKey = (new G_Protocol4Parser).parse(oldKey);
1328 var clientKey = oldKey[this.CLIENT_KEY_NAME];
1329 var wrappedKey = oldKey[this.WRAPPED_KEY_NAME];
1330
1331 if (oldKey && clientKey && wrappedKey && !this.hasKey()) {
1332 G_Debug(this, "Read old key from disk.");
1333 this.replaceKey_(clientKey, wrappedKey);
1334 }
1335 }
1336
1337
1338 //@line 36 "/home/visbrero/mnt/roisin/rev_control/hg/mozilla/toolkit/components/url-classifier/content/xml-fetcher.js"
1339
1340 // A simple class that encapsulates a request. You'll notice the
1341 // style here is different from the rest of the extension; that's
1342 // because this was re-used from really old code we had. At some
1343 // point it might be nice to replace this with something better
1344 // (e.g., something that has explicit onerror handler, ability
1345 // to set headers, and so on).
1346 //
1347 // The only interesting thing here is its ability to strip cookies
1348 // from the request.
1349
1350 /**
1351 * Because we might be in a component, we can't just assume that
1352 * XMLHttpRequest exists. So we use this tiny factory function to wrap the
1353 * XPCOM version.
1354 *
1355 * @return XMLHttpRequest object
1356 */
PROT_NewXMLHttpRequest
1357 function PROT_NewXMLHttpRequest() {
1358 var Cc = Components.classes;
1359 var Ci = Components.interfaces;
1360 var request = Cc["@mozilla.org/xmlextras/xmlhttprequest;1"]
1361 .createInstance(Ci.nsIXMLHttpRequest);
1362 // Need the following so we get onerror/load/progresschange
1363 request.QueryInterface(Ci.nsIJSXMLHttpRequest);
1364 return request;
1365 }
1366
1367 /**
1368 * A helper class that does HTTP GETs and calls back a function with
1369 * the content it receives. Asynchronous, so uses a closure for the
1370 * callback.
1371 *
1372 * @param opt_stripCookies Boolean indicating whether we should strip
1373 * cookies from this request
1374 *
1375 * @constructor
1376 */
PROT_XMLFetcher
1377 function PROT_XMLFetcher(opt_stripCookies) {
1378 this.debugZone = "xmlfetcher";
1379 this._request = PROT_NewXMLHttpRequest();
1380 this._stripCookies = !!opt_stripCookies;
1381 }
1382
1383 PROT_XMLFetcher.prototype = {
1384 /**
1385 * Function that will be called back upon fetch completion.
1386 */
1387 _callback: null,
1388
1389
1390 /**
1391 * Fetches some content.
1392 *
1393 * @param page URL to fetch
1394 * @param callback Function to call back when complete.
1395 */
get
1396 get: function(page, callback) {
1397 this._request.abort(); // abort() is asynchronous, so
1398 this._request = PROT_NewXMLHttpRequest();
1399 this._callback = callback;
1400 var asynchronous = true;
1401 this._request.open("GET", page, asynchronous);
1402 this._request.channel.notificationCallbacks = this;
1403
1404 if (this._stripCookies)
1405 new PROT_CookieStripper(this._request.channel);
1406
1407 // Create a closure
1408 var self = this;
anon:1409:39
1409 this._request.onreadystatechange = function() {
1410 self.readyStateChange(self);
1411 }
1412
1413 this._request.send(null);
1414 },
1415
1416 /**
1417 * Called periodically by the request to indicate some state change. 4
1418 * means content has been received.
1419 */
readyStateChange
1420 readyStateChange: function(fetcher) {
1421 if (fetcher._request.readyState != 4)
1422 return;
1423
1424 // If the request fails, on trunk we get status set to
1425 // NS_ERROR_NOT_AVAILABLE. On 1.8.1 branch we get an exception
1426 // forwarded from nsIHttpChannel::GetResponseStatus. To be consistent
1427 // between branch and trunk, we send back NS_ERROR_NOT_AVAILABLE for
1428 // http failures.
1429 var responseText = null;
1430 var status = Components.results.NS_ERROR_NOT_AVAILABLE;
1431 try {
1432 G_Debug(this, "xml fetch status code: \"" +
1433 fetcher._request.status + "\"");
1434 status = fetcher._request.status;
1435 responseText = fetcher._request.responseText;
1436 } catch(e) {
1437 G_Debug(this, "Caught exception trying to read xmlhttprequest " +
1438 "status/response.");
1439 G_Debug(this, e);
1440 }
1441 if (fetcher._callback)
1442 fetcher._callback(responseText, status);
1443 },
1444
1445 // Suppress any certificate errors
notifyCertProblem
1446 notifyCertProblem: function(socketInfo, status, targetSite) {
1447 return true;
1448 },
1449
1450 // Suppress any ssl errors
notifySSLError
1451 notifySSLError: function(socketInfo, error, targetSite) {
1452 return true;
1453 },
1454
1455 // nsIInterfaceRequestor
getInterface
1456 getInterface: function(iid) {
1457 return this.QueryInterface(iid);
1458 },
1459
QueryInterface
1460 QueryInterface: function(iid) {
1461 if (!iid.equals(Components.interfaces.nsIBadCertListener2) &&
1462 !iid.equals(Components.interfaces.nsISSLErrorListener) &&
1463 !iid.equals(Components.interfaces.nsIInterfaceRequestor) &&
1464 !iid.equals(Components.interfaces.nsISupports))
1465 throw Components.results.NS_ERROR_NO_INTERFACE;
1466 return this;
1467 }
1468 };
1469
1470
1471 /**
1472 * This class knows how to strip cookies from an HTTP request. It
1473 * listens for http-on-modify-request, and modifies the request
1474 * accordingly. We can't do this using xmlhttprequest.setHeader() or
1475 * nsIChannel.setRequestHeader() before send()ing because the cookie
1476 * service is called after send().
1477 *
1478 * @param channel nsIChannel in which the request is happening
1479 * @constructor
1480 */
PROT_CookieStripper
1481 function PROT_CookieStripper(channel) {
1482 this.debugZone = "cookiestripper";
1483 this.topic_ = "http-on-modify-request";
1484 this.channel_ = channel;
1485
1486 var Cc = Components.classes;
1487 var Ci = Components.interfaces;
1488 this.observerService_ = Cc["@mozilla.org/observer-service;1"]
1489 .getService(Ci.nsIObserverService);
1490 this.observerService_.addObserver(this, this.topic_, false);
1491
1492 // If the request doesn't issue, don't hang around forever
1493 var twentySeconds = 20 * 1000;
1494 this.alarm_ = new G_Alarm(BindToObject(this.stopObserving, this),
1495 twentySeconds);
1496 }
1497
1498 /**
1499 * Invoked by the observerservice. See nsIObserve.
1500 */
observe
1501 PROT_CookieStripper.prototype.observe = function(subject, topic, data) {
1502 if (topic != this.topic_ || subject != this.channel_)
1503 return;
1504
1505 G_Debug(this, "Stripping cookies for channel.");
1506
1507 this.channel_.QueryInterface(Components.interfaces.nsIHttpChannel);
1508 this.channel_.setRequestHeader("Cookie", "", false /* replace, not add */);
1509 this.alarm_.cancel();
1510 this.stopObserving();
1511 }
1512
1513 /**
1514 * Remove us from the observerservice
1515 */
stopObserving
1516 PROT_CookieStripper.prototype.stopObserving = function() {
1517 G_Debug(this, "Removing observer");
1518 this.observerService_.removeObserver(this, this.topic_);
1519 this.channel_ = this.alarm_ = this.observerService_ = null;
1520 }
1521
1522 /**
1523 * XPCOM cruft
1524 */
QueryInterface
1525 PROT_CookieStripper.prototype.QueryInterface = function(iid) {
1526 var Ci = Components.interfaces;
1527 if (iid.equals(Ci.nsISupports) || iid.equals(Ci.nsIObserve))
1528 return this;
1529 throw Components.results.NS_ERROR_NO_INTERFACE;
1530 }
1531
1532 //@line 57 "/home/visbrero/mnt/roisin/rev_control/hg/mozilla/toolkit/components/url-classifier/src/nsUrlClassifierLib.js"
1533
1534 // Expose this whole component.
1535 var lib = this;
1536
UrlClassifierLib
1537 function UrlClassifierLib() {
1538 this.wrappedJSObject = lib;
1539 }
1540
1541 // Module object
UrlClassifierLibMod
1542 function UrlClassifierLibMod() {
1543 this.firstTime = true;
1544 this.cid = Components.ID("{26a4a019-2827-4a89-a85c-5931a678823a}");
1545 this.progid = "@mozilla.org/url-classifier/jslib;1";
1546 }
1547
registerSelf
1548 UrlClassifierLibMod.prototype.registerSelf = function(compMgr, fileSpec, loc, type) {
1549 if (this.firstTime) {
1550 this.firstTime = false;
1551 throw Components.results.NS_ERROR_FACTORY_REGISTER_AGAIN;
1552 }
1553 compMgr = compMgr.QueryInterface(Ci.nsIComponentRegistrar);
1554 compMgr.registerFactoryLocation(this.cid,
1555 "UrlClassifier JS Lib",
1556 this.progid,
1557 fileSpec,
1558 loc,
1559 type);
1560 };
1561
getClassObject
1562 UrlClassifierLibMod.prototype.getClassObject = function(compMgr, cid, iid) {
1563 if (!cid.equals(this.cid))
1564 throw Components.results.NS_ERROR_NO_INTERFACE;
1565 if (!iid.equals(Ci.nsIFactory))
1566 throw Components.results.NS_ERROR_NOT_IMPLEMENTED;
1567
1568 return this.factory;
1569 }
1570
canUnload
1571 UrlClassifierLibMod.prototype.canUnload = function(compMgr) {
1572 return true;
1573 }
1574
1575 UrlClassifierLibMod.prototype.factory = {
createInstance
1576 createInstance: function(outer, iid) {
1577 if (outer != null)
1578 throw Components.results.NS_ERROR_NO_AGGREGATION;
1579 return new UrlClassifierLib();
1580 }
1581 };
1582
1583 var LibModInst = new UrlClassifierLibMod();
1584
NSGetModule
1585 function NSGetModule(compMgr, fileSpec) {
1586 return LibModInst;
1587 }