1 #! /usr/local/bin/python -O
4 ObexFTP Virtual FileSystem for Midnight Commander.
6 Author: Oleg BroytMann <phd@phd.pp.ru>.
7 Copyright (C) 2004 PhiloSoft Design.
10 Manipulate a cell phone's filesystem calling obexftp binary. This is a complete
11 user-mode solution, no kernel modules required (unlike SieFS or such). The
12 script implements all commands of Midnight VFS, except for undocumented "run";
13 but there are no runnable files in the cell phone. The script is written in
14 Python because I love Python, the best of all languages, and I need to parse
15 XML directory listings from obexftp.
17 The script requires Midnight Commander 3.1+ (http://www.ibiblio.org/mc/),
18 Python 2.2+ (http://www.python.org/),
19 OpenOBEX 1.0.1+ (http://openobex.sourceforge.net/)
20 and ObexFTP 0.10.4+ (http://triq.net/obexftp).
22 Edit the script, and correct the shebang path, if your python is not in the
23 /usr/local. Edit the full path to the obexftp binary (see below). Put the file
24 in the /usr/[local/]lib/mc/extfs, and add a line "obexftp" to the
25 /usr/[local/]lib/mc/extfs/extfs.ini.
27 Create somewhere a transport file. The transport file can have any name, and is
28 expected to be a text file with at least one line defining the transport to
29 your device. Other lines in the file are ignored.
31 First word in the line is a transport name - Bluetooth, TTY or IrDA. The name
34 For the Bluetooth transport put there a line "Bluetooth CP:AD:RE:SS channel",
35 where CP:AD:RE:SS is the hardware address of the device you want to connect to,
36 and channel is the OBEX File Transfer channel; you can discover the address and
37 the channel for your device by using commands like "hcitool scan" and "sdptool
40 For the TTY put the device name: "tty /dev/ttyUSB0".
42 For the IrDA: just put "IrDA" in the file.
44 Now run this "cd" command in the Midnight Commander (in the "bindings" files
45 the command is "%cd"): cd description#obexftp, where "description" is the name
46 of your file. The VFS script uses obexftp to connect to the device and list
47 files and directories. Please be warned that opening the VFS for the first time
48 is VERY slow, because the script needs to scan the entire cell phone's
49 filesystem. And there must be timeouts between connections, which don't make
50 the scanning faster. Midnight Commander caches the result, so you can browse
56 __revision__ = "$Id: obexftp,v 1.12 2004/06/23 16:05:34 phd Exp $"
57 __date__ = "$Date: 2004/06/23 16:05:34 $"[7:-2]
58 __author__ = "Oleg Broytmann <phd@phd.pp.ru>"
59 __copyright__ = "Copyright (C) 2004 PhiloSoft Design"
62 # Change this to suite your needs
63 obexftp_prog = "/usr/local/obex/bin/obexftp"
68 import xml.dom.minidom
69 from tempfile import mkdtemp
73 sys.stderr.write(msg + '\n')
82 ObexFTP Virtual FileSystem for Midnight Commander version %s
85 Put it in /usr/lib/mc/extfs. For more information read the source!""" % (
86 __version__, __author__, __copyright__
90 def setup_transport():
91 """Setup transport parameters for the obexftp program"""
92 transport_file = open(sys.argv[2], 'r')
93 line = transport_file.readline()
94 transport_file.close()
96 parts = line.strip().split()
97 transport = parts[0].lower()
99 if transport == "bluetooth":
100 return ' '.join(["-b", parts[1], "-B", parts[2]])
101 elif transport == "tty":
102 return ' '.join(["-t", parts[1]])
103 elif transport == "irda":
106 error("Unknown transport '%s'; expected 'bluetooth', 'tty' or 'irda'" % transport)
109 # Parse ObexFTP XML directory listings
111 class DirectoryEntry(object):
112 """Represent remote files and directories"""
114 def __init__(self, type):
118 self.perm = "-rw-rw-rw-"
119 elif type == "folder":
120 self.perm = "drwxrwxrwx"
122 raise ValueError, "unknown type '%s'; expected 'file' or 'folder'" % self.type
125 if not hasattr(self, "modified"): # telecom
126 return "01-01-70 0:0"
127 date, time = self.modified.split('T')
128 year, month, day = date[2:4], date[4:6], date[6:8]
129 hour, minute = time[:2], time[2:4]
130 return "%s-%s-%s %s:%s" % (month, day, year, hour, minute)
131 mtime = property(mtime)
134 if self.type == "file":
135 return """<%s: type=file, name=%s, size=%s, mtime=%s at 0x%x>""" % (
136 self.__class__.__name__, self.name, self.size, self.mtime, id(self)
138 if self.type == "folder":
139 if hasattr(self, "modified"):
140 return """<%s: type=directory, name=%s, mtime=%s at 0x%x>""" % (
141 self.__class__.__name__, self.name, self.mtime, id(self)
144 return """<%s: type=directory, name=%s at 0x%x>""" % (
145 self.__class__.__name__, self.name, id(self)
147 raise ValueError, "unknown type '%s'; expected 'file' or 'folder'" % self.type
149 def get_entries(dom, type):
151 for obj in dom.getElementsByTagName(type):
152 entry = DirectoryEntry(type)
153 attrs = obj.attributes
154 for i in range(attrs.length):
156 setattr(entry, attr.name, attr.value)
157 entries.append(entry)
161 def recursive_list(obexftp_args, directory):
162 """List the directory recursively"""
163 pipe = os.popen("%s %s -l '%s' 2>/dev/null" % (obexftp_prog, obexftp_args, directory), 'r')
164 listing = pipe.read()
170 dom = xml.dom.minidom.parseString(listing)
171 directories = get_entries(dom, "folder")
172 files = get_entries(dom, "file")
174 for entry in directories + files:
175 fullpath = "%s/%s" % (directory, entry.name)
176 if fullpath.startswith('//'): fullpath = fullpath[1:]
177 print entry.perm, "1 user group", entry.size, entry.mtime, fullpath
179 for entry in directories:
180 fullpath = "%s/%s" % (directory, entry.name)
181 if fullpath.startswith('//'): fullpath = fullpath[1:]
183 recursive_list(obexftp_args, fullpath)
186 """List the entire VFS"""
187 obexftp_args = setup_transport()
188 recursive_list(obexftp_args, '/')
191 # A unique directory for temporary files
197 tmpdir_name = mkdtemp(".tmp", "mcobex-")
198 os.chdir(tmpdir_name)
200 def cleanup_tmpdir():
202 shutil.rmtree(tmpdir_name)
205 def mcobex_copyout():
206 """Get a file from the VFS"""
207 obexftp_args = setup_transport()
208 obex_filename = sys.argv[3]
209 real_filename = sys.argv[4]
212 os.system("%s %s -g '%s' 2>/dev/null" % (obexftp_prog, obexftp_args, obex_filename))
214 os.rename(os.path.basename(obex_filename), real_filename)
221 """Put a file to the VFS"""
222 obexftp_args = setup_transport()
223 obex_filename = sys.argv[3]
224 real_filename = sys.argv[4]
225 dirname, filename = os.path.split(obex_filename)
228 os.rename(real_filename, filename)
229 os.system("%s %s -c '%s' -p '%s' 2>/dev/null" % (obexftp_prog, obexftp_args,
232 os.rename(filename, real_filename) # by some reason MC wants the file back
237 """Remove a file from the VFS"""
238 obexftp_args = setup_transport()
239 obex_filename = sys.argv[3]
240 os.system("%s %s -k '%s' 2>/dev/null" % (obexftp_prog, obexftp_args, obex_filename))
244 """Create a directory in the VFS"""
245 obexftp_args = setup_transport()
246 obex_dirname = sys.argv[3]
247 os.system("%s %s -C '%s' 2>/dev/null" % (obexftp_prog, obexftp_args, obex_dirname))
250 mcobex_rmdir = mcobex_rm
254 command = sys.argv[1]
255 procname = "mcobex_" + command
257 if not g.has_key(procname):
258 error("Unknown command %s" % command)