##// END OF EJS Templates
prompt user for http authentication info...
Vadim Gelfer -
r2281:7761597b default
parent child Browse files
Show More
@@ -1,142 +1,169 b''
1 1 # httprepo.py - HTTP repository proxy classes for mercurial
2 2 #
3 3 # Copyright 2005 Matt Mackall <mpm@selenic.com>
4 4 #
5 5 # This software may be used and distributed according to the terms
6 6 # of the GNU General Public License, incorporated herein by reference.
7 7
8 8 from node import *
9 9 from remoterepo import *
10 10 from i18n import gettext as _
11 11 from demandload import *
12 12 demandload(globals(), "hg os urllib urllib2 urlparse zlib util httplib")
13 13
14 class passwordmgr(urllib2.HTTPPasswordMgr):
15 def __init__(self, ui):
16 urllib2.HTTPPasswordMgr.__init__(self)
17 self.ui = ui
18
19 def find_user_password(self, realm, authuri):
20 authinfo = urllib2.HTTPPasswordMgr.find_user_password(
21 self, realm, authuri)
22 if authinfo != (None, None):
23 return authinfo
24
25 self.ui.write(_("http authorization required\n"))
26 self.ui.status(_("realm: %s\n") % realm)
27 user = self.ui.prompt(_("user:"), default=None)
28 passwd = self.ui.getpass()
29
30 self.add_password(realm, authuri, user, passwd)
31 return (user, passwd)
32
14 33 class httprepository(remoterepository):
15 34 def __init__(self, ui, path):
16 35 # fix missing / after hostname
17 36 s = urlparse.urlsplit(path)
18 37 partial = s[2]
19 38 if not partial: partial = "/"
20 39 self.url = urlparse.urlunsplit((s[0], s[1], partial, '', ''))
21 40 self.ui = ui
22 41 no_list = [ "localhost", "127.0.0.1" ]
23 42 host = ui.config("http_proxy", "host")
24 43 if host is None:
25 44 host = os.environ.get("http_proxy")
26 45 if host and host.startswith('http://'):
27 46 host = host[7:]
28 47 user = ui.config("http_proxy", "user")
29 48 passwd = ui.config("http_proxy", "passwd")
30 49 no = ui.config("http_proxy", "no")
31 50 if no is None:
32 51 no = os.environ.get("no_proxy")
33 52 if no:
34 53 no_list = no_list + no.split(",")
35 54
36 55 no_proxy = 0
37 56 for h in no_list:
38 57 if (path.startswith("http://" + h + "/") or
39 58 path.startswith("http://" + h + ":") or
40 59 path == "http://" + h):
41 60 no_proxy = 1
42 61
43 62 # Note: urllib2 takes proxy values from the environment and those will
44 63 # take precedence
45 64 for env in ["HTTP_PROXY", "http_proxy", "no_proxy"]:
46 65 try:
47 66 if os.environ.has_key(env):
48 67 del os.environ[env]
49 68 except OSError:
50 69 pass
51 70
52 71 proxy_handler = urllib2.BaseHandler()
53 72 if host and not no_proxy:
54 73 proxy_handler = urllib2.ProxyHandler({"http" : "http://" + host})
55 74
56 authinfo = None
75 proxyauthinfo = None
57 76 if user and passwd:
58 77 passmgr = urllib2.HTTPPasswordMgrWithDefaultRealm()
59 78 passmgr.add_password(None, host, user, passwd)
60 authinfo = urllib2.ProxyBasicAuthHandler(passmgr)
79 proxyauthinfo = urllib2.ProxyBasicAuthHandler(passmgr)
61 80
62 opener = urllib2.build_opener(proxy_handler, authinfo)
81 if ui.interactive:
82 passmgr = passwordmgr(ui)
83 opener = urllib2.build_opener(
84 proxy_handler, proxyauthinfo,
85 urllib2.HTTPBasicAuthHandler(passmgr),
86 urllib2.HTTPDigestAuthHandler(passmgr))
87 else:
88 opener = urllib2.build_opener(proxy_handler, proxyauthinfo)
89
63 90 # 1.0 here is the _protocol_ version
64 91 opener.addheaders = [('User-agent', 'mercurial/proto-1.0')]
65 92 urllib2.install_opener(opener)
66 93
67 94 def dev(self):
68 95 return -1
69 96
70 97 def lock(self):
71 98 raise util.Abort(_('operation not supported over http'))
72 99
73 100 def do_cmd(self, cmd, **args):
74 101 self.ui.debug(_("sending %s command\n") % cmd)
75 102 q = {"cmd": cmd}
76 103 q.update(args)
77 104 qs = urllib.urlencode(q)
78 105 cu = "%s?%s" % (self.url, qs)
79 106 resp = urllib2.urlopen(cu)
80 107 proto = resp.headers['content-type']
81 108
82 109 # accept old "text/plain" and "application/hg-changegroup" for now
83 110 if not proto.startswith('application/mercurial') and \
84 111 not proto.startswith('text/plain') and \
85 112 not proto.startswith('application/hg-changegroup'):
86 113 raise hg.RepoError(_("'%s' does not appear to be an hg repository") %
87 114 self.url)
88 115
89 116 if proto.startswith('application/mercurial'):
90 117 version = proto[22:]
91 118 if float(version) > 0.1:
92 119 raise hg.RepoError(_("'%s' uses newer protocol %s") %
93 120 (self.url, version))
94 121
95 122 return resp
96 123
97 124 def heads(self):
98 125 d = self.do_cmd("heads").read()
99 126 try:
100 127 return map(bin, d[:-1].split(" "))
101 128 except:
102 129 self.ui.warn(_("unexpected response:\n") + d[:400] + "\n...\n")
103 130 raise
104 131
105 132 def branches(self, nodes):
106 133 n = " ".join(map(hex, nodes))
107 134 d = self.do_cmd("branches", nodes=n).read()
108 135 try:
109 136 br = [ tuple(map(bin, b.split(" "))) for b in d.splitlines() ]
110 137 return br
111 138 except:
112 139 self.ui.warn(_("unexpected response:\n") + d[:400] + "\n...\n")
113 140 raise
114 141
115 142 def between(self, pairs):
116 143 n = "\n".join(["-".join(map(hex, p)) for p in pairs])
117 144 d = self.do_cmd("between", pairs=n).read()
118 145 try:
119 146 p = [ l and map(bin, l.split(" ")) or [] for l in d.splitlines() ]
120 147 return p
121 148 except:
122 149 self.ui.warn(_("unexpected response:\n") + d[:400] + "\n...\n")
123 150 raise
124 151
125 152 def changegroup(self, nodes, kind):
126 153 n = " ".join(map(hex, nodes))
127 154 f = self.do_cmd("changegroup", roots=n)
128 155 bytes = 0
129 156
130 157 def zgenerator(f):
131 158 zd = zlib.decompressobj()
132 159 try:
133 160 for chnk in f:
134 161 yield zd.decompress(chnk)
135 162 except httplib.HTTPException, inst:
136 163 raise IOError(None, _('connection ended unexpectedly'))
137 164 yield zd.flush()
138 165
139 166 return util.chunkbuffer(zgenerator(util.filechunkiter(f)))
140 167
141 168 class httpsrepository(httprepository):
142 169 pass
@@ -1,281 +1,284 b''
1 1 # ui.py - user interface bits for mercurial
2 2 #
3 3 # Copyright 2005 Matt Mackall <mpm@selenic.com>
4 4 #
5 5 # This software may be used and distributed according to the terms
6 6 # of the GNU General Public License, incorporated herein by reference.
7 7
8 8 import ConfigParser
9 9 from i18n import gettext as _
10 10 from demandload import *
11 demandload(globals(), "errno os re smtplib socket sys tempfile util")
11 demandload(globals(), "errno getpass os re smtplib socket sys tempfile util")
12 12
13 13 class ui(object):
14 14 def __init__(self, verbose=False, debug=False, quiet=False,
15 15 interactive=True, traceback=False, parentui=None):
16 16 self.overlay = {}
17 17 if parentui is None:
18 18 # this is the parent of all ui children
19 19 self.parentui = None
20 20 self.cdata = ConfigParser.SafeConfigParser()
21 21 self.readconfig(util.rcpath())
22 22
23 23 self.quiet = self.configbool("ui", "quiet")
24 24 self.verbose = self.configbool("ui", "verbose")
25 25 self.debugflag = self.configbool("ui", "debug")
26 26 self.interactive = self.configbool("ui", "interactive", True)
27 27 self.traceback = traceback
28 28
29 29 self.updateopts(verbose, debug, quiet, interactive)
30 30 self.diffcache = None
31 31 self.header = []
32 32 self.prev_header = []
33 33 self.revlogopts = self.configrevlog()
34 34 else:
35 35 # parentui may point to an ui object which is already a child
36 36 self.parentui = parentui.parentui or parentui
37 37 parent_cdata = self.parentui.cdata
38 38 self.cdata = ConfigParser.SafeConfigParser(parent_cdata.defaults())
39 39 # make interpolation work
40 40 for section in parent_cdata.sections():
41 41 self.cdata.add_section(section)
42 42 for name, value in parent_cdata.items(section, raw=True):
43 43 self.cdata.set(section, name, value)
44 44
45 45 def __getattr__(self, key):
46 46 return getattr(self.parentui, key)
47 47
48 48 def updateopts(self, verbose=False, debug=False, quiet=False,
49 49 interactive=True, traceback=False):
50 50 self.quiet = (self.quiet or quiet) and not verbose and not debug
51 51 self.verbose = (self.verbose or verbose) or debug
52 52 self.debugflag = (self.debugflag or debug)
53 53 self.interactive = (self.interactive and interactive)
54 54 self.traceback = self.traceback or traceback
55 55
56 56 def readconfig(self, fn, root=None):
57 57 if isinstance(fn, basestring):
58 58 fn = [fn]
59 59 for f in fn:
60 60 try:
61 61 self.cdata.read(f)
62 62 except ConfigParser.ParsingError, inst:
63 63 raise util.Abort(_("Failed to parse %s\n%s") % (f, inst))
64 64 # translate paths relative to root (or home) into absolute paths
65 65 if root is None:
66 66 root = os.path.expanduser('~')
67 67 for name, path in self.configitems("paths"):
68 68 if path and path.find("://") == -1 and not os.path.isabs(path):
69 69 self.cdata.set("paths", name, os.path.join(root, path))
70 70
71 71 def setconfig(self, section, name, val):
72 72 self.overlay[(section, name)] = val
73 73
74 74 def config(self, section, name, default=None):
75 75 if self.overlay.has_key((section, name)):
76 76 return self.overlay[(section, name)]
77 77 if self.cdata.has_option(section, name):
78 78 try:
79 79 return self.cdata.get(section, name)
80 80 except ConfigParser.InterpolationError, inst:
81 81 raise util.Abort(_("Error in configuration:\n%s") % inst)
82 82 if self.parentui is None:
83 83 return default
84 84 else:
85 85 return self.parentui.config(section, name, default)
86 86
87 87 def configbool(self, section, name, default=False):
88 88 if self.overlay.has_key((section, name)):
89 89 return self.overlay[(section, name)]
90 90 if self.cdata.has_option(section, name):
91 91 try:
92 92 return self.cdata.getboolean(section, name)
93 93 except ConfigParser.InterpolationError, inst:
94 94 raise util.Abort(_("Error in configuration:\n%s") % inst)
95 95 if self.parentui is None:
96 96 return default
97 97 else:
98 98 return self.parentui.configbool(section, name, default)
99 99
100 100 def configitems(self, section):
101 101 items = {}
102 102 if self.parentui is not None:
103 103 items = dict(self.parentui.configitems(section))
104 104 if self.cdata.has_section(section):
105 105 try:
106 106 items.update(dict(self.cdata.items(section)))
107 107 except ConfigParser.InterpolationError, inst:
108 108 raise util.Abort(_("Error in configuration:\n%s") % inst)
109 109 x = items.items()
110 110 x.sort()
111 111 return x
112 112
113 113 def walkconfig(self, seen=None):
114 114 if seen is None:
115 115 seen = {}
116 116 for (section, name), value in self.overlay.iteritems():
117 117 yield section, name, value
118 118 seen[section, name] = 1
119 119 for section in self.cdata.sections():
120 120 for name, value in self.cdata.items(section):
121 121 if (section, name) in seen: continue
122 122 yield section, name, value.replace('\n', '\\n')
123 123 seen[section, name] = 1
124 124 if self.parentui is not None:
125 125 for parent in self.parentui.walkconfig(seen):
126 126 yield parent
127 127
128 128 def extensions(self):
129 129 return self.configitems("extensions")
130 130
131 131 def hgignorefiles(self):
132 132 result = []
133 133 cfgitems = self.configitems("ui")
134 134 for key, value in cfgitems:
135 135 if key == 'ignore' or key.startswith('ignore.'):
136 136 path = os.path.expanduser(value)
137 137 result.append(path)
138 138 return result
139 139
140 140 def configrevlog(self):
141 141 ret = {}
142 142 for x in self.configitems("revlog"):
143 143 k = x[0].lower()
144 144 ret[k] = x[1]
145 145 return ret
146 146 def diffopts(self):
147 147 if self.diffcache:
148 148 return self.diffcache
149 149 ret = { 'showfunc' : True, 'ignorews' : False}
150 150 for x in self.configitems("diff"):
151 151 k = x[0].lower()
152 152 v = x[1]
153 153 if v:
154 154 v = v.lower()
155 155 if v == 'true':
156 156 value = True
157 157 else:
158 158 value = False
159 159 ret[k] = value
160 160 self.diffcache = ret
161 161 return ret
162 162
163 163 def username(self):
164 164 """Return default username to be used in commits.
165 165
166 166 Searched in this order: $HGUSER, [ui] section of hgrcs, $EMAIL
167 167 and stop searching if one of these is set.
168 168 Abort if found username is an empty string to force specifying
169 169 the commit user elsewhere, e.g. with line option or repo hgrc.
170 170 If not found, use $LOGNAME or $USERNAME +"@full.hostname".
171 171 """
172 172 user = os.environ.get("HGUSER")
173 173 if user is None:
174 174 user = self.config("ui", "username")
175 175 if user is None:
176 176 user = os.environ.get("EMAIL")
177 177 if user is None:
178 178 user = os.environ.get("LOGNAME") or os.environ.get("USERNAME")
179 179 if user:
180 180 user = "%s@%s" % (user, socket.getfqdn())
181 181 if not user:
182 182 raise util.Abort(_("Please specify a username."))
183 183 return user
184 184
185 185 def shortuser(self, user):
186 186 """Return a short representation of a user name or email address."""
187 187 if not self.verbose: user = util.shortuser(user)
188 188 return user
189 189
190 190 def expandpath(self, loc):
191 191 """Return repository location relative to cwd or from [paths]"""
192 192 if loc.find("://") != -1 or os.path.exists(loc):
193 193 return loc
194 194
195 195 return self.config("paths", loc, loc)
196 196
197 197 def write(self, *args):
198 198 if self.header:
199 199 if self.header != self.prev_header:
200 200 self.prev_header = self.header
201 201 self.write(*self.header)
202 202 self.header = []
203 203 for a in args:
204 204 sys.stdout.write(str(a))
205 205
206 206 def write_header(self, *args):
207 207 for a in args:
208 208 self.header.append(str(a))
209 209
210 210 def write_err(self, *args):
211 211 try:
212 212 if not sys.stdout.closed: sys.stdout.flush()
213 213 for a in args:
214 214 sys.stderr.write(str(a))
215 215 except IOError, inst:
216 216 if inst.errno != errno.EPIPE:
217 217 raise
218 218
219 219 def flush(self):
220 220 try: sys.stdout.flush()
221 221 except: pass
222 222 try: sys.stderr.flush()
223 223 except: pass
224 224
225 225 def readline(self):
226 226 return sys.stdin.readline()[:-1]
227 def prompt(self, msg, pat, default="y"):
227 def prompt(self, msg, pat=None, default="y"):
228 228 if not self.interactive: return default
229 229 while 1:
230 230 self.write(msg, " ")
231 231 r = self.readline()
232 if re.match(pat, r):
232 if not pat or re.match(pat, r):
233 233 return r
234 234 else:
235 235 self.write(_("unrecognized response\n"))
236 def getpass(self, prompt=None, default=None):
237 if not self.interactive: return default
238 return getpass.getpass(prompt or _('password: '))
236 239 def status(self, *msg):
237 240 if not self.quiet: self.write(*msg)
238 241 def warn(self, *msg):
239 242 self.write_err(*msg)
240 243 def note(self, *msg):
241 244 if self.verbose: self.write(*msg)
242 245 def debug(self, *msg):
243 246 if self.debugflag: self.write(*msg)
244 247 def edit(self, text, user):
245 248 (fd, name) = tempfile.mkstemp(prefix="hg-editor-", suffix=".txt",
246 249 text=True)
247 250 try:
248 251 f = os.fdopen(fd, "w")
249 252 f.write(text)
250 253 f.close()
251 254
252 255 editor = (os.environ.get("HGEDITOR") or
253 256 self.config("ui", "editor") or
254 257 os.environ.get("EDITOR", "vi"))
255 258
256 259 util.system("%s \"%s\"" % (editor, name),
257 260 environ={'HGUSER': user},
258 261 onerr=util.Abort, errprefix=_("edit failed"))
259 262
260 263 f = open(name)
261 264 t = f.read()
262 265 f.close()
263 266 t = re.sub("(?m)^HG:.*\n", "", t)
264 267 finally:
265 268 os.unlink(name)
266 269
267 270 return t
268 271
269 272 def sendmail(self):
270 273 s = smtplib.SMTP()
271 274 s.connect(host = self.config('smtp', 'host', 'mail'),
272 275 port = int(self.config('smtp', 'port', 25)))
273 276 if self.configbool('smtp', 'tls'):
274 277 s.ehlo()
275 278 s.starttls()
276 279 s.ehlo()
277 280 username = self.config('smtp', 'username')
278 281 password = self.config('smtp', 'password')
279 282 if username and password:
280 283 s.login(username, password)
281 284 return s
General Comments 0
You need to be logged in to leave comments. Login now