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 SiemensFS 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, IrDA or TTY. 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 The content with the IrDA just put "IrDA" in the file.
42 For the TTY put a device name: "tty /dev/rfcomm0".
44 Now run this "cd" command in the Midnight Commander (in the "bindings" files
45 the command is "%cd"): cd description#obexftp. The VFS script uses obexftp to
46 connect to the device and list files and directories. Please be warned that
47 opening the VFS for the first time is VERY slow, because the script needs to
48 scan the entire cell phone's filesystem. And there must be a timeout between
49 connections, which doesn't make the scanning process faster. Midnight Commander
50 caches the result, so you can browse directories quickly.
55 __revision__ = "$Id: obexftp,v 1.8 2004/06/16 16:17:43 phd Exp $"
56 __date__ = "$Date: 2004/06/16 16:17:43 $"[7:-2]
57 __author__ = "Oleg Broytmann <phd@phd.pp.ru>"
58 __copyright__ = "Copyright (C) 2004 PhiloSoft Design"
61 # Change this to suite your needs
62 obexftp_prog = "/usr/local/obex/bin/obexftp"
67 import xml.dom.minidom
68 from tempfile import mkdtemp
72 sys.stderr.write(msg + '\n')
81 ObexFTP Virtual FileSystem for Midnight Commander version %s
84 Put it in /usr/lib/mc/extfs. For more information read the source!""" % (
85 __version__, __author__, __copyright__
89 def setup_transport():
90 """Setup transport parameters for the obexftp program"""
91 transport_file = open(sys.argv[2], 'r')
92 line = transport_file.readline().strip()
93 transport_file.close()
96 transport = parts[0].lower()
98 if transport == "bluetooth":
99 return ' '.join(["-b", parts[1], "-B", parts[2]])
100 elif transport == "tty":
101 return ' '.join(["-t", parts[1]])
102 elif transport == "irda":
105 error("Unknown transport '%s'; expected 'bluetooth', 'tty' or 'irda'" % base_filename)
108 # Parse ObexFTP XML directory listings
110 class DirectoryEntry(object):
111 """Represent remote files and directories"""
113 def __init__(self, type):
117 self.perm = "-rw-rw-rw-"
118 elif type == "folder":
119 self.perm = "drwxrwxrwx"
121 raise ValueError, "unknown type '%s'; expected 'file' or 'folder'" % self.type
124 if not hasattr(self, "modified"): # telecom
125 return "01-01-70 0:0"
126 date, time = self.modified.split('T')
127 year, month, day = date[2:4], date[4:6], date[6:8]
128 hour, minute = time[:2], time[2:4]
129 return "%s-%s-%s %s:%s" % (month, day, year, hour, minute)
130 mtime = property(mtime)
133 if self.type == "file":
134 return """<%s: type=file, name=%s, size=%s, mtime=%s at 0x%x>""" % (
135 self.__class__.__name__, self.name, self.size, self.mtime, id(self)
137 if self.type == "folder":
138 if hasattr(self, "modified"):
139 return """<%s: type=directory, name=%s, mtime=%s at 0x%x>""" % (
140 self.__class__.__name__, self.name, self.mtime, id(self)
143 return """<%s: type=directory, name=%s at 0x%x>""" % (
144 self.__class__.__name__, self.name, id(self)
146 raise ValueError, "unknown type '%s'; expected 'file' or 'folder'" % self.type
148 def get_entries(dom, type):
150 for obj in dom.getElementsByTagName(type):
151 entry = DirectoryEntry(type)
152 attrs = obj.attributes
153 for i in range(attrs.length):
155 setattr(entry, attr.name, attr.value)
156 entries.append(entry)
160 def recursive_list(obexftp_args, directory):
161 """List the directory recursively"""
162 pipe = os.popen("%s %s -l '%s' 2>/dev/null" % (obexftp_prog, obexftp_args, directory), 'r')
163 listing = pipe.read()
169 dom = xml.dom.minidom.parseString(listing)
170 directories = get_entries(dom, "folder")
171 files = get_entries(dom, "file")
173 for entry in directories + files:
174 fullpath = "%s/%s" % (directory, entry.name)
175 if fullpath.startswith('//'): fullpath = fullpath[1:]
176 print entry.perm, "1 user group", entry.size, entry.mtime, fullpath
178 for entry in directories:
179 fullpath = "%s/%s" % (directory, entry.name)
180 if fullpath.startswith('//'): fullpath = fullpath[1:]
182 recursive_list(obexftp_args, fullpath)
185 """List the entire VFS"""
186 obexftp_args = setup_transport()
187 recursive_list(obexftp_args, '/')
190 # A unique directory for temporary files
196 tmpdir_name = mkdtemp(".tmp", "mcobex-")
197 os.chdir(tmpdir_name)
199 def cleanup_tmpdir():
201 shutil.rmtree(tmpdir_name)
204 def mcobex_copyout():
205 """Get a file from the VFS"""
206 obexftp_args = setup_transport()
207 obex_filename = sys.argv[3]
208 real_filename = sys.argv[4]
211 os.system("%s %s -g '%s' 2>/dev/null" % (obexftp_prog, obexftp_args, obex_filename))
213 os.rename(os.path.basename(obex_filename), real_filename)
220 """Put a file to the VFS"""
221 obexftp_args = setup_transport()
222 obex_filename = sys.argv[3]
223 real_filename = sys.argv[4]
224 dirname, filename = os.path.split(obex_filename)
227 os.rename(real_filename, filename)
228 os.system("%s %s -c '%s' -p '%s' 2>/dev/null" % (obexftp_prog, obexftp_args,
231 os.rename(filename, real_filename) # by some reason MC wants the file back
236 """Remove a file from the VFS"""
237 obexftp_args = setup_transport()
238 obex_filename = sys.argv[3]
239 os.system("%s %s -k '%s' 2>/dev/null" % (obexftp_prog, obexftp_args, obex_filename))
243 """Create a directory in the VFS"""
244 obexftp_args = setup_transport()
245 obex_dirname = sys.argv[3]
246 os.system("%s %s -C '%s' 2>/dev/null" % (obexftp_prog, obexftp_args, obex_dirname))
249 mcobex_rmdir = mcobex_rm
253 command = sys.argv[1]
254 procname = "mcobex_" + command
256 if not g.has_key(procname):
257 error("Unknown command %s" % command)