Changed "Copied" to "Copy".
[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.3.0"
41 __revision__ = "$Id: obexftp,v 1.4 2004/06/13 19:47:25 phd Exp $"
42 __date__ = "$Date: 2004/06/13 19:47: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, 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    dirname, filname = os.path.split(dummy_filename)
224
225    setup_tmpdir()
226    os.rename(real_filename, )
227    os.system("%s %s -c '%s' -p '%s' 2>/dev/null" % (obexftp_prog, obexftp_args,
228       dirname, filename
229    ))
230    cleanup_tmpdir()
231
232
233 def mcobex_rm():
234    """Remove a file from the VFS"""
235    obexftp_args = setup_transport()
236    dummy_filename = sys.argv[3]
237
238    real_file = open(".dummy.tmp", 'a')
239    real_file.write("Remove %s\n" % dummy_filename)
240    real_file.close()
241
242
243 def mcobex_mkdir():
244    """Create a directory in the VFS"""
245    obexftp_args = setup_transport()
246    dummy_dirname = sys.argv[3]
247
248    real_file = open(".dummy.tmp", 'a')
249    real_file.write("Create %s\n" % dummy_dirname)
250    real_file.close()
251
252
253 def mcobex_rmdir():
254    """Remove a directory from the VFS"""
255    obexftp_args = setup_transport()
256    dummy_dirname = sys.argv[3]
257
258    real_file = open(".dummy.tmp", 'a')
259    real_file.write("Remove %s\n" % dummy_dirname)
260    real_file.close()
261
262
263 g = globals()
264 command = sys.argv[1]
265 procname = "mcobex_" + command
266
267 if not g.has_key(procname):
268    error("Unknown command %s" % command)
269
270 try:
271    g[procname]()
272 except:
273    import traceback
274    error = open("error", 'a')
275    traceback.print_exc(file=error)
276    error.close()
277    sys.exit(1)