0
|
1 #! /usr/bin/python |
|
2 # -*- coding: utf-8 -*- |
|
3 |
|
4 """ |
|
5 jp: a SAT command line tool |
|
6 Copyright (C) 2009 Jérôme Poisson (goffi@goffi.org) |
|
7 |
|
8 This program is free software: you can redistribute it and/or modify |
|
9 it under the terms of the GNU General Public License as published by |
|
10 the Free Software Foundation, either version 3 of the License, or |
|
11 (at your option) any later version. |
|
12 |
|
13 This program is distributed in the hope that it will be useful, |
|
14 but WITHOUT ANY WARRANTY; without even the implied warranty of |
|
15 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
|
16 GNU General Public License for more details. |
|
17 |
|
18 You should have received a copy of the GNU General Public License |
|
19 along with this program. If not, see <http://www.gnu.org/licenses/>. |
|
20 """ |
|
21 |
|
22 #consts |
|
23 name = "jp" |
|
24 version = "0.0.1" |
|
25 about = name+" v"+version+""" (c) Jérôme Poisson (aka Goffi) 2009 |
|
26 |
|
27 --- |
|
28 """+name+""" Copyright (C) 2009 Jérôme Poisson (aka Goffi) |
|
29 This program comes with ABSOLUTELY NO WARRANTY; |
|
30 This is free software, and you are welcome to redistribute it |
|
31 under certain conditions. |
|
32 --- |
|
33 |
|
34 This software is a command line tool for jabber |
|
35 Get the latest version at http://www.goffi.org |
|
36 """ |
|
37 |
|
38 global pbar_available |
|
39 pbar_available = True #checked before using ProgressBar |
|
40 |
|
41 ### logging ### |
|
42 import logging |
|
43 from logging import debug, info, error, warning |
|
44 logging.basicConfig(level=logging.DEBUG, |
|
45 format='%(message)s') |
|
46 ### |
|
47 |
|
48 import sys |
|
49 import os |
|
50 from os.path import abspath, basename, dirname |
|
51 from optparse import OptionParser |
|
52 import pdb |
|
53 from tools.jid import JID |
|
54 import gobject |
|
55 from sat_bridge_frontend.DBus import DBusBridgeFrontend |
|
56 import tarfile |
|
57 try: |
|
58 from progressbar import ProgressBar, Percentage, Bar, ETA, FileTransferSpeed |
|
59 except ImportError, e: |
|
60 info ('ProgressBar not available, please download it at http://pypi.python.org/pypi/progressbar') |
|
61 info ('Progress bar deactivated\n--\n') |
|
62 pbar_available=False |
|
63 |
|
64 |
|
65 |
|
66 |
|
67 class JP(): |
|
68 def __init__(self): |
|
69 self.bridge=DBusBridgeFrontend() |
|
70 self.transfert_id = None |
|
71 |
|
72 def check_options(self): |
|
73 """Check command line options""" |
|
74 usage=""" |
|
75 %prog [options] [FILE1 FILE2 ...] JID |
|
76 %prog -w [options] [JID1 JID2 ...] |
|
77 |
|
78 %prog --help for options list |
|
79 """ |
|
80 parser = OptionParser(usage=usage,version=about) |
|
81 |
|
82 parser.add_option("-b", "--bz2", action="store_true", default=False, |
|
83 help="Make a bzip2 tarball") |
|
84 parser.add_option("-w", "--wait-file", action="store_true", default=False, |
|
85 help="Wait for a file to be sent by a contact") |
|
86 parser.add_option("-m", "--multiple", action="store_true", default=False, |
|
87 help="Accept multiple files (you'll have to stop manually)") |
|
88 parser.add_option("-f", "--force", action="store_true", default=False, |
|
89 help="Force overwritting of existing files") |
|
90 parser.add_option("-p", "--progress", action="store_true", default=False, |
|
91 help="Show progress bar") |
|
92 parser.add_option("-s", "--separate", action="store_true", default=False, |
|
93 help="Separate xmpp messages: send one message per line instead of one message alone.") |
|
94 parser.add_option("-n", "--new-line", action="store_true", default=False, |
|
95 help="Add a new line at the beginning of the input (usefull for ascii art ;))") |
|
96 |
|
97 (self.options, args) = parser.parse_args() |
|
98 |
|
99 if len(args) < 1 and not self.options.wait_file: |
|
100 parser.error("You must specify the destination JID (Jabber ID)") |
|
101 |
|
102 if self.options.wait_file: |
|
103 #several jid |
|
104 self.dest_jids = args |
|
105 else: |
|
106 #one dest_jid, other args are files |
|
107 self.dest_jid = JID(args[-1]) |
|
108 if not self.dest_jid.is_valid: |
|
109 error ("%s is not a valid JID !", self.dest_jid) |
|
110 exit(1) |
|
111 self.files = args[:-1] |
|
112 |
|
113 if not pbar_available and self.options.progress: |
|
114 self.options.progress = False |
|
115 error ("Option progress is not available, deactivated.") |
|
116 |
|
117 if self.options.progress or self.options.wait_file: |
|
118 self.start_loop = True #We have to use loop for these options |
|
119 else: |
|
120 self.start_loop = False |
|
121 |
|
122 |
|
123 return args |
|
124 |
|
125 def check_jabber_status(self): |
|
126 """Check that jabber status is allright""" |
|
127 if not self.bridge.isConnected(): |
|
128 error("SAT is not conneted, please connect before using jp") |
|
129 exit(1) |
|
130 |
|
131 |
|
132 def send_stdin(self): |
|
133 """Send incomming data on stdin to jabber contact""" |
|
134 header = "\n" if self.options.new_line else "" |
|
135 |
|
136 if self.options.separate: #we send stdin in several messages |
|
137 if header: |
|
138 self.bridge.sendMessage(self.dest_jid, header) |
|
139 while (True): |
|
140 line = sys.stdin.readline() |
|
141 if not line: |
|
142 break |
|
143 self.bridge.sendMessage(self.dest_jid, line.replace("\n","")) |
|
144 else: |
|
145 self.bridge.sendMessage(self.dest_jid, header + "".join(sys.stdin.readlines())) |
|
146 |
|
147 def send_files(self): |
|
148 """Send files to jabber contact""" |
|
149 |
|
150 for file in self.files: |
|
151 if not os.path.exists(file): |
|
152 error ("File [%s] doesn't exist !" % file) |
|
153 exit(1) |
|
154 if not self.options.bz2 and os.path.isdir(file): |
|
155 error ("[%s] is a dir ! Please send files inside or use compression" % file) |
|
156 exit(1) |
|
157 |
|
158 if self.options.bz2: |
|
159 tmpfile = (basename(self.files[0]) or basename(dirname(self.files[0])) ) + '.tar.bz2' #FIXME: tmp, need an algorithm to find a good name/path |
|
160 if os.path.exists(tmpfile): |
|
161 error ("tmp file (%s) already exists ! Please remove it", tmpfile) |
|
162 exit(1) |
|
163 warning("bz2 is an experimental option at an early dev stage, use with caution") |
|
164 #FIXME: check free space, writting perm, tmp dir, filename (watch for OS used) |
|
165 info("Starting compression, please wait...") |
|
166 sys.stdout.flush() |
|
167 bz2=tarfile.open(tmpfile, "w:bz2") |
|
168 for file in self.files: |
|
169 info("Adding %s", file) |
|
170 bz2.add(file) |
|
171 bz2.close() |
|
172 info("OK !") |
|
173 path = abspath(tmpfile) |
|
174 self.transfert_id = self.bridge.sendFile(self.dest_jid, path) |
|
175 else: |
|
176 for file in self.files: |
|
177 path = abspath(file) |
|
178 self.transfert_id = self.bridge.sendFile(self.dest_jid, path) #FIXME: show progress only for last transfert_id |
|
179 |
|
180 #TODO: manage ProgressBar |
|
181 |
|
182 def askConfirmation(self, type, id, data): |
|
183 """CB used for file transfert, accept files depending on parameters""" |
|
184 answer_data={} |
|
185 if type == "FILE_TRANSFERT": |
|
186 if self.dest_jids and not data['from'] in self.dest_jids: |
|
187 return #file is not sent by a filtered jid |
|
188 |
|
189 answer_data["dest_path"] = os.getcwd()+'/'+data['filename'] |
|
190 |
|
191 if self.options.force or not os.path.exists(answer_data["dest_path"]): |
|
192 self.bridge.confirmationAnswer(id, True, answer_data) |
|
193 info("Accepted file [%s] from %s", data['filename'], data['from']) |
|
194 self.transfert_id = id |
|
195 else: |
|
196 self.bridge.confirmationAnswer(id, False, answer_data) |
|
197 warning("Refused file [%s] from %s: a file with the same name already exist", data['filename'], data['from']) |
|
198 |
|
199 |
|
200 if not self.options.multiple and not self.options.progress: |
|
201 #we just accept one file |
|
202 self.loop.quit() |
|
203 |
|
204 |
|
205 def wait_file(self): |
|
206 """Wait for a file and write it on local dir""" |
|
207 self.bridge.register("askConfirmation", self.askConfirmation, "request") |
|
208 |
|
209 def progressCB(self): |
|
210 if self.transfert_id: |
|
211 data = self.bridge.getProgress(self.transfert_id) |
|
212 if data: |
|
213 if not data['position']: |
|
214 data['position'] = '0' |
|
215 if not self.pbar: |
|
216 #first answer, we must construct the bar |
|
217 self.pbar = ProgressBar(int(data['size']),["Progress: ",Percentage()," ",Bar()," ",FileTransferSpeed()," ",ETA()]) |
|
218 self.pbar.start() |
|
219 |
|
220 self.pbar.update(int(data['position'])) |
|
221 elif self.pbar: |
|
222 self.pbar.finish() |
|
223 if not self.options.multiple: |
|
224 self.loop.quit() |
|
225 return False |
|
226 |
|
227 return True |
|
228 |
|
229 def go(self): |
|
230 self.check_options() |
|
231 self.check_jabber_status() |
|
232 if self.options.wait_file: |
|
233 self.wait_file() |
|
234 else: |
|
235 if not self.files: #we send message only if there are no files to send |
|
236 self.send_stdin() |
|
237 else: |
|
238 self.send_files() |
|
239 |
|
240 if self.start_loop: |
|
241 self.loop = gobject.MainLoop() |
|
242 if self.options.progress: |
|
243 self.pbar = None |
|
244 gobject.timeout_add(10, self.progressCB) |
|
245 try: |
|
246 self.loop.run() |
|
247 except KeyboardInterrupt: |
|
248 info("User interruption: good bye") |
|
249 |
|
250 |
|
251 if __name__ == "__main__": |
|
252 jp = JP() |
|
253 jp.go() |