4 from twisted.python import usage
5 from allmydata.util.assertutil import precondition
6 from allmydata.util.encodingutil import unicode_to_url, quote_output, argv_to_abspath
7 from allmydata.util.fileutil import abspath_expanduser_unicode
10 _default_nodedir = None
11 if sys.platform == 'win32':
12 from allmydata.windows import registry
13 path = registry.get_base_dir_path()
15 precondition(isinstance(path, unicode), path)
16 _default_nodedir = abspath_expanduser_unicode(path)
18 if _default_nodedir is None:
19 path = abspath_expanduser_unicode(u"~/.tahoe")
20 precondition(isinstance(path, unicode), path)
21 _default_nodedir = path
23 def get_default_nodedir():
24 return _default_nodedir
27 class BaseOptions(usage.Options):
28 # unit tests can override these to point at StringIO instances
34 ["quiet", "q", "Operate silently."],
35 ["version", "V", "Display version numbers and exit."],
36 ["version-and-path", None, "Display version numbers and paths to their locations and exit."],
39 ["node-directory", "d", None, "Specify which Tahoe node directory should be used." + (
40 _default_nodedir and (" [default for most commands: " + quote_output(_default_nodedir) + "]") or "")],
43 def opt_version(self):
45 print >>self.stdout, allmydata.get_package_versions_string()
48 def opt_version_and_path(self):
50 print >>self.stdout, allmydata.get_package_versions_string(show_paths=True)
55 default_nodedir = _default_nodedir
59 ["basedir", "C", None, "Same as --node-directory."],
62 ["multiple", "m", "Specify multiple node directories at once"],
65 def parseArgs(self, *args):
66 if self['node-directory'] and self['basedir']:
67 raise usage.UsageError("The --node-directory (or -d) and --basedir (or -C) "
68 "options cannot both be used.")
70 if self['node-directory'] or self['basedir']:
71 self.basedirs = [argv_to_abspath(self['node-directory'] or self['basedir'])]
75 if self.allow_multiple and self['multiple']:
76 self.basedirs.extend(map(argv_to_abspath, args))
79 self.basedirs.append(argv_to_abspath(args[0]))
81 raise usage.UsageError("I wasn't expecting so many arguments." +
82 (self.allow_multiple and
83 " Use the --multiple option to specify more than one node directory." or ""))
85 if len(args) == 0 and self.default_nodedir and not self.basedirs:
86 self.basedirs.append(self.default_nodedir)
88 self.basedirs.append(argv_to_abspath(args[0]))
90 def postOptions(self):
92 raise usage.UsageError("A base directory for the node must be provided.")
94 self['basedirs'] = self.basedirs
97 DEFAULT_ALIAS = u"tahoe"
100 def get_aliases(nodedir):
101 from allmydata import uri
103 aliasfile = os.path.join(nodedir, "private", "aliases")
104 rootfile = os.path.join(nodedir, "private", "root_dir.cap")
106 f = open(rootfile, "r")
107 rootcap = f.read().strip()
109 aliases[DEFAULT_ALIAS] = uri.from_string_dirnode(rootcap).to_string()
110 except EnvironmentError:
113 f = codecs.open(aliasfile, "r", "utf-8")
114 for line in f.readlines():
116 if line.startswith("#") or not line:
118 name, cap = line.split(u":", 1)
119 # normalize it: remove http: prefix, urldecode
120 cap = cap.strip().encode('utf-8')
121 aliases[name] = uri.from_string_dirnode(cap).to_string()
122 except EnvironmentError:
126 class DefaultAliasMarker:
129 pretend_platform_uses_lettercolon = False # for tests
130 def platform_uses_lettercolon_drivename():
131 if ("win32" in sys.platform.lower()
132 or "cygwin" in sys.platform.lower()
133 or pretend_platform_uses_lettercolon):
138 class TahoeError(Exception):
139 def __init__(self, msg):
140 Exception.__init__(self, msg)
143 def display(self, err):
144 print >>err, self.msg
147 class UnknownAliasError(TahoeError):
148 def __init__(self, msg):
149 TahoeError.__init__(self, "error: " + msg)
152 def get_alias(aliases, path_unicode, default):
154 Transform u"work:path/filename" into (aliases[u"work"], u"path/filename".encode('utf-8')).
155 If default=None, then an empty alias is indicated by returning
156 DefaultAliasMarker. We special-case strings with a recognized cap URI
157 prefix, to make it easy to access specific files/directories by their
159 If the transformed alias is either not found in aliases, or is blank
160 and default is not found in aliases, an UnknownAliasError is
163 precondition(isinstance(path_unicode, unicode), path_unicode)
165 from allmydata import uri
166 path = path_unicode.encode('utf-8').strip(" ")
167 if uri.has_uri_prefix(path):
168 # We used to require "URI:blah:./foo" in order to get a subpath,
169 # stripping out the ":./" sequence. We still allow that for compatibility,
170 # but now also allow just "URI:blah/foo".
171 sep = path.find(":./")
173 return path[:sep], path[sep+3:]
176 return path[:sep], path[sep+1:]
178 colon = path.find(":")
182 return DefaultAliasMarker, path
183 if default not in aliases:
184 raise UnknownAliasError("No alias specified, and the default "
185 "'tahoe' alias doesn't exist. To create "
186 "it, use 'tahoe create-alias tahoe'.")
187 return aliases[default], path
188 if colon == 1 and default is None and platform_uses_lettercolon_drivename():
189 # treat C:\why\must\windows\be\so\weird as a local path, not a tahoe
190 # file in the "C:" alias
191 return DefaultAliasMarker, path
193 # decoding must succeed because path is valid UTF-8 and colon & space are ASCII
194 alias = path[:colon].decode('utf-8')
196 # no alias, but there's a colon in a dirname/filename, like
199 return DefaultAliasMarker, path
200 if default not in aliases:
201 raise UnknownAliasError("No alias specified, and the default "
202 "'tahoe' alias doesn't exist. To create "
203 "it, use 'tahoe create-alias tahoe'.")
204 return aliases[default], path
205 if alias not in aliases:
206 raise UnknownAliasError("Unknown alias %s, please create it with 'tahoe add-alias' or 'tahoe create-alias'." %
208 return aliases[alias], path[colon+1:]
210 def escape_path(path):
211 segments = path.split("/")
212 return "/".join([urllib.quote(unicode_to_url(s)) for s in segments])