Mercurial > libervia-backend
comparison frontends/src/jp/jp @ 401:b2caa2615c4c
jp roster name manegement + Pipe transfer
- added experimental pipe transfer protocol (based on xep-0096)
- jp now manage roster name to jid conversion (if an argument correspond to a roster name, it replace it with the corresponding jid)
- jp now add last known resource of when a resource is needed but we have a bare jid (e.g. for file or pipe transfer)
author | Goffi <goffi@goffi.org> |
---|---|
date | Fri, 07 Oct 2011 00:25:15 +0200 |
parents | 393b35aa86d2 |
children | f03688bdb858 |
comparison
equal
deleted
inserted
replaced
400:22788653ae8d | 401:b2caa2615c4c |
---|---|
54 import pdb | 54 import pdb |
55 from sat.tools.jid import JID | 55 from sat.tools.jid import JID |
56 import gobject | 56 import gobject |
57 from sat_frontends.bridge.DBus import DBusBridgeFrontend,BridgeExceptionNoService | 57 from sat_frontends.bridge.DBus import DBusBridgeFrontend,BridgeExceptionNoService |
58 import tarfile | 58 import tarfile |
59 import tempfile | |
60 import shutil | |
59 try: | 61 try: |
60 from progressbar import ProgressBar, Percentage, Bar, ETA, FileTransferSpeed | 62 from progressbar import ProgressBar, Percentage, Bar, ETA, FileTransferSpeed |
61 except ImportError, e: | 63 except ImportError, e: |
62 info (_('ProgressBar not available, please download it at http://pypi.python.org/pypi/progressbar')) | 64 info (_('ProgressBar not available, please download it at http://pypi.python.org/pypi/progressbar')) |
63 info (_('Progress bar deactivated\n--\n')) | 65 info (_('Progress bar deactivated\n--\n')) |
103 help=_("Separate xmpp messages: send one message per line instead of one message alone.")) | 105 help=_("Separate xmpp messages: send one message per line instead of one message alone.")) |
104 parser.add_option("-n", "--new-line", action="store_true", default=False, | 106 parser.add_option("-n", "--new-line", action="store_true", default=False, |
105 help=_("Add a new line at the beginning of the input (usefull for ascii art ;))")) | 107 help=_("Add a new line at the beginning of the input (usefull for ascii art ;))")) |
106 parser.add_option("--connect", action="store_true", default=False, | 108 parser.add_option("--connect", action="store_true", default=False, |
107 help=_("Connect the profile before doing anything else")) | 109 help=_("Connect the profile before doing anything else")) |
110 parser.add_option("--pipe-in", action="store_true", default=False, | |
111 help=_("Wait for the reception of a pipe stream")) | |
112 parser.add_option("--pipe-out", action="store_true", default=False, | |
113 help=_("Pipe a stream out ")) | |
108 | 114 |
109 (self.options, args) = parser.parse_args() | 115 (self.options, args) = parser.parse_args() |
110 | 116 |
111 if len(args) < 1 and not self.options.wait_file: | 117 if len(args) < 1 and not self.options.wait_file: |
112 parser.error(_("You must specify the destination JID (Jabber ID)").encode('utf-8')) | 118 parser.error(_("You must specify the destination JID (Jabber ID)").encode('utf-8')) |
113 | 119 |
114 if self.options.wait_file: | 120 if self.options.wait_file or self.options.pipe_in: |
115 #several jid | 121 #several jid |
116 self.dest_jids = args | 122 self.dest_jids = [arg.decode('utf-8') for arg in args] |
117 else: | 123 else: |
118 #one dest_jid, other args are files | 124 #one dest_jid, other args are files |
119 self.dest_jid = JID(args[-1]) | 125 self.dest_jid = JID(args[-1].decode('utf-8')) |
120 if not self.dest_jid.is_valid: | |
121 error (_("%s is not a valid JID !"), self.dest_jid) | |
122 exit(1) | |
123 self.files = args[:-1] | 126 self.files = args[:-1] |
124 | 127 |
125 if not pbar_available and self.options.progress: | 128 if not pbar_available and self.options.progress: |
126 self.options.progress = False | 129 self.options.progress = False |
127 error (_("Option progress is not available, deactivated.")) | 130 error (_("Option progress is not available, deactivated.")) |
128 | 131 |
129 if self.options.progress or self.options.wait_file or self.options.connect: | 132 if self.options.progress or self.options.wait_file or self.options.connect or self.options.pipe_in: |
130 self.start_loop = True #We have to use loop for these options | 133 self.start_loop = True #We have to use loop for these options |
131 else: | 134 else: |
132 self.start_loop = False | 135 self.start_loop = False |
133 | 136 |
134 | 137 |
153 error(_(u"SàT is not conneted, please connect before using jp")) | 156 error(_(u"SàT is not conneted, please connect before using jp")) |
154 exit(1) | 157 exit(1) |
155 | 158 |
156 self.connected() | 159 self.connected() |
157 | 160 |
158 | 161 def check_jids(self): |
162 """Check jids validity, transform roster name to corresponding jids""" | |
163 names2jid = {} | |
164 | |
165 for contact in self.bridge.getContacts(self.options.profile): | |
166 _jid, attr, groups = contact | |
167 if attr.has_key("name"): | |
168 names2jid[attr["name"].lower()] = _jid | |
169 | |
170 def expandJid(jid): | |
171 _jid = jid.lower() | |
172 return unicode(names2jid[_jid] if _jid in names2jid else jid) | |
173 | |
174 def check(jid): | |
175 if not jid.is_valid: | |
176 error (_("%s is not a valid JID !"), self.dest_jid) | |
177 exit(1) | |
178 | |
179 try: | |
180 self.dest_jid = expandJid(self.dest_jid) | |
181 check(self.dest_jid) | |
182 except AttributeError: | |
183 pass | |
184 try: | |
185 for i in range(len(self.dest_jids)): | |
186 self.dest_jids[i] = expandJid(self.dest_jids[i]) | |
187 check(self.dest_jids[i]) | |
188 except AttributeError: | |
189 pass | |
159 | 190 |
160 def send_stdin(self): | 191 def send_stdin(self): |
161 """Send incomming data on stdin to jabber contact""" | 192 """Send incomming data on stdin to jabber contact""" |
162 header = "\n" if self.options.new_line else "" | 193 header = "\n" if self.options.new_line else "" |
163 | 194 |
170 break | 201 break |
171 self.bridge.sendMessage(self.dest_jid, line.replace("\n",""), profile_key=self.profile) | 202 self.bridge.sendMessage(self.dest_jid, line.replace("\n",""), profile_key=self.profile) |
172 else: | 203 else: |
173 self.bridge.sendMessage(self.dest_jid, header + "".join(sys.stdin.readlines()), profile_key=self.profile) | 204 self.bridge.sendMessage(self.dest_jid, header + "".join(sys.stdin.readlines()), profile_key=self.profile) |
174 | 205 |
206 | |
207 def pipe_out(self): | |
208 """Create named pipe, and send stdin to it""" | |
209 tmp_dir = tempfile.mkdtemp() | |
210 fifopath = os.path.join(tmp_dir,"pipe_out") | |
211 os.mkfifo(fifopath) | |
212 self.bridge.pipeOut(self._getFullJid(self.dest_jid), fifopath, {}, profile_key=self.profile) | |
213 f = open(fifopath, 'w+') | |
214 shutil.copyfileobj(sys.stdin, f) | |
215 f.close() | |
216 shutil.rmtree(tmp_dir) | |
217 | |
218 | |
175 def send_files(self): | 219 def send_files(self): |
176 """Send files to jabber contact""" | 220 """Send files to jabber contact""" |
177 | 221 |
178 for file in self.files: | 222 for file in self.files: |
179 if not os.path.exists(file): | 223 if not os.path.exists(file): |
181 exit(1) | 225 exit(1) |
182 if not self.options.bz2 and os.path.isdir(file): | 226 if not self.options.bz2 and os.path.isdir(file): |
183 error (_("[%s] is a dir ! Please send files inside or use compression") % file) | 227 error (_("[%s] is a dir ! Please send files inside or use compression") % file) |
184 exit(1) | 228 exit(1) |
185 | 229 |
230 full_dest_jid = self._getFullJid(self.dest_jid) | |
186 if self.options.bz2: | 231 if self.options.bz2: |
187 tmpfile = (basename(self.files[0]) or basename(dirname(self.files[0])) ) + '.tar.bz2' #FIXME: tmp, need an algorithm to find a good name/path | 232 tmpfile = (basename(self.files[0]) or basename(dirname(self.files[0])) ) + '.tar.bz2' #FIXME: tmp, need an algorithm to find a good name/path |
188 if os.path.exists(tmpfile): | 233 if os.path.exists(tmpfile): |
189 error (_("tmp file (%s) already exists ! Please remove it"), tmpfile) | 234 error (_("tmp file (%s) already exists ! Please remove it"), tmpfile) |
190 exit(1) | 235 exit(1) |
197 info(_("Adding %s"), file) | 242 info(_("Adding %s"), file) |
198 bz2.add(file) | 243 bz2.add(file) |
199 bz2.close() | 244 bz2.close() |
200 info(_("OK !")) | 245 info(_("OK !")) |
201 path = abspath(tmpfile) | 246 path = abspath(tmpfile) |
202 self.transfer_id = self.bridge.sendFile(self.dest_jid, path, {}, profile_key=self.profile) | 247 self.transfer_id = self.bridge.sendFile(full_dest_jid, path, {}, profile_key=self.profile) |
203 else: | 248 else: |
204 for file in self.files: | 249 for file in self.files: |
205 path = abspath(file) | 250 path = abspath(file) |
206 self.transfer_id = self.bridge.sendFile(self.dest_jid, path, {}, profile_key=self.profile) #FIXME: show progress only for last transfer_id | 251 self.transfer_id = self.bridge.sendFile(full_dest_jid, path, {}, profile_key=self.profile) #FIXME: show progress only for last transfer_id |
207 | 252 |
208 #TODO: manage ProgressBar | 253 |
254 def _getFullJid(self, param_jid): | |
255 """Return the full jid if possible (add last resource when find a bare jid""" | |
256 _jid = JID(param_jid) | |
257 if not _jid.resource: | |
258 #if the resource is not given, we try to add the last known resource | |
259 last_resource = self.bridge.getLastResource(param_jid, self.options.profile) | |
260 if last_resource: | |
261 return "%s/%s" % (_jid.short, last_resource) | |
262 return param_jid | |
263 | |
209 | 264 |
210 def askConfirmation(self, type, id, data): | 265 def askConfirmation(self, type, id, data): |
211 """CB used for file transfer, accept files depending on parameters""" | 266 """CB used for file transfer, accept files depending on parameters""" |
212 answer_data={} | 267 answer_data={} |
213 if type == "FILE_TRANSFER": | 268 if type == "FILE_TRANSFER": |
214 if self.dest_jids and not data['from'] in self.dest_jids: | 269 if not self.options.wait_file: |
270 return | |
271 if self.dest_jids and not JID(data['from']).short in [JID(_jid).short for _jid in self.dest_jids]: | |
215 return #file is not sent by a filtered jid | 272 return #file is not sent by a filtered jid |
216 | 273 |
217 answer_data["dest_path"] = os.getcwd()+'/'+data['filename'] | 274 answer_data["dest_path"] = os.getcwd()+'/'+data['filename'] |
218 | 275 |
219 if self.options.force or not os.path.exists(answer_data["dest_path"]): | 276 if self.options.force or not os.path.exists(answer_data["dest_path"]): |
226 | 283 |
227 | 284 |
228 if not self.options.multiple and not self.options.progress: | 285 if not self.options.multiple and not self.options.progress: |
229 #we just accept one file | 286 #we just accept one file |
230 self.loop.quit() | 287 self.loop.quit() |
288 elif type == "PIPE_TRANSFER": | |
289 if not self.options.pipe_in: | |
290 return | |
291 if self.dest_jids and not JID(data['from']).short in [JID(_jid).short for _jid in self.dest_jids]: | |
292 return #pipe stream is not sent by a filtered jid | |
293 | |
294 tmp_dir = tempfile.mkdtemp() | |
295 fifopath = os.path.join(tmp_dir,"pipe_in") | |
296 answer_data["dest_path"] = fifopath | |
297 os.mkfifo(fifopath) | |
298 self.bridge.confirmationAnswer(id, True, answer_data) | |
299 f = open(fifopath, 'r') | |
300 shutil.copyfileobj(f, sys.stdout) | |
301 f.close() | |
302 shutil.rmtree(tmp_dir) | |
303 self.loop.quit() | |
304 | |
231 | 305 |
232 def actionResult(self, type, id, data): | 306 def actionResult(self, type, id, data): |
233 #FIXME | 307 #FIXME |
234 info (_("FIXME: actionResult not implemented")) | 308 info (_("FIXME: actionResult not implemented")) |
235 | 309 |
236 def wait_file(self): | 310 def confirmation_reply(self): |
237 """Wait for a file and write it on local dir""" | 311 """Auto reply to confirmations requests""" |
238 self.bridge.register("askConfirmation", self.askConfirmation) | 312 self.bridge.register("askConfirmation", self.askConfirmation) |
239 | 313 |
240 def progressCB(self): | 314 def progressCB(self): |
241 if self.transfer_id: | 315 if self.transfer_id: |
242 data = self.bridge.getProgress(self.transfer_id) | 316 data = self.bridge.getProgress(self.transfer_id) |
267 except KeyboardInterrupt: | 341 except KeyboardInterrupt: |
268 info(_("User interruption: good bye")) | 342 info(_("User interruption: good bye")) |
269 | 343 |
270 def connected(self): | 344 def connected(self): |
271 """This is called when the profile is connected""" | 345 """This is called when the profile is connected""" |
272 if self.options.wait_file: | 346 self.check_jids() |
273 self.wait_file() | 347 if self.options.wait_file or self.options.pipe_in: |
274 else: | 348 self.confirmation_reply() |
275 if not self.files: #we send message only if there are no files to send | 349 else: |
350 if self.files: | |
351 self.send_files() | |
352 elif self.options.pipe_out: | |
353 self.pipe_out() | |
354 else: | |
276 self.send_stdin() | 355 self.send_stdin() |
277 else: | |
278 self.send_files() | |
279 | 356 |
280 if self.options.progress: | 357 if self.options.progress: |
281 self.pbar = None | 358 self.pbar = None |
282 gobject.timeout_add(10, self.progressCB) | 359 gobject.timeout_add(10, self.progressCB) |
283 | 360 |
284 if not self.options.progress and not self.options.wait_file: | 361 if self.start_loop and not self.options.progress and not self.options.wait_file and not self.options.pipe_in: |
285 self.loop.quit() | 362 self.loop.quit() |
286 | 363 |
287 | 364 |
288 if __name__ == "__main__": | 365 if __name__ == "__main__": |
289 jp = JP() | 366 jp = JP() |