Ticket #1425: blacklist5.darcs.patch

File blacklist5.darcs.patch, 69.6 KB (added by davidsarah, at 2011-08-24T17:12:56Z)

Implementation, tests and docs for blacklists. This version allows listing directories containing a blacklisted child. Inclusion of blacklist.py fixed. fixes #1425

Line 
11 patch for repository davidsarah@dev.allmydata.org:/home/darcs/tahoe/trunk:
2
3Wed Aug 24 16:59:28 BST 2011  david-sarah@jacaranda.org
4  * Implementation, tests and docs for blacklists. This version allows listing directories containing a blacklisted child. Inclusion of blacklist.py fixed. fixes #1425
5
6New patches:
7
8[Implementation, tests and docs for blacklists. This version allows listing directories containing a blacklisted child. Inclusion of blacklist.py fixed. fixes #1425
9david-sarah@jacaranda.org**20110824155928
10 Ignore-this: a306f36bb6640eaf046e66dc4beeb11c
11] {
12hunk ./docs/configuration.rst 609
13   "which peers am I connected to" list), and the shortened form (the first
14   few characters) is recorded in various log messages.
15 
16+``access.blacklist``
17+
18+  Gateway nodes may find it necessary to prohibit access to certain files. The
19+  web-API has a facility to block access to filecaps by their storage index,
20+  returning a 403 "Forbidden" error instead of the original file. For more
21+  details, see the "Access Blacklist" section of `<frontends/webapi.rst>`_.
22+
23 
24 Example
25 =======
26hunk ./docs/frontends/webapi.rst 39
27 8.  `Static Files in /public_html`_
28 9.  `Safety and Security Issues -- Names vs. URIs`_
29 10. `Concurrency Issues`_
30+11. `Access Blacklist`_
31 
32 
33 Enabling the web-API port
34hunk ./docs/frontends/webapi.rst 1959
35 Coordination Directive" sections of `mutable.rst <../specifications/mutable.rst>`_.
36 
37 
38+Access Blacklist
39+================
40+
41+Gateway nodes may find it necessary to prohibit access to certain files. The
42+web-API has a facility to block access to filecaps by their storage index,
43+returning a 403 "Forbidden" error instead of the original file.
44+
45+This blacklist is recorded in $NODEDIR/access.blacklist, and contains one
46+blocked file per line. Comment lines (starting with ``#``) are ignored. Each
47+line consists of the storage-index (in the usual base32 format as displayed
48+by the "More Info" page, or by the "tahoe debug dump-cap" command), followed
49+by whitespace, followed by a reason string, which will be included in the 403
50+error message. This could hold a URL to a page that explains why the file is
51+blocked, for example.
52+
53+So for example, if you found a need to block access to a file with filecap
54+``URI:CHK:n7r3m6wmomelk4sep3kw5cvduq:os7ijw5c3maek7pg65e5254k2fzjflavtpejjyhshpsxuqzhcwwq:3:20:14861``,
55+you could do the following::
56+
57+ tahoe debug dump-cap URI:CHK:n7r3m6wmomelk4sep3kw5cvduq:os7ijw5c3maek7pg65e5254k2fzjflavtpejjyhshpsxuqzhcwwq:3:20:14861
58+ -> storage index: whpepioyrnff7orecjolvbudeu
59+ echo "whpepioyrnff7orecjolvbudeu my puppy told me to" >>$NODEDIR/access.blacklist
60+ tahoe restart $NODEDIR
61+ tahoe get URI:CHK:n7r3m6wmomelk4sep3kw5cvduq:os7ijw5c3maek7pg65e5254k2fzjflavtpejjyhshpsxuqzhcwwq:3:20:14861
62+ -> error, 403 Access Prohibited: my puppy told me to
63+
64+The ``access.blacklist`` file will be checked each time a file or directory
65+is accessed: the file's ``mtime`` is used to decide whether it need to be
66+reloaded. Therefore no node restart is necessary when creating the initial
67+blacklist, nor when adding second, third, or additional entries to the list.
68+When modifying the file, be careful to update it atomically, otherwise a
69+request may arrive while the file is only halfway written, and the partial
70+file may be incorrectly parsed.
71+
72+The blacklist is applied to all access paths (including FTP, SFTP, and CLI
73+operations), not just the web-API. The blacklist also applies to directories.
74+If a directory is blacklisted, the gateway will refuse access to both that
75+directory and any child files/directories underneath it, when accessed via
76+"DIRCAP/SUBDIR/FILENAME" -style URLs. Users who go directly to the child
77+file/dir will bypass the blacklist.
78+
79+The node will log the SI of the file being blocked, and the reason code, into
80+the ``logs/twistd.log`` file.
81+
82+
83 .. [1] URLs and HTTP and UTF-8, Oh My
84 
85  HTTP does not provide a mechanism to specify the character set used to
86addfile ./src/allmydata/blacklist.py
87hunk ./src/allmydata/blacklist.py 1
88+
89+import os
90+
91+from zope.interface import implements
92+from twisted.internet import defer
93+from twisted.python import log as twisted_log
94+
95+from allmydata.interfaces import IFileNode, IFilesystemNode
96+from allmydata.util import base32
97+from allmydata.util.encodingutil import quote_output
98+
99+
100+class FileProhibited(Exception):
101+    """This client has been configured to prohibit access to this object."""
102+    def __init__(self, reason):
103+        Exception.__init__(self, "Access Prohibited: %s" % quote_output(reason, encoding='utf-8', quotemarks=False))
104+        self.reason = reason
105+
106+
107+class Blacklist:
108+    def __init__(self, blacklist_fn):
109+        self.blacklist_fn = blacklist_fn
110+        self.last_mtime = None
111+        self.entries = {}
112+        self.read_blacklist() # sets .last_mtime and .entries
113+
114+    def read_blacklist(self):
115+        try:
116+            current_mtime = os.stat(self.blacklist_fn).st_mtime
117+        except EnvironmentError:
118+            # unreadable blacklist file means no blacklist
119+            self.entries.clear()
120+            return
121+        try:
122+            if self.last_mtime is None or current_mtime > self.last_mtime:
123+                self.entries.clear()
124+                for line in open(self.blacklist_fn, "r").readlines():
125+                    line = line.strip()
126+                    if not line or line.startswith("#"):
127+                        continue
128+                    si_s, reason = line.split(None, 1)
129+                    si = base32.a2b(si_s) # must be valid base32
130+                    self.entries[si] = reason
131+                self.last_mtime = current_mtime
132+        except Exception, e:
133+            twisted_log.err(e, "unparseable blacklist file")
134+            raise
135+
136+    def check_storageindex(self, si):
137+        self.read_blacklist()
138+        reason = self.entries.get(si, None)
139+        if reason is not None:
140+            # log this to logs/twistd.log, since web logs go there too
141+            twisted_log.msg("blacklist prohibited access to SI %s: %s" %
142+                            (base32.b2a(si), reason))
143+        return reason
144+
145+
146+class ProhibitedNode:
147+    implements(IFileNode)
148+
149+    def __init__(self, wrapped_node, reason):
150+        assert IFilesystemNode.providedBy(wrapped_node), wrapped_node
151+        self.wrapped_node = wrapped_node
152+        self.reason = reason
153+
154+    def get_cap(self):
155+        return self.wrapped_node.get_cap()
156+
157+    def get_readcap(self):
158+        return self.wrapped_node.get_readcap()
159+
160+    def is_readonly(self):
161+        return self.wrapped_node.is_readonly()
162+
163+    def is_mutable(self):
164+        return self.wrapped_node.is_mutable()
165+
166+    def is_unknown(self):
167+        return self.wrapped_node.is_unknown()
168+
169+    def is_allowed_in_immutable_directory(self):
170+        return self.wrapped_node.is_allowed_in_immutable_directory()
171+
172+    def is_alleged_immutable(self):
173+        return self.wrapped_node.is_alleged_immutable()
174+
175+    def raise_error(self):
176+        # We don't raise an exception here because that would prevent the node from being listed.
177+        pass
178+
179+    def get_uri(self):
180+        return self.wrapped_node.get_uri()
181+
182+    def get_write_uri(self):
183+        return self.wrapped_node.get_write_uri()
184+
185+    def get_readonly_uri(self):
186+        return self.wrapped_node.get_readonly_uri()
187+
188+    def get_storage_index(self):
189+        return self.wrapped_node.get_storage_index()
190+
191+    def get_verify_cap(self):
192+        return self.wrapped_node.get_verify_cap()
193+
194+    def get_repair_cap(self):
195+        return self.wrapped_node.get_repair_cap()
196+
197+    def get_size(self):
198+        return None
199+
200+    def get_current_size(self):
201+        return defer.succeed(None)
202+
203+    def get_size_of_best_version(self):
204+        return defer.succeed(None)
205+
206+    def check(self, monitor, verify, add_lease):
207+        return defer.succeed(None)
208+
209+    def check_and_repair(self, monitor, verify, add_lease):
210+        return defer.succeed(None)
211+
212+    def get_version(self):
213+        return None
214+
215+    # Omitting any of these methods would fail safe; they are just to ensure correct error reporting.
216+
217+    def get_best_readable_version(self):
218+        raise FileProhibited(self.reason)
219+
220+    def download_best_version(self):
221+        raise FileProhibited(self.reason)
222+
223+    def get_best_mutable_version(self):
224+        raise FileProhibited(self.reason)
225+
226+    def overwrite(self, new_contents):
227+        raise FileProhibited(self.reason)
228+
229+    def modify(self, modifier_cb):
230+        raise FileProhibited(self.reason)
231+
232+    def get_servermap(self, mode):
233+        raise FileProhibited(self.reason)
234+
235+    def download_version(self, servermap, version):
236+        raise FileProhibited(self.reason)
237+
238+    def upload(self, new_contents, servermap):
239+        raise FileProhibited(self.reason)
240+
241+    def get_writekey(self):
242+        raise FileProhibited(self.reason)
243hunk ./src/allmydata/client.py 28
244 from allmydata.interfaces import IStatsProducer, RIStubClient, \
245                                  SDMF_VERSION, MDMF_VERSION
246 from allmydata.nodemaker import NodeMaker
247+from allmydata.blacklist import Blacklist
248 
249 
250 KiB=1024
251hunk ./src/allmydata/client.py 283
252         self.terminator.setServiceParent(self)
253         self.add_service(Uploader(helper_furl, self.stats_provider))
254         self.init_stub_client()
255+        self.init_blacklist()
256         self.init_nodemaker()
257 
258     def init_client_storage_broker(self):
259hunk ./src/allmydata/client.py 336
260         d.addErrback(log.err, facility="tahoe.init",
261                      level=log.BAD, umid="OEHq3g")
262 
263+    def init_blacklist(self):
264+        fn = os.path.join(self.basedir, "access.blacklist")
265+        self.blacklist = Blacklist(fn)
266+
267     def init_nodemaker(self):
268         self.nodemaker = NodeMaker(self.storage_broker,
269                                    self._secret_holder,
270hunk ./src/allmydata/client.py 347
271                                    self.getServiceNamed("uploader"),
272                                    self.terminator,
273                                    self.get_encoding_parameters(),
274-                                   self._key_generator)
275+                                   self._key_generator,
276+                                   self.blacklist)
277         default = self.get_config("client", "mutable.format", default="sdmf")
278         if default == "mdmf":
279             self.mutable_file_default = MDMF_VERSION
280hunk ./src/allmydata/interfaces.py 787
281         read-only access with others, use get_readonly_uri().
282         """
283 
284-    def get_write_uri(n):
285+    def get_write_uri():
286         """Return the URI string that can be used by others to get write
287         access to this node, if it is writeable. If this is a read-only node,
288         return None."""
289hunk ./src/allmydata/nodemaker.py 12
290 from allmydata.mutable.publish import MutableData
291 from allmydata.dirnode import DirectoryNode, pack_children
292 from allmydata.unknown import UnknownNode
293+from allmydata.blacklist import ProhibitedNode
294 from allmydata import uri
295 
296hunk ./src/allmydata/nodemaker.py 15
297+
298 class NodeMaker:
299     implements(INodeMaker)
300 
301hunk ./src/allmydata/nodemaker.py 21
302     def __init__(self, storage_broker, secret_holder, history,
303                  uploader, terminator,
304-                 default_encoding_parameters, key_generator):
305+                 default_encoding_parameters, key_generator,
306+                 blacklist=None):
307         self.storage_broker = storage_broker
308         self.secret_holder = secret_holder
309         self.history = history
310hunk ./src/allmydata/nodemaker.py 30
311         self.terminator = terminator
312         self.default_encoding_parameters = default_encoding_parameters
313         self.key_generator = key_generator
314+        self.blacklist = blacklist
315 
316         self._node_cache = weakref.WeakValueDictionary() # uri -> node
317 
318hunk ./src/allmydata/nodemaker.py 69
319         else:
320             memokey = "M" + bigcap
321         if memokey in self._node_cache:
322-            return self._node_cache[memokey]
323-        cap = uri.from_string(bigcap, deep_immutable=deep_immutable, name=name)
324-        node = self._create_from_single_cap(cap)
325-        if node:
326-            self._node_cache[memokey] = node  # note: WeakValueDictionary
327+            node = self._node_cache[memokey]
328         else:
329hunk ./src/allmydata/nodemaker.py 71
330-            # don't cache UnknownNode
331-            node = UnknownNode(writecap, readcap, deep_immutable=deep_immutable, name=name)
332+            cap = uri.from_string(bigcap, deep_immutable=deep_immutable,
333+                                  name=name)
334+            node = self._create_from_single_cap(cap)
335+            if node:
336+                self._node_cache[memokey] = node  # note: WeakValueDictionary
337+            else:
338+                # don't cache UnknownNode
339+                node = UnknownNode(writecap, readcap,
340+                                   deep_immutable=deep_immutable, name=name)
341+
342+        if self.blacklist:
343+            si = node.get_storage_index()
344+            # if this node is blacklisted, return the reason, otherwise return None
345+            reason = self.blacklist.check_storageindex(si)
346+            if reason is not None:
347+                # The original node object is cached above, not the ProhibitedNode wrapper.
348+                # This ensures that removing the blacklist entry will make the node
349+                # accessible if create_from_cap is called again.
350+                node = ProhibitedNode(node, reason)
351         return node
352 
353     def _create_from_single_cap(self, cap):
354hunk ./src/allmydata/test/test_web.py 173
355         self.history = FakeHistory()
356         self.uploader = FakeUploader()
357         self.uploader.setServiceParent(self)
358+        self.blacklist = None
359         self.nodemaker = FakeNodeMaker(None, self._secret_holder, None,
360                                        self.uploader, None,
361                                        None, None)
362hunk ./src/allmydata/test/test_web.py 5266
363 
364         return d
365 
366+    def test_blacklist(self):
367+        # download from a blacklisted URI, get an error
368+        self.basedir = "web/Grid/blacklist"
369+        self.set_up_grid()
370+        c0 = self.g.clients[0]
371+        c0_basedir = c0.basedir
372+        fn = os.path.join(c0_basedir, "access.blacklist")
373+        self.uris = {}
374+        DATA = "off-limits " * 50
375+
376+        d = c0.upload(upload.Data(DATA, convergence=""))
377+        def _stash_uri_and_create_dir(ur):
378+            self.uri = ur.uri
379+            self.url = "uri/"+self.uri
380+            u = uri.from_string_filenode(self.uri)
381+            self.si = u.get_storage_index()
382+            childnode = c0.create_node_from_uri(self.uri, None)
383+            return c0.create_dirnode({u"blacklisted.txt": (childnode,{}) })
384+        d.addCallback(_stash_uri_and_create_dir)
385+        def _stash_dir(node):
386+            self.dir_node = node
387+            self.dir_uri = node.get_uri()
388+            self.dir_url = "uri/"+self.dir_uri
389+        d.addCallback(_stash_dir)
390+        d.addCallback(lambda ign: self.GET(self.dir_url, followRedirect=True))
391+        def _check_dir_html(body):
392+            self.failUnlessIn("<html>", body)
393+            self.failUnlessIn("blacklisted.txt</a>", body)
394+        d.addCallback(_check_dir_html)
395+        d.addCallback(lambda ign: self.GET(self.url))
396+        d.addCallback(lambda body: self.failUnlessEqual(DATA, body))
397+
398+        def _blacklist(ign):
399+            f = open(fn, "w")
400+            f.write(" # this is a comment\n")
401+            f.write(" \n")
402+            f.write("\n") # also exercise blank lines
403+            f.write("%s %s\n" % (base32.b2a(self.si), "off-limits to you"))
404+            f.close()
405+            # clients should be checking the blacklist each time, so we don't
406+            # need to restart the client
407+        d.addCallback(_blacklist)
408+        d.addCallback(lambda ign: self.shouldHTTPError("get_from_blacklisted_uri",
409+                                                       403, "Forbidden",
410+                                                       "Access Prohibited: off-limits",
411+                                                       self.GET, self.url))
412+
413+        # We should still be able to list the parent directory, in HTML...
414+        d.addCallback(lambda ign: self.GET(self.dir_url, followRedirect=True))
415+        def _check_dir_html2(body):
416+            self.failUnlessIn("<html>", body)
417+            self.failUnlessIn("blacklisted.txt</strike>", body)
418+        d.addCallback(_check_dir_html2)
419+
420+        # ... and in JSON (used by CLI).
421+        d.addCallback(lambda ign: self.GET(self.dir_url+"?t=json", followRedirect=True))
422+        def _check_dir_json(res):
423+            data = simplejson.loads(res)
424+            self.failUnless(isinstance(data, list), data)
425+            self.failUnlessEqual(data[0], "dirnode")
426+            self.failUnless(isinstance(data[1], dict), data)
427+            self.failUnlessIn("children", data[1])
428+            self.failUnlessIn("blacklisted.txt", data[1]["children"])
429+            childdata = data[1]["children"]["blacklisted.txt"]
430+            self.failUnless(isinstance(childdata, list), data)
431+            self.failUnlessEqual(childdata[0], "filenode")
432+            self.failUnless(isinstance(childdata[1], dict), data)
433+        d.addCallback(_check_dir_json)
434+
435+        def _unblacklist(ign):
436+            open(fn, "w").close()
437+            # the Blacklist object watches mtime to tell when the file has
438+            # changed, but on windows this test will run faster than the
439+            # filesystem's mtime resolution. So we edit Blacklist.last_mtime
440+            # to force a reload.
441+            self.g.clients[0].blacklist.last_mtime -= 2.0
442+        d.addCallback(_unblacklist)
443+
444+        # now a read should work
445+        d.addCallback(lambda ign: self.GET(self.url))
446+        d.addCallback(lambda body: self.failUnlessEqual(DATA, body))
447+
448+        # read again to exercise the blacklist-is-unchanged logic
449+        d.addCallback(lambda ign: self.GET(self.url))
450+        d.addCallback(lambda body: self.failUnlessEqual(DATA, body))
451+
452+        # now add a blacklisted directory, and make sure files under it are
453+        # refused too
454+        def _add_dir(ign):
455+            childnode = c0.create_node_from_uri(self.uri, None)
456+            return c0.create_dirnode({u"child": (childnode,{}) })
457+        d.addCallback(_add_dir)
458+        def _get_dircap(dn):
459+            self.dir_si_b32 = base32.b2a(dn.get_storage_index())
460+            self.dir_url_base = "uri/"+dn.get_write_uri()
461+            self.dir_url_json1 = "uri/"+dn.get_write_uri()+"?t=json"
462+            self.dir_url_json2 = "uri/"+dn.get_write_uri()+"/?t=json"
463+            self.dir_url_json_ro = "uri/"+dn.get_readonly_uri()+"/?t=json"
464+            self.child_url = "uri/"+dn.get_readonly_uri()+"/child"
465+        d.addCallback(_get_dircap)
466+        d.addCallback(lambda ign: self.GET(self.dir_url_base, followRedirect=True))
467+        d.addCallback(lambda body: self.failUnlessIn("<html>", body))
468+        d.addCallback(lambda ign: self.GET(self.dir_url_json1))
469+        d.addCallback(lambda res: simplejson.loads(res))  # just check it decodes
470+        d.addCallback(lambda ign: self.GET(self.dir_url_json2))
471+        d.addCallback(lambda res: simplejson.loads(res))  # just check it decodes
472+        d.addCallback(lambda ign: self.GET(self.dir_url_json_ro))
473+        d.addCallback(lambda res: simplejson.loads(res))  # just check it decodes
474+        d.addCallback(lambda ign: self.GET(self.child_url))
475+        d.addCallback(lambda body: self.failUnlessEqual(DATA, body))
476+
477+        def _block_dir(ign):
478+            f = open(fn, "w")
479+            f.write("%s %s\n" % (self.dir_si_b32, "dir-off-limits to you"))
480+            f.close()
481+            self.g.clients[0].blacklist.last_mtime -= 2.0
482+        d.addCallback(_block_dir)
483+        d.addCallback(lambda ign: self.shouldHTTPError("get_from_blacklisted_dir base",
484+                                                       403, "Forbidden",
485+                                                       "Access Prohibited: dir-off-limits",
486+                                                       self.GET, self.dir_url_base))
487+        d.addCallback(lambda ign: self.shouldHTTPError("get_from_blacklisted_dir json1",
488+                                                       403, "Forbidden",
489+                                                       "Access Prohibited: dir-off-limits",
490+                                                       self.GET, self.dir_url_json1))
491+        d.addCallback(lambda ign: self.shouldHTTPError("get_from_blacklisted_dir json2",
492+                                                       403, "Forbidden",
493+                                                       "Access Prohibited: dir-off-limits",
494+                                                       self.GET, self.dir_url_json2))
495+        d.addCallback(lambda ign: self.shouldHTTPError("get_from_blacklisted_dir json_ro",
496+                                                       403, "Forbidden",
497+                                                       "Access Prohibited: dir-off-limits",
498+                                                       self.GET, self.dir_url_json_ro))
499+        d.addCallback(lambda ign: self.shouldHTTPError("get_from_blacklisted_dir child",
500+                                                       403, "Forbidden",
501+                                                       "Access Prohibited: dir-off-limits",
502+                                                       self.GET, self.child_url))
503+        return d
504+
505+
506 class CompletelyUnhandledError(Exception):
507     pass
508 class ErrorBoom(rend.Page):
509hunk ./src/allmydata/web/common.py 9
510 from nevow import loaders, appserver
511 from nevow.inevow import IRequest
512 from nevow.util import resource_filename
513+from allmydata import blacklist
514 from allmydata.interfaces import ExistingChildError, NoSuchChildError, \
515      FileTooLargeError, NotEnoughSharesError, NoSharesError, \
516      EmptyPathnameComponentError, MustBeDeepImmutableError, \
517hunk ./src/allmydata/web/common.py 261
518              "The cap is being passed in a read slot (ro_uri), or was retrieved "
519              "from a read slot as an unknown cap.") % quoted_name
520         return (t, http.BAD_REQUEST)
521+    if f.check(blacklist.FileProhibited):
522+        t = "Access Prohibited: %s" % quote_output(f.value.reason, encoding="utf-8", quotemarks=False)
523+        return (t, http.FORBIDDEN)
524     if f.check(WebError):
525         return (f.value.text, f.value.code)
526     if f.check(FileTooLargeError):
527hunk ./src/allmydata/web/directory.py 20
528 from allmydata.interfaces import IDirectoryNode, IFileNode, IFilesystemNode, \
529      IImmutableFileNode, IMutableFileNode, ExistingChildError, \
530      NoSuchChildError, EmptyPathnameComponentError, SDMF_VERSION, MDMF_VERSION
531+from allmydata.blacklist import ProhibitedNode
532 from allmydata.monitor import Monitor, OperationCancelledError
533 from allmydata import dirnode
534 from allmydata.web.common import text_plain, WebError, \
535hunk ./src/allmydata/web/directory.py 768
536             ctx.fillSlots("size", "-")
537             info_link = "%s/uri/%s/?t=info" % (root, quoted_uri)
538 
539+        elif isinstance(target, ProhibitedNode):
540+            ctx.fillSlots("filename", T.strike[name])
541+            if IDirectoryNode.providedBy(target.wrapped_node):
542+                blacklisted_type = "DIR-BLACKLISTED"
543+            else:
544+                blacklisted_type = "BLACKLISTED"
545+            ctx.fillSlots("type", blacklisted_type)
546+            ctx.fillSlots("size", "-")
547+            info_link = None
548+            ctx.fillSlots("info", ["Access Prohibited:", T.br, target.reason])
549+
550         else:
551             # unknown
552             ctx.fillSlots("filename", html.escape(name))
553hunk ./src/allmydata/web/directory.py 794
554             # writecap and the readcap
555             info_link = "%s?t=info" % urllib.quote(name)
556 
557-        ctx.fillSlots("info", T.a(href=info_link)["More Info"])
558+        if info_link:
559+            ctx.fillSlots("info", T.a(href=info_link)["More Info"])
560 
561         return ctx.tag
562 
563hunk ./src/allmydata/web/filenode.py 15
564 from allmydata.mutable.publish import MutableFileHandle
565 from allmydata.mutable.common import MODE_READ
566 from allmydata.util import log, base32
567+from allmydata.util.encodingutil import quote_output
568+from allmydata.blacklist import FileProhibited, ProhibitedNode
569 
570 from allmydata.web.common import text_plain, WebError, RenderMixin, \
571      boolean_of_arg, get_arg, should_create_intermediate_directories, \
572hunk ./src/allmydata/web/filenode.py 157
573 
574     def childFactory(self, ctx, name):
575         req = IRequest(ctx)
576+        if isinstance(self.node, ProhibitedNode):
577+            raise FileProhibited(self.node.reason)
578         if should_create_intermediate_directories(req):
579hunk ./src/allmydata/web/filenode.py 160
580-            raise WebError("Cannot create directory '%s', because its "
581-                           "parent is a file, not a directory" % name)
582-        raise WebError("Files have no children, certainly not named '%s'"
583-                       % name)
584+            raise WebError("Cannot create directory %s, because its "
585+                           "parent is a file, not a directory" % quote_output(name, encoding='utf-8'))
586+        raise WebError("Files have no children, certainly not named %s"
587+                       % quote_output(name, encoding='utf-8'))
588 
589     def render_GET(self, ctx):
590         req = IRequest(ctx)
591hunk ./src/allmydata/webish.py 132
592     name = "webish"
593 
594     def __init__(self, client, webport, nodeurl_path=None, staticdir=None,
595-                                        clock=None):
596+                 clock=None):
597         service.MultiService.__init__(self)
598         # the 'data' argument to all render() methods default to the Client
599         # the 'clock' argument to root.Root is, if set, a
600}
601
602Context:
603
604[mutable/layout.py: fix unused import. refs #393
605david-sarah@jacaranda.org**20110816225043
606 Ignore-this: 7c9d6d91521ceb9a7abd14b2c60c0604
607]
608[mutable/retrieve.py: cosmetics and remove a stale comment. refs #393
609david-sarah@jacaranda.org**20110816214612
610 Ignore-this: 916e60c9dff1ef85595822e609ff34b7
611]
612[mutable/filenode.py: don't fetch more segments than necesasry to update the file
613Kevan Carstensen <kevan@isnotajoke.com>**20110813210005
614 Ignore-this: 2b0ad0533baa6f19f18851317dfc9f15
615]
616[test/test_mutable: test for incorrect div_ceil equations
617Kevan Carstensen <kevan@isnotajoke.com>**20110813183936
618 Ignore-this: 74e6061ab2ec5e706a1235611f87d5d6
619]
620[mutable/retrieve.py: use floor division to calculate segment boundaries, don't fetch more segments than necessary
621Kevan Carstensen <kevan@isnotajoke.com>**20110813183833
622 Ignore-this: 3e272249107afd3fbc1dd30c6a4f1e31
623]
624[mdmf: clean up boolean expressions, correct typos, remove self._paused, and don't unconditionally initialize block hash trees, asll as suggested by davidsarahs' review comments
625Kevan Carstensen <kevan@isnotajoke.com>**20110813183710
626 Ignore-this: cc6ad9f98b64f379151aa58b77b6c4e5
627]
628[now that tests pass with full-size keys, return test-keys to normal (522bit)
629warner@lothar.com**20110811175418
630 Ignore-this: dbce8a6699ba9a90d91cffbc8aa87900
631]
632[fix SHARE_HASH_CHAIN_SIZE computation
633warner@lothar.com**20110811175350
634 Ignore-this: 4508359d2207c8c1b7552b546697264
635]
636[More idiomatic resolution of the conflict between ticket393-MDMF-2 and trunk. refs #393
637david-sarah@jacaranda.org**20110810202942
638 Ignore-this: 7fc54a30ab0bc6ce75b7d819800c1182
639]
640[Replace the hard-coded 522-bit RSA key size used for tests with a TEST_RSA_KEY_SIZE constant defined in test/common.py (part 2). refs #393
641david-sarah@jacaranda.org**20110810202310
642 Ignore-this: 7fbd4d004279599bbcb10f7b31fb010f
643]
644[Replace the hard-coded 522-bit RSA key size used for tests with a TEST_RSA_KEY_SIZE constant defined in test/common.py (part 1). refs #393
645david-sarah@jacaranda.org**20110810202243
646 Ignore-this: c58d8130a2f383ff4421c632499b027b
647]
648[merge some minor conflicts in test code from the 393-2 branch and trunk
649zooko@zooko.com**20110810172139
650 Ignore-this: 4a16f13eeae585c7c1dbe18c67072c90
651]
652[doc: eliminate the phrase "rootcap" from doc/frontends/FTP-and-SFTP.rst
653zooko@zooko.com**20110809132601
654 Ignore-this: f7e1dd212daa65c81fb57977bce24304
655 Two different people have asked me for help, saying they couldn't figure out what a "rootcap" is. Hopefully just calling it a "cap" will make it easier for them to find out from the other docs what it is.
656]
657[test_web.py: fix a test failure dependent on whether simplejson.loads returns a unicode or str object.
658david-sarah@jacaranda.org**20110808213925
659 Ignore-this: f7b267be8be56fcabc968e3c89999490
660]
661[immutable/filenode: fix pyflakes warnings
662Kevan Carstensen <kevan@isnotajoke.com>**20110807004514
663 Ignore-this: e8d875bf8b1c5571e31b0eff42ecf64c
664]
665[test: fix assorted tests broken by MDMF changes
666Kevan Carstensen <kevan@isnotajoke.com>**20110807004459
667 Ignore-this: 9a0dc7e5c74bfe840a9fce278619a103
668]
669[uri: add MDMF and MDMF directory caps, add extension hint support
670Kevan Carstensen <kevan@isnotajoke.com>**20110807004436
671 Ignore-this: 6486b7d4dc0e849c6b1e9cdfb6318eac
672]
673[test/test_mutable: tests for MDMF
674Kevan Carstensen <kevan@isnotajoke.com>**20110807004414
675 Ignore-this: 29f9c3a806d67df0ed09c4f0d857d347
676 
677 These are their own patch because they cut across a lot of the changes
678 I've made in implementing MDMF in such a way as to make it difficult to
679 split them up into the other patches.
680]
681[webapi changes for MDMF
682Kevan Carstensen <kevan@isnotajoke.com>**20110807004348
683 Ignore-this: d6d4dac680baa4c99b05882b3828796c
684 
685     - Learn how to create MDMF files and directories through the
686       mutable-type argument.
687     - Operate with the interface changes associated with MDMF and #993.
688     - Learn how to do partial updates of mutable files.
689]
690[mutable/servermap: Rework the servermap to work with MDMF mutable files
691Kevan Carstensen <kevan@isnotajoke.com>**20110807004259
692 Ignore-this: 154b987fa0af716c41185b88ff7ee2e1
693]
694[dirnode: teach dirnode to make MDMF directories
695Kevan Carstensen <kevan@isnotajoke.com>**20110807004224
696 Ignore-this: 765ccd6a07ff752bf6057a3dab9e5abd
697]
698[Fix some test failures caused by #393 patch.
699david-sarah@jacaranda.org**20110802032810
700 Ignore-this: 7f65e5adb5c859af289cea7011216fef
701]
702[docs: amend configuration, webapi documentation to talk about MDMF
703Kevan Carstensen <kevan@isnotajoke.com>**20110802022056
704 Ignore-this: 4cab9b7e4ab79cc1efdabe2d457f27a6
705]
706[cli: teach CLI how to create MDMF mutable files
707Kevan Carstensen <kevan@isnotajoke.com>**20110802021613
708 Ignore-this: 18d0ff98e75be231eed3c53319e76936
709 
710 Specifically, 'tahoe mkdir' and 'tahoe put' now take a --mutable-type
711 argument.
712]
713[frontends/sftpd: Resolve incompatibilities between SFTP frontend and MDMF changes
714Kevan Carstensen <kevan@isnotajoke.com>**20110802021207
715 Ignore-this: 5e0f6e961048f71d4eed6d30210ffd2e
716]
717[mutable/layout: Define MDMF share format, write tools for working with MDMF share format
718Kevan Carstensen <kevan@isnotajoke.com>**20110802021120
719 Ignore-this: fa76ef4800939e19ba3cbc22a2eab4e
720 
721 The changes in layout.py are mostly concerned with the MDMF share
722 format. In particular, we define read and write proxy objects used by
723 retrieval, publishing, and other code to write and read the MDMF share
724 format. We create equivalent proxies for SDMF objects so that these
725 objects can be suitably general.
726]
727[immutable/filenode: implement unified filenode interface
728Kevan Carstensen <kevan@isnotajoke.com>**20110802020905
729 Ignore-this: d9a442fc285157f134f5d1b4607c6a48
730]
731[immutable/literal.py: Implement interface changes in literal nodes.
732Kevan Carstensen <kevan@isnotajoke.com>**20110802020814
733 Ignore-this: 4371e71a50e65ce2607c4d67d3a32171
734]
735[test/common: Alter common test code to work with MDMF.
736Kevan Carstensen <kevan@isnotajoke.com>**20110802015643
737 Ignore-this: e564403182d0030439b168dd9f8726fa
738 
739 This mostly has to do with making the test code implement the new
740 unified filenode interfaces.
741]
742[mutable: train checker and repairer to work with MDMF mutable files
743Kevan Carstensen <kevan@isnotajoke.com>**20110802015140
744 Ignore-this: 8b1928925bed63708b71ab0de8d4306f
745]
746[nodemaker: teach nodemaker about MDMF caps
747Kevan Carstensen <kevan@isnotajoke.com>**20110802014926
748 Ignore-this: 430c73121b6883b99626cfd652fc65c4
749]
750[client: teach client how to create and work with MDMF files
751Kevan Carstensen <kevan@isnotajoke.com>**20110802014811
752 Ignore-this: d72fbc4c2ca63f00d9ab9dc2919098ff
753]
754[mutable/filenode: Modify mutable filenodes for use with MDMF
755Kevan Carstensen <kevan@isnotajoke.com>**20110802014501
756 Ignore-this: 3c230bb0ebe60a94c667b0ee0c3b28e0
757 
758 In particular:
759     - Break MutableFileNode and MutableFileVersion into distinct classes.
760     - Implement the interface modifications made for MDMF.
761     - Be aware of MDMF caps.
762     - Learn how to create and work with MDMF files.
763]
764[nodemaker: teach nodemaker how to create MDMF mutable files
765Kevan Carstensen <kevan@isnotajoke.com>**20110802014258
766 Ignore-this: 2bf1fd4f8c1d1ad0e855c678347b76c2
767]
768[interfaces: change interfaces to work with MDMF
769Kevan Carstensen <kevan@isnotajoke.com>**20110802014119
770 Ignore-this: 2f441022cf888c044bc9e6dd609db139
771 
772 A lot of this work concerns #993, in that it unifies (to an extent) the
773 interfaces of mutable and immutable files.
774]
775[mutable/publish: teach the publisher how to publish MDMF mutable files
776Kevan Carstensen <kevan@isnotajoke.com>**20110802013931
777 Ignore-this: 115217ec2b289452ec774cb725da8a86
778 
779 Like the downloader, the publisher needs some substantial changes to handle multiple segment mutable files.
780]
781[mutable/retrieve: rework the mutable downloader to handle multiple-segment files
782Kevan Carstensen <kevan@isnotajoke.com>**20110802013524
783 Ignore-this: 398d11b5cb993b50e5e4fa6e7a3856dc
784 
785 The downloader needs substantial reworking to handle multiple segment
786 mutable files, which it needs to handle for MDMF.
787]
788[Fix repeated 'the' in license text.
789david-sarah@jacaranda.org**20110819204836
790 Ignore-this: b3bd4e9ec22029fe15533ad2a60003ad
791]
792[Remove Non-Profit Open Software License from the set of 'added permission' licenses. Although it actually does qualify as an Open Source license (because it allows relicensing under plain OSL), its wording is unclear and could easily be misunderstood, and it contributes to incompatible license proliferation.
793david-sarah@jacaranda.org**20110819204742
794 Ignore-this: 7373819a6b5367581356728ea62cabb1
795]
796[docs: change links that pointed to COPYING.TGPPL.html to point to COPYING.TGPPL.rst instead
797zooko@zooko.com**20110819060142
798 Ignore-this: 301652554fd7ab4bfa5aa8f8a2863a9e
799]
800[docs: formatting: reflow to fill-column 77
801zooko@zooko.com**20110819060110
802 Ignore-this: ed1317c126f07c63b944bd2fa6aa2d21
803]
804[docs: formatting: M-x whitespace-cleanup
805zooko@zooko.com**20110819060041
806 Ignore-this: 8554b16a25067094d0dc4dc71e1b3950
807]
808[licensing: add to the list of licenses that we grant the added permission for
809zooko@zooko.com**20110819054656
810 Ignore-this: eb1490416ac6b7414a27f150a8a8a047
811 Added: most of the ones listed on the FSF's "List of Free Software, GPL Incompatible Licenses", plus the Non-Profit Open Software License.
812]
813[docs: reflow the added text at the top of COPYING.GPL to fill-column 77
814zooko@zooko.com**20110819053059
815 Ignore-this: e994ed6ffbcc12656406f11cb862ce99
816]
817[docs: reformat COPYING.TGPPL.html to COPYING.TGPPL.rst
818zooko@zooko.com**20110819052753
819 Ignore-this: 34ddf623e0a6de008ba859ca9c92b2fd
820]
821[docs: reflow docs/logging.rst to fill-column 77
822zooko@zooko.com**20110819044103
823 Ignore-this: a6901f2244995f278ddf8d75d29410bf
824]
825[doc: fix formatting error in docs/logging.rst
826zooko@zooko.com**20110819043946
827 Ignore-this: fa182dbbe7f4fda15e0a8bfcf7f00051
828]
829[Cleanups for suppression of UserWarnings. refs #1435
830david-sarah@jacaranda.org**20110818040749
831 Ignore-this: 3863ef399c1c382a1365d51f000d314c
832]
833[suppress warning emitted by newer zope.interface with Nevow 0.10
834zooko@zooko.com**20110817203134
835 Ignore-this: b86d4ce0ed1c0da76d1f9eaf8d08d9c4
836 refs #1435
837]
838[doc: formatting: reflow to fill-column=77
839zooko@zooko.com**20110809132510
840 Ignore-this: 2d6d2e203d52925968b4451f36364792
841]
842[allmydata/__init__.py, test_version.py: make version parsing understand '<=', with test. refs #1435
843david-sarah@jacaranda.org**20110815035153
844 Ignore-this: 8c3a75f4a2b42b56bac48b5053c5e9c2
845]
846[_auto_deps.py: change the requirement for zope.interface to <= 3.6.2, >= 3.6.6. fixes #1435
847david-sarah@jacaranda.org**20110815025347
848 Ignore-this: 17a88c0f6573f044fbcd6b666667bd37
849]
850[Makefile and setup.py: remove setup.py commands that we no longer need, and their uses in the Makefile. Delete a stale and incorrect comment about updating _version.py. Also fix some coding style checks in the Makefile to operate on all source files.
851david-sarah@jacaranda.org**20110801031952
852 Ignore-this: 80a435dee3bc6e29058d4b37ff579922
853]
854[remove misc/debian[_helpers], rely upon official packaging instead. fixes #1454
855warner@lothar.com**20110811182705
856 Ignore-this: 79673cafc7c108db49b5ab908d7b4668
857]
858[Makefile: remove targets that used misc/debian[_helpers] which no longer exist. Also change docs/debian.rst to reflect the fact that we no longer support building .debs using those targets. refs #1454
859david-sarah@jacaranda.org**20110801031857
860 Ignore-this: 347cbeff45757db630ce34d0cfb84f92
861]
862[replace tabs with spaces in the #1441 'tahoe debug' synopsis
863warner@lothar.com**20110811173704
864 Ignore-this: 513fbfb18a3dd93119ea3700118df7ee
865]
866[Correct the information printed by '/usr/bin/tahoe debug --help' on Debian/Ubuntu. fixes #1441
867david-sarah@jacaranda.org**20110724162530
868 Ignore-this: 30d4b8c20e420e9a9d1b73eba1113ae
869]
870[doc: edit the explanation of K-of-N tradeoffs
871zooko@zooko.com**20110804193409
872 Ignore-this: ab6f4e35a995c2099340b5c9c5d30f40
873]
874[doc: clean up formatting of doc/configuration.rst
875zooko@zooko.com**20110804192722
876 Ignore-this: 7a98a3a8afb7e5441ff1f534211199ba
877 reflow to 77 chars line width, M-x white-space cleanup, blank link between name and definition
878]
879[Add test for webopen. fixes #1149
880david-sarah@jacaranda.org**20110724211659
881 Ignore-this: 1e22853f7eb05e24c3141d56a513f661
882]
883[test_client.py: relax a check in test_create_drop_uploader so that it should pass on Python 2.4.x. refs #1429
884david-sarah@jacaranda.org**20110810052504
885 Ignore-this: 1380749ceaf33c30e26c50d57476616c
886]
887[test/common_util.py: correct fix to mkdir_nonascii. refs #1472
888david-sarah@jacaranda.org**20110810051906
889 Ignore-this: 93c0c33370bc47d95c26c4cce8e05290
890]
891[test/common_util.py: fix a typo. refs #1472
892david-sarah@jacaranda.org**20110810044235
893 Ignore-this: f88643d7c82cb3577686d77bbff9e2bc
894]
895[test_client.py, test_drop_upload.py: fix pyflakes warnings.
896david-sarah@jacaranda.org**20110810034505
897 Ignore-this: 1e2d71bf2f43d63cbb423d32a6f96793
898]
899[Factor out methods dealing with non-ASCII directories and filenames from test_drop_upload.py into common_util.py. refs #1429, #1472
900david-sarah@jacaranda.org**20110810031558
901 Ignore-this: 3de8f945fa7a58fc318a1184bad0fd1a
902]
903[test_client.py: add a test that the drop-uploader is initialized correctly by client.py. Also give the DropUploader service a name, which is necessary for the test. refs #1429
904david-sarah@jacaranda.org**20110810030538
905 Ignore-this: 13d511ea9bbe9da2dcffe4a91ce94eae
906]
907[drop-upload: rename 'start' method to 'startService', which is what you're supposed to use to start a Service. refs #1429
908david-sarah@jacaranda.org**20110810030345
909 Ignore-this: d1f5e5c63937ea37be37324e2f1ae99d
910]
911[test_drop_upload.py: add comment explaining why we don't use FilePath.setContent. refs #1429
912david-sarah@jacaranda.org**20110810025942
913 Ignore-this: b95358030b63cb467d1d7f1b9a9b6978
914]
915[test_drop_upload.py: fix some grammatical and spelling nits. refs #1429
916david-sarah@jacaranda.org**20110809221231
917 Ignore-this: fd331acddd9f754173f274a34fe62f03
918]
919[drop-upload: report the configured local directory being absent differently from it being a file
920zooko@zooko.com**20110809220930
921 Ignore-this: a08879100f5f20e609be3f0ffa3b25cc
922 refs #1429
923]
924[drop-upload: rename the 'upload.uri' parameter to 'upload.dircap', and a couple of cleanups to error messages. refs #1429
925zooko@zooko.com**20110809220508
926 Ignore-this: 4846368cbe331e8653bdce1f314e276b
927 I rerecorded this patch, originally by David-Sarah, to use "darcs replace" instead of editing to do the renames. This uncovered one missed rename in Client.init_drop_uploader. (Which also means that code isn't exercised by the current unit tests.)
928 refs #1429
929]
930[drop-upload test for non-existent local dir separately from test for non-directory local dir
931zooko@zooko.com**20110809220115
932 Ignore-this: cd85f345c02f5cb71b1c1527bd4ebddc
933 A candidate patch for #1429 has a bug when it is using FilePath.is_dir() to detect whether the configured local dir exists and is a directory. FilePath.is_dir() raises exception, instead of returning False, if the thing doesn't exist. This test is to make sure that DropUploader.__init__ raise different exceptions for those two cases.
934 refs #1429
935]
936[drop-upload: unit tests for the configuration options being named "cap" instead of "uri"
937zooko@zooko.com**20110809215913
938 Ignore-this: 958c78fffb3d76b3e4817647f824e7f9
939 This is a subset of a patch that David-Sarah attached to #1429. This is just the unit-tests part of that patch, and uses darcs record instead of hunks to change the names.
940 refs #1429
941]
942[src/allmydata/storage/server.py: use the filesystem of storage/shares/, rather than storage/, to calculate remaining space. fixes #1384
943david-sarah@jacaranda.org**20110719022752
944 Ignore-this: a4781043cfd453dbb66ae4f108d80bea
945]
946[test_storage.py: test that we are using the filesystem of storage/shares/, rather than storage/, to calculate remaining space, and that the HTML status output reflects the values returned by fileutil.get_disk_stats. This version works with older versions of the mock library. refs #1384
947david-sarah@jacaranda.org**20110809190722
948 Ignore-this: db447caca37a459ca49563efa58db58c
949]
950[Work around ref #1472 by having test_drop_upload delete the non-ASCII directories it creates.
951david-sarah@jacaranda.org**20110809012334
952 Ignore-this: 5881fd5db419ba8ad12e0b2a82f6c4f0
953]
954[Remove all trailing whitespace from .py files.
955david-sarah@jacaranda.org**20110809001117
956 Ignore-this: d2658b5ce44af70cc606ae4d3085b7cc
957]
958[test_drop_upload.py: fix unused imports. refs #1429
959david-sarah@jacaranda.org**20110808235422
960 Ignore-this: 834f6b946bfea699d7d8c743edd66671
961]
962[Documentation for drop-upload frontend. refs #1429
963david-sarah@jacaranda.org**20110808182146
964 Ignore-this: b33110834e586c0b784d1736c2af5779
965]
966[Drop-upload frontend, rerecorded for 1.9 beta (and correcting a minor mistake). Includes some fixes for Windows but not the Windows inotify implementation. fixes #1429
967david-sarah@jacaranda.org**20110808234049
968 Ignore-this: 67f824c7f554e9a3a85f9fd2e1123d97
969]
970[node.py: ensure that client and introducer nodes record their port number and use that port on the next restart, fixing a regression caused by #1385. fixes #1469.
971david-sarah@jacaranda.org**20110806221934
972 Ignore-this: 1aa9d340b6570320ab2f9edc89c9e0a8
973]
974[test_runner.py: fix a race condition in the test when NODE_URL_FILE is written before PORTNUM_FILE. refs #1469
975david-sarah@jacaranda.org**20110806231842
976 Ignore-this: ab01ae7cec3a073e29eec473e64052a0
977]
978[test_runner.py: cleanups of HOTLINE_FILE writing and removal.
979david-sarah@jacaranda.org**20110806231652
980 Ignore-this: 25f5c5d6f5d8faebb26a4ce80110a335
981]
982[test_runner.py: remove an unused constant.
983david-sarah@jacaranda.org**20110806221416
984 Ignore-this: eade2695cbabbea9cafeaa8debe410bb
985]
986[node.py: fix the error path for a missing config option so that it works for a Unicode base directory.
987david-sarah@jacaranda.org**20110806221007
988 Ignore-this: 4eb9cc04b2ce05182a274a0d69dafaf3
989]
990[test_runner.py: test that client and introducer nodes record their port number and use that port on the next restart. This tests for a regression caused by ref #1385.
991david-sarah@jacaranda.org**20110806220635
992 Ignore-this: 40a0c040b142dbddd47e69b3c3712f5
993]
994[test_runner.py: fix a bug in CreateNode.do_create introduced in changeset [5114] when the tahoe.cfg file has been written with CRLF line endings. refs #1385
995david-sarah@jacaranda.org**20110804003032
996 Ignore-this: 7b7afdcf99da6671afac2d42828883eb
997]
998[test_client.py: repair Basic.test_error_on_old_config_files. refs #1385
999david-sarah@jacaranda.org**20110803235036
1000 Ignore-this: 31e2a9c3febe55948de7e144353663e
1001]
1002[test_checker.py: increase timeout for TooParallel.test_immutable again. The ARM buildslave took 38 seconds, so 40 seconds is too close to the edge; make it 80.
1003david-sarah@jacaranda.org**20110803214042
1004 Ignore-this: 2d8026a6b25534e01738f78d6c7495cb
1005]
1006[test_runner.py: fix RunNode.test_introducer to not rely on the mtime of introducer.furl to detect when the node has restarted. Instead we detect when node.url has been written. refs #1385
1007david-sarah@jacaranda.org**20110803180917
1008 Ignore-this: 11ddc43b107beca42cb78af88c5c394c
1009]
1010[Further improve error message about old config files. refs #1385
1011david-sarah@jacaranda.org**20110803174546
1012 Ignore-this: 9d6cc3c288d9863dce58faafb3855917
1013]
1014[Slightly improve error message about old config files (avoid unnecessary Unicode escaping). refs #1385
1015david-sarah@jacaranda.org**20110803163848
1016 Ignore-this: a3e3930fba7ccf90b8db3d2ed5829df4
1017]
1018[test_checker.py: increase timeout for TooParallel.test_immutable (was consistently failing on ARM buildslave).
1019david-sarah@jacaranda.org**20110803163213
1020 Ignore-this: d0efceaf12628e8791862b80c85b5d56
1021]
1022[Fix the bug that prevents an introducer from starting when introducer.furl already exists. Also remove some dead code that used to read old config files, and rename 'warn_about_old_config_files' to reflect that it's not a warning. refs #1385
1023david-sarah@jacaranda.org**20110803013212
1024 Ignore-this: 2d6cd14bd06a7493b26f2027aff78f4d
1025]
1026[test_runner.py: modify RunNode.test_introducer to test that starting an introducer works when the introducer.furl file already exists. refs #1385
1027david-sarah@jacaranda.org**20110803012704
1028 Ignore-this: 8cf7f27ac4bfbb5ad8ca4a974106d437
1029]
1030[verifier: correct a bug introduced in changeset [5106] that caused us to only verify the first block of a file. refs #1395
1031david-sarah@jacaranda.org**20110802172437
1032 Ignore-this: 87fb77854a839ff217dce73544775b11
1033]
1034[test_repairer: add a deterministic test of share data corruption that always flips the bits of the last byte of the share data. refs #1395
1035david-sarah@jacaranda.org**20110802175841
1036 Ignore-this: 72f54603785007e88220c8d979e08be7
1037]
1038[verifier: serialize the fetching of blocks within a share so that we don't use too much RAM
1039zooko@zooko.com**20110802063703
1040 Ignore-this: debd9bac07dcbb6803f835a9e2eabaa1
1041 
1042 Shares are still verified in parallel, but within a share, don't request a
1043 block until the previous block has been verified and the memory we used to hold
1044 it has been freed up.
1045 
1046 Patch originally due to Brian. This version has a mockery-patchery-style test
1047 which is "low tech" (it implements the patching inline in the test code instead
1048 of using an extension of the mock.patch() function from the mock library) and
1049 which unpatches in case of exception.
1050 
1051 fixes #1395
1052]
1053[add docs about timing-channel attacks
1054Brian Warner <warner@lothar.com>**20110802044541
1055 Ignore-this: 73114d5f5ed9ce252597b707dba3a194
1056]
1057['test-coverage' now needs PYTHONPATH=. to find TOP/twisted/plugins/
1058Brian Warner <warner@lothar.com>**20110802041952
1059 Ignore-this: d40f1f4cb426ea1c362fc961baedde2
1060]
1061[remove nodeid from WriteBucketProxy classes and customers
1062warner@lothar.com**20110801224317
1063 Ignore-this: e55334bb0095de11711eeb3af827e8e8
1064 refs #1363
1065]
1066[remove get_serverid() from ReadBucketProxy and customers, including Checker
1067warner@lothar.com**20110801224307
1068 Ignore-this: 837aba457bc853e4fd413ab1a94519cb
1069 and debug.py dump-share commands
1070 refs #1363
1071]
1072[reject old-style (pre-Tahoe-LAFS-v1.3) configuration files
1073zooko@zooko.com**20110801232423
1074 Ignore-this: b58218fcc064cc75ad8f05ed0c38902b
1075 Check for the existence of any of them and if any are found raise exception which will abort the startup of the node.
1076 This is a backwards-incompatible change for anyone who is still using old-style configuration files.
1077 fixes #1385
1078]
1079[whitespace-cleanup
1080zooko@zooko.com**20110725015546
1081 Ignore-this: 442970d0545183b97adc7bd66657876c
1082]
1083[tests: use fileutil.write() instead of open() to ensure timely close even without CPython-style reference counting
1084zooko@zooko.com**20110331145427
1085 Ignore-this: 75aae4ab8e5fa0ad698f998aaa1888ce
1086 Some of these already had an explicit close() but I went ahead and replaced them with fileutil.write() as well for the sake of uniformity.
1087]
1088[Address Kevan's comment in #776 about Options classes missed when adding 'self.command_name'. refs #776, #1359
1089david-sarah@jacaranda.org**20110801221317
1090 Ignore-this: 8881d42cf7e6a1d15468291b0cb8fab9
1091]
1092[docs/frontends/webapi.rst: change some more instances of 'delete' or 'remove' to 'unlink', change some section titles, and use two blank lines between all sections. refs #776, #1104
1093david-sarah@jacaranda.org**20110801220919
1094 Ignore-this: 572327591137bb05c24c44812d4b163f
1095]
1096[cleanup: implement rm as a synonym for unlink rather than vice-versa. refs #776
1097david-sarah@jacaranda.org**20110801220108
1098 Ignore-this: 598dcbed870f4f6bb9df62de9111b343
1099]
1100[docs/webapi.rst: address Kevan's comments about use of 'delete' on ref #1104
1101david-sarah@jacaranda.org**20110801205356
1102 Ignore-this: 4fbf03864934753c951ddeff64392491
1103]
1104[docs: some changes of 'delete' or 'rm' to 'unlink'. refs #1104
1105david-sarah@jacaranda.org**20110713002722
1106 Ignore-this: 304d2a330d5e6e77d5f1feed7814b21c
1107]
1108[WUI: change the label of the button to unlink a file from 'del' to 'unlink'. Also change some internal names to 'unlink', and allow 't=unlink' as a synonym for 't=delete' in the web-API interface. Incidentally, improve a test to check for the rename button as well as the unlink button. fixes #1104
1109david-sarah@jacaranda.org**20110713001218
1110 Ignore-this: 3eef6b3f81b94a9c0020a38eb20aa069
1111]
1112[src/allmydata/web/filenode.py: delete a stale comment that was made incorrect by changeset [3133].
1113david-sarah@jacaranda.org**20110801203009
1114 Ignore-this: b3912e95a874647027efdc97822dd10e
1115]
1116[fix typo introduced during rebasing of 'remove get_serverid from
1117Brian Warner <warner@lothar.com>**20110801200341
1118 Ignore-this: 4235b0f585c0533892193941dbbd89a8
1119 DownloadStatus.add_dyhb_request and customers' patch, to fix test failure.
1120]
1121[remove get_serverid from DownloadStatus.add_dyhb_request and customers
1122zooko@zooko.com**20110801185401
1123 Ignore-this: db188c18566d2d0ab39a80c9dc8f6be6
1124 This patch is a rebase of a patch originally written by Brian. I didn't change any of the intent of Brian's patch, just ported it to current trunk.
1125 refs #1363
1126]
1127[remove get_serverid from DownloadStatus.add_block_request and customers
1128zooko@zooko.com**20110801185344
1129 Ignore-this: 8bfa8201d6147f69b0fbe31beea9c1e
1130 This is a rebase of a patch Brian originally wrote. I haven't changed the intent of that patch, just ported it to trunk.
1131 refs #1363
1132]
1133[apply zooko's advice: storage_client get_known_servers() returns a frozenset, caller sorts
1134warner@lothar.com**20110801174452
1135 Ignore-this: 2aa13ea6cbed4e9084bd604bf8633692
1136 refs #1363
1137]
1138[test_immutable.Test: rewrite to use NoNetworkGrid, now takes 2.7s not 97s
1139warner@lothar.com**20110801174444
1140 Ignore-this: 54f30b5d7461d2b3514e2a0172f3a98c
1141 remove now-unused ShareManglingMixin
1142 refs #1363
1143]
1144[DownloadStatus.add_known_share wants to be used by Finder, web.status
1145warner@lothar.com**20110801174436
1146 Ignore-this: 1433bcd73099a579abe449f697f35f9
1147 refs #1363
1148]
1149[replace IServer.name() with get_name(), and get_longname()
1150warner@lothar.com**20110801174428
1151 Ignore-this: e5a6f7f6687fd7732ddf41cfdd7c491b
1152 
1153 This patch was originally written by Brian, but was re-recorded by Zooko to use
1154 darcs replace instead of hunks for any file in which it would result in fewer
1155 total hunks.
1156 refs #1363
1157]
1158[upload.py: apply David-Sarah's advice rename (un)contacted(2) trackers to first_pass/second_pass/next_pass
1159zooko@zooko.com**20110801174143
1160 Ignore-this: e36e1420bba0620a0107bd90032a5198
1161 This patch was written by Brian but was re-recorded by Zooko (with David-Sarah looking on) to use darcs replace instead of editing to rename the three variables to their new names.
1162 refs #1363
1163]
1164[Coalesce multiple Share.loop() calls, make downloads faster. Closes #1268.
1165Brian Warner <warner@lothar.com>**20110801151834
1166 Ignore-this: 48530fce36c01c0ff708f61c2de7e67a
1167]
1168[src/allmydata/_auto_deps.py: 'i686' is another way of spelling x86.
1169david-sarah@jacaranda.org**20110801034035
1170 Ignore-this: 6971e0621db2fba794d86395b4d51038
1171]
1172[tahoe_rm.py: better error message when there is no path. refs #1292
1173david-sarah@jacaranda.org**20110122064212
1174 Ignore-this: ff3bb2c9f376250e5fd77eb009e09018
1175]
1176[test_cli.py: Test for error message when 'tahoe rm' is invoked without a path. refs #1292
1177david-sarah@jacaranda.org**20110104105108
1178 Ignore-this: 29ec2f2e0251e446db96db002ad5dd7d
1179]
1180[src/allmydata/__init__.py: suppress a spurious warning from 'bin/tahoe --version[-and-path]' about twisted-web and twisted-core packages.
1181david-sarah@jacaranda.org**20110801005209
1182 Ignore-this: 50e7cd53cca57b1870d9df0361c7c709
1183]
1184[test_cli.py: use to_str on fields loaded using simplejson.loads in new tests. refs #1304
1185david-sarah@jacaranda.org**20110730032521
1186 Ignore-this: d1d6dfaefd1b4e733181bf127c79c00b
1187]
1188[cli: make 'tahoe cp' overwrite mutable files in-place
1189Kevan Carstensen <kevan@isnotajoke.com>**20110729202039
1190 Ignore-this: b2ad21a19439722f05c49bfd35b01855
1191]
1192[SFTP: write an error message to standard error for unrecognized shell commands. Change the existing message for shell sessions to be written to standard error, and refactor some duplicated code. Also change the lines of the error messages to end in CRLF, and take into account Kevan's review comments. fixes #1442, #1446
1193david-sarah@jacaranda.org**20110729233102
1194 Ignore-this: d2f2bb4664f25007d1602bf7333e2cdd
1195]
1196[src/allmydata/scripts/cli.py: fix pyflakes warning.
1197david-sarah@jacaranda.org**20110728021402
1198 Ignore-this: 94050140ddb99865295973f49927c509
1199]
1200[Fix the help synopses of CLI commands to include [options] in the right place. fixes #1359, fixes #636
1201david-sarah@jacaranda.org**20110724225440
1202 Ignore-this: 2a8e488a5f63dabfa9db9efd83768a5
1203]
1204[encodingutil: argv and output encodings are always the same on all platforms. Lose the unnecessary generality of them being different. fixes #1120
1205david-sarah@jacaranda.org**20110629185356
1206 Ignore-this: 5ebacbe6903dfa83ffd3ff8436a97787
1207]
1208[docs/man/tahoe.1: add man page. fixes #1420
1209david-sarah@jacaranda.org**20110724171728
1210 Ignore-this: fc7601ec7f25494288d6141d0ae0004c
1211]
1212[Update the dependency on zope.interface to fix an incompatiblity between Nevow and zope.interface 3.6.4. fixes #1435
1213david-sarah@jacaranda.org**20110721234941
1214 Ignore-this: 2ff3fcfc030fca1a4d4c7f1fed0f2aa9
1215]
1216[frontends/ftpd.py: remove the check for IWriteFile.close since we're now guaranteed to be using Twisted >= 10.1 which has it.
1217david-sarah@jacaranda.org**20110722000320
1218 Ignore-this: 55cd558b791526113db3f83c00ec328a
1219]
1220[Update the dependency on Twisted to >= 10.1. This allows us to simplify some documentation: it's no longer necessary to install pywin32 on Windows, or apply a patch to Twisted in order to use the FTP frontend. fixes #1274, #1438. refs #1429
1221david-sarah@jacaranda.org**20110721233658
1222 Ignore-this: 81b41745477163c9b39c0b59db91cc62
1223]
1224[misc/build_helpers/run_trial.py: undo change to block pywin32 (it didn't work because run_trial.py is no longer used). refs #1334
1225david-sarah@jacaranda.org**20110722035402
1226 Ignore-this: 5d03f544c4154f088e26c7107494bf39
1227]
1228[misc/build_helpers/run_trial.py: ensure that pywin32 is not on the sys.path when running the test suite. Includes some temporary debugging printouts that will be removed. refs #1334
1229david-sarah@jacaranda.org**20110722024907
1230 Ignore-this: 5141a9f83a4085ed4ca21f0bbb20bb9c
1231]
1232[docs/running.rst: use 'tahoe run ~/.tahoe' instead of 'tahoe run' (the default is the current directory, unlike 'tahoe start').
1233david-sarah@jacaranda.org**20110718005949
1234 Ignore-this: 81837fbce073e93d88a3e7ae3122458c
1235]
1236[docs/running.rst: say to put the introducer.furl in tahoe.cfg.
1237david-sarah@jacaranda.org**20110717194315
1238 Ignore-this: 954cc4c08e413e8c62685d58ff3e11f3
1239]
1240[README.txt: say that quickstart.rst is in the docs directory.
1241david-sarah@jacaranda.org**20110717192400
1242 Ignore-this: bc6d35a85c496b77dbef7570677ea42a
1243]
1244[setup: remove the dependency on foolscap's "secure_connections" extra, add a dependency on pyOpenSSL
1245zooko@zooko.com**20110717114226
1246 Ignore-this: df222120d41447ce4102616921626c82
1247 fixes #1383
1248]
1249[test_sftp.py cleanup: remove a redundant definition of failUnlessReallyEqual.
1250david-sarah@jacaranda.org**20110716181813
1251 Ignore-this: 50113380b368c573f07ac6fe2eb1e97f
1252]
1253[docs: add missing link in NEWS.rst
1254zooko@zooko.com**20110712153307
1255 Ignore-this: be7b7eb81c03700b739daa1027d72b35
1256]
1257[contrib: remove the contributed fuse modules and the entire contrib/ directory, which is now empty
1258zooko@zooko.com**20110712153229
1259 Ignore-this: 723c4f9e2211027c79d711715d972c5
1260 Also remove a couple of vestigial references to figleaf, which is long gone.
1261 fixes #1409 (remove contrib/fuse)
1262]
1263[add Protovis.js-based download-status timeline visualization
1264Brian Warner <warner@lothar.com>**20110629222606
1265 Ignore-this: 477ccef5c51b30e246f5b6e04ab4a127
1266 
1267 provide status overlap info on the webapi t=json output, add decode/decrypt
1268 rate tooltips, add zoomin/zoomout buttons
1269]
1270[add more download-status data, fix tests
1271Brian Warner <warner@lothar.com>**20110629222555
1272 Ignore-this: e9e0b7e0163f1e95858aa646b9b17b8c
1273]
1274[prepare for viz: improve DownloadStatus events
1275Brian Warner <warner@lothar.com>**20110629222542
1276 Ignore-this: 16d0bde6b734bb501aa6f1174b2b57be
1277 
1278 consolidate IDownloadStatusHandlingConsumer stuff into DownloadNode
1279]
1280[docs: fix error in crypto specification that was noticed by Taylor R Campbell <campbell+tahoe@mumble.net>
1281zooko@zooko.com**20110629185711
1282 Ignore-this: b921ed60c1c8ba3c390737fbcbe47a67
1283]
1284[setup.py: don't make bin/tahoe.pyscript executable. fixes #1347
1285david-sarah@jacaranda.org**20110130235809
1286 Ignore-this: 3454c8b5d9c2c77ace03de3ef2d9398a
1287]
1288[Makefile: remove targets relating to 'setup.py check_auto_deps' which no longer exists. fixes #1345
1289david-sarah@jacaranda.org**20110626054124
1290 Ignore-this: abb864427a1b91bd10d5132b4589fd90
1291]
1292[Makefile: add 'make check' as an alias for 'make test'. Also remove an unnecessary dependency of 'test' on 'build' and 'src/allmydata/_version.py'. fixes #1344
1293david-sarah@jacaranda.org**20110623205528
1294 Ignore-this: c63e23146c39195de52fb17c7c49b2da
1295]
1296[Rename test_package_initialization.py to (much shorter) test_import.py .
1297Brian Warner <warner@lothar.com>**20110611190234
1298 Ignore-this: 3eb3dbac73600eeff5cfa6b65d65822
1299 
1300 The former name was making my 'ls' listings hard to read, by forcing them
1301 down to just two columns.
1302]
1303[tests: fix tests to accomodate [20110611153758-92b7f-0ba5e4726fb6318dac28fb762a6512a003f4c430]
1304zooko@zooko.com**20110611163741
1305 Ignore-this: 64073a5f39e7937e8e5e1314c1a302d1
1306 Apparently none of the two authors (stercor, terrell), three reviewers (warner, davidsarah, terrell), or one committer (me) actually ran the tests. This is presumably due to #20.
1307 fixes #1412
1308]
1309[wui: right-align the size column in the WUI
1310zooko@zooko.com**20110611153758
1311 Ignore-this: 492bdaf4373c96f59f90581c7daf7cd7
1312 Thanks to Ted "stercor" Rolle Jr. and Terrell Russell.
1313 fixes #1412
1314]
1315[docs: three minor fixes
1316zooko@zooko.com**20110610121656
1317 Ignore-this: fec96579eb95aceb2ad5fc01a814c8a2
1318 CREDITS for arc for stats tweak
1319 fix link to .zip file in quickstart.rst (thanks to ChosenOne for noticing)
1320 English usage tweak
1321]
1322[docs/running.rst: fix stray HTML (not .rst) link noticed by ChosenOne.
1323david-sarah@jacaranda.org**20110609223719
1324 Ignore-this: fc50ac9c94792dcac6f1067df8ac0d4a
1325]
1326[server.py:  get_latencies now reports percentiles _only_ if there are sufficient observations for the interpretation of the percentile to be unambiguous.
1327wilcoxjg@gmail.com**20110527120135
1328 Ignore-this: 2e7029764bffc60e26f471d7c2b6611e
1329 interfaces.py:  modified the return type of RIStatsProvider.get_stats to allow for None as a return value
1330 NEWS.rst, stats.py: documentation of change to get_latencies
1331 stats.rst: now documents percentile modification in get_latencies
1332 test_storage.py:  test_latencies now expects None in output categories that contain too few samples for the associated percentile to be unambiguously reported.
1333 fixes #1392
1334]
1335[docs: revert link in relnotes.txt from NEWS.rst to NEWS, since the former did not exist at revision 5000.
1336david-sarah@jacaranda.org**20110517011214
1337 Ignore-this: 6a5be6e70241e3ec0575641f64343df7
1338]
1339[docs: convert NEWS to NEWS.rst and change all references to it.
1340david-sarah@jacaranda.org**20110517010255
1341 Ignore-this: a820b93ea10577c77e9c8206dbfe770d
1342]
1343[docs: remove out-of-date docs/testgrid/introducer.furl and containing directory. fixes #1404
1344david-sarah@jacaranda.org**20110512140559
1345 Ignore-this: 784548fc5367fac5450df1c46890876d
1346]
1347[scripts/common.py: don't assume that the default alias is always 'tahoe' (it is, but the API of get_alias doesn't say so). refs #1342
1348david-sarah@jacaranda.org**20110130164923
1349 Ignore-this: a271e77ce81d84bb4c43645b891d92eb
1350]
1351[setup: don't catch all Exception from check_requirement(), but only PackagingError and ImportError
1352zooko@zooko.com**20110128142006
1353 Ignore-this: 57d4bc9298b711e4bc9dc832c75295de
1354 I noticed this because I had accidentally inserted a bug which caused AssertionError to be raised from check_requirement().
1355]
1356[M-x whitespace-cleanup
1357zooko@zooko.com**20110510193653
1358 Ignore-this: dea02f831298c0f65ad096960e7df5c7
1359]
1360[docs: fix typo in running.rst, thanks to arch_o_median
1361zooko@zooko.com**20110510193633
1362 Ignore-this: ca06de166a46abbc61140513918e79e8
1363]
1364[relnotes.txt: don't claim to work on Cygwin (which has been untested for some time). refs #1342
1365david-sarah@jacaranda.org**20110204204902
1366 Ignore-this: 85ef118a48453d93fa4cddc32d65b25b
1367]
1368[relnotes.txt: forseeable -> foreseeable. refs #1342
1369david-sarah@jacaranda.org**20110204204116
1370 Ignore-this: 746debc4d82f4031ebf75ab4031b3a9
1371]
1372[replace remaining .html docs with .rst docs
1373zooko@zooko.com**20110510191650
1374 Ignore-this: d557d960a986d4ac8216d1677d236399
1375 Remove install.html (long since deprecated).
1376 Also replace some obsolete references to install.html with references to quickstart.rst.
1377 Fix some broken internal references within docs/historical/historical_known_issues.txt.
1378 Thanks to Ravi Pinjala and Patrick McDonald.
1379 refs #1227
1380]
1381[docs: FTP-and-SFTP.rst: fix a minor error and update the information about which version of Twisted fixes #1297
1382zooko@zooko.com**20110428055232
1383 Ignore-this: b63cfb4ebdbe32fb3b5f885255db4d39
1384]
1385[munin tahoe_files plugin: fix incorrect file count
1386francois@ctrlaltdel.ch**20110428055312
1387 Ignore-this: 334ba49a0bbd93b4a7b06a25697aba34
1388 fixes #1391
1389]
1390[corrected "k must never be smaller than N" to "k must never be greater than N"
1391secorp@allmydata.org**20110425010308
1392 Ignore-this: 233129505d6c70860087f22541805eac
1393]
1394[Fix a test failure in test_package_initialization on Python 2.4.x due to exceptions being stringified differently than in later versions of Python. refs #1389
1395david-sarah@jacaranda.org**20110411190738
1396 Ignore-this: 7847d26bc117c328c679f08a7baee519
1397]
1398[tests: add test for including the ImportError message and traceback entry in the summary of errors from importing dependencies. refs #1389
1399david-sarah@jacaranda.org**20110410155844
1400 Ignore-this: fbecdbeb0d06a0f875fe8d4030aabafa
1401]
1402[allmydata/__init__.py: preserve the message and last traceback entry (file, line number, function, and source line) of ImportErrors in the package versions string. fixes #1389
1403david-sarah@jacaranda.org**20110410155705
1404 Ignore-this: 2f87b8b327906cf8bfca9440a0904900
1405]
1406[remove unused variable detected by pyflakes
1407zooko@zooko.com**20110407172231
1408 Ignore-this: 7344652d5e0720af822070d91f03daf9
1409]
1410[allmydata/__init__.py: Nicer reporting of unparseable version numbers in dependencies. fixes #1388
1411david-sarah@jacaranda.org**20110401202750
1412 Ignore-this: 9c6bd599259d2405e1caadbb3e0d8c7f
1413]
1414[update FTP-and-SFTP.rst: the necessary patch is included in Twisted-10.1
1415Brian Warner <warner@lothar.com>**20110325232511
1416 Ignore-this: d5307faa6900f143193bfbe14e0f01a
1417]
1418[control.py: remove all uses of s.get_serverid()
1419warner@lothar.com**20110227011203
1420 Ignore-this: f80a787953bd7fa3d40e828bde00e855
1421]
1422[web: remove some uses of s.get_serverid(), not all
1423warner@lothar.com**20110227011159
1424 Ignore-this: a9347d9cf6436537a47edc6efde9f8be
1425]
1426[immutable/downloader/fetcher.py: remove all get_serverid() calls
1427warner@lothar.com**20110227011156
1428 Ignore-this: fb5ef018ade1749348b546ec24f7f09a
1429]
1430[immutable/downloader/fetcher.py: fix diversity bug in server-response handling
1431warner@lothar.com**20110227011153
1432 Ignore-this: bcd62232c9159371ae8a16ff63d22c1b
1433 
1434 When blocks terminate (either COMPLETE or CORRUPT/DEAD/BADSEGNUM), the
1435 _shares_from_server dict was being popped incorrectly (using shnum as the
1436 index instead of serverid). I'm still thinking through the consequences of
1437 this bug. It was probably benign and really hard to detect. I think it would
1438 cause us to incorrectly believe that we're pulling too many shares from a
1439 server, and thus prefer a different server rather than asking for a second
1440 share from the first server. The diversity code is intended to spread out the
1441 number of shares simultaneously being requested from each server, but with
1442 this bug, it might be spreading out the total number of shares requested at
1443 all, not just simultaneously. (note that SegmentFetcher is scoped to a single
1444 segment, so the effect doesn't last very long).
1445]
1446[immutable/downloader/share.py: reduce get_serverid(), one left, update ext deps
1447warner@lothar.com**20110227011150
1448 Ignore-this: d8d56dd8e7b280792b40105e13664554
1449 
1450 test_download.py: create+check MyShare instances better, make sure they share
1451 Server objects, now that finder.py cares
1452]
1453[immutable/downloader/finder.py: reduce use of get_serverid(), one left
1454warner@lothar.com**20110227011146
1455 Ignore-this: 5785be173b491ae8a78faf5142892020
1456]
1457[immutable/offloaded.py: reduce use of get_serverid() a bit more
1458warner@lothar.com**20110227011142
1459 Ignore-this: b48acc1b2ae1b311da7f3ba4ffba38f
1460]
1461[immutable/upload.py: reduce use of get_serverid()
1462warner@lothar.com**20110227011138
1463 Ignore-this: ffdd7ff32bca890782119a6e9f1495f6
1464]
1465[immutable/checker.py: remove some uses of s.get_serverid(), not all
1466warner@lothar.com**20110227011134
1467 Ignore-this: e480a37efa9e94e8016d826c492f626e
1468]
1469[add remaining get_* methods to storage_client.Server, NoNetworkServer, and
1470warner@lothar.com**20110227011132
1471 Ignore-this: 6078279ddf42b179996a4b53bee8c421
1472 MockIServer stubs
1473]
1474[upload.py: rearrange _make_trackers a bit, no behavior changes
1475warner@lothar.com**20110227011128
1476 Ignore-this: 296d4819e2af452b107177aef6ebb40f
1477]
1478[happinessutil.py: finally rename merge_peers to merge_servers
1479warner@lothar.com**20110227011124
1480 Ignore-this: c8cd381fea1dd888899cb71e4f86de6e
1481]
1482[test_upload.py: factor out FakeServerTracker
1483warner@lothar.com**20110227011120
1484 Ignore-this: 6c182cba90e908221099472cc159325b
1485]
1486[test_upload.py: server-vs-tracker cleanup
1487warner@lothar.com**20110227011115
1488 Ignore-this: 2915133be1a3ba456e8603885437e03
1489]
1490[happinessutil.py: server-vs-tracker cleanup
1491warner@lothar.com**20110227011111
1492 Ignore-this: b856c84033562d7d718cae7cb01085a9
1493]
1494[upload.py: more tracker-vs-server cleanup
1495warner@lothar.com**20110227011107
1496 Ignore-this: bb75ed2afef55e47c085b35def2de315
1497]
1498[upload.py: fix var names to avoid confusion between 'trackers' and 'servers'
1499warner@lothar.com**20110227011103
1500 Ignore-this: 5d5e3415b7d2732d92f42413c25d205d
1501]
1502[refactor: s/peer/server/ in immutable/upload, happinessutil.py, test_upload
1503warner@lothar.com**20110227011100
1504 Ignore-this: 7ea858755cbe5896ac212a925840fe68
1505 
1506 No behavioral changes, just updating variable/method names and log messages.
1507 The effects outside these three files should be minimal: some exception
1508 messages changed (to say "server" instead of "peer"), and some internal class
1509 names were changed. A few things still use "peer" to minimize external
1510 changes, like UploadResults.timings["peer_selection"] and
1511 happinessutil.merge_peers, which can be changed later.
1512]
1513[storage_client.py: clean up test_add_server/test_add_descriptor, remove .test_servers
1514warner@lothar.com**20110227011056
1515 Ignore-this: efad933e78179d3d5fdcd6d1ef2b19cc
1516]
1517[test_client.py, upload.py:: remove KiB/MiB/etc constants, and other dead code
1518warner@lothar.com**20110227011051
1519 Ignore-this: dc83c5794c2afc4f81e592f689c0dc2d
1520]
1521[test: increase timeout on a network test because Francois's ARM machine hit that timeout
1522zooko@zooko.com**20110317165909
1523 Ignore-this: 380c345cdcbd196268ca5b65664ac85b
1524 I'm skeptical that the test was proceeding correctly but ran out of time. It seems more likely that it had gotten hung. But if we raise the timeout to an even more extravagant number then we can be even more certain that the test was never going to finish.
1525]
1526[docs/configuration.rst: add a "Frontend Configuration" section
1527Brian Warner <warner@lothar.com>**20110222014323
1528 Ignore-this: 657018aa501fe4f0efef9851628444ca
1529 
1530 this points to docs/frontends/*.rst, which were previously underlinked
1531]
1532[web/filenode.py: avoid calling req.finish() on closed HTTP connections. Closes #1366
1533"Brian Warner <warner@lothar.com>"**20110221061544
1534 Ignore-this: 799d4de19933f2309b3c0c19a63bb888
1535]
1536[Add unit tests for cross_check_pkg_resources_versus_import, and a regression test for ref #1355. This requires a little refactoring to make it testable.
1537david-sarah@jacaranda.org**20110221015817
1538 Ignore-this: 51d181698f8c20d3aca58b057e9c475a
1539]
1540[allmydata/__init__.py: .name was used in place of the correct .__name__ when printing an exception. Also, robustify string formatting by using %r instead of %s in some places. fixes #1355.
1541david-sarah@jacaranda.org**20110221020125
1542 Ignore-this: b0744ed58f161bf188e037bad077fc48
1543]
1544[Refactor StorageFarmBroker handling of servers
1545Brian Warner <warner@lothar.com>**20110221015804
1546 Ignore-this: 842144ed92f5717699b8f580eab32a51
1547 
1548 Pass around IServer instance instead of (peerid, rref) tuple. Replace
1549 "descriptor" with "server". Other replacements:
1550 
1551  get_all_servers -> get_connected_servers/get_known_servers
1552  get_servers_for_index -> get_servers_for_psi (now returns IServers)
1553 
1554 This change still needs to be pushed further down: lots of code is now
1555 getting the IServer and then distributing (peerid, rref) internally.
1556 Instead, it ought to distribute the IServer internally and delay
1557 extracting a serverid or rref until the last moment.
1558 
1559 no_network.py was updated to retain parallelism.
1560]
1561[TAG allmydata-tahoe-1.8.2
1562warner@lothar.com**20110131020101]
1563Patch bundle hash:
1564e8bf54c31b30dff2db851c04f7fab3fac5c95053