Temporary directory. CopyOut (get a file from the phone).
[extfs.d.git] / obexftp
1 #! /usr/local/bin/python -O
2
3 """
4 ObexFTP VFS for Midnight Commander. Manipulate a cell phone's filesystem using obexftp.
5
6 Author: Oleg BroytMann <phd@phd.pp.ru>.
7 Copyright (C) 2004 PhiloSoft Design.
8 License: GPL.
9
10 The script requires Midnight Commander 3.1+ (http://www.ibiblio.org/mc/),
11 Python 2.2+ (http://www.python.org/), OpenOBEX 1.0.1+ (http://openobex.sourceforge.net/)
12 and ObexFTP 0.10.4+ (http://triq.net/obexftp).
13
14 Edit the full path to the obexftp binary (see below). Put the file to the
15 /usr/[local/]lib/mc/extfs, and add a line "obexftp" to the
16 /usr/[local/]lib/mc/extfs/extfs.ini. Then create somewhere a file called
17 "irda", "bluetooth" or "tty" to connect to the device using IrDA, Bluetooth or
18 TTY transport.
19
20 For the "bluetooth" put there a line "CP:AD:RE:SS channel", where CP:AD:RE:SS
21 is the hardware address of the device you want to connect to, and channel is
22 the OBEX File Transfer channel; you can discover the address and the channel
23 for your device by using commands like "hcitool scan" and "sdptool browse".
24 Other lines in the file are ignored.
25
26 Put a device name like /dev/rfcomm0 into the "tty" file.
27
28 The content for the "irda" file is ignored.
29
30 Now run this "cd" command in the Midnight Commander (in the "bindings" files
31 the command is "%cd"): cd bluetooth#obexftp. The VFS script use obexftp to try
32 to connect to the device and list files and directories. Plese be warned that
33 opening the VFS for the first time is VERY slow, because the script needs to
34 scan the entire cell phone's filesystem. Often obexftp fails to list a
35 directory, and the script retries after a second or two timeouts, which don't
36 make the scanning process faster. Midnight Commander caches the result.
37
38 """
39
40 __version__ = "0.2.0"
41 __revision__ = "$Id: obexftp,v 1.3 2004/06/13 19:41:52 phd Exp $"
42 __date__ = "$Date: 2004/06/13 19:41:52 $"[7:-2]
43 __author__ = "Oleg Broytmann <phd@phd.pp.ru>"
44 __copyright__ = "Copyright (C) 2004 PhiloSoft Design"
45
46
47 # Change this to suite your needs
48 obexftp_prog = "/usr/local/obex/bin/obexftp"
49
50
51 import sys, time
52 import os, shutil
53 import xml.dom.minidom
54 from tempfile import mkdtemp
55
56
57 def log_error(msg):
58    sys.stderr.write(msg + '\n')
59
60 def error(msg):
61    log_error(msg + '\n')
62    sys.exit(1)
63
64
65 if len(sys.argv) < 2:
66    error("""\
67 It is not a program - it is a VFS for Midnight Commander.
68 Put it in /usr/lib/mc/extfs.""")
69
70
71 def setup_transport():
72    """Setup transport parameters for the obexftp program"""
73    transport_filename = sys.argv[2]
74    base_filename = os.path.basename(transport_filename)
75
76    if base_filename == "bluetooth":
77       transport_file = open(transport_filename, 'r')
78       line = transport_file.readline().strip()
79       transport_file.close()
80       bdaddr, channel = line.split()
81       return ' '.join(["-b", bdaddr, "-B", channel])
82    elif base_filename == "tty":
83       transport_file = open(transport_filename, 'r')
84       device = transport_file.readline().strip()
85       transport_file.close()
86       return ' '.join(["-t", device])
87    elif base_filename == "irda":
88       return "-i"
89    else:
90       error("Unknown transport '%s'; expected 'bluetooth', 'tty' or 'irda'" % base_filename)
91
92
93 # Parse ObexFTP XML directory listings
94
95 class DirectoryEntry(object):
96    def __init__(self, type):
97       self.type = type
98       self.size = 0
99       if type == "file":
100          self.perm = "-rw-rw-rw-"
101       elif type == "folder":
102          self.perm = "drw-rw-rw-"
103       else:
104          raise ValueError, "unknown type '%s'; expected 'file' or 'folder'" % self.type
105
106    def mtime(self):
107       if not hasattr(self, "modified"): # telecom
108          return "01-01-70 0:0"
109       date, time = self.modified.split('T')
110       year, month, day = date[2:4], date[4:6], date[6:8]
111       hour, minute = time[:2], time[2:4]
112       return "%s-%s-%s %s:%s" % (month, day, year, hour, minute)
113    mtime = property(mtime)
114
115    def __repr__(self):
116       if self.type == "file":
117          return """<%s: type=file, name=%s, size=%s, mtime=%s at 0x%x>""" % (
118             self.__class__.__name__, self.name, self.size, self.mtime, id(self)
119          )
120       if self.type == "folder":
121          if hasattr(self, "modified"):
122             return """<%s: type=directory, name=%s, mtime=%s at 0x%x>""" % (
123                self.__class__.__name__, self.name, self.mtime, id(self)
124             )
125          else: # telecom
126             return """<%s: type=directory, name=%s at 0x%x>""" % (
127                self.__class__.__name__, self.name, id(self)
128             )
129       raise ValueError, "unknown type '%s'; expected 'file' or 'folder'" % self.type
130
131 def get_entries(dom, tag):
132    entries = []
133    for subtag in dom.getElementsByTagName(tag):
134       entry = DirectoryEntry(tag)
135       attrs = subtag.attributes
136       for i in range(attrs.length):
137          attr = attrs.item(i)
138          setattr(entry, attr.name, attr.value)
139       entries.append(entry)
140    return entries
141
142
143 def recursive_list(obexftp_args, directory):
144    """List the directory recursively"""
145    debug = open("debug", 'a')
146    for i in range(3):
147       time.sleep(2*i)
148       pipe = os.popen("%s %s -l '%s' 2>/dev/null" % (obexftp_prog, obexftp_args, directory), 'r')
149       listing = pipe.read()
150       pipe.close()
151
152       if listing:
153          break
154
155       debug.write("Cannot list '%s', retrying...\n" % directory)
156
157    if not listing:
158       debug.write("Cannot list '%s'\n" % directory)
159       debug.close()
160       return
161
162    debug.write("Got listing of '%s'\n" % directory)
163
164    try:
165       dom = xml.dom.minidom.parseString(listing)
166    except:
167       obex_xml = open("obex.xml", 'a')
168       obex_xml.write(listing)
169       obex_xml.close()
170       raise
171
172    directories = get_entries(dom, "folder")
173    files = get_entries(dom, "file")
174
175    for entry in directories + files:
176       fullpath = "%s/%s" % (directory, entry.name)
177       if fullpath.startswith('//'): fullpath = fullpath[1:]
178       print entry.perm, "1 user group", entry.size, entry.mtime, fullpath
179    debug.close()
180
181    for entry in directories:
182       fullpath = "%s/%s" % (directory, entry.name)
183       if fullpath.startswith('//'): fullpath = fullpath[1:]
184       recursive_list(obexftp_args, fullpath)
185
186 def mcobex_list():
187    """List the entire VFS"""
188    obexftp_args = setup_transport()
189    recursive_list(obexftp_args, '/')
190
191
192 # A unique directory for temporary files
193
194 tmpdir_name = None
195
196 def setup_tmpdir():
197    global tmpdir_name
198    tmpdir_name = mkdtemp(".tmp", "mcobex-")
199    os.chdir(tmpdir_name)
200
201 def cleanup_tmpdir():
202    os.chdir(os.pardir)
203    shutil.rmtree(tmpdir_name)
204
205
206 def mcobex_copyout():
207    """Get a file from the VFS"""
208    obexftp_args = setup_transport()
209    dummy_filename = sys.argv[3]
210    real_filename = sys.argv[4]
211
212    setup_tmpdir()
213    os.system("%s %s -g '%s' 2>/dev/null" % (obexftp_prog, obexftp_args, dummy_filename))
214    os.rename(os.path.basename(dummy_filename), real_filename)
215    cleanup_tmpdir()
216
217
218 def mcobex_copyin():
219    """Put a file to the VFS"""
220    obexftp_args = setup_transport()
221    dummy_filename = sys.argv[3]
222    real_filename = sys.argv[4]
223
224    real_file = open(real_filename + "-dummy.tmp", 'w')
225    real_file.write("Copied from %s\n" % real_filename)
226    real_file.write("Copied  to  %s\n" % dummy_filename)
227    real_file.close()
228
229
230 def mcobex_rm():
231    """Remove a file from the VFS"""
232    obexftp_args = setup_transport()
233    dummy_filename = sys.argv[3]
234
235    real_file = open(".dummy.tmp", 'a')
236    real_file.write("Remove %s\n" % dummy_filename)
237    real_file.close()
238
239
240 def mcobex_mkdir():
241    """Create a directory in the VFS"""
242    obexftp_args = setup_transport()
243    dummy_dirname = sys.argv[3]
244
245    real_file = open(".dummy.tmp", 'a')
246    real_file.write("Create %s\n" % dummy_dirname)
247    real_file.close()
248
249
250 def mcobex_rmdir():
251    """Remove a directory from the VFS"""
252    obexftp_args = setup_transport()
253    dummy_dirname = sys.argv[3]
254
255    real_file = open(".dummy.tmp", 'a')
256    real_file.write("Remove %s\n" % dummy_dirname)
257    real_file.close()
258
259
260 g = globals()
261 command = sys.argv[1]
262 procname = "mcobex_" + command
263
264 if not g.has_key(procname):
265    error("Unknown command %s" % command)
266
267 try:
268    g[procname]()
269 except:
270    import traceback
271    error = open("error", 'a')
272    traceback.print_exc(file=error)
273    error.close()
274    sys.exit(1)