o ~_O@s:ddlmZeddlmZddlmZddlZddlZddl Z ddl Z ddl Z ddl Z ddlZ ddlZ ddlZddlZddlmZddlmZddlmZddlmZmZGd d d e jjZGd d d ejjZGd ddej j!Z"ej #de"ej #de"ej #de"ej #de"ej j$%gddS))standard_library)str)rangeN)config)log)util)BackendExceptionFatalBackendExceptionc@s eZdZdZddZddZdS)CustomMethodRequestz This request subclass allows explicit specification of the HTTP request method. Basic urllib.request.Request class chooses GET or POST depending on self.has_data() cOs&||_tjjj|g|Ri|dSN)methodurllibrequestRequest__init__)selfr argskwargsrB/usr/lib/python3/dist-packages/duplicity/backends/webdavbackend.pyr3s zCustomMethodRequest.__init__cCs|jSr )r rrrr get_method7szCustomMethodRequest.get_methodN)__name__ __module__ __qualname____doc__rrrrrrr -s r c@s$eZdZddZddZddZdS)VerifiedHTTPSConnectioncOsz ddladdlaWn tyttdwtjjj|g|Ri|t j |_ gd|_ |j sI|j D]}t j|}t j|rH||_ nq5|j r]t |j t js_ttd|j dSdS)Nrz%Missing socket or ssl python modules.)z~/.duplicity/cacert.pemz~/duplicity_cacert.pemz/etc/duplicity/cacert.pemz*Cacert database file '%s' is not readable.)socketssl ImportErrorr _httpclientHTTPSConnectionrrssl_cacert_file cacert_filecacert_candidatesospath expanduserisfileaccessR_OK)rrrr(rrrr<s*       z VerifiedHTTPSConnection.__init__cCst|j|jf|j}|jr||_|dtt vr2t j t j j |j tjd}|j||jd|_dStjr;ttd|j sJttdd|jt j|t j|j d|_dS)Ncreate_default_context)cafilecapath)server_hostnamezHOption '--ssl-cacert-path' is not supported with python 2.7.8 and below.aFor certificate verification with python 2.7.8 or earlier a cacert database file is needed in one of these locations: %s Hints: Consult the man page, chapter 'SSL Certificate Verification'. Consider using the options --ssl-cacert-file, --ssl-no-check-certificate .z, ) cert_reqsca_certs)rcreate_connectionhostporttimeout _tunnel_hostsocktunneldirrr-Purpose SERVER_AUTHr%rssl_cacert_path wrap_socketr r joinr& CERT_REQUIRED)rr8contextrrrconnectWs0     zVerifiedHTTPSConnection.connectc OsRztjjj|g|Ri|WStjy(}z tdt|t j j d}~ww)NzSSL failed: %s) r!r"r#rrSSLErrorrruexcr ErrorCode backend_error)rrrerrrr{szVerifiedHTTPSConnection.requestN)rrrrrBrrrrrr;s $rc@seZdZdZ dZ ddZddZddZd d Zd*d d Z ddZ d+ddZ ddZ ddZ ddZddZddZddZd d!Zd"d#Zd$d%Zd&d'Zd(d)ZdS), WebDAVBackendz}Backend for accessing a WebDAV repository. webdav backend contributed in 2006 by Jesper Zedlitz z_cCstjj||ddi|_||_d|_d|_|j|_| |_ | |j |_ ttdtjfttd|j|jfttd|j fd|_dS)N Connectionz keep-alivezUsing WebDAV protocol %szUsing WebDAV host %s port %szUsing WebDAV directory %s) duplicitybackendBackendrheaders parsed_urldigest_challengedigest_auth_handlerusername get_passwordpassword sanitize_pathr( directoryrInfor r webdav_protohostnamer5conn)rrNrrrrs   zWebDAVBackend.__init__cCs"|rtd}|d|dSdS)Nz/+/)recompilesub)rr(foldpathrrrrTs zWebDAVBackend.sanitize_pathcCs(d}|D] }|j|jkr||j}q|S)N)nodeType TEXT_NODEdata)rnodelistrcnoderrrgetTexts   zWebDAVBackend.getTextcCs|jdddS)NT)forced)rBrrrr_retry_cleanupszWebDAVBackend._retry_cleanupFcCs|s|jr|jj|jjkrdSttd|jj||jjdvr2t j |jj|jj |_dS|jjdvrUt jrIt j |jj|jj |_dSt|jj|jj |_dSttd|jj)z Connect or re-connect to the server, updates self.conn # reconnect on errors as a precaution, there are errors e.g. # "[Errno 32] Broken pipe" or SSl errors that render the connection unusable Nz WebDAV create connection on '%s')webdavr!)webdavshttpszWebDAV Unknown URI scheme: %s)rYr4rNrXrrVr _closeschemer!r"HTTPConnectionr5rssl_no_check_certificater#rr )rrgrrrrBs   zWebDAVBackend.connectcCs|jr |jdSdSr )rYcloserrrrrlszWebDAVBackend._closeNrcCs||tj|d}|jdur|||jd<t t d|||jft t dt t ||j ||||j|j }t t d|j|jf|jdvr|dkr|d d}||rtt d tj||d kr~tt d tj||_||jj|_|||j||d Stt d|jdkr||||||jd<t t dt t d|||jft t dt t ||j ||||j|j }t t d|j|jf|S)zi Wraps the connection.request method to retry once if authentication is required z/:~N Authorizationz&WebDAV %s %s request with headers: %s zWebDAV data length: %s z+WebDAV response status %s with reason '%s'.)i-i.PROPFINDlocationzWebDAV redirect to: %s z&WebDAV redirected 10 times. Giving up.z4WebDAV missing location header in redirect response.iz3WebDAV retry request with authentification headers.z'WebDAV %s %s request2 with headers: %s z,WebDAV response2 status %s with reason '%s'.) rlrBr parsequoterOget_digest_authorizationrMrrVr lenrrYr getresponsestatusreason getheaderrpNoticeunquoter rJrK ParsedUrlrNrTr(rUreadget_authorization)rr r(rb redirected quoted_pathresponse redirect_urlrrrrs@       zWebDAVBackend.requestc Csz|dd}|dd\}}Wn tyYdSw|dddkr^z|WSty?ttd | YSt y]}zttd || WYd}~Sd}~ww|d krh| S| ||_ | |S) zY Fetches the auth header based on the requested method (basic or digest) zwww-authenticater_ ruN,r negotiatezkpython-kerberos needed to use kerberos authorization, falling back to basic auth.zWKerberos authorization failed: %s. Falling back to basic auth.basic)r}split ValueErrorlowerget_kerberos_authorizationrrWarnr get_basic_authorization Exceptionparse_digest_challengerOrx)rrr(auth_hdrtoken challengerGrrrrs.         zWebDAVBackend.get_authorizationcCstjtj|Sr )r rparse_keqv_listparse_http_list)rchallenge_stringrrrrsz$WebDAVBackend.parse_digest_challengecCs<ddl}|d|jj\}}||d||}d|S)NrzHTTP@%sr_z Negotiate %s)kerberosauthGSSClientInitrYr4authGSSClientStepauthGSSClientResponse)rrr ctxtgtrrrrs   z(WebDAVBackend.get_kerberos_authorizationcCs*d|j|jf}dt|S)z/ Returns the basic auth header %s:%szBasic %s)rQrSbase64 b64encodeencodestripdecode)r auth_stringrrrrsz%WebDAVBackend.get_basic_authorizationc Cs|j}|jdur tj}|d|jj|j|j tj ||_|j dkr'dp(d}|j r4d|j |j fp6|j }d|||f}t|jj|}|j||j}d|S)z0 Returns the digest auth header Nrjrkr!rz %s://%s%sz Digest %s)rNrPr rHTTPPasswordMgrWithDefaultRealm add_passwordrYr4rQrSHTTPDigestAuthHandlerrmr5rXr _methodrrO) rr(u pw_managerrmrX dummy_url dummy_reqrrrrrx%s  z&WebDAVBackend.get_digest_authorizationc Cs2d}zzd|jd<|d|j|j}|jd=|jdkr.||gWW|r-|SS|jdvr<|}|n|j}|j}|t d||ft d|ft j j|}g}|d|d D]}||}|rw||qi|WW|r|SSty} z| d} ~ ww|r|ww) N1Depthrr)zBad status code %s reason %s.z%szd:hrefzD:href)rMrrUlistbodyr{rpmakedirrr|rrDebugxmldomminidom parseStringgetElementsByTagName taste_hrefappendr) rrdocumentr{r|rresulthreffilenamerGrrr_list9sL         zWebDAVBackend._listcCs|jd}|ddkr|dd}tdt|D]N}d|d|dd}d|jd<|d|}|jd=td ||j f|j d krgtt d ||d |}|j d krgt t d||j |j fqdS)z(Make (nested) directories on the server.rZr_rrurrrrzChecking existence dir %s: %drzCreating missing directory %sMKCOLzWebDAV MKCOL %s failed: %s %sN) rUrrryr?rMrrrVr{r rr|)rdirsidrresrrrr\s$         zWebDAVBackend.makedircCs||j}tjtj|}|j}t t d||f|j dur9|j |j j ks9d|j |j j f}t |||jrI||jdd}|SdS)z Internal helper to taste the given href node and, if it is a duplicity file, collect it as a result file. @return: A matching filename, or None if the href did not match. z.WebDAV path decoding and translation: %s -> %sNzReceived filename was in the form of a full url, but the hostname (%s) did not match that of the webdav backend url (%s) - aborting as a conservative safety measure. If this happens to you, please report the problemr_ru)rf childNodesrr rvurlparserr(rrr rXrNr startswithrUreplace)rr raw_filenamerNrmrrrrts$   zWebDAVBackend.taste_hrefc Cs|jt|}d}zQz7|d}|d|}|jdkr-t|||r(J|n|j}|j }|t t d||fWnt yP}z|d}~wwW|rZ|dSdS|rc|ww)NwbGETrz(WebDAV GET Bad status code %s reason %s.) rUrfsdecodeopenrr{shutil copyfileobjrpr|rr r) rremote_filename local_pathurlr target_filer{r|rGrrr_gets4        zWebDAVBackend._getc Cs|jt|}d}zLz2|d}|d||}|jdvr(||n|j}|j}|t t d||fWnt yK}z|d}~wwW|rU|dSdS|r^|ww)NrbPUT)rrz(WebDAV PUT Bad status code %s reason %s.) rUrrrrrr{rpr|rr r) r source_pathrrr source_filer{r|rGrrr_puts2     zWebDAVBackend._putc Cs|jt|}d}zDz*|d|}|jdvr ||n|j}|j}|tt d||fWnt yC}z|d}~wwW|rM|dSdS|rV|ww)NDELETE)rrz(WebDAV DEL Bad status code %s reason %s.) rUrrrr{rrpr|rr r)rrrrr{r|rGrrr_deletes0     zWebDAVBackend._delete)F)Nr)rrrrrrrTrfrhrBrlrrrrrrxrrrrrrrrrrrHs.  ,#% rHr!rkrirj)r!rkrirj)&futurerinstall_aliasesbuiltinsrrr http.clientr!r'r[rurllib.requestr urllib.parse urllib.errorxml.dom.minidomrduplicity.backendrJrrrduplicity.errorsrr rrr r"r#rrKrLrHregister_backend uses_netlocextendrrrrs8      I\