##// END OF EJS Templates
url: remember http password database in ui object...
liscju -
r29378:fea71f66 default
parent child Browse files
Show More
@@ -1,1355 +1,1362
1 # ui.py - user interface bits for mercurial
1 # ui.py - user interface bits for mercurial
2 #
2 #
3 # Copyright 2005-2007 Matt Mackall <mpm@selenic.com>
3 # Copyright 2005-2007 Matt Mackall <mpm@selenic.com>
4 #
4 #
5 # This software may be used and distributed according to the terms of the
5 # This software may be used and distributed according to the terms of the
6 # GNU General Public License version 2 or any later version.
6 # GNU General Public License version 2 or any later version.
7
7
8 from __future__ import absolute_import
8 from __future__ import absolute_import
9
9
10 import errno
10 import errno
11 import getpass
11 import getpass
12 import inspect
12 import inspect
13 import os
13 import os
14 import re
14 import re
15 import socket
15 import socket
16 import sys
16 import sys
17 import tempfile
17 import tempfile
18 import traceback
18 import traceback
19
19
20 from .i18n import _
20 from .i18n import _
21 from .node import hex
21 from .node import hex
22
22
23 from . import (
23 from . import (
24 config,
24 config,
25 error,
25 error,
26 formatter,
26 formatter,
27 progress,
27 progress,
28 scmutil,
28 scmutil,
29 util,
29 util,
30 )
30 )
31
31
32 urlreq = util.urlreq
33
32 samplehgrcs = {
34 samplehgrcs = {
33 'user':
35 'user':
34 """# example user config (see "hg help config" for more info)
36 """# example user config (see "hg help config" for more info)
35 [ui]
37 [ui]
36 # name and email, e.g.
38 # name and email, e.g.
37 # username = Jane Doe <jdoe@example.com>
39 # username = Jane Doe <jdoe@example.com>
38 username =
40 username =
39
41
40 [extensions]
42 [extensions]
41 # uncomment these lines to enable some popular extensions
43 # uncomment these lines to enable some popular extensions
42 # (see "hg help extensions" for more info)
44 # (see "hg help extensions" for more info)
43 #
45 #
44 # pager =
46 # pager =
45 # color =""",
47 # color =""",
46
48
47 'cloned':
49 'cloned':
48 """# example repository config (see "hg help config" for more info)
50 """# example repository config (see "hg help config" for more info)
49 [paths]
51 [paths]
50 default = %s
52 default = %s
51
53
52 # path aliases to other clones of this repo in URLs or filesystem paths
54 # path aliases to other clones of this repo in URLs or filesystem paths
53 # (see "hg help config.paths" for more info)
55 # (see "hg help config.paths" for more info)
54 #
56 #
55 # default-push = ssh://jdoe@example.net/hg/jdoes-fork
57 # default-push = ssh://jdoe@example.net/hg/jdoes-fork
56 # my-fork = ssh://jdoe@example.net/hg/jdoes-fork
58 # my-fork = ssh://jdoe@example.net/hg/jdoes-fork
57 # my-clone = /home/jdoe/jdoes-clone
59 # my-clone = /home/jdoe/jdoes-clone
58
60
59 [ui]
61 [ui]
60 # name and email (local to this repository, optional), e.g.
62 # name and email (local to this repository, optional), e.g.
61 # username = Jane Doe <jdoe@example.com>
63 # username = Jane Doe <jdoe@example.com>
62 """,
64 """,
63
65
64 'local':
66 'local':
65 """# example repository config (see "hg help config" for more info)
67 """# example repository config (see "hg help config" for more info)
66 [paths]
68 [paths]
67 # path aliases to other clones of this repo in URLs or filesystem paths
69 # path aliases to other clones of this repo in URLs or filesystem paths
68 # (see "hg help config.paths" for more info)
70 # (see "hg help config.paths" for more info)
69 #
71 #
70 # default = http://example.com/hg/example-repo
72 # default = http://example.com/hg/example-repo
71 # default-push = ssh://jdoe@example.net/hg/jdoes-fork
73 # default-push = ssh://jdoe@example.net/hg/jdoes-fork
72 # my-fork = ssh://jdoe@example.net/hg/jdoes-fork
74 # my-fork = ssh://jdoe@example.net/hg/jdoes-fork
73 # my-clone = /home/jdoe/jdoes-clone
75 # my-clone = /home/jdoe/jdoes-clone
74
76
75 [ui]
77 [ui]
76 # name and email (local to this repository, optional), e.g.
78 # name and email (local to this repository, optional), e.g.
77 # username = Jane Doe <jdoe@example.com>
79 # username = Jane Doe <jdoe@example.com>
78 """,
80 """,
79
81
80 'global':
82 'global':
81 """# example system-wide hg config (see "hg help config" for more info)
83 """# example system-wide hg config (see "hg help config" for more info)
82
84
83 [extensions]
85 [extensions]
84 # uncomment these lines to enable some popular extensions
86 # uncomment these lines to enable some popular extensions
85 # (see "hg help extensions" for more info)
87 # (see "hg help extensions" for more info)
86 #
88 #
87 # blackbox =
89 # blackbox =
88 # color =
90 # color =
89 # pager =""",
91 # pager =""",
90 }
92 }
91
93
92 class ui(object):
94 class ui(object):
93 def __init__(self, src=None):
95 def __init__(self, src=None):
94 # _buffers: used for temporary capture of output
96 # _buffers: used for temporary capture of output
95 self._buffers = []
97 self._buffers = []
96 # 3-tuple describing how each buffer in the stack behaves.
98 # 3-tuple describing how each buffer in the stack behaves.
97 # Values are (capture stderr, capture subprocesses, apply labels).
99 # Values are (capture stderr, capture subprocesses, apply labels).
98 self._bufferstates = []
100 self._bufferstates = []
99 # When a buffer is active, defines whether we are expanding labels.
101 # When a buffer is active, defines whether we are expanding labels.
100 # This exists to prevent an extra list lookup.
102 # This exists to prevent an extra list lookup.
101 self._bufferapplylabels = None
103 self._bufferapplylabels = None
102 self.quiet = self.verbose = self.debugflag = self.tracebackflag = False
104 self.quiet = self.verbose = self.debugflag = self.tracebackflag = False
103 self._reportuntrusted = True
105 self._reportuntrusted = True
104 self._ocfg = config.config() # overlay
106 self._ocfg = config.config() # overlay
105 self._tcfg = config.config() # trusted
107 self._tcfg = config.config() # trusted
106 self._ucfg = config.config() # untrusted
108 self._ucfg = config.config() # untrusted
107 self._trustusers = set()
109 self._trustusers = set()
108 self._trustgroups = set()
110 self._trustgroups = set()
109 self.callhooks = True
111 self.callhooks = True
110 # Insecure server connections requested.
112 # Insecure server connections requested.
111 self.insecureconnections = False
113 self.insecureconnections = False
112
114
113 if src:
115 if src:
114 self.fout = src.fout
116 self.fout = src.fout
115 self.ferr = src.ferr
117 self.ferr = src.ferr
116 self.fin = src.fin
118 self.fin = src.fin
117
119
118 self._tcfg = src._tcfg.copy()
120 self._tcfg = src._tcfg.copy()
119 self._ucfg = src._ucfg.copy()
121 self._ucfg = src._ucfg.copy()
120 self._ocfg = src._ocfg.copy()
122 self._ocfg = src._ocfg.copy()
121 self._trustusers = src._trustusers.copy()
123 self._trustusers = src._trustusers.copy()
122 self._trustgroups = src._trustgroups.copy()
124 self._trustgroups = src._trustgroups.copy()
123 self.environ = src.environ
125 self.environ = src.environ
124 self.callhooks = src.callhooks
126 self.callhooks = src.callhooks
125 self.insecureconnections = src.insecureconnections
127 self.insecureconnections = src.insecureconnections
126 self.fixconfig()
128 self.fixconfig()
129
130 self.httppasswordmgrdb = src.httppasswordmgrdb
127 else:
131 else:
128 self.fout = sys.stdout
132 self.fout = sys.stdout
129 self.ferr = sys.stderr
133 self.ferr = sys.stderr
130 self.fin = sys.stdin
134 self.fin = sys.stdin
131
135
132 # shared read-only environment
136 # shared read-only environment
133 self.environ = os.environ
137 self.environ = os.environ
134 # we always trust global config files
138 # we always trust global config files
135 for f in scmutil.rcpath():
139 for f in scmutil.rcpath():
136 self.readconfig(f, trust=True)
140 self.readconfig(f, trust=True)
137
141
142 self.httppasswordmgrdb = urlreq.httppasswordmgrwithdefaultrealm()
143
138 def copy(self):
144 def copy(self):
139 return self.__class__(self)
145 return self.__class__(self)
140
146
141 def resetstate(self):
147 def resetstate(self):
142 """Clear internal state that shouldn't persist across commands"""
148 """Clear internal state that shouldn't persist across commands"""
143 if self._progbar:
149 if self._progbar:
144 self._progbar.resetstate() # reset last-print time of progress bar
150 self._progbar.resetstate() # reset last-print time of progress bar
151 self.httppasswordmgrdb = urlreq.httppasswordmgrwithdefaultrealm()
145
152
146 def formatter(self, topic, opts):
153 def formatter(self, topic, opts):
147 return formatter.formatter(self, topic, opts)
154 return formatter.formatter(self, topic, opts)
148
155
149 def _trusted(self, fp, f):
156 def _trusted(self, fp, f):
150 st = util.fstat(fp)
157 st = util.fstat(fp)
151 if util.isowner(st):
158 if util.isowner(st):
152 return True
159 return True
153
160
154 tusers, tgroups = self._trustusers, self._trustgroups
161 tusers, tgroups = self._trustusers, self._trustgroups
155 if '*' in tusers or '*' in tgroups:
162 if '*' in tusers or '*' in tgroups:
156 return True
163 return True
157
164
158 user = util.username(st.st_uid)
165 user = util.username(st.st_uid)
159 group = util.groupname(st.st_gid)
166 group = util.groupname(st.st_gid)
160 if user in tusers or group in tgroups or user == util.username():
167 if user in tusers or group in tgroups or user == util.username():
161 return True
168 return True
162
169
163 if self._reportuntrusted:
170 if self._reportuntrusted:
164 self.warn(_('not trusting file %s from untrusted '
171 self.warn(_('not trusting file %s from untrusted '
165 'user %s, group %s\n') % (f, user, group))
172 'user %s, group %s\n') % (f, user, group))
166 return False
173 return False
167
174
168 def readconfig(self, filename, root=None, trust=False,
175 def readconfig(self, filename, root=None, trust=False,
169 sections=None, remap=None):
176 sections=None, remap=None):
170 try:
177 try:
171 fp = open(filename)
178 fp = open(filename)
172 except IOError:
179 except IOError:
173 if not sections: # ignore unless we were looking for something
180 if not sections: # ignore unless we were looking for something
174 return
181 return
175 raise
182 raise
176
183
177 cfg = config.config()
184 cfg = config.config()
178 trusted = sections or trust or self._trusted(fp, filename)
185 trusted = sections or trust or self._trusted(fp, filename)
179
186
180 try:
187 try:
181 cfg.read(filename, fp, sections=sections, remap=remap)
188 cfg.read(filename, fp, sections=sections, remap=remap)
182 fp.close()
189 fp.close()
183 except error.ConfigError as inst:
190 except error.ConfigError as inst:
184 if trusted:
191 if trusted:
185 raise
192 raise
186 self.warn(_("ignored: %s\n") % str(inst))
193 self.warn(_("ignored: %s\n") % str(inst))
187
194
188 if self.plain():
195 if self.plain():
189 for k in ('debug', 'fallbackencoding', 'quiet', 'slash',
196 for k in ('debug', 'fallbackencoding', 'quiet', 'slash',
190 'logtemplate', 'statuscopies', 'style',
197 'logtemplate', 'statuscopies', 'style',
191 'traceback', 'verbose'):
198 'traceback', 'verbose'):
192 if k in cfg['ui']:
199 if k in cfg['ui']:
193 del cfg['ui'][k]
200 del cfg['ui'][k]
194 for k, v in cfg.items('defaults'):
201 for k, v in cfg.items('defaults'):
195 del cfg['defaults'][k]
202 del cfg['defaults'][k]
196 # Don't remove aliases from the configuration if in the exceptionlist
203 # Don't remove aliases from the configuration if in the exceptionlist
197 if self.plain('alias'):
204 if self.plain('alias'):
198 for k, v in cfg.items('alias'):
205 for k, v in cfg.items('alias'):
199 del cfg['alias'][k]
206 del cfg['alias'][k]
200 if self.plain('revsetalias'):
207 if self.plain('revsetalias'):
201 for k, v in cfg.items('revsetalias'):
208 for k, v in cfg.items('revsetalias'):
202 del cfg['revsetalias'][k]
209 del cfg['revsetalias'][k]
203 if self.plain('templatealias'):
210 if self.plain('templatealias'):
204 for k, v in cfg.items('templatealias'):
211 for k, v in cfg.items('templatealias'):
205 del cfg['templatealias'][k]
212 del cfg['templatealias'][k]
206
213
207 if trusted:
214 if trusted:
208 self._tcfg.update(cfg)
215 self._tcfg.update(cfg)
209 self._tcfg.update(self._ocfg)
216 self._tcfg.update(self._ocfg)
210 self._ucfg.update(cfg)
217 self._ucfg.update(cfg)
211 self._ucfg.update(self._ocfg)
218 self._ucfg.update(self._ocfg)
212
219
213 if root is None:
220 if root is None:
214 root = os.path.expanduser('~')
221 root = os.path.expanduser('~')
215 self.fixconfig(root=root)
222 self.fixconfig(root=root)
216
223
217 def fixconfig(self, root=None, section=None):
224 def fixconfig(self, root=None, section=None):
218 if section in (None, 'paths'):
225 if section in (None, 'paths'):
219 # expand vars and ~
226 # expand vars and ~
220 # translate paths relative to root (or home) into absolute paths
227 # translate paths relative to root (or home) into absolute paths
221 root = root or os.getcwd()
228 root = root or os.getcwd()
222 for c in self._tcfg, self._ucfg, self._ocfg:
229 for c in self._tcfg, self._ucfg, self._ocfg:
223 for n, p in c.items('paths'):
230 for n, p in c.items('paths'):
224 if not p:
231 if not p:
225 continue
232 continue
226 if '%%' in p:
233 if '%%' in p:
227 self.warn(_("(deprecated '%%' in path %s=%s from %s)\n")
234 self.warn(_("(deprecated '%%' in path %s=%s from %s)\n")
228 % (n, p, self.configsource('paths', n)))
235 % (n, p, self.configsource('paths', n)))
229 p = p.replace('%%', '%')
236 p = p.replace('%%', '%')
230 p = util.expandpath(p)
237 p = util.expandpath(p)
231 if not util.hasscheme(p) and not os.path.isabs(p):
238 if not util.hasscheme(p) and not os.path.isabs(p):
232 p = os.path.normpath(os.path.join(root, p))
239 p = os.path.normpath(os.path.join(root, p))
233 c.set("paths", n, p)
240 c.set("paths", n, p)
234
241
235 if section in (None, 'ui'):
242 if section in (None, 'ui'):
236 # update ui options
243 # update ui options
237 self.debugflag = self.configbool('ui', 'debug')
244 self.debugflag = self.configbool('ui', 'debug')
238 self.verbose = self.debugflag or self.configbool('ui', 'verbose')
245 self.verbose = self.debugflag or self.configbool('ui', 'verbose')
239 self.quiet = not self.debugflag and self.configbool('ui', 'quiet')
246 self.quiet = not self.debugflag and self.configbool('ui', 'quiet')
240 if self.verbose and self.quiet:
247 if self.verbose and self.quiet:
241 self.quiet = self.verbose = False
248 self.quiet = self.verbose = False
242 self._reportuntrusted = self.debugflag or self.configbool("ui",
249 self._reportuntrusted = self.debugflag or self.configbool("ui",
243 "report_untrusted", True)
250 "report_untrusted", True)
244 self.tracebackflag = self.configbool('ui', 'traceback', False)
251 self.tracebackflag = self.configbool('ui', 'traceback', False)
245
252
246 if section in (None, 'trusted'):
253 if section in (None, 'trusted'):
247 # update trust information
254 # update trust information
248 self._trustusers.update(self.configlist('trusted', 'users'))
255 self._trustusers.update(self.configlist('trusted', 'users'))
249 self._trustgroups.update(self.configlist('trusted', 'groups'))
256 self._trustgroups.update(self.configlist('trusted', 'groups'))
250
257
251 def backupconfig(self, section, item):
258 def backupconfig(self, section, item):
252 return (self._ocfg.backup(section, item),
259 return (self._ocfg.backup(section, item),
253 self._tcfg.backup(section, item),
260 self._tcfg.backup(section, item),
254 self._ucfg.backup(section, item),)
261 self._ucfg.backup(section, item),)
255 def restoreconfig(self, data):
262 def restoreconfig(self, data):
256 self._ocfg.restore(data[0])
263 self._ocfg.restore(data[0])
257 self._tcfg.restore(data[1])
264 self._tcfg.restore(data[1])
258 self._ucfg.restore(data[2])
265 self._ucfg.restore(data[2])
259
266
260 def setconfig(self, section, name, value, source=''):
267 def setconfig(self, section, name, value, source=''):
261 for cfg in (self._ocfg, self._tcfg, self._ucfg):
268 for cfg in (self._ocfg, self._tcfg, self._ucfg):
262 cfg.set(section, name, value, source)
269 cfg.set(section, name, value, source)
263 self.fixconfig(section=section)
270 self.fixconfig(section=section)
264
271
265 def _data(self, untrusted):
272 def _data(self, untrusted):
266 return untrusted and self._ucfg or self._tcfg
273 return untrusted and self._ucfg or self._tcfg
267
274
268 def configsource(self, section, name, untrusted=False):
275 def configsource(self, section, name, untrusted=False):
269 return self._data(untrusted).source(section, name) or 'none'
276 return self._data(untrusted).source(section, name) or 'none'
270
277
271 def config(self, section, name, default=None, untrusted=False):
278 def config(self, section, name, default=None, untrusted=False):
272 if isinstance(name, list):
279 if isinstance(name, list):
273 alternates = name
280 alternates = name
274 else:
281 else:
275 alternates = [name]
282 alternates = [name]
276
283
277 for n in alternates:
284 for n in alternates:
278 value = self._data(untrusted).get(section, n, None)
285 value = self._data(untrusted).get(section, n, None)
279 if value is not None:
286 if value is not None:
280 name = n
287 name = n
281 break
288 break
282 else:
289 else:
283 value = default
290 value = default
284
291
285 if self.debugflag and not untrusted and self._reportuntrusted:
292 if self.debugflag and not untrusted and self._reportuntrusted:
286 for n in alternates:
293 for n in alternates:
287 uvalue = self._ucfg.get(section, n)
294 uvalue = self._ucfg.get(section, n)
288 if uvalue is not None and uvalue != value:
295 if uvalue is not None and uvalue != value:
289 self.debug("ignoring untrusted configuration option "
296 self.debug("ignoring untrusted configuration option "
290 "%s.%s = %s\n" % (section, n, uvalue))
297 "%s.%s = %s\n" % (section, n, uvalue))
291 return value
298 return value
292
299
293 def configsuboptions(self, section, name, default=None, untrusted=False):
300 def configsuboptions(self, section, name, default=None, untrusted=False):
294 """Get a config option and all sub-options.
301 """Get a config option and all sub-options.
295
302
296 Some config options have sub-options that are declared with the
303 Some config options have sub-options that are declared with the
297 format "key:opt = value". This method is used to return the main
304 format "key:opt = value". This method is used to return the main
298 option and all its declared sub-options.
305 option and all its declared sub-options.
299
306
300 Returns a 2-tuple of ``(option, sub-options)``, where `sub-options``
307 Returns a 2-tuple of ``(option, sub-options)``, where `sub-options``
301 is a dict of defined sub-options where keys and values are strings.
308 is a dict of defined sub-options where keys and values are strings.
302 """
309 """
303 data = self._data(untrusted)
310 data = self._data(untrusted)
304 main = data.get(section, name, default)
311 main = data.get(section, name, default)
305 if self.debugflag and not untrusted and self._reportuntrusted:
312 if self.debugflag and not untrusted and self._reportuntrusted:
306 uvalue = self._ucfg.get(section, name)
313 uvalue = self._ucfg.get(section, name)
307 if uvalue is not None and uvalue != main:
314 if uvalue is not None and uvalue != main:
308 self.debug('ignoring untrusted configuration option '
315 self.debug('ignoring untrusted configuration option '
309 '%s.%s = %s\n' % (section, name, uvalue))
316 '%s.%s = %s\n' % (section, name, uvalue))
310
317
311 sub = {}
318 sub = {}
312 prefix = '%s:' % name
319 prefix = '%s:' % name
313 for k, v in data.items(section):
320 for k, v in data.items(section):
314 if k.startswith(prefix):
321 if k.startswith(prefix):
315 sub[k[len(prefix):]] = v
322 sub[k[len(prefix):]] = v
316
323
317 if self.debugflag and not untrusted and self._reportuntrusted:
324 if self.debugflag and not untrusted and self._reportuntrusted:
318 for k, v in sub.items():
325 for k, v in sub.items():
319 uvalue = self._ucfg.get(section, '%s:%s' % (name, k))
326 uvalue = self._ucfg.get(section, '%s:%s' % (name, k))
320 if uvalue is not None and uvalue != v:
327 if uvalue is not None and uvalue != v:
321 self.debug('ignoring untrusted configuration option '
328 self.debug('ignoring untrusted configuration option '
322 '%s:%s.%s = %s\n' % (section, name, k, uvalue))
329 '%s:%s.%s = %s\n' % (section, name, k, uvalue))
323
330
324 return main, sub
331 return main, sub
325
332
326 def configpath(self, section, name, default=None, untrusted=False):
333 def configpath(self, section, name, default=None, untrusted=False):
327 'get a path config item, expanded relative to repo root or config file'
334 'get a path config item, expanded relative to repo root or config file'
328 v = self.config(section, name, default, untrusted)
335 v = self.config(section, name, default, untrusted)
329 if v is None:
336 if v is None:
330 return None
337 return None
331 if not os.path.isabs(v) or "://" not in v:
338 if not os.path.isabs(v) or "://" not in v:
332 src = self.configsource(section, name, untrusted)
339 src = self.configsource(section, name, untrusted)
333 if ':' in src:
340 if ':' in src:
334 base = os.path.dirname(src.rsplit(':')[0])
341 base = os.path.dirname(src.rsplit(':')[0])
335 v = os.path.join(base, os.path.expanduser(v))
342 v = os.path.join(base, os.path.expanduser(v))
336 return v
343 return v
337
344
338 def configbool(self, section, name, default=False, untrusted=False):
345 def configbool(self, section, name, default=False, untrusted=False):
339 """parse a configuration element as a boolean
346 """parse a configuration element as a boolean
340
347
341 >>> u = ui(); s = 'foo'
348 >>> u = ui(); s = 'foo'
342 >>> u.setconfig(s, 'true', 'yes')
349 >>> u.setconfig(s, 'true', 'yes')
343 >>> u.configbool(s, 'true')
350 >>> u.configbool(s, 'true')
344 True
351 True
345 >>> u.setconfig(s, 'false', 'no')
352 >>> u.setconfig(s, 'false', 'no')
346 >>> u.configbool(s, 'false')
353 >>> u.configbool(s, 'false')
347 False
354 False
348 >>> u.configbool(s, 'unknown')
355 >>> u.configbool(s, 'unknown')
349 False
356 False
350 >>> u.configbool(s, 'unknown', True)
357 >>> u.configbool(s, 'unknown', True)
351 True
358 True
352 >>> u.setconfig(s, 'invalid', 'somevalue')
359 >>> u.setconfig(s, 'invalid', 'somevalue')
353 >>> u.configbool(s, 'invalid')
360 >>> u.configbool(s, 'invalid')
354 Traceback (most recent call last):
361 Traceback (most recent call last):
355 ...
362 ...
356 ConfigError: foo.invalid is not a boolean ('somevalue')
363 ConfigError: foo.invalid is not a boolean ('somevalue')
357 """
364 """
358
365
359 v = self.config(section, name, None, untrusted)
366 v = self.config(section, name, None, untrusted)
360 if v is None:
367 if v is None:
361 return default
368 return default
362 if isinstance(v, bool):
369 if isinstance(v, bool):
363 return v
370 return v
364 b = util.parsebool(v)
371 b = util.parsebool(v)
365 if b is None:
372 if b is None:
366 raise error.ConfigError(_("%s.%s is not a boolean ('%s')")
373 raise error.ConfigError(_("%s.%s is not a boolean ('%s')")
367 % (section, name, v))
374 % (section, name, v))
368 return b
375 return b
369
376
370 def configint(self, section, name, default=None, untrusted=False):
377 def configint(self, section, name, default=None, untrusted=False):
371 """parse a configuration element as an integer
378 """parse a configuration element as an integer
372
379
373 >>> u = ui(); s = 'foo'
380 >>> u = ui(); s = 'foo'
374 >>> u.setconfig(s, 'int1', '42')
381 >>> u.setconfig(s, 'int1', '42')
375 >>> u.configint(s, 'int1')
382 >>> u.configint(s, 'int1')
376 42
383 42
377 >>> u.setconfig(s, 'int2', '-42')
384 >>> u.setconfig(s, 'int2', '-42')
378 >>> u.configint(s, 'int2')
385 >>> u.configint(s, 'int2')
379 -42
386 -42
380 >>> u.configint(s, 'unknown', 7)
387 >>> u.configint(s, 'unknown', 7)
381 7
388 7
382 >>> u.setconfig(s, 'invalid', 'somevalue')
389 >>> u.setconfig(s, 'invalid', 'somevalue')
383 >>> u.configint(s, 'invalid')
390 >>> u.configint(s, 'invalid')
384 Traceback (most recent call last):
391 Traceback (most recent call last):
385 ...
392 ...
386 ConfigError: foo.invalid is not an integer ('somevalue')
393 ConfigError: foo.invalid is not an integer ('somevalue')
387 """
394 """
388
395
389 v = self.config(section, name, None, untrusted)
396 v = self.config(section, name, None, untrusted)
390 if v is None:
397 if v is None:
391 return default
398 return default
392 try:
399 try:
393 return int(v)
400 return int(v)
394 except ValueError:
401 except ValueError:
395 raise error.ConfigError(_("%s.%s is not an integer ('%s')")
402 raise error.ConfigError(_("%s.%s is not an integer ('%s')")
396 % (section, name, v))
403 % (section, name, v))
397
404
398 def configbytes(self, section, name, default=0, untrusted=False):
405 def configbytes(self, section, name, default=0, untrusted=False):
399 """parse a configuration element as a quantity in bytes
406 """parse a configuration element as a quantity in bytes
400
407
401 Units can be specified as b (bytes), k or kb (kilobytes), m or
408 Units can be specified as b (bytes), k or kb (kilobytes), m or
402 mb (megabytes), g or gb (gigabytes).
409 mb (megabytes), g or gb (gigabytes).
403
410
404 >>> u = ui(); s = 'foo'
411 >>> u = ui(); s = 'foo'
405 >>> u.setconfig(s, 'val1', '42')
412 >>> u.setconfig(s, 'val1', '42')
406 >>> u.configbytes(s, 'val1')
413 >>> u.configbytes(s, 'val1')
407 42
414 42
408 >>> u.setconfig(s, 'val2', '42.5 kb')
415 >>> u.setconfig(s, 'val2', '42.5 kb')
409 >>> u.configbytes(s, 'val2')
416 >>> u.configbytes(s, 'val2')
410 43520
417 43520
411 >>> u.configbytes(s, 'unknown', '7 MB')
418 >>> u.configbytes(s, 'unknown', '7 MB')
412 7340032
419 7340032
413 >>> u.setconfig(s, 'invalid', 'somevalue')
420 >>> u.setconfig(s, 'invalid', 'somevalue')
414 >>> u.configbytes(s, 'invalid')
421 >>> u.configbytes(s, 'invalid')
415 Traceback (most recent call last):
422 Traceback (most recent call last):
416 ...
423 ...
417 ConfigError: foo.invalid is not a byte quantity ('somevalue')
424 ConfigError: foo.invalid is not a byte quantity ('somevalue')
418 """
425 """
419
426
420 value = self.config(section, name)
427 value = self.config(section, name)
421 if value is None:
428 if value is None:
422 if not isinstance(default, str):
429 if not isinstance(default, str):
423 return default
430 return default
424 value = default
431 value = default
425 try:
432 try:
426 return util.sizetoint(value)
433 return util.sizetoint(value)
427 except error.ParseError:
434 except error.ParseError:
428 raise error.ConfigError(_("%s.%s is not a byte quantity ('%s')")
435 raise error.ConfigError(_("%s.%s is not a byte quantity ('%s')")
429 % (section, name, value))
436 % (section, name, value))
430
437
431 def configlist(self, section, name, default=None, untrusted=False):
438 def configlist(self, section, name, default=None, untrusted=False):
432 """parse a configuration element as a list of comma/space separated
439 """parse a configuration element as a list of comma/space separated
433 strings
440 strings
434
441
435 >>> u = ui(); s = 'foo'
442 >>> u = ui(); s = 'foo'
436 >>> u.setconfig(s, 'list1', 'this,is "a small" ,test')
443 >>> u.setconfig(s, 'list1', 'this,is "a small" ,test')
437 >>> u.configlist(s, 'list1')
444 >>> u.configlist(s, 'list1')
438 ['this', 'is', 'a small', 'test']
445 ['this', 'is', 'a small', 'test']
439 """
446 """
440
447
441 def _parse_plain(parts, s, offset):
448 def _parse_plain(parts, s, offset):
442 whitespace = False
449 whitespace = False
443 while offset < len(s) and (s[offset].isspace() or s[offset] == ','):
450 while offset < len(s) and (s[offset].isspace() or s[offset] == ','):
444 whitespace = True
451 whitespace = True
445 offset += 1
452 offset += 1
446 if offset >= len(s):
453 if offset >= len(s):
447 return None, parts, offset
454 return None, parts, offset
448 if whitespace:
455 if whitespace:
449 parts.append('')
456 parts.append('')
450 if s[offset] == '"' and not parts[-1]:
457 if s[offset] == '"' and not parts[-1]:
451 return _parse_quote, parts, offset + 1
458 return _parse_quote, parts, offset + 1
452 elif s[offset] == '"' and parts[-1][-1] == '\\':
459 elif s[offset] == '"' and parts[-1][-1] == '\\':
453 parts[-1] = parts[-1][:-1] + s[offset]
460 parts[-1] = parts[-1][:-1] + s[offset]
454 return _parse_plain, parts, offset + 1
461 return _parse_plain, parts, offset + 1
455 parts[-1] += s[offset]
462 parts[-1] += s[offset]
456 return _parse_plain, parts, offset + 1
463 return _parse_plain, parts, offset + 1
457
464
458 def _parse_quote(parts, s, offset):
465 def _parse_quote(parts, s, offset):
459 if offset < len(s) and s[offset] == '"': # ""
466 if offset < len(s) and s[offset] == '"': # ""
460 parts.append('')
467 parts.append('')
461 offset += 1
468 offset += 1
462 while offset < len(s) and (s[offset].isspace() or
469 while offset < len(s) and (s[offset].isspace() or
463 s[offset] == ','):
470 s[offset] == ','):
464 offset += 1
471 offset += 1
465 return _parse_plain, parts, offset
472 return _parse_plain, parts, offset
466
473
467 while offset < len(s) and s[offset] != '"':
474 while offset < len(s) and s[offset] != '"':
468 if (s[offset] == '\\' and offset + 1 < len(s)
475 if (s[offset] == '\\' and offset + 1 < len(s)
469 and s[offset + 1] == '"'):
476 and s[offset + 1] == '"'):
470 offset += 1
477 offset += 1
471 parts[-1] += '"'
478 parts[-1] += '"'
472 else:
479 else:
473 parts[-1] += s[offset]
480 parts[-1] += s[offset]
474 offset += 1
481 offset += 1
475
482
476 if offset >= len(s):
483 if offset >= len(s):
477 real_parts = _configlist(parts[-1])
484 real_parts = _configlist(parts[-1])
478 if not real_parts:
485 if not real_parts:
479 parts[-1] = '"'
486 parts[-1] = '"'
480 else:
487 else:
481 real_parts[0] = '"' + real_parts[0]
488 real_parts[0] = '"' + real_parts[0]
482 parts = parts[:-1]
489 parts = parts[:-1]
483 parts.extend(real_parts)
490 parts.extend(real_parts)
484 return None, parts, offset
491 return None, parts, offset
485
492
486 offset += 1
493 offset += 1
487 while offset < len(s) and s[offset] in [' ', ',']:
494 while offset < len(s) and s[offset] in [' ', ',']:
488 offset += 1
495 offset += 1
489
496
490 if offset < len(s):
497 if offset < len(s):
491 if offset + 1 == len(s) and s[offset] == '"':
498 if offset + 1 == len(s) and s[offset] == '"':
492 parts[-1] += '"'
499 parts[-1] += '"'
493 offset += 1
500 offset += 1
494 else:
501 else:
495 parts.append('')
502 parts.append('')
496 else:
503 else:
497 return None, parts, offset
504 return None, parts, offset
498
505
499 return _parse_plain, parts, offset
506 return _parse_plain, parts, offset
500
507
501 def _configlist(s):
508 def _configlist(s):
502 s = s.rstrip(' ,')
509 s = s.rstrip(' ,')
503 if not s:
510 if not s:
504 return []
511 return []
505 parser, parts, offset = _parse_plain, [''], 0
512 parser, parts, offset = _parse_plain, [''], 0
506 while parser:
513 while parser:
507 parser, parts, offset = parser(parts, s, offset)
514 parser, parts, offset = parser(parts, s, offset)
508 return parts
515 return parts
509
516
510 result = self.config(section, name, untrusted=untrusted)
517 result = self.config(section, name, untrusted=untrusted)
511 if result is None:
518 if result is None:
512 result = default or []
519 result = default or []
513 if isinstance(result, basestring):
520 if isinstance(result, basestring):
514 result = _configlist(result.lstrip(' ,\n'))
521 result = _configlist(result.lstrip(' ,\n'))
515 if result is None:
522 if result is None:
516 result = default or []
523 result = default or []
517 return result
524 return result
518
525
519 def hasconfig(self, section, name, untrusted=False):
526 def hasconfig(self, section, name, untrusted=False):
520 return self._data(untrusted).hasitem(section, name)
527 return self._data(untrusted).hasitem(section, name)
521
528
522 def has_section(self, section, untrusted=False):
529 def has_section(self, section, untrusted=False):
523 '''tell whether section exists in config.'''
530 '''tell whether section exists in config.'''
524 return section in self._data(untrusted)
531 return section in self._data(untrusted)
525
532
526 def configitems(self, section, untrusted=False, ignoresub=False):
533 def configitems(self, section, untrusted=False, ignoresub=False):
527 items = self._data(untrusted).items(section)
534 items = self._data(untrusted).items(section)
528 if ignoresub:
535 if ignoresub:
529 newitems = {}
536 newitems = {}
530 for k, v in items:
537 for k, v in items:
531 if ':' not in k:
538 if ':' not in k:
532 newitems[k] = v
539 newitems[k] = v
533 items = newitems.items()
540 items = newitems.items()
534 if self.debugflag and not untrusted and self._reportuntrusted:
541 if self.debugflag and not untrusted and self._reportuntrusted:
535 for k, v in self._ucfg.items(section):
542 for k, v in self._ucfg.items(section):
536 if self._tcfg.get(section, k) != v:
543 if self._tcfg.get(section, k) != v:
537 self.debug("ignoring untrusted configuration option "
544 self.debug("ignoring untrusted configuration option "
538 "%s.%s = %s\n" % (section, k, v))
545 "%s.%s = %s\n" % (section, k, v))
539 return items
546 return items
540
547
541 def walkconfig(self, untrusted=False):
548 def walkconfig(self, untrusted=False):
542 cfg = self._data(untrusted)
549 cfg = self._data(untrusted)
543 for section in cfg.sections():
550 for section in cfg.sections():
544 for name, value in self.configitems(section, untrusted):
551 for name, value in self.configitems(section, untrusted):
545 yield section, name, value
552 yield section, name, value
546
553
547 def plain(self, feature=None):
554 def plain(self, feature=None):
548 '''is plain mode active?
555 '''is plain mode active?
549
556
550 Plain mode means that all configuration variables which affect
557 Plain mode means that all configuration variables which affect
551 the behavior and output of Mercurial should be
558 the behavior and output of Mercurial should be
552 ignored. Additionally, the output should be stable,
559 ignored. Additionally, the output should be stable,
553 reproducible and suitable for use in scripts or applications.
560 reproducible and suitable for use in scripts or applications.
554
561
555 The only way to trigger plain mode is by setting either the
562 The only way to trigger plain mode is by setting either the
556 `HGPLAIN' or `HGPLAINEXCEPT' environment variables.
563 `HGPLAIN' or `HGPLAINEXCEPT' environment variables.
557
564
558 The return value can either be
565 The return value can either be
559 - False if HGPLAIN is not set, or feature is in HGPLAINEXCEPT
566 - False if HGPLAIN is not set, or feature is in HGPLAINEXCEPT
560 - True otherwise
567 - True otherwise
561 '''
568 '''
562 if 'HGPLAIN' not in os.environ and 'HGPLAINEXCEPT' not in os.environ:
569 if 'HGPLAIN' not in os.environ and 'HGPLAINEXCEPT' not in os.environ:
563 return False
570 return False
564 exceptions = os.environ.get('HGPLAINEXCEPT', '').strip().split(',')
571 exceptions = os.environ.get('HGPLAINEXCEPT', '').strip().split(',')
565 if feature and exceptions:
572 if feature and exceptions:
566 return feature not in exceptions
573 return feature not in exceptions
567 return True
574 return True
568
575
569 def username(self):
576 def username(self):
570 """Return default username to be used in commits.
577 """Return default username to be used in commits.
571
578
572 Searched in this order: $HGUSER, [ui] section of hgrcs, $EMAIL
579 Searched in this order: $HGUSER, [ui] section of hgrcs, $EMAIL
573 and stop searching if one of these is set.
580 and stop searching if one of these is set.
574 If not found and ui.askusername is True, ask the user, else use
581 If not found and ui.askusername is True, ask the user, else use
575 ($LOGNAME or $USER or $LNAME or $USERNAME) + "@full.hostname".
582 ($LOGNAME or $USER or $LNAME or $USERNAME) + "@full.hostname".
576 """
583 """
577 user = os.environ.get("HGUSER")
584 user = os.environ.get("HGUSER")
578 if user is None:
585 if user is None:
579 user = self.config("ui", ["username", "user"])
586 user = self.config("ui", ["username", "user"])
580 if user is not None:
587 if user is not None:
581 user = os.path.expandvars(user)
588 user = os.path.expandvars(user)
582 if user is None:
589 if user is None:
583 user = os.environ.get("EMAIL")
590 user = os.environ.get("EMAIL")
584 if user is None and self.configbool("ui", "askusername"):
591 if user is None and self.configbool("ui", "askusername"):
585 user = self.prompt(_("enter a commit username:"), default=None)
592 user = self.prompt(_("enter a commit username:"), default=None)
586 if user is None and not self.interactive():
593 if user is None and not self.interactive():
587 try:
594 try:
588 user = '%s@%s' % (util.getuser(), socket.getfqdn())
595 user = '%s@%s' % (util.getuser(), socket.getfqdn())
589 self.warn(_("no username found, using '%s' instead\n") % user)
596 self.warn(_("no username found, using '%s' instead\n") % user)
590 except KeyError:
597 except KeyError:
591 pass
598 pass
592 if not user:
599 if not user:
593 raise error.Abort(_('no username supplied'),
600 raise error.Abort(_('no username supplied'),
594 hint=_("use 'hg config --edit' "
601 hint=_("use 'hg config --edit' "
595 'to set your username'))
602 'to set your username'))
596 if "\n" in user:
603 if "\n" in user:
597 raise error.Abort(_("username %s contains a newline\n")
604 raise error.Abort(_("username %s contains a newline\n")
598 % repr(user))
605 % repr(user))
599 return user
606 return user
600
607
601 def shortuser(self, user):
608 def shortuser(self, user):
602 """Return a short representation of a user name or email address."""
609 """Return a short representation of a user name or email address."""
603 if not self.verbose:
610 if not self.verbose:
604 user = util.shortuser(user)
611 user = util.shortuser(user)
605 return user
612 return user
606
613
607 def expandpath(self, loc, default=None):
614 def expandpath(self, loc, default=None):
608 """Return repository location relative to cwd or from [paths]"""
615 """Return repository location relative to cwd or from [paths]"""
609 try:
616 try:
610 p = self.paths.getpath(loc)
617 p = self.paths.getpath(loc)
611 if p:
618 if p:
612 return p.rawloc
619 return p.rawloc
613 except error.RepoError:
620 except error.RepoError:
614 pass
621 pass
615
622
616 if default:
623 if default:
617 try:
624 try:
618 p = self.paths.getpath(default)
625 p = self.paths.getpath(default)
619 if p:
626 if p:
620 return p.rawloc
627 return p.rawloc
621 except error.RepoError:
628 except error.RepoError:
622 pass
629 pass
623
630
624 return loc
631 return loc
625
632
626 @util.propertycache
633 @util.propertycache
627 def paths(self):
634 def paths(self):
628 return paths(self)
635 return paths(self)
629
636
630 def pushbuffer(self, error=False, subproc=False, labeled=False):
637 def pushbuffer(self, error=False, subproc=False, labeled=False):
631 """install a buffer to capture standard output of the ui object
638 """install a buffer to capture standard output of the ui object
632
639
633 If error is True, the error output will be captured too.
640 If error is True, the error output will be captured too.
634
641
635 If subproc is True, output from subprocesses (typically hooks) will be
642 If subproc is True, output from subprocesses (typically hooks) will be
636 captured too.
643 captured too.
637
644
638 If labeled is True, any labels associated with buffered
645 If labeled is True, any labels associated with buffered
639 output will be handled. By default, this has no effect
646 output will be handled. By default, this has no effect
640 on the output returned, but extensions and GUI tools may
647 on the output returned, but extensions and GUI tools may
641 handle this argument and returned styled output. If output
648 handle this argument and returned styled output. If output
642 is being buffered so it can be captured and parsed or
649 is being buffered so it can be captured and parsed or
643 processed, labeled should not be set to True.
650 processed, labeled should not be set to True.
644 """
651 """
645 self._buffers.append([])
652 self._buffers.append([])
646 self._bufferstates.append((error, subproc, labeled))
653 self._bufferstates.append((error, subproc, labeled))
647 self._bufferapplylabels = labeled
654 self._bufferapplylabels = labeled
648
655
649 def popbuffer(self):
656 def popbuffer(self):
650 '''pop the last buffer and return the buffered output'''
657 '''pop the last buffer and return the buffered output'''
651 self._bufferstates.pop()
658 self._bufferstates.pop()
652 if self._bufferstates:
659 if self._bufferstates:
653 self._bufferapplylabels = self._bufferstates[-1][2]
660 self._bufferapplylabels = self._bufferstates[-1][2]
654 else:
661 else:
655 self._bufferapplylabels = None
662 self._bufferapplylabels = None
656
663
657 return "".join(self._buffers.pop())
664 return "".join(self._buffers.pop())
658
665
659 def write(self, *args, **opts):
666 def write(self, *args, **opts):
660 '''write args to output
667 '''write args to output
661
668
662 By default, this method simply writes to the buffer or stdout,
669 By default, this method simply writes to the buffer or stdout,
663 but extensions or GUI tools may override this method,
670 but extensions or GUI tools may override this method,
664 write_err(), popbuffer(), and label() to style output from
671 write_err(), popbuffer(), and label() to style output from
665 various parts of hg.
672 various parts of hg.
666
673
667 An optional keyword argument, "label", can be passed in.
674 An optional keyword argument, "label", can be passed in.
668 This should be a string containing label names separated by
675 This should be a string containing label names separated by
669 space. Label names take the form of "topic.type". For example,
676 space. Label names take the form of "topic.type". For example,
670 ui.debug() issues a label of "ui.debug".
677 ui.debug() issues a label of "ui.debug".
671
678
672 When labeling output for a specific command, a label of
679 When labeling output for a specific command, a label of
673 "cmdname.type" is recommended. For example, status issues
680 "cmdname.type" is recommended. For example, status issues
674 a label of "status.modified" for modified files.
681 a label of "status.modified" for modified files.
675 '''
682 '''
676 if self._buffers and not opts.get('prompt', False):
683 if self._buffers and not opts.get('prompt', False):
677 self._buffers[-1].extend(a for a in args)
684 self._buffers[-1].extend(a for a in args)
678 else:
685 else:
679 self._progclear()
686 self._progclear()
680 for a in args:
687 for a in args:
681 self.fout.write(a)
688 self.fout.write(a)
682
689
683 def write_err(self, *args, **opts):
690 def write_err(self, *args, **opts):
684 self._progclear()
691 self._progclear()
685 try:
692 try:
686 if self._bufferstates and self._bufferstates[-1][0]:
693 if self._bufferstates and self._bufferstates[-1][0]:
687 return self.write(*args, **opts)
694 return self.write(*args, **opts)
688 if not getattr(self.fout, 'closed', False):
695 if not getattr(self.fout, 'closed', False):
689 self.fout.flush()
696 self.fout.flush()
690 for a in args:
697 for a in args:
691 self.ferr.write(a)
698 self.ferr.write(a)
692 # stderr may be buffered under win32 when redirected to files,
699 # stderr may be buffered under win32 when redirected to files,
693 # including stdout.
700 # including stdout.
694 if not getattr(self.ferr, 'closed', False):
701 if not getattr(self.ferr, 'closed', False):
695 self.ferr.flush()
702 self.ferr.flush()
696 except IOError as inst:
703 except IOError as inst:
697 if inst.errno not in (errno.EPIPE, errno.EIO, errno.EBADF):
704 if inst.errno not in (errno.EPIPE, errno.EIO, errno.EBADF):
698 raise
705 raise
699
706
700 def flush(self):
707 def flush(self):
701 try: self.fout.flush()
708 try: self.fout.flush()
702 except (IOError, ValueError): pass
709 except (IOError, ValueError): pass
703 try: self.ferr.flush()
710 try: self.ferr.flush()
704 except (IOError, ValueError): pass
711 except (IOError, ValueError): pass
705
712
706 def _isatty(self, fh):
713 def _isatty(self, fh):
707 if self.configbool('ui', 'nontty', False):
714 if self.configbool('ui', 'nontty', False):
708 return False
715 return False
709 return util.isatty(fh)
716 return util.isatty(fh)
710
717
711 def interface(self, feature):
718 def interface(self, feature):
712 """what interface to use for interactive console features?
719 """what interface to use for interactive console features?
713
720
714 The interface is controlled by the value of `ui.interface` but also by
721 The interface is controlled by the value of `ui.interface` but also by
715 the value of feature-specific configuration. For example:
722 the value of feature-specific configuration. For example:
716
723
717 ui.interface.histedit = text
724 ui.interface.histedit = text
718 ui.interface.chunkselector = curses
725 ui.interface.chunkselector = curses
719
726
720 Here the features are "histedit" and "chunkselector".
727 Here the features are "histedit" and "chunkselector".
721
728
722 The configuration above means that the default interfaces for commands
729 The configuration above means that the default interfaces for commands
723 is curses, the interface for histedit is text and the interface for
730 is curses, the interface for histedit is text and the interface for
724 selecting chunk is crecord (the best curses interface available).
731 selecting chunk is crecord (the best curses interface available).
725
732
726 Consider the following exemple:
733 Consider the following exemple:
727 ui.interface = curses
734 ui.interface = curses
728 ui.interface.histedit = text
735 ui.interface.histedit = text
729
736
730 Then histedit will use the text interface and chunkselector will use
737 Then histedit will use the text interface and chunkselector will use
731 the default curses interface (crecord at the moment).
738 the default curses interface (crecord at the moment).
732 """
739 """
733 alldefaults = frozenset(["text", "curses"])
740 alldefaults = frozenset(["text", "curses"])
734
741
735 featureinterfaces = {
742 featureinterfaces = {
736 "chunkselector": [
743 "chunkselector": [
737 "text",
744 "text",
738 "curses",
745 "curses",
739 ]
746 ]
740 }
747 }
741
748
742 # Feature-specific interface
749 # Feature-specific interface
743 if feature not in featureinterfaces.keys():
750 if feature not in featureinterfaces.keys():
744 # Programming error, not user error
751 # Programming error, not user error
745 raise ValueError("Unknown feature requested %s" % feature)
752 raise ValueError("Unknown feature requested %s" % feature)
746
753
747 availableinterfaces = frozenset(featureinterfaces[feature])
754 availableinterfaces = frozenset(featureinterfaces[feature])
748 if alldefaults > availableinterfaces:
755 if alldefaults > availableinterfaces:
749 # Programming error, not user error. We need a use case to
756 # Programming error, not user error. We need a use case to
750 # define the right thing to do here.
757 # define the right thing to do here.
751 raise ValueError(
758 raise ValueError(
752 "Feature %s does not handle all default interfaces" %
759 "Feature %s does not handle all default interfaces" %
753 feature)
760 feature)
754
761
755 if self.plain():
762 if self.plain():
756 return "text"
763 return "text"
757
764
758 # Default interface for all the features
765 # Default interface for all the features
759 defaultinterface = "text"
766 defaultinterface = "text"
760 i = self.config("ui", "interface", None)
767 i = self.config("ui", "interface", None)
761 if i in alldefaults:
768 if i in alldefaults:
762 defaultinterface = i
769 defaultinterface = i
763
770
764 choseninterface = defaultinterface
771 choseninterface = defaultinterface
765 f = self.config("ui", "interface.%s" % feature, None)
772 f = self.config("ui", "interface.%s" % feature, None)
766 if f in availableinterfaces:
773 if f in availableinterfaces:
767 choseninterface = f
774 choseninterface = f
768
775
769 if i is not None and defaultinterface != i:
776 if i is not None and defaultinterface != i:
770 if f is not None:
777 if f is not None:
771 self.warn(_("invalid value for ui.interface: %s\n") %
778 self.warn(_("invalid value for ui.interface: %s\n") %
772 (i,))
779 (i,))
773 else:
780 else:
774 self.warn(_("invalid value for ui.interface: %s (using %s)\n") %
781 self.warn(_("invalid value for ui.interface: %s (using %s)\n") %
775 (i, choseninterface))
782 (i, choseninterface))
776 if f is not None and choseninterface != f:
783 if f is not None and choseninterface != f:
777 self.warn(_("invalid value for ui.interface.%s: %s (using %s)\n") %
784 self.warn(_("invalid value for ui.interface.%s: %s (using %s)\n") %
778 (feature, f, choseninterface))
785 (feature, f, choseninterface))
779
786
780 return choseninterface
787 return choseninterface
781
788
782 def interactive(self):
789 def interactive(self):
783 '''is interactive input allowed?
790 '''is interactive input allowed?
784
791
785 An interactive session is a session where input can be reasonably read
792 An interactive session is a session where input can be reasonably read
786 from `sys.stdin'. If this function returns false, any attempt to read
793 from `sys.stdin'. If this function returns false, any attempt to read
787 from stdin should fail with an error, unless a sensible default has been
794 from stdin should fail with an error, unless a sensible default has been
788 specified.
795 specified.
789
796
790 Interactiveness is triggered by the value of the `ui.interactive'
797 Interactiveness is triggered by the value of the `ui.interactive'
791 configuration variable or - if it is unset - when `sys.stdin' points
798 configuration variable or - if it is unset - when `sys.stdin' points
792 to a terminal device.
799 to a terminal device.
793
800
794 This function refers to input only; for output, see `ui.formatted()'.
801 This function refers to input only; for output, see `ui.formatted()'.
795 '''
802 '''
796 i = self.configbool("ui", "interactive", None)
803 i = self.configbool("ui", "interactive", None)
797 if i is None:
804 if i is None:
798 # some environments replace stdin without implementing isatty
805 # some environments replace stdin without implementing isatty
799 # usually those are non-interactive
806 # usually those are non-interactive
800 return self._isatty(self.fin)
807 return self._isatty(self.fin)
801
808
802 return i
809 return i
803
810
804 def termwidth(self):
811 def termwidth(self):
805 '''how wide is the terminal in columns?
812 '''how wide is the terminal in columns?
806 '''
813 '''
807 if 'COLUMNS' in os.environ:
814 if 'COLUMNS' in os.environ:
808 try:
815 try:
809 return int(os.environ['COLUMNS'])
816 return int(os.environ['COLUMNS'])
810 except ValueError:
817 except ValueError:
811 pass
818 pass
812 return util.termwidth()
819 return util.termwidth()
813
820
814 def formatted(self):
821 def formatted(self):
815 '''should formatted output be used?
822 '''should formatted output be used?
816
823
817 It is often desirable to format the output to suite the output medium.
824 It is often desirable to format the output to suite the output medium.
818 Examples of this are truncating long lines or colorizing messages.
825 Examples of this are truncating long lines or colorizing messages.
819 However, this is not often not desirable when piping output into other
826 However, this is not often not desirable when piping output into other
820 utilities, e.g. `grep'.
827 utilities, e.g. `grep'.
821
828
822 Formatted output is triggered by the value of the `ui.formatted'
829 Formatted output is triggered by the value of the `ui.formatted'
823 configuration variable or - if it is unset - when `sys.stdout' points
830 configuration variable or - if it is unset - when `sys.stdout' points
824 to a terminal device. Please note that `ui.formatted' should be
831 to a terminal device. Please note that `ui.formatted' should be
825 considered an implementation detail; it is not intended for use outside
832 considered an implementation detail; it is not intended for use outside
826 Mercurial or its extensions.
833 Mercurial or its extensions.
827
834
828 This function refers to output only; for input, see `ui.interactive()'.
835 This function refers to output only; for input, see `ui.interactive()'.
829 This function always returns false when in plain mode, see `ui.plain()'.
836 This function always returns false when in plain mode, see `ui.plain()'.
830 '''
837 '''
831 if self.plain():
838 if self.plain():
832 return False
839 return False
833
840
834 i = self.configbool("ui", "formatted", None)
841 i = self.configbool("ui", "formatted", None)
835 if i is None:
842 if i is None:
836 # some environments replace stdout without implementing isatty
843 # some environments replace stdout without implementing isatty
837 # usually those are non-interactive
844 # usually those are non-interactive
838 return self._isatty(self.fout)
845 return self._isatty(self.fout)
839
846
840 return i
847 return i
841
848
842 def _readline(self, prompt=''):
849 def _readline(self, prompt=''):
843 if self._isatty(self.fin):
850 if self._isatty(self.fin):
844 try:
851 try:
845 # magically add command line editing support, where
852 # magically add command line editing support, where
846 # available
853 # available
847 import readline
854 import readline
848 # force demandimport to really load the module
855 # force demandimport to really load the module
849 readline.read_history_file
856 readline.read_history_file
850 # windows sometimes raises something other than ImportError
857 # windows sometimes raises something other than ImportError
851 except Exception:
858 except Exception:
852 pass
859 pass
853
860
854 # call write() so output goes through subclassed implementation
861 # call write() so output goes through subclassed implementation
855 # e.g. color extension on Windows
862 # e.g. color extension on Windows
856 self.write(prompt, prompt=True)
863 self.write(prompt, prompt=True)
857
864
858 # instead of trying to emulate raw_input, swap (self.fin,
865 # instead of trying to emulate raw_input, swap (self.fin,
859 # self.fout) with (sys.stdin, sys.stdout)
866 # self.fout) with (sys.stdin, sys.stdout)
860 oldin = sys.stdin
867 oldin = sys.stdin
861 oldout = sys.stdout
868 oldout = sys.stdout
862 sys.stdin = self.fin
869 sys.stdin = self.fin
863 sys.stdout = self.fout
870 sys.stdout = self.fout
864 # prompt ' ' must exist; otherwise readline may delete entire line
871 # prompt ' ' must exist; otherwise readline may delete entire line
865 # - http://bugs.python.org/issue12833
872 # - http://bugs.python.org/issue12833
866 line = raw_input(' ')
873 line = raw_input(' ')
867 sys.stdin = oldin
874 sys.stdin = oldin
868 sys.stdout = oldout
875 sys.stdout = oldout
869
876
870 # When stdin is in binary mode on Windows, it can cause
877 # When stdin is in binary mode on Windows, it can cause
871 # raw_input() to emit an extra trailing carriage return
878 # raw_input() to emit an extra trailing carriage return
872 if os.linesep == '\r\n' and line and line[-1] == '\r':
879 if os.linesep == '\r\n' and line and line[-1] == '\r':
873 line = line[:-1]
880 line = line[:-1]
874 return line
881 return line
875
882
876 def prompt(self, msg, default="y"):
883 def prompt(self, msg, default="y"):
877 """Prompt user with msg, read response.
884 """Prompt user with msg, read response.
878 If ui is not interactive, the default is returned.
885 If ui is not interactive, the default is returned.
879 """
886 """
880 if not self.interactive():
887 if not self.interactive():
881 self.write(msg, ' ', default or '', "\n")
888 self.write(msg, ' ', default or '', "\n")
882 return default
889 return default
883 try:
890 try:
884 r = self._readline(self.label(msg, 'ui.prompt'))
891 r = self._readline(self.label(msg, 'ui.prompt'))
885 if not r:
892 if not r:
886 r = default
893 r = default
887 if self.configbool('ui', 'promptecho'):
894 if self.configbool('ui', 'promptecho'):
888 self.write(r, "\n")
895 self.write(r, "\n")
889 return r
896 return r
890 except EOFError:
897 except EOFError:
891 raise error.ResponseExpected()
898 raise error.ResponseExpected()
892
899
893 @staticmethod
900 @staticmethod
894 def extractchoices(prompt):
901 def extractchoices(prompt):
895 """Extract prompt message and list of choices from specified prompt.
902 """Extract prompt message and list of choices from specified prompt.
896
903
897 This returns tuple "(message, choices)", and "choices" is the
904 This returns tuple "(message, choices)", and "choices" is the
898 list of tuple "(response character, text without &)".
905 list of tuple "(response character, text without &)".
899
906
900 >>> ui.extractchoices("awake? $$ &Yes $$ &No")
907 >>> ui.extractchoices("awake? $$ &Yes $$ &No")
901 ('awake? ', [('y', 'Yes'), ('n', 'No')])
908 ('awake? ', [('y', 'Yes'), ('n', 'No')])
902 >>> ui.extractchoices("line\\nbreak? $$ &Yes $$ &No")
909 >>> ui.extractchoices("line\\nbreak? $$ &Yes $$ &No")
903 ('line\\nbreak? ', [('y', 'Yes'), ('n', 'No')])
910 ('line\\nbreak? ', [('y', 'Yes'), ('n', 'No')])
904 >>> ui.extractchoices("want lots of $$money$$?$$Ye&s$$N&o")
911 >>> ui.extractchoices("want lots of $$money$$?$$Ye&s$$N&o")
905 ('want lots of $$money$$?', [('s', 'Yes'), ('o', 'No')])
912 ('want lots of $$money$$?', [('s', 'Yes'), ('o', 'No')])
906 """
913 """
907
914
908 # Sadly, the prompt string may have been built with a filename
915 # Sadly, the prompt string may have been built with a filename
909 # containing "$$" so let's try to find the first valid-looking
916 # containing "$$" so let's try to find the first valid-looking
910 # prompt to start parsing. Sadly, we also can't rely on
917 # prompt to start parsing. Sadly, we also can't rely on
911 # choices containing spaces, ASCII, or basically anything
918 # choices containing spaces, ASCII, or basically anything
912 # except an ampersand followed by a character.
919 # except an ampersand followed by a character.
913 m = re.match(r'(?s)(.+?)\$\$([^\$]*&[^ \$].*)', prompt)
920 m = re.match(r'(?s)(.+?)\$\$([^\$]*&[^ \$].*)', prompt)
914 msg = m.group(1)
921 msg = m.group(1)
915 choices = [p.strip(' ') for p in m.group(2).split('$$')]
922 choices = [p.strip(' ') for p in m.group(2).split('$$')]
916 return (msg,
923 return (msg,
917 [(s[s.index('&') + 1].lower(), s.replace('&', '', 1))
924 [(s[s.index('&') + 1].lower(), s.replace('&', '', 1))
918 for s in choices])
925 for s in choices])
919
926
920 def promptchoice(self, prompt, default=0):
927 def promptchoice(self, prompt, default=0):
921 """Prompt user with a message, read response, and ensure it matches
928 """Prompt user with a message, read response, and ensure it matches
922 one of the provided choices. The prompt is formatted as follows:
929 one of the provided choices. The prompt is formatted as follows:
923
930
924 "would you like fries with that (Yn)? $$ &Yes $$ &No"
931 "would you like fries with that (Yn)? $$ &Yes $$ &No"
925
932
926 The index of the choice is returned. Responses are case
933 The index of the choice is returned. Responses are case
927 insensitive. If ui is not interactive, the default is
934 insensitive. If ui is not interactive, the default is
928 returned.
935 returned.
929 """
936 """
930
937
931 msg, choices = self.extractchoices(prompt)
938 msg, choices = self.extractchoices(prompt)
932 resps = [r for r, t in choices]
939 resps = [r for r, t in choices]
933 while True:
940 while True:
934 r = self.prompt(msg, resps[default])
941 r = self.prompt(msg, resps[default])
935 if r.lower() in resps:
942 if r.lower() in resps:
936 return resps.index(r.lower())
943 return resps.index(r.lower())
937 self.write(_("unrecognized response\n"))
944 self.write(_("unrecognized response\n"))
938
945
939 def getpass(self, prompt=None, default=None):
946 def getpass(self, prompt=None, default=None):
940 if not self.interactive():
947 if not self.interactive():
941 return default
948 return default
942 try:
949 try:
943 self.write_err(self.label(prompt or _('password: '), 'ui.prompt'))
950 self.write_err(self.label(prompt or _('password: '), 'ui.prompt'))
944 # disable getpass() only if explicitly specified. it's still valid
951 # disable getpass() only if explicitly specified. it's still valid
945 # to interact with tty even if fin is not a tty.
952 # to interact with tty even if fin is not a tty.
946 if self.configbool('ui', 'nontty'):
953 if self.configbool('ui', 'nontty'):
947 return self.fin.readline().rstrip('\n')
954 return self.fin.readline().rstrip('\n')
948 else:
955 else:
949 return getpass.getpass('')
956 return getpass.getpass('')
950 except EOFError:
957 except EOFError:
951 raise error.ResponseExpected()
958 raise error.ResponseExpected()
952 def status(self, *msg, **opts):
959 def status(self, *msg, **opts):
953 '''write status message to output (if ui.quiet is False)
960 '''write status message to output (if ui.quiet is False)
954
961
955 This adds an output label of "ui.status".
962 This adds an output label of "ui.status".
956 '''
963 '''
957 if not self.quiet:
964 if not self.quiet:
958 opts['label'] = opts.get('label', '') + ' ui.status'
965 opts['label'] = opts.get('label', '') + ' ui.status'
959 self.write(*msg, **opts)
966 self.write(*msg, **opts)
960 def warn(self, *msg, **opts):
967 def warn(self, *msg, **opts):
961 '''write warning message to output (stderr)
968 '''write warning message to output (stderr)
962
969
963 This adds an output label of "ui.warning".
970 This adds an output label of "ui.warning".
964 '''
971 '''
965 opts['label'] = opts.get('label', '') + ' ui.warning'
972 opts['label'] = opts.get('label', '') + ' ui.warning'
966 self.write_err(*msg, **opts)
973 self.write_err(*msg, **opts)
967 def note(self, *msg, **opts):
974 def note(self, *msg, **opts):
968 '''write note to output (if ui.verbose is True)
975 '''write note to output (if ui.verbose is True)
969
976
970 This adds an output label of "ui.note".
977 This adds an output label of "ui.note".
971 '''
978 '''
972 if self.verbose:
979 if self.verbose:
973 opts['label'] = opts.get('label', '') + ' ui.note'
980 opts['label'] = opts.get('label', '') + ' ui.note'
974 self.write(*msg, **opts)
981 self.write(*msg, **opts)
975 def debug(self, *msg, **opts):
982 def debug(self, *msg, **opts):
976 '''write debug message to output (if ui.debugflag is True)
983 '''write debug message to output (if ui.debugflag is True)
977
984
978 This adds an output label of "ui.debug".
985 This adds an output label of "ui.debug".
979 '''
986 '''
980 if self.debugflag:
987 if self.debugflag:
981 opts['label'] = opts.get('label', '') + ' ui.debug'
988 opts['label'] = opts.get('label', '') + ' ui.debug'
982 self.write(*msg, **opts)
989 self.write(*msg, **opts)
983
990
984 def edit(self, text, user, extra=None, editform=None, pending=None):
991 def edit(self, text, user, extra=None, editform=None, pending=None):
985 extra_defaults = {
992 extra_defaults = {
986 'prefix': 'editor',
993 'prefix': 'editor',
987 'suffix': '.txt',
994 'suffix': '.txt',
988 }
995 }
989 if extra is not None:
996 if extra is not None:
990 extra_defaults.update(extra)
997 extra_defaults.update(extra)
991 extra = extra_defaults
998 extra = extra_defaults
992 (fd, name) = tempfile.mkstemp(prefix='hg-' + extra['prefix'] + '-',
999 (fd, name) = tempfile.mkstemp(prefix='hg-' + extra['prefix'] + '-',
993 suffix=extra['suffix'], text=True)
1000 suffix=extra['suffix'], text=True)
994 try:
1001 try:
995 f = os.fdopen(fd, "w")
1002 f = os.fdopen(fd, "w")
996 f.write(text)
1003 f.write(text)
997 f.close()
1004 f.close()
998
1005
999 environ = {'HGUSER': user}
1006 environ = {'HGUSER': user}
1000 if 'transplant_source' in extra:
1007 if 'transplant_source' in extra:
1001 environ.update({'HGREVISION': hex(extra['transplant_source'])})
1008 environ.update({'HGREVISION': hex(extra['transplant_source'])})
1002 for label in ('intermediate-source', 'source', 'rebase_source'):
1009 for label in ('intermediate-source', 'source', 'rebase_source'):
1003 if label in extra:
1010 if label in extra:
1004 environ.update({'HGREVISION': extra[label]})
1011 environ.update({'HGREVISION': extra[label]})
1005 break
1012 break
1006 if editform:
1013 if editform:
1007 environ.update({'HGEDITFORM': editform})
1014 environ.update({'HGEDITFORM': editform})
1008 if pending:
1015 if pending:
1009 environ.update({'HG_PENDING': pending})
1016 environ.update({'HG_PENDING': pending})
1010
1017
1011 editor = self.geteditor()
1018 editor = self.geteditor()
1012
1019
1013 self.system("%s \"%s\"" % (editor, name),
1020 self.system("%s \"%s\"" % (editor, name),
1014 environ=environ,
1021 environ=environ,
1015 onerr=error.Abort, errprefix=_("edit failed"))
1022 onerr=error.Abort, errprefix=_("edit failed"))
1016
1023
1017 f = open(name)
1024 f = open(name)
1018 t = f.read()
1025 t = f.read()
1019 f.close()
1026 f.close()
1020 finally:
1027 finally:
1021 os.unlink(name)
1028 os.unlink(name)
1022
1029
1023 return t
1030 return t
1024
1031
1025 def system(self, cmd, environ=None, cwd=None, onerr=None, errprefix=None):
1032 def system(self, cmd, environ=None, cwd=None, onerr=None, errprefix=None):
1026 '''execute shell command with appropriate output stream. command
1033 '''execute shell command with appropriate output stream. command
1027 output will be redirected if fout is not stdout.
1034 output will be redirected if fout is not stdout.
1028 '''
1035 '''
1029 out = self.fout
1036 out = self.fout
1030 if any(s[1] for s in self._bufferstates):
1037 if any(s[1] for s in self._bufferstates):
1031 out = self
1038 out = self
1032 return util.system(cmd, environ=environ, cwd=cwd, onerr=onerr,
1039 return util.system(cmd, environ=environ, cwd=cwd, onerr=onerr,
1033 errprefix=errprefix, out=out)
1040 errprefix=errprefix, out=out)
1034
1041
1035 def traceback(self, exc=None, force=False):
1042 def traceback(self, exc=None, force=False):
1036 '''print exception traceback if traceback printing enabled or forced.
1043 '''print exception traceback if traceback printing enabled or forced.
1037 only to call in exception handler. returns true if traceback
1044 only to call in exception handler. returns true if traceback
1038 printed.'''
1045 printed.'''
1039 if self.tracebackflag or force:
1046 if self.tracebackflag or force:
1040 if exc is None:
1047 if exc is None:
1041 exc = sys.exc_info()
1048 exc = sys.exc_info()
1042 cause = getattr(exc[1], 'cause', None)
1049 cause = getattr(exc[1], 'cause', None)
1043
1050
1044 if cause is not None:
1051 if cause is not None:
1045 causetb = traceback.format_tb(cause[2])
1052 causetb = traceback.format_tb(cause[2])
1046 exctb = traceback.format_tb(exc[2])
1053 exctb = traceback.format_tb(exc[2])
1047 exconly = traceback.format_exception_only(cause[0], cause[1])
1054 exconly = traceback.format_exception_only(cause[0], cause[1])
1048
1055
1049 # exclude frame where 'exc' was chained and rethrown from exctb
1056 # exclude frame where 'exc' was chained and rethrown from exctb
1050 self.write_err('Traceback (most recent call last):\n',
1057 self.write_err('Traceback (most recent call last):\n',
1051 ''.join(exctb[:-1]),
1058 ''.join(exctb[:-1]),
1052 ''.join(causetb),
1059 ''.join(causetb),
1053 ''.join(exconly))
1060 ''.join(exconly))
1054 else:
1061 else:
1055 output = traceback.format_exception(exc[0], exc[1], exc[2])
1062 output = traceback.format_exception(exc[0], exc[1], exc[2])
1056 self.write_err(''.join(output))
1063 self.write_err(''.join(output))
1057 return self.tracebackflag or force
1064 return self.tracebackflag or force
1058
1065
1059 def geteditor(self):
1066 def geteditor(self):
1060 '''return editor to use'''
1067 '''return editor to use'''
1061 if sys.platform == 'plan9':
1068 if sys.platform == 'plan9':
1062 # vi is the MIPS instruction simulator on Plan 9. We
1069 # vi is the MIPS instruction simulator on Plan 9. We
1063 # instead default to E to plumb commit messages to
1070 # instead default to E to plumb commit messages to
1064 # avoid confusion.
1071 # avoid confusion.
1065 editor = 'E'
1072 editor = 'E'
1066 else:
1073 else:
1067 editor = 'vi'
1074 editor = 'vi'
1068 return (os.environ.get("HGEDITOR") or
1075 return (os.environ.get("HGEDITOR") or
1069 self.config("ui", "editor") or
1076 self.config("ui", "editor") or
1070 os.environ.get("VISUAL") or
1077 os.environ.get("VISUAL") or
1071 os.environ.get("EDITOR", editor))
1078 os.environ.get("EDITOR", editor))
1072
1079
1073 @util.propertycache
1080 @util.propertycache
1074 def _progbar(self):
1081 def _progbar(self):
1075 """setup the progbar singleton to the ui object"""
1082 """setup the progbar singleton to the ui object"""
1076 if (self.quiet or self.debugflag
1083 if (self.quiet or self.debugflag
1077 or self.configbool('progress', 'disable', False)
1084 or self.configbool('progress', 'disable', False)
1078 or not progress.shouldprint(self)):
1085 or not progress.shouldprint(self)):
1079 return None
1086 return None
1080 return getprogbar(self)
1087 return getprogbar(self)
1081
1088
1082 def _progclear(self):
1089 def _progclear(self):
1083 """clear progress bar output if any. use it before any output"""
1090 """clear progress bar output if any. use it before any output"""
1084 if '_progbar' not in vars(self): # nothing loaded yet
1091 if '_progbar' not in vars(self): # nothing loaded yet
1085 return
1092 return
1086 if self._progbar is not None and self._progbar.printed:
1093 if self._progbar is not None and self._progbar.printed:
1087 self._progbar.clear()
1094 self._progbar.clear()
1088
1095
1089 def progress(self, topic, pos, item="", unit="", total=None):
1096 def progress(self, topic, pos, item="", unit="", total=None):
1090 '''show a progress message
1097 '''show a progress message
1091
1098
1092 By default a textual progress bar will be displayed if an operation
1099 By default a textual progress bar will be displayed if an operation
1093 takes too long. 'topic' is the current operation, 'item' is a
1100 takes too long. 'topic' is the current operation, 'item' is a
1094 non-numeric marker of the current position (i.e. the currently
1101 non-numeric marker of the current position (i.e. the currently
1095 in-process file), 'pos' is the current numeric position (i.e.
1102 in-process file), 'pos' is the current numeric position (i.e.
1096 revision, bytes, etc.), unit is a corresponding unit label,
1103 revision, bytes, etc.), unit is a corresponding unit label,
1097 and total is the highest expected pos.
1104 and total is the highest expected pos.
1098
1105
1099 Multiple nested topics may be active at a time.
1106 Multiple nested topics may be active at a time.
1100
1107
1101 All topics should be marked closed by setting pos to None at
1108 All topics should be marked closed by setting pos to None at
1102 termination.
1109 termination.
1103 '''
1110 '''
1104 if self._progbar is not None:
1111 if self._progbar is not None:
1105 self._progbar.progress(topic, pos, item=item, unit=unit,
1112 self._progbar.progress(topic, pos, item=item, unit=unit,
1106 total=total)
1113 total=total)
1107 if pos is None or not self.configbool('progress', 'debug'):
1114 if pos is None or not self.configbool('progress', 'debug'):
1108 return
1115 return
1109
1116
1110 if unit:
1117 if unit:
1111 unit = ' ' + unit
1118 unit = ' ' + unit
1112 if item:
1119 if item:
1113 item = ' ' + item
1120 item = ' ' + item
1114
1121
1115 if total:
1122 if total:
1116 pct = 100.0 * pos / total
1123 pct = 100.0 * pos / total
1117 self.debug('%s:%s %s/%s%s (%4.2f%%)\n'
1124 self.debug('%s:%s %s/%s%s (%4.2f%%)\n'
1118 % (topic, item, pos, total, unit, pct))
1125 % (topic, item, pos, total, unit, pct))
1119 else:
1126 else:
1120 self.debug('%s:%s %s%s\n' % (topic, item, pos, unit))
1127 self.debug('%s:%s %s%s\n' % (topic, item, pos, unit))
1121
1128
1122 def log(self, service, *msg, **opts):
1129 def log(self, service, *msg, **opts):
1123 '''hook for logging facility extensions
1130 '''hook for logging facility extensions
1124
1131
1125 service should be a readily-identifiable subsystem, which will
1132 service should be a readily-identifiable subsystem, which will
1126 allow filtering.
1133 allow filtering.
1127
1134
1128 *msg should be a newline-terminated format string to log, and
1135 *msg should be a newline-terminated format string to log, and
1129 then any values to %-format into that format string.
1136 then any values to %-format into that format string.
1130
1137
1131 **opts currently has no defined meanings.
1138 **opts currently has no defined meanings.
1132 '''
1139 '''
1133
1140
1134 def label(self, msg, label):
1141 def label(self, msg, label):
1135 '''style msg based on supplied label
1142 '''style msg based on supplied label
1136
1143
1137 Like ui.write(), this just returns msg unchanged, but extensions
1144 Like ui.write(), this just returns msg unchanged, but extensions
1138 and GUI tools can override it to allow styling output without
1145 and GUI tools can override it to allow styling output without
1139 writing it.
1146 writing it.
1140
1147
1141 ui.write(s, 'label') is equivalent to
1148 ui.write(s, 'label') is equivalent to
1142 ui.write(ui.label(s, 'label')).
1149 ui.write(ui.label(s, 'label')).
1143 '''
1150 '''
1144 return msg
1151 return msg
1145
1152
1146 def develwarn(self, msg, stacklevel=1, config=None):
1153 def develwarn(self, msg, stacklevel=1, config=None):
1147 """issue a developer warning message
1154 """issue a developer warning message
1148
1155
1149 Use 'stacklevel' to report the offender some layers further up in the
1156 Use 'stacklevel' to report the offender some layers further up in the
1150 stack.
1157 stack.
1151 """
1158 """
1152 if not self.configbool('devel', 'all-warnings'):
1159 if not self.configbool('devel', 'all-warnings'):
1153 if config is not None and not self.configbool('devel', config):
1160 if config is not None and not self.configbool('devel', config):
1154 return
1161 return
1155 msg = 'devel-warn: ' + msg
1162 msg = 'devel-warn: ' + msg
1156 stacklevel += 1 # get in develwarn
1163 stacklevel += 1 # get in develwarn
1157 if self.tracebackflag:
1164 if self.tracebackflag:
1158 util.debugstacktrace(msg, stacklevel, self.ferr, self.fout)
1165 util.debugstacktrace(msg, stacklevel, self.ferr, self.fout)
1159 self.log('develwarn', '%s at:\n%s' %
1166 self.log('develwarn', '%s at:\n%s' %
1160 (msg, ''.join(util.getstackframes(stacklevel))))
1167 (msg, ''.join(util.getstackframes(stacklevel))))
1161 else:
1168 else:
1162 curframe = inspect.currentframe()
1169 curframe = inspect.currentframe()
1163 calframe = inspect.getouterframes(curframe, 2)
1170 calframe = inspect.getouterframes(curframe, 2)
1164 self.write_err('%s at: %s:%s (%s)\n'
1171 self.write_err('%s at: %s:%s (%s)\n'
1165 % ((msg,) + calframe[stacklevel][1:4]))
1172 % ((msg,) + calframe[stacklevel][1:4]))
1166 self.log('develwarn', '%s at: %s:%s (%s)\n',
1173 self.log('develwarn', '%s at: %s:%s (%s)\n',
1167 msg, *calframe[stacklevel][1:4])
1174 msg, *calframe[stacklevel][1:4])
1168
1175
1169 def deprecwarn(self, msg, version):
1176 def deprecwarn(self, msg, version):
1170 """issue a deprecation warning
1177 """issue a deprecation warning
1171
1178
1172 - msg: message explaining what is deprecated and how to upgrade,
1179 - msg: message explaining what is deprecated and how to upgrade,
1173 - version: last version where the API will be supported,
1180 - version: last version where the API will be supported,
1174 """
1181 """
1175 if not (self.configbool('devel', 'all-warnings')
1182 if not (self.configbool('devel', 'all-warnings')
1176 or self.configbool('devel', 'deprec-warn')):
1183 or self.configbool('devel', 'deprec-warn')):
1177 return
1184 return
1178 msg += ("\n(compatibility will be dropped after Mercurial-%s,"
1185 msg += ("\n(compatibility will be dropped after Mercurial-%s,"
1179 " update your code.)") % version
1186 " update your code.)") % version
1180 self.develwarn(msg, stacklevel=2, config='deprec-warn')
1187 self.develwarn(msg, stacklevel=2, config='deprec-warn')
1181
1188
1182 class paths(dict):
1189 class paths(dict):
1183 """Represents a collection of paths and their configs.
1190 """Represents a collection of paths and their configs.
1184
1191
1185 Data is initially derived from ui instances and the config files they have
1192 Data is initially derived from ui instances and the config files they have
1186 loaded.
1193 loaded.
1187 """
1194 """
1188 def __init__(self, ui):
1195 def __init__(self, ui):
1189 dict.__init__(self)
1196 dict.__init__(self)
1190
1197
1191 for name, loc in ui.configitems('paths', ignoresub=True):
1198 for name, loc in ui.configitems('paths', ignoresub=True):
1192 # No location is the same as not existing.
1199 # No location is the same as not existing.
1193 if not loc:
1200 if not loc:
1194 continue
1201 continue
1195 loc, sub = ui.configsuboptions('paths', name)
1202 loc, sub = ui.configsuboptions('paths', name)
1196 self[name] = path(ui, name, rawloc=loc, suboptions=sub)
1203 self[name] = path(ui, name, rawloc=loc, suboptions=sub)
1197
1204
1198 def getpath(self, name, default=None):
1205 def getpath(self, name, default=None):
1199 """Return a ``path`` from a string, falling back to default.
1206 """Return a ``path`` from a string, falling back to default.
1200
1207
1201 ``name`` can be a named path or locations. Locations are filesystem
1208 ``name`` can be a named path or locations. Locations are filesystem
1202 paths or URIs.
1209 paths or URIs.
1203
1210
1204 Returns None if ``name`` is not a registered path, a URI, or a local
1211 Returns None if ``name`` is not a registered path, a URI, or a local
1205 path to a repo.
1212 path to a repo.
1206 """
1213 """
1207 # Only fall back to default if no path was requested.
1214 # Only fall back to default if no path was requested.
1208 if name is None:
1215 if name is None:
1209 if not default:
1216 if not default:
1210 default = ()
1217 default = ()
1211 elif not isinstance(default, (tuple, list)):
1218 elif not isinstance(default, (tuple, list)):
1212 default = (default,)
1219 default = (default,)
1213 for k in default:
1220 for k in default:
1214 try:
1221 try:
1215 return self[k]
1222 return self[k]
1216 except KeyError:
1223 except KeyError:
1217 continue
1224 continue
1218 return None
1225 return None
1219
1226
1220 # Most likely empty string.
1227 # Most likely empty string.
1221 # This may need to raise in the future.
1228 # This may need to raise in the future.
1222 if not name:
1229 if not name:
1223 return None
1230 return None
1224
1231
1225 try:
1232 try:
1226 return self[name]
1233 return self[name]
1227 except KeyError:
1234 except KeyError:
1228 # Try to resolve as a local path or URI.
1235 # Try to resolve as a local path or URI.
1229 try:
1236 try:
1230 # We don't pass sub-options in, so no need to pass ui instance.
1237 # We don't pass sub-options in, so no need to pass ui instance.
1231 return path(None, None, rawloc=name)
1238 return path(None, None, rawloc=name)
1232 except ValueError:
1239 except ValueError:
1233 raise error.RepoError(_('repository %s does not exist') %
1240 raise error.RepoError(_('repository %s does not exist') %
1234 name)
1241 name)
1235
1242
1236 _pathsuboptions = {}
1243 _pathsuboptions = {}
1237
1244
1238 def pathsuboption(option, attr):
1245 def pathsuboption(option, attr):
1239 """Decorator used to declare a path sub-option.
1246 """Decorator used to declare a path sub-option.
1240
1247
1241 Arguments are the sub-option name and the attribute it should set on
1248 Arguments are the sub-option name and the attribute it should set on
1242 ``path`` instances.
1249 ``path`` instances.
1243
1250
1244 The decorated function will receive as arguments a ``ui`` instance,
1251 The decorated function will receive as arguments a ``ui`` instance,
1245 ``path`` instance, and the string value of this option from the config.
1252 ``path`` instance, and the string value of this option from the config.
1246 The function should return the value that will be set on the ``path``
1253 The function should return the value that will be set on the ``path``
1247 instance.
1254 instance.
1248
1255
1249 This decorator can be used to perform additional verification of
1256 This decorator can be used to perform additional verification of
1250 sub-options and to change the type of sub-options.
1257 sub-options and to change the type of sub-options.
1251 """
1258 """
1252 def register(func):
1259 def register(func):
1253 _pathsuboptions[option] = (attr, func)
1260 _pathsuboptions[option] = (attr, func)
1254 return func
1261 return func
1255 return register
1262 return register
1256
1263
1257 @pathsuboption('pushurl', 'pushloc')
1264 @pathsuboption('pushurl', 'pushloc')
1258 def pushurlpathoption(ui, path, value):
1265 def pushurlpathoption(ui, path, value):
1259 u = util.url(value)
1266 u = util.url(value)
1260 # Actually require a URL.
1267 # Actually require a URL.
1261 if not u.scheme:
1268 if not u.scheme:
1262 ui.warn(_('(paths.%s:pushurl not a URL; ignoring)\n') % path.name)
1269 ui.warn(_('(paths.%s:pushurl not a URL; ignoring)\n') % path.name)
1263 return None
1270 return None
1264
1271
1265 # Don't support the #foo syntax in the push URL to declare branch to
1272 # Don't support the #foo syntax in the push URL to declare branch to
1266 # push.
1273 # push.
1267 if u.fragment:
1274 if u.fragment:
1268 ui.warn(_('("#fragment" in paths.%s:pushurl not supported; '
1275 ui.warn(_('("#fragment" in paths.%s:pushurl not supported; '
1269 'ignoring)\n') % path.name)
1276 'ignoring)\n') % path.name)
1270 u.fragment = None
1277 u.fragment = None
1271
1278
1272 return str(u)
1279 return str(u)
1273
1280
1274 class path(object):
1281 class path(object):
1275 """Represents an individual path and its configuration."""
1282 """Represents an individual path and its configuration."""
1276
1283
1277 def __init__(self, ui, name, rawloc=None, suboptions=None):
1284 def __init__(self, ui, name, rawloc=None, suboptions=None):
1278 """Construct a path from its config options.
1285 """Construct a path from its config options.
1279
1286
1280 ``ui`` is the ``ui`` instance the path is coming from.
1287 ``ui`` is the ``ui`` instance the path is coming from.
1281 ``name`` is the symbolic name of the path.
1288 ``name`` is the symbolic name of the path.
1282 ``rawloc`` is the raw location, as defined in the config.
1289 ``rawloc`` is the raw location, as defined in the config.
1283 ``pushloc`` is the raw locations pushes should be made to.
1290 ``pushloc`` is the raw locations pushes should be made to.
1284
1291
1285 If ``name`` is not defined, we require that the location be a) a local
1292 If ``name`` is not defined, we require that the location be a) a local
1286 filesystem path with a .hg directory or b) a URL. If not,
1293 filesystem path with a .hg directory or b) a URL. If not,
1287 ``ValueError`` is raised.
1294 ``ValueError`` is raised.
1288 """
1295 """
1289 if not rawloc:
1296 if not rawloc:
1290 raise ValueError('rawloc must be defined')
1297 raise ValueError('rawloc must be defined')
1291
1298
1292 # Locations may define branches via syntax <base>#<branch>.
1299 # Locations may define branches via syntax <base>#<branch>.
1293 u = util.url(rawloc)
1300 u = util.url(rawloc)
1294 branch = None
1301 branch = None
1295 if u.fragment:
1302 if u.fragment:
1296 branch = u.fragment
1303 branch = u.fragment
1297 u.fragment = None
1304 u.fragment = None
1298
1305
1299 self.url = u
1306 self.url = u
1300 self.branch = branch
1307 self.branch = branch
1301
1308
1302 self.name = name
1309 self.name = name
1303 self.rawloc = rawloc
1310 self.rawloc = rawloc
1304 self.loc = str(u)
1311 self.loc = str(u)
1305
1312
1306 # When given a raw location but not a symbolic name, validate the
1313 # When given a raw location but not a symbolic name, validate the
1307 # location is valid.
1314 # location is valid.
1308 if not name and not u.scheme and not self._isvalidlocalpath(self.loc):
1315 if not name and not u.scheme and not self._isvalidlocalpath(self.loc):
1309 raise ValueError('location is not a URL or path to a local '
1316 raise ValueError('location is not a URL or path to a local '
1310 'repo: %s' % rawloc)
1317 'repo: %s' % rawloc)
1311
1318
1312 suboptions = suboptions or {}
1319 suboptions = suboptions or {}
1313
1320
1314 # Now process the sub-options. If a sub-option is registered, its
1321 # Now process the sub-options. If a sub-option is registered, its
1315 # attribute will always be present. The value will be None if there
1322 # attribute will always be present. The value will be None if there
1316 # was no valid sub-option.
1323 # was no valid sub-option.
1317 for suboption, (attr, func) in _pathsuboptions.iteritems():
1324 for suboption, (attr, func) in _pathsuboptions.iteritems():
1318 if suboption not in suboptions:
1325 if suboption not in suboptions:
1319 setattr(self, attr, None)
1326 setattr(self, attr, None)
1320 continue
1327 continue
1321
1328
1322 value = func(ui, self, suboptions[suboption])
1329 value = func(ui, self, suboptions[suboption])
1323 setattr(self, attr, value)
1330 setattr(self, attr, value)
1324
1331
1325 def _isvalidlocalpath(self, path):
1332 def _isvalidlocalpath(self, path):
1326 """Returns True if the given path is a potentially valid repository.
1333 """Returns True if the given path is a potentially valid repository.
1327 This is its own function so that extensions can change the definition of
1334 This is its own function so that extensions can change the definition of
1328 'valid' in this case (like when pulling from a git repo into a hg
1335 'valid' in this case (like when pulling from a git repo into a hg
1329 one)."""
1336 one)."""
1330 return os.path.isdir(os.path.join(path, '.hg'))
1337 return os.path.isdir(os.path.join(path, '.hg'))
1331
1338
1332 @property
1339 @property
1333 def suboptions(self):
1340 def suboptions(self):
1334 """Return sub-options and their values for this path.
1341 """Return sub-options and their values for this path.
1335
1342
1336 This is intended to be used for presentation purposes.
1343 This is intended to be used for presentation purposes.
1337 """
1344 """
1338 d = {}
1345 d = {}
1339 for subopt, (attr, _func) in _pathsuboptions.iteritems():
1346 for subopt, (attr, _func) in _pathsuboptions.iteritems():
1340 value = getattr(self, attr)
1347 value = getattr(self, attr)
1341 if value is not None:
1348 if value is not None:
1342 d[subopt] = value
1349 d[subopt] = value
1343 return d
1350 return d
1344
1351
1345 # we instantiate one globally shared progress bar to avoid
1352 # we instantiate one globally shared progress bar to avoid
1346 # competing progress bars when multiple UI objects get created
1353 # competing progress bars when multiple UI objects get created
1347 _progresssingleton = None
1354 _progresssingleton = None
1348
1355
1349 def getprogbar(ui):
1356 def getprogbar(ui):
1350 global _progresssingleton
1357 global _progresssingleton
1351 if _progresssingleton is None:
1358 if _progresssingleton is None:
1352 # passing 'ui' object to the singleton is fishy,
1359 # passing 'ui' object to the singleton is fishy,
1353 # this is how the extension used to work but feel free to rework it.
1360 # this is how the extension used to work but feel free to rework it.
1354 _progresssingleton = progress.progbar(ui)
1361 _progresssingleton = progress.progbar(ui)
1355 return _progresssingleton
1362 return _progresssingleton
@@ -1,520 +1,520
1 # url.py - HTTP handling for mercurial
1 # url.py - HTTP handling for mercurial
2 #
2 #
3 # Copyright 2005, 2006, 2007, 2008 Matt Mackall <mpm@selenic.com>
3 # Copyright 2005, 2006, 2007, 2008 Matt Mackall <mpm@selenic.com>
4 # Copyright 2006, 2007 Alexis S. L. Carvalho <alexis@cecm.usp.br>
4 # Copyright 2006, 2007 Alexis S. L. Carvalho <alexis@cecm.usp.br>
5 # Copyright 2006 Vadim Gelfer <vadim.gelfer@gmail.com>
5 # Copyright 2006 Vadim Gelfer <vadim.gelfer@gmail.com>
6 #
6 #
7 # This software may be used and distributed according to the terms of the
7 # This software may be used and distributed according to the terms of the
8 # GNU General Public License version 2 or any later version.
8 # GNU General Public License version 2 or any later version.
9
9
10 from __future__ import absolute_import
10 from __future__ import absolute_import
11
11
12 import base64
12 import base64
13 import httplib
13 import httplib
14 import os
14 import os
15 import socket
15 import socket
16
16
17 from .i18n import _
17 from .i18n import _
18 from . import (
18 from . import (
19 error,
19 error,
20 httpconnection as httpconnectionmod,
20 httpconnection as httpconnectionmod,
21 keepalive,
21 keepalive,
22 sslutil,
22 sslutil,
23 util,
23 util,
24 )
24 )
25 stringio = util.stringio
25 stringio = util.stringio
26
26
27 urlerr = util.urlerr
27 urlerr = util.urlerr
28 urlreq = util.urlreq
28 urlreq = util.urlreq
29
29
30 class passwordmgr(object):
30 class passwordmgr(object):
31 def __init__(self, ui, passwddb):
31 def __init__(self, ui, passwddb):
32 self.ui = ui
32 self.ui = ui
33 self.passwddb = passwddb
33 self.passwddb = passwddb
34
34
35 def add_password(self, realm, uri, user, passwd):
35 def add_password(self, realm, uri, user, passwd):
36 return self.passwddb.add_password(realm, uri, user, passwd)
36 return self.passwddb.add_password(realm, uri, user, passwd)
37
37
38 def find_user_password(self, realm, authuri):
38 def find_user_password(self, realm, authuri):
39 authinfo = self.passwddb.find_user_password(realm, authuri)
39 authinfo = self.passwddb.find_user_password(realm, authuri)
40 user, passwd = authinfo
40 user, passwd = authinfo
41 if user and passwd:
41 if user and passwd:
42 self._writedebug(user, passwd)
42 self._writedebug(user, passwd)
43 return (user, passwd)
43 return (user, passwd)
44
44
45 if not user or not passwd:
45 if not user or not passwd:
46 res = httpconnectionmod.readauthforuri(self.ui, authuri, user)
46 res = httpconnectionmod.readauthforuri(self.ui, authuri, user)
47 if res:
47 if res:
48 group, auth = res
48 group, auth = res
49 user, passwd = auth.get('username'), auth.get('password')
49 user, passwd = auth.get('username'), auth.get('password')
50 self.ui.debug("using auth.%s.* for authentication\n" % group)
50 self.ui.debug("using auth.%s.* for authentication\n" % group)
51 if not user or not passwd:
51 if not user or not passwd:
52 u = util.url(authuri)
52 u = util.url(authuri)
53 u.query = None
53 u.query = None
54 if not self.ui.interactive():
54 if not self.ui.interactive():
55 raise error.Abort(_('http authorization required for %s') %
55 raise error.Abort(_('http authorization required for %s') %
56 util.hidepassword(str(u)))
56 util.hidepassword(str(u)))
57
57
58 self.ui.write(_("http authorization required for %s\n") %
58 self.ui.write(_("http authorization required for %s\n") %
59 util.hidepassword(str(u)))
59 util.hidepassword(str(u)))
60 self.ui.write(_("realm: %s\n") % realm)
60 self.ui.write(_("realm: %s\n") % realm)
61 if user:
61 if user:
62 self.ui.write(_("user: %s\n") % user)
62 self.ui.write(_("user: %s\n") % user)
63 else:
63 else:
64 user = self.ui.prompt(_("user:"), default=None)
64 user = self.ui.prompt(_("user:"), default=None)
65
65
66 if not passwd:
66 if not passwd:
67 passwd = self.ui.getpass()
67 passwd = self.ui.getpass()
68
68
69 self.passwddb.add_password(realm, authuri, user, passwd)
69 self.passwddb.add_password(realm, authuri, user, passwd)
70 self._writedebug(user, passwd)
70 self._writedebug(user, passwd)
71 return (user, passwd)
71 return (user, passwd)
72
72
73 def _writedebug(self, user, passwd):
73 def _writedebug(self, user, passwd):
74 msg = _('http auth: user %s, password %s\n')
74 msg = _('http auth: user %s, password %s\n')
75 self.ui.debug(msg % (user, passwd and '*' * len(passwd) or 'not set'))
75 self.ui.debug(msg % (user, passwd and '*' * len(passwd) or 'not set'))
76
76
77 def find_stored_password(self, authuri):
77 def find_stored_password(self, authuri):
78 return self.passwddb.find_user_password(None, authuri)
78 return self.passwddb.find_user_password(None, authuri)
79
79
80 class proxyhandler(urlreq.proxyhandler):
80 class proxyhandler(urlreq.proxyhandler):
81 def __init__(self, ui):
81 def __init__(self, ui):
82 proxyurl = ui.config("http_proxy", "host") or os.getenv('http_proxy')
82 proxyurl = ui.config("http_proxy", "host") or os.getenv('http_proxy')
83 # XXX proxyauthinfo = None
83 # XXX proxyauthinfo = None
84
84
85 if proxyurl:
85 if proxyurl:
86 # proxy can be proper url or host[:port]
86 # proxy can be proper url or host[:port]
87 if not (proxyurl.startswith('http:') or
87 if not (proxyurl.startswith('http:') or
88 proxyurl.startswith('https:')):
88 proxyurl.startswith('https:')):
89 proxyurl = 'http://' + proxyurl + '/'
89 proxyurl = 'http://' + proxyurl + '/'
90 proxy = util.url(proxyurl)
90 proxy = util.url(proxyurl)
91 if not proxy.user:
91 if not proxy.user:
92 proxy.user = ui.config("http_proxy", "user")
92 proxy.user = ui.config("http_proxy", "user")
93 proxy.passwd = ui.config("http_proxy", "passwd")
93 proxy.passwd = ui.config("http_proxy", "passwd")
94
94
95 # see if we should use a proxy for this url
95 # see if we should use a proxy for this url
96 no_list = ["localhost", "127.0.0.1"]
96 no_list = ["localhost", "127.0.0.1"]
97 no_list.extend([p.lower() for
97 no_list.extend([p.lower() for
98 p in ui.configlist("http_proxy", "no")])
98 p in ui.configlist("http_proxy", "no")])
99 no_list.extend([p.strip().lower() for
99 no_list.extend([p.strip().lower() for
100 p in os.getenv("no_proxy", '').split(',')
100 p in os.getenv("no_proxy", '').split(',')
101 if p.strip()])
101 if p.strip()])
102 # "http_proxy.always" config is for running tests on localhost
102 # "http_proxy.always" config is for running tests on localhost
103 if ui.configbool("http_proxy", "always"):
103 if ui.configbool("http_proxy", "always"):
104 self.no_list = []
104 self.no_list = []
105 else:
105 else:
106 self.no_list = no_list
106 self.no_list = no_list
107
107
108 proxyurl = str(proxy)
108 proxyurl = str(proxy)
109 proxies = {'http': proxyurl, 'https': proxyurl}
109 proxies = {'http': proxyurl, 'https': proxyurl}
110 ui.debug('proxying through http://%s:%s\n' %
110 ui.debug('proxying through http://%s:%s\n' %
111 (proxy.host, proxy.port))
111 (proxy.host, proxy.port))
112 else:
112 else:
113 proxies = {}
113 proxies = {}
114
114
115 # urllib2 takes proxy values from the environment and those
115 # urllib2 takes proxy values from the environment and those
116 # will take precedence if found. So, if there's a config entry
116 # will take precedence if found. So, if there's a config entry
117 # defining a proxy, drop the environment ones
117 # defining a proxy, drop the environment ones
118 if ui.config("http_proxy", "host"):
118 if ui.config("http_proxy", "host"):
119 for env in ["HTTP_PROXY", "http_proxy", "no_proxy"]:
119 for env in ["HTTP_PROXY", "http_proxy", "no_proxy"]:
120 try:
120 try:
121 if env in os.environ:
121 if env in os.environ:
122 del os.environ[env]
122 del os.environ[env]
123 except OSError:
123 except OSError:
124 pass
124 pass
125
125
126 urlreq.proxyhandler.__init__(self, proxies)
126 urlreq.proxyhandler.__init__(self, proxies)
127 self.ui = ui
127 self.ui = ui
128
128
129 def proxy_open(self, req, proxy, type_):
129 def proxy_open(self, req, proxy, type_):
130 host = req.get_host().split(':')[0]
130 host = req.get_host().split(':')[0]
131 for e in self.no_list:
131 for e in self.no_list:
132 if host == e:
132 if host == e:
133 return None
133 return None
134 if e.startswith('*.') and host.endswith(e[2:]):
134 if e.startswith('*.') and host.endswith(e[2:]):
135 return None
135 return None
136 if e.startswith('.') and host.endswith(e[1:]):
136 if e.startswith('.') and host.endswith(e[1:]):
137 return None
137 return None
138
138
139 return urlreq.proxyhandler.proxy_open(self, req, proxy, type_)
139 return urlreq.proxyhandler.proxy_open(self, req, proxy, type_)
140
140
141 def _gen_sendfile(orgsend):
141 def _gen_sendfile(orgsend):
142 def _sendfile(self, data):
142 def _sendfile(self, data):
143 # send a file
143 # send a file
144 if isinstance(data, httpconnectionmod.httpsendfile):
144 if isinstance(data, httpconnectionmod.httpsendfile):
145 # if auth required, some data sent twice, so rewind here
145 # if auth required, some data sent twice, so rewind here
146 data.seek(0)
146 data.seek(0)
147 for chunk in util.filechunkiter(data):
147 for chunk in util.filechunkiter(data):
148 orgsend(self, chunk)
148 orgsend(self, chunk)
149 else:
149 else:
150 orgsend(self, data)
150 orgsend(self, data)
151 return _sendfile
151 return _sendfile
152
152
153 has_https = util.safehasattr(urlreq, 'httpshandler')
153 has_https = util.safehasattr(urlreq, 'httpshandler')
154 if has_https:
154 if has_https:
155 try:
155 try:
156 _create_connection = socket.create_connection
156 _create_connection = socket.create_connection
157 except AttributeError:
157 except AttributeError:
158 _GLOBAL_DEFAULT_TIMEOUT = object()
158 _GLOBAL_DEFAULT_TIMEOUT = object()
159
159
160 def _create_connection(address, timeout=_GLOBAL_DEFAULT_TIMEOUT,
160 def _create_connection(address, timeout=_GLOBAL_DEFAULT_TIMEOUT,
161 source_address=None):
161 source_address=None):
162 # lifted from Python 2.6
162 # lifted from Python 2.6
163
163
164 msg = "getaddrinfo returns an empty list"
164 msg = "getaddrinfo returns an empty list"
165 host, port = address
165 host, port = address
166 for res in socket.getaddrinfo(host, port, 0, socket.SOCK_STREAM):
166 for res in socket.getaddrinfo(host, port, 0, socket.SOCK_STREAM):
167 af, socktype, proto, canonname, sa = res
167 af, socktype, proto, canonname, sa = res
168 sock = None
168 sock = None
169 try:
169 try:
170 sock = socket.socket(af, socktype, proto)
170 sock = socket.socket(af, socktype, proto)
171 if timeout is not _GLOBAL_DEFAULT_TIMEOUT:
171 if timeout is not _GLOBAL_DEFAULT_TIMEOUT:
172 sock.settimeout(timeout)
172 sock.settimeout(timeout)
173 if source_address:
173 if source_address:
174 sock.bind(source_address)
174 sock.bind(source_address)
175 sock.connect(sa)
175 sock.connect(sa)
176 return sock
176 return sock
177
177
178 except socket.error as msg:
178 except socket.error as msg:
179 if sock is not None:
179 if sock is not None:
180 sock.close()
180 sock.close()
181
181
182 raise socket.error(msg)
182 raise socket.error(msg)
183
183
184 class httpconnection(keepalive.HTTPConnection):
184 class httpconnection(keepalive.HTTPConnection):
185 # must be able to send big bundle as stream.
185 # must be able to send big bundle as stream.
186 send = _gen_sendfile(keepalive.HTTPConnection.send)
186 send = _gen_sendfile(keepalive.HTTPConnection.send)
187
187
188 def connect(self):
188 def connect(self):
189 if has_https and self.realhostport: # use CONNECT proxy
189 if has_https and self.realhostport: # use CONNECT proxy
190 self.sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
190 self.sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
191 self.sock.connect((self.host, self.port))
191 self.sock.connect((self.host, self.port))
192 if _generic_proxytunnel(self):
192 if _generic_proxytunnel(self):
193 # we do not support client X.509 certificates
193 # we do not support client X.509 certificates
194 self.sock = sslutil.wrapsocket(self.sock, None, None, None,
194 self.sock = sslutil.wrapsocket(self.sock, None, None, None,
195 serverhostname=self.host)
195 serverhostname=self.host)
196 else:
196 else:
197 keepalive.HTTPConnection.connect(self)
197 keepalive.HTTPConnection.connect(self)
198
198
199 def getresponse(self):
199 def getresponse(self):
200 proxyres = getattr(self, 'proxyres', None)
200 proxyres = getattr(self, 'proxyres', None)
201 if proxyres:
201 if proxyres:
202 if proxyres.will_close:
202 if proxyres.will_close:
203 self.close()
203 self.close()
204 self.proxyres = None
204 self.proxyres = None
205 return proxyres
205 return proxyres
206 return keepalive.HTTPConnection.getresponse(self)
206 return keepalive.HTTPConnection.getresponse(self)
207
207
208 # general transaction handler to support different ways to handle
208 # general transaction handler to support different ways to handle
209 # HTTPS proxying before and after Python 2.6.3.
209 # HTTPS proxying before and after Python 2.6.3.
210 def _generic_start_transaction(handler, h, req):
210 def _generic_start_transaction(handler, h, req):
211 tunnel_host = getattr(req, '_tunnel_host', None)
211 tunnel_host = getattr(req, '_tunnel_host', None)
212 if tunnel_host:
212 if tunnel_host:
213 if tunnel_host[:7] not in ['http://', 'https:/']:
213 if tunnel_host[:7] not in ['http://', 'https:/']:
214 tunnel_host = 'https://' + tunnel_host
214 tunnel_host = 'https://' + tunnel_host
215 new_tunnel = True
215 new_tunnel = True
216 else:
216 else:
217 tunnel_host = req.get_selector()
217 tunnel_host = req.get_selector()
218 new_tunnel = False
218 new_tunnel = False
219
219
220 if new_tunnel or tunnel_host == req.get_full_url(): # has proxy
220 if new_tunnel or tunnel_host == req.get_full_url(): # has proxy
221 u = util.url(tunnel_host)
221 u = util.url(tunnel_host)
222 if new_tunnel or u.scheme == 'https': # only use CONNECT for HTTPS
222 if new_tunnel or u.scheme == 'https': # only use CONNECT for HTTPS
223 h.realhostport = ':'.join([u.host, (u.port or '443')])
223 h.realhostport = ':'.join([u.host, (u.port or '443')])
224 h.headers = req.headers.copy()
224 h.headers = req.headers.copy()
225 h.headers.update(handler.parent.addheaders)
225 h.headers.update(handler.parent.addheaders)
226 return
226 return
227
227
228 h.realhostport = None
228 h.realhostport = None
229 h.headers = None
229 h.headers = None
230
230
231 def _generic_proxytunnel(self):
231 def _generic_proxytunnel(self):
232 proxyheaders = dict(
232 proxyheaders = dict(
233 [(x, self.headers[x]) for x in self.headers
233 [(x, self.headers[x]) for x in self.headers
234 if x.lower().startswith('proxy-')])
234 if x.lower().startswith('proxy-')])
235 self.send('CONNECT %s HTTP/1.0\r\n' % self.realhostport)
235 self.send('CONNECT %s HTTP/1.0\r\n' % self.realhostport)
236 for header in proxyheaders.iteritems():
236 for header in proxyheaders.iteritems():
237 self.send('%s: %s\r\n' % header)
237 self.send('%s: %s\r\n' % header)
238 self.send('\r\n')
238 self.send('\r\n')
239
239
240 # majority of the following code is duplicated from
240 # majority of the following code is duplicated from
241 # httplib.HTTPConnection as there are no adequate places to
241 # httplib.HTTPConnection as there are no adequate places to
242 # override functions to provide the needed functionality
242 # override functions to provide the needed functionality
243 res = self.response_class(self.sock,
243 res = self.response_class(self.sock,
244 strict=self.strict,
244 strict=self.strict,
245 method=self._method)
245 method=self._method)
246
246
247 while True:
247 while True:
248 version, status, reason = res._read_status()
248 version, status, reason = res._read_status()
249 if status != httplib.CONTINUE:
249 if status != httplib.CONTINUE:
250 break
250 break
251 while True:
251 while True:
252 skip = res.fp.readline().strip()
252 skip = res.fp.readline().strip()
253 if not skip:
253 if not skip:
254 break
254 break
255 res.status = status
255 res.status = status
256 res.reason = reason.strip()
256 res.reason = reason.strip()
257
257
258 if res.status == 200:
258 if res.status == 200:
259 while True:
259 while True:
260 line = res.fp.readline()
260 line = res.fp.readline()
261 if line == '\r\n':
261 if line == '\r\n':
262 break
262 break
263 return True
263 return True
264
264
265 if version == 'HTTP/1.0':
265 if version == 'HTTP/1.0':
266 res.version = 10
266 res.version = 10
267 elif version.startswith('HTTP/1.'):
267 elif version.startswith('HTTP/1.'):
268 res.version = 11
268 res.version = 11
269 elif version == 'HTTP/0.9':
269 elif version == 'HTTP/0.9':
270 res.version = 9
270 res.version = 9
271 else:
271 else:
272 raise httplib.UnknownProtocol(version)
272 raise httplib.UnknownProtocol(version)
273
273
274 if res.version == 9:
274 if res.version == 9:
275 res.length = None
275 res.length = None
276 res.chunked = 0
276 res.chunked = 0
277 res.will_close = 1
277 res.will_close = 1
278 res.msg = httplib.HTTPMessage(stringio())
278 res.msg = httplib.HTTPMessage(stringio())
279 return False
279 return False
280
280
281 res.msg = httplib.HTTPMessage(res.fp)
281 res.msg = httplib.HTTPMessage(res.fp)
282 res.msg.fp = None
282 res.msg.fp = None
283
283
284 # are we using the chunked-style of transfer encoding?
284 # are we using the chunked-style of transfer encoding?
285 trenc = res.msg.getheader('transfer-encoding')
285 trenc = res.msg.getheader('transfer-encoding')
286 if trenc and trenc.lower() == "chunked":
286 if trenc and trenc.lower() == "chunked":
287 res.chunked = 1
287 res.chunked = 1
288 res.chunk_left = None
288 res.chunk_left = None
289 else:
289 else:
290 res.chunked = 0
290 res.chunked = 0
291
291
292 # will the connection close at the end of the response?
292 # will the connection close at the end of the response?
293 res.will_close = res._check_close()
293 res.will_close = res._check_close()
294
294
295 # do we have a Content-Length?
295 # do we have a Content-Length?
296 # NOTE: RFC 2616, section 4.4, #3 says we ignore this if
296 # NOTE: RFC 2616, section 4.4, #3 says we ignore this if
297 # transfer-encoding is "chunked"
297 # transfer-encoding is "chunked"
298 length = res.msg.getheader('content-length')
298 length = res.msg.getheader('content-length')
299 if length and not res.chunked:
299 if length and not res.chunked:
300 try:
300 try:
301 res.length = int(length)
301 res.length = int(length)
302 except ValueError:
302 except ValueError:
303 res.length = None
303 res.length = None
304 else:
304 else:
305 if res.length < 0: # ignore nonsensical negative lengths
305 if res.length < 0: # ignore nonsensical negative lengths
306 res.length = None
306 res.length = None
307 else:
307 else:
308 res.length = None
308 res.length = None
309
309
310 # does the body have a fixed length? (of zero)
310 # does the body have a fixed length? (of zero)
311 if (status == httplib.NO_CONTENT or status == httplib.NOT_MODIFIED or
311 if (status == httplib.NO_CONTENT or status == httplib.NOT_MODIFIED or
312 100 <= status < 200 or # 1xx codes
312 100 <= status < 200 or # 1xx codes
313 res._method == 'HEAD'):
313 res._method == 'HEAD'):
314 res.length = 0
314 res.length = 0
315
315
316 # if the connection remains open, and we aren't using chunked, and
316 # if the connection remains open, and we aren't using chunked, and
317 # a content-length was not provided, then assume that the connection
317 # a content-length was not provided, then assume that the connection
318 # WILL close.
318 # WILL close.
319 if (not res.will_close and
319 if (not res.will_close and
320 not res.chunked and
320 not res.chunked and
321 res.length is None):
321 res.length is None):
322 res.will_close = 1
322 res.will_close = 1
323
323
324 self.proxyres = res
324 self.proxyres = res
325
325
326 return False
326 return False
327
327
328 class httphandler(keepalive.HTTPHandler):
328 class httphandler(keepalive.HTTPHandler):
329 def http_open(self, req):
329 def http_open(self, req):
330 return self.do_open(httpconnection, req)
330 return self.do_open(httpconnection, req)
331
331
332 def _start_transaction(self, h, req):
332 def _start_transaction(self, h, req):
333 _generic_start_transaction(self, h, req)
333 _generic_start_transaction(self, h, req)
334 return keepalive.HTTPHandler._start_transaction(self, h, req)
334 return keepalive.HTTPHandler._start_transaction(self, h, req)
335
335
336 if has_https:
336 if has_https:
337 class httpsconnection(httplib.HTTPConnection):
337 class httpsconnection(httplib.HTTPConnection):
338 response_class = keepalive.HTTPResponse
338 response_class = keepalive.HTTPResponse
339 default_port = httplib.HTTPS_PORT
339 default_port = httplib.HTTPS_PORT
340 # must be able to send big bundle as stream.
340 # must be able to send big bundle as stream.
341 send = _gen_sendfile(keepalive.safesend)
341 send = _gen_sendfile(keepalive.safesend)
342 getresponse = keepalive.wrapgetresponse(httplib.HTTPConnection)
342 getresponse = keepalive.wrapgetresponse(httplib.HTTPConnection)
343
343
344 def __init__(self, host, port=None, key_file=None, cert_file=None,
344 def __init__(self, host, port=None, key_file=None, cert_file=None,
345 *args, **kwargs):
345 *args, **kwargs):
346 httplib.HTTPConnection.__init__(self, host, port, *args, **kwargs)
346 httplib.HTTPConnection.__init__(self, host, port, *args, **kwargs)
347 self.key_file = key_file
347 self.key_file = key_file
348 self.cert_file = cert_file
348 self.cert_file = cert_file
349
349
350 def connect(self):
350 def connect(self):
351 self.sock = _create_connection((self.host, self.port))
351 self.sock = _create_connection((self.host, self.port))
352
352
353 host = self.host
353 host = self.host
354 if self.realhostport: # use CONNECT proxy
354 if self.realhostport: # use CONNECT proxy
355 _generic_proxytunnel(self)
355 _generic_proxytunnel(self)
356 host = self.realhostport.rsplit(':', 1)[0]
356 host = self.realhostport.rsplit(':', 1)[0]
357 self.sock = sslutil.wrapsocket(
357 self.sock = sslutil.wrapsocket(
358 self.sock, self.key_file, self.cert_file, ui=self.ui,
358 self.sock, self.key_file, self.cert_file, ui=self.ui,
359 serverhostname=host)
359 serverhostname=host)
360 sslutil.validatesocket(self.sock)
360 sslutil.validatesocket(self.sock)
361
361
362 class httpshandler(keepalive.KeepAliveHandler, urlreq.httpshandler):
362 class httpshandler(keepalive.KeepAliveHandler, urlreq.httpshandler):
363 def __init__(self, ui):
363 def __init__(self, ui):
364 keepalive.KeepAliveHandler.__init__(self)
364 keepalive.KeepAliveHandler.__init__(self)
365 urlreq.httpshandler.__init__(self)
365 urlreq.httpshandler.__init__(self)
366 self.ui = ui
366 self.ui = ui
367 self.pwmgr = passwordmgr(self.ui,
367 self.pwmgr = passwordmgr(self.ui,
368 urlreq.httppasswordmgrwithdefaultrealm())
368 self.ui.httppasswordmgrdb)
369
369
370 def _start_transaction(self, h, req):
370 def _start_transaction(self, h, req):
371 _generic_start_transaction(self, h, req)
371 _generic_start_transaction(self, h, req)
372 return keepalive.KeepAliveHandler._start_transaction(self, h, req)
372 return keepalive.KeepAliveHandler._start_transaction(self, h, req)
373
373
374 def https_open(self, req):
374 def https_open(self, req):
375 # req.get_full_url() does not contain credentials and we may
375 # req.get_full_url() does not contain credentials and we may
376 # need them to match the certificates.
376 # need them to match the certificates.
377 url = req.get_full_url()
377 url = req.get_full_url()
378 user, password = self.pwmgr.find_stored_password(url)
378 user, password = self.pwmgr.find_stored_password(url)
379 res = httpconnectionmod.readauthforuri(self.ui, url, user)
379 res = httpconnectionmod.readauthforuri(self.ui, url, user)
380 if res:
380 if res:
381 group, auth = res
381 group, auth = res
382 self.auth = auth
382 self.auth = auth
383 self.ui.debug("using auth.%s.* for authentication\n" % group)
383 self.ui.debug("using auth.%s.* for authentication\n" % group)
384 else:
384 else:
385 self.auth = None
385 self.auth = None
386 return self.do_open(self._makeconnection, req)
386 return self.do_open(self._makeconnection, req)
387
387
388 def _makeconnection(self, host, port=None, *args, **kwargs):
388 def _makeconnection(self, host, port=None, *args, **kwargs):
389 keyfile = None
389 keyfile = None
390 certfile = None
390 certfile = None
391
391
392 if len(args) >= 1: # key_file
392 if len(args) >= 1: # key_file
393 keyfile = args[0]
393 keyfile = args[0]
394 if len(args) >= 2: # cert_file
394 if len(args) >= 2: # cert_file
395 certfile = args[1]
395 certfile = args[1]
396 args = args[2:]
396 args = args[2:]
397
397
398 # if the user has specified different key/cert files in
398 # if the user has specified different key/cert files in
399 # hgrc, we prefer these
399 # hgrc, we prefer these
400 if self.auth and 'key' in self.auth and 'cert' in self.auth:
400 if self.auth and 'key' in self.auth and 'cert' in self.auth:
401 keyfile = self.auth['key']
401 keyfile = self.auth['key']
402 certfile = self.auth['cert']
402 certfile = self.auth['cert']
403
403
404 conn = httpsconnection(host, port, keyfile, certfile, *args,
404 conn = httpsconnection(host, port, keyfile, certfile, *args,
405 **kwargs)
405 **kwargs)
406 conn.ui = self.ui
406 conn.ui = self.ui
407 return conn
407 return conn
408
408
409 class httpdigestauthhandler(urlreq.httpdigestauthhandler):
409 class httpdigestauthhandler(urlreq.httpdigestauthhandler):
410 def __init__(self, *args, **kwargs):
410 def __init__(self, *args, **kwargs):
411 urlreq.httpdigestauthhandler.__init__(self, *args, **kwargs)
411 urlreq.httpdigestauthhandler.__init__(self, *args, **kwargs)
412 self.retried_req = None
412 self.retried_req = None
413
413
414 def reset_retry_count(self):
414 def reset_retry_count(self):
415 # Python 2.6.5 will call this on 401 or 407 errors and thus loop
415 # Python 2.6.5 will call this on 401 or 407 errors and thus loop
416 # forever. We disable reset_retry_count completely and reset in
416 # forever. We disable reset_retry_count completely and reset in
417 # http_error_auth_reqed instead.
417 # http_error_auth_reqed instead.
418 pass
418 pass
419
419
420 def http_error_auth_reqed(self, auth_header, host, req, headers):
420 def http_error_auth_reqed(self, auth_header, host, req, headers):
421 # Reset the retry counter once for each request.
421 # Reset the retry counter once for each request.
422 if req is not self.retried_req:
422 if req is not self.retried_req:
423 self.retried_req = req
423 self.retried_req = req
424 self.retried = 0
424 self.retried = 0
425 return urlreq.httpdigestauthhandler.http_error_auth_reqed(
425 return urlreq.httpdigestauthhandler.http_error_auth_reqed(
426 self, auth_header, host, req, headers)
426 self, auth_header, host, req, headers)
427
427
428 class httpbasicauthhandler(urlreq.httpbasicauthhandler):
428 class httpbasicauthhandler(urlreq.httpbasicauthhandler):
429 def __init__(self, *args, **kwargs):
429 def __init__(self, *args, **kwargs):
430 self.auth = None
430 self.auth = None
431 urlreq.httpbasicauthhandler.__init__(self, *args, **kwargs)
431 urlreq.httpbasicauthhandler.__init__(self, *args, **kwargs)
432 self.retried_req = None
432 self.retried_req = None
433
433
434 def http_request(self, request):
434 def http_request(self, request):
435 if self.auth:
435 if self.auth:
436 request.add_unredirected_header(self.auth_header, self.auth)
436 request.add_unredirected_header(self.auth_header, self.auth)
437
437
438 return request
438 return request
439
439
440 def https_request(self, request):
440 def https_request(self, request):
441 if self.auth:
441 if self.auth:
442 request.add_unredirected_header(self.auth_header, self.auth)
442 request.add_unredirected_header(self.auth_header, self.auth)
443
443
444 return request
444 return request
445
445
446 def reset_retry_count(self):
446 def reset_retry_count(self):
447 # Python 2.6.5 will call this on 401 or 407 errors and thus loop
447 # Python 2.6.5 will call this on 401 or 407 errors and thus loop
448 # forever. We disable reset_retry_count completely and reset in
448 # forever. We disable reset_retry_count completely and reset in
449 # http_error_auth_reqed instead.
449 # http_error_auth_reqed instead.
450 pass
450 pass
451
451
452 def http_error_auth_reqed(self, auth_header, host, req, headers):
452 def http_error_auth_reqed(self, auth_header, host, req, headers):
453 # Reset the retry counter once for each request.
453 # Reset the retry counter once for each request.
454 if req is not self.retried_req:
454 if req is not self.retried_req:
455 self.retried_req = req
455 self.retried_req = req
456 self.retried = 0
456 self.retried = 0
457 return urlreq.httpbasicauthhandler.http_error_auth_reqed(
457 return urlreq.httpbasicauthhandler.http_error_auth_reqed(
458 self, auth_header, host, req, headers)
458 self, auth_header, host, req, headers)
459
459
460 def retry_http_basic_auth(self, host, req, realm):
460 def retry_http_basic_auth(self, host, req, realm):
461 user, pw = self.passwd.find_user_password(realm, req.get_full_url())
461 user, pw = self.passwd.find_user_password(realm, req.get_full_url())
462 if pw is not None:
462 if pw is not None:
463 raw = "%s:%s" % (user, pw)
463 raw = "%s:%s" % (user, pw)
464 auth = 'Basic %s' % base64.b64encode(raw).strip()
464 auth = 'Basic %s' % base64.b64encode(raw).strip()
465 if req.headers.get(self.auth_header, None) == auth:
465 if req.headers.get(self.auth_header, None) == auth:
466 return None
466 return None
467 self.auth = auth
467 self.auth = auth
468 req.add_unredirected_header(self.auth_header, auth)
468 req.add_unredirected_header(self.auth_header, auth)
469 return self.parent.open(req)
469 return self.parent.open(req)
470 else:
470 else:
471 return None
471 return None
472
472
473 handlerfuncs = []
473 handlerfuncs = []
474
474
475 def opener(ui, authinfo=None):
475 def opener(ui, authinfo=None):
476 '''
476 '''
477 construct an opener suitable for urllib2
477 construct an opener suitable for urllib2
478 authinfo will be added to the password manager
478 authinfo will be added to the password manager
479 '''
479 '''
480 # experimental config: ui.usehttp2
480 # experimental config: ui.usehttp2
481 if ui.configbool('ui', 'usehttp2', False):
481 if ui.configbool('ui', 'usehttp2', False):
482 handlers = [
482 handlers = [
483 httpconnectionmod.http2handler(
483 httpconnectionmod.http2handler(
484 ui,
484 ui,
485 passwordmgr(ui, urlreq.httppasswordmgrwithdefaultrealm()))
485 passwordmgr(ui, ui.httppasswordmgrdb))
486 ]
486 ]
487 else:
487 else:
488 handlers = [httphandler()]
488 handlers = [httphandler()]
489 if has_https:
489 if has_https:
490 handlers.append(httpshandler(ui))
490 handlers.append(httpshandler(ui))
491
491
492 handlers.append(proxyhandler(ui))
492 handlers.append(proxyhandler(ui))
493
493
494 passmgr = passwordmgr(ui, urlreq.httppasswordmgrwithdefaultrealm())
494 passmgr = passwordmgr(ui, ui.httppasswordmgrdb)
495 if authinfo is not None:
495 if authinfo is not None:
496 passmgr.add_password(*authinfo)
496 passmgr.add_password(*authinfo)
497 user, passwd = authinfo[2:4]
497 user, passwd = authinfo[2:4]
498 ui.debug('http auth: user %s, password %s\n' %
498 ui.debug('http auth: user %s, password %s\n' %
499 (user, passwd and '*' * len(passwd) or 'not set'))
499 (user, passwd and '*' * len(passwd) or 'not set'))
500
500
501 handlers.extend((httpbasicauthhandler(passmgr),
501 handlers.extend((httpbasicauthhandler(passmgr),
502 httpdigestauthhandler(passmgr)))
502 httpdigestauthhandler(passmgr)))
503 handlers.extend([h(ui, passmgr) for h in handlerfuncs])
503 handlers.extend([h(ui, passmgr) for h in handlerfuncs])
504 opener = urlreq.buildopener(*handlers)
504 opener = urlreq.buildopener(*handlers)
505
505
506 # 1.0 here is the _protocol_ version
506 # 1.0 here is the _protocol_ version
507 opener.addheaders = [('User-agent', 'mercurial/proto-1.0')]
507 opener.addheaders = [('User-agent', 'mercurial/proto-1.0')]
508 opener.addheaders.append(('Accept', 'application/mercurial-0.1'))
508 opener.addheaders.append(('Accept', 'application/mercurial-0.1'))
509 return opener
509 return opener
510
510
511 def open(ui, url_, data=None):
511 def open(ui, url_, data=None):
512 u = util.url(url_)
512 u = util.url(url_)
513 if u.scheme:
513 if u.scheme:
514 u.scheme = u.scheme.lower()
514 u.scheme = u.scheme.lower()
515 url_, authinfo = u.authinfo()
515 url_, authinfo = u.authinfo()
516 else:
516 else:
517 path = util.normpath(os.path.abspath(url_))
517 path = util.normpath(os.path.abspath(url_))
518 url_ = 'file://' + urlreq.pathname2url(path)
518 url_ = 'file://' + urlreq.pathname2url(path)
519 authinfo = None
519 authinfo = None
520 return opener(ui, authinfo).open(url_, data)
520 return opener(ui, authinfo).open(url_, data)
General Comments 0
You need to be logged in to leave comments. Login now