~bzr-pqm/bzr/bzr.dev

« back to all changes in this revision

Viewing changes to bzrlib/transport/sftp.py

(Martin Pool) Make it optional for transports to implement lock_read and lock_write by testing that they either work or raise TransportNotPossible.

Show diffs side-by-side

added added

removed removed

Lines of Context:
133
133
            pass
134
134
 
135
135
 
136
 
class SFTPTransport(Transport):
 
136
class SFTPUrlHandling(Transport):
 
137
    """Mix-in that does common handling of SSH/SFTP URLs."""
 
138
 
 
139
    def __init__(self, base):
 
140
        self._parse_url(base)
 
141
        base = self._unparse_url(self._path)
 
142
        if base[-1] != '/':
 
143
            base += '/'
 
144
        super(SFTPUrlHandling, self).__init__(base)
 
145
 
 
146
    def _parse_url(self, url):
 
147
        (self._scheme,
 
148
         self._username, self._password,
 
149
         self._host, self._port, self._path) = self._split_url(url)
 
150
 
 
151
    def _unparse_url(self, path):
 
152
        """Return a URL for a path relative to this transport.
 
153
        """
 
154
        path = urllib.quote(path)
 
155
        # handle homedir paths
 
156
        if not path.startswith('/'):
 
157
            path = "/~/" + path
 
158
        netloc = urllib.quote(self._host)
 
159
        if self._username is not None:
 
160
            netloc = '%s@%s' % (urllib.quote(self._username), netloc)
 
161
        if self._port is not None:
 
162
            netloc = '%s:%d' % (netloc, self._port)
 
163
        return urlparse.urlunparse((self._scheme, netloc, path, '', '', ''))
 
164
 
 
165
    def _split_url(self, url):
 
166
        (scheme, username, password, host, port, path) = split_url(url)
 
167
        ## assert scheme == 'sftp'
 
168
 
 
169
        # the initial slash should be removed from the path, and treated
 
170
        # as a homedir relative path (the path begins with a double slash
 
171
        # if it is absolute).
 
172
        # see draft-ietf-secsh-scp-sftp-ssh-uri-03.txt
 
173
        # RBC 20060118 we are not using this as its too user hostile. instead
 
174
        # we are following lftp and using /~/foo to mean '~/foo'.
 
175
        # handle homedir paths
 
176
        if path.startswith('/~/'):
 
177
            path = path[3:]
 
178
        elif path == '/~':
 
179
            path = ''
 
180
        return (scheme, username, password, host, port, path)
 
181
 
 
182
    def _remote_path(self, relpath):
 
183
        """Return the path to be passed along the sftp protocol for relpath.
 
184
        
 
185
        :param relpath: is a urlencoded string.
 
186
        """
 
187
        return self._combine_paths(self._path, relpath)
 
188
 
 
189
 
 
190
class SFTPTransport(SFTPUrlHandling):
137
191
    """Transport implementation for SFTP access."""
138
192
 
139
193
    _do_prefetch = _default_do_prefetch
155
209
    _max_request_size = 32768
156
210
 
157
211
    def __init__(self, base, clone_from=None):
158
 
        assert base.startswith('sftp://')
159
 
        self._parse_url(base)
160
 
        base = self._unparse_url()
161
 
        if base[-1] != '/':
162
 
            base += '/'
163
212
        super(SFTPTransport, self).__init__(base)
164
213
        if clone_from is None:
165
214
            self._sftp_connect()
228
277
        return path
229
278
 
230
279
    def relpath(self, abspath):
231
 
        username, password, host, port, path = self._split_url(abspath)
 
280
        scheme, username, password, host, port, path = self._split_url(abspath)
232
281
        error = []
233
282
        if (username != self._username):
234
283
            error.append('username mismatch')
692
741
        # that we have taken the lock.
693
742
        return SFTPLock(relpath, self)
694
743
 
695
 
    def _unparse_url(self, path=None):
696
 
        """Gives a url for a relative reference."""
697
 
        if path is None:
698
 
            path = self._path
699
 
        path = urllib.quote(path)
700
 
        # handle homedir paths
701
 
        if not path.startswith('/'):
702
 
            path = "/~/" + path
703
 
        netloc = urllib.quote(self._host)
704
 
        if self._username is not None:
705
 
            netloc = '%s@%s' % (urllib.quote(self._username), netloc)
706
 
        if self._port is not None:
707
 
            netloc = '%s:%d' % (netloc, self._port)
708
 
        return urlparse.urlunparse(('sftp', netloc, path, '', '', ''))
709
 
 
710
 
    def _split_url(self, url):
711
 
        (scheme, username, password, host, port, path) = split_url(url)
712
 
        assert scheme == 'sftp'
713
 
 
714
 
        # the initial slash should be removed from the path, and treated
715
 
        # as a homedir relative path (the path begins with a double slash
716
 
        # if it is absolute).
717
 
        # see draft-ietf-secsh-scp-sftp-ssh-uri-03.txt
718
 
        # RBC 20060118 we are not using this as its too user hostile. instead
719
 
        # we are following lftp and using /~/foo to mean '~/foo'.
720
 
        # handle homedir paths
721
 
        if path.startswith('/~/'):
722
 
            path = path[3:]
723
 
        elif path == '/~':
724
 
            path = ''
725
 
        return (username, password, host, port, path)
726
 
 
727
 
    def _parse_url(self, url):
728
 
        (self._username, self._password,
729
 
         self._host, self._port, self._path) = self._split_url(url)
730
 
 
731
744
    def _sftp_connect(self):
732
745
        """Connect to the remote sftp server.
733
746
        After this, self._sftp should have a valid connection (or