[Commits] (morgen) Added better error handling, and added a "Test WebDAV settings" button the accounts dialog

commits at osafoundation.org commits at osafoundation.org
Thu Feb 3 10:02:08 PST 2005


Commit by: morgen
Modified files:
chandler/application/dialogs/AccountPreferences.py 1.23 1.24
chandler/application/dialogs/AccountPreferences.wdr 1.10 1.11
chandler/application/dialogs/AccountPreferences_wdr.xrc 1.9 1.10
chandler/parcels/osaf/framework/sharing/Sharing.py 1.57 1.58
chandler/parcels/osaf/framework/sharing/WebDAV.py 1.11 1.12

Log message:
Added better error handling, and added a "Test WebDAV settings" button the accounts dialog


ViewCVS links:
http://cvs.osafoundation.org/index.cgi/chandler/application/dialogs/AccountPreferences.py.diff?r1=text&tr1=1.23&r2=text&tr2=1.24
http://cvs.osafoundation.org/index.cgi/chandler/application/dialogs/AccountPreferences.wdr.diff?r1=text&tr1=1.10&r2=text&tr2=1.11
http://cvs.osafoundation.org/index.cgi/chandler/application/dialogs/AccountPreferences_wdr.xrc.diff?r1=text&tr1=1.9&r2=text&tr2=1.10
http://cvs.osafoundation.org/index.cgi/chandler/parcels/osaf/framework/sharing/Sharing.py.diff?r1=text&tr1=1.57&r2=text&tr2=1.58
http://cvs.osafoundation.org/index.cgi/chandler/parcels/osaf/framework/sharing/WebDAV.py.diff?r1=text&tr1=1.11&r2=text&tr2=1.12

Index: chandler/parcels/osaf/framework/sharing/WebDAV.py
diff -u chandler/parcels/osaf/framework/sharing/WebDAV.py:1.11 chandler/parcels/osaf/framework/sharing/WebDAV.py:1.12
--- chandler/parcels/osaf/framework/sharing/WebDAV.py:1.11	Wed Feb  2 19:10:47 2005
+++ chandler/parcels/osaf/framework/sharing/WebDAV.py	Thu Feb  3 10:02:07 2005
@@ -1,5 +1,5 @@
-__version__ = "$Revision: 1.11 $"
-__date__ = "$Date: 2005/02/03 03:10:47 $"
+__version__ = "$Revision: 1.12 $"
+__date__ = "$Date: 2005/02/03 18:02:07 $"
 __copyright__ = "Copyright (c) 2005 Open Source Applications Foundation"
 __license__ = "http://osafoundation.org/Chandler_0.1_license_terms.htm"
 
@@ -48,6 +48,16 @@
             self.conn = httplib.HTTPConnection(self.host, self.port)
 
         self.conn.debuglevel = 0
+        try:
+            logger.debug("Connecting to %s" % self.host)
+            self.conn.connect()
+        except socket.gaierror, err:
+            # @@@MOR can these exceptions mean anything else?
+            message = "Unknown host %s" % self.host
+            raise ConnectionError(message=message)
+        except socket.herror, err:
+            message = "Unknown host %s" % self.host
+            raise ConnectionError(message=message)
 
     def mkcol(self, url, extraHeaders={ }):
         return self._request('MKCOL', url, extraHeaders=extraHeaders)
@@ -92,7 +102,7 @@
         resources = []
         resp = self.propfind(url, depth=1, extraHeaders=extraHeaders)
         if resp.status != httplib.MULTI_STATUS:
-            raise WebDAVException() # @@@MOR Any way to recover from this?
+            raise WebDAVException(status=resp.status)
 
         # Parse the propfind, pulling out the URLs for each child along
         # with their ETAGs, and storing them in the resourceList dictionary:
@@ -104,6 +114,7 @@
         except:
             logging.error("Parsing response failed: %s" % text)
             raise
+
         node = doc.children.children
         while node:
             if node.type == "element":
@@ -208,7 +219,8 @@
                     continue
                 else:
                     logger.debug("Illegal redirect: %s to %s" % (url, newurl))
-                    raise IllegalRedirect()
+                    message = "Illegal redirect: %s to %s" % (url, newurl)
+                    raise IllegalRedirect(message=message)
 
             return response
 
@@ -217,7 +229,9 @@
         raise ConnectionError()
 
 class WebDAVException(Exception):
-    pass
+    def __init__(self, status=None, message=None):
+        self.status = status
+        self.message = message
 
 class ConnectionError(WebDAVException):
     pass
@@ -235,9 +249,10 @@
 # ----------------------------------------------------------------------------
 
 
-NO_ACCESS  = 0
-READ_ONLY  = 1
-READ_WRITE = 2
+CANT_CONNECT = -1
+NO_ACCESS    = 0
+READ_ONLY    = 1
+READ_WRITE   = 2
 
 def checkAccess(host, port=80, useSSL=False, username=None, password=None,
                 path=None):
@@ -262,8 +277,12 @@
             portString = ":%d" % port
 
     url = "%s://%s%s%s" % (scheme, host, portString, path)
-    response = client.propfind(url, depth=0)
-    body = response.read()
+    try:
+        response = client.propfind(url, depth=0)
+        body = response.read()
+    except ConnectionError, err:
+        return (CANT_CONNECT, None)
+
     status = response.status
     # print "PROPFIND:", url, status
     if status < 200 or status >= 300: # failed to read

Index: chandler/application/dialogs/AccountPreferences_wdr.xrc
diff -u chandler/application/dialogs/AccountPreferences_wdr.xrc:1.9 chandler/application/dialogs/AccountPreferences_wdr.xrc:1.10
--- chandler/application/dialogs/AccountPreferences_wdr.xrc:1.9	Tue Feb  1 12:44:25 2005
+++ chandler/application/dialogs/AccountPreferences_wdr.xrc	Thu Feb  3 10:02:07 2005
@@ -474,6 +474,20 @@
                         <label>Use as default account for sharing</label>
                     </object>
                 </object>
+                <object class="sizeritem">
+                    <flag>wxALIGN_CENTER|wxALL</flag>
+                    <border>5</border>
+                    <object class="wxStaticText" name="ID_TEXT">
+                        <label></label>
+                    </object>
+                </object>
+                <object class="sizeritem">
+                    <flag>wxALIGN_CENTER_VERTICAL|wxALL</flag>
+                    <border>5</border>
+                    <object class="wxButton" name="WEBDAV_TEST">
+                        <label>Click to test this account now</label>
+                    </object>
+                </object>
             </object>
         </object>
     </object>

Index: chandler/application/dialogs/AccountPreferences.py
diff -u chandler/application/dialogs/AccountPreferences.py:1.23 chandler/application/dialogs/AccountPreferences.py:1.24
--- chandler/application/dialogs/AccountPreferences.py:1.23	Tue Feb  1 12:44:25 2005
+++ chandler/application/dialogs/AccountPreferences.py	Thu Feb  3 10:02:06 2005
@@ -5,6 +5,7 @@
 from repository.item.Query import KindQuery
 import osaf.contentmodel.mail.Mail as Mail
 import application.dialogs.Util
+import osaf.framework.sharing.WebDAV as WebDAV
 
 # Used to lookup the mail model parcel:
 MAIL_MODEL = "http://osafoundation.org/parcels/osaf/contentmodel/mail"
@@ -44,6 +45,7 @@
     for (field, desc) in fields.iteritems():
         item.setAttributeValue(desc['attr'], values[field])
 
+
 # Used to map form fields to item attributes:
 PANELS = {
     "IMAP" : {
@@ -156,6 +158,9 @@
             },
         },
         "id" : "WebDAVPanel",
+        "callbacks" : (
+            ("WEBDAV_TEST", "OnTestWebDAV"),
+        )
     },
 }
 
@@ -366,6 +371,9 @@
                     wx.EVT_RADIOBUTTON(control, control.GetId(),
                                        self.OnExclusiveRadioButton)
 
+        for callbackReg in PANELS[self.currentPanelType].get('callbacks', ()):
+            self.Bind(wx.EVT_BUTTON, getattr(self, callbackReg[1]),
+                      id=wx.xrc.XRCID(callbackReg[0]))
 
 
     def __StoreFormData(self, panelType, panel, data):
@@ -400,6 +408,38 @@
     def OnCancel(self, evt):
         self.EndModal(False)
 
+    def OnTestWebDAV(self, evt):
+        self.__StoreFormData(self.currentPanelType, self.currentPanel,
+         self.data[self.currentIndex]['values'])
+
+        data = self.data[self.currentIndex]['values']
+
+        host = data['WEBDAV_SERVER']
+        port = data['WEBDAV_PORT']
+        useSSL = data['WEBDAV_USE_SSL']
+        username = data['WEBDAV_USERNAME']
+        password = data['WEBDAV_PASSWORD']
+        path = data['WEBDAV_PATH']
+        access = WebDAV.checkAccess(host, port=port, useSSL=useSSL,
+                                    username=username, password=password,
+                                    path=path)
+        result = access[0]
+        status = access[1]
+        if result == WebDAV.CANT_CONNECT:
+            msg = "Couldn't connect to %s.\nPlease double-check the server name and port settings." % host
+        elif result == WebDAV.NO_ACCESS:
+            msg = "Permission denied by server '%s'." % host
+        elif result == WebDAV.READ_ONLY:
+            msg = "You have read access but not write access."
+        elif result == WebDAV.READ_WRITE:
+            msg = "Test was successful.\nThis account has read/write access."
+        else:
+            # This shouldn't happen
+            msg = "Test failed with an unknown response."
+
+        application.dialogs.Util.ok(self, "WebDAV Test Results", msg)
+
+
     def OnAccountSel(self, evt):
         # Huh? This is always False!
         # if not evt.IsSelection(): return

Index: chandler/parcels/osaf/framework/sharing/Sharing.py
diff -u chandler/parcels/osaf/framework/sharing/Sharing.py:1.57 chandler/parcels/osaf/framework/sharing/Sharing.py:1.58
--- chandler/parcels/osaf/framework/sharing/Sharing.py:1.57	Wed Feb  2 12:35:55 2005
+++ chandler/parcels/osaf/framework/sharing/Sharing.py	Thu Feb  3 10:02:07 2005
@@ -1,5 +1,5 @@
-__version__ = "$Revision: 1.57 $"
-__date__ = "$Date: 2005/02/02 20:35:55 $"
+__version__ = "$Revision: 1.58 $"
+__date__ = "$Date: 2005/02/03 18:02:07 $"
 __copyright__ = "Copyright (c) 2004 Open Source Applications Foundation"
 __license__ = "http://osafoundation.org/Chandler_0.1_license_terms.htm"
 
@@ -585,7 +585,6 @@
 
     def getLocation(self):  # must implement
         """ Return the url of the share """
-        # @@@MOR need to handle https
 
         (host, port, sharePath, username, password, useSSL) = self.__getSettings()
         scheme = "http"
@@ -649,8 +648,16 @@
     def exists(self):
         super(WebDAVConduit, self).exists()
 
-        resp = self.__getClient().head(self.getLocation())
-        resp.read()
+        try:
+            resp = self.__getClient().head(self.getLocation())
+            resp.read()
+        except WebDAV.ConnectionError, err:
+            raise CouldNotConnect(message=err.message)
+
+        if resp.status == httplib.UNAUTHORIZED:
+            message = "Not authorized to PUT %s" % url
+            raise NotAuthorized(message=message)
+
         if resp.status == httplib.NOT_FOUND:
             return False
         else:
@@ -663,9 +670,36 @@
 
         if style == ImportExportFormat.STYLE_DIRECTORY:
             url = self.getLocation()
-            resp = self.__getClient().mkcol(url)
-            resp.read() # Always need to read each response
-            # @@@MOR Raise an exception if already exists?
+            try:
+                resp = self.__getClient().mkcol(url)
+                resp.read() # Always need to read each response
+            except WebDAV.ConnectionError, err:
+                raise CouldNotConnect(message=err.message)
+
+            if resp.status == httplib.METHOD_NOT_ALLOWED:
+                # already exists
+                message = "Collection at %s already exists" % url
+                raise AlreadyExists(message=message)
+
+            if resp.status == httplib.UNAUTHORIZED:
+                # not authorized
+                message = "Not authorized to create collection %s" % url
+                raise NotAuthorized(message=message)
+
+            if resp.status == httplib.CONFLICT:
+                # this happens if you try to create a collection within a
+                # nonexistent collection
+                message = "Parent collection for %s not found" % url
+                raise NotFound(message=message)
+
+            if resp.status == httplib.FORBIDDEN:
+                # the server doesn't allow the creation of a collection here
+                message = "Server doesn't allow the creation of collections at %s" % url
+                raise IllegalOperation(message=message)
+
+            if resp.status != httplib.CREATED:
+                 message = "WebDAV error, status = %d" % resp.status
+                 raise IllegalOperation(message=message)
 
     def destroy(self):
         print " @@@MOR unimplemented"
@@ -678,13 +712,34 @@
         """
         url = self.__getItemURL(item)
         text = self.share.format.exportProcess(item)
-        resp = self.__getClient().put(url, text)
-        resp.read() # Always need to read each response
+
+        try:
+            resp = self.__getClient().put(url, text)
+            resp.read() # Always need to read each response
+        except WebDAV.ConnectionError, err:
+            raise CouldNotConnect(message=err.message)
+
+        # 201 = new, 204 = overwrite
+
+        if resp.status == httplib.UNAUTHORIZED:
+            message = "Not authorized to PUT %s" % url
+            raise NotAuthorized(message=message)
+
+        if resp.status == httplib.FORBIDDEN or resp.status == httplib.CONFLICT:
+            # seen if trying to PUT to a nonexistent collection (@@@MOR verify)
+            message = "Parent collection for %s is not found" % url
+            raise NotFound(message=message)
+
         etag = resp.getheader('ETag', None)
         if not etag:
             # mod_dav doesn't give us back an etag upon PUT
-            resp = self.__getClient().head(url)
-            resp.read() # Always need to read each response
+
+            try:
+                resp = self.__getClient().head(url)
+                resp.read() # Always need to read each response
+            except WebDAV.ConnectionError, err:
+                raise CouldNotConnect(message=err.message)
+
             etag = resp.getheader('ETag', None)
             if not etag:
                 print "HEAD didn't give me an etag"
@@ -705,13 +760,30 @@
     def _deleteItem(self, itemPath): # must implement
         itemURL = self.__URLFromPath(itemPath)
         logger.info("...removing from server: %s" % itemURL)
-        resp = self.__getClient().delete(itemURL)
-        deleteResp = resp.read()
+
+        try:
+            resp = self.__getClient().delete(itemURL)
+            deleteResp = resp.read()
+        except WebDAV.ConnectionError, err:
+            raise CouldNotConnect(message=err.message)
 
     def _getItem(self, itemPath, into=None): # must implement
         itemURL = self.__URLFromPath(itemPath)
-        resp = self.__getClient().get(itemURL)
-        text = resp.read()
+
+        try:
+            resp = self.__getClient().get(itemURL)
+            text = resp.read()
+        except WebDAV.ConnectionError, err:
+            raise CouldNotConnect(message=err.message)
+
+        if resp.status == httplib.NOT_FOUND:
+            message = "Not found: %s" % url
+            raise NotFound(message=message)
+
+        if resp.status == httplib.UNAUTHORIZED:
+            message = "Not authorized to get %s" % url
+            raise NotAuthorized(message=message)
+
         etag = resp.getheader('ETag', None)
         etag = self.__cleanEtag(etag)
         item = self.share.format.importProcess(text, item=into)
@@ -726,14 +798,42 @@
 
         if style == ImportExportFormat.STYLE_DIRECTORY:
 
-            resources = self.__getClient().ls(location + "/")
+            try:
+                resources = self.__getClient().ls(location + "/")
+
+            except WebDAV.ConnectionError, err:
+                raise CouldNotConnect(message=err.message)
+
+            except WebDAV.WebDAVException, e:
+
+                if e.status == httplib.NOT_FOUND:
+                    raise NotFound(message="Not found: %s" % location)
+
+                if e.status == httplib.UNAUTHORIZED:
+                    raise NotAllowed(message="Not allowed: %s" % location)
+
+                raise
+
             for (path, etag) in resources:
                 etag = self.__cleanEtag(etag)
                 resourceList[path] = { 'data' : etag }
 
         elif style == ImportExportFormat.STYLE_SINGLE:
-            resp = self.__getClient().head(location)
-            resp.read() # Always need to read each response
+
+            try:
+                resp = self.__getClient().head(location)
+                resp.read() # Always need to read each response
+            except WebDAV.ConnectionError, err:
+                raise CouldNotConnect(message=err.message)
+
+            if resp.status == httplib.NOT_FOUND:
+                message = "Not found: %s" % url
+                raise NotFound(message=message)
+
+            if resp.status == httplib.UNAUTHORIZED:
+                message = "Not authorized to get %s" % url
+                raise NotAuthorized(message=message)
+
             etag = resp.getheader('ETag', None)
             etag = self.__cleanEtag(etag)
             path = urlparse.urlparse(location)[2]
@@ -763,23 +863,30 @@
 
 class SharingError(Exception):
     """ Generic Sharing exception. """
-    pass
+    def __init__(self, message=None):
+        self.message = message
 
 class AlreadyExists(SharingError):
     """ Exception raised if a share already exists. """
-    pass
 
 class NotFound(SharingError):
     """ Exception raised if a share/resource wasn't found. """
-    pass
 
 class NotAllowed(SharingError):
     """ Exception raised if we don't have access. """
-    pass
 
 class Misconfigured(SharingError):
     """ Exception raised if a share isn't properly configured. """
-    pass
+
+class CouldNotConnect(SharingError):
+    """ Exception raised if a conduit can't connect to an external entity
+        due to DNS/network problems.
+    """
+
+class IllegalOperation(SharingError):
+    """ Exception raised if the entity a conduit is communicating with is
+        denying an operation for some reason not covered by other exceptions.
+    """
 
 # = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = =
 
@@ -1542,6 +1649,13 @@
             return False
 
 
+def syncShare(share):
+    """ @@@MOR In progress
+    try:
+        share.sync()
+    except WebDAV.ConnectionError, err:
+    """
+
 
 def syncAll(view):
     """ Synchronize all active shares.



More information about the Commits mailing list