4 from twisted.python import usage
5 from allmydata.util.assertutil import precondition
6 from allmydata.util.encodingutil import unicode_to_url, quote_output, \
7 quote_local_unicode_path, argv_to_abspath
8 from allmydata.util.fileutil import abspath_expanduser_unicode
11 _default_nodedir = None
12 if sys.platform == 'win32':
13 from allmydata.windows import registry
14 path = registry.get_base_dir_path()
16 precondition(isinstance(path, unicode), path)
17 _default_nodedir = abspath_expanduser_unicode(path)
19 if _default_nodedir is None:
20 path = abspath_expanduser_unicode(u"~/.tahoe")
21 precondition(isinstance(path, unicode), path)
22 _default_nodedir = path
24 def get_default_nodedir():
25 return _default_nodedir
28 class BaseOptions(usage.Options):
30 super(BaseOptions, self).__init__()
31 self.command_name = os.path.basename(sys.argv[0])
32 if self.command_name == 'trial':
33 self.command_name = 'tahoe'
35 # Only allow "tahoe --version", not e.g. "tahoe start --version"
36 def opt_version(self):
37 raise usage.UsageError("--version not allowed on subcommands")
39 class BasedirOptions(BaseOptions):
40 default_nodedir = _default_nodedir
43 ["basedir", "C", None, "Specify which Tahoe base directory should be used. [default: %s]"
44 % quote_local_unicode_path(_default_nodedir)],
47 def parseArgs(self, basedir=None):
48 if self.parent['node-directory'] and self['basedir']:
49 raise usage.UsageError("The --node-directory (or -d) and --basedir (or -C) options cannot both be used.")
50 if self.parent['node-directory'] and basedir:
51 raise usage.UsageError("The --node-directory (or -d) option and a basedir argument cannot both be used.")
52 if self['basedir'] and basedir:
53 raise usage.UsageError("The --basedir (or -C) option and a basedir argument cannot both be used.")
56 b = argv_to_abspath(basedir)
58 b = argv_to_abspath(self['basedir'])
59 elif self.parent['node-directory']:
60 b = argv_to_abspath(self.parent['node-directory'])
61 elif self.default_nodedir:
62 b = self.default_nodedir
64 raise usage.UsageError("No default basedir available, you must provide one with --node-directory, --basedir, or a basedir argument")
67 def postOptions(self):
68 if not self['basedir']:
69 raise usage.UsageError("A base directory for the node must be provided.")
71 class NoDefaultBasedirOptions(BasedirOptions):
72 default_nodedir = None
75 ["basedir", "C", None, "Specify which Tahoe base directory should be used."],
78 # This is overridden in order to ensure we get a "Wrong number of arguments."
79 # error when more than one argument is given.
80 def parseArgs(self, basedir=None):
81 BasedirOptions.parseArgs(self, basedir)
83 def getSynopsis(self):
84 return "Usage: %s [global-opts] %s [options] NODEDIR" % (self.command_name, self.subcommand_name)
87 DEFAULT_ALIAS = u"tahoe"
90 def get_aliases(nodedir):
92 aliasfile = os.path.join(nodedir, "private", "aliases")
93 rootfile = os.path.join(nodedir, "private", "root_dir.cap")
95 f = open(rootfile, "r")
96 rootcap = f.read().strip()
98 aliases[DEFAULT_ALIAS] = rootcap
99 except EnvironmentError:
102 f = codecs.open(aliasfile, "r", "utf-8")
103 for line in f.readlines():
105 if line.startswith("#") or not line:
107 name, cap = line.split(u":", 1)
108 # normalize it: remove http: prefix, urldecode
109 cap = cap.strip().encode('utf-8')
111 except EnvironmentError:
115 class DefaultAliasMarker:
118 pretend_platform_uses_lettercolon = False # for tests
119 def platform_uses_lettercolon_drivename():
120 if ("win32" in sys.platform.lower()
121 or "cygwin" in sys.platform.lower()
122 or pretend_platform_uses_lettercolon):
127 class TahoeError(Exception):
128 def __init__(self, msg):
129 Exception.__init__(self, msg)
132 def display(self, err):
133 print >>err, self.msg
136 class UnknownAliasError(TahoeError):
137 def __init__(self, msg):
138 TahoeError.__init__(self, "error: " + msg)
141 def get_alias(aliases, path_unicode, default):
143 Transform u"work:path/filename" into (aliases[u"work"], u"path/filename".encode('utf-8')).
144 If default=None, then an empty alias is indicated by returning
145 DefaultAliasMarker. We special-case strings with a recognized cap URI
146 prefix, to make it easy to access specific files/directories by their
148 If the transformed alias is either not found in aliases, or is blank
149 and default is not found in aliases, an UnknownAliasError is
152 precondition(isinstance(path_unicode, unicode), path_unicode)
154 from allmydata import uri
155 path = path_unicode.encode('utf-8').strip(" ")
156 if uri.has_uri_prefix(path):
157 # We used to require "URI:blah:./foo" in order to get a subpath,
158 # stripping out the ":./" sequence. We still allow that for compatibility,
159 # but now also allow just "URI:blah/foo".
160 sep = path.find(":./")
162 return path[:sep], path[sep+3:]
165 return path[:sep], path[sep+1:]
167 colon = path.find(":")
171 return DefaultAliasMarker, path
172 if default not in aliases:
173 raise UnknownAliasError("No alias specified, and the default %s alias doesn't exist. "
174 "To create it, use 'tahoe create-alias %s'."
175 % (quote_output(default), quote_output(default, quotemarks=False)))
176 return uri.from_string_dirnode(aliases[default]).to_string(), path
177 if colon == 1 and default is None and platform_uses_lettercolon_drivename():
178 # treat C:\why\must\windows\be\so\weird as a local path, not a tahoe
179 # file in the "C:" alias
180 return DefaultAliasMarker, path
182 # decoding must succeed because path is valid UTF-8 and colon & space are ASCII
183 alias = path[:colon].decode('utf-8')
185 # no alias, but there's a colon in a dirname/filename, like
188 return DefaultAliasMarker, path
189 if default not in aliases:
190 raise UnknownAliasError("No alias specified, and the default %s alias doesn't exist. "
191 "To create it, use 'tahoe create-alias %s'."
192 % (quote_output(default), quote_output(default, quotemarks=False)))
193 return uri.from_string_dirnode(aliases[default]).to_string(), path
194 if alias not in aliases:
195 raise UnknownAliasError("Unknown alias %s, please create it with 'tahoe add-alias' or 'tahoe create-alias'." %
197 return uri.from_string_dirnode(aliases[alias]).to_string(), path[colon+1:]
199 def escape_path(path):
200 # this always returns bytes, specifically US-ASCII, valid URL characters
201 segments = path.split("/")
202 return "/".join([urllib.quote(unicode_to_url(s)) for s in segments])