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