Wrote more comments. Added URLs for all required packages.
[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.1.0"
41 __revision__ = "$Id: obexftp,v 1.2 2004/06/13 18:49:25 phd Exp $"
42 __date__ = "$Date: 2004/06/13 18:49:25 $"[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, os, time
52 import xml.dom.minidom
53
54 def log_error(msg):
55    sys.stderr.write(msg + '\n')
56
57 def error(msg):
58    log_error(msg + '\n')
59    sys.exit(1)
60
61
62 if len(sys.argv) < 2:
63    error("""\
64 It is not a program - it is a VFS for Midnight Commander.
65 Put it in /usr/lib/mc/extfs.""")
66
67
68 def setup_transport():
69    """Setup transport parameters for the obexftp program"""
70    transport_filename = sys.argv[2]
71    base_filename = os.path.basename(transport_filename)
72
73    if base_filename == "bluetooth":
74       transport_file = open(transport_filename, 'r')
75       line = transport_file.readline().strip()
76       transport_file.close()
77       bdaddr, channel = line.split()
78       return ' '.join(["-b", bdaddr, "-B", channel])
79    elif base_filename == "tty":
80       transport_file = open(transport_filename, 'r')
81       device = transport_file.readline().strip()
82       transport_file.close()
83       return ' '.join(["-t", device])
84    elif base_filename == "irda":
85       return "-i"
86    else:
87       error("Unknown transport '%s'; expected 'bluetooth', 'tty' or 'irda'" % base_filename)
88
89
90 # Parse ObexFTP XML directory listings
91
92 class DirectoryEntry(object):
93    def __init__(self, type):
94       self.type = type
95       self.size = 0
96       if type == "file":
97          self.perm = "-rw-rw-rw-"
98       elif type == "folder":
99          self.perm = "drw-rw-rw-"
100       else:
101          raise ValueError, "unknown type '%s'; expected 'file' or 'folder'" % self.type
102
103    def mtime(self):
104       if not hasattr(self, "modified"): # telecom
105          return "01-01-70 0:0"
106       date, time = self.modified.split('T')
107       year, month, day = date[2:4], date[4:6], date[6:8]
108       hour, minute = time[:2], time[2:4]
109       return "%s-%s-%s %s:%s" % (month, day, year, hour, minute)
110    mtime = property(mtime)
111
112    def __repr__(self):
113       if self.type == "file":
114          return """<%s: type=file, name=%s, size=%s, mtime=%s at 0x%x>""" % (
115             self.__class__.__name__, self.name, self.size, self.mtime, id(self)
116          )
117       if self.type == "folder":
118          if hasattr(self, "modified"):
119             return """<%s: type=directory, name=%s, mtime=%s at 0x%x>""" % (
120                self.__class__.__name__, self.name, self.mtime, id(self)
121             )
122          else: # telecom
123             return """<%s: type=directory, name=%s at 0x%x>""" % (
124                self.__class__.__name__, self.name, id(self)
125             )
126       raise ValueError, "unknown type '%s'; expected 'file' or 'folder'" % self.type
127
128 def get_entries(dom, tag):
129    entries = []
130    for subtag in dom.getElementsByTagName(tag):
131       entry = DirectoryEntry(tag)
132       attrs = subtag.attributes
133       for i in range(attrs.length):
134          attr = attrs.item(i)
135          setattr(entry, attr.name, attr.value)
136       entries.append(entry)
137    return entries
138
139
140 def _recursive_list(obexftp_args, directory):
141    """List the directory recursively"""
142    debug = open("debug", 'a')
143    for i in range(3):
144       time.sleep(2*i)
145       pipe = os.popen("%s %s -l '%s' 2>/dev/null" % (obexftp_prog, obexftp_args, directory), 'r')
146       listing = pipe.read()
147       pipe.close()
148
149       if listing:
150          break
151
152       debug.write("Cannot list '%s', retrying...\n" % directory)
153
154    if not listing:
155       debug.write("Cannot list '%s'\n" % directory)
156       debug.close()
157       return
158
159    debug.write("Got listing of '%s'\n" % directory)
160
161    try:
162       dom = xml.dom.minidom.parseString(listing)
163    except:
164       obex_xml = open("obex.xml", 'a')
165       obex_xml.write(listing)
166       obex_xml.close()
167       raise
168
169    directories = get_entries(dom, "folder")
170    files = get_entries(dom, "file")
171
172    for entry in directories + files:
173       fullpath = "%s/%s" % (directory, entry.name)
174       if fullpath.startswith('//'): fullpath = fullpath[1:]
175       print entry.perm, "1 user group", entry.size, entry.mtime, fullpath
176    debug.close()
177
178    for entry in directories:
179       fullpath = "%s/%s" % (directory, entry.name)
180       if fullpath.startswith('//'): fullpath = fullpath[1:]
181       _recursive_list(obexftp_args, fullpath)
182
183 def mcobex_list():
184    """List the entire VFS"""
185    obexftp_args = setup_transport()
186    _recursive_list(obexftp_args, '/')
187
188
189 def mcobex_copyout():
190    """Get a file from the VFS"""
191    obexftp_args = setup_transport()
192    dummy_filename = sys.argv[3]
193    real_filename = sys.argv[4]
194
195    real_file = open(real_filename, 'w')
196    real_file.write("Copied from %s\n" % dummy_filename)
197    real_file.write("Copied  to  %s\n" % real_filename)
198    real_file.close()
199
200
201 def mcobex_copyin():
202    """Put a file to the VFS"""
203    obexftp_args = setup_transport()
204    dummy_filename = sys.argv[3]
205    real_filename = sys.argv[4]
206
207    real_file = open(real_filename + "-dummy.tmp", 'w')
208    real_file.write("Copied from %s\n" % real_filename)
209    real_file.write("Copied  to  %s\n" % dummy_filename)
210    real_file.close()
211
212
213 def mcobex_rm():
214    """Remove a file from the VFS"""
215    obexftp_args = setup_transport()
216    dummy_filename = sys.argv[3]
217
218    real_file = open(".dummy.tmp", 'a')
219    real_file.write("Remove %s\n" % dummy_filename)
220    real_file.close()
221
222
223 def mcobex_mkdir():
224    """Create a directory in the VFS"""
225    obexftp_args = setup_transport()
226    dummy_dirname = sys.argv[3]
227
228    real_file = open(".dummy.tmp", 'a')
229    real_file.write("Create %s\n" % dummy_dirname)
230    real_file.close()
231
232
233 def mcobex_rmdir():
234    """Remove a directory from the VFS"""
235    obexftp_args = setup_transport()
236    dummy_dirname = sys.argv[3]
237
238    real_file = open(".dummy.tmp", 'a')
239    real_file.write("Remove %s\n" % dummy_dirname)
240    real_file.close()
241
242
243 g = globals()
244 command = sys.argv[1]
245 procname = "mcobex_" + command
246
247 if not g.has_key(procname):
248    error("Unknown command %s" % command)
249
250 try:
251    g[procname]()
252 except:
253    import traceback
254    error = open("error", 'a')
255    traceback.print_exc(file=error)
256    error.close()
257    sys.exit(1)