comparison sat_frontends/jp/cmd_file.py @ 2624:56f94936df1e

code style reformatting using black
author Goffi <goffi@goffi.org>
date Wed, 27 Jun 2018 20:14:46 +0200
parents c0401a72cbb4
children 003b8b4b56a7
comparison
equal deleted inserted replaced
2623:49533de4540b 2624:56f94936df1e
27 from sat_frontends.jp.constants import Const as C 27 from sat_frontends.jp.constants import Const as C
28 from sat_frontends.jp import common 28 from sat_frontends.jp import common
29 from sat_frontends.tools import jid 29 from sat_frontends.tools import jid
30 from sat.tools.common.ansi import ANSI as A 30 from sat.tools.common.ansi import ANSI as A
31 import tempfile 31 import tempfile
32 import xml.etree.ElementTree as ET # FIXME: used temporarily to manage XMLUI 32 import xml.etree.ElementTree as ET # FIXME: used temporarily to manage XMLUI
33 from functools import partial 33 from functools import partial
34 import json 34 import json
35 35
36 __commands__ = ["File"] 36 __commands__ = ["File"]
37 37
38 38
39 class Send(base.CommandBase): 39 class Send(base.CommandBase):
40 def __init__(self, host): 40 def __init__(self, host):
41 super(Send, self).__init__(host, 'send', use_progress=True, use_verbose=True, help=_('send a file to a contact')) 41 super(Send, self).__init__(
42 self.need_loop=True 42 host,
43 "send",
44 use_progress=True,
45 use_verbose=True,
46 help=_("send a file to a contact"),
47 )
48 self.need_loop = True
43 49
44 def add_parser_options(self): 50 def add_parser_options(self):
45 self.parser.add_argument("files", type=str, nargs='+', metavar='file', help=_(u"a list of file")) 51 self.parser.add_argument(
46 self.parser.add_argument("jid", type=base.unicode_decoder, help=_(u"the destination jid")) 52 "files", type=str, nargs="+", metavar="file", help=_(u"a list of file")
47 self.parser.add_argument("-b", "--bz2", action="store_true", help=_(u"make a bzip2 tarball")) 53 )
48 self.parser.add_argument("-d", "--path", type=base.unicode_decoder, help=(u"path to the directory where the file must be stored")) 54 self.parser.add_argument(
49 self.parser.add_argument("-N", "--namespace", type=base.unicode_decoder, help=(u"namespace of the file")) 55 "jid", type=base.unicode_decoder, help=_(u"the destination jid")
50 self.parser.add_argument("-n", "--name", type=base.unicode_decoder, default=u'', help=(u"name to use (DEFAULT: use source file name)")) 56 )
57 self.parser.add_argument(
58 "-b", "--bz2", action="store_true", help=_(u"make a bzip2 tarball")
59 )
60 self.parser.add_argument(
61 "-d",
62 "--path",
63 type=base.unicode_decoder,
64 help=(u"path to the directory where the file must be stored"),
65 )
66 self.parser.add_argument(
67 "-N",
68 "--namespace",
69 type=base.unicode_decoder,
70 help=(u"namespace of the file"),
71 )
72 self.parser.add_argument(
73 "-n",
74 "--name",
75 type=base.unicode_decoder,
76 default=u"",
77 help=(u"name to use (DEFAULT: use source file name)"),
78 )
51 79
52 def start(self): 80 def start(self):
53 """Send files to jabber contact""" 81 """Send files to jabber contact"""
54 self.send_files() 82 self.send_files()
55 83
56 def onProgressStarted(self, metadata): 84 def onProgressStarted(self, metadata):
57 self.disp(_(u'File copy started'),2) 85 self.disp(_(u"File copy started"), 2)
58 86
59 def onProgressFinished(self, metadata): 87 def onProgressFinished(self, metadata):
60 self.disp(_(u'File sent successfully'),2) 88 self.disp(_(u"File sent successfully"), 2)
61 89
62 def onProgressError(self, error_msg): 90 def onProgressError(self, error_msg):
63 if error_msg == C.PROGRESS_ERROR_DECLINED: 91 if error_msg == C.PROGRESS_ERROR_DECLINED:
64 self.disp(_(u'The file has been refused by your contact')) 92 self.disp(_(u"The file has been refused by your contact"))
65 else: 93 else:
66 self.disp(_(u'Error while sending file: {}').format(error_msg),error=True) 94 self.disp(_(u"Error while sending file: {}").format(error_msg), error=True)
67 95
68 def gotId(self, data, file_): 96 def gotId(self, data, file_):
69 """Called when a progress id has been received 97 """Called when a progress id has been received
70 98
71 @param pid(unicode): progress id 99 @param pid(unicode): progress id
72 @param file_(str): file path 100 @param file_(str): file path
73 """ 101 """
74 #FIXME: this show progress only for last progress_id 102 # FIXME: this show progress only for last progress_id
75 self.disp(_(u"File request sent to {jid}".format(jid=self.full_dest_jid)), 1) 103 self.disp(_(u"File request sent to {jid}".format(jid=self.full_dest_jid)), 1)
76 try: 104 try:
77 self.progress_id = data['progress'] 105 self.progress_id = data["progress"]
78 except KeyError: 106 except KeyError:
79 # TODO: if 'xmlui' key is present, manage xmlui message display 107 # TODO: if 'xmlui' key is present, manage xmlui message display
80 self.disp(_(u"Can't send file to {jid}".format(jid=self.full_dest_jid)), error=True) 108 self.disp(
109 _(u"Can't send file to {jid}".format(jid=self.full_dest_jid)), error=True
110 )
81 self.host.quit(2) 111 self.host.quit(2)
82 112
83 def error(self, failure): 113 def error(self, failure):
84 self.disp(_("Error while trying to send a file: {reason}").format(reason=failure), error=True) 114 self.disp(
115 _("Error while trying to send a file: {reason}").format(reason=failure),
116 error=True,
117 )
85 self.host.quit(1) 118 self.host.quit(1)
86 119
87 def send_files(self): 120 def send_files(self):
88 for file_ in self.args.files: 121 for file_ in self.args.files:
89 if not os.path.exists(file_): 122 if not os.path.exists(file_):
90 self.disp(_(u"file [{}] doesn't exist !").format(file_), error=True) 123 self.disp(_(u"file [{}] doesn't exist !").format(file_), error=True)
91 self.host.quit(1) 124 self.host.quit(1)
92 if not self.args.bz2 and os.path.isdir(file_): 125 if not self.args.bz2 and os.path.isdir(file_):
93 self.disp(_(u"[{}] is a dir ! Please send files inside or use compression").format(file_)) 126 self.disp(
127 _(
128 u"[{}] is a dir ! Please send files inside or use compression"
129 ).format(file_)
130 )
94 self.host.quit(1) 131 self.host.quit(1)
95 132
96 self.full_dest_jid = self.host.get_full_jid(self.args.jid) 133 self.full_dest_jid = self.host.get_full_jid(self.args.jid)
97 extra = {} 134 extra = {}
98 if self.args.path: 135 if self.args.path:
99 extra[u'path'] = self.args.path 136 extra[u"path"] = self.args.path
100 if self.args.namespace: 137 if self.args.namespace:
101 extra[u'namespace'] = self.args.namespace 138 extra[u"namespace"] = self.args.namespace
102 139
103 if self.args.bz2: 140 if self.args.bz2:
104 with tempfile.NamedTemporaryFile('wb', delete=False) as buf: 141 with tempfile.NamedTemporaryFile("wb", delete=False) as buf:
105 self.host.addOnQuitCallback(os.unlink, buf.name) 142 self.host.addOnQuitCallback(os.unlink, buf.name)
106 self.disp(_(u"bz2 is an experimental option, use with caution")) 143 self.disp(_(u"bz2 is an experimental option, use with caution"))
107 #FIXME: check free space 144 # FIXME: check free space
108 self.disp(_(u"Starting compression, please wait...")) 145 self.disp(_(u"Starting compression, please wait..."))
109 sys.stdout.flush() 146 sys.stdout.flush()
110 bz2 = tarfile.open(mode="w:bz2", fileobj=buf) 147 bz2 = tarfile.open(mode="w:bz2", fileobj=buf)
111 archive_name = u'{}.tar.bz2'.format(os.path.basename(self.args.files[0]) or u'compressed_files') 148 archive_name = u"{}.tar.bz2".format(
149 os.path.basename(self.args.files[0]) or u"compressed_files"
150 )
112 for file_ in self.args.files: 151 for file_ in self.args.files:
113 self.disp(_(u"Adding {}").format(file_), 1) 152 self.disp(_(u"Adding {}").format(file_), 1)
114 bz2.add(file_) 153 bz2.add(file_)
115 bz2.close() 154 bz2.close()
116 self.disp(_(u"Done !"), 1) 155 self.disp(_(u"Done !"), 1)
117 156
118 self.host.bridge.fileSend(self.full_dest_jid, buf.name, self.args.name or archive_name, '', extra, self.profile, 157 self.host.bridge.fileSend(
119 callback=lambda pid, file_=buf.name: self.gotId(pid, file_), errback=self.error) 158 self.full_dest_jid,
159 buf.name,
160 self.args.name or archive_name,
161 "",
162 extra,
163 self.profile,
164 callback=lambda pid, file_=buf.name: self.gotId(pid, file_),
165 errback=self.error,
166 )
120 else: 167 else:
121 for file_ in self.args.files: 168 for file_ in self.args.files:
122 path = os.path.abspath(file_) 169 path = os.path.abspath(file_)
123 self.host.bridge.fileSend(self.full_dest_jid, path, self.args.name, '', extra, self.profile, 170 self.host.bridge.fileSend(
124 callback=lambda pid, file_=file_: self.gotId(pid, file_), errback=self.error) 171 self.full_dest_jid,
172 path,
173 self.args.name,
174 "",
175 extra,
176 self.profile,
177 callback=lambda pid, file_=file_: self.gotId(pid, file_),
178 errback=self.error,
179 )
125 180
126 181
127 class Request(base.CommandBase): 182 class Request(base.CommandBase):
128
129 def __init__(self, host): 183 def __init__(self, host):
130 super(Request, self).__init__(host, 'request', use_progress=True, use_verbose=True, help=_('request a file from a contact')) 184 super(Request, self).__init__(
131 self.need_loop=True 185 host,
186 "request",
187 use_progress=True,
188 use_verbose=True,
189 help=_("request a file from a contact"),
190 )
191 self.need_loop = True
132 192
133 @property 193 @property
134 def filename(self): 194 def filename(self):
135 return self.args.name or self.args.hash or u"output" 195 return self.args.name or self.args.hash or u"output"
136 196
137 def add_parser_options(self): 197 def add_parser_options(self):
138 self.parser.add_argument("jid", type=base.unicode_decoder, help=_(u"the destination jid")) 198 self.parser.add_argument(
139 self.parser.add_argument("-D", "--dest", type=base.unicode_decoder, help=_(u"destination path where the file will be saved (default: [current_dir]/[name|hash])")) 199 "jid", type=base.unicode_decoder, help=_(u"the destination jid")
140 self.parser.add_argument("-n", "--name", type=base.unicode_decoder, default=u'', help=_(u"name of the file")) 200 )
141 self.parser.add_argument("-H", "--hash", type=base.unicode_decoder, default=u'', help=_(u"hash of the file")) 201 self.parser.add_argument(
142 self.parser.add_argument("-a", "--hash-algo", type=base.unicode_decoder, default=u'sha-256', help=_(u"hash algorithm use for --hash (default: sha-256)")) 202 "-D",
143 self.parser.add_argument("-d", "--path", type=base.unicode_decoder, help=(u"path to the directory containing the file")) 203 "--dest",
144 self.parser.add_argument("-N", "--namespace", type=base.unicode_decoder, help=(u"namespace of the file")) 204 type=base.unicode_decoder,
145 self.parser.add_argument("-f", "--force", action='store_true', help=_(u"overwrite existing file without confirmation")) 205 help=_(
206 u"destination path where the file will be saved (default: [current_dir]/[name|hash])"
207 ),
208 )
209 self.parser.add_argument(
210 "-n",
211 "--name",
212 type=base.unicode_decoder,
213 default=u"",
214 help=_(u"name of the file"),
215 )
216 self.parser.add_argument(
217 "-H",
218 "--hash",
219 type=base.unicode_decoder,
220 default=u"",
221 help=_(u"hash of the file"),
222 )
223 self.parser.add_argument(
224 "-a",
225 "--hash-algo",
226 type=base.unicode_decoder,
227 default=u"sha-256",
228 help=_(u"hash algorithm use for --hash (default: sha-256)"),
229 )
230 self.parser.add_argument(
231 "-d",
232 "--path",
233 type=base.unicode_decoder,
234 help=(u"path to the directory containing the file"),
235 )
236 self.parser.add_argument(
237 "-N",
238 "--namespace",
239 type=base.unicode_decoder,
240 help=(u"namespace of the file"),
241 )
242 self.parser.add_argument(
243 "-f",
244 "--force",
245 action="store_true",
246 help=_(u"overwrite existing file without confirmation"),
247 )
146 248
147 def onProgressStarted(self, metadata): 249 def onProgressStarted(self, metadata):
148 self.disp(_(u'File copy started'),2) 250 self.disp(_(u"File copy started"), 2)
149 251
150 def onProgressFinished(self, metadata): 252 def onProgressFinished(self, metadata):
151 self.disp(_(u'File received successfully'),2) 253 self.disp(_(u"File received successfully"), 2)
152 254
153 def onProgressError(self, error_msg): 255 def onProgressError(self, error_msg):
154 if error_msg == C.PROGRESS_ERROR_DECLINED: 256 if error_msg == C.PROGRESS_ERROR_DECLINED:
155 self.disp(_(u'The file request has been refused')) 257 self.disp(_(u"The file request has been refused"))
156 else: 258 else:
157 self.disp(_(u'Error while requesting file: {}').format(error_msg), error=True) 259 self.disp(_(u"Error while requesting file: {}").format(error_msg), error=True)
158 260
159 def gotId(self, progress_id): 261 def gotId(self, progress_id):
160 """Called when a progress id has been received 262 """Called when a progress id has been received
161 263
162 @param progress_id(unicode): progress id 264 @param progress_id(unicode): progress id
163 """ 265 """
164 self.progress_id = progress_id 266 self.progress_id = progress_id
165 267
166 def error(self, failure): 268 def error(self, failure):
167 self.disp(_("Error while trying to send a file: {reason}").format(reason=failure), error=True) 269 self.disp(
270 _("Error while trying to send a file: {reason}").format(reason=failure),
271 error=True,
272 )
168 self.host.quit(1) 273 self.host.quit(1)
169 274
170 def start(self): 275 def start(self):
171 if not self.args.name and not self.args.hash: 276 if not self.args.name and not self.args.hash:
172 self.parser.error(_(u'at least one of --name or --hash must be provided')) 277 self.parser.error(_(u"at least one of --name or --hash must be provided"))
173 # extra = dict(self.args.extra) 278 #  extra = dict(self.args.extra)
174 if self.args.dest: 279 if self.args.dest:
175 path = os.path.abspath(os.path.expanduser(self.args.dest)) 280 path = os.path.abspath(os.path.expanduser(self.args.dest))
176 if os.path.isdir(path): 281 if os.path.isdir(path):
177 path = os.path.join(path, self.filename) 282 path = os.path.join(path, self.filename)
178 else: 283 else:
179 path = os.path.abspath(self.filename) 284 path = os.path.abspath(self.filename)
180 285
181 if os.path.exists(path) and not self.args.force: 286 if os.path.exists(path) and not self.args.force:
182 message = _(u'File {path} already exists! Do you want to overwrite?').format(path=path) 287 message = _(u"File {path} already exists! Do you want to overwrite?").format(
183 confirm = raw_input(u"{} (y/N) ".format(message).encode('utf-8')) 288 path=path
289 )
290 confirm = raw_input(u"{} (y/N) ".format(message).encode("utf-8"))
184 if confirm not in (u"y", u"Y"): 291 if confirm not in (u"y", u"Y"):
185 self.disp(_(u"file request cancelled")) 292 self.disp(_(u"file request cancelled"))
186 self.host.quit(2) 293 self.host.quit(2)
187 294
188 self.full_dest_jid = self.host.get_full_jid(self.args.jid) 295 self.full_dest_jid = self.host.get_full_jid(self.args.jid)
189 extra = {} 296 extra = {}
190 if self.args.path: 297 if self.args.path:
191 extra[u'path'] = self.args.path 298 extra[u"path"] = self.args.path
192 if self.args.namespace: 299 if self.args.namespace:
193 extra[u'namespace'] = self.args.namespace 300 extra[u"namespace"] = self.args.namespace
194 self.host.bridge.fileJingleRequest(self.full_dest_jid, 301 self.host.bridge.fileJingleRequest(
195 path, 302 self.full_dest_jid,
196 self.args.name, 303 path,
197 self.args.hash, 304 self.args.name,
198 self.args.hash_algo if self.args.hash else u'', 305 self.args.hash,
199 extra, 306 self.args.hash_algo if self.args.hash else u"",
200 self.profile, 307 extra,
201 callback=self.gotId, 308 self.profile,
202 errback=partial(self.errback, 309 callback=self.gotId,
203 msg=_(u"can't request file: {}"), 310 errback=partial(
204 exit_code=C.EXIT_BRIDGE_ERRBACK)) 311 self.errback,
312 msg=_(u"can't request file: {}"),
313 exit_code=C.EXIT_BRIDGE_ERRBACK,
314 ),
315 )
205 316
206 317
207 class Receive(base.CommandAnswering): 318 class Receive(base.CommandAnswering):
208
209 def __init__(self, host): 319 def __init__(self, host):
210 super(Receive, self).__init__(host, 'receive', use_progress=True, use_verbose=True, help=_('wait for a file to be sent by a contact')) 320 super(Receive, self).__init__(
211 self._overwrite_refused = False # True when one overwrite as already been refused 321 host,
212 self.action_callbacks = {C.META_TYPE_FILE: self.onFileAction, 322 "receive",
213 C.META_TYPE_OVERWRITE: self.onOverwriteAction} 323 use_progress=True,
324 use_verbose=True,
325 help=_("wait for a file to be sent by a contact"),
326 )
327 self._overwrite_refused = False # True when one overwrite as already been refused
328 self.action_callbacks = {
329 C.META_TYPE_FILE: self.onFileAction,
330 C.META_TYPE_OVERWRITE: self.onOverwriteAction,
331 }
214 332
215 def onProgressStarted(self, metadata): 333 def onProgressStarted(self, metadata):
216 self.disp(_(u'File copy started'),2) 334 self.disp(_(u"File copy started"), 2)
217 335
218 def onProgressFinished(self, metadata): 336 def onProgressFinished(self, metadata):
219 self.disp(_(u'File received successfully'),2) 337 self.disp(_(u"File received successfully"), 2)
220 if metadata.get('hash_verified', False): 338 if metadata.get("hash_verified", False):
221 try: 339 try:
222 self.disp(_(u'hash checked: {algo}:{checksum}').format( 340 self.disp(
223 algo=metadata['hash_algo'], 341 _(u"hash checked: {algo}:{checksum}").format(
224 checksum=metadata['hash']), 342 algo=metadata["hash_algo"], checksum=metadata["hash"]
225 1) 343 ),
344 1,
345 )
226 except KeyError: 346 except KeyError:
227 self.disp(_(u'hash is checked but hash value is missing', 1), error=True) 347 self.disp(_(u"hash is checked but hash value is missing", 1), error=True)
228 else: 348 else:
229 self.disp(_(u"hash can't be verified"), 1) 349 self.disp(_(u"hash can't be verified"), 1)
230 350
231 def onProgressError(self, error_msg): 351 def onProgressError(self, error_msg):
232 self.disp(_(u'Error while receiving file: {}').format(error_msg),error=True) 352 self.disp(_(u"Error while receiving file: {}").format(error_msg), error=True)
233 353
234 def getXmluiId(self, action_data): 354 def getXmluiId(self, action_data):
235 # FIXME: we temporarily use ElementTree, but a real XMLUI managing module 355 # FIXME: we temporarily use ElementTree, but a real XMLUI managing module
236 # should be available in the futur 356 # should be available in the futur
237 # TODO: XMLUI module 357 # TODO: XMLUI module
238 try: 358 try:
239 xml_ui = action_data['xmlui'] 359 xml_ui = action_data["xmlui"]
240 except KeyError: 360 except KeyError:
241 self.disp(_(u"Action has no XMLUI"), 1) 361 self.disp(_(u"Action has no XMLUI"), 1)
242 else: 362 else:
243 ui = ET.fromstring(xml_ui.encode('utf-8')) 363 ui = ET.fromstring(xml_ui.encode("utf-8"))
244 xmlui_id = ui.get('submit') 364 xmlui_id = ui.get("submit")
245 if not xmlui_id: 365 if not xmlui_id:
246 self.disp(_(u"Invalid XMLUI received"), error=True) 366 self.disp(_(u"Invalid XMLUI received"), error=True)
247 return xmlui_id 367 return xmlui_id
248 368
249 def onFileAction(self, action_data, action_id, security_limit, profile): 369 def onFileAction(self, action_data, action_id, security_limit, profile):
250 xmlui_id = self.getXmluiId(action_data) 370 xmlui_id = self.getXmluiId(action_data)
251 if xmlui_id is None: 371 if xmlui_id is None:
252 return self.host.quitFromSignal(1) 372 return self.host.quitFromSignal(1)
253 try: 373 try:
254 from_jid = jid.JID(action_data['meta_from_jid']) 374 from_jid = jid.JID(action_data["meta_from_jid"])
255 except KeyError: 375 except KeyError:
256 self.disp(_(u"Ignoring action without from_jid data"), 1) 376 self.disp(_(u"Ignoring action without from_jid data"), 1)
257 return 377 return
258 try: 378 try:
259 progress_id = action_data['meta_progress_id'] 379 progress_id = action_data["meta_progress_id"]
260 except KeyError: 380 except KeyError:
261 self.disp(_(u"ignoring action without progress id"), 1) 381 self.disp(_(u"ignoring action without progress id"), 1)
262 return 382 return
263 383
264 if not self.bare_jids or from_jid.bare in self.bare_jids: 384 if not self.bare_jids or from_jid.bare in self.bare_jids:
265 if self._overwrite_refused: 385 if self._overwrite_refused:
266 self.disp(_(u"File refused because overwrite is needed"), error=True) 386 self.disp(_(u"File refused because overwrite is needed"), error=True)
267 self.host.bridge.launchAction(xmlui_id, {'cancelled': C.BOOL_TRUE}, profile_key=profile) 387 self.host.bridge.launchAction(
388 xmlui_id, {"cancelled": C.BOOL_TRUE}, profile_key=profile
389 )
268 return self.host.quitFromSignal(2) 390 return self.host.quitFromSignal(2)
269 self.progress_id = progress_id 391 self.progress_id = progress_id
270 xmlui_data = {'path': self.path} 392 xmlui_data = {"path": self.path}
271 self.host.bridge.launchAction(xmlui_id, xmlui_data, profile_key=profile) 393 self.host.bridge.launchAction(xmlui_id, xmlui_data, profile_key=profile)
272 394
273 def onOverwriteAction(self, action_data, action_id, security_limit, profile): 395 def onOverwriteAction(self, action_data, action_id, security_limit, profile):
274 xmlui_id = self.getXmluiId(action_data) 396 xmlui_id = self.getXmluiId(action_data)
275 if xmlui_id is None: 397 if xmlui_id is None:
276 return self.host.quitFromSignal(1) 398 return self.host.quitFromSignal(1)
277 try: 399 try:
278 progress_id = action_data['meta_progress_id'] 400 progress_id = action_data["meta_progress_id"]
279 except KeyError: 401 except KeyError:
280 self.disp(_(u"ignoring action without progress id"), 1) 402 self.disp(_(u"ignoring action without progress id"), 1)
281 return 403 return
282 self.disp(_(u"Overwriting needed"), 1) 404 self.disp(_(u"Overwriting needed"), 1)
283 405
286 self.disp(_(u"Overwrite accepted"), 2) 408 self.disp(_(u"Overwrite accepted"), 2)
287 else: 409 else:
288 self.disp(_(u"Refused to overwrite"), 2) 410 self.disp(_(u"Refused to overwrite"), 2)
289 self._overwrite_refused = True 411 self._overwrite_refused = True
290 412
291 xmlui_data = {'answer': C.boolConst(self.args.force)} 413 xmlui_data = {"answer": C.boolConst(self.args.force)}
292 self.host.bridge.launchAction(xmlui_id, xmlui_data, profile_key=profile) 414 self.host.bridge.launchAction(xmlui_id, xmlui_data, profile_key=profile)
293 415
294 def add_parser_options(self): 416 def add_parser_options(self):
295 self.parser.add_argument("jids", type=base.unicode_decoder, nargs="*", help=_(u'jids accepted (accept everything if none is specified)')) 417 self.parser.add_argument(
296 self.parser.add_argument("-m", "--multiple", action="store_true", help=_(u"accept multiple files (you'll have to stop manually)")) 418 "jids",
297 self.parser.add_argument("-f", "--force", action="store_true", help=_(u"force overwritting of existing files (/!\\ name is choosed by sender)")) 419 type=base.unicode_decoder,
298 self.parser.add_argument("--path", default='.', metavar='DIR', help=_(u"destination path (default: working directory)")) 420 nargs="*",
421 help=_(u"jids accepted (accept everything if none is specified)"),
422 )
423 self.parser.add_argument(
424 "-m",
425 "--multiple",
426 action="store_true",
427 help=_(u"accept multiple files (you'll have to stop manually)"),
428 )
429 self.parser.add_argument(
430 "-f",
431 "--force",
432 action="store_true",
433 help=_(
434 u"force overwritting of existing files (/!\\ name is choosed by sender)"
435 ),
436 )
437 self.parser.add_argument(
438 "--path",
439 default=".",
440 metavar="DIR",
441 help=_(u"destination path (default: working directory)"),
442 )
299 443
300 def start(self): 444 def start(self):
301 self.bare_jids = [jid.JID(jid_).bare for jid_ in self.args.jids] 445 self.bare_jids = [jid.JID(jid_).bare for jid_ in self.args.jids]
302 self.path = os.path.abspath(self.args.path) 446 self.path = os.path.abspath(self.args.path)
303 if not os.path.isdir(self.path): 447 if not os.path.isdir(self.path):
304 self.disp(_(u"Given path is not a directory !", error=True)) 448 self.disp(_(u"Given path is not a directory !", error=True))
305 self.host.quit(2) 449 self.host.quit(2)
306 if self.args.multiple: 450 if self.args.multiple:
307 self.host.quit_on_progress_end = False 451 self.host.quit_on_progress_end = False
308 self.disp(_(u"waiting for incoming file request"),2) 452 self.disp(_(u"waiting for incoming file request"), 2)
309 453
310 454
311 class Upload(base.CommandBase): 455 class Upload(base.CommandBase):
312
313 def __init__(self, host): 456 def __init__(self, host):
314 super(Upload, self).__init__(host, 'upload', use_progress=True, use_verbose=True, help=_('upload a file')) 457 super(Upload, self).__init__(
315 self.need_loop=True 458 host, "upload", use_progress=True, use_verbose=True, help=_("upload a file")
459 )
460 self.need_loop = True
316 461
317 def add_parser_options(self): 462 def add_parser_options(self):
318 self.parser.add_argument("file", type=str, help=_("file to upload")) 463 self.parser.add_argument("file", type=str, help=_("file to upload"))
319 self.parser.add_argument("jid", type=base.unicode_decoder, nargs='?', help=_("jid of upload component (nothing to autodetect)")) 464 self.parser.add_argument(
320 self.parser.add_argument("--ignore-tls-errors", action="store_true", help=_("ignore invalide TLS certificate")) 465 "jid",
466 type=base.unicode_decoder,
467 nargs="?",
468 help=_("jid of upload component (nothing to autodetect)"),
469 )
470 self.parser.add_argument(
471 "--ignore-tls-errors",
472 action="store_true",
473 help=_("ignore invalide TLS certificate"),
474 )
321 475
322 def onProgressStarted(self, metadata): 476 def onProgressStarted(self, metadata):
323 self.disp(_(u'File upload started'),2) 477 self.disp(_(u"File upload started"), 2)
324 478
325 def onProgressFinished(self, metadata): 479 def onProgressFinished(self, metadata):
326 self.disp(_(u'File uploaded successfully'),2) 480 self.disp(_(u"File uploaded successfully"), 2)
327 try: 481 try:
328 url = metadata['url'] 482 url = metadata["url"]
329 except KeyError: 483 except KeyError:
330 self.disp(u'download URL not found in metadata') 484 self.disp(u"download URL not found in metadata")
331 else: 485 else:
332 self.disp(_(u'URL to retrieve the file:'),1) 486 self.disp(_(u"URL to retrieve the file:"), 1)
333 # XXX: url is display alone on a line to make parsing easier 487 # XXX: url is display alone on a line to make parsing easier
334 self.disp(url) 488 self.disp(url)
335 489
336 def onProgressError(self, error_msg): 490 def onProgressError(self, error_msg):
337 self.disp(_(u'Error while uploading file: {}').format(error_msg),error=True) 491 self.disp(_(u"Error while uploading file: {}").format(error_msg), error=True)
338 492
339 def gotId(self, data, file_): 493 def gotId(self, data, file_):
340 """Called when a progress id has been received 494 """Called when a progress id has been received
341 495
342 @param pid(unicode): progress id 496 @param pid(unicode): progress id
343 @param file_(str): file path 497 @param file_(str): file path
344 """ 498 """
345 try: 499 try:
346 self.progress_id = data['progress'] 500 self.progress_id = data["progress"]
347 except KeyError: 501 except KeyError:
348 # TODO: if 'xmlui' key is present, manage xmlui message display 502 # TODO: if 'xmlui' key is present, manage xmlui message display
349 self.disp(_(u"Can't upload file"), error=True) 503 self.disp(_(u"Can't upload file"), error=True)
350 self.host.quit(2) 504 self.host.quit(2)
351 505
352 def error(self, failure): 506 def error(self, failure):
353 self.disp(_("Error while trying to upload a file: {reason}").format(reason=failure), error=True) 507 self.disp(
508 _("Error while trying to upload a file: {reason}").format(reason=failure),
509 error=True,
510 )
354 self.host.quit(1) 511 self.host.quit(1)
355 512
356 def start(self): 513 def start(self):
357 file_ = self.args.file 514 file_ = self.args.file
358 if not os.path.exists(file_): 515 if not os.path.exists(file_):
360 self.host.quit(1) 517 self.host.quit(1)
361 if os.path.isdir(file_): 518 if os.path.isdir(file_):
362 self.disp(_(u"[{}] is a dir! Can't upload a dir").format(file_)) 519 self.disp(_(u"[{}] is a dir! Can't upload a dir").format(file_))
363 self.host.quit(1) 520 self.host.quit(1)
364 521
365 self.full_dest_jid = self.host.get_full_jid(self.args.jid) if self.args.jid is not None else '' 522 self.full_dest_jid = (
523 self.host.get_full_jid(self.args.jid) if self.args.jid is not None else ""
524 )
366 options = {} 525 options = {}
367 if self.args.ignore_tls_errors: 526 if self.args.ignore_tls_errors:
368 options['ignore_tls_errors'] = C.BOOL_TRUE 527 options["ignore_tls_errors"] = C.BOOL_TRUE
369 528
370 path = os.path.abspath(file_) 529 path = os.path.abspath(file_)
371 self.host.bridge.fileUpload(path, '', self.full_dest_jid, options, self.profile, callback=lambda pid, file_=file_: self.gotId(pid, file_), errback=self.error) 530 self.host.bridge.fileUpload(
531 path,
532 "",
533 self.full_dest_jid,
534 options,
535 self.profile,
536 callback=lambda pid, file_=file_: self.gotId(pid, file_),
537 errback=self.error,
538 )
372 539
373 540
374 class ShareList(base.CommandBase): 541 class ShareList(base.CommandBase):
375
376 def __init__(self, host): 542 def __init__(self, host):
377 extra_outputs = {'default': self.default_output} 543 extra_outputs = {"default": self.default_output}
378 super(ShareList, self).__init__(host, 'list', use_output=C.OUTPUT_LIST_DICT, extra_outputs=extra_outputs, help=_(u'retrieve files shared by an entity'), use_verbose=True) 544 super(ShareList, self).__init__(
379 self.need_loop=True 545 host,
546 "list",
547 use_output=C.OUTPUT_LIST_DICT,
548 extra_outputs=extra_outputs,
549 help=_(u"retrieve files shared by an entity"),
550 use_verbose=True,
551 )
552 self.need_loop = True
380 553
381 def add_parser_options(self): 554 def add_parser_options(self):
382 self.parser.add_argument("-d", "--path", default=u'', help=_(u"path to the directory containing the files")) 555 self.parser.add_argument(
383 self.parser.add_argument("jid", type=base.unicode_decoder, nargs='?', default='', help=_("jid of sharing entity (nothing to check our own jid)")) 556 "-d",
557 "--path",
558 default=u"",
559 help=_(u"path to the directory containing the files"),
560 )
561 self.parser.add_argument(
562 "jid",
563 type=base.unicode_decoder,
564 nargs="?",
565 default="",
566 help=_("jid of sharing entity (nothing to check our own jid)"),
567 )
384 568
385 def file_gen(self, files_data): 569 def file_gen(self, files_data):
386 for file_data in files_data: 570 for file_data in files_data:
387 yield file_data[u'name'] 571 yield file_data[u"name"]
388 yield file_data.get(u'size', '') 572 yield file_data.get(u"size", "")
389 yield file_data.get(u'hash','') 573 yield file_data.get(u"hash", "")
390 574
391 def _name_filter(self, name, row): 575 def _name_filter(self, name, row):
392 if row.type == C.FILE_TYPE_DIRECTORY: 576 if row.type == C.FILE_TYPE_DIRECTORY:
393 return A.color(C.A_DIRECTORY, name) 577 return A.color(C.A_DIRECTORY, name)
394 elif row.type == C.FILE_TYPE_FILE: 578 elif row.type == C.FILE_TYPE_FILE:
395 return A.color(C.A_FILE, name) 579 return A.color(C.A_FILE, name)
396 else: 580 else:
397 self.disp(_(u'unknown file type: {type}').format(type=row.type), error=True) 581 self.disp(_(u"unknown file type: {type}").format(type=row.type), error=True)
398 return name 582 return name
399 583
400 def _size_filter(self, size, row): 584 def _size_filter(self, size, row):
401 if not size: 585 if not size:
402 return u'' 586 return u""
403 size = int(size) 587 size = int(size)
404 # cf. https://stackoverflow.com/a/1094933 (thanks) 588 #  cf. https://stackoverflow.com/a/1094933 (thanks)
405 suffix = u'o' 589 suffix = u"o"
406 for unit in [u'', u'Ki', u'Mi', u'Gi', u'Ti', u'Pi', u'Ei', u'Zi']: 590 for unit in [u"", u"Ki", u"Mi", u"Gi", u"Ti", u"Pi", u"Ei", u"Zi"]:
407 if abs(size) < 1024.0: 591 if abs(size) < 1024.0:
408 return A.color(A.BOLD, u"{:.2f}".format(size), unit, suffix) 592 return A.color(A.BOLD, u"{:.2f}".format(size), unit, suffix)
409 size /= 1024.0 593 size /= 1024.0
410 594
411 return A.color(A.BOLD, u"{:.2f}".format(size), u'Yi', suffix) 595 return A.color(A.BOLD, u"{:.2f}".format(size), u"Yi", suffix)
412 596
413 def default_output(self, files_data): 597 def default_output(self, files_data):
414 """display files a way similar to ls""" 598 """display files a way similar to ls"""
415 files_data.sort(key=lambda d: d['name'].lower()) 599 files_data.sort(key=lambda d: d["name"].lower())
416 show_header = False 600 show_header = False
417 if self.verbosity == 0: 601 if self.verbosity == 0:
418 headers = (u'name', u'type') 602 headers = (u"name", u"type")
419 elif self.verbosity == 1: 603 elif self.verbosity == 1:
420 headers = (u'name', u'type', u'size') 604 headers = (u"name", u"type", u"size")
421 elif self.verbosity > 1: 605 elif self.verbosity > 1:
422 show_header = True 606 show_header = True
423 headers = (u'name', u'type', u'size', u'hash') 607 headers = (u"name", u"type", u"size", u"hash")
424 table = common.Table.fromDict(self.host, 608 table = common.Table.fromDict(
425 files_data, 609 self.host,
426 headers, 610 files_data,
427 filters={u'name': self._name_filter, 611 headers,
428 u'size': self._size_filter}, 612 filters={u"name": self._name_filter, u"size": self._size_filter},
429 defaults={u'size': u'', 613 defaults={u"size": u"", u"hash": u""},
430 u'hash': u''}, 614 )
431 ) 615 table.display_blank(show_header=show_header, hide_cols=["type"])
432 table.display_blank(show_header=show_header, hide_cols=['type'])
433 616
434 def _FISListCb(self, files_data): 617 def _FISListCb(self, files_data):
435 self.output(files_data) 618 self.output(files_data)
436 self.host.quit() 619 self.host.quit()
437 620
440 self.args.jid, 623 self.args.jid,
441 self.args.path, 624 self.args.path,
442 {}, 625 {},
443 self.profile, 626 self.profile,
444 callback=self._FISListCb, 627 callback=self._FISListCb,
445 errback=partial(self.errback, 628 errback=partial(
446 msg=_(u"can't retrieve shared files: {}"), 629 self.errback,
447 exit_code=C.EXIT_BRIDGE_ERRBACK)) 630 msg=_(u"can't retrieve shared files: {}"),
631 exit_code=C.EXIT_BRIDGE_ERRBACK,
632 ),
633 )
448 634
449 635
450 class SharePath(base.CommandBase): 636 class SharePath(base.CommandBase):
451
452 def __init__(self, host): 637 def __init__(self, host):
453 super(SharePath, self).__init__(host, 'path', help=_(u'share a file or directory'), use_verbose=True) 638 super(SharePath, self).__init__(
454 self.need_loop=True 639 host, "path", help=_(u"share a file or directory"), use_verbose=True
640 )
641 self.need_loop = True
455 642
456 def add_parser_options(self): 643 def add_parser_options(self):
457 self.parser.add_argument("-n", "--name", type=base.unicode_decoder, default=u'', help=_(u"virtual name to use (default: use directory/file name)")) 644 self.parser.add_argument(
645 "-n",
646 "--name",
647 type=base.unicode_decoder,
648 default=u"",
649 help=_(u"virtual name to use (default: use directory/file name)"),
650 )
458 perm_group = self.parser.add_mutually_exclusive_group() 651 perm_group = self.parser.add_mutually_exclusive_group()
459 perm_group.add_argument("-j", "--jid", type=base.unicode_decoder, action='append', dest="jids", default=[], help=_(u"jid of contacts allowed to retrieve the files")) 652 perm_group.add_argument(
460 perm_group.add_argument("--public", action='store_true', help=_(u"share publicly the file(s) (/!\\ *everybody* will be able to access them)")) 653 "-j",
461 self.parser.add_argument("path", type=base.unicode_decoder, help=_(u"path to a file or directory to share")) 654 "--jid",
462 655 type=base.unicode_decoder,
656 action="append",
657 dest="jids",
658 default=[],
659 help=_(u"jid of contacts allowed to retrieve the files"),
660 )
661 perm_group.add_argument(
662 "--public",
663 action="store_true",
664 help=_(
665 u"share publicly the file(s) (/!\\ *everybody* will be able to access them)"
666 ),
667 )
668 self.parser.add_argument(
669 "path",
670 type=base.unicode_decoder,
671 help=_(u"path to a file or directory to share"),
672 )
463 673
464 def _FISSharePathCb(self, name): 674 def _FISSharePathCb(self, name):
465 self.disp(_(u'{path} shared under the name "{name}"').format( 675 self.disp(
466 path = self.path, 676 _(u'{path} shared under the name "{name}"').format(path=self.path, name=name)
467 name = name)) 677 )
468 self.host.quit() 678 self.host.quit()
469 679
470 def start(self): 680 def start(self):
471 self.path = os.path.abspath(self.args.path) 681 self.path = os.path.abspath(self.args.path)
472 if self.args.public: 682 if self.args.public:
473 access = {u'read': {u'type': u'public'}} 683 access = {u"read": {u"type": u"public"}}
474 else: 684 else:
475 jids = self.args.jids 685 jids = self.args.jids
476 if jids: 686 if jids:
477 access = {u'read': {u'type': 'whitelist', 687 access = {u"read": {u"type": "whitelist", u"jids": jids}}
478 u'jids': jids}}
479 else: 688 else:
480 access = {} 689 access = {}
481 self.host.bridge.FISSharePath( 690 self.host.bridge.FISSharePath(
482 self.args.name, 691 self.args.name,
483 self.path, 692 self.path,
484 json.dumps(access, ensure_ascii=False), 693 json.dumps(access, ensure_ascii=False),
485 self.profile, 694 self.profile,
486 callback=self._FISSharePathCb, 695 callback=self._FISSharePathCb,
487 errback=partial(self.errback, 696 errback=partial(
488 msg=_(u"can't share path: {}"), 697 self.errback,
489 exit_code=C.EXIT_BRIDGE_ERRBACK)) 698 msg=_(u"can't share path: {}"),
699 exit_code=C.EXIT_BRIDGE_ERRBACK,
700 ),
701 )
490 702
491 703
492 class Share(base.CommandBase): 704 class Share(base.CommandBase):
493 subcommands = (ShareList, SharePath) 705 subcommands = (ShareList, SharePath)
494 706
495 def __init__(self, host): 707 def __init__(self, host):
496 super(Share, self).__init__(host, 'share', use_profile=False, help=_(u'files sharing management')) 708 super(Share, self).__init__(
709 host, "share", use_profile=False, help=_(u"files sharing management")
710 )
497 711
498 712
499 class File(base.CommandBase): 713 class File(base.CommandBase):
500 subcommands = (Send, Request, Receive, Upload, Share) 714 subcommands = (Send, Request, Receive, Upload, Share)
501 715
502 def __init__(self, host): 716 def __init__(self, host):
503 super(File, self).__init__(host, 'file', use_profile=False, help=_(u'files sending/receiving/management')) 717 super(File, self).__init__(
718 host, "file", use_profile=False, help=_(u"files sending/receiving/management")
719 )