"""webdavfsutil: simplified client interface to a WebDAV server External requirements: davlib <> egenix MX libraries <> rp_util (local) utilwebdavresponseparser (local) """ import base64, traceback, types, string, os import mx.DateTime as mxDT import davlib import rp_util, utilwebdavresponseparser as UWDRP class webdavfsutil: """parent to webdav connection""" __HOST = '' __PORT = 0 __USER = '' __PASSWORD = '' __HDR_ADDTL = {} __curdir = '' #__davconn = None def __init__(self, host='', port=None, user='', password='', blnUseAuthentication = False): self.__HOST = host self.__PORT = port self.__USER = user self.__PASSWORD = password if blnUseAuthentication == True: strbase64 = base64.encodestring('%s:%s' % (self.__USER, self.__PASSWORD)) #FIXED: base64.encodestring was adding \n to value, which corrupted binary uploads self.__HDR_ADDTL = {"Authorization": "Basic %s" % string.strip(strbase64)} #self.__davconn = davconn(self.__HOST, self.__PORT) def __del__(self): #if self.__davconn != None: #self.__davconn.close() pass #class __davconn: #"""return new WebDAV connection to follow HTTP rules""" #def __init__(self): #return davconn(self.__HOST, self.__PORT) def curdir(self): """fake current dir""" if len(self.__curdir) == 0: self.__curdir = '/' return self.__curdir def chdir(self, strPath=''): """change curdir to new path""" self.__curdir = strPath def join(self, *args): """join 2 or more paths""" strTemp = string.join(args, '/') while string.find(strTemp, '//') != -1: strTemp = string.replace(strTemp, '//', '/') return strTemp def __getallpropsforURL(self, strPath='', depth=0): """get the allprops (multistatus) response for URL""" vReturn = None try: vResponse = davconn(self.__HOST, self.__PORT).allprops(strPath, depth, extra_hdrs=self.__HDR_ADDTL) #parse response if vResponse.status >= 400: #err vReturn = None elif vResponse.status == 207: #multistatus vReturn = vResponse.read() else: #unexpected! vReturn = None except: print traceback.print_exc() vReturn = None return vReturn def isdir(self, strpath): """connect to server, get response, parse, return true if collection""" vReturn = None try: vResponse = davconn(self.__HOST, self.__PORT).allprops(strpath, 0, extra_hdrs=self.__HDR_ADDTL) #vResponse = davlib.DAVResponse() #parse response if vResponse.status >= 400: #err vReturn = None elif vResponse.status == 207: #multistatus vReturn = UWDRP.IsACollection(vResponse.read()) if vReturn != True: vReturn = False else: #unexpected! vReturn = None except: print traceback.print_exc() vReturn = None return vReturn def isfile(self, strpath): """connect to server, get response, parse, return true if collection""" vReturn = None try: vResponse = davconn(self.__HOST, self.__PORT).allprops(strpath, 0, extra_hdrs=self.__HDR_ADDTL) #vResponse = davlib.DAVResponse() #parse response if vResponse.status >= 400: #err vReturn = None elif vResponse.status == 207: #multistatus vReturn = UWDRP.IsACollection(vResponse.read()) if vReturn != None: vReturn = not vReturn else: #unexpected! vReturn = None except: print traceback.print_exc() vReturn = None return vReturn def exists(self, strpath): """returns None on any errors; otherwise T/F""" try: vResponse = davconn(self.__HOST, self.__PORT).allprops(strpath, 0, extra_hdrs=self.__HDR_ADDTL) #vResponse = davlib.DAVResponse() if vResponse.status == 404: return False elif vResponse.status >= 400: #err return None elif vResponse.status == 207: #multistatus return True else: #unexpected! return None except: print traceback.print_exc() return None def getctime(self, strPath=''): """get created time (creationdate)""" try: vResponse = self.__getallpropsforURL(strPath) if vResponse != None: #parse for creationdate strXPath = './{DAV:}response/{DAV:}propstat/{DAV:}prop/{DAV:}creationdate' vCD = UWDRP.GetAnyValue(vResponse, strXPath) if vCD != None: #it's a date! dtX = mxDT.Parser.DateTimeFromString(vCD) return dtX #fail out return None except: print traceback.print_exc() return None def getmtime(self, strPath=''): """get modified time (getlastmodified)""" try: vResponse = self.__getallpropsforURL(strPath) if vResponse != None: #parse for creationdate strXPath = './{DAV:}response/{DAV:}propstat/{DAV:}prop/{DAV:}getlastmodified' vCD = UWDRP.GetAnyValue(vResponse, strXPath) if vCD != None: #it's a date! dtX = mxDT.Parser.DateTimeFromString(vCD) return dtX #fail out return None except: print traceback.print_exc() return None def getsize(self, strPath=''): """get modified time (getlastmodified)""" try: vResponse = self.__getallpropsforURL(strPath) if vResponse != None: #parse for creationdate strXPath = './{DAV:}response/{DAV:}propstat/{DAV:}prop/{DAV:}getcontentlength' vCD = UWDRP.GetAnyValue(vResponse, strXPath) if vCD != None: #got a value! return int(vCD) #fail out return None except: print traceback.print_exc() return None def listdir(self, strPath=''): """list contents of specified URL""" #gets all files/collections under URL try: assert len(strPath) > 0, 'XPath must have at least 1 character' vResponse = self.__getallpropsforURL(strPath, 1) if vResponse != None: #parse for list of all URLs lURLs = UWDRP.GetListOfXFromResponse(vResponse, UWDRP.STR_HREF) if lURLs == None: return None #remove parent from collection try: #append trailing slash if missing if strPath[-1] != '/': strPath += '/' lURLs.remove(strPath) except ValueError: pass except: print traceback.print_exc() #remove parent from URLs, & # remove leading & trailing slashes, to conform with os.listdir conventions #lURLs2 = map(string.strip, lURLs, '/') #AJIX: why doesn't this work? Only first item gets the treatment... for intX in range(len(lURLs)): lURLs[intX] = lURLs[intX][len(strPath):] lURLs[intX] = string.strip(lURLs[intX], '/') #return! return lURLs except: print traceback.print_exc() return None def rmdir(self, strPath=''): """remove a collection""" try: vResponse = davconn(self.__HOST, self.__PORT).delete(strPath, self.__HDR_ADDTL) if vResponse.status == 204: #default success code return True elif vResponse.status == 207: #multi-status; probably failure #TODO: parse to get msg return False else: return False except: print traceback.print_exc() return None def unlink(self, strPath=''): """remove a file (identical to rmdir)""" try: vResponse = davconn(self.__HOST, self.__PORT).delete(strPath, self.__HDR_ADDTL) if vResponse.status == 204: #default success code return True elif vResponse.status == 207: #multi-status; probably failure #TODO: parse to get msg return False else: return False except: print traceback.print_exc() return None def mkdir(self, strPath=''): """create a collection 201 (Created) - The collection or structured resource was created in its entirety. 403 (Forbidden) - This indicates at least one of two conditions: 1) the server does not allow the creation of collections at the given location in its namespace, or 2) the parent collection of the Request-URI exists but cannot accept members. 405 (Method Not Allowed) - MKCOL can only be executed on a deleted/non-existent resource. 409 (Conflict) - A collection cannot be made at the Request-URI until one or more intermediate collections have been created. 415 (Unsupported Media Type)- The server does not support the request type of the body. 507 (Insufficient Storage) - The resource does not have sufficient space to record the state of the resource after the execution of this method.""" try: vResponse = davconn(self.__HOST, self.__PORT).mkcol(strPath, self.__HDR_ADDTL) if vResponse.status == 201: return True elif vResponse.status in [403, 405, 409, 415, 507]: return False else: print 'mkdir(%s)==%s' % (strPath, vResponse.status) return None except: print traceback.print_exc() return None def upload(self, strServerPath='', strLocalPath='', blnBinary=None): """ 'put' a file on server NOTE: strServerPath must be FULL PATH starting at the root (/)""" #def put(self, url, contents, content_type=None, content_enc=None, extra_hdrs={ }): try: #require both paths if len(strLocalPath) == 0 or len(strLocalPath) == 0: return False #require that local path exists if os.path.exists(strLocalPath) != True: return False #determine if text or binary blnIsBinary = False #check if user passed in value if blnBinary != None: blnIsBinary = bool(blnBinary) else: #test file if rp_util.istextfile(strLocalPath): blnIsBinary = False else: blnIsBinary = True #put! if not blnIsBinary: varData = file(strLocalPath, 'r').read() strContentType = None else: varData = file(strLocalPath, 'rb').read() strContentType = 'application/octet-stream' vResponse = davconn(self.__HOST, self.__PORT).put(strServerPath, varData, content_type = strContentType, extra_hdrs=self.__HDR_ADDTL) if vResponse.status == 201: return True elif vResponse.status == 204:# (No Content) #The source resource was successfully copied to a pre-existing destination resource. return True elif vResponse.status == 409: #(Conflict) return False else: return False except: traceback.print_exc() return None #def uploadbinary(self, strServerPath='', strLocalPath=''): #"""'put' a binary file on server #NOTE: strServerPath must be FULL PATH starting at the root (/)""" ##def put(self, url, contents, content_type=None, content_enc=None, extra_hdrs={ }): #try: ##require both paths #if len(strLocalPath) == 0 or len(strLocalPath) == 0: #return False ##require that local path exists #if os.path.exists(strLocalPath) != True: #return False ##put! ##AJIX: waiting on binary upload instructions... #vData = file(strLocalPath, 'rb').read() #vCT = 'application/octet-stream' #Content-Type ##self.__HDR_ADDTL['Content-Length'] = str(len(vData)) #handled by httplib ##self.__HDR_ADDTL['Content-Encoding'] = 'None' #extraneous? #vResponse = davconn(self.__HOST, self.__PORT).put(strServerPath, #vData, #content_type = vCT, #extra_hdrs=self.__HDR_ADDTL) #if vResponse.status == 201: #return True #elif vResponse.status == 204:# (No Content) ##The source resource was successfully copied to a pre-existing destination resource. #return True #elif vResponse.status == 409: #(Conflict) #return False #else: #print '%s: %s' % (vResponse.status, vResponse.reason) #return False #except: #traceback.print_exc() #return None class davconn(davlib.DAV): def _request(self, method, url, *rest, **kw): #print 'REQUEST:', method, url response = apply(davlib.DAV._request, (self, method, url) + rest, kw) return response if __name__ == '__main__': print __doc__