]> git.phdru.name Git - extfs.d.git/blobdiff - obexftp
Ignore my Siemens-S55 obexftp transport file and obexftp-mcextfs.log.
[extfs.d.git] / obexftp
diff --git a/obexftp b/obexftp
index 5aa46c523b9b957898e6507927b6a05a70b52382..1f4a0339b324b4e165a0a3f5fc4040a06fe4a143 100755 (executable)
--- a/obexftp
+++ b/obexftp
 ObexFTP Virtual FileSystem for Midnight Commander.
 
 Author: Oleg BroytMann <phd@phd.pp.ru>.
 ObexFTP Virtual FileSystem for Midnight Commander.
 
 Author: Oleg BroytMann <phd@phd.pp.ru>.
-Copyright (C) 2004 PhiloSoft Design.
+Copyright (C) 2004, 2005 PhiloSoft Design.
 License: GPL.
 
 License: GPL.
 
-Manipulate a cell phone's filesystem calling obexftp binary. This is a complete
-user-mode solution, no kernel modules required (unlike SieFS or such). The
-script implements all commands of Midnight VFS, except for undocumented "run";
-but there are no runnable files in the cell phone. The script is written in
-Python because I love Python, the best of all languages, and I need to parse
-XML directory listings from obexftp.
+Manipulate a cell phone's filesystem calling obexftp binary. This is a
+complete user-mode solution, no kernel modules required (unlike SieFS or
+such). The script implements all commands of Midnight VFS, except for
+undocumented "run"; anyway there are no runnable files in the cell phone. The
+script is written in Python because I love Python, the best of all languages ;),
+and I need to parse XML directory listings from obexftp.
 
 The script requires Midnight Commander 3.1+ (http://www.ibiblio.org/mc/),
 
 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).
+Python 2.3+ (http://www.python.org/),
+OpenOBEX 1.0.1+ (http://openobex.sourceforge.net/) and
+ObexFTP 0.10.4+ (http://triq.net/obexftp).
 
 Edit the script, and correct the shebang path, if your python is not in the
 /usr/local. Edit the full path to the obexftp binary (see below). Put the file
 in the /usr/[local/]lib/mc/extfs, and add a line "obexftp" to the
 /usr/[local/]lib/mc/extfs/extfs.ini.
 
 
 Edit the script, and correct the shebang path, if your python is not in the
 /usr/local. Edit the full path to the obexftp binary (see below). Put the file
 in the /usr/[local/]lib/mc/extfs, and add a line "obexftp" to the
 /usr/[local/]lib/mc/extfs/extfs.ini.
 
-Create somewhere a transport file. The transport file can have any name, and is
-expected to be a text file with at least one line defining the transport to
+Create somewhere a transport file. The transport file may have any name, and
+is expected to be a text file with at least one line defining the transport to
 your device. Other lines in the file are ignored.
 
 your device. Other lines in the file are ignored.
 
-First word in the line is a transport name - Bluetooth, IrDA or TTY. The name
+First word in the line is a transport name - Bluetooth, TTY or IrDA. The name
 is case-insensitive.
 
 For the Bluetooth transport put there a line "Bluetooth CP:AD:RE:SS channel",
 is case-insensitive.
 
 For the Bluetooth transport put there a line "Bluetooth 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".
+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".
 
 
-For the TTY put a device name: "tty /dev/rfcomm0".
+For the TTY put the device name: "tty /dev/ttyUSB0".
 
 For the IrDA: just put "IrDA" in the file.
 
 
 For the IrDA: just put "IrDA" in the file.
 
-Now run this "cd" command in the Midnight Commander (in the "bindings" files
-the command is "%cd"): cd description#obexftp, where "description" is the name
-of your file. The VFS script uses obexftp to connect to the device and list
-files and directories. Please be warned that opening the VFS for the first time
-is VERY slow, because the script needs to scan the entire cell phone's
+Now run this "cd" command in the Midnight Commander (in the "bindings" file
+the command is "%cd"): cd transport#obexftp, where "transport" is the name of
+your transport file. The script uses obexftp to connect to the device and list
+files and directories. Please 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. And there must be timeouts between connections, which don't make
 filesystem. And there must be timeouts between connections, which don't make
-the scanning faster. Midnight Commander caches the result, so you can browse
-directories quickly.
-
+the scanning faster. Midnight Commander caches the result so you can browse
+and manipulate files and directories quickly.
+
+Please note that manipulating the filesystem using your phone's internal
+filemanager in parallel with the VFS leads to disagreement between the VFS
+cache and the phone. It is not very dangerous but inconvenient. There is no
+way to clear the VFS cache in Midnight Commander and reread the filesystem.
+You have to exit the VFS (cd /, for example) and return back using cd
+transport#obexftp command. Sometimes even this doesn't help - Midnight
+Commander shows the same cached VFS image. Exit Midnight Commander and
+restart it.
+
+If something goes wrong set the logging level (see setLevel() below) to INFO
+or DEBUG and look in the obexftp-mcextfs.log file. The file is put in the same
+directory as the transport file, if it possible; if not the file will be put
+into a temporary directory, usually /tmp, or /var/tmp, or whatever directory
+is named in $TMP environment variable.
 """
 
 """
 
-__version__ = "1.0.0"
-__revision__ = "$Id: obexftp,v 1.10 2004/06/19 17:32:52 phd Exp $"
-__date__ = "$Date: 2004/06/19 17:32:52 $"[7:-2]
+__version__ = "1.2.2"
+__revision__ = "$Id$"
+__date__ = "$Date$"[7:-2]
 __author__ = "Oleg Broytmann <phd@phd.pp.ru>"
 __author__ = "Oleg Broytmann <phd@phd.pp.ru>"
-__copyright__ = "Copyright (C) 2004 PhiloSoft Design"
+__copyright__ = "Copyright (C) 2004, 2005 PhiloSoft Design"
 
 
 # Change this to suite your needs
 obexftp_prog = "/usr/local/obex/bin/obexftp"
 
 
 
 
 # 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
-
+import sys, os, shutil
+from time import sleep
+import xml.dom.minidom, locale
+from tempfile import mkstemp, mkdtemp, _candidate_tempdir_list
 
 
-def log_error(msg):
-   sys.stderr.write(msg + '\n')
 
 
-def error(msg):
-   log_error(msg)
-   sys.exit(1)
+import logging
+logger = logging.getLogger('obexftp-mcextfs')
+log_err_handler = logging.StreamHandler(sys.stderr)
+logger.addHandler(log_err_handler)
+logger.setLevel(logging.ERROR)
 
 
 
 
-if len(sys.argv) < 2:
-   error("""\
+if len(sys.argv) < 3:
+   logger.critical("""\
 ObexFTP Virtual FileSystem for Midnight Commander version %s
 Author: %s
 %s
 ObexFTP Virtual FileSystem for Midnight Commander version %s
 Author: %s
 %s
-Put it in /usr/lib/mc/extfs. For more information read the source!""" % (
-   __version__, __author__, __copyright__
-))
 
 
+This is not a program. It is ObexFTP Virtual FileSystem for Midnight Commander.
+Put it in /usr/lib/mc/extfs. For more information read the source!""",
+   __version__, __author__, __copyright__
+)
+   sys.exit(1)
 
 
-def setup_transport():
-   """Setup transport parameters for the obexftp program"""
-   transport_file = open(sys.argv[2], 'r')
-   line = transport_file.readline().strip()
-   transport_file.close()
 
 
-   parts = line.split()
-   transport = parts[0].lower()
+tempdirlist = _candidate_tempdir_list()
+tempdirlist.insert(0, os.path.abspath(os.path.dirname(sys.argv[2])))
 
 
-   if transport == "bluetooth":
-      return ' '.join(["-b", parts[1], "-B", parts[2]])
-   elif transport == "tty":
-      return ' '.join(["-t", parts[1]])
-   elif transport == "irda":
-      return "-i"
+found = False
+for tempdir in tempdirlist:
+   try:
+      logfile_name = os.path.join(tempdir, 'obexftp-mcextfs.log')
+      logfile = open(logfile_name, 'w')
+   except IOError:
+      pass
    else:
    else:
-      error("Unknown transport '%s'; expected 'bluetooth', 'tty' or 'irda'" % base_filename)
+      found = True
+      logfile.close()
+      break
+
+if not found:
+   logger.critical("Cannot initialize error log file in directories %s" % str(tempdirlist))
+   sys.exit(1)
+
+logger.removeHandler(log_err_handler)
+logger.addHandler(logging.FileHandler(logfile_name))
+
+locale.setlocale(locale.LC_ALL, '')
+charset = locale.getpreferredencoding()
 
 
 # Parse ObexFTP XML directory listings
 
 class DirectoryEntry(object):
 
 
 # Parse ObexFTP XML directory listings
 
 class DirectoryEntry(object):
-   """Represent remote files and directories"""
+   """Represent a remote file or a directory"""
 
    def __init__(self, type):
       self.type = type
 
    def __init__(self, type):
       self.type = type
@@ -158,13 +181,58 @@ def get_entries(dom, type):
    return entries
 
 
    return entries
 
 
-def recursive_list(obexftp_args, directory):
+# 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 _read(fd):
+   out = []
+   while True:
+      s = os.read(fd, 1024)
+      if not s:
+         break
+      out.append(s)
+   return ''.join(out)
+
+
+def _run(*args):
+   """Run the obexftp binary catching errors"""
+
+   out_fd, out_filename = mkstemp(".tmp", "mcobex-", tmpdir_name)
+   err_fd, err_filename = mkstemp(".tmp", "mcobex-", tmpdir_name)
+
+   command = "%s %s %s >%s 2>%s" % (obexftp_prog, obexftp_args, ' '.join(args),
+      out_filename, err_filename)
+
+   logger.debug("Running command %s", command)
+   os.system(command)
+
+   result = _read(out_fd)
+   os.remove(out_filename)
+
+   errors = _read(err_fd)
+   os.remove(err_filename)
+
+   logger.debug("    result: %s", result)
+   logger.debug("    errors: %s", errors)
+   return result, errors
+
+
+def recursive_list(directory='/'):
    """List the directory recursively"""
    """List the directory recursively"""
-   pipe = os.popen("%s %s -l '%s' 2>/dev/null" % (obexftp_prog, obexftp_args, directory), 'r')
-   listing = pipe.read()
-   pipe.close()
+   listing, errors = _run("-l '%s'" % directory)
 
    if not listing:
 
    if not listing:
+      logger.error("Error reading XML listing: %s", errors)
       return
 
    dom = xml.dom.minidom.parseString(listing)
       return
 
    dom = xml.dom.minidom.parseString(listing)
@@ -173,88 +241,137 @@ def recursive_list(obexftp_args, directory):
 
    for entry in directories + files:
       fullpath = "%s/%s" % (directory, entry.name)
 
    for entry in directories + files:
       fullpath = "%s/%s" % (directory, entry.name)
+      fullpath = fullpath.encode(charset)
       if fullpath.startswith('//'): fullpath = fullpath[1:]
       print entry.perm, "1 user group", entry.size, entry.mtime, fullpath
 
    for entry in directories:
       fullpath = "%s/%s" % (directory, entry.name)
       if fullpath.startswith('//'): fullpath = fullpath[1:]
       if fullpath.startswith('//'): fullpath = fullpath[1:]
       print entry.perm, "1 user group", entry.size, entry.mtime, fullpath
 
    for entry in directories:
       fullpath = "%s/%s" % (directory, entry.name)
       if fullpath.startswith('//'): fullpath = fullpath[1:]
-      time.sleep(1)
-      recursive_list(obexftp_args, fullpath)
+      sleep(1)
+      recursive_list(fullpath)
 
 def mcobex_list():
    """List the entire VFS"""
 
 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)
+   setup_tmpdir()
+   try:
+      recursive_list()
+   finally:
+      cleanup_tmpdir()
 
 
 def mcobex_copyout():
    """Get a file from the VFS"""
 
 
 def mcobex_copyout():
    """Get a file from the VFS"""
-   obexftp_args = setup_transport()
    obex_filename = sys.argv[3]
    real_filename = sys.argv[4]
 
    setup_tmpdir()
    obex_filename = sys.argv[3]
    real_filename = sys.argv[4]
 
    setup_tmpdir()
-   os.system("%s %s -g '%s' 2>/dev/null" % (obexftp_prog, obexftp_args, obex_filename))
    try:
    try:
-      os.rename(os.path.basename(obex_filename), real_filename)
-   except OSError:
-      pass
-   cleanup_tmpdir()
+      _run("-g '%s'" % obex_filename)
+      try:
+         os.rename(os.path.basename(obex_filename), real_filename)
+      except OSError:
+         logger.exception("Error CopyOut %s to %s", obex_filename, real_filename)
+   finally:
+      cleanup_tmpdir()
 
 
 def mcobex_copyin():
    """Put a file to the VFS"""
 
 
 def mcobex_copyin():
    """Put a file to the VFS"""
-   obexftp_args = setup_transport()
    obex_filename = sys.argv[3]
    real_filename = sys.argv[4]
    dirname, filename = os.path.split(obex_filename)
 
    setup_tmpdir()
    obex_filename = sys.argv[3]
    real_filename = sys.argv[4]
    dirname, filename = os.path.split(obex_filename)
 
    setup_tmpdir()
-   os.rename(real_filename, filename)
-   os.system("%s %s -c '%s' -p '%s' 2>/dev/null" % (obexftp_prog, obexftp_args,
-      dirname, filename
-   ))
-   os.rename(filename, real_filename) # by some reason MC wants the file back
-   cleanup_tmpdir()
+   try:
+      try:
+         os.rename(real_filename, filename)
+         _run("-c '%s' -p '%s'" % (dirname, filename))
+         os.rename(filename, real_filename) # by some reason MC wants the file back
+      except OSError:
+         logger.exception("Error CopyIn %s to %s", real_filename, obex_filename)
+   finally:
+      cleanup_tmpdir()
 
 
 def mcobex_rm():
    """Remove a file from the VFS"""
 
 
 def mcobex_rm():
    """Remove a file from the VFS"""
-   obexftp_args = setup_transport()
    obex_filename = sys.argv[3]
    obex_filename = sys.argv[3]
-   os.system("%s %s -k '%s' 2>/dev/null" % (obexftp_prog, obexftp_args, obex_filename))
+   try:
+      _run("-k '%s'" % obex_filename)
+   finally:
+      cleanup_tmpdir()
 
 
 def mcobex_mkdir():
    """Create a directory in the VFS"""
 
 
 def mcobex_mkdir():
    """Create a directory in the VFS"""
-   obexftp_args = setup_transport()
    obex_dirname = sys.argv[3]
    obex_dirname = sys.argv[3]
-   os.system("%s %s -C '%s' 2>/dev/null" % (obexftp_prog, obexftp_args, obex_dirname))
+   try:
+      _run("-C '%s'" % obex_dirname)
+   finally:
+      cleanup_tmpdir()
 
 
 mcobex_rmdir = mcobex_rm
 
 
 
 
 mcobex_rmdir = mcobex_rm
 
 
-g = globals()
+def transport_error(error_str):
+   logger.critical("Error parsing the transport file: %s" % error_str)
+   sys.exit(1)
+
+def setup_transport():
+   """Setup transport parameters for the obexftp program"""
+   try:
+      transport_file = open(sys.argv[2], 'r')
+      line = transport_file.readline()
+      transport_file.close()
+   except IOError:
+      transport_error("cannot read '%s'" % sys.argv[2])
+
+   parts = line.strip().split()
+   transport = parts[0].lower()
+
+   if transport == "bluetooth":
+      if len(parts) < 3:
+         transport_error("not enough arguments for 'bluetooth' transport")
+      elif len(parts) > 3:
+         transport_error("too many arguments for 'bluetooth' transport")
+      return ' '.join(["-b", parts[1], "-B", parts[2]])
+   elif transport == "tty":
+      if len(parts) < 2:
+         transport_error("not enough arguments for 'tty' transport")
+      elif len(parts) > 2:
+         transport_error("too many arguments for 'tty' transport")
+      return ' '.join(["-t", parts[1]])
+   elif transport == "irda":
+      if len(parts) > 1:
+         transport_error("too many arguments for 'irda' transport")
+      return "-i"
+   else:
+      logger.critical("Unknown transport '%s'; expected 'bluetooth', 'tty' or 'irda'", transport)
+      sys.exit(1)
+
+
 command = sys.argv[1]
 procname = "mcobex_" + command
 
 command = sys.argv[1]
 procname = "mcobex_" + command
 
+g = globals()
 if not g.has_key(procname):
 if not g.has_key(procname):
-   error("Unknown command %s" % command)
+   logger.critical("Unknown command %s", command)
+   sys.exit(1)
+
+
+try:
+   obexftp_args = setup_transport()
+except SystemExit:
+   raise
+except:
+   logger.exception("Error parsing the transport file")
+   sys.exit(1)
 
 
-g[procname]()
+try:
+   g[procname]()
+except SystemExit:
+   raise
+except:
+   logger.exception("Error during run")