Fix bug 441992 - caldav calendar isn't switched off after canceling the login dialog. r=mmecca
authorPhilipp Kewisch <mozilla@kewis.ch>
Thu, 28 Jul 2011 05:43:00 +0200
changeset 8788 8e731eb1d074f231517f8367aff423d08ed6b2d3
parent 8787 e7058e17b135112eeecc0c8ed2b8ff2fcf5f9926
child 8789 a709a5fdca1c6cfa9bd0ca8a1ae8ad5f6d449960
push id158
push userbugzilla@standard8.plus.com
push dateTue, 27 Sep 2011 19:18:14 +0000
treeherdercomm-beta@e47b99c61e4d [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersmmecca
bugs441992
Fix bug 441992 - caldav calendar isn't switched off after canceling the login dialog. r=mmecca
calendar/providers/caldav/calDavCalendar.js
calendar/providers/ics/calICSCalendar.js
--- a/calendar/providers/caldav/calDavCalendar.js
+++ b/calendar/providers/caldav/calDavCalendar.js
@@ -1396,16 +1396,25 @@ calDavCalendar.prototype = {
                         " on initial PROPFIND for calendar " + thisCalendar.name);
             } catch (ex) {
                 cal.LOG("CalDAV: Error without status on initial PROPFIND for calendar " +
                         thisCalendar.name);
                 thisCalendar.completeCheckServerInfo(aChangeLogListener,
                                                      Components.interfaces.calIErrors.DAV_NOT_DAV);
                 return;
             }
+
+            if (request.responseStatus == 401 || request.responseStatus == 403) {
+                // Auth was cancelled, disable this calendar with auto-enable
+                thisCalendar.setProperty("disabled", "true");
+                thisCalendar.setProperty("auto-enabled", "true");
+                thisCalendar.completeCheckServerInfo(aChangeLogListener, Components.results.NS_ERROR_ABORT);
+                return;
+            }
+
             var wwwauth;
             try {
                 wwwauth = request.getRequestHeader("Authorization");
                 thisCalendar.mAuthScheme = wwwauth.split(" ")[0];
             } catch (ex) {
                 // no auth header could mean a public calendar
                 thisCalendar.mAuthScheme = "none";
             }
--- a/calendar/providers/ics/calICSCalendar.js
+++ b/calendar/providers/ics/calICSCalendar.js
@@ -145,21 +145,21 @@ calICSCalendar.prototype = {
         this.mUri = aUri;
         this.mMemoryCalendar.uri = this.mUri;
 
         // Use the ioservice, to create a channel, which makes finding the
         // right hooks to use easier.
         var channel = cal.getIOService().newChannelFromURI(this.mUri);
 
         if (calInstanceOf(channel, Components.interfaces.nsIHttpChannel)) {
-            this.mHooks = new httpHooks();
+            this.mHooks = new httpHooks(this);
         } else if (calInstanceOf(channel, Components.interfaces.nsIFileChannel)) {
-            this.mHooks = new fileHooks();
+            this.mHooks = new fileHooks(this);
         } else {
-            this.mHooks = new dummyHooks();
+            this.mHooks = new dummyHooks(this);
         }
     },
 
     getProperty: function calICSCalendar_getProperty(aName) {
         switch (aName) {
             case "requiresNetwork":
                 return (!this.uri.schemeIs("file"));
         }
@@ -862,17 +862,18 @@ calICSObserver.prototype = {
  *
  * Different protocols need different checks (webdav can do etag, but
  * local files need last-modified stamps), hence different hooks for each
  * types
  */
 
 // dummyHooks are for transport types that don't have hooks of their own.
 // Also serves as poor-mans interface definition.
-function dummyHooks() {
+function dummyHooks(calendar) {
+    this.mCalendar = calendar;
 }
 
 dummyHooks.prototype = {
     onBeforeGet: function(aChannel, aForceRefresh) {
         return true;
     },
     
     /**
@@ -890,17 +891,18 @@ dummyHooks.prototype = {
     },
     
     onAfterPut: function(aChannel, aRespFunc) {
         aRespFunc();
         return true;
     }
 };
 
-function httpHooks() {
+function httpHooks(calendar) {
+    this.mCalendar = calendar;
     this.mChannel = null;
 }
 
 httpHooks.prototype = {
     onBeforeGet: function(aChannel, aForceRefresh) {
         this.mChannel = aChannel;
         if (this.mEtag && !aForceRefresh) {
             var httpchannel = aChannel.QueryInterface(Components.interfaces.nsIHttpChannel);
@@ -910,27 +912,34 @@ httpHooks.prototype = {
         }
 
         return true;
     },
     
     onAfterGet: function(aForceRefresh) {
         var httpchannel = this.mChannel.QueryInterface(Components.interfaces.nsIHttpChannel);
 
-        // 304: Not Modified
-        // Can use the old data, so tell the caller that it can skip parsing.
-        if (httpchannel.responseStatus == 304) {
-            return false;
-        }
+        switch (httpchannel.responseStatus) {
+            case 304:
+                // 304: Not Modified
+                // Can use the old data, so tell the caller that it can skip parsing.
+                return false;
+            case 404:
+                // 404: Not Found
+                // This is a new calendar. Shouldn't try to parse it. But it also
+                // isn't a failure, so don't throw.
+                return false;
 
-        // 404: Not Found
-        // This is a new calendar. Shouldn't try to parse it. But it also
-        // isn't a failure, so don't throw.
-        if (httpchannel.responseStatus == 404) {
-            return false;
+            case 401:
+            case 403:
+                // 401/403: Not Authorized
+                // The user likely cancelled the login dialog.
+                this.mCalendar.setProperty("disabled", "true");
+                this.mCalendar.setProperty("auto-enabled", "true");
+                return false;
         }
 
         try {
             this.mEtag = httpchannel.getResponseHeader("ETag");
         } catch(e) {
             // No etag header. Now what?
             this.mEtag = null;
         }
@@ -957,17 +966,17 @@ httpHooks.prototype = {
         } catch(e) {
             // There was no ETag header on the response. This means that
             // putting is not atomic. This is bad. Race conditions can happen,
             // because there is a time in which we don't know the right
             // etag.
             // Try to do the best we can, by immediatly getting the etag.
 
             var etagListener = {};
-            var thisCalendar = this; // need to reference in callback
+            var thisHook = this; // need to reference in callback
 
             etagListener.onStreamComplete =
                 function ics_etLoSC(aLoader, aContext, aStatus, aResultLength,
                                     aResult) {
                 var resultConverter = Components.classes["@mozilla.org/intl/scriptableunicodeconverter"]
                                                 .createInstance(Components
                                                 .interfaces.nsIScriptableUnicodeConverter);
                 resultConverter.charset = "UTF-8";
@@ -975,19 +984,19 @@ httpHooks.prototype = {
                 var str;
                 try {
                     str = resultConverter.convertFromByteArray(aResult, aResultLength);
                 } catch (e) {
                     LOG("Failed to fetch channel etag");
                 }
                 var multistatus = cal.safeNewXML(str);
                 try {
-                    thisCalendar.mEtag = multistatus..D::getetag;
+                    thisHook.mEtag = multistatus..D::getetag;
                 } catch (e) {
-                    thisCalendar.mEtag = null;
+                    thisHook.mEtag = null;
                 }
                 aRespFunc();
             }
             var D = new Namespace("D", "DAV:");
             default xml namespace = D;
             var queryXml = <D:propfind xmlns:D="DAV:">
                     <D:prop>
                       <D:getetag/>
@@ -1016,17 +1025,18 @@ httpHooks.prototype = {
         if (aIid.equals(Components.interfaces.nsIProgressEventSink)) {
             return this;
         } else {    
             throw Components.results.NS_ERROR_NO_INTERFACE;
         }
     }
 };
 
-function fileHooks() {
+function fileHooks(calendar) {
+    this.mCalendar = calendar;
     this.mChannel = null;
 }
 
 fileHooks.prototype = {
     onBeforeGet: function fH_onBeforeGet(aChannel, aForceRefresh) {
         this.mChannel = aChannel;
         let fileChannel = this.mChannel.QueryInterface(Components.interfaces.nsIFileChannel);
         return true;