#!/usr/bin/python
#
# Copyright (C) 2004 Geoffrey T. Dairiki <dairiki@dairiki.org>
#
# This file is part of Pyfsp, a pure python implementation
# of the FSP file transfer protocol.
#
# Pyfsp is free software; you can redistribute it and/or
# modify it under the terms of the GNU General Public License
# as published by the Free Software Foundation; either version 2
# of the License, or (at your option) any later version.
#
# Pyfsp is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software
# Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
import sys
import os
import cmd
import shlex
import fsplib.client
class _autohelp(type):
def __new__(type_, name, bases, dict):
for m in dict.keys():
if m.startswith('do_') and dict[m].__doc__:
helpname = 'help_' + m[3:]
if dict.has_key(helpname):
continue
doc = dict[m].__doc__
def help(self):
print doc
dict[helpname] = help
return type.__new__(type_, name, bases, dict)
class Program(cmd.Cmd, object):
__metaclass__ = _autohelp
prompt = 'fsp> '
def __call__(self, host=None, port=fsplib.DEFAULT_FSP_PORT, password=None):
self.fsp = fsplib.client.FspClient()
if host:
msg = self._open(host, port)
if password:
self.fsp.set_password(password)
else:
msg = None
try:
self.cmdloop(msg)
finally:
if self.fsp.connected:
self.fsp.disconnect()
def onecmd(self, line):
try:
return cmd.Cmd.onecmd(self, line)
except fsplib.ServerError, ex:
print 'server error: %s' % ex
#except fsplib.client.ClientError, ex:
# print ex
except Exception, ex:
print '%s.%s: %s' % (ex.__class__.__module__,
ex.__class__.__name__, ex)
def help_open(self):
print """open host [port]
Establish a connection to the specifed host. An optional port number
may be supplied. The default port is %d.
""" % fsplib.DEFAULT_FSP_PORT
def do_open(self, line):
"""open host [port]
Establish a connection to the specifed host. An optional port number
may be supplied. The default port is %d.
""" % fsplib.DEFAULT_FSP_PORT
self._open(*shlex.split(line))
def _open(self, host, port=fsplib.DEFAULT_FSP_PORT):
info = self.fsp.connect(host, int(port))
return "connected %s:%d (%s)" % (self.fsp.host, self.fsp.port,
info.version.strip())
def do_password(self, line):
"""password password
Set the current password. Note that it is possible for each
directory on the FSP server can have a different password,
so this can get confusing.
"""
self.fsp.set_password(line)
def do_close(self, line):
"""close
Terminate the FSP session with the remote server, and return
to the command interpreter.
"""
self.fsp.disconnect()
# FIXME: todo
# do_bookmark
# do_bookmarks
# do_chmod ?
# do_pdir
# do_pls
# do_version
# Not needed?
# do_set (or maybe password could be a variable?)
# do_show
# do_type (transfer type)
# do_lcd (?)
# do_lls
# do_lmkdir
# do_lpage
# do_lpwd
# do_lrename
# do_lrm
# do_lrmdir
def do_cat(self, line):
"""cat remote-file [...]
This downloads the specified file(s) and outputs them directly
to the screen. You will probably find the page command more
useful.
"""
for fn in shlex.split(line):
self.getfile(fn, sys.stdout)
def do_cd(self, line):
"""cd remote-directory
Change the working directory on the remote machine to
remote-directory.
"""
dir = self.fsp.chdir(line)
if dir.readme:
print dir.readme
print "FLAGS = 0x%02x" % dir.flags
def do_dir(self, line):
"""dir [options] [remote-directory] [local-file]
Print a listing of the contents of a directory on the remote
machine. (FIXME: more)
"""
# FIXME: (ls -CF)
print self.fsp.listdir(line)
def do_get(self, line):
"""get [options] remote-file [...]
Download file(s) from the server.
"""
# FIXME: wildcards, options, status output
for fn in shlex.split(line):
self.getfile(fn, open(os.path.basename(fn), "w"))
do_ls = do_dir # FIXME:
def do_mkdir(self, line):
"""mkdir remote-path
Create a new directory on the server.
"""
newdir = self.fsp.mkdir(line)
print "FLAGS = 0x%02x" % newdir.flags
def do_page(self, line):
"""page remote-file [...]
This downloads the specified file(s) and views them using the
program specified in your $PAGER environment variable.
"""
for fn in shlex.split(line):
self.getfile(fn, self.pager())
def do_put(self, line):
"""put [options] remote-file [...]
Upload file(s) to the server.
"""
# FIXME: wildcards, options, status output
for fn in shlex.split(line):
infp = open(fn)
outfp = self.fsp.open(os.path.basename(fn), "w")
buf = infp.read(1024)
while buf:
# FIXME: buffering?
nw = outfp.raw_write(buf)
buf = buf[nw:]
if not buf:
buf = infp.read(1024)
outfp.close()
def do_pwd(self, line):
"""pwd
Display the current working directory on the server.
"""
print self.fsp.cwd
def do_rename(self, line):
"""rename remote-file remote-path
Rename a file on the server.
"""
src, dst = shlex.split(line)
self.fsp.rename(src, dst)
def do_rm(self, line):
"""rm remote-file [...]
Delete files on the server.
"""
# FIXME: wildcards, options, status output
for fn in shlex.split(line):
self.fsp.remove(fn)
def do_rmdir(self, line):
"""rmdir remote-directory
Remove a directory on the server.
"""
# FIXME: this doesn't seem to work (is it an fspd problem?)
self.fsp.rmdir(line)
def do_debug(self, line):
"""show protocol-level debugging information
This command is mostly for internal testing. You could type
debug 1
to turn debugging mode on. Then you could see all messages
between the program and the remote server, and things that are
only printed in debugging mode.
"""
self.fsp.set_debuglevel(int(line))
def do_bye(self, line):
"""bye
Terminate the FSP seesion and exit fsp. An end of file will
also terminate the session and exit.
"""
if self.fsp.connected:
self.fsp.disconnect()
return 'quit'
def do_quit(self, line):
"""quit
A synonym for bye.
"""
return self.do_bye(line)
def do_EOF(self, line):
"""An end of file will terminate the session and exit."""
self.do_bye(line)
print
return 'quit'
def getfile(self, path, outfp):
fp = self.fsp.open(path)
buf = fp.raw_read()
while buf:
outfp.write(buf)
buf = fp.raw_read()
def pager(self):
pager = os.environ.get('PAGER', 'less')
try:
return os.popen(pager, 'w')
except:
return os.popen('more', 'w')
main = Program()
if __name__ == '__main__':
main()