#! /usr/local/bin/python -O """ ObexFTP VFS for Midnight Commander. Manipulate a cell phone's filesystem using obexftp. Author: Oleg BroytMann . Copyright (C) 2004 PhiloSoft Design. License: GPL. The script requires Midnight Commander 3.1+ (http://www.ibiblio.org/mc/), Python 2.2+ (http://www.python.org/), OpenOBEX 1.0.1+ (http://openobex.sourceforge.net/) and ObexFTP 0.10.4+ (http://triq.net/obexftp). Edit the full path to the obexftp binary (see below). Put the file to the /usr/[local/]lib/mc/extfs, and add a line "obexftp" to the /usr/[local/]lib/mc/extfs/extfs.ini. Then create somewhere a file called "irda", "bluetooth" or "tty" to connect to the device using IrDA, Bluetooth or TTY transport. For the "bluetooth" put there a line "CP:AD:RE:SS channel", where CP:AD:RE:SS is the hardware address of the device you want to connect to, and channel is the OBEX File Transfer channel; you can discover the address and the channel for your device by using commands like "hcitool scan" and "sdptool browse". Other lines in the file are ignored. Put a device name like /dev/rfcomm0 into the "tty" file. The content for the "irda" file is ignored. Now run this "cd" command in the Midnight Commander (in the "bindings" files the command is "%cd"): cd bluetooth#obexftp. The VFS script use obexftp to try to connect to the device and list files and directories. Plese be warned that opening the VFS for the first time is VERY slow, because the script needs to scan the entire cell phone's filesystem. Often obexftp fails to list a directory, and the script retries after a second or two timeouts, which don't make the scanning process faster. Midnight Commander caches the result. """ __version__ = "0.3.0" __revision__ = "$Id: obexftp,v 1.4 2004/06/13 19:47:25 phd Exp $" __date__ = "$Date: 2004/06/13 19:47:25 $"[7:-2] __author__ = "Oleg Broytmann " __copyright__ = "Copyright (C) 2004 PhiloSoft Design" # Change this to suite your needs obexftp_prog = "/usr/local/obex/bin/obexftp" import sys, time import os, shutil import xml.dom.minidom from tempfile import mkdtemp def log_error(msg): sys.stderr.write(msg + '\n') def error(msg): log_error(msg + '\n') sys.exit(1) if len(sys.argv) < 2: error("""\ It is not a program - it is a VFS for Midnight Commander. Put it in /usr/lib/mc/extfs.""") def setup_transport(): """Setup transport parameters for the obexftp program""" transport_filename = sys.argv[2] base_filename = os.path.basename(transport_filename) if base_filename == "bluetooth": transport_file = open(transport_filename, 'r') line = transport_file.readline().strip() transport_file.close() bdaddr, channel = line.split() return ' '.join(["-b", bdaddr, "-B", channel]) elif base_filename == "tty": transport_file = open(transport_filename, 'r') device = transport_file.readline().strip() transport_file.close() return ' '.join(["-t", device]) elif base_filename == "irda": return "-i" else: error("Unknown transport '%s'; expected 'bluetooth', 'tty' or 'irda'" % base_filename) # Parse ObexFTP XML directory listings class DirectoryEntry(object): def __init__(self, type): self.type = type self.size = 0 if type == "file": self.perm = "-rw-rw-rw-" elif type == "folder": self.perm = "drw-rw-rw-" else: raise ValueError, "unknown type '%s'; expected 'file' or 'folder'" % self.type def mtime(self): if not hasattr(self, "modified"): # telecom return "01-01-70 0:0" date, time = self.modified.split('T') year, month, day = date[2:4], date[4:6], date[6:8] hour, minute = time[:2], time[2:4] return "%s-%s-%s %s:%s" % (month, day, year, hour, minute) mtime = property(mtime) def __repr__(self): if self.type == "file": return """<%s: type=file, name=%s, size=%s, mtime=%s at 0x%x>""" % ( self.__class__.__name__, self.name, self.size, self.mtime, id(self) ) if self.type == "folder": if hasattr(self, "modified"): return """<%s: type=directory, name=%s, mtime=%s at 0x%x>""" % ( self.__class__.__name__, self.name, self.mtime, id(self) ) else: # telecom return """<%s: type=directory, name=%s at 0x%x>""" % ( self.__class__.__name__, self.name, id(self) ) raise ValueError, "unknown type '%s'; expected 'file' or 'folder'" % self.type def get_entries(dom, tag): entries = [] for subtag in dom.getElementsByTagName(tag): entry = DirectoryEntry(tag) attrs = subtag.attributes for i in range(attrs.length): attr = attrs.item(i) setattr(entry, attr.name, attr.value) entries.append(entry) return entries def recursive_list(obexftp_args, directory): """List the directory recursively""" debug = open("debug", 'a') for i in range(3): time.sleep(2*i) pipe = os.popen("%s %s -l '%s' 2>/dev/null" % (obexftp_prog, obexftp_args, directory), 'r') listing = pipe.read() pipe.close() if listing: break debug.write("Cannot list '%s', retrying...\n" % directory) if not listing: debug.write("Cannot list '%s'\n" % directory) debug.close() return debug.write("Got listing of '%s'\n" % directory) try: dom = xml.dom.minidom.parseString(listing) except: obex_xml = open("obex.xml", 'a') obex_xml.write(listing) obex_xml.close() raise directories = get_entries(dom, "folder") files = get_entries(dom, "file") for entry in directories + files: fullpath = "%s/%s" % (directory, entry.name) if fullpath.startswith('//'): fullpath = fullpath[1:] print entry.perm, "1 user group", entry.size, entry.mtime, fullpath debug.close() for entry in directories: fullpath = "%s/%s" % (directory, entry.name) if fullpath.startswith('//'): fullpath = fullpath[1:] recursive_list(obexftp_args, fullpath) def mcobex_list(): """List the entire VFS""" obexftp_args = setup_transport() recursive_list(obexftp_args, '/') # A unique directory for temporary files tmpdir_name = None def setup_tmpdir(): global tmpdir_name tmpdir_name = mkdtemp(".tmp", "mcobex-") os.chdir(tmpdir_name) def cleanup_tmpdir(): os.chdir(os.pardir) shutil.rmtree(tmpdir_name) def mcobex_copyout(): """Get a file from the VFS""" obexftp_args = setup_transport() dummy_filename = sys.argv[3] real_filename = sys.argv[4] setup_tmpdir() os.system("%s %s -g '%s' 2>/dev/null" % (obexftp_prog, obexftp_args, dummy_filename)) os.rename(os.path.basename(dummy_filename), real_filename) cleanup_tmpdir() def mcobex_copyin(): """Put a file to the VFS""" obexftp_args = setup_transport() dummy_filename = sys.argv[3] real_filename = sys.argv[4] dirname, filname = os.path.split(dummy_filename) setup_tmpdir() os.rename(real_filename, ) os.system("%s %s -c '%s' -p '%s' 2>/dev/null" % (obexftp_prog, obexftp_args, dirname, filename )) cleanup_tmpdir() def mcobex_rm(): """Remove a file from the VFS""" obexftp_args = setup_transport() dummy_filename = sys.argv[3] real_file = open(".dummy.tmp", 'a') real_file.write("Remove %s\n" % dummy_filename) real_file.close() def mcobex_mkdir(): """Create a directory in the VFS""" obexftp_args = setup_transport() dummy_dirname = sys.argv[3] real_file = open(".dummy.tmp", 'a') real_file.write("Create %s\n" % dummy_dirname) real_file.close() def mcobex_rmdir(): """Remove a directory from the VFS""" obexftp_args = setup_transport() dummy_dirname = sys.argv[3] real_file = open(".dummy.tmp", 'a') real_file.write("Remove %s\n" % dummy_dirname) real_file.close() g = globals() command = sys.argv[1] procname = "mcobex_" + command if not g.has_key(procname): error("Unknown command %s" % command) try: g[procname]() except: import traceback error = open("error", 'a') traceback.print_exc(file=error) error.close() sys.exit(1)