]> git.rkrishnan.org Git - tahoe-lafs/tahoe-lafs.git/blob - src/allmydata/scripts/magic_folder_cli.py
Fix call to argv_to_abspath. Also rename localdir to local_dir.
[tahoe-lafs/tahoe-lafs.git] / src / allmydata / scripts / magic_folder_cli.py
1
2 import os
3 from cStringIO import StringIO
4 from twisted.python import usage
5
6 from .common import BaseOptions, BasedirOptions, get_aliases
7 from .cli import MakeDirectoryOptions, LnOptions, CreateAliasOptions
8 import tahoe_mv
9 from allmydata.util.encodingutil import argv_to_abspath
10 from allmydata.util import fileutil
11 from allmydata import uri
12
13 INVITE_SEPARATOR = "+"
14
15 class CreateOptions(BasedirOptions):
16     nickname = None
17     local_dir = None
18     synopsis = "MAGIC_ALIAS: [NICKNAME LOCAL_DIR]"
19     def parseArgs(self, alias, nickname=None, local_dir=None):
20         BasedirOptions.parseArgs(self)
21         if not alias.endswith(':'):
22             raise usage.UsageError("An alias must end with a ':' character.")
23         self.alias = alias[:-1]
24         self.nickname = nickname
25         self.local_dir = None if local_dir is None else argv_to_abspath(local_dir)
26         if self.nickname and not self.local_dir:
27             raise usage.UsageError("If NICKNAME is specified then LOCAL_DIR must also be specified.")
28         node_url_file = os.path.join(self['node-directory'], "node.url")
29         self['node-url'] = fileutil.read(node_url_file).strip()
30
31 def _delegate_options(source_options, target_options):
32     target_options.aliases = get_aliases(source_options['node-directory'])
33     target_options["node-url"] = source_options["node-url"]
34     target_options["node-directory"] = source_options["node-directory"]
35     target_options.stdin = StringIO("")
36     target_options.stdout = StringIO()
37     target_options.stderr = StringIO()
38     return target_options
39
40 def create(options):
41     from allmydata.scripts import tahoe_add_alias
42     create_alias_options = _delegate_options(options, CreateAliasOptions())
43     create_alias_options.alias = options.alias
44
45     rc = tahoe_add_alias.create_alias(create_alias_options)
46     if rc != 0:
47         print >>options.stderr, create_alias_options.stderr.getvalue()
48         return rc
49     print >>options.stdout, create_alias_options.stdout.getvalue()
50
51     if options.nickname is not None:
52         invite_options = _delegate_options(options, InviteOptions())
53         invite_options.alias = options.alias
54         invite_options.nickname = options.nickname
55         rc = invite(invite_options)
56         if rc != 0:
57             print >>options.stderr, "magic-folder: failed to invite after create\n"
58             print >>options.stderr, invite_options.stderr.getvalue()
59             return rc
60         invite_code = invite_options.stdout.getvalue().strip()
61         join_options = _delegate_options(options, JoinOptions())
62         join_options.invite_code = invite_code
63         fields = invite_code.split(INVITE_SEPARATOR)
64         if len(fields) != 2:
65             raise usage.UsageError("Invalid invite code.")
66         join_options.magic_readonly_cap, join_options.dmd_write_cap = fields
67         join_options.local_dir = options.local_dir
68         rc = join(join_options)
69         if rc != 0:
70             print >>options.stderr, "magic-folder: failed to join after create\n"
71             print >>options.stderr, join_options.stderr.getvalue()
72             return rc
73     return 0
74
75 class InviteOptions(BasedirOptions):
76     nickname = None
77     synopsis = "MAGIC_ALIAS: NICKNAME"
78     stdin = StringIO("")
79     def parseArgs(self, alias, nickname=None):
80         BasedirOptions.parseArgs(self)
81         if not alias.endswith(':'):
82             raise usage.UsageError("An alias must end with a ':' character.")
83         self.alias = alias[:-1]
84         self.nickname = nickname
85         node_url_file = os.path.join(self['node-directory'], "node.url")
86         self['node-url'] = open(node_url_file, "r").read().strip()
87         aliases = get_aliases(self['node-directory'])
88         self.aliases = aliases
89
90 def invite(options):
91     from allmydata.scripts import tahoe_mkdir
92     mkdir_options = _delegate_options(options, MakeDirectoryOptions())
93     mkdir_options.where = None
94
95     rc = tahoe_mkdir.mkdir(mkdir_options)
96     if rc != 0:
97         print >>options.stderr, "magic-folder: failed to mkdir\n"
98         return rc
99     dmd_write_cap = mkdir_options.stdout.getvalue().strip()
100     dmd_readonly_cap = unicode(uri.from_string(dmd_write_cap).get_readonly().to_string(), 'utf-8')
101     if dmd_readonly_cap is None:
102         print >>options.stderr, "magic-folder: failed to diminish dmd write cap\n"
103         return 1
104
105     magic_write_cap = get_aliases(options["node-directory"])[options.alias]
106     magic_readonly_cap = unicode(uri.from_string(magic_write_cap).get_readonly().to_string(), 'utf-8')
107     # tahoe ln CLIENT_READCAP COLLECTIVE_WRITECAP/NICKNAME
108     ln_options = _delegate_options(options, LnOptions())
109     ln_options.from_file = dmd_readonly_cap
110     ln_options.to_file = u"%s/%s" % (magic_write_cap, options.nickname)
111     rc = tahoe_mv.mv(ln_options, mode="link")
112     if rc != 0:
113         print >>options.stderr, "magic-folder: failed to create link\n"
114         print >>options.stderr, ln_options.stderr.getvalue()
115         return rc
116
117     print >>options.stdout, "%s%s%s" % (magic_readonly_cap, INVITE_SEPARATOR, dmd_write_cap)
118     return 0
119
120 class JoinOptions(BasedirOptions):
121     synopsis = "INVITE_CODE LOCAL_DIR"
122     dmd_write_cap = ""
123     magic_readonly_cap = ""
124     def parseInvite(self, invite_code, local_dir):
125         self.local_dir = None if local_dir is None else argv_to_abspath(local_dir)
126         fields = invite_code.split(INVITE_SEPARATOR)
127         if len(fields) != 2:
128             raise usage.UsageError("Invalid invite code.")
129         self.magic_readonly_cap, self.dmd_write_cap = fields
130     def parseArgs(self, invite_code, local_dir):
131         BasedirOptions.parseArgs(self)
132         self.parseInvite(invite_code, local_dir)
133
134 def join(options):
135     dmd_cap_file = os.path.join(options["node-directory"], "private/magic_folder_dircap")
136     collective_readcap_file = os.path.join(options["node-directory"], "private/collective_dircap")
137
138     fileutil.write(dmd_cap_file, options.dmd_write_cap)
139     fileutil.write(collective_readcap_file, options.magic_readonly_cap)
140     fileutil.write(os.path.join(options["node-directory"], "tahoe.cfg"),
141                    "[magic_folder]\nenabled = True\nlocal.directory = %s\n"
142                    % (options.local_dir.encode('utf-8'),), mode="ab")
143     return 0
144
145 class MagicFolderCommand(BaseOptions):
146     subCommands = [
147         ["create", None, CreateOptions, "Create a Magic Folder."],
148         ["invite", None, InviteOptions, "Invite someone to a Magic Folder."],
149         ["join", None, JoinOptions, "Join a Magic Folder."],
150     ]
151     def postOptions(self):
152         if not hasattr(self, 'subOptions'):
153             raise usage.UsageError("must specify a subcommand")
154     def getSynopsis(self):
155         return "Usage: tahoe [global-options] magic SUBCOMMAND"
156     def getUsage(self, width=None):
157         t = BaseOptions.getUsage(self, width)
158         t += """\
159 Please run e.g. 'tahoe magic-folder create --help' for more details on each
160 subcommand.
161 """
162         return t
163
164 subDispatch = {
165     "create": create,
166     "invite": invite,
167     "join": join,
168 }
169
170 def do_magic_folder(options):
171     so = options.subOptions
172     so.stdout = options.stdout
173     so.stderr = options.stderr
174     f = subDispatch[options.subCommand]
175     return f(so)
176
177 subCommands = [
178     ["magic-folder", None, MagicFolderCommand,
179      "Magic Folder subcommands: use 'tahoe magic-folder' for a list."],
180 ]
181
182 dispatch = {
183     "magic-folder": do_magic_folder,
184 }