Correct the Bash completion script behaviour.
[dput.git] / dput / methods / http.py
blob9884b11f9349ba90b26e3cc6c943bbfb81209c79
1 # -*- coding: utf-8; -*-
3 # dput/methods/http.py
4 # Part of ‘dput’, a Debian package upload toolkit.
6 # This is free software, and you are welcome to redistribute it under
7 # certain conditions; see the end of this file for copyright
8 # information, grant of license, and disclaimer of warranty.
10 """ Implementation for HTTP upload method. """
12 from __future__ import absolute_import
14 import getpass
15 import httplib
16 import os
17 import sys
18 import urllib2
20 from ..helper import dputhelper
23 class PromptingPasswordMgr(urllib2.HTTPPasswordMgr):
24 """ Password manager that prompts at the terminal.
26 Custom HTTP password manager that prompts for a password using
27 getpass() if required, and mangles the saved URL so that only
28 one password is prompted for.
30 """
32 def __init__(self, username):
33 urllib2.HTTPPasswordMgr.__init__(self)
34 self.username = username
36 def find_user_password(self, realm, authuri):
37 # Hack so that we only prompt for a password once
38 authuri = self.reduce_uri(authuri)[0]
39 authinfo = urllib2.HTTPPasswordMgr.find_user_password(
40 self, realm, authuri)
41 if authinfo != (None, None):
42 return authinfo
44 password = getpass.getpass(" Password for %s:" % realm)
45 self.add_password(realm, authuri, self.username, password)
46 return (self.username, password)
49 class AuthHandlerHackAround:
50 """ Fake request and parent object. """
52 def __init__(self, url, resp_headers, pwman):
53 # fake request header dict
54 self.headers = {}
55 # data
56 self.url = url
57 self.resp_headers = resp_headers
58 self.authhandlers = []
59 self.timeout = {}
60 # digest untested
61 for authhandler_class in [
62 urllib2.HTTPBasicAuthHandler, urllib2.HTTPDigestAuthHandler]:
63 ah = authhandler_class(pwman)
64 ah.add_parent(self)
65 self.authhandlers.append(ah)
67 # fake request methods
68 def add_header(self, k, v):
69 self.headers[k] = v
71 def add_unredirected_header(self, k, v):
72 self.headers[k] = v
74 def get_full_url(self):
75 return self.url
77 # fake parent method
78 def open(self, *args, **keywords):
79 pass
81 # and what we really want
82 def get_auth_headers(self):
83 for ah in self.authhandlers:
84 try:
85 ah.http_error_401(self, None, 401, None, self.resp_headers)
86 except ValueError as e:
87 pass
88 if self.headers:
89 return self.headers
90 return self.headers
93 def upload(
94 fqdn, login, incoming, files_to_upload, debug, dummy,
95 progress=0, protocol="http"):
96 """ Upload the files via WebDAV. """
98 # note: requires >= python 2.4 httplib
100 # EXCEPTION HANDLING!
101 if protocol == 'https':
102 connclass = httplib.HTTPSConnection
103 elif protocol == 'http':
104 connclass = httplib.HTTPConnection
105 else:
106 sys.stderr.write("Wrong protocol for upload http[s].py method\n")
107 sys.exit(1)
108 if not incoming.startswith('/'):
109 incoming = '/' + incoming
110 if not incoming.endswith('/'):
111 incoming += '/'
112 unprocessed_files_to_upload = files_to_upload[:]
113 auth_headers = {}
114 pwman = PromptingPasswordMgr(login)
115 while unprocessed_files_to_upload:
116 afile = unprocessed_files_to_upload[0]
117 path_to_package, package_name = os.path.split(afile)
118 sys.stdout.write(" Uploading %s: " % package_name)
119 sys.stdout.flush()
120 try:
121 size = os.stat(afile).st_size
122 except:
123 sys.stderr.write("Determining size of file '%s' failed\n" % afile)
124 sys.exit(1)
125 f = open(afile, 'rb')
126 if progress:
127 f = dputhelper.FileWithProgress(
128 f, ptype=progress,
129 progressf=sys.stderr,
130 size=size)
131 url_path = incoming + package_name
132 url = "%s://%s%s" % (protocol, fqdn, url_path)
133 if debug:
134 sys.stdout.write("D: HTTP-PUT to URL: %s\n" % url)
135 conn = connclass(fqdn)
136 conn.putrequest("PUT", url_path, skip_accept_encoding=True)
137 # Host: should be automatic
138 conn.putheader('User-Agent', 'dput')
139 for k, v in auth_headers.items():
140 conn.putheader(k, v)
141 conn.putheader('Connection', 'close')
142 conn.putheader('Content-Length', str(size))
143 conn.endheaders()
144 pos = 0
145 while pos < size:
146 # sending in 64k steps (screws progress a bit)
147 s = f.read(65536)
148 conn.send(s)
149 pos += len(s)
150 f.close()
151 s = ""
152 res = conn.getresponse()
153 if res.status >= 200 and res.status < 300:
154 sys.stdout.write("done.\n")
155 del unprocessed_files_to_upload[0]
156 elif res.status == 401 and not auth_headers:
157 sys.stdout.write("need authentication.\n")
158 auth_headers = AuthHandlerHackAround(
159 url, res.msg, pwman).get_auth_headers()
160 else:
161 if res.status == 401:
162 sys.stdout.write(
163 "Upload failed as unauthorized: %s\n"
164 " Maybe wrong username or password?\n" % res.reason)
165 else:
166 sys.stdout.write(
167 "Upload failed: %d %s\n" % (res.status, res.reason))
168 sys.exit(1)
169 # must be done, but we're not interested
170 res.read()
173 # Copyright © 2015–2016 Ben Finney <bignose@debian.org>
174 # Copyright © 2008–2012 Y Giridhar Appaji Nag <appaji@debian.org>
175 # Copyright © 2007–2008 Thomas Viehmann <tv@beamnet.de>
177 # This is free software: you may copy, modify, and/or distribute this work
178 # under the terms of the GNU General Public License as published by the
179 # Free Software Foundation; version 2 of that license or any later version.
180 # No warranty expressed or implied. See the file ‘LICENSE.GPL-2’ for details.
183 # Local variables:
184 # coding: utf-8
185 # mode: python
186 # End:
187 # vim: fileencoding=utf-8 filetype=python :