##// END OF EJS Templates
merge with crew.
Vadim Gelfer -
r2471:6904e1ef merge default
parent child Browse files
Show More
@@ -381,6 +381,14 b' web::'
381 Default is false.
381 Default is false.
382 allowpull;;
382 allowpull;;
383 Whether to allow pulling from the repository. Default is true.
383 Whether to allow pulling from the repository. Default is true.
384 allow_push;;
385 Whether to allow pushing to the repository. If empty or not set,
386 push is not allowed. If the special value "*", any remote user
387 can push, including unauthenticated users. Otherwise, the remote
388 user must have been authenticated, and the authenticated user name
389 must be present in this list (separated by whitespace or ",").
390 The contents of the allow_push list are examined after the
391 deny_push list.
384 allowzip;;
392 allowzip;;
385 (DEPRECATED) Whether to allow .zip downloading of repo revisions.
393 (DEPRECATED) Whether to allow .zip downloading of repo revisions.
386 Default is false. This feature creates temporary files.
394 Default is false. This feature creates temporary files.
@@ -391,6 +399,13 b' web::'
391 contact;;
399 contact;;
392 Name or email address of the person in charge of the repository.
400 Name or email address of the person in charge of the repository.
393 Default is "unknown".
401 Default is "unknown".
402 deny_push;;
403 Whether to deny pushing to the repository. If empty or not set,
404 push is not denied. If the special value "*", all remote users
405 are denied push. Otherwise, unauthenticated users are all denied,
406 and any authenticated user name present in this list (separated by
407 whitespace or ",") is also denied. The contents of the deny_push
408 list are examined before the allow_push list.
394 description;;
409 description;;
395 Textual description of the repository's purpose or contents.
410 Textual description of the repository's purpose or contents.
396 Default is "unknown".
411 Default is "unknown".
@@ -407,6 +422,9 b' web::'
407 Maximum number of files to list per changeset. Default is 10.
422 Maximum number of files to list per changeset. Default is 10.
408 port;;
423 port;;
409 Port to listen on. Default is 8000.
424 Port to listen on. Default is 8000.
425 push_ssl;;
426 Whether to require that inbound pushes be transported over SSL to
427 prevent password sniffing. Default is true.
410 style;;
428 style;;
411 Which template map style to use.
429 Which template map style to use.
412 templates;;
430 templates;;
@@ -10,6 +10,7 b''
10 */
10 */
11
11
12 #include <Python.h>
12 #include <Python.h>
13 #include <stdint.h>
13 #include <stdlib.h>
14 #include <stdlib.h>
14 #include <string.h>
15 #include <string.h>
15
16
@@ -379,11 +379,20 b' def dodiff(fp, ui, repo, node1, node2, f'
379 if node2:
379 if node2:
380 change = repo.changelog.read(node2)
380 change = repo.changelog.read(node2)
381 mmap2 = repo.manifest.read(change[0])
381 mmap2 = repo.manifest.read(change[0])
382 date2 = util.datestr(change[2])
382 _date2 = util.datestr(change[2])
383 def date2(f):
384 return _date2
383 def read(f):
385 def read(f):
384 return repo.file(f).read(mmap2[f])
386 return repo.file(f).read(mmap2[f])
385 else:
387 else:
386 date2 = util.datestr()
388 tz = util.makedate()[1]
389 _date2 = util.datestr()
390 def date2(f):
391 try:
392 return util.datestr((os.lstat(repo.wjoin(f)).st_mtime, tz))
393 except IOError, err:
394 if err.errno != errno.ENOENT: raise
395 return _date2
387 def read(f):
396 def read(f):
388 return repo.wread(f)
397 return repo.wread(f)
389
398
@@ -401,17 +410,17 b' def dodiff(fp, ui, repo, node1, node2, f'
401 if f in mmap:
410 if f in mmap:
402 to = repo.file(f).read(mmap[f])
411 to = repo.file(f).read(mmap[f])
403 tn = read(f)
412 tn = read(f)
404 fp.write(mdiff.unidiff(to, date1, tn, date2, f, r, text=text,
413 fp.write(mdiff.unidiff(to, date1, tn, date2(f), f, r, text=text,
405 showfunc=showfunc, ignorews=ignorews))
414 showfunc=showfunc, ignorews=ignorews))
406 for f in added:
415 for f in added:
407 to = None
416 to = None
408 tn = read(f)
417 tn = read(f)
409 fp.write(mdiff.unidiff(to, date1, tn, date2, f, r, text=text,
418 fp.write(mdiff.unidiff(to, date1, tn, date2(f), f, r, text=text,
410 showfunc=showfunc, ignorews=ignorews))
419 showfunc=showfunc, ignorews=ignorews))
411 for f in removed:
420 for f in removed:
412 to = repo.file(f).read(mmap[f])
421 to = repo.file(f).read(mmap[f])
413 tn = None
422 tn = None
414 fp.write(mdiff.unidiff(to, date1, tn, date2, f, r, text=text,
423 fp.write(mdiff.unidiff(to, date1, tn, date2(f), f, r, text=text,
415 showfunc=showfunc, ignorews=ignorews))
424 showfunc=showfunc, ignorews=ignorews))
416
425
417 def trimuser(ui, name, rev, revcache):
426 def trimuser(ui, name, rev, revcache):
@@ -11,34 +11,60 b' from demandload import *'
11 from i18n import gettext as _
11 from i18n import gettext as _
12 demandload(globals(), "localrepo bundlerepo httprepo sshrepo statichttprepo")
12 demandload(globals(), "localrepo bundlerepo httprepo sshrepo statichttprepo")
13
13
14 def bundle(ui, path):
15 if path.startswith('bundle://'):
16 path = path[9:]
17 else:
18 path = path[7:]
19 s = path.split("+", 1)
20 if len(s) == 1:
21 repopath, bundlename = "", s[0]
22 else:
23 repopath, bundlename = s
24 return bundlerepo.bundlerepository(ui, repopath, bundlename)
25
26 def hg(ui, path):
27 ui.warn(_("hg:// syntax is deprecated, please use http:// instead\n"))
28 return httprepo.httprepository(ui, path.replace("hg://", "http://"))
29
30 def local_(ui, path, create=0):
31 return localrepo.localrepository(ui, path, create)
32
33 def old_http(ui, path):
34 ui.warn(_("old-http:// syntax is deprecated, "
35 "please use static-http:// instead\n"))
36 return statichttprepo.statichttprepository(
37 ui, path.replace("old-http://", "http://"))
38
39 def static_http(ui, path):
40 return statichttprepo.statichttprepository(
41 ui, path.replace("static-http://", "http://"))
42
43 protocols = {
44 'bundle': bundle,
45 'file': local_,
46 'hg': hg,
47 'http': lambda ui, path: httprepo.httprepository(ui, path),
48 'https': lambda ui, path: httprepo.httpsrepository(ui, path),
49 'old-http': old_http,
50 'ssh': lambda ui, path: sshrepo.sshrepository(ui, path),
51 'static-http': static_http,
52 None: local_,
53 }
54
14 def repository(ui, path=None, create=0):
55 def repository(ui, path=None, create=0):
15 if path:
56 scheme = path
16 if path.startswith("http://"):
57 if scheme:
17 return httprepo.httprepository(ui, path)
58 c = scheme.find(':')
18 if path.startswith("https://"):
59 scheme = c >= 0 and scheme[:c]
19 return httprepo.httpsrepository(ui, path)
60 if not scheme: scheme = None
20 if path.startswith("hg://"):
61 try:
21 ui.warn(_("hg:// syntax is deprecated, "
62 ctor = protocols[scheme]
22 "please use http:// instead\n"))
63 if create:
23 return httprepo.httprepository(
64 return ctor(ui, path, create)
24 ui, path.replace("hg://", "http://"))
65 return ctor(ui, path)
25 if path.startswith("old-http://"):
66 except KeyError:
26 ui.warn(_("old-http:// syntax is deprecated, "
67 raise util.Abort(_('protocol "%s" not known') % scheme)
27 "please use static-http:// instead\n"))
68 except TypeError:
28 return statichttprepo.statichttprepository(
69 raise util.Abort(_('cannot create new repository over "%s" protocol') %
29 ui, path.replace("old-http://", "http://"))
70 (scheme or 'file'))
30 if path.startswith("static-http://"):
31 return statichttprepo.statichttprepository(
32 ui, path.replace("static-http://", "http://"))
33 if path.startswith("ssh://"):
34 return sshrepo.sshrepository(ui, path)
35 if path.startswith("bundle://"):
36 path = path[9:]
37 s = path.split("+", 1)
38 if len(s) == 1:
39 repopath, bundlename = "", s[0]
40 else:
41 repopath, bundlename = s
42 return bundlerepo.bundlerepository(ui, repopath, bundlename)
43
44 return localrepo.localrepository(ui, path, create)
@@ -10,7 +10,7 b' import os'
10 import os.path
10 import os.path
11 import mimetypes
11 import mimetypes
12 from mercurial.demandload import demandload
12 from mercurial.demandload import demandload
13 demandload(globals(), "re zlib ConfigParser cStringIO")
13 demandload(globals(), "re zlib ConfigParser cStringIO sys tempfile")
14 demandload(globals(), "mercurial:mdiff,ui,hg,util,archival,templater")
14 demandload(globals(), "mercurial:mdiff,ui,hg,util,archival,templater")
15 demandload(globals(), "mercurial.hgweb.request:hgrequest")
15 demandload(globals(), "mercurial.hgweb.request:hgrequest")
16 demandload(globals(), "mercurial.hgweb.common:get_mtime,staticfile")
16 demandload(globals(), "mercurial.hgweb.common:get_mtime,staticfile")
@@ -835,7 +835,97 b' class hgweb(object):'
835 or self.t("error", error="%r not found" % fname))
835 or self.t("error", error="%r not found" % fname))
836
836
837 def do_capabilities(self, req):
837 def do_capabilities(self, req):
838 resp = ''
838 resp = 'unbundle'
839 req.httphdr("application/mercurial-0.1", length=len(resp))
839 req.httphdr("application/mercurial-0.1", length=len(resp))
840 req.write(resp)
840 req.write(resp)
841
841
842 def check_perm(self, req, op, default):
843 '''check permission for operation based on user auth.
844 return true if op allowed, else false.
845 default is policy to use if no config given.'''
846
847 user = req.env.get('REMOTE_USER')
848
849 deny = self.repo.ui.config('web', 'deny_' + op, '')
850 deny = deny.replace(',', ' ').split()
851
852 if deny and (not user or deny == ['*'] or user in deny):
853 return False
854
855 allow = self.repo.ui.config('web', 'allow_' + op, '')
856 allow = allow.replace(',', ' ').split()
857
858 return (allow and (allow == ['*'] or user in allow)) or default
859
860 def do_unbundle(self, req):
861 def bail(response, headers={}):
862 length = int(req.env['CONTENT_LENGTH'])
863 for s in util.filechunkiter(req, limit=length):
864 # drain incoming bundle, else client will not see
865 # response when run outside cgi script
866 pass
867 req.httphdr("application/mercurial-0.1", headers=headers)
868 req.write('0\n')
869 req.write(response)
870
871 # require ssl by default, auth info cannot be sniffed and
872 # replayed
873 ssl_req = self.repo.ui.configbool('web', 'push_ssl', True)
874 if ssl_req and not req.env.get('HTTPS'):
875 bail(_('ssl required\n'))
876 return
877
878 # do not allow push unless explicitly allowed
879 if not self.check_perm(req, 'push', False):
880 bail(_('push not authorized\n'),
881 headers={'status': '401 Unauthorized'})
882 return
883
884 req.httphdr("application/mercurial-0.1")
885
886 their_heads = req.form['heads'][0].split(' ')
887
888 def check_heads():
889 heads = map(hex, self.repo.heads())
890 return their_heads == [hex('force')] or their_heads == heads
891
892 # fail early if possible
893 if not check_heads():
894 bail(_('unsynced changes\n'))
895 return
896
897 # do not lock repo until all changegroup data is
898 # streamed. save to temporary file.
899
900 fd, tempname = tempfile.mkstemp(prefix='hg-unbundle-')
901 fp = os.fdopen(fd, 'wb+')
902 try:
903 length = int(req.env['CONTENT_LENGTH'])
904 for s in util.filechunkiter(req, limit=length):
905 fp.write(s)
906
907 lock = self.repo.lock()
908 try:
909 if not check_heads():
910 req.write('0\n')
911 req.write(_('unsynced changes\n'))
912 return
913
914 fp.seek(0)
915
916 # send addchangegroup output to client
917
918 old_stdout = sys.stdout
919 sys.stdout = cStringIO.StringIO()
920
921 try:
922 ret = self.repo.addchangegroup(fp, 'serve')
923 req.write('%d\n' % ret)
924 req.write(sys.stdout.getvalue())
925 finally:
926 sys.stdout = old_stdout
927 finally:
928 lock.release()
929 finally:
930 fp.close()
931 os.unlink(tempname)
@@ -18,6 +18,9 b' class hgrequest(object):'
18 self.form = cgi.parse(self.inp, self.env, keep_blank_values=1)
18 self.form = cgi.parse(self.inp, self.env, keep_blank_values=1)
19 self.will_close = True
19 self.will_close = True
20
20
21 def read(self, count=-1):
22 return self.inp.read(count)
23
21 def write(self, *things):
24 def write(self, *things):
22 for thing in things:
25 for thing in things:
23 if hasattr(thing, "__iter__"):
26 if hasattr(thing, "__iter__"):
@@ -42,9 +45,9 b' class hgrequest(object):'
42 self.out.write("%s: %s\r\n" % header)
45 self.out.write("%s: %s\r\n" % header)
43 self.out.write("\r\n")
46 self.out.write("\r\n")
44
47
45 def httphdr(self, type, filename=None, length=0):
48 def httphdr(self, type, filename=None, length=0, headers={}):
46
49 headers = headers.items()
47 headers = [('Content-type', type)]
50 headers.append(('Content-type', type))
48 if filename:
51 if filename:
49 headers.append(('Content-disposition', 'attachment; filename=%s' %
52 headers.append(('Content-disposition', 'attachment; filename=%s' %
50 filename))
53 filename))
@@ -10,7 +10,7 b' from remoterepo import *'
10 from i18n import gettext as _
10 from i18n import gettext as _
11 from demandload import *
11 from demandload import *
12 demandload(globals(), "hg os urllib urllib2 urlparse zlib util httplib")
12 demandload(globals(), "hg os urllib urllib2 urlparse zlib util httplib")
13 demandload(globals(), "keepalive")
13 demandload(globals(), "errno keepalive tempfile socket")
14
14
15 class passwordmgr(urllib2.HTTPPasswordMgrWithDefaultRealm):
15 class passwordmgr(urllib2.HTTPPasswordMgrWithDefaultRealm):
16 def __init__(self, ui):
16 def __init__(self, ui):
@@ -69,6 +69,22 b' def netlocunsplit(host, port, user=None,'
69 return userpass + '@' + hostport
69 return userpass + '@' + hostport
70 return hostport
70 return hostport
71
71
72 class httpconnection(keepalive.HTTPConnection):
73 # must be able to send big bundle as stream.
74
75 def send(self, data):
76 if isinstance(data, str):
77 keepalive.HTTPConnection.send(self, data)
78 else:
79 # if auth required, some data sent twice, so rewind here
80 data.seek(0)
81 for chunk in util.filechunkiter(data):
82 keepalive.HTTPConnection.send(self, chunk)
83
84 class httphandler(keepalive.HTTPHandler):
85 def http_open(self, req):
86 return self.do_open(httpconnection, req)
87
72 class httprepository(remoterepository):
88 class httprepository(remoterepository):
73 def __init__(self, ui, path):
89 def __init__(self, ui, path):
74 self.caps = None
90 self.caps = None
@@ -86,7 +102,7 b' class httprepository(remoterepository):'
86
102
87 proxyurl = ui.config("http_proxy", "host") or os.getenv('http_proxy')
103 proxyurl = ui.config("http_proxy", "host") or os.getenv('http_proxy')
88 proxyauthinfo = None
104 proxyauthinfo = None
89 handler = keepalive.HTTPHandler()
105 handler = httphandler()
90
106
91 if proxyurl:
107 if proxyurl:
92 # proxy can be proper url or host[:port]
108 # proxy can be proper url or host[:port]
@@ -154,6 +170,8 b' class httprepository(remoterepository):'
154 self.caps = self.do_read('capabilities').split()
170 self.caps = self.do_read('capabilities').split()
155 except hg.RepoError:
171 except hg.RepoError:
156 self.caps = ()
172 self.caps = ()
173 self.ui.debug(_('capabilities: %s\n') %
174 (' '.join(self.caps or ['none'])))
157 return self.caps
175 return self.caps
158
176
159 capabilities = property(get_caps)
177 capabilities = property(get_caps)
@@ -165,13 +183,19 b' class httprepository(remoterepository):'
165 raise util.Abort(_('operation not supported over http'))
183 raise util.Abort(_('operation not supported over http'))
166
184
167 def do_cmd(self, cmd, **args):
185 def do_cmd(self, cmd, **args):
186 data = args.pop('data', None)
187 headers = args.pop('headers', {})
168 self.ui.debug(_("sending %s command\n") % cmd)
188 self.ui.debug(_("sending %s command\n") % cmd)
169 q = {"cmd": cmd}
189 q = {"cmd": cmd}
170 q.update(args)
190 q.update(args)
171 qs = urllib.urlencode(q)
191 qs = urllib.urlencode(q)
172 cu = "%s?%s" % (self.url, qs)
192 cu = "%s?%s" % (self.url, qs)
173 try:
193 try:
174 resp = urllib2.urlopen(cu)
194 resp = urllib2.urlopen(urllib2.Request(cu, data, headers))
195 except urllib2.HTTPError, inst:
196 if inst.code == 401:
197 raise util.Abort(_('authorization failed'))
198 raise
175 except httplib.HTTPException, inst:
199 except httplib.HTTPException, inst:
176 self.ui.debug(_('http error while sending %s command\n') % cmd)
200 self.ui.debug(_('http error while sending %s command\n') % cmd)
177 self.ui.print_exc()
201 self.ui.print_exc()
@@ -249,7 +273,34 b' class httprepository(remoterepository):'
249 return util.chunkbuffer(zgenerator(util.filechunkiter(f)))
273 return util.chunkbuffer(zgenerator(util.filechunkiter(f)))
250
274
251 def unbundle(self, cg, heads, source):
275 def unbundle(self, cg, heads, source):
252 raise util.Abort(_('operation not supported over http'))
276 # have to stream bundle to a temp file because we do not have
277 # http 1.1 chunked transfer.
278
279 fd, tempname = tempfile.mkstemp(prefix='hg-unbundle-')
280 fp = os.fdopen(fd, 'wb+')
281 try:
282 for chunk in util.filechunkiter(cg):
283 fp.write(chunk)
284 length = fp.tell()
285 try:
286 rfp = self.do_cmd(
287 'unbundle', data=fp,
288 headers={'content-length': length,
289 'content-type': 'application/octet-stream'},
290 heads=' '.join(map(hex, heads)))
291 try:
292 ret = int(rfp.readline())
293 self.ui.write(rfp.read())
294 return ret
295 finally:
296 rfp.close()
297 except socket.error, err:
298 if err[0] in (errno.ECONNRESET, errno.EPIPE):
299 raise util.Abort(_('push failed: %s'), err[1])
300 raise util.Abort(err[1])
301 finally:
302 fp.close()
303 os.unlink(tempname)
253
304
254 class httpsrepository(httprepository):
305 class httpsrepository(httprepository):
255 pass
306 pass
@@ -1115,9 +1115,8 b' class localrepository(object):'
1115 # servers, http servers).
1115 # servers, http servers).
1116
1116
1117 if 'unbundle' in remote.capabilities:
1117 if 'unbundle' in remote.capabilities:
1118 self.push_unbundle(remote, force, revs)
1118 return self.push_unbundle(remote, force, revs)
1119 else:
1119 return self.push_addchangegroup(remote, force, revs)
1120 self.push_addchangegroup(remote, force, revs)
1121
1120
1122 def prepush(self, remote, force, revs):
1121 def prepush(self, remote, force, revs):
1123 base = {}
1122 base = {}
@@ -23,13 +23,15 b''
23 #include <Python.h>
23 #include <Python.h>
24 #include <stdlib.h>
24 #include <stdlib.h>
25 #include <string.h>
25 #include <string.h>
26
26 #ifdef _WIN32
27 #ifdef _WIN32
27 #ifdef _MSC_VER
28 # ifdef _MSC_VER
28 #define inline __inline
29 /* msvc 6.0 has problems */
30 # define inline __inline
29 typedef unsigned long uint32_t;
31 typedef unsigned long uint32_t;
30 #else
32 # else
31 #include <stdint.h>
33 # include <stdint.h>
32 #endif
34 # endif
33 static uint32_t ntohl(uint32_t x)
35 static uint32_t ntohl(uint32_t x)
34 {
36 {
35 return ((x & 0x000000ffUL) << 24) |
37 return ((x & 0x000000ffUL) << 24) |
@@ -38,8 +40,10 b' static uint32_t ntohl(uint32_t x)'
38 ((x & 0xff000000UL) >> 24);
40 ((x & 0xff000000UL) >> 24);
39 }
41 }
40 #else
42 #else
41 #include <sys/types.h>
43 /* not windows */
42 #include <arpa/inet.h>
44 # include <sys/types.h>
45 # include <arpa/inet.h>
46 # include <stdint.h>
43 #endif
47 #endif
44
48
45 static char mpatch_doc[] = "Efficient binary patching.";
49 static char mpatch_doc[] = "Efficient binary patching.";
@@ -821,16 +821,22 b' class chunkbuffer(object):'
821 s, self.buf = self.buf[:l], buffer(self.buf, l)
821 s, self.buf = self.buf[:l], buffer(self.buf, l)
822 return s
822 return s
823
823
824 def filechunkiter(f, size = 65536):
824 def filechunkiter(f, size=65536, limit=None):
825 """Create a generator that produces all the data in the file size
825 """Create a generator that produces the data in the file size
826 (default 65536) bytes at a time. Chunks may be less than size
826 (default 65536) bytes at a time, up to optional limit (default is
827 bytes if the chunk is the last chunk in the file, or the file is a
827 to read all data). Chunks may be less than size bytes if the
828 socket or some other type of file that sometimes reads less data
828 chunk is the last chunk in the file, or the file is a socket or
829 than is requested."""
829 some other type of file that sometimes reads less data than is
830 s = f.read(size)
830 requested."""
831 while len(s) > 0:
831 assert size >= 0
832 assert limit is None or limit >= 0
833 while True:
834 if limit is None: nbytes = size
835 else: nbytes = min(limit, size)
836 s = nbytes and f.read(nbytes)
837 if not s: break
838 if limit: limit -= len(s)
832 yield s
839 yield s
833 s = f.read(size)
834
840
835 def makedate():
841 def makedate():
836 lt = time.localtime()
842 lt = time.localtime()
General Comments 0
You need to be logged in to leave comments. Login now