]> git.phdru.name Git - extfs.d.git/blob - obexftp
Version 0.5.2. More documentation and messages.
[extfs.d.git] / obexftp
1 #! /usr/local/bin/python -O
2
3 """
4 ObexFTP Virtual FileSystem for Midnight Commander.
5
6 Author: Oleg BroytMann <phd@phd.pp.ru>.
7 Copyright (C) 2004 PhiloSoft Design.
8 License: GPL.
9
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.
16
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).
21
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.
26
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.
30
31 First word in the line is a transport name - Bluetooth, IrDA or TTY. The name
32 is case-insensitive.
33
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
38 browse".
39
40 The content with the IrDA just put "IrDA" in the file.
41
42 For the TTY put a device name: "tty /dev/rfcomm0".
43
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.
51
52 """
53
54 __version__ = "0.5.2"
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"
59
60
61 # Change this to suite your needs
62 obexftp_prog = "/usr/local/obex/bin/obexftp"
63
64
65 import sys, time
66 import os, shutil
67 import xml.dom.minidom
68 from tempfile import mkdtemp
69
70
71 def log_error(msg):
72    sys.stderr.write(msg + '\n')
73
74 def error(msg):
75    log_error(msg)
76    sys.exit(1)
77
78
79 if len(sys.argv) < 2:
80    error("""\
81 ObexFTP Virtual FileSystem for Midnight Commander version %s
82 Author: %s
83 %s
84 Put it in /usr/lib/mc/extfs. For more information read the source!""" % (
85    __version__, __author__, __copyright__
86 ))
87
88
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()
94
95    parts = line.split()
96    transport = parts[0].lower()
97
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":
103       return "-i"
104    else:
105       error("Unknown transport '%s'; expected 'bluetooth', 'tty' or 'irda'" % base_filename)
106
107
108 # Parse ObexFTP XML directory listings
109
110 class DirectoryEntry(object):
111    """Represent remote files and directories"""
112
113    def __init__(self, type):
114       self.type = type
115       self.size = 0
116       if type == "file":
117          self.perm = "-rw-rw-rw-"
118       elif type == "folder":
119          self.perm = "drwxrwxrwx"
120       else:
121          raise ValueError, "unknown type '%s'; expected 'file' or 'folder'" % self.type
122
123    def mtime(self):
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)
131
132    def __repr__(self):
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)
136          )
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)
141             )
142          else: # telecom
143             return """<%s: type=directory, name=%s at 0x%x>""" % (
144                self.__class__.__name__, self.name, id(self)
145             )
146       raise ValueError, "unknown type '%s'; expected 'file' or 'folder'" % self.type
147
148 def get_entries(dom, type):
149    entries = []
150    for obj in dom.getElementsByTagName(type):
151       entry = DirectoryEntry(type)
152       attrs = obj.attributes
153       for i in range(attrs.length):
154          attr = attrs.item(i)
155          setattr(entry, attr.name, attr.value)
156       entries.append(entry)
157    return entries
158
159
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()
164    pipe.close()
165
166    if not listing:
167       return
168
169    dom = xml.dom.minidom.parseString(listing)
170    directories = get_entries(dom, "folder")
171    files = get_entries(dom, "file")
172
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
177
178    for entry in directories:
179       fullpath = "%s/%s" % (directory, entry.name)
180       if fullpath.startswith('//'): fullpath = fullpath[1:]
181       time.sleep(1)
182       recursive_list(obexftp_args, fullpath)
183
184 def mcobex_list():
185    """List the entire VFS"""
186    obexftp_args = setup_transport()
187    recursive_list(obexftp_args, '/')
188
189
190 # A unique directory for temporary files
191
192 tmpdir_name = None
193
194 def setup_tmpdir():
195    global tmpdir_name
196    tmpdir_name = mkdtemp(".tmp", "mcobex-")
197    os.chdir(tmpdir_name)
198
199 def cleanup_tmpdir():
200    os.chdir(os.pardir)
201    shutil.rmtree(tmpdir_name)
202
203
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]
209
210    setup_tmpdir()
211    os.system("%s %s -g '%s' 2>/dev/null" % (obexftp_prog, obexftp_args, obex_filename))
212    try:
213       os.rename(os.path.basename(obex_filename), real_filename)
214    except OSError:
215       pass
216    cleanup_tmpdir()
217
218
219 def mcobex_copyin():
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)
225
226    setup_tmpdir()
227    os.rename(real_filename, filename)
228    os.system("%s %s -c '%s' -p '%s' 2>/dev/null" % (obexftp_prog, obexftp_args,
229       dirname, filename
230    ))
231    os.rename(filename, real_filename) # by some reason MC wants the file back
232    cleanup_tmpdir()
233
234
235 def mcobex_rm():
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))
240
241
242 def mcobex_mkdir():
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))
247
248
249 mcobex_rmdir = mcobex_rm
250
251
252 g = globals()
253 command = sys.argv[1]
254 procname = "mcobex_" + command
255
256 if not g.has_key(procname):
257    error("Unknown command %s" % command)
258
259 g[procname]()