Fix bug 247486 - can't load several calendars with different passwords on same server/realm. r=mschroeder
authorPhilipp Kewisch <mozilla@kewis.ch>
Fri, 05 Aug 2011 20:51:39 +0200
changeset 8357 29ea4eb2c6fbb292cc6b1d1a826c382053d3caa8
parent 8356 0f3c47940325cb003984493ad2a97d491cde36ee
child 8358 952e2f8e45ca646eee493a67dd277019e1ba54d8
push idunknown
push userunknown
push dateunknown
reviewersmschroeder
bugs247486
Fix bug 247486 - can't load several calendars with different passwords on same server/realm. r=mschroeder
calendar/base/modules/calProviderUtils.jsm
calendar/base/public/calICalendar.idl
calendar/base/src/calCalendarManager.js
--- a/calendar/base/modules/calProviderUtils.jsm
+++ b/calendar/base/modules/calProviderUtils.jsm
@@ -135,16 +135,21 @@ cal.safeNewXML = function calSafeNewXML(
  *
  * return cal.InterfaceRequestor_getInterface.apply(this, arguments);
  *
  * or
  * ...
  * getInterface: cal.InterfaceRequestor_getInterface,
  * ...
  *
+ * NOTE: If the server only provides one realm for all calendars, be sure that
+ * the |this| object implements calICalendar. In this case the calendar name
+ * will be appended to the realm. If you need that feature disabled, see the
+ * capabilities section of calICalendar.idl
+ *
  * @param aIID      The interface ID to return
  */
 cal.InterfaceRequestor_getInterface = function calInterfaceRequestor_getInterface(aIID) {
     // Support Auth Prompt Interfaces
     if (aIID.equals(Components.interfaces.nsIAuthPrompt2)) {
         if (!this.calAuthPrompt) {
             this.calAuthPrompt = new cal.auth.Prompt();
         }
--- a/calendar/base/public/calICalendar.idl
+++ b/calendar/base/public/calICalendar.idl
@@ -194,16 +194,22 @@ interface calICalendar : nsISupports
    * fields. An array should be specified with the values, the default
    * values are specified here. Extensions using this need to take care of
    * adding any UI elements needed in an overlay. To make sure the correct
    * elements are shown, those elements should additionally specify an attribute
    * "provider", with the type of the provider.
    *
    *   capabilities.privacy.values = ["PUBLIC", "CONFIDENTIAL", "PRIVATE"];
    *
+   * The following special capability disables rewriting the WWW-Authenticate
+   * header on HTTP requests to include the calendar name. The default value
+   * is false, i.e rewriting is NOT disabled.
+   *
+   *   capabilities.realmrewrite.disabled = false
+   *
    * @param aName property name
    * @return value (string, integer and boolean values are supported),
    *               else null
    */
   nsIVariant getProperty(in AUTF8String aName);
 
   /**
    * Sets a calendar property.
--- a/calendar/base/src/calCalendarManager.js
+++ b/calendar/base/src/calCalendarManager.js
@@ -186,30 +186,32 @@ calCalendarManager.prototype = {
         if (cal.isSunbird()) {
             this.loginMasterPassword();
         }
         this.mNetworkCalendarCount = 0;
         this.mReadonlyCalendarCount = 0;
         this.mCalendarCount = 0;
 
         Services.obs.addObserver(this, "http-on-modify-request", false);
+        Services.obs.addObserver(this, "http-on-examine-response", false);
 
         aCompleteListener.onResult(null, Components.results.NS_OK);
     },
 
     shutdown: function ccm_shutdown(aCompleteListener) {
         for each (var cal in this.mCache) {
             cal.removeObserver(this.mCalObservers[cal.id]);
         }
 
         this.cleanupOfflineObservers();
 
         Services.obs.removeObserver(this, "profile-after-change");
         Services.obs.removeObserver(this, "profile-before-change");
         Services.obs.removeObserver(this, "http-on-modify-request");
+        Services.obs.removeObserver(this, "http-on-examine-response");
         AddonManager.removeAddonListener(gCalendarManagerAddonListener);
 
         aCompleteListener.onResult(null, Components.results.NS_OK);
     },
 
 
     setupOfflineObservers: function ccm_setupOfflineObservers() {
         Services.obs.addObserver(this, "network:offline-status-changed", false);
@@ -256,16 +258,42 @@ calCalendarManager.prototype = {
                 break;
             case "network:offline-status-changed":
                 for each (var calendar in this.mCache) {
                     if (calendar instanceof calCachedCalendar) {
                         calendar.onOfflineStatusChanged(aData == "offline");
                     }
                 }
                 break;
+            case "http-on-examine-response":
+                try {
+                    let channel = aSubject.QueryInterface(Components.interfaces.nsIHttpChannel);
+                    if (channel.notificationCallbacks) {
+                        // We use the notification callbacks to get the calendar interface,
+                        // which likely works for our requests since getInterface is called
+                        // from the calendar provider context.
+                        let authHeader = channel.getResponseHeader("WWW-Authenticate");
+                        let calendar = channel.notificationCallbacks
+                                              .getInterface(Components.interfaces.calICalendar);
+                        if (!calendar.getProperty("capabilities.realmrewrite.disabled")) {
+                            // The provider may choose to explicitly disable the
+                            // rewriting, for example if all calendars on a
+                            // domain have the same credentials
+                            authHeader = authHeader.replace(/realm="(.*)"/, 'realm="$1 (' + calendar.name + ')"');
+                            channel.setResponseHeader("WWW-Authenticate", authHeader, false);
+                        }
+                    }
+                } catch (e if e.result == Components.results.NS_NOINTERFACE ||
+                              e.result == Components.results.NS_ERROR_NOT_AVAILABLE) {
+                    // Possible reasons we got here:
+                    // - Its not a http channel (wtf? Oh well)
+                    // - The owner is not a calICalendar (looks like its not our deal)
+                    // - The WWW-Authenticate header is missing (thats ok)
+                }
+                break;
             case "http-on-modify-request":
                 // Unfortunately, the ability to do this with a general pref has
                 // been removed. Calendar servers might still want to know what
                 // client is used for access, so add our UA String to each
                 // request.
                 let httpChannel = aSubject.QueryInterface(Components.interfaces.nsIHttpChannel);
                 try {
                     // NOTE: For some reason, this observer call doesn't have