]> git.rkrishnan.org Git - tahoe-lafs/tahoe-lafs.git/blob - src/allmydata/scripts/common.py
f4d81108b75d82cbd6b1742518f3b945757ea3aa
[tahoe-lafs/tahoe-lafs.git] / src / allmydata / scripts / common.py
1
2 import os, sys, urllib
3 import codecs
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
8
9 class BaseOptions:
10     # unit tests can override these to point at StringIO instances
11     stdin = sys.stdin
12     stdout = sys.stdout
13     stderr = sys.stderr
14
15     optFlags = [
16         ["quiet", "q", "Operate silently."],
17         ["version", "V", "Display version numbers and exit."],
18         ["version-and-path", None, "Display version numbers and paths to their locations and exit."],
19         ]
20
21     def opt_version(self):
22         import allmydata
23         print >>self.stdout, allmydata.get_package_versions_string()
24         sys.exit(0)
25
26     def opt_version_and_path(self):
27         import allmydata
28         print >>self.stdout, allmydata.get_package_versions_string(show_paths=True)
29         sys.exit(0)
30
31
32 class BasedirMixin:
33     optFlags = [
34         ["multiple", "m", "allow multiple basedirs to be specified at once"],
35         ]
36
37     def postOptions(self):
38         if not self.basedirs:
39             raise usage.UsageError("<basedir> parameter is required")
40         if self['basedir']:
41             del self['basedir']
42         self['basedirs'] = self.basedirs
43
44     def parseArgs(self, *args):
45         self.basedirs = []
46         if self['basedir']:
47             precondition(isinstance(self['basedir'], str), self['basedir'])
48             self.basedirs.append(argv_to_abspath(self['basedir']))
49         if self['multiple']:
50             self.basedirs.extend(map(argv_to_abspath, args))
51         else:
52             if len(args) == 0 and not self.basedirs:
53                 if sys.platform == 'win32':
54                     from allmydata.windows import registry
55                     rbdp = registry.get_base_dir_path()
56                     if rbdp:
57                         precondition(isinstance(registry.get_base_dir_path(), unicode), registry.get_base_dir_path())
58                         self.basedirs.append(rbdp)
59                 else:
60                     self.basedirs.append(abspath_expanduser_unicode(u"~/.tahoe"))
61             if len(args) > 0:
62                 self.basedirs.append(argv_to_abspath(args[0]))
63             if len(args) > 1:
64                 raise usage.UsageError("I wasn't expecting so many arguments")
65
66 class NoDefaultBasedirMixin(BasedirMixin):
67     def parseArgs(self, *args):
68         # create-client won't default to --basedir=~/.tahoe
69         self.basedirs = []
70         if self['basedir']:
71             self.basedirs.append(argv_to_abspath(self['basedir']))
72         if self['multiple']:
73             self.basedirs.extend(map(argv_to_abspath, args))
74         else:
75             if len(args) > 0:
76                 self.basedirs.append(argv_to_abspath(args[0]))
77             if len(args) > 1:
78                 raise usage.UsageError("I wasn't expecting so many arguments")
79         if not self.basedirs:
80             raise usage.UsageError("--basedir must be provided")
81
82 DEFAULT_ALIAS = u"tahoe"
83
84
85 def get_aliases(nodedir):
86     from allmydata import uri
87     aliases = {}
88     aliasfile = os.path.join(nodedir, "private", "aliases")
89     rootfile = os.path.join(nodedir, "private", "root_dir.cap")
90     try:
91         f = open(rootfile, "r")
92         rootcap = f.read().strip()
93         if rootcap:
94             aliases[u"tahoe"] = uri.from_string_dirnode(rootcap).to_string()
95     except EnvironmentError:
96         pass
97     try:
98         f = codecs.open(aliasfile, "r", "utf-8")
99         for line in f.readlines():
100             line = line.strip()
101             if line.startswith("#") or not line:
102                 continue
103             name, cap = line.split(u":", 1)
104             # normalize it: remove http: prefix, urldecode
105             cap = cap.strip().encode('utf-8')
106             aliases[name] = uri.from_string_dirnode(cap).to_string()
107     except EnvironmentError:
108         pass
109     return aliases
110
111 class DefaultAliasMarker:
112     pass
113
114 pretend_platform_uses_lettercolon = False # for tests
115 def platform_uses_lettercolon_drivename():
116     if ("win32" in sys.platform.lower()
117         or "cygwin" in sys.platform.lower()
118         or pretend_platform_uses_lettercolon):
119         return True
120     return False
121
122
123 class TahoeError(Exception):
124     def __init__(self, msg):
125         Exception.__init__(self, msg)
126         self.msg = msg
127
128     def display(self, err):
129         print >>err, self.msg
130
131
132 class UnknownAliasError(TahoeError):
133     def __init__(self, msg):
134         TahoeError.__init__(self, "error: " + msg)
135
136
137 def get_alias(aliases, path_unicode, default):
138     """
139     Transform u"work:path/filename" into (aliases[u"work"], u"path/filename".encode('utf-8')).
140     If default=None, then an empty alias is indicated by returning
141     DefaultAliasMarker. We special-case strings with a recognized cap URI
142     prefix, to make it easy to access specific files/directories by their
143     caps.
144     If the transformed alias is either not found in aliases, or is blank
145     and default is not found in aliases, an UnknownAliasError is
146     raised.
147     """
148     precondition(isinstance(path_unicode, unicode), path_unicode)
149
150     from allmydata import uri
151     path = path_unicode.encode('utf-8').strip(" ")
152     if uri.has_uri_prefix(path):
153         # We used to require "URI:blah:./foo" in order to get a subpath,
154         # stripping out the ":./" sequence. We still allow that for compatibility,
155         # but now also allow just "URI:blah/foo".
156         sep = path.find(":./")
157         if sep != -1:
158             return path[:sep], path[sep+3:]
159         sep = path.find("/")
160         if sep != -1:
161             return path[:sep], path[sep+1:]
162         return path, ""
163     colon = path.find(":")
164     if colon == -1:
165         # no alias
166         if default == None:
167             return DefaultAliasMarker, path
168         if default not in aliases:
169             raise UnknownAliasError("No alias specified, and the default "
170                                     "'tahoe' alias doesn't exist. To create "
171                                     "it, use 'tahoe create-alias tahoe'.")
172         return aliases[default], path
173     if colon == 1 and default is None and platform_uses_lettercolon_drivename():
174         # treat C:\why\must\windows\be\so\weird as a local path, not a tahoe
175         # file in the "C:" alias
176         return DefaultAliasMarker, path
177
178     # decoding must succeed because path is valid UTF-8 and colon & space are ASCII
179     alias = path[:colon].decode('utf-8')
180     if u"/" in alias:
181         # no alias, but there's a colon in a dirname/filename, like
182         # "foo/bar:7"
183         if default == None:
184             return DefaultAliasMarker, path
185         if default not in aliases:
186             raise UnknownAliasError("No alias specified, and the default "
187                                     "'tahoe' alias doesn't exist. To create "
188                                     "it, use 'tahoe create-alias tahoe'.")
189         return aliases[default], path
190     if alias not in aliases:
191         raise UnknownAliasError("Unknown alias %s, please create it with 'tahoe add-alias' or 'tahoe create-alias'." %
192                                 quote_output(alias))
193     return aliases[alias], path[colon+1:]
194
195 def escape_path(path):
196     segments = path.split("/")
197     return "/".join([urllib.quote(unicode_to_url(s)) for s in segments])