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()