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):
29 super(BaseOptions, self).__init__()
30 self.command_name = os.path.basename(sys.argv[0])
31 if self.command_name == 'trial':
32 self.command_name = 'tahoe'
34 # Only allow "tahoe --version", not e.g. "tahoe start --version"
35 def opt_version(self):
36 raise usage.UsageError("--version not allowed on subcommands")
38 class BasedirOptions(BaseOptions):
39 default_nodedir = _default_nodedir
42 ["basedir", "C", None, "Same as --node-directory (default %s)."
43 % get_default_nodedir()],
46 def parseArgs(self, basedir=None):
47 if self.parent['node-directory'] and self['basedir']:
48 raise usage.UsageError("The --node-directory (or -d) and --basedir (or -C) options cannot both be used.")
49 if self.parent['node-directory'] and basedir:
50 raise usage.UsageError("The --node-directory (or -d) option and a basedir argument cannot both be used.")
51 if self['basedir'] and basedir:
52 raise usage.UsageError("The --basedir (or -C) option and a basedir argument cannot both be used.")
55 b = argv_to_abspath(basedir)
57 b = argv_to_abspath(self['basedir'])
58 elif self.parent['node-directory']:
59 b = argv_to_abspath(self.parent['node-directory'])
60 elif self.default_nodedir:
61 b = self.default_nodedir
63 raise usage.UsageError("No default basedir available, you must provide one with --node-directory, --basedir, or a basedir argument")
66 def postOptions(self):
67 if not self['basedir']:
68 raise usage.UsageError("A base directory for the node must be provided.")
71 DEFAULT_ALIAS = u"tahoe"
74 def get_aliases(nodedir):
75 from allmydata import uri
77 aliasfile = os.path.join(nodedir, "private", "aliases")
78 rootfile = os.path.join(nodedir, "private", "root_dir.cap")
80 f = open(rootfile, "r")
81 rootcap = f.read().strip()
83 aliases[DEFAULT_ALIAS] = uri.from_string_dirnode(rootcap).to_string()
84 except EnvironmentError:
87 f = codecs.open(aliasfile, "r", "utf-8")
88 for line in f.readlines():
90 if line.startswith("#") or not line:
92 name, cap = line.split(u":", 1)
93 # normalize it: remove http: prefix, urldecode
94 cap = cap.strip().encode('utf-8')
95 aliases[name] = uri.from_string_dirnode(cap).to_string()
96 except EnvironmentError:
100 class DefaultAliasMarker:
103 pretend_platform_uses_lettercolon = False # for tests
104 def platform_uses_lettercolon_drivename():
105 if ("win32" in sys.platform.lower()
106 or "cygwin" in sys.platform.lower()
107 or pretend_platform_uses_lettercolon):
112 class TahoeError(Exception):
113 def __init__(self, msg):
114 Exception.__init__(self, msg)
117 def display(self, err):
118 print >>err, self.msg
121 class UnknownAliasError(TahoeError):
122 def __init__(self, msg):
123 TahoeError.__init__(self, "error: " + msg)
126 def get_alias(aliases, path_unicode, default):
128 Transform u"work:path/filename" into (aliases[u"work"], u"path/filename".encode('utf-8')).
129 If default=None, then an empty alias is indicated by returning
130 DefaultAliasMarker. We special-case strings with a recognized cap URI
131 prefix, to make it easy to access specific files/directories by their
133 If the transformed alias is either not found in aliases, or is blank
134 and default is not found in aliases, an UnknownAliasError is
137 precondition(isinstance(path_unicode, unicode), path_unicode)
139 from allmydata import uri
140 path = path_unicode.encode('utf-8').strip(" ")
141 if uri.has_uri_prefix(path):
142 # We used to require "URI:blah:./foo" in order to get a subpath,
143 # stripping out the ":./" sequence. We still allow that for compatibility,
144 # but now also allow just "URI:blah/foo".
145 sep = path.find(":./")
147 return path[:sep], path[sep+3:]
150 return path[:sep], path[sep+1:]
152 colon = path.find(":")
156 return DefaultAliasMarker, path
157 if default not in aliases:
158 raise UnknownAliasError("No alias specified, and the default %s alias doesn't exist. "
159 "To create it, use 'tahoe create-alias %s'."
160 % (quote_output(default), quote_output(default, quotemarks=False)))
161 return aliases[default], path
162 if colon == 1 and default is None and platform_uses_lettercolon_drivename():
163 # treat C:\why\must\windows\be\so\weird as a local path, not a tahoe
164 # file in the "C:" alias
165 return DefaultAliasMarker, path
167 # decoding must succeed because path is valid UTF-8 and colon & space are ASCII
168 alias = path[:colon].decode('utf-8')
170 # no alias, but there's a colon in a dirname/filename, like
173 return DefaultAliasMarker, path
174 if default not in aliases:
175 raise UnknownAliasError("No alias specified, and the default %s alias doesn't exist. "
176 "To create it, use 'tahoe create-alias %s'."
177 % (quote_output(default), quote_output(default, quotemarks=False)))
178 return aliases[default], path
179 if alias not in aliases:
180 raise UnknownAliasError("Unknown alias %s, please create it with 'tahoe add-alias' or 'tahoe create-alias'." %
182 return aliases[alias], path[colon+1:]
184 def escape_path(path):
185 segments = path.split("/")
186 return "/".join([urllib.quote(unicode_to_url(s)) for s in segments])