Mercurial > traipse
comparison upmana/mercurial/httprepo.py @ 28:ff154cf3350c ornery-orc
Traipse 'OpenRPG' {100203-00}
Traipse is a distribution of OpenRPG that is designed to be easy to
setup and go. Traipse also makes it easy for developers to work on code
without fear of sacrifice. 'Ornery-Orc' continues the trend of 'Grumpy'
and adds fixes to the code. 'Ornery-Orc's main goal is to offer more
advanced features and enhance the productivity of the user.
Update Summary (Stable)
New Features:
New Bookmarks Feature
New 'boot' command to remote admin
New confirmation window for sent nodes
Miniatures Layer pop up box allows users to turn off Mini labels, from
FlexiRPG
New Zoom Mouse plugin added
New Images added to Plugin UI
Switching to Element Tree
New Map efficiency, from FlexiRPG
New Status Bar to Update Manager
New TrueDebug Class in orpg_log (See documentation for usage)
New Portable Mercurial
New Tip of the Day, from Core and community
New Reference Syntax added for custom PC sheets
New Child Reference for gametree
New Parent Reference for gametree
New Gametree Recursion method, mapping, context sensitivity, and
effeciency..
New Features node with bonus nodes and Node Referencing help added
New Dieroller structure from Core
New DieRoller portability for odd Dice
New 7th Sea die roller; ie [7k3] = [7d10.takeHighest(3).open(10)]
New 'Mythos' System die roller added
New vs. die roller method for WoD; ie [3v3] = [3d10.vs(3)]. Included for
Mythos roller also
New Warhammer FRPG Die Roller (Special thanks to Puu-san for the
support)
New EZ_Tree Reference system. Push a button, Traipse the tree, get a
reference (Beta!)
New Grids act more like Spreadsheets in Use mode, with Auto Calc
Fixes:
Fix to allow for portability to an OpenSUSE linux OS
Fix to mplay_client for Fedora and OpenSUSE
Fix to Text based Server
Fix to Remote Admin Commands
Fix to Pretty Print, from Core
Fix to Splitter Nodes not being created
Fix to massive amounts of images loading, from Core
Fix to Map from gametree not showing to all clients
Fix to gametree about menus
Fix to Password Manager check on startup
Fix to PC Sheets from tool nodes. They now use the tabber_panel
Fix to Whiteboard ID to prevent random line or text deleting.
Fixes to Server, Remote Server, and Server GUI
Fix to Update Manager; cleaner clode for saved repositories
Fixes made to Settings Panel and now reactive settings when Ok is
pressed
Fixes to Alternity roller's attack roll. Uses a simple Tuple instead of
a Splice
Fix to Use panel of Forms and Tabbers. Now longer enters design mode
Fix made Image Fetching. New fetching image and new failed image
Fix to whiteboard ID's to prevent non updated clients from ruining the
fix.
default_manifest.xml renamed to default_upmana.xml
author | sirebral |
---|---|
date | Wed, 03 Feb 2010 22:16:49 -0600 |
parents | |
children |
comparison
equal
deleted
inserted
replaced
27:51428d30c59e | 28:ff154cf3350c |
---|---|
1 # httprepo.py - HTTP repository proxy classes for mercurial | |
2 # | |
3 # Copyright 2005, 2006 Matt Mackall <mpm@selenic.com> | |
4 # Copyright 2006 Vadim Gelfer <vadim.gelfer@gmail.com> | |
5 # | |
6 # This software may be used and distributed according to the terms of the | |
7 # GNU General Public License version 2, incorporated herein by reference. | |
8 | |
9 from node import bin, hex, nullid | |
10 from i18n import _ | |
11 import repo, changegroup, statichttprepo, error, url, util | |
12 import os, urllib, urllib2, urlparse, zlib, httplib | |
13 import errno, socket | |
14 | |
15 def zgenerator(f): | |
16 zd = zlib.decompressobj() | |
17 try: | |
18 for chunk in util.filechunkiter(f): | |
19 yield zd.decompress(chunk) | |
20 except httplib.HTTPException: | |
21 raise IOError(None, _('connection ended unexpectedly')) | |
22 yield zd.flush() | |
23 | |
24 class httprepository(repo.repository): | |
25 def __init__(self, ui, path): | |
26 self.path = path | |
27 self.caps = None | |
28 self.handler = None | |
29 scheme, netloc, urlpath, query, frag = urlparse.urlsplit(path) | |
30 if query or frag: | |
31 raise util.Abort(_('unsupported URL component: "%s"') % | |
32 (query or frag)) | |
33 | |
34 # urllib cannot handle URLs with embedded user or passwd | |
35 self._url, authinfo = url.getauthinfo(path) | |
36 | |
37 self.ui = ui | |
38 self.ui.debug(_('using %s\n') % self._url) | |
39 | |
40 self.urlopener = url.opener(ui, authinfo) | |
41 | |
42 def __del__(self): | |
43 for h in self.urlopener.handlers: | |
44 h.close() | |
45 if hasattr(h, "close_all"): | |
46 h.close_all() | |
47 | |
48 def url(self): | |
49 return self.path | |
50 | |
51 # look up capabilities only when needed | |
52 | |
53 def get_caps(self): | |
54 if self.caps is None: | |
55 try: | |
56 self.caps = set(self.do_read('capabilities').split()) | |
57 except error.RepoError: | |
58 self.caps = set() | |
59 self.ui.debug(_('capabilities: %s\n') % | |
60 (' '.join(self.caps or ['none']))) | |
61 return self.caps | |
62 | |
63 capabilities = property(get_caps) | |
64 | |
65 def lock(self): | |
66 raise util.Abort(_('operation not supported over http')) | |
67 | |
68 def do_cmd(self, cmd, **args): | |
69 data = args.pop('data', None) | |
70 headers = args.pop('headers', {}) | |
71 self.ui.debug(_("sending %s command\n") % cmd) | |
72 q = {"cmd": cmd} | |
73 q.update(args) | |
74 qs = '?%s' % urllib.urlencode(q) | |
75 cu = "%s%s" % (self._url, qs) | |
76 try: | |
77 if data: | |
78 self.ui.debug(_("sending %s bytes\n") % len(data)) | |
79 resp = self.urlopener.open(urllib2.Request(cu, data, headers)) | |
80 except urllib2.HTTPError, inst: | |
81 if inst.code == 401: | |
82 raise util.Abort(_('authorization failed')) | |
83 raise | |
84 except httplib.HTTPException, inst: | |
85 self.ui.debug(_('http error while sending %s command\n') % cmd) | |
86 self.ui.traceback() | |
87 raise IOError(None, inst) | |
88 except IndexError: | |
89 # this only happens with Python 2.3, later versions raise URLError | |
90 raise util.Abort(_('http error, possibly caused by proxy setting')) | |
91 # record the url we got redirected to | |
92 resp_url = resp.geturl() | |
93 if resp_url.endswith(qs): | |
94 resp_url = resp_url[:-len(qs)] | |
95 if self._url != resp_url: | |
96 self.ui.status(_('real URL is %s\n') % resp_url) | |
97 self._url = resp_url | |
98 try: | |
99 proto = resp.getheader('content-type') | |
100 except AttributeError: | |
101 proto = resp.headers['content-type'] | |
102 | |
103 safeurl = url.hidepassword(self._url) | |
104 # accept old "text/plain" and "application/hg-changegroup" for now | |
105 if not (proto.startswith('application/mercurial-') or | |
106 proto.startswith('text/plain') or | |
107 proto.startswith('application/hg-changegroup')): | |
108 self.ui.debug(_("requested URL: '%s'\n") % url.hidepassword(cu)) | |
109 raise error.RepoError(_("'%s' does not appear to be an hg repository") | |
110 % safeurl) | |
111 | |
112 if proto.startswith('application/mercurial-'): | |
113 try: | |
114 version = proto.split('-', 1)[1] | |
115 version_info = tuple([int(n) for n in version.split('.')]) | |
116 except ValueError: | |
117 raise error.RepoError(_("'%s' sent a broken Content-Type " | |
118 "header (%s)") % (safeurl, proto)) | |
119 if version_info > (0, 1): | |
120 raise error.RepoError(_("'%s' uses newer protocol %s") % | |
121 (safeurl, version)) | |
122 | |
123 return resp | |
124 | |
125 def do_read(self, cmd, **args): | |
126 fp = self.do_cmd(cmd, **args) | |
127 try: | |
128 return fp.read() | |
129 finally: | |
130 # if using keepalive, allow connection to be reused | |
131 fp.close() | |
132 | |
133 def lookup(self, key): | |
134 self.requirecap('lookup', _('look up remote revision')) | |
135 d = self.do_cmd("lookup", key = key).read() | |
136 success, data = d[:-1].split(' ', 1) | |
137 if int(success): | |
138 return bin(data) | |
139 raise error.RepoError(data) | |
140 | |
141 def heads(self): | |
142 d = self.do_read("heads") | |
143 try: | |
144 return map(bin, d[:-1].split(" ")) | |
145 except: | |
146 raise error.ResponseError(_("unexpected response:"), d) | |
147 | |
148 def branchmap(self): | |
149 d = self.do_read("branchmap") | |
150 try: | |
151 branchmap = {} | |
152 for branchpart in d.splitlines(): | |
153 branchheads = branchpart.split(' ') | |
154 branchname = urllib.unquote(branchheads[0]) | |
155 branchheads = [bin(x) for x in branchheads[1:]] | |
156 branchmap[branchname] = branchheads | |
157 return branchmap | |
158 except: | |
159 raise error.ResponseError(_("unexpected response:"), d) | |
160 | |
161 def branches(self, nodes): | |
162 n = " ".join(map(hex, nodes)) | |
163 d = self.do_read("branches", nodes=n) | |
164 try: | |
165 br = [ tuple(map(bin, b.split(" "))) for b in d.splitlines() ] | |
166 return br | |
167 except: | |
168 raise error.ResponseError(_("unexpected response:"), d) | |
169 | |
170 def between(self, pairs): | |
171 batch = 8 # avoid giant requests | |
172 r = [] | |
173 for i in xrange(0, len(pairs), batch): | |
174 n = " ".join(["-".join(map(hex, p)) for p in pairs[i:i + batch]]) | |
175 d = self.do_read("between", pairs=n) | |
176 try: | |
177 r += [ l and map(bin, l.split(" ")) or [] for l in d.splitlines() ] | |
178 except: | |
179 raise error.ResponseError(_("unexpected response:"), d) | |
180 return r | |
181 | |
182 def changegroup(self, nodes, kind): | |
183 n = " ".join(map(hex, nodes)) | |
184 f = self.do_cmd("changegroup", roots=n) | |
185 return util.chunkbuffer(zgenerator(f)) | |
186 | |
187 def changegroupsubset(self, bases, heads, source): | |
188 self.requirecap('changegroupsubset', _('look up remote changes')) | |
189 baselst = " ".join([hex(n) for n in bases]) | |
190 headlst = " ".join([hex(n) for n in heads]) | |
191 f = self.do_cmd("changegroupsubset", bases=baselst, heads=headlst) | |
192 return util.chunkbuffer(zgenerator(f)) | |
193 | |
194 def unbundle(self, cg, heads, source): | |
195 # have to stream bundle to a temp file because we do not have | |
196 # http 1.1 chunked transfer. | |
197 | |
198 type = "" | |
199 types = self.capable('unbundle') | |
200 # servers older than d1b16a746db6 will send 'unbundle' as a | |
201 # boolean capability | |
202 try: | |
203 types = types.split(',') | |
204 except AttributeError: | |
205 types = [""] | |
206 if types: | |
207 for x in types: | |
208 if x in changegroup.bundletypes: | |
209 type = x | |
210 break | |
211 | |
212 tempname = changegroup.writebundle(cg, None, type) | |
213 fp = url.httpsendfile(tempname, "rb") | |
214 try: | |
215 try: | |
216 resp = self.do_read( | |
217 'unbundle', data=fp, | |
218 headers={'Content-Type': 'application/octet-stream'}, | |
219 heads=' '.join(map(hex, heads))) | |
220 resp_code, output = resp.split('\n', 1) | |
221 try: | |
222 ret = int(resp_code) | |
223 except ValueError, err: | |
224 raise error.ResponseError( | |
225 _('push failed (unexpected response):'), resp) | |
226 self.ui.write(output) | |
227 return ret | |
228 except socket.error, err: | |
229 if err[0] in (errno.ECONNRESET, errno.EPIPE): | |
230 raise util.Abort(_('push failed: %s') % err[1]) | |
231 raise util.Abort(err[1]) | |
232 finally: | |
233 fp.close() | |
234 os.unlink(tempname) | |
235 | |
236 def stream_out(self): | |
237 return self.do_cmd('stream_out') | |
238 | |
239 class httpsrepository(httprepository): | |
240 def __init__(self, ui, path): | |
241 if not url.has_https: | |
242 raise util.Abort(_('Python support for SSL and HTTPS ' | |
243 'is not installed')) | |
244 httprepository.__init__(self, ui, path) | |
245 | |
246 def instance(ui, path, create): | |
247 if create: | |
248 raise util.Abort(_('cannot create new http repository')) | |
249 try: | |
250 if path.startswith('https:'): | |
251 inst = httpsrepository(ui, path) | |
252 else: | |
253 inst = httprepository(ui, path) | |
254 inst.between([(nullid, nullid)]) | |
255 return inst | |
256 except error.RepoError: | |
257 ui.note('(falling back to static-http)\n') | |
258 return statichttprepo.instance(ui, "static-" + path, create) |