[pyftpdlib] r1246 committed - [No log message]

19 views
Skip to first unread message

pyft...@googlecode.com

unread,
Feb 5, 2014, 9:28:17 PM2/5/14
to pyftpdli...@googlegroups.com
Revision: 1246
Author: g.rodola
Date: Thu Feb 6 02:27:59 2014 UTC
Log: [No log message]
http://code.google.com/p/pyftpdlib/source/detail?r=1246

Added:
/tags/release-1.3.0
Deleted:
/tags/release-1.3.0/Makefile
/tags/release-1.3.0/make.bat
Modified:
/tags/release-1.3.0/CREDITS
/tags/release-1.3.0/HISTORY
/tags/release-1.3.0/MANIFEST.in
/tags/release-1.3.0/demo/anti_flood_ftpd.py
/tags/release-1.3.0/demo/tls_ftpd.py
/tags/release-1.3.0/demo/unix_daemon.py
/tags/release-1.3.0/demo/unix_ftpd.py
/tags/release-1.3.0/demo/winnt_ftpd.py
/tags/release-1.3.0/pyftpdlib/__init__.py
/tags/release-1.3.0/pyftpdlib/__main__.py
/tags/release-1.3.0/pyftpdlib/authorizers.py
/tags/release-1.3.0/pyftpdlib/contrib/handlers.py
/tags/release-1.3.0/pyftpdlib/filesystems.py
/tags/release-1.3.0/pyftpdlib/ftpserver.py
/tags/release-1.3.0/pyftpdlib/handlers.py
/tags/release-1.3.0/pyftpdlib/ioloop.py
/tags/release-1.3.0/pyftpdlib/log.py
/tags/release-1.3.0/pyftpdlib/servers.py
/tags/release-1.3.0/setup.py
/tags/release-1.3.0/test/bench.py
/tags/release-1.3.0/test/test_contrib.py
/tags/release-1.3.0/test/test_ftpd.py

=======================================
--- /trunk/Makefile Mon Dec 30 10:45:50 2013 UTC
+++ /dev/null
@@ -1,59 +0,0 @@
-# Shortcuts for various tasks (UNIX only).
-# To use a specific Python version run:
-# $ make install PYTHON=python3.3
-
-.PHONY: build install uninstall test test_contrib nosetests pep8 pyflakes \
- clean upload-src
-
-PYTHON=python
-TSCRIPT=test/test_ftpd.py
-FLAGS=
-
-all: test
-
-clean:
- rm -f `find . -type f -name \*.py[co]`
- rm -f `find . -type f -name .\*~`
- rm -f `find . -type f -name \*.orig`
- rm -f `find . -type f -name \*.bak`
- rm -f `find . -type f -name \*.rej`
- rm -rf `find . -type d -name __pycache__`
- rm -rf *.egg-info
- rm -rf build
- rm -rf dist
-
-build: clean
- $(PYTHON) setup.py build
-
-install: build
- if test $(PYTHON) = python2.4; then \
- $(PYTHON) setup.py install; \
- elif test $(PYTHON) = python2.5; then \
- $(PYTHON) setup.py install; \
- else \
- $(PYTHON) setup.py install --user; \
- fi
-
-uninstall:
- pip-`$(PYTHON) -c "import sys; sys.stdout.write('.'.join(map(str,
sys.version_info)[:2]))"` uninstall -y -v pyftpdlib
-
-test: install
- $(PYTHON) $(TSCRIPT)
-
-test-contrib:
- $(PYTHON) test/test_contrib.py
-
-nosetest: install
- # $ make nosetest FLAGS=test_name
- nosetests $(TSCRIPT) -v -m $(FLAGS)
-
-pep8:
- pep8 pyftpdlib/ demo/ test/ setup.py --ignore E302
-
-pyflakes:
- # ignore doctests
- export PYFLAKES_NODOCTEST=1 && \
- pyflakes pyftpdlib/ demo/ test/ setup.py
-
-upload-src: clean
- $(PYTHON) setup.py sdist upload
=======================================
--- /trunk/make.bat Mon Dec 30 11:46:07 2013 UTC
+++ /dev/null
@@ -1,82 +0,0 @@
-@echo off
-
-rem
==========================================================================
-rem Shortcuts for various tasks, emulating UNIX "make" on Windows.
-rem It is primarly intended as a shortcut for installing pyftpdlib and
running
-rem tests (just run "make.bat test").
-rem By default C:\Python27\python.exe is used.
-rem To use another Python version run:
-rem set PYTHON=C:\Python24\python.exe & make.bat test
-rem
==========================================================================
-
-
-if "%PYTHON%" == "" (
- set PYTHON=C:\Python27\python.exe
-)
-if "%TSCRIPT%" == "" (
- set TSCRIPT=test\test_ftpd.py
-)
-
-
-if "%1" == "help" (
- :help
- echo Run `make ^<target^>` where ^<target^> is one of:
- echo clean clean build files
- echo install compile and install
- echo uninstall uninstall
- echo test run tests
- echo test-contrib run contrib tests
- goto :eof
-)
-
-if "%1" == "clean" (
- :clean
- for /r %%R in (__pycache__) do if exist %%R (rmdir /S /Q %%R)
- for /r %%R in (*.pyc) do if exist %%R (del /s %%R)
- for /r %%R in (*.pyd) do if exist %%R (del /s %%R)
- for /r %%R in (*.orig) do if exist %%R (del /s %%R)
- for /r %%R in (*.bak) do if exist %%R (del /s %%R)
- for /r %%R in (*.rej) do if exist %%R (del /s %%R)
- if exist pyftpdlib.egg-info (rmdir /S /Q pyftpdlib.egg-info)
- if exist build (rmdir /S /Q build)
- if exist dist (rmdir /S /Q dist)
- goto :eof
-)
-
-if "%1" == "install" (
- :install
- if %PYTHON%==C:\Python24\python.exe (
- %PYTHON% setup.py build -c mingw32 install
- ) else if %PYTHON%==C:\Python25\python.exe (
- %PYTHON% setup.py build -c mingw32 install
- ) else (
- %PYTHON% setup.py build install
- )
- goto :eof
-)
-
-if "%1" == "uninstall" (
- :uninstall
- rmdir /S /Q %PYTHON%\Lib\site-packages\pyftpdlib*
- goto :eof
-)
-
-if "%1" == "test" (
- :test
- call :install
- %PYTHON% %TSCRIPT%
- goto :eof
-)
-
-if "%1" == "test-contrib" (
- :test
- call :install
- %PYTHON% test\test_contrib.py
- goto :eof
-)
-
-goto :help
-
-:error
- echo last command returned an error; exiting
- exit /b %errorlevel%
=======================================
--- /trunk/CREDITS Tue Feb 4 22:32:54 2014 UTC
+++ /tags/release-1.3.0/CREDITS Thu Feb 6 02:27:59 2014 UTC
@@ -170,6 +170,3 @@
N: Michael Ross
E: michael...@gmail.com
D: issue 273
-
-E: d...@devicenull.org
-D: issue 280
=======================================
--- /trunk/HISTORY Tue Feb 4 22:32:54 2014 UTC
+++ /tags/release-1.3.0/HISTORY Thu Feb 6 02:27:59 2014 UTC
@@ -4,21 +4,6 @@
History
=======

-Version: 1.3.1 - Date: XXXX-XX-XX
----------------------------------
-
-ENHANCEMENTS
-
- * #277: added a make file for running tests and for other repetitive tasks
- (also for Windows).
-
-BUG FIXES
-
- * #261: (FTPS) SSL shutdown does not properly work on Windows.
- * #280: (Python 2) unable to complete directory listing with invalid UTF8
- characters. (patch by d...@devicenull.org)
-
-
Version: 1.3.0 - Date: 2013-11-07
---------------------------------

=======================================
--- /trunk/MANIFEST.in Mon Dec 30 11:14:47 2013 UTC
+++ /tags/release-1.3.0/MANIFEST.in Thu Feb 6 02:27:59 2014 UTC
@@ -5,8 +5,6 @@
include HISTORY
include INSTALL
include LICENSE
-include make.bat
-include Makefile
include MANIFEST.in
include README
include setup.py
=======================================
--- /trunk/demo/anti_flood_ftpd.py Sun Dec 8 21:40:25 2013 UTC
+++ /tags/release-1.3.0/demo/anti_flood_ftpd.py Thu Feb 6 02:27:59 2014 UTC
@@ -51,8 +51,7 @@
def __init__(self, *args, **kwargs):
FTPHandler.__init__(self, *args, **kwargs)
self.processed_cmds = 0
- self.pcmds_callback = \
- self.ioloop.call_every(1, self.check_processed_cmds)
+ self.pcmds_callback = self.ioloop.call_every(1,
self.check_processed_cmds)

def on_connect(self):
# called when client connects.
=======================================
--- /trunk/demo/tls_ftpd.py Sun Dec 8 21:40:25 2013 UTC
+++ /tags/release-1.3.0/demo/tls_ftpd.py Thu Feb 6 02:27:59 2014 UTC
@@ -41,11 +41,9 @@
from pyftpdlib.handlers import TLS_FTPHandler
from pyftpdlib.servers import FTPServer

-
CERTFILE = os.path.abspath(os.path.join(os.path.dirname(__file__),
"keycert.pem"))

-
def main():
authorizer = DummyAuthorizer()
authorizer.add_user('user', '12345', '.', perm='elradfmw')
=======================================
--- /trunk/demo/unix_daemon.py Sun Dec 8 21:40:25 2013 UTC
+++ /tags/release-1.3.0/demo/unix_daemon.py Thu Feb 6 02:27:59 2014 UTC
@@ -79,13 +79,11 @@
LOG_FILE = "/var/log/pyftpdlib.log"
WORKDIR = os.getcwd()
UMASK = 0
-
-
+
def print_(s):
sys.stdout.write(s + '\n')
sys.stdout.flush()

-
def pid_exists(pid):
"""Return True if a process with the given PID is currently running."""
try:
@@ -96,7 +94,6 @@
else:
return True

-
def get_pid():
"""Return the PID saved in the pid file if possible, else None."""
try:
@@ -107,7 +104,6 @@
if err.errno != errno.ENOENT:
raise

-
def stop():
"""Keep attempting to stop the daemon for 5 seconds, first using
SIGTERM, then using SIGKILL.
@@ -136,7 +132,6 @@
sys.exit("\ncould not kill daemon (pid %s)" % pid)
time.sleep(0.1)

-
def status():
"""Print daemon status and exit."""
pid = get_pid()
@@ -146,7 +141,6 @@
print_("daemon running with pid %s" % pid)
sys.exit(0)

-
def get_server():
"""Return a pre-configured FTP server instance."""
handler = FTPHandler
@@ -155,26 +149,25 @@
server = FTPServer((HOST, PORT), handler)
return server

-
def daemonize():
"""A wrapper around python-daemonize context manager."""
def _daemonize():
- pid = os.fork()
+ pid = os.fork()
if pid > 0:
# exit first parent
- sys.exit(0)
-
+ sys.exit(0)
+
# decouple from parent environment
- os.chdir(WORKDIR)
- os.setsid()
- os.umask(0)
-
- # do second fork
- pid = os.fork()
+ os.chdir(WORKDIR)
+ os.setsid()
+ os.umask(0)
+
+ # do second fork
+ pid = os.fork()
if pid > 0:
# exit from second parent
- sys.exit(0)
-
+ sys.exit(0)
+
# redirect standard file descriptors
sys.stdout.flush()
sys.stderr.flush()
@@ -184,10 +177,10 @@
os.dup2(si.fileno(), sys.stdin.fileno())
os.dup2(so.fileno(), sys.stdout.fileno())
os.dup2(se.fileno(), sys.stderr.fileno())
-
+
# write pidfile
pid = str(os.getpid())
- f = open(PID_FILE, 'w')
+ f = open(PID_FILE,'w')
f.write("%s\n" % pid)
f.close()
atexit.register(lambda: os.remove(PID_FILE))
@@ -197,7 +190,7 @@
sys.exit('daemon already running (pid %s)' % pid)
# instance FTPd before daemonizing, so that in case of problems we
# get an exception here and exit immediately
- server = get_server()
+ server = get_server()
_daemonize()
server.serve_forever()

=======================================
--- /trunk/demo/unix_ftpd.py Sun Dec 8 21:40:25 2013 UTC
+++ /tags/release-1.3.0/demo/unix_ftpd.py Thu Feb 6 02:27:59 2014 UTC
@@ -43,8 +43,7 @@


def main():
- authorizer = UnixAuthorizer(rejected_users=["root"],
- require_valid_shell=True)
+ authorizer = UnixAuthorizer(rejected_users=["root"],
require_valid_shell=True)
handler = FTPHandler
handler.authorizer = authorizer
handler.abstracted_fs = UnixFilesystem
=======================================
--- /trunk/demo/winnt_ftpd.py Sun Dec 8 21:40:25 2013 UTC
+++ /tags/release-1.3.0/demo/winnt_ftpd.py Thu Feb 6 02:27:59 2014 UTC
@@ -47,8 +47,7 @@
# Use Guest user with empty password to handle anonymous sessions.
# Guest user must be enabled first, empty password set and profile
# directory specified.
- # authorizer = WindowsAuthorizer(anonymous_user="Guest",
- # anonymous_password="")
+ #authorizer = WindowsAuthorizer(anonymous_user="Guest",
anonymous_password="")
handler = FTPHandler
handler.authorizer = authorizer
ftpd = FTPServer(('', 21), handler)
=======================================
--- /trunk/pyftpdlib/__init__.py Sun Dec 8 21:40:25 2013 UTC
+++ /tags/release-1.3.0/pyftpdlib/__init__.py Thu Feb 6 02:27:59 2014 UTC
@@ -88,16 +88,15 @@
[I 13-02-19 10:55:42] use sendfile(2): True
[I 13-02-19 10:55:45] 127.0.0.1:34178-[] FTP session opened (connect)
[I 13-02-19 10:55:48] 127.0.0.1:34178-[user] USER 'user' logged in.
-[I 13-02-19 10:56:27] 127.0.0.1:34179-[user] RETR /home/giampaolo/.vimrc
- completed=1 bytes=1700 seconds=0.001
+[I 13-02-19 10:56:27] 127.0.0.1:34179-[user] RETR /home/giampaolo/.vimrc
completed=1 bytes=1700 seconds=0.001
[I 13-02-19 10:56:39] 127.0.0.1:34179-[user] FTP session closed
(disconnect).
"""

+import logging

-__ver__ = '1.3.0'
-__author__ = "Giampaolo Rodola' <g.ro...@gmail.com>"
-__web__ = 'http://code.google.com/p/pyftpdlib/'
-
+__ver__ = '1.3.0'
+__author__ = "Giampaolo Rodola' <g.ro...@gmail.com>"
+__web__ = 'http://code.google.com/p/pyftpdlib/'

def _depwarn(msg):
"""
=======================================
--- /trunk/pyftpdlib/__main__.py Sun Jan 19 11:44:10 2014 UTC
+++ /tags/release-1.3.0/pyftpdlib/__main__.py Thu Feb 6 02:27:59 2014 UTC
@@ -57,7 +57,6 @@
result.append(help_text)
return ''.join(result)

-
def main():
"""Start a stand alone anonymous FTP server."""
usage = "python -m pyftpdlib.ftpserver [options]"
@@ -67,7 +66,7 @@
help="specify the interface to run on (default all "
"interfaces)")
parser.add_option('-p', '--port', type="int", default=2121,
metavar="PORT",
- help="specify port number to run on (default 2121)")
+ help="specify port number to run on (default 21)")
parser.add_option('-w', '--write', action="store_true", default=False,
help="grants write access for the anonymous user "
"(default read-only)")
=======================================
--- /trunk/pyftpdlib/authorizers.py Sun Dec 8 21:40:25 2013 UTC
+++ /tags/release-1.3.0/pyftpdlib/authorizers.py Thu Feb 6 02:27:59 2014
UTC
@@ -45,10 +45,10 @@
"""


+import os
+import warnings
import errno
-import os
import sys
-import warnings

from pyftpdlib._compat import PY3, unicode, getcwdu

@@ -56,7 +56,7 @@
__all__ = ['DummyAuthorizer',
#'BaseUnixAuthorizer', 'UnixAuthorizer',
#'BaseWindowsAuthorizer', 'WindowsAuthorizer',
- ]
+ ]


# ===================================================================
@@ -66,7 +66,6 @@
class AuthorizerError(Exception):
"""Base class for authorizer exceptions."""

-
class AuthenticationFailed(Exception):
"""Exception raised when authentication fails for any reason."""

@@ -98,7 +97,7 @@
self.user_table = {}

def add_user(self, username, password, homedir, perm='elr',
- msg_login="Login successful.", msg_quit="Goodbye."):
+ msg_login="Login successful.", msg_quit="Goodbye."):
"""Add a user to the virtual users table.

AuthorizerError exceptions raised on error conditions such as
@@ -235,8 +234,8 @@
if self._issubpath(path, dir):
if recursive:
return perm in operm
- if (path == dir or os.path.dirname(path) == dir
- and not os.path.isdir(path)):
+ if (path == dir) or (os.path.dirname(path) == dir \
+ and not os.path.isdir(path)):
return perm in operm

return perm in self.user_table[username]['perm']
@@ -258,9 +257,7 @@
for p in perm:
if p not in self.read_perms + self.write_perms:
raise ValueError('no such permission %r' % p)
- if (username == 'anonymous'
- and p in self.write_perms
- and not warned):
+ if (username == 'anonymous') and (p in self.write_perms) and
not warned:
warnings.warn("write permissions assigned to anonymous
user.",
RuntimeWarning)
warned = 1
@@ -277,7 +274,6 @@
methods as first argument with the actual user used to handle
anonymous sessions.
"""
-
def wrapper(self, username, *args, **kwargs):
if username == 'anonymous':
username = self.anonymous_user or username
@@ -303,8 +299,8 @@
def __init__(self):
"""Check for errors in the constructor."""
if self.rejected_users and self.allowed_users:
- raise AuthorizerError("rejected_users and allowed_users
options "
- "are mutually exclusive")
+ raise AuthorizerError("rejected_users and allowed_users
options are "
+ "mutually exclusive")

users = self._get_system_users()
for user in (self.allowed_users or self.rejected_users):
@@ -326,10 +322,9 @@
"""Overrides the options specified in the class constructor
for a specific user.
"""
- if (not password and not homedir and not perm and not msg_login
- and not msg_quit):
- raise AuthorizerError(
- "at least one keyword argument must be specified")
+ if not password and not homedir and not perm and not msg_login \
+ and not msg_quit:
+ raise AuthorizerError("at least one keyword argument must be
specified")
if self.allowed_users and username not in self.allowed_users:
raise AuthorizerError('%s is not an allowed user' % username)
if self.rejected_users and username in self.rejected_users:
@@ -344,12 +339,11 @@
if username in self._dummy_authorizer.user_table:
# re-set parameters
del self._dummy_authorizer.user_table[username]
- self._dummy_authorizer.add_user(username,
- password or "",
- homedir or getcwdu(),
- perm or "",
- msg_login or "",
- msg_quit or "")
+ self._dummy_authorizer.add_user(username, password or "",
+ homedir or getcwdu(),
+ perm or "",
+ msg_login or "",
+ msg_quit or "")
if homedir is None:
self._dummy_authorizer.user_table[username]['home'] = ""

@@ -391,9 +385,7 @@

# Note: requires python >= 2.5
try:
- import crypt
- import pwd
- import spwd
+ import pwd, spwd, crypt
except ImportError:
pass
else:
@@ -497,6 +489,7 @@
def has_perm(self, username, perm, path=None):
return perm in self.get_perms(username)

+
class UnixAuthorizer(_Base, BaseUnixAuthorizer):
"""A wrapper on top of BaseUnixAuthorizer providing options
to specify what users should be allowed to login, per-user
@@ -521,12 +514,12 @@
# --- public API

def __init__(self, global_perm="elradfmw",
- allowed_users=None,
- rejected_users=None,
- require_valid_shell=True,
- anonymous_user=None,
- msg_login="Login successful.",
- msg_quit="Goodbye."):
+ allowed_users=None,
+ rejected_users=None,
+ require_valid_shell=True,
+ anonymous_user=None,
+ msg_login="Login successful.",
+ msg_quit="Goodbye."):
"""Parameters:

- (string) global_perm:
@@ -584,8 +577,8 @@
raise AuthorizerError("user %s has not a valid
shell"
% username)

- def override_user(self, username, password=None, homedir=None,
- perm=None, msg_login=None, msg_quit=None):
+ def override_user(self, username, password=None, homedir=None,
perm=None,
+ msg_login=None, msg_quit=None):
"""Overrides the options specified in the class constructor
for a specific user.
"""
@@ -613,8 +606,7 @@
password,
handler)
if self.require_valid_shell and username != 'anonymous':
if not self._has_valid_shell(username):
- raise AuthenticationFailed(
- self.msg_invalid_shell % username)
+ raise AuthenticationFailed(self.msg_invalid_shell %
username)

@replace_anonymous
def has_user(self, username):
@@ -673,11 +665,7 @@
pass
# Note: requires pywin32 extension
try:
- import pywintypes
- import win32api
- import win32con
- import win32net
- import win32security
+ import win32security, win32net, pywintypes, win32con, win32api
except ImportError:
pass
else:
@@ -714,10 +702,9 @@
@replace_anonymous
def impersonate_user(self, username, password):
"""Impersonate the security context of another user."""
- handler = win32security.LogonUser(
- username, None, password,
- win32con.LOGON32_LOGON_INTERACTIVE,
- win32con.LOGON32_PROVIDER_DEFAULT)
+ handler = win32security.LogonUser(username, None, password,
+
win32con.LOGON32_LOGON_INTERACTIVE,
+
win32con.LOGON32_PROVIDER_DEFAULT)
win32security.ImpersonateLoggedOnUser(handler)
handler.Close()

@@ -736,17 +723,17 @@
"""
try:
sid = win32security.ConvertSidToStringSid(
- win32security.LookupAccountName(None, username)[0])
+ win32security.LookupAccountName(None, username)[0])
except pywintypes.error:
err = sys.exc_info()[1]
raise AuthorizerError(err)
- path = r"SOFTWARE\Microsoft\Windows NT" \
- r"\CurrentVersion\ProfileList" + "\\" + sid
+ path = r"SOFTWARE\Microsoft\Windows
NT\CurrentVersion\ProfileList" + \
+ "\\" + sid
try:
key = winreg.OpenKey(winreg.HKEY_LOCAL_MACHINE, path)
except WindowsError:
- raise AuthorizerError(
- "No profile directory defined for user %s" % username)
+ raise AuthorizerError("No profile directory defined for
user %s"
+ % username)
value = winreg.QueryValueEx(key, "ProfileImagePath")[0]
home = win32api.ExpandEnvironmentStrings(value)
if not PY3 and not isinstance(home, unicode):
@@ -758,8 +745,7 @@
"""Return all users defined on the Windows system."""
# XXX - Does Windows allow usernames with chars outside of
# ASCII set? In that case we need to convert this to unicode.
- return [entry['name'] for entry in
- win32net.NetUserEnum(None, 0)[0]]
+ return [entry['name'] for entry in win32net.NetUserEnum(None,
0)[0]]

def get_msg_login(self, username):
return "Login successful."
@@ -773,6 +759,7 @@
def has_perm(self, username, perm, path=None):
return perm in self.get_perms(username)

+
class WindowsAuthorizer(_Base, BaseWindowsAuthorizer):
"""A wrapper on top of BaseWindowsAuthorizer providing options
to specify what users should be allowed to login, per-user
@@ -793,14 +780,13 @@

# --- public API

- def __init__(self,
- global_perm="elradfmw",
- allowed_users=None,
- rejected_users=None,
- anonymous_user=None,
- anonymous_password=None,
- msg_login="Login successful.",
- msg_quit="Goodbye."):
+ def __init__(self, global_perm="elradfmw",
+ allowed_users=None,
+ rejected_users=None,
+ anonymous_user=None,
+ anonymous_password=None,
+ msg_login="Login successful.",
+ msg_quit="Goodbye."):
"""Parameters:

- (string) global_perm:
@@ -855,8 +841,8 @@
self.anonymous_password)
self.terminate_impersonation(None)

- def override_user(self, username, password=None, homedir=None,
- perm=None, msg_login=None, msg_quit=None):
+ def override_user(self, username, password=None, homedir=None,
perm=None,
+ msg_login=None, msg_quit=None):
"""Overrides the options specified in the class constructor
for a specific user.
"""
@@ -883,8 +869,8 @@
if overridden_password != password:
raise AuthenticationFailed(self.msg_wrong_password)
else:
- BaseWindowsAuthorizer.validate_authentication(
- self, username, password, handler)
+ BaseWindowsAuthorizer.validate_authentication(self,
username,
+ password,
handler)

def impersonate_user(self, username, password):
"""Impersonate the security context of another user."""
=======================================
--- /trunk/pyftpdlib/contrib/handlers.py Mon Dec 30 11:33:10 2013 UTC
+++ /tags/release-1.3.0/pyftpdlib/contrib/handlers.py Thu Feb 6 02:27:59
2014 UTC
@@ -35,7 +35,6 @@
"use pyftpdlib.handlers instead")

try:
- from pyftpdlib.handlers import (SSLConnection, TLS_FTPHandler,
- TLS_DTPHandler)
+ from pyftpdlib.handlers import SSLConnection, TLS_FTPHandler,
TLS_DTPHandler
except ImportError:
pass
=======================================
--- /trunk/pyftpdlib/filesystems.py Tue Feb 4 22:32:54 2014 UTC
+++ /tags/release-1.3.0/pyftpdlib/filesystems.py Thu Feb 6 02:27:59 2014
UTC
@@ -49,8 +49,8 @@
__all__ = ['FilesystemError', 'AbstractedFS']


-_months_map = {1: 'Jan', 2: 'Feb', 3: 'Mar', 4: 'Apr', 5: 'May', 6: 'Jun',
- 7: 'Jul', 8: 'Aug', 9: 'Sep', 10: 'Oct', 11: 'Nov',
12: 'Dec'}
+_months_map = {1:'Jan', 2:'Feb', 3:'Mar', 4:'Apr', 5:'May', 6:'Jun',
7:'Jul',
+ 8:'Aug', 9:'Sep', 10:'Oct', 11:'Nov', 12:'Dec'}


# ===================================================================
@@ -63,7 +63,6 @@
send a customized error string to the client.
"""

-
# ===================================================================
# --- base class
# ===================================================================
@@ -240,7 +239,6 @@
def __init__(self, fd, name):
self.file = fd
self.name = name
-
def __getattr__(self, attr):
return getattr(self.file, attr)

@@ -299,13 +297,13 @@
#assert isinstance(path, unicode), path
return os.stat(path)

- if hasattr(os, 'lstat'):
- def lstat(self, path):
- """Like stat but does not follow symbolic links."""
- # on python 2 we might also get bytes from os.lisdir()
- #assert isinstance(path, unicode), path
- return os.lstat(path)
- else:
+ def lstat(self, path):
+ """Like stat but does not follow symbolic links."""
+ # on python 2 we might also get bytes from os.lisdir()
+ #assert isinstance(path, unicode), path
+ return os.lstat(path)
+
+ if not hasattr(os, 'lstat'):
lstat = stat

if hasattr(os, 'readlink'):
@@ -359,33 +357,28 @@
assert isinstance(path, unicode), path
return os.path.lexists(path)

- if pwd is not None:
- def get_user_by_uid(self, uid):
- """Return the username associated with user id.
- If this can't be determined return raw uid instead.
- On Windows just return "owner".
- """
- try:
- return pwd.getpwuid(uid).pw_name
- except KeyError:
- return uid
- else:
- def get_user_by_uid(self, uid):
- return "owner"
+ def get_user_by_uid(self, uid):
+ """Return the username associated with user id.
+ If this can't be determined return raw uid instead.
+ On Windows just return "owner".
+ """
+ try:
+ return pwd.getpwuid(uid).pw_name
+ except KeyError:
+ return uid
+
+ def get_group_by_gid(self, gid):
+ """Return the groupname associated with group id.
+ If this can't be determined return raw gid instead.
+ On Windows just return "group".
+ """
+ try:
+ return grp.getgrgid(gid).gr_name
+ except KeyError:
+ return gid

- if grp is not None:
- def get_group_by_gid(self, gid):
- """Return the groupname associated with group id.
- If this can't be determined return raw gid instead.
- On Windows just return "group".
- """
- try:
- return grp.getgrgid(gid).gr_name
- except KeyError:
- return gid
- else:
- def get_group_by_gid(self, gid):
- return "group"
+ if pwd is None: get_user_by_uid = lambda x, y: "owner"
+ if grp is None: get_group_by_gid = lambda x, y: "group"

# --- Listing utilities

@@ -455,7 +448,7 @@
# http://bugs.python.org/issue683592
file = os.path.join(bytes(basedir), bytes(basename))
if not isinstance(basename, unicode):
- basename = unicode(basename, 'utf8', 'ignore')
+ basename = unicode(basename, 'utf8')
else:
file = os.path.join(basedir, basename)
try:
@@ -503,8 +496,8 @@
raise

# formatting is matched with proftpd ls output
- line = "%s %3s %-8s %-8s %8s %s %s\r\n" % (
- perms, nlinks, uname, gname, size, mtimestr, basename)
+ line = "%s %3s %-8s %-8s %8s %s %s\r\n" % (perms, nlinks,
uname, gname,
+ size, mtimestr,
basename)
yield line.encode('utf8', self.cmd_channel.unicode_errors)

def format_mlsx(self, basedir, listing, perms, facts, ignore_err=True):
@@ -529,9 +522,9 @@
This is how output could appear to the client issuing
a MLSD request:

- type=file;size=156;perm=r;modify=20071029155301;unique=8012;
music.mp3
+ type=file;size=156;perm=r;modify=20071029155301;unique=801cd2;
music.mp3
type=dir;size=0;perm=el;modify=20071127230206;unique=801e33; ebooks
- type=file;size=211;perm=r;modify=20071103093626;unique=192;
module.py
+ type=file;size=211;perm=r;modify=20071103093626;unique=801e32;
module.py
"""
assert isinstance(basedir, unicode), basedir
if listing:
@@ -568,7 +561,7 @@
# http://bugs.python.org/issue683592
file = os.path.join(bytes(basedir), bytes(basename))
if not isinstance(basename, unicode):
- basename = unicode(basename, 'utf8', 'ignore')
+ basename = unicode(basename, 'utf8')
else:
file = os.path.join(basedir, basename)
# in order to properly implement 'unique' fact (RFC-3659,
@@ -636,7 +629,7 @@
retfacts['unique'] = "%xg%x" % (st.st_dev, st.st_ino)

# facts can be in any order but we sort them by name
- factstring = "".join(["%s=%s;" % (x, retfacts[x])
+ factstring = "".join(["%s=%s;" % (x, retfacts[x]) \
for x in sorted(retfacts.keys())])
line = "%s %s\r\n" % (factstring, basename)
yield line.encode('utf8', self.cmd_channel.unicode_errors)
=======================================
--- /trunk/pyftpdlib/ftpserver.py Sun Dec 8 21:40:25 2013 UTC
+++ /tags/release-1.3.0/pyftpdlib/ftpserver.py Thu Feb 6 02:27:59 2014 UTC
@@ -46,10 +46,10 @@

from pyftpdlib import _depwarn, __ver__

-__all__ = ['proto_cmds', 'Error', 'log', 'logline', 'logerror',
- 'DummyAuthorizer', 'AuthorizerError', 'FTPHandler', 'FTPServer',
- 'PassiveDTP', 'ActiveDTP', 'DTPHandler', 'ThrottledDTPHandler',
- 'FileProducer', 'BufferedIteratorProducer', 'AbstractedFS']
+__all__ =
['proto_cmds', 'Error', 'log', 'logline', 'logerror', 'DummyAuthorizer',
+ 'AuthorizerError', 'FTPHandler', 'FTPServer', 'PassiveDTP',
+ 'ActiveDTP', 'DTPHandler', 'ThrottledDTPHandler', 'FileProducer',
+ 'BufferedIteratorProducer', 'AbstractedFS']

_depwarn("pyftpdlib.ftpserver module is deprecated")

@@ -57,34 +57,29 @@
class CallLater(object):
def __init__(self, *args, **kwargs):
_depwarn("CallLater is deprecated; use "
- "pyftpdlib.ioloop.IOLoop.instance().call_later() instead")
+ "pyftpdlib.ioloop.IOLoop.instance().call_later() instead")
from pyftpdlib.ioloop import IOLoop
IOLoop.instance().call_later(*args, **kwargs)

-
class CallEvery(object):
def __init__(self, *args, **kwargs):
_depwarn("CallEvery is deprecated; use "
- "pyftpdlib.ioloop.IOLoop.instance().call_every() instead")
+ "pyftpdlib.ioloop.IOLoop.instance().call_every() instead")
from pyftpdlib.ioloop import IOLoop
IOLoop.instance().call_every(*args, **kwargs)

-
def log(msg):
_depwarn("pyftpdlib.ftpserver.log() is deprecated")
logger.info(msg)

-
def logline(msg):
_depwarn("pyftpdlib.ftpserver.logline() is deprecated")
logger.debug(msg)

-
def logerror(msg):
_depwarn("pyftpdlib.ftpserver.logline() is deprecated")
logger.error(msg)

-
if __name__ == '__main__':
from pyftpdlib import main
main()
=======================================
--- /trunk/pyftpdlib/handlers.py Mon Dec 30 11:46:07 2013 UTC
+++ /tags/release-1.3.0/pyftpdlib/handlers.py Thu Feb 6 02:27:59 2014 UTC
@@ -30,16 +30,16 @@
# ======================================================================

import asynchat
-import errno
-import glob
-import logging
+import time
+import sys
import os
-import random
+import errno
import socket
-import sys
-import time
import traceback
+import glob
+import random
import warnings
+import logging
try:
import pwd
import grp
@@ -47,14 +47,12 @@
pwd = grp = None

from pyftpdlib import __ver__
+from pyftpdlib.log import logger
+from pyftpdlib.filesystems import FilesystemError, AbstractedFS
+from pyftpdlib._compat import PY3, b, u, getcwdu, unicode, xrange, next
+from pyftpdlib.ioloop import AsyncChat, Connector, Acceptor, timer,
_DISCONNECTED
from pyftpdlib.authorizers import (DummyAuthorizer, AuthenticationFailed,
AuthorizerError)
-from pyftpdlib._compat import PY3, b, u, getcwdu, unicode, xrange, next
-from pyftpdlib.filesystems import FilesystemError, AbstractedFS
-from pyftpdlib.ioloop import (AsyncChat, Connector, Acceptor, timer,
- _DISCONNECTED)
-from pyftpdlib.log import logger
-

def _import_sendfile():
# By default attempt to use os.sendfile introduced in Python 3.3:
@@ -78,150 +76,103 @@


proto_cmds = {
- 'ABOR': dict(
- perm=None, auth=True, arg=False,
- help='Syntax: ABOR (abort transfer).'),
- 'ALLO': dict(
- perm=None, auth=True, arg=True,
- help='Syntax: ALLO <SP> bytes (noop; allocate storage).'),
- 'APPE': dict(
- perm='a', auth=True, arg=True,
- help='Syntax: APPE <SP> file-name (append data to file).'),
- 'CDUP': dict(
- perm='e', auth=True, arg=False,
- help='Syntax: CDUP (go to parent directory).'),
- 'CWD': dict(
- perm='e', auth=True, arg=None,
- help='Syntax: CWD [<SP> dir-name] (change working directory).'),
- 'DELE': dict(
- perm='d', auth=True, arg=True,
- help='Syntax: DELE <SP> file-name (delete file).'),
- 'EPRT': dict(
- perm=None, auth=True, arg=True,
- help='Syntax: EPRT <SP> |proto|ip|port| (extended active mode).'),
- 'EPSV': dict(
- perm=None, auth=True, arg=None,
- help='Syntax: EPSV [<SP> proto/"ALL"] (extended passive mode).'),
- 'FEAT': dict(
- perm=None, auth=False, arg=False,
- help='Syntax: FEAT (list all new features supported).'),
- 'HELP': dict(
- perm=None, auth=False, arg=None,
- help='Syntax: HELP [<SP> cmd] (show help).'),
- 'LIST': dict(
- perm='l', auth=True, arg=None,
- help='Syntax: LIST [<SP> path] (list files).'),
- 'MDTM': dict(
- perm='l', auth=True, arg=True,
- help='Syntax: MDTM [<SP> path] (file last modification time).'),
- 'MLSD': dict(
- perm='l', auth=True, arg=None,
- help='Syntax: MLSD [<SP> path] (list directory).'),
- 'MLST': dict(
- perm='l', auth=True, arg=None,
- help='Syntax: MLST [<SP> path] (show information about path).'),
- 'MODE': dict(
- perm=None, auth=True, arg=True,
- help='Syntax: MODE <SP> mode (noop; set data transfer mode).'),
- 'MKD': dict(
- perm='m', auth=True, arg=True,
- help='Syntax: MKD <SP> path (create directory).'),
- 'NLST': dict(
- perm='l', auth=True, arg=None,
- help='Syntax: NLST [<SP> path] (list path in a compact form).'),
- 'NOOP': dict(
- perm=None, auth=False, arg=False,
- help='Syntax: NOOP (just do nothing).'),
- 'OPTS': dict(
- perm=None, auth=True, arg=True,
- help='Syntax: OPTS <SP> cmd [<SP> option] (set option for
command).'),
- 'PASS': dict(
- perm=None, auth=False, arg=None,
- help='Syntax: PASS [<SP> password] (set user password).'),
- 'PASV': dict(
- perm=None, auth=True, arg=False,
- help='Syntax: PASV (open passive data connection).'),
- 'PORT': dict(
- perm=None, auth=True, arg=True,
- help='Syntax: PORT <sp> h,h,h,h,p,p (open active data
connection).'),
- 'PWD': dict(
- perm=None, auth=True, arg=False,
- help='Syntax: PWD (get current working directory).'),
- 'QUIT': dict(
- perm=None, auth=False, arg=False,
- help='Syntax: QUIT (quit current session).'),
- 'REIN': dict(
- perm=None, auth=True, arg=False,
- help='Syntax: REIN (flush account).'),
- 'REST': dict(
- perm=None, auth=True, arg=True,
- help='Syntax: REST <SP> offset (set file offset).'),
- 'RETR': dict(
- perm='r', auth=True, arg=True,
- help='Syntax: RETR <SP> file-name (retrieve a file).'),
- 'RMD': dict(
- perm='d', auth=True, arg=True,
- help='Syntax: RMD <SP> dir-name (remove directory).'),
- 'RNFR': dict(
- perm='f', auth=True, arg=True,
- help='Syntax: RNFR <SP> file-name (rename (source name)).'),
- 'RNTO': dict(
- perm='f', auth=True, arg=True,
- help='Syntax: RNTO <SP> file-name (rename (destination name)).'),
- 'SITE': dict(
- perm=None, auth=False, arg=True,
- help='Syntax: SITE <SP> site-command (execute SITE command).'),
- 'SITE HELP': dict(
- perm=None, auth=False, arg=None,
- help='Syntax: SITE HELP [<SP> cmd] (show SITE command help).'),
- 'SITE CHMOD': dict(
- perm='M', auth=True, arg=True,
- help='Syntax: SITE CHMOD <SP> mode path (change file mode).'),
- 'SIZE': dict(
- perm='l', auth=True, arg=True,
- help='Syntax: SIZE <SP> file-name (get file size).'),
- 'STAT': dict(
- perm='l', auth=False, arg=None,
- help='Syntax: STAT [<SP> path name] (server stats [list files]).'),
- 'STOR': dict(
- perm='w', auth=True, arg=True,
- help='Syntax: STOR <SP> file-name (store a file).'),
- 'STOU': dict(
- perm='w', auth=True, arg=None,
- help='Syntax: STOU [<SP> name] (store a file with a unique
name).'),
- 'STRU': dict(
- perm=None, auth=True, arg=True,
- help='Syntax: STRU <SP> type (noop; set file structure).'),
- 'SYST': dict(
- perm=None, auth=False, arg=False,
- help='Syntax: SYST (get operating system type).'),
- 'TYPE': dict(
- perm=None, auth=True, arg=True,
- help='Syntax: TYPE <SP> [A | I] (set transfer type).'),
- 'USER': dict(
- perm=None, auth=False, arg=True,
- help='Syntax: USER <SP> user-name (set username).'),
- 'XCUP': dict(
- perm='e', auth=True, arg=False,
- help='Syntax: XCUP (obsolete; go to parent directory).'),
- 'XCWD': dict(
- perm='e', auth=True, arg=None,
- help='Syntax: XCWD [<SP> dir-name] (obsolete; change directory).'),
- 'XMKD': dict(
- perm='m', auth=True, arg=True,
- help='Syntax: XMKD <SP> dir-name (obsolete; create directory).'),
- 'XPWD': dict(
- perm=None, auth=True, arg=False,
- help='Syntax: XPWD (obsolete; get current dir).'),
- 'XRMD': dict(
- perm='d', auth=True, arg=True,
- help='Syntax: XRMD <SP> dir-name (obsolete; remove directory).'),
-}
+ 'ABOR' : dict(perm=None, auth=True, arg=False,
+ help='Syntax: ABOR (abort transfer).'),
+ 'ALLO' : dict(perm=None, auth=True, arg=True,
+ help='Syntax: ALLO <SP> bytes (noop; allocate
storage).'),
+ 'APPE' : dict(perm='a', auth=True, arg=True,
+ help='Syntax: APPE <SP> file-name (append data to
file).'),
+ 'CDUP' : dict(perm='e', auth=True, arg=False,
+ help='Syntax: CDUP (go to parent directory).'),
+ 'CWD' : dict(perm='e', auth=True, arg=None,
+ help='Syntax: CWD [<SP> dir-name] (change working
directory).'),
+ 'DELE' : dict(perm='d', auth=True, arg=True,
+ help='Syntax: DELE <SP> file-name (delete file).'),
+ 'EPRT' : dict(perm=None, auth=True, arg=True,
+ help='Syntax: EPRT <SP> |proto|ip|port| (extended active
mode).'),
+ 'EPSV' : dict(perm=None, auth=True, arg=None,
+ help='Syntax: EPSV [<SP> proto/"ALL"] (extended passive
mode).'),
+ 'FEAT' : dict(perm=None, auth=False, arg=False,
+ help='Syntax: FEAT (list all new features supported).'),
+ 'HELP' : dict(perm=None, auth=False, arg=None,
+ help='Syntax: HELP [<SP> cmd] (show help).'),
+ 'LIST' : dict(perm='l', auth=True, arg=None,
+ help='Syntax: LIST [<SP> path] (list files).'),
+ 'MDTM' : dict(perm='l', auth=True, arg=True,
+ help='Syntax: MDTM [<SP> path] (file last modification
time).'),
+ 'MLSD' : dict(perm='l', auth=True, arg=None,
+ help='Syntax: MLSD [<SP> path] (list directory).'),
+ 'MLST' : dict(perm='l', auth=True, arg=None,
+ help='Syntax: MLST [<SP> path] (show information about
path).'),
+ 'MODE' : dict(perm=None, auth=True, arg=True,
+ help='Syntax: MODE <SP> mode (noop; set data transfer
mode).'),
+ 'MKD' : dict(perm='m', auth=True, arg=True,
+ help='Syntax: MKD <SP> path (create directory).'),
+ 'NLST' : dict(perm='l', auth=True, arg=None,
+ help='Syntax: NLST [<SP> path] (list path in a compact
form).'),
+ 'NOOP' : dict(perm=None, auth=False, arg=False,
+ help='Syntax: NOOP (just do nothing).'),
+ 'OPTS' : dict(perm=None, auth=True, arg=True,
+ help='Syntax: OPTS <SP> cmd [<SP> option] (set option
for command).'),
+ 'PASS' : dict(perm=None, auth=False, arg=None,
+ help='Syntax: PASS [<SP> password] (set user
password).'),
+ 'PASV' : dict(perm=None, auth=True, arg=False,
+ help='Syntax: PASV (open passive data connection).'),
+ 'PORT' : dict(perm=None, auth=True, arg=True,
+ help='Syntax: PORT <sp> h1,h2,h3,h4,p1,p2 (open active
data connection).'),
+ 'PWD' : dict(perm=None, auth=True, arg=False,
+ help='Syntax: PWD (get current working directory).'),
+ 'QUIT' : dict(perm=None, auth=False, arg=False,
+ help='Syntax: QUIT (quit current session).'),
+ 'REIN' : dict(perm=None, auth=True, arg=False,
+ help='Syntax: REIN (flush account).'),
+ 'REST' : dict(perm=None, auth=True, arg=True,
+ help='Syntax: REST <SP> offset (set file offset).'),
+ 'RETR' : dict(perm='r', auth=True, arg=True,
+ help='Syntax: RETR <SP> file-name (retrieve a file).'),
+ 'RMD' : dict(perm='d', auth=True, arg=True,
+ help='Syntax: RMD <SP> dir-name (remove directory).'),
+ 'RNFR' : dict(perm='f', auth=True, arg=True,
+ help='Syntax: RNFR <SP> file-name (rename (source
name)).'),
+ 'RNTO' : dict(perm='f', auth=True, arg=True,
+ help='Syntax: RNTO <SP> file-name (rename (destination
name)).'),
+ 'SITE' : dict(perm=None, auth=False, arg=True,
+ help='Syntax: SITE <SP> site-command (execute SITE
command).'),
+ 'SITE HELP' : dict(perm=None, auth=False, arg=None,
+ help='Syntax: SITE HELP [<SP> site-command] (show
SITE command help).'),
+ 'SITE CHMOD': dict(perm='M', auth=True, arg=True,
+ help='Syntax: SITE CHMOD <SP> mode path (change
file mode).'),
+ 'SIZE' : dict(perm='l', auth=True, arg=True,
+ help='Syntax: SIZE <SP> file-name (get file size).'),
+ 'STAT' : dict(perm='l', auth=False, arg=None,
+ help='Syntax: STAT [<SP> path name] (server stats [list
files]).'),
+ 'STOR' : dict(perm='w', auth=True, arg=True,
+ help='Syntax: STOR <SP> file-name (store a file).'),
+ 'STOU' : dict(perm='w', auth=True, arg=None,
+ help='Syntax: STOU [<SP> file-name] (store a file with a
unique name).'),
+ 'STRU' : dict(perm=None, auth=True, arg=True,
+ help='Syntax: STRU <SP> type (noop; set file
structure).'),
+ 'SYST' : dict(perm=None, auth=False, arg=False,
+ help='Syntax: SYST (get operating system type).'),
+ 'TYPE' : dict(perm=None, auth=True, arg=True,
+ help='Syntax: TYPE <SP> [A | I] (set transfer type).'),
+ 'USER' : dict(perm=None, auth=False, arg=True,
+ help='Syntax: USER <SP> user-name (set username).'),
+ 'XCUP' : dict(perm='e', auth=True, arg=False,
+ help='Syntax: XCUP (obsolete; go to parent directory).'),
+ 'XCWD' : dict(perm='e', auth=True, arg=None,
+ help='Syntax: XCWD [<SP> dir-name] (obsolete; change
directory).'),
+ 'XMKD' : dict(perm='m', auth=True, arg=True,
+ help='Syntax: XMKD <SP> dir-name (obsolete; create
directory).'),
+ 'XPWD' : dict(perm=None, auth=True, arg=False,
+ help='Syntax: XPWD (obsolete; get current dir).'),
+ 'XRMD' : dict(perm='d', auth=True, arg=True,
+ help='Syntax: XRMD <SP> dir-name (obsolete; remove
directory).'),
+ }

if not hasattr(os, 'chmod'):
del proto_cmds['SITE CHMOD']

-
def _strerror(err):
if isinstance(err, EnvironmentError):
try:
@@ -234,7 +185,6 @@
else:
return str(err)

-
def _support_hybrid_ipv6():
"""Return True if it is possible to use hybrid IPv6/IPv4 sockets
on this platform.
@@ -256,7 +206,6 @@

SUPPORTS_HYBRID_IPV6 = _support_hybrid_ipv6()

-
class _FileReadWriteError(OSError):
"""Exception raised when reading or writing a file during a
transfer."""

@@ -357,9 +306,8 @@
ip = ip[7:]
# The format of 227 response in not standardized.
# This is the most expected:
- resp = '227 Entering passive mode (%s,%d,%d).' % (
- ip.replace('.', ','), port // 256, port % 256)
- self.cmd_channel.respond(resp)
+ self.cmd_channel.respond('227 Entering passive mode
(%s,%d,%d).' % (
+ ip.replace('.', ','), port // 256, port %
256))
else:
self.cmd_channel.respond('229 Entering extended passive mode '
'(|||%d|).' % port)
@@ -384,15 +332,15 @@
sock.close()
except socket.error:
pass
- msg = '425 Rejected data connection from foreign address '
\
- '%s:%s.' % (addr[0], addr[1])
+ msg = '425 Rejected data connection from foreign
address %s:%s.' \
+ %(addr[0], addr[1])
self.cmd_channel.respond_w_warning(msg)
# do not close listening socket: it couldn't be client's
blame
return
else:
# site-to-site FTP allowed
- msg = 'Established data connection with foreign address ' \
- '%s:%s.' % (addr[0], addr[1])
+ msg = 'Established data connection with foreign
address %s:%s.'\
+ % (addr[0], addr[1])
self.cmd_channel.log(msg, logfun=logger.warning)
# Immediately close the current channel (we accept only one
# connection at time) and avoid running out of max connections
@@ -509,8 +457,7 @@
if self.cmd_channel.connected:
msg = "Active data channel timed out."
self.cmd_channel.respond("421 " + msg, logfun=logger.info)
- self.cmd_channel.log_cmd(
- self._cmd, self._normalized_addr, 421, msg)
+ self.cmd_channel.log_cmd(self._cmd, self._normalized_addr,
421, msg)
self.close()

def handle_close(self):
@@ -522,8 +469,7 @@
if self.cmd_channel.connected:
msg = "Can't connect to specified address."
self.cmd_channel.respond("425 " + msg)
- self.cmd_channel.log_cmd(
- self._cmd, self._normalized_addr, 425, msg)
+ self.cmd_channel.log_cmd(self._cmd, self._normalized_addr,
425, msg)

def handle_error(self):
"""Called to handle any uncaught exceptions."""
@@ -601,8 +547,7 @@
# if we get an exception here we want the dispatcher
# instance to set socket attribute before closing, see:
# http://code.google.com/p/pyftpdlib/issues/detail?id=188
- AsyncChat.__init__(
- self, socket.socket(), ioloop=cmd_channel.ioloop)
+ AsyncChat.__init__(self, socket.socket(),
ioloop=cmd_channel.ioloop)
# http://code.google.com/p/pyftpdlib/issues/detail?id=143
self.close()
if err.args[0] == errno.EINVAL:
@@ -624,8 +569,8 @@
addr = "%s:%s" % self.socket.getpeername()[:2]
except socket.error:
addr = None
- status = [self.__class__.__module__ + "." +
self.__class__.__name__]
- status.append("(addr=%s, user=%r, receive=%r, file=%r)"
+ status = [self.__class__.__module__+ "." + self.__class__.__name__]
+ status.append("(addr=%s, user=%r, receive=%r, file=%r)" \
% (addr, self.cmd_channel.username or '',
self.receive, getattr(self.file_obj, 'name', '')))
return '<%s at %#x>' % (' '.join(status), id(self))
@@ -633,9 +578,9 @@
__str__ = __repr__

def _use_sendfile(self, producer):
- return (self.cmd_channel.use_sendfile
- and isinstance(producer, FileProducer)
- and producer.type == 'i')
+ return self.cmd_channel.use_sendfile \
+ and isinstance(producer, FileProducer) \
+ and producer.type == 'i'

def push(self, data):
self._initialized = True
@@ -752,7 +697,7 @@
if p is None:
if not self.ac_out_buffer:
self.producer_fifo.pop()
- # self.close()
+ #self.close()
self.handle_close()
return
elif isinstance(p, str):
@@ -778,7 +723,7 @@
self.tot_bytes_received += len(chunk)
if not chunk:
self.transfer_finished = True
- # self.close() # <-- asyncore.recv() already do that...
+ #self.close() # <-- asyncore.recv() already do that...
return
if self._data_wrapper is not None:
chunk = self._data_wrapper(chunk)
@@ -857,7 +802,7 @@
self._resp = ("226 Transfer complete.", logger.debug)
else:
tot_bytes = self.get_transmitted_bytes()
- self._resp = ("426 Transfer aborted; %d bytes
transmitted."
+ self._resp = ("426 Transfer aborted; %d bytes
transmitted." \
% tot_bytes, logger.debug)
finally:
self.close()
@@ -879,13 +824,12 @@
if self.file_obj is not None:
filename = self.file_obj.name
elapsed_time = round(self.get_elapsed_time(), 3)
- self.cmd_channel.log_transfer(
- cmd=self.cmd,
- filename=self.file_obj.name,
- receive=self.receive,
- completed=self.transfer_finished,
- elapsed=elapsed_time,
- bytes=self.get_transmitted_bytes())
+ self.cmd_channel.log_transfer(cmd=self.cmd,
+ filename=self.file_obj.name,
+ receive=self.receive,
+
completed=self.transfer_finished,
+ elapsed=elapsed_time,
+
bytes=self.get_transmitted_bytes())
if self.transfer_finished:
if self.receive:
self.cmd_channel.on_file_received(filename)
@@ -906,7 +850,6 @@
pass
else:
class _AsyncChatNewStyle(object, AsyncChat):
-
def __init__(self, *args, **kwargs):
super(object, self).__init__(*args, **kwargs) # bypass object

@@ -987,8 +930,8 @@

self.del_channel()
self._cancel_throttler()
- self._throttler = self.ioloop.call_later(
- sleepfor, unsleep, _errback=self.handle_error)
+ self._throttler = self.ioloop.call_later(sleepfor, unsleep,
+
_errback=self.handle_error)
self._timenext = now + 1

def close(self):
@@ -1206,7 +1149,7 @@
self._rnfr = None
self._idler = None
self._log_debug =
logging.getLogger('pyftpdlib').getEffectiveLevel() \
- <= logging.DEBUG
+ <= logging.DEBUG

if os.name == 'posix':
self._current_facts.append('unique')
@@ -1254,7 +1197,7 @@
else: # python < 2.5
ip, port = self.socket.getsockname()[:2]
self._af = socket.getaddrinfo(ip, port, socket.AF_UNSPEC,
- socket.SOCK_STREAM)[0][0]
+ socket.SOCK_STREAM)[0][0]

# try to handle urgent data inline
try:
@@ -1276,11 +1219,11 @@
return

if self.timeout:
- self._idler = self.ioloop.call_later(
- self.timeout, self.handle_timeout,
_errback=self.handle_error)
+ self._idler = self.ioloop.call_later(self.timeout,
self.handle_timeout,
+
_errback=self.handle_error)

def __repr__(self):
- status = [self.__class__.__module__ + "." +
self.__class__.__name__]
+ status = [self.__class__.__module__+ "." + self.__class__.__name__]
status.append("(addr=%s:%s, user=%r)" % (self.remote_ip,
self.remote_port, self.username or ''))
return '<%s at %#x>' % (' '.join(status), id(self))
@@ -1374,18 +1317,18 @@
self._in_buffer_len = 0

cmd = line.split(' ')[0].upper()
- arg = line[len(cmd) + 1:]
+ arg = line[len(cmd)+1:]
try:
self.pre_process_command(line, cmd, arg)
except UnicodeEncodeError:
- self.respond("501 can't decode path (server filesystem
encoding "
+ self.respond("501 can't decode path (server filesystem
encoding " \
"is %s)" % sys.getfilesystemencoding())

def pre_process_command(self, line, cmd, arg):
kwargs = {}
if cmd == "SITE" and arg:
cmd = "SITE %s" % arg.split(' ')[0].upper()
- arg = line[len(cmd) + 1:]
+ arg = line[len(cmd)+1:]

if cmd != 'PASS':
self.logline("<- %s" % line)
@@ -1626,7 +1569,7 @@
"""
# Close accepting DTP only. By closing ActiveDTP DTPHandler
# would receive a closed socket object.
- # self._shutdown_connecting_dtp()
+ #self._shutdown_connecting_dtp()
if self._dtp_acceptor is not None:
self._dtp_acceptor.close()
self._dtp_acceptor = None
@@ -1669,8 +1612,9 @@
# data transfer finished, restart the idle timer
if self._idler is not None and not self._idler.cancelled:
self._idler.cancel()
- self._idler = self.ioloop.call_later(
- self.timeout, self.handle_timeout,
_errback=self.handle_error)
+ self._idler = self.ioloop.call_later(self.timeout,
+ self.handle_timeout,
+
_errback=self.handle_error)

# --- utility

@@ -1704,8 +1648,7 @@
- (file) file: the file[-like] object to send (if any).
"""
if self.data_channel is not None:
- self.respond(
- "125 Data connection already open. Transfer starting.")
+ self.respond("125 Data connection already open. Transfer
starting.")
if file:
self.data_channel.file_obj = file
try:
@@ -1720,8 +1663,7 @@
# dealing with this exception is up to DTP (see bug #84)
self.data_channel.handle_error()
else:
- self.respond(
- "150 File status okay. About to open data connection.")
+ self.respond("150 File status okay. About to open data
connection.")
self._out_dtp_queue = (data, isproducer, file, cmd)

def flush_account(self):
@@ -1850,10 +1792,12 @@
number of bytes transmitted.
"""
line = '%s %s completed=%s bytes=%s seconds=%s' % \
- (cmd, filename, completed and 1 or 0, bytes, elapsed)
+ (cmd, filename, completed and 1 or 0, bytes, elapsed)
self.log(line)

+
# --- connection
+
def _make_eport(self, ip, port):
"""Establish an active data channel with remote client which
issued a PORT or EPRT command.
@@ -1874,7 +1818,7 @@
remote_ip = remote_ip[7:]
if not self.permit_foreign_addresses and ip != remote_ip:
msg = "501 Rejected data connection to foreign address %s:%s."
\
- % (ip, port)
+ % (ip, port)
self.respond_w_warning(msg)
return

@@ -2036,8 +1980,7 @@
self._make_epasv(extmode=True)
elif line.lower() == 'all':
self._epsvall = True
- self.respond(
- '220 Other commands other than EPSV are now disabled.')
+ self.respond('220 Other commands other than EPSV are now
disabled.')
else:
if self._af == socket.AF_INET:
self.respond('501 Unknown network protocol (use 1).')
@@ -2143,9 +2086,8 @@
basedir, basename = os.path.split(path)
perms = self.authorizer.get_perms(self.username)
try:
- iterator = self.run_as_current_user(
- self.fs.format_mlsx, basedir, [basename], perms,
- self._current_facts, ignore_err=False)
+ iterator = self.run_as_current_user(self.fs.format_mlsx,
basedir,
+ [basename], perms, self._current_facts,
ignore_err=False)
data = b('').join(iterator)
except (OSError, FilesystemError):
err = sys.exc_info()[1]
@@ -2180,7 +2122,7 @@
else:
perms = self.authorizer.get_perms(self.username)
iterator = self.fs.format_mlsx(path, listing, perms,
- self._current_facts)
+ self._current_facts)
producer = BufferedIteratorProducer(iterator)
self.push_dtp_data(producer, isproducer=True, cmd="MLSD")
return path
@@ -2246,7 +2188,7 @@
except (EnvironmentError, FilesystemError):
err = sys.exc_info()[1]
why = _strerror(err)
- self.respond('550 %s.' % why)
+ self.respond('550 %s.' %why)
return

if rest_pos:
@@ -2282,6 +2224,7 @@
self._in_dtp_queue = (fd, cmd)
return file

+
def ftp_STOU(self, line):
"""Store a file on the server with a unique name.
On success return the file path, else None.
@@ -2370,15 +2313,13 @@
def ftp_ABOR(self, line):
"""Abort the current data transfer."""
# ABOR received while no data channel exists
- if (self._dtp_acceptor is None
- and self._dtp_connector is None
- and self.data_channel is None):
+ if (self._dtp_acceptor is None) and (self._dtp_connector is None) \
+ and (self.data_channel is None):
self.respond("225 No transfer to abort.")
return
else:
# a PASV or PORT was received but connection wasn't made yet
- if (self._dtp_acceptor is not None
- or self._dtp_connector is not None):
+ if self._dtp_acceptor is not None or self._dtp_connector is
not None:
self._shutdown_connecting_dtp()
resp = "225 ABOR command successful; data channel closed."

@@ -2402,7 +2343,9 @@
resp = "225 ABOR command successful; data channel
closed."
self.respond(resp)

+
# --- authentication
+
def ftp_USER(self, line):
"""Set the username for the current session."""
# RFC-959 specifies a 530 response to the USER command if the
@@ -2473,10 +2416,9 @@
raise ValueError('type(home) != text')
else:
warnings.warn(
- '%s.get_home_dir returned a non-unicode string;
now '
- 'casting to unicode' % (
- self.authorizer.__class__.__name__),
- RuntimeWarning)
+ '%s.get_home_dir returned a non-unicode string;
now ' \
+ 'casting to unicode' %
self.authorizer.__class__.__name__,
+ RuntimeWarning)
home = home.decode('utf8')

if len(msg_login) <= 75:
@@ -2506,7 +2448,9 @@
# code to be given in this case, but this is wrong...
self.respond("230 Ready for new user.")

+
# --- filesystem operations
+
def ftp_PWD(self, line):
"""Return the name of the current working directory to the
client."""
# The 257 response is supposed to include the directory
@@ -2570,7 +2514,7 @@
line = self.fs.fs2ftp(path)
if self._current_type == 'a':
why = "SIZE not allowed in ASCII mode"
- self.respond("550 %s." % why)
+ self.respond("550 %s." %why)
return
if not self.fs.isfile(self.fs.realpath(path)):
why = "%s is not retrievable" % line
@@ -2624,13 +2568,12 @@
except (OSError, FilesystemError):
err = sys.exc_info()[1]
why = _strerror(err)
- self.respond('550 %s.' % why)
+ self.respond('550 %s.' %why)
else:
# The 257 response is supposed to include the directory
# name and in case it contains embedded double-quotes
# they must be doubled (see RFC-959, chapter 7, appendix 2).
- self.respond(
- '257 "%s" directory created.' % line.replace('"', '""'))
+ self.respond('257 "%s" directory created.' %
line.replace('"', '""'))
return path

def ftp_RMD(self, path):
@@ -2695,7 +2638,9 @@
self.respond("250 Renaming ok.")
return (src, path)

+
# --- others
+
def ftp_TYPE(self, line):
"""Set current type data type to binary/ascii"""
type = line.upper().replace(' ', '')
@@ -2794,7 +2739,7 @@
except (OSError, FilesystemError):
err = sys.exc_info()[1]
why = _strerror(err)
- self.respond('550 %s.' % why)
+ self.respond('550 %s.' %why)
else:
self.push('213-Status of "%s":\r\n' % line)
self.push_with_producer(BufferedIteratorProducer(iterator))
@@ -2804,7 +2749,7 @@
def ftp_FEAT(self, line):
"""List all new features supported as defined in RFC-2398."""
features = set(['UTF8', 'TVFS'])
- features.update([feat for feat in ('EPRT', 'EPSV', 'MDTM', 'SIZE')
+ features.update([feat for feat in ('EPRT', 'EPSV', 'MDTM', 'SIZE')
\
if feat in self.proto_cmds])
features.update(self._extra_feats)
if 'MLST' in self.proto_cmds or 'MLSD' in self.proto_cmds:
@@ -2841,8 +2786,7 @@
self.respond('501 %s.' % err)
else:
facts = [x.lower() for x in arg.split(';')]
- self._current_facts = \
- [x for x in facts if x in self._available_facts]
+ self._current_facts = [x for x in facts if x in
self._available_facts]
f = ''.join([x + ';' for x in self._current_facts])
self.respond('200 MLST OPTS ' + f)

@@ -2876,8 +2820,7 @@
# provide a compact list of recognized commands
def formatted_help():
cmds = []
- keys = [x for x in self.proto_cmds.keys()
- if not x.startswith('SITE ')]
+ keys = [x for x in self.proto_cmds.keys() if not
x.startswith('SITE ')]
keys.sort()
while keys:
elems = tuple((keys[0:8]))
@@ -2944,29 +2887,31 @@
# ftp.exe) still use them.

def ftp_XCUP(self, line):
- "Change to the parent directory. Synonym for CDUP. Deprecated."
+ """Change to the parent directory. Synonym for CDUP. Deprecated."""
return self.ftp_CDUP(line)

def ftp_XCWD(self, line):
- "Change the current working directory. Synonym for CWD.
Deprecated."
+ """Change the current working directory. Synonym for CWD.
Deprecated."""
return self.ftp_CWD(line)

def ftp_XMKD(self, line):
- "Create the specified directory. Synonym for MKD. Deprecated."
+ """Create the specified directory. Synonym for MKD. Deprecated."""
return self.ftp_MKD(line)

def ftp_XPWD(self, line):
- "Return the current working directory. Synonym for PWD.
Deprecated."
+ """Return the current working directory. Synonym for PWD.
Deprecated."""
return self.ftp_PWD(line)

def ftp_XRMD(self, line):
- "Remove the specified directory. Synonym for RMD. Deprecated."
+ """Remove the specified directory. Synonym for RMD. Deprecated."""
return self.ftp_RMD(line)


+
# ===================================================================
# --- FTP over SSL
# ===================================================================
+
# requires PyOpenSSL - http://pypi.python.org/pypi/pyOpenSSL
try:
from OpenSSL import SSL
@@ -2975,16 +2920,14 @@
else:
_ssl_proto_cmds = proto_cmds.copy()
_ssl_proto_cmds.update({
- 'AUTH': dict(
- perm=None, auth=False, arg=True,
- help='Syntax: AUTH <SP> TLS|SSL (set up secure control
channel).'),
- 'PBSZ': dict(
- perm=None, auth=False, arg=True,
- help='Syntax: PBSZ <SP> 0 (negotiate TLS buffer).'),
- 'PROT': dict(
- perm=None, auth=False, arg=True,
- help='Syntax: PROT <SP> [C|P] (set up un/secure data
channel).'),
- })
+ 'AUTH': dict(perm=None, auth=False, arg=True,
+ help='Syntax: AUTH <SP> TLS|SSL (set up secure
control channel).'),
+ 'PBSZ': dict(perm=None, auth=False, arg=True,
+ help='Syntax: PBSZ <SP> 0 (negotiate TLS buffer).'),
+ 'PROT': dict(perm=None, auth=False, arg=True,
+ help='Syntax: PROT <SP> [C|P] (set up un/secure data
channel).'),
+ })
+

class SSLConnection(_AsyncChatNewStyle):
"""An AsyncChat subclass supporting TLS/SSL."""
@@ -3119,21 +3062,19 @@
twisted/internet/tcp.py code has been used as an example.
"""
self._ssl_closing = True
- if os.name == 'posix':
- # since SSL_shutdown() doesn't report errors, an empty
- # write call is done first, to try to detect if the
- # connection has gone away
- try:
- os.write(self.socket.fileno(), b(''))
- except (OSError, socket.error):
- err = sys.exc_info()[1]
- if err.args[0] in (errno.EINTR, errno.EWOULDBLOCK,
- errno.ENOBUFS):
- return
- elif err.args[0] in _DISCONNECTED:
- return super(SSLConnection, self).close()
- else:
- raise
+ # since SSL_shutdown() doesn't report errors, an empty
+ # write call is done first, to try to detect if the
+ # connection has gone away
+ try:
+ os.write(self.socket.fileno(), b(''))
+ except (OSError, socket.error):
+ err = sys.exc_info()[1]
+ if err.args[0] in (errno.EINTR, errno.EWOULDBLOCK,
errno.ENOBUFS):
+ return
+ elif err.args[0] in _DISCONNECTED:
+ return super(SSLConnection, self).close()
+ else:
+ raise
# Ok, this a mess, but the underlying OpenSSL API simply
# *SUCKS* and I really couldn't do any better.
#
@@ -3198,6 +3139,7 @@
self._ssl_closing = False
super(SSLConnection, self).close()

+
class TLS_DTPHandler(SSLConnection, DTPHandler):
"""A DTPHandler subclass supporting TLS/SSL."""

@@ -3218,6 +3160,7 @@
self.cmd_channel.log_cmd("PROT", "P", 522, "SSL handshake
failed.")
self.close()

+
class TLS_FTPHandler(SSLConnection, FTPHandler):
"""A FTPHandler subclass supporting TLS/SSL.
Implements AUTH, PBSZ and PROT commands (RFC-2228 and RFC-4217).
@@ -3343,11 +3286,10 @@
# From RFC-4217: "As the SSL/TLS protocols self-negotiate
# their levels, there is no need to distinguish between SSL
# and TLS in the application layer".
- self.respond('234 AUTH %s successful.' % arg)
+ self.respond('234 AUTH %s successful.' %arg)
self.secure_connection(self.ssl_context)
else:
- self.respond(
- "502 Unrecognized encryption type (use TLS or SSL).")
+ self.respond("502 Unrecognized encryption type (use TLS or
SSL).")

def ftp_PBSZ(self, line):
"""Negotiate size of buffer for secure data transfer.
@@ -3355,8 +3297,7 @@
Any other value is accepted but ignored.
"""
if not isinstance(self.socket, SSL.Connection):
- self.respond(
- "503 PBSZ not allowed on insecure control connection.")
+ self.respond("503 PBSZ not allowed on insecure control
connection.")
else:
self.respond('200 PBSZ=0 successful.')
self._pbsz = True
@@ -3365,11 +3306,9 @@
"""Setup un/secure data channel."""
arg = line.upper()
if not isinstance(self.socket, SSL.Connection):
- self.respond(
- "503 PROT not allowed on insecure control connection.")
+ self.respond("503 PROT not allowed on insecure control
connection.")
elif not self._pbsz:
- self.respond(
- "503 You must issue the PBSZ command prior to PROT.")
+ self.respond("503 You must issue the PBSZ command prior to
PROT.")
elif arg == 'C':
self.respond('200 Protection set to Clear')
self._prot = False
@@ -3377,6 +3316,6 @@
self.respond('200 Protection set to Private')
self._prot = True
elif arg in ('S', 'E'):
- self.respond('521 PROT %s unsupported (use C or P).' % arg)
+ self.respond('521 PROT %s unsupported (use C or P).' %arg)
else:
self.respond("502 Unrecognized PROT type (use C or P).")
=======================================
--- /trunk/pyftpdlib/ioloop.py Mon Dec 30 11:33:10 2013 UTC
+++ /tags/release-1.3.0/pyftpdlib/ioloop.py Thu Feb 6 02:27:59 2014 UTC
@@ -83,17 +83,17 @@
IOLoop.instance().loop()
"""

+import asyncore
import asynchat
-import asyncore
import errno
-import heapq
-import logging
+import select
import os
-import select
-import socket
import sys
+import traceback
import time
-import traceback
+import heapq
+import socket
+import logging
try:
import threading
except ImportError:
@@ -148,8 +148,8 @@
# remove cancelled tasks and re-heapify the queue if the
# number of cancelled tasks is more than the half of the
# entire queue
- if (self._cancellations > 512
- and self._cancellations > (len(self._tasks) >> 1)):
+ if self._cancellations > 512 \
+ and self._cancellations > (len(self._tasks) >> 1):
self.reheapify()

try:
@@ -210,9 +210,9 @@
sig = object.__repr__(self)
else:
sig = repr(self._target)
- sig += ' args=%s, kwargs=%s, cancelled=%s, secs=%s' % (
- self._args or '[]', self._kwargs or '{}', self.cancelled,
- self._delay)
+ sig += ' args=%s, kwargs=%s, cancelled=%s, secs=%s' \
+ % (self._args or '[]', self._kwargs or '{}',
self.cancelled,
+ self._delay)
return '<%s>' % sig

__str__ = __repr__
@@ -331,6 +331,7 @@
# localize variable access to minimize overhead
poll = self.poll
socket_map = self.socket_map
+ tasks = self.sched._tasks
sched_poll = self.sched.poll

if timeout is not None:
@@ -611,11 +612,11 @@
kevents = []
if events & self.WRITE:
kevents.append(select.kevent(
- fd, filter=select.KQ_FILTER_WRITE, flags=flags))
+ fd, filter=select.KQ_FILTER_WRITE, flags=flags))
if events & self.READ or not kevents:
# always read when there is not a write
kevents.append(select.kevent(
- fd, filter=select.KQ_FILTER_READ, flags=flags))
+ fd, filter=select.KQ_FILTER_READ, flags=flags))
# even though control() takes a list, it seems to return
# EINVAL on Mac OS X (10.6) when there is more than one
# event in the list
@@ -623,13 +624,12 @@
self._kqueue.control([kevent], 0)

# localize variable access to minimize overhead
- def poll(self,
- timeout,
- _len=len,
- _READ=select.KQ_FILTER_READ,
- _WRITE=select.KQ_FILTER_WRITE,
- _EOF=select.KQ_EV_EOF,
- _ERROR=select.KQ_EV_ERROR):
+ def poll(self, timeout,
+ _len=len,
+ _READ=select.KQ_FILTER_READ,
+ _WRITE=select.KQ_FILTER_WRITE,
+ _EOF=select.KQ_EV_EOF,
+ _ERROR=select.KQ_EV_ERROR):
try:
kevents = self._kqueue.control(None, _len(self.socket_map),
timeout)
@@ -687,7 +687,6 @@
errno.ECONNABORTED, errno.EPIPE, errno.EBADF))
_RETRY = frozenset((errno.EAGAIN, errno.EWOULDBLOCK))

-
class Acceptor(asyncore.dispatcher):
"""Same as base asyncore.dispatcher and supposed to be used to
accept new connections.
@@ -801,8 +800,7 @@
# the remote client is using IPv4 and its address
is
# represented as an IPv4-mapped IPv6 address which
# looks like this ::ffff:151.12.5.65, see:
- # http://en.wikipedia.org/wiki/IPv6\
- # IPv4-mapped_addresses
+ #
http://en.wikipedia.org/wiki/IPv6#IPv4-mapped_addresses
#
http://tools.ietf.org/html/rfc3493.html#section-3.7
# We truncate the first bytes to make it look like
a
# common IPv4 address.
=======================================
--- /trunk/pyftpdlib/log.py Mon Dec 30 11:33:10 2013 UTC
+++ /tags/release-1.3.0/pyftpdlib/log.py Thu Feb 6 02:27:59 2014 UTC
@@ -43,14 +43,13 @@
import curses
except ImportError:
curses = None
-
+
from pyftpdlib._compat import unicode

+# default logger

-# default logger
logger = logging.getLogger('pyftpdlib')

-
def _stderr_supports_color():
color = False
if curses is not None and sys.stderr.isatty():
@@ -90,19 +89,14 @@
# works with unicode strings. The explicit calls to
# unicode() below are harmless in python2 but will do the
# right conversion in python 3.
- fg_color = (curses.tigetstr("setaf") or curses.tigetstr("setf")
- or "")
+ fg_color = (curses.tigetstr("setaf") or
curses.tigetstr("setf") or "")
if (3, 0) < sys.version_info < (3, 2, 3):
fg_color = unicode(fg_color, "ascii")
self._colors = {
- # blues
- logging.DEBUG: unicode(curses.tparm(fg_color, 4), "ascii"),
- # green
- logging.INFO: unicode(curses.tparm(fg_color, 2), "ascii"),
- # yellow
- logging.WARNING: unicode(curses.tparm(fg_color,
3), "ascii"),
- # red
- logging.ERROR: unicode(curses.tparm(fg_color, 1), "ascii")
+ logging.DEBUG: unicode(curses.tparm(fg_color,
4), "ascii"), # blue
+ logging.INFO: unicode(curses.tparm(fg_color,
2), "ascii"), # green
+ logging.WARNING: unicode(curses.tparm(fg_color,
3), "ascii"), # yellow
+ logging.ERROR: unicode(curses.tparm(fg_color,
1), "ascii") # red
}
self._normal = unicode(curses.tigetstr("sgr0"), "ascii")

@@ -149,7 +143,6 @@
formatted = formatted.rstrip() + "\n" + record.exc_text
return formatted.replace("\n", "\n ")

-
def _config_logging():
channel = logging.StreamHandler()
channel.setFormatter(LogFormatter())
=======================================
--- /trunk/pyftpdlib/servers.py Sun Dec 8 21:40:25 2013 UTC
+++ /tags/release-1.3.0/pyftpdlib/servers.py Thu Feb 6 02:27:59 2014 UTC
@@ -77,7 +77,6 @@
__all__ = ['FTPServer']
_BSD = 'bsd' in sys.platform

-
# ===================================================================
# --- base class
# ===================================================================
@@ -200,7 +199,7 @@
Also, logs server start and stop.
"""
if handle_exit:
- log = handle_exit and blocking
+ log = handle_exit and blocking == True
if log:
self._log_start()
try:
@@ -209,9 +208,8 @@
pass
if blocking:
if log:
- logger.info(
- ">>> shutting down FTP server (%s active fds) <<<",
- self._map_len())
+ logger.info(">>> shutting down FTP server (%s active
fds) <<<",
+ self._map_len())
self.close_all()
else:
self.ioloop.loop(timeout, blocking)
@@ -330,8 +328,8 @@
poll_timeout = getattr(self, 'poll_timeout', None)
soonest_timeout = poll_timeout

- while (ioloop.socket_map or ioloop.sched._tasks) and \
- not self._exit.is_set():
+ while (ioloop.socket_map or ioloop.sched._tasks) and not \
+ self._exit.is_set():
try:
if ioloop.socket_map:
poll(timeout=soonest_timeout)
@@ -344,11 +342,9 @@
# functions are supposed to be cancel()ed on
close()
# but by using threads we can incur into
# synchronization issues such as this one.
- # https://code.google.com/p/pyftpdlib/issues/
- # detail?id=245
+ #
https://code.google.com/p/pyftpdlib/issues/detail?id=245
if not ioloop.socket_map:
- # get rid of cancel()led calls
- ioloop.sched.reheapify()
+ ioloop.sched.reheapify() # get rid of
cancel()led calls
soonest_timeout = sched_poll()
if soonest_timeout:
time.sleep(min(soonest_timeout, 1))
@@ -378,8 +374,8 @@
raise
else:
if poll_timeout:
- if (soonest_timeout is None
- or soonest_timeout > poll_timeout):
+ if soonest_timeout is None \
+ or soonest_timeout > poll_timeout:
soonest_timeout = poll_timeout
finally:
ioloop.close()
@@ -417,7 +413,7 @@
def serve_forever(self, timeout=None, blocking=True, handle_exit=True):
self._exit.clear()
if handle_exit:
- log = handle_exit and blocking
+ log = handle_exit and blocking == True
if log:
self._log_start()
try:
@@ -426,9 +422,8 @@
pass
if blocking:
if log:
- logger.info(
- ">>> shutting down FTP server (%s active workers)
<<<",
- self._map_len())
+ logger.info(">>> shutting down FTP server (%s active "
\
+ "workers) <<<", self._map_len())
self.close_all()
else:
self.ioloop.loop(timeout, blocking)
=======================================
--- /trunk/setup.py Mon Dec 30 11:33:10 2013 UTC
+++ /tags/release-1.3.0/setup.py Thu Feb 6 02:27:59 2014 UTC
@@ -43,7 +43,6 @@
except ImportError:
from distutils.core import setup

-
def get_version():
INIT = os.path.abspath(os.path.join(os.path.dirname(__file__),
'pyftpdlib', '__init__.py'))
@@ -60,11 +59,10 @@
finally:
f.close()

-
name = 'pyftpdlib'
version = get_version()
-download_url = ("http://pyftpdlib.googlecode.com/files/" + name + "-" +
- version + ".tar.gz")
+download_url = "http://pyftpdlib.googlecode.com/files/" + name + "-" + \
+ version
+ ".tar.gz"

if sys.version_info < (2, 4):
sys.exit('python version not supported (min 2.4)')
@@ -73,9 +71,9 @@
name=name,
version=version,
description='High-level asynchronous FTP server library',
- long_description="Python FTP server library provides an high-level "
- "portable interface to easily write asynchronous FTP "
- "servers with Python.",
+ long_description="Python FTP server library provides an high-level
portable "
+ "interface to easily write asynchronous FTP servers
with "
+ "Python.",
license='License :: OSI Approved :: MIT License',
platforms='Platform Independent',
author="Giampaolo Rodola'",
@@ -87,29 +85,29 @@
'sendfile', 'asynchronous', 'nonblocking', 'eventdriven',
'rfc959', 'rfc1123', 'rfc2228', 'rfc2428', 'rfc2640', 'rfc3659'],
classifiers=[
- 'Development Status :: 5 - Production/Stable',
- 'Environment :: Console',
- 'Intended Audience :: Developers',
- 'Intended Audience :: System Administrators',
- 'License :: OSI Approved :: MIT License',
- 'Operating System :: OS Independent',
- 'Programming Language :: Python',
- 'Topic :: Internet :: File Transfer Protocol (FTP)',
- 'Topic :: Software Development :: Libraries :: Python Modules',
- 'Topic :: System :: Filesystems',
- 'Programming Language :: Python',
- 'Programming Language :: Python :: 2',
- 'Programming Language :: Python :: 2.4',
- 'Programming Language :: Python :: 2.5',
- 'Programming Language :: Python :: 2.6',
- 'Programming Language :: Python :: 2.7',
- 'Programming Language :: Python :: 3',
- 'Programming Language :: Python :: 3.0',
- 'Programming Language :: Python :: 3.1',
- 'Programming Language :: Python :: 3.2',
- 'Programming Language :: Python :: 3.3'
- ],
-)
+ 'Development Status :: 5 - Production/Stable',
+ 'Environment :: Console',
+ 'Intended Audience :: Developers',
+ 'Intended Audience :: System Administrators',
+ 'License :: OSI Approved :: MIT License',
+ 'Operating System :: OS Independent',
+ 'Programming Language :: Python',
+ 'Topic :: Internet :: File Transfer Protocol (FTP)',
+ 'Topic :: Software Development :: Libraries :: Python Modules',
+ 'Topic :: System :: Filesystems',
+ 'Programming Language :: Python',
+ 'Programming Language :: Python :: 2',
+ 'Programming Language :: Python :: 2.4',
+ 'Programming Language :: Python :: 2.5',
+ 'Programming Language :: Python :: 2.6',
+ 'Programming Language :: Python :: 2.7',
+ 'Programming Language :: Python :: 3',
+ 'Programming Language :: Python :: 3.0',
+ 'Programming Language :: Python :: 3.1',
+ 'Programming Language :: Python :: 3.2',
+ 'Programming Language :: Python :: 3.3',
+ ],
+ )


# suggest to install pysendfile
=======================================
--- /trunk/test/bench.py Mon Dec 30 11:33:10 2013 UTC
+++ /tags/release-1.3.0/test/bench.py Thu Feb 6 02:27:59 2014 UTC
@@ -90,6 +90,7 @@
# 300 concurrent clients (QUIT) 0.00 secs


+
from __future__ import with_statement, division
import ftplib
import sys
@@ -110,7 +111,6 @@
except ImportError:
psutil = None

-
HOST = 'localhost'
PORT = 21
USER = None
@@ -135,38 +135,34 @@
def b(s):
return s

+# http://goo.gl/6V8Rm
+def hilite(string, ok=True, bold=False):
+ """Return an highlighted version of 'string'."""
+ attr = []
+ if ok is None: # no color
+ pass
+ elif ok: # green
+ attr.append('32')
+ else: # red
+ attr.append('31')
+ if bold:
+ attr.append('1')
+ return '\x1b[%sm%s\x1b[0m' % (';'.join(attr), string)

if not sys.stdout.isatty() or os.name != 'posix':
def hilite(s, *args, **kwargs):
return s
-else:
- # http://goo.gl/6V8Rm
- def hilite(string, ok=True, bold=False):
- """Return an highlighted version of 'string'."""
- attr = []
- if ok is None: # no color
- pass
- elif ok: # green
- attr.append('32')
- else: # red
- attr.append('31')
- if bold:
- attr.append('1')
- return '\x1b[%sm%s\x1b[0m' % (';'.join(attr), string)
-

server_memory = []

-
def print_bench(what, value, unit=""):
s = "%s %s %-8s" % (hilite("%-50s" % what, ok=None, bold=0),
- hilite("%8.2f" % value),
- unit)
+ hilite("%8.2f" % value),
+ unit)
if server_memory:
s += "%s" % hilite(server_memory.pop())
print_(s.strip())

-
# http://goo.gl/zeJZl
def bytes2human(n, format="%(value).1f%(symbol)s"):
"""
@@ -178,14 +174,13 @@
symbols = ('B', 'K', 'M', 'G', 'T', 'P', 'E', 'Z', 'Y')
prefix = {}
for i, s in enumerate(symbols[1:]):
- prefix[s] = 1 << (i + 1) * 10
+ prefix[s] = 1 << (i+1)*10
for symbol in reversed(symbols[1:]):
if n >= prefix[symbol]:
value = float(n) / prefix[symbol]
return format % locals()
return format % dict(symbol=symbols[0], value=n)

-
# http://goo.gl/zeJZl
def human2bytes(s):
"""
@@ -199,12 +194,11 @@
num = s[:-1]
assert num.isdigit() and letter in symbols, s
num = float(num)
- prefix = {symbols[0]: 1}
+ prefix = {symbols[0]:1}
for i, s in enumerate(symbols[1:]):
- prefix[s] = 1 << (i + 1) * 10
+ prefix[s] = 1 << (i+1)*10
return int(num * prefix[letter])

-
def register_memory():
"""Register an approximation of memory used by FTP server process
and all of its children.
@@ -229,7 +223,6 @@
mem += get_mem(child)
server_memory.append(bytes2human(mem))

-
def timethis(what):
""""Utility function for making simple benchmarks (calculates time
calls).
It can be used either as a context manager or as a decorator.
@@ -243,15 +236,14 @@
res = (stop - start)
print_bench(what, res, "secs")

- if hasattr(what, "__call__"):
- def timed(*args, **kwargs):
+ if hasattr(what,"__call__"):
+ def timed(*args,**kwargs):
with benchmark():
- return what(*args, **kwargs)
+ return what(*args,**kwargs)
return timed
else:
return benchmark()

-
def connect():
"""Connect to FTP server, login and return an ftplib.FTP instance."""
if sys.version_info >= (2, 6):
@@ -265,7 +257,6 @@
ftp.login(USER, PASSWORD)
return ftp

-
def retr(ftp):
"""Same as ftplib's retrbinary() but discard the received data."""
ftp.voidcmd('TYPE I')
@@ -279,7 +270,6 @@
conn.close()
ftp.voidresp()

-
def stor(ftp, size):
"""Same as ftplib's storbinary() but just sends dummy data
instead of reading it from a real file.
@@ -296,7 +286,6 @@
conn.close()
ftp.voidresp()

-
def bytes_per_second(ftp, retr=True):
"""Return the number of bytes transmitted in 1 second."""
bytes = 0
@@ -339,7 +328,6 @@
ftp.quit()
return bytes

-
def cleanup():
ftp = connect()
try:
@@ -369,7 +357,6 @@
def handle_error(self):
raise

-
class AsyncWriter(asynchat.async_chat):
"""Just write dummy data to a connected socket, asynchronously."""
class ChunkProducer:
@@ -392,7 +379,6 @@
def handle_error(self):
raise

-
class AsyncQuit(asynchat.async_chat):

def __init__(self, sock):
@@ -410,7 +396,6 @@
def handle_error(self):
raise

-
class OptFormatter(optparse.IndentedHelpFormatter):

def format_epilog(self, s):
@@ -425,36 +410,31 @@
result.append(help_text)
return ''.join(result)

-
def main():
global HOST, PORT, USER, PASSWORD, SERVER_PROC, TIMEOUT
- USAGE = "%s -u USERNAME -p PASSWORD [-H] [-P] [-b] [-n] [-s] [-k]" % (
- __file__)
+ USAGE = "%s -u USERNAME -p PASSWORD [-H] [-P] [-b] [-n] [-s] [-k]" %
__file__
parser = optparse.OptionParser(usage=USAGE,

epilog=__doc__[__doc__.find('Example'):],
formatter=OptFormatter())
parser.add_option('-u', '--user', dest='user', help='username')
parser.add_option('-p', '--pass', dest='password', help='password')
- parser.add_option('-H', '--host', dest='host', default=HOST,
- help='hostname')
+ parser.add_option('-H', '--host', dest='host', default=HOST,
help='hostname')
parser.add_option('-P', '--port', dest='port', default=PORT,
help='port',
type=int)
- parser.add_option('-b', '--benchmark', dest='benchmark',
- default='transfer',
+ parser.add_option('-b', '--benchmark', dest='benchmark',
default='transfer',
help="benchmark type
('transfer', 'concurrence', 'all')")
- parser.add_option('-n', '--clients', dest='clients', default=200,
- type="int",
- help="number of concurrent clients used by "
- "'concurrence' benchmark")
+ parser.add_option('-n', '--clients', dest='clients', default=200,
type="int",
+ help="number of concurrent clients used
by 'concurrence' "
+ "benchmark")
parser.add_option('-s', '--filesize', dest='filesize', default="10M",
help="file size used by 'concurrence' benchmark "
"(e.g. '10M')")
parser.add_option('-k', '--pid', dest='pid', default=None, type="int",
- help="the PID of the server process to bench memory "
- "usage")
+ help="the PID of the server process to bench memory
usage")
parser.add_option('-t', '--timeout', dest='timeout',
default=TIMEOUT, type="int", help="the socket
timeout")

+
options, args = parser.parse_args()
if not options.user or not options.password:
sys.exit(USAGE)
@@ -501,8 +481,8 @@

def bench_multi_retr(clients):
stor(clients[0], FILE_SIZE)
- with timethis("%s concurrent clients (RETR %s file)" % (
- howmany, bytes2human(FILE_SIZE))):
+ with timethis("%s concurrent clients (RETR %s file)" \
+ % (howmany, bytes2human(FILE_SIZE))):
for ftp in clients:
ftp.voidcmd('TYPE I')
conn = ftp.transfercmd("RETR " + TESTFN)
@@ -513,8 +493,8 @@
ftp.voidresp()

def bench_multi_stor(clients):
- with timethis("%s concurrent clients (STOR %s file)" % (
- howmany, bytes2human(FILE_SIZE))):
+ with timethis("%s concurrent clients (STOR %s file)" \
+ % (howmany, bytes2human(FILE_SIZE))):
for ftp in clients:
ftp.voidcmd('TYPE I')
conn = ftp.transfercmd("STOR " + TESTFN)
@@ -549,8 +529,8 @@
# start benchmark
if SERVER_PROC is not None:
register_memory()
- print_("(starting with %s of memory being used)" % (
- hilite(server_memory.pop())))
+ print_("(starting with %s of memory being used)" \
+ % hilite(server_memory.pop()))
if options.benchmark == 'transfer':
bench_stor()
bench_retr()
=======================================
--- /trunk/test/test_contrib.py Mon Dec 30 11:33:10 2013 UTC
+++ /tags/release-1.3.0/test/test_contrib.py Thu Feb 6 02:27:59 2014 UTC
@@ -66,10 +66,8 @@
from test_ftpd import *


-FTPS_SUPPORT = (hasattr(ftplib, 'FTP_TLS') and
- hasattr(handlers, 'TLS_FTPHandler'))
-CERTFILE = os.path.abspath(os.path.join(os.path.dirname(__file__),
- 'keycert.pem'))
+FTPS_SUPPORT = hasattr(ftplib, 'FTP_TLS') and
hasattr(handlers, 'TLS_FTPHandler')
+CERTFILE =
os.path.abspath(os.path.join(os.path.dirname(__file__), 'keycert.pem'))
MPROCESS_SUPPORT = hasattr(servers, 'MultiprocessFTPServer')


@@ -116,80 +114,37 @@
class TLSTestMixin:
pass

-class TestFtpAuthenticationTLSMixin(TLSTestMixin, TestFtpAuthentication):
- pass
-
-class TestTFtpDummyCmdsTLSMixin(TLSTestMixin, TestFtpDummyCmds):
- pass
-
-class TestFtpCmdsSemanticTLSMixin(TLSTestMixin, TestFtpCmdsSemantic):
- pass
-
-class TestFtpFsOperationsTLSMixin(TLSTestMixin, TestFtpFsOperations):
- pass
-
-class TestFtpStoreDataTLSMixin(TLSTestMixin, TestFtpStoreData):
- pass
-
-class TestFtpRetrieveDataTLSMixin(TLSTestMixin, TestFtpRetrieveData):
- pass
-
-class TestFtpListingCmdsTLSMixin(TLSTestMixin, TestFtpListingCmds):
- pass
+class TestFtpAuthenticationTLSMixin(TLSTestMixin, TestFtpAuthentication):
pass
+class TestTFtpDummyCmdsTLSMixin(TLSTestMixin, TestFtpDummyCmds): pass
+class TestFtpCmdsSemanticTLSMixin(TLSTestMixin, TestFtpCmdsSemantic): pass
+class TestFtpFsOperationsTLSMixin(TLSTestMixin, TestFtpFsOperations): pass
+class TestFtpStoreDataTLSMixin(TLSTestMixin, TestFtpStoreData): pass
+class TestFtpRetrieveDataTLSMixin(TLSTestMixin, TestFtpRetrieveData): pass
+class TestFtpListingCmdsTLSMixin(TLSTestMixin, TestFtpListingCmds): pass

class TestFtpAbortTLSMixin(TLSTestMixin, TestFtpAbort):
- def test_oob_abor(self):
- pass
+ def test_oob_abor(self): pass

class TestTimeoutsTLSMixin(TLSTestMixin, TestTimeouts):
- def test_data_timeout_not_reached(self):
- pass
+ def test_data_timeout_not_reached(self): pass

-class TestConfigurableOptionsTLSMixin(TLSTestMixin,
TestConfigurableOptions):
- pass
-
+class TestConfigurableOptionsTLSMixin(TLSTestMixin,
TestConfigurableOptions): pass
class TestCallbacksTLSMixin(TLSTestMixin, TestCallbacks):
- def test_on_file_received(self):
- pass
-
- def test_on_file_sent(self):
- pass
-
- def test_on_incomplete_file_received(self):
- pass
-
- def test_on_incomplete_file_sent(self):
- pass
-
- def test_on_connect(self):
- pass
-
- def test_on_disconnect(self):
- pass
-
- def test_on_login(self):
- pass
-
- def test_on_login_failed(self):
- pass
-
- def test_on_logout_quit(self):
- pass
-
- def test_on_logout_rein(self):
- pass
-
- def test_on_logout_user_issued_twice(self):
- pass
-
-class TestIPv4EnvironmentTLSMixin(TLSTestMixin, TestIPv4Environment):
- pass
-
-class TestIPv6EnvironmentTLSMixin(TLSTestMixin, TestIPv6Environment):
- pass
+ def test_on_file_received(self): pass
+ def test_on_file_sent(self): pass
+ def test_on_incomplete_file_received(self): pass
+ def test_on_incomplete_file_sent(self): pass
+ def test_on_connect(self): pass
+ def test_on_disconnect(self): pass
+ def test_on_login(self): pass
+ def test_on_login_failed(self): pass
+ def test_on_logout_quit(self): pass
+ def test_on_logout_rein(self): pass
+ def test_on_logout_user_issued_twice(self): pass

-class TestCornerCasesTLSMixin(TLSTestMixin, TestCornerCases):
- pass
+class TestIPv4EnvironmentTLSMixin(TLSTestMixin, TestIPv4Environment): pass
+class TestIPv6EnvironmentTLSMixin(TLSTestMixin, TestIPv6Environment): pass
+class TestCornerCasesTLSMixin(TLSTestMixin, TestCornerCases): pass


# =====================================================================
@@ -202,50 +157,22 @@
class ThreadFTPTestMixin:
server_class = TFTPd

-class TestFtpAuthenticationThreadMixin(ThreadFTPTestMixin,
- TestFtpAuthentication):
- pass
-
-class TestTFtpDummyCmdsThreadMixin(ThreadFTPTestMixin, TestFtpDummyCmds):
- pass
-
-class TestFtpCmdsSemanticThreadMixin(ThreadFTPTestMixin,
TestFtpCmdsSemantic):
- pass
-
-class TestFtpFsOperationsThreadMixin(ThreadFTPTestMixin,
TestFtpFsOperations):
- pass
-
-class TestFtpStoreDataThreadMixin(ThreadFTPTestMixin, TestFtpStoreData):
- pass
-
-class TestFtpRetrieveDataThreadMixin(ThreadFTPTestMixin,
TestFtpRetrieveData):
- pass
-
-class TestFtpListingCmdsThreadMixin(ThreadFTPTestMixin,
TestFtpListingCmds):
- pass
-
-class TestFtpAbortThreadMixin(ThreadFTPTestMixin, TestFtpAbort):
- pass
-
+class TestFtpAuthenticationThreadMixin(ThreadFTPTestMixin,
TestFtpAuthentication): pass
+class TestTFtpDummyCmdsThreadMixin(ThreadFTPTestMixin, TestFtpDummyCmds):
pass
+class TestFtpCmdsSemanticThreadMixin(ThreadFTPTestMixin,
TestFtpCmdsSemantic): pass
+class TestFtpFsOperationsThreadMixin(ThreadFTPTestMixin,
TestFtpFsOperations): pass
+class TestFtpStoreDataThreadMixin(ThreadFTPTestMixin, TestFtpStoreData):
pass
+class TestFtpRetrieveDataThreadMixin(ThreadFTPTestMixin,
TestFtpRetrieveData): pass
+class TestFtpListingCmdsThreadMixin(ThreadFTPTestMixin,
TestFtpListingCmds): pass
+class TestFtpAbortThreadMixin(ThreadFTPTestMixin, TestFtpAbort): pass
#class TestTimeoutsThreadMixin(ThreadFTPTestMixin, TestTimeouts):
# def test_data_timeout_not_reached(self): pass
-#class TestConfOptsThreadMixin(ThreadFTPTestMixin,
TestConfigurableOptions):
-# pass
-
-class TestCallbacksThreadMixin(ThreadFTPTestMixin, TestCallbacks):
- pass
-
-class TestIPv4EnvironmentThreadMixin(ThreadFTPTestMixin,
TestIPv4Environment):
- pass
-
-class TestIPv6EnvironmentThreadMixin(ThreadFTPTestMixin,
TestIPv6Environment):
- pass
-
-class TestCornerCasesThreadMixin(ThreadFTPTestMixin, TestCornerCases):
- pass
-
-class TestFTPServerThreadMixin(ThreadFTPTestMixin, TestFTPServer):
- pass
+#class TestConfigurableOptionsThreadMixin(ThreadFTPTestMixin,
TestConfigurableOptions): pass
+class TestCallbacksThreadMixin(ThreadFTPTestMixin, TestCallbacks): pass
+class TestIPv4EnvironmentThreadMixin(ThreadFTPTestMixin,
TestIPv4Environment): pass
+class TestIPv6EnvironmentThreadMixin(ThreadFTPTestMixin,
TestIPv6Environment): pass
+class TestCornerCasesThreadMixin(ThreadFTPTestMixin, TestCornerCases): pass
+class TestFTPServerThreadMixin(ThreadFTPTestMixin, TestFTPServer): pass


# =====================================================================
@@ -262,50 +189,27 @@
class MProcFTPTestMixin:
pass

-class TestFtpAuthenticationMProcMixin(MProcFTPTestMixin,
- TestFtpAuthentication):
- pass
-
-class TestTFtpDummyCmdsMProcMixin(MProcFTPTestMixin, TestFtpDummyCmds):
- pass
-
-class TestFtpCmdsSemanticMProcMixin(MProcFTPTestMixin,
TestFtpCmdsSemantic):
- pass
+class TestFtpAuthenticationMProcMixin(MProcFTPTestMixin,
TestFtpAuthentication): pass
+class TestTFtpDummyCmdsMProcMixin(MProcFTPTestMixin, TestFtpDummyCmds):
pass
+class TestFtpCmdsSemanticMProcMixin(MProcFTPTestMixin,
TestFtpCmdsSemantic): pass

class TestFtpFsOperationsMProcMixin(MProcFTPTestMixin,
TestFtpFsOperations):
def test_unforeseen_mdtm_event(self):
pass

-class TestFtpStoreDataMProcMixin(MProcFTPTestMixin, TestFtpStoreData):
- pass
-
-class TestFtpRetrieveDataMProcMixin(MProcFTPTestMixin,
TestFtpRetrieveData):
- pass
-
-class TestFtpListingCmdsMProcMixin(MProcFTPTestMixin, TestFtpListingCmds):
- pass
-
-class TestFtpAbortMProcMixin(MProcFTPTestMixin, TestFtpAbort):
- pass
-
+class TestFtpStoreDataMProcMixin(MProcFTPTestMixin, TestFtpStoreData): pass
+class TestFtpRetrieveDataMProcMixin(MProcFTPTestMixin,
TestFtpRetrieveData): pass
+class TestFtpListingCmdsMProcMixin(MProcFTPTestMixin, TestFtpListingCmds):
pass
+class TestFtpAbortMProcMixin(MProcFTPTestMixin, TestFtpAbort): pass
#class TestTimeoutsMProcMixin(MProcFTPTestMixin, TestTimeouts):
# def test_data_timeout_not_reached(self): pass
-#class TestConfiOptsMProcMixin(MProcFTPTestMixin, TestConfigurableOptions):
-# pass
+#class TestConfigurableOptionsMProcMixin(MProcFTPTestMixin,
TestConfigurableOptions): pass
#class TestCallbacksMProcMixin(MProcFTPTestMixin, TestCallbacks): pass
+class TestIPv4EnvironmentMProcMixin(MProcFTPTestMixin,
TestIPv4Environment): pass
+class TestIPv6EnvironmentMProcMixin(MProcFTPTestMixin,
TestIPv6Environment): pass
+class TestCornerCasesMProcMixin(MProcFTPTestMixin, TestCornerCases): pass
+class TestFTPServerMProcMixin(MProcFTPTestMixin, TestFTPServer): pass

-class TestIPv4EnvironmentMProcMixin(MProcFTPTestMixin,
TestIPv4Environment):
- pass
-
-class TestIPv6EnvironmentMProcMixin(MProcFTPTestMixin,
TestIPv6Environment):
- pass
-
-class TestCornerCasesMProcMixin(MProcFTPTestMixin, TestCornerCases):
- pass
-
-class TestFTPServerMProcMixin(MProcFTPTestMixin, TestFTPServer):
- pass
-

# =====================================================================
# dedicated FTPs tests
@@ -338,7 +242,7 @@
return
raise self.failureException("%s != %s" % (str(why), msg))
else:
- if hasattr(excClass, '__name__'):
+ if hasattr(excClass,'__name__'):
excName = excClass.__name__
else:
excName = str(excClass)
@@ -519,10 +423,8 @@
return
raise self.failureException("%s != %s" % (str(why), msg))
else:
- if hasattr(excClass, '__name__'):
- excName = excClass.__name__
- else:
- excName = str(excClass)
+ if hasattr(excClass,'__name__'): excName = excClass.__name__
+ else: excName = str(excClass)
raise self.failureException("%s not raised" % excName)

# --- /utils
@@ -556,12 +458,10 @@
auth = self.authorizer_class()
current_user = self.get_current_user()
nonexistent_user = self.get_nonexistent_user()
- self.assertRaises(
- AuthenticationFailed,
- auth.validate_authentication, current_user, 'wrongpasswd',
None)
- self.assertRaises(
- AuthenticationFailed,
- auth.validate_authentication, nonexistent_user, 'bar', None)
+ self.assertRaises(AuthenticationFailed,
+ auth.validate_authentication, current_user, 'wrongpasswd',
None)
+ self.assertRaises(AuthenticationFailed,
+ auth.validate_authentication, nonexistent_user, 'bar',
None)

def test_impersonate_user(self):
auth = self.authorizer_class()
@@ -569,16 +469,13 @@
try:
if self.authorizer_class.__name__ == 'UnixAuthorizer':
auth.impersonate_user(self.get_current_user(), '')
- self.assertRaises(
- AuthorizerError,
- auth.impersonate_user, nonexistent_user, 'pwd')
+ self.assertRaises(AuthorizerError,
+ auth.impersonate_user,
nonexistent_user, 'pwd')
else:
- self.assertRaises(
- Win32ExtError,
- auth.impersonate_user, nonexistent_user, 'pwd')
- self.assertRaises(
- Win32ExtError,
- auth.impersonate_user, self.get_current_user(), '')
+ self.assertRaises(Win32ExtError,
+ auth.impersonate_user, nonexistent_user, 'pwd')
+ self.assertRaises(Win32ExtError,
+ auth.impersonate_user,
self.get_current_user(), '')
finally:
auth.terminate_impersonation('')

@@ -604,27 +501,20 @@

def test_error_options(self):
wrong_user = self.get_nonexistent_user()
- self.assertRaisesWithMsg(
- AuthorizerError,
- "rejected_users and allowed_users options are mutually
exclusive",
- self.authorizer_class, allowed_users=['foo'],
- rejected_users=['bar'])
- self.assertRaisesWithMsg(
- AuthorizerError,
- 'invalid username "anonymous"',
- self.authorizer_class, allowed_users=['anonymous'])
- self.assertRaisesWithMsg(
- AuthorizerError,
- 'invalid username "anonymous"',
- self.authorizer_class, rejected_users=['anonymous'])
- self.assertRaisesWithMsg(
- AuthorizerError,
- 'unknown user %s' % wrong_user,
- self.authorizer_class, allowed_users=[wrong_user])
+ self.assertRaisesWithMsg(AuthorizerError,
+ "rejected_users and allowed_users options are mutually
exclusive",
+ self.authorizer_class, allowed_users=['foo'],
rejected_users=['bar'])
+ self.assertRaisesWithMsg(AuthorizerError,
+ 'invalid username "anonymous"',
+ self.authorizer_class,
allowed_users=['anonymous'])
+ self.assertRaisesWithMsg(AuthorizerError,
+ 'invalid username "anonymous"',
+ self.authorizer_class,
rejected_users=['anonymous'])
self.assertRaisesWithMsg(AuthorizerError,
- 'unknown user %s' % wrong_user,
- self.authorizer_class,
- rejected_users=[wrong_user])
+ 'unknown user %s' % wrong_user,
+ self.authorizer_class,
allowed_users=[wrong_user])
+ self.assertRaisesWithMsg(AuthorizerError, 'unknown user %s' %
wrong_user,
+ self.authorizer_class,
rejected_users=[wrong_user])

def test_override_user_password(self):
auth = self.authorizer_class()
@@ -634,8 +524,7 @@

self.assertRaises(AuthenticationFailed(auth.validate_authentication,
user, 'bar', None))
# make sure other settings keep using default values
- self.assertEqual(auth.get_home_dir(user),
- self.get_current_user_homedir())
+ self.assertEqual(auth.get_home_dir(user),
self.get_current_user_homedir())
self.assertEqual(auth.get_perms(user), "elradfmw")
self.assertEqual(auth.get_msg_login(user), "Login successful.")
self.assertEqual(auth.get_msg_quit(user), "Goodbye.")
@@ -647,8 +536,7 @@
auth.override_user(user, homedir=dir)
self.assertEqual(auth.get_home_dir(user), dir)
# make sure other settings keep using default values
- # self.assertEqual(auth.get_home_dir(user),
- # self.get_current_user_homedir())
+ #self.assertEqual(auth.get_home_dir(user),
self.get_current_user_homedir())
self.assertEqual(auth.get_perms(user), "elradfmw")
self.assertEqual(auth.get_msg_login(user), "Login successful.")
self.assertEqual(auth.get_msg_quit(user), "Goodbye.")
@@ -659,8 +547,7 @@
auth.override_user(user, perm="elr")
self.assertEqual(auth.get_perms(user), "elr")
# make sure other settings keep using default values
- self.assertEqual(auth.get_home_dir(user),
- self.get_current_user_homedir())
+ self.assertEqual(auth.get_home_dir(user),
self.get_current_user_homedir())
#self.assertEqual(auth.get_perms(user), "elradfmw")
self.assertEqual(auth.get_msg_login(user), "Login successful.")
self.assertEqual(auth.get_msg_quit(user), "Goodbye.")
@@ -672,8 +559,7 @@
self.assertEqual(auth.get_msg_login(user), "foo")
self.assertEqual(auth.get_msg_quit(user), "bar")
# make sure other settings keep using default values
- self.assertEqual(auth.get_home_dir(user),
- self.get_current_user_homedir())
+ self.assertEqual(auth.get_home_dir(user),
self.get_current_user_homedir())
self.assertEqual(auth.get_perms(user), "elradfmw")
#self.assertEqual(auth.get_msg_login(user), "Login successful.")
#self.assertEqual(auth.get_msg_quit(user), "Goodbye.")
@@ -689,14 +575,12 @@
another_user = x
break
nonexistent_user = self.get_nonexistent_user()
- self.assertRaisesWithMsg(
- AuthorizerError,
- "at least one keyword argument must be specified",
- auth.override_user, this_user)
+ self.assertRaisesWithMsg(AuthorizerError,
+ "at least one keyword argument must be
specified",
+ auth.override_user, this_user)
self.assertRaisesWithMsg(AuthorizerError,
'no such user %s' % nonexistent_user,
- auth.override_user, nonexistent_user,
- perm='r')
+ auth.override_user, nonexistent_user,
perm='r')
if self.authorizer_class.__name__ == 'UnixAuthorizer':
auth = self.authorizer_class(allowed_users=[this_user],
require_valid_shell=False)
@@ -717,8 +601,7 @@
auth.override_user, this_user, perm='r')
self.assertRaisesWithMsg(AuthorizerError,
"can't assign password to anonymous user",
- auth.override_user, "anonymous",
- password='foo')
+ auth.override_user, "anonymous",
password='foo')


# =====================================================================
@@ -731,8 +614,8 @@
authorizer_class = getattr(authorizers, "UnixAuthorizer", None)

def test_get_perms_anonymous(self):
- auth = authorizers.UnixAuthorizer(
- global_perm='elr', anonymous_user=self.get_current_user())
+ auth = authorizers.UnixAuthorizer(global_perm='elr',
+
anonymous_user=self.get_current_user())
self.assertTrue('e' in auth.get_perms('anonymous'))
self.assertFalse('w' in auth.get_perms('anonymous'))
warnings.filterwarnings("ignore")
@@ -741,8 +624,8 @@
self.assertTrue('w' in auth.get_perms('anonymous'))

def test_has_perm_anonymous(self):
- auth = authorizers.UnixAuthorizer(
- global_perm='elr', anonymous_user=self.get_current_user())
+ auth = authorizers.UnixAuthorizer(global_perm='elr',
+
anonymous_user=self.get_current_user())
self.assertTrue(auth.has_perm(self.get_current_user(), 'r'))
self.assertFalse(auth.has_perm(self.get_current_user(), 'w'))
self.assertTrue(auth.has_perm('anonymous', 'e'))
@@ -767,9 +650,8 @@
require_valid_shell=False)
self.assertRaises(AuthenticationFailed,
auth.validate_authentication, 'foo', 'passwd',
None)
- self.assertRaises(
- AuthenticationFailed,
- auth.validate_authentication, current_user, 'passwd', None)
+ self.assertRaises(AuthenticationFailed,
+ auth.validate_authentication, current_user, 'passwd',
None)
auth.validate_authentication('anonymous', 'passwd', None)

def test_require_valid_shell(self):
@@ -785,10 +667,9 @@
self.fail("no user found")

user = get_fake_shell_user()
- self.assertRaisesWithMsg(
- AuthorizerError,
- "user %s has not a valid shell" % user,
- authorizers.UnixAuthorizer, allowed_users=[user])
+ self.assertRaisesWithMsg(AuthorizerError,
+ "user %s has not a valid shell" % user,
+ authorizers.UnixAuthorizer,
allowed_users=[user])
# commented as it first fails for invalid home
#self.assertRaisesWithMsg(ValueError,
# "user %s has not a valid shell" % user,
@@ -824,8 +705,7 @@
def test_wrong_anonymous_credentials(self):
user = self.get_current_user()
self.assertRaises(Win32ExtError, self.authorizer_class,
- anonymous_user=user,
- anonymous_password='$|1wrongpasswd')
+ anonymous_user=user, anonymous_password='$|
1wrongpasswd')


# =====================================================================
@@ -855,21 +735,20 @@

# FTPS tests
if FTPS_SUPPORT:
- ftps_tests = [
- TestFTPS,
- TestFtpAuthenticationTLSMixin,
- TestTFtpDummyCmdsTLSMixin,
- TestFtpCmdsSemanticTLSMixin,
- TestFtpFsOperationsTLSMixin,
- TestFtpStoreDataTLSMixin,
- TestFtpRetrieveDataTLSMixin,
- TestFtpListingCmdsTLSMixin,
- TestFtpAbortTLSMixin,
- TestTimeoutsTLSMixin,
- TestConfigurableOptionsTLSMixin,
- TestCallbacksTLSMixin,
- TestCornerCasesTLSMixin,
- ]
+ ftps_tests = [TestFTPS,
+ TestFtpAuthenticationTLSMixin,
+ TestTFtpDummyCmdsTLSMixin,
+ TestFtpCmdsSemanticTLSMixin,
+ TestFtpFsOperationsTLSMixin,
+ TestFtpStoreDataTLSMixin,
+ TestFtpRetrieveDataTLSMixin,
+ TestFtpListingCmdsTLSMixin,
+ TestFtpAbortTLSMixin,
+ TestTimeoutsTLSMixin,
+ TestConfigurableOptionsTLSMixin,
+ TestCallbacksTLSMixin,
+ TestCornerCasesTLSMixin,
+ ]
if SUPPORTS_IPV4:
ftps_tests.append(TestIPv4EnvironmentTLSMixin)
if SUPPORTS_IPV6:
@@ -930,7 +809,7 @@
try:
authorizers.UnixAuthorizer()
except AuthorizerError: # not root
- warn("UnixAuthorizer tests skipped (root privileges are "
+ warn("UnixAuthorizer tests skipped (root privileges are " \
"required)")
else:
tests.append(TestUnixAuthorizer)
@@ -950,7 +829,7 @@
try:
import win32api
except ImportError:
- warn("WindowsAuthorizer tests skipped (pywin32 extension "
+ warn("WindowsAuthorizer tests skipped (pywin32 extension "
\
"is required)")
else:
warn("WindowsAuthorizer tests skipped")
=======================================
--- /trunk/test/test_ftpd.py Mon Dec 30 11:46:07 2013 UTC
+++ /tags/release-1.3.0/test/test_ftpd.py Thu Feb 6 02:27:59 2014 UTC
@@ -30,23 +30,23 @@
#
# ======================================================================

-import atexit
-import errno
-import ftplib
-import logging
+import threading
+import unittest
+import socket
import os
-import random
+import shutil
+import time
import re
-import select
-import shutil
-import socket
-import stat
-import sys
import tempfile
-import threading
-import time
-import unittest
+import ftplib
+import random
import warnings
+import sys
+import errno
+import atexit
+import stat
+import logging
+import select
try:
from StringIO import StringIO as BytesIO
except ImportError:
@@ -55,22 +55,19 @@
import ssl
except ImportError:
ssl = None
+try:
+ import sendfile
+except ImportError:
+ sendfile = None

-sendfile = None
-if os.name == 'posix':
- try:
- import sendfile
- except ImportError:
- pass
-
-from pyftpdlib._compat import PY3, u, b, getcwdu, callable
-from pyftpdlib.authorizers import DummyAuthorizer, AuthenticationFailed
-from pyftpdlib.filesystems import AbstractedFS
+import pyftpdlib.__main__
+from pyftpdlib.ioloop import IOLoop
from pyftpdlib.handlers import (FTPHandler, DTPHandler,
ThrottledDTPHandler,
SUPPORTS_HYBRID_IPV6)
-from pyftpdlib.ioloop import IOLoop
from pyftpdlib.servers import FTPServer
-import pyftpdlib.__main__
+from pyftpdlib.filesystems import AbstractedFS
+from pyftpdlib.authorizers import DummyAuthorizer, AuthenticationFailed
+from pyftpdlib._compat import PY3, u, b, getcwdu, callable


# Attempt to use IP rather than hostname (test suite will run a lot faster)
@@ -105,7 +102,6 @@
SUPPORTS_IPV6 = socket.has_ipv6 and try_address('::1',
family=socket.AF_INET6)
SUPPORTS_SENDFILE = hasattr(os, 'sendfile') or sendfile is not None

-
def safe_remove(*files):
"Convenience function for removing temporary test files"
for file in files:
@@ -116,7 +112,6 @@
if err.errno != errno.ENOENT:
raise

-
def safe_rmdir(dir):
"Convenience function for removing temporary test directories"
try:
@@ -126,7 +121,6 @@
if err.errno != errno.ENOENT:
raise

-
def safe_mkdir(dir):
"Convenience function for creating a directory"
try:
@@ -136,7 +130,6 @@
if err.errno != errno.EEXIST:
raise

-
def touch(name):
"""Create a file and return its name."""
f = open(name, 'w')
@@ -145,7 +138,6 @@
finally:
f.close()

-
def remove_test_files():
"""Remove files and directores created during tests."""
for name in os.listdir(u('.')):
@@ -155,16 +147,13 @@
else:
os.remove(name)

-
def warn(msg):
"""Add warning message to be executed on exit."""
atexit.register(warnings.warn, str(msg) + " - tests have been skipped",
RuntimeWarning)

-
def disable_log_warning(inst):
"""Temporarily set FTP server's logging level to ERROR."""
-
def wrapper(self, *args, **kwargs):
logger = logging.getLogger('pyftpdlib')
level = logger.getEffectiveLevel()
@@ -175,7 +164,6 @@
logger.setLevel(level)
return wrapper

-
def skip_other_tests():
"""Decorator which skips all tests except the decorated one.
http://mail.python.org/pipermail/python-ideas/2010-August/007992.html
@@ -183,7 +171,6 @@
from unittest import TextTestRunner as _TextTestRunner

class CustomTestRunner(_TextTestRunner):
-
def run(self, test):
if test._tests:
for t1 in test._tests:
@@ -205,7 +192,6 @@

return outer

-
def cleanup():
"""Cleanup function executed on interpreter exit."""
remove_test_files()
@@ -327,7 +313,7 @@
expected_regexp = re.compile(expected_regexp)
if not expected_regexp.search(msg):
raise self.failureException('"%s" does not
match "%s"' %
- (expected_regexp.pattern,
msg))
+ (expected_regexp.pattern, msg))
else:
if hasattr(expected_exception, '__name__'):
exc_name = expected_exception.__name__
@@ -409,8 +395,7 @@
ae(fs.ftp2fs(u('a/b/..')), join(root, u('sub/a')))
ae(fs.ftp2fs(u('a/b/../..')), join(root, u('sub')))
ae(fs.ftp2fs(u('a/b/../../..')), root)
- # UNC paths must be collapsed
- ae(fs.ftp2fs(u('//a')), join(root, u('a')))
+ ae(fs.ftp2fs(u('//a')), join(root, u('a'))) # UNC paths must
be collapsed

if os.sep == '\\':
goforit(u(r'C:\dir'))
@@ -425,6 +410,54 @@
# os.sep == ':'? Don't know... let's try it anyway
goforit(getcwdu())

+ def test_ftp2fs(self):
+ # Tests for ftp2fs method.
+ ae = self.assertEqual
+ fs = AbstractedFS(u('/'), None)
+ join = lambda x, y: os.path.join(x, y.replace('/', os.sep))
+
+ def goforit(root):
+ fs._root = root
+ fs._cwd = u('/')
+ ae(fs.ftp2fs(u('')), root)
+ ae(fs.ftp2fs(u('/')), root)
+ ae(fs.ftp2fs(u('.')), root)
+ ae(fs.ftp2fs(u('..')), root)
+ ae(fs.ftp2fs(u('a')), join(root, u('a')))
+ ae(fs.ftp2fs(u('/a')), join(root, u('a')))
+ ae(fs.ftp2fs(u('/a/')), join(root, u('a')))
+ ae(fs.ftp2fs(u('a/..')), root)
+ ae(fs.ftp2fs(u('a/b')), join(root, r'a/b'))
+ ae(fs.ftp2fs(u('/a/b')), join(root, r'a/b'))
+ ae(fs.ftp2fs(u('/a/b/..')), join(root, u('a')))
+ ae(fs.ftp2fs(u('/a/b/../..')), root)
+ fs._cwd = u('/sub')
+ ae(fs.ftp2fs(u('')), join(root, u('sub')))
+ ae(fs.ftp2fs(u('/')), root)
+ ae(fs.ftp2fs(u('.')), join(root, u('sub')))
+ ae(fs.ftp2fs(u('..')), root)
+ ae(fs.ftp2fs(u('a')), join(root, u('sub/a')))
+ ae(fs.ftp2fs(u('a/')), join(root, u('sub/a')))
+ ae(fs.ftp2fs(u('a/..')), join(root, u('sub')))
+ ae(fs.ftp2fs(u('a/b')), join(root, 'sub/a/b'))
+ ae(fs.ftp2fs(u('a/b/..')), join(root, u('sub/a')))
+ ae(fs.ftp2fs(u('a/b/../..')), join(root, u('sub')))
+ ae(fs.ftp2fs(u('a/b/../../..')), root)
+ ae(fs.ftp2fs(u('//a')), join(root, u('a'))) # UNC paths must
be collapsed
+
+ if os.sep == '\\':
+ goforit(u(r'C:\dir'))
+ goforit(u('C:\\'))
+ # on DOS-derived filesystems (e.g. Windows) this is the same
+ # as specifying the current drive directory (e.g. 'C:\\')
+ goforit(u('\\'))
+ elif os.sep == '/':
+ goforit(u('/home/user'))
+ goforit(u('/'))
+ else:
+ # os.sep == ':'? Don't know... let's try it anyway
+ goforit(getcwdu())
+
def test_fs2ftp(self):
# Tests for fs2ftp method.
ae = self.assertEqual
@@ -436,8 +469,7 @@
ae(fs.fs2ftp(root), u('/'))
ae(fs.fs2ftp(join(root, u('/'))), u('/'))
ae(fs.fs2ftp(join(root, u('.'))), u('/'))
- # can't escape from root
- ae(fs.fs2ftp(join(root, u('..'))), u('/'))
+ ae(fs.fs2ftp(join(root, u('..'))), u('/')) # can't escape
from root
ae(fs.fs2ftp(join(root, u('a'))), u('/a'))
ae(fs.fs2ftp(join(root, u('a/'))), u('/a'))
ae(fs.fs2ftp(join(root, u('a/..'))), u('/'))
@@ -519,8 +551,7 @@
# temporarily change warnings to exceptions for the purposes of testing
def setUp(self):
self.tempdir = tempfile.mkdtemp(dir=HOME)
- self.subtempdir = tempfile.mkdtemp(
- dir=os.path.join(HOME, self.tempdir))
+ self.subtempdir = tempfile.mkdtemp(dir=os.path.join(HOME,
self.tempdir))
self.tempfile = touch(os.path.join(self.tempdir, TESTFN))
self.subtempfile = touch(os.path.join(self.subtempdir, TESTFN))
warnings.filterwarnings("error")
@@ -575,17 +606,15 @@
auth.add_anonymous, HOME, perm='?')
# expect warning on write permissions assigned to anonymous user
for x in "adfmw":
- self.assertRaisesRegex(
- RuntimeWarning,
- "write permissions assigned to anonymous user.",
- auth.add_anonymous, HOME, perm=x)
+ self.assertRaisesRegex(RuntimeWarning,
+ "write permissions assigned to anonymous
user.",
+ auth.add_anonymous, HOME, perm=x)

def test_override_perm_interface(self):
auth = DummyAuthorizer()
auth.add_user(USER, PASSWD, HOME, perm='elr')
# raise exc if user does not exists
- self.assertRaises(KeyError, auth.override_perm, USER + 'w',
- HOME, 'elr')
+ self.assertRaises(KeyError, auth.override_perm, USER+'w',
HOME, 'elr')
# raise exc if path does not exist or it's not a directory
self.assertRaisesRegex(ValueError,
'no such directory',
@@ -600,14 +629,13 @@
# expect warning on write permissions assigned to anonymous user
auth.add_anonymous(HOME)
for p in "adfmw":
- self.assertRaisesRegex(
- RuntimeWarning,
- "write permissions assigned to anonymous user.",
- auth.override_perm, 'anonymous', HOME, p)
+ self.assertRaisesRegex(RuntimeWarning,
+ "write permissions assigned to anonymous
user.",
+ auth.override_perm, 'anonymous', HOME, p)
# raise on attempt to override home directory permissions
self.assertRaisesRegex(ValueError,
- "can't override home directory permissions",
- auth.override_perm, USER, HOME, perm='w')
+ "can't override home directory
permissions",
+ auth.override_perm, USER, HOME, perm='w')
# raise on attempt to override a path escaping home directory
if os.path.dirname(HOME) != HOME:
self.assertRaisesRegex(ValueError,
@@ -631,13 +659,11 @@

self.assertEqual(auth.has_perm(USER, 'w', HOME + '@'), False)
self.assertEqual(auth.has_perm(USER, 'w', self.tempdir + '@'),
False)
- path = os.path.join(self.tempdir + '@',
- os.path.basename(self.tempfile))
+ path = os.path.join(self.tempdir + '@',
os.path.basename(self.tempfile))
self.assertEqual(auth.has_perm(USER, 'w', path), False)
# test case-sensitiveness
if (os.name in ('nt', 'ce')) or (sys.platform == 'cygwin'):
- self.assertEqual(auth.has_perm(USER, 'w',
- self.tempdir.upper()), True)
+ self.assertEqual(auth.has_perm(USER, 'w',
self.tempdir.upper()), True)

def test_override_perm_not_recursive_paths(self):
auth = DummyAuthorizer()
@@ -652,13 +678,11 @@

self.assertEqual(auth.has_perm(USER, 'w', HOME + '@'), False)
self.assertEqual(auth.has_perm(USER, 'w', self.tempdir + '@'),
False)
- path = os.path.join(self.tempdir + '@',
- os.path.basename(self.tempfile))
+ path = os.path.join(self.tempdir + '@',
os.path.basename(self.tempfile))
self.assertEqual(auth.has_perm(USER, 'w', path), False)
# test case-sensitiveness
if (os.name in ('nt', 'ce')) or (sys.platform == 'cygwin'):
- self.assertEqual(auth.has_perm(USER, 'w',
self.tempdir.upper()),
- True)
+ self.assertEqual(auth.has_perm(USER, 'w',
self.tempdir.upper()), True)


class TestCallLater(TestCase):
@@ -725,8 +749,7 @@

def test_errback(self):
l = []
- self.ioloop.call_later(
- 0.0, lambda: 1 // 0, _errback=lambda: l.append(True))
+ self.ioloop.call_later(0.0, lambda: 1//0, _errback=lambda:
l.append(True))
self.scheduler()
self.assertEqual(l, [True])

@@ -805,14 +828,11 @@

def test_errback(self):
l = []
- self.ioloop.call_every(
- 0.0, lambda: 1 // 0, _errback=lambda: l.append(True))
+ self.ioloop.call_every(0.0, lambda: 1//0, _errback=lambda:
l.append(True))
self.scheduler()
self.assertTrue(l)

-
class TestFtpAuthentication(TestCase):
-
"test: USER, PASS, REIN."
server_class = FTPd
client_class = ftplib.FTP
@@ -929,7 +949,7 @@
self.client.login(user=USER, passwd=PASSWD)
self.client.sendcmd('pwd')
self.dummyfile.seek(0)
- self.assertEqual(hash(data), hash(self.dummyfile.read()))
+ self.assertEqual(hash(data), hash (self.dummyfile.read()))
conn.close()

def test_user(self):
@@ -981,7 +1001,7 @@
self.client.sendcmd('pass ' + PASSWD)
self.client.sendcmd('pwd')
self.dummyfile.seek(0)
- self.assertEqual(hash(data), hash(self.dummyfile.read()))
+ self.assertEqual(hash(data), hash (self.dummyfile.read()))
conn.close()


@@ -1044,16 +1064,13 @@
def test_site(self):
self.assertRaises(ftplib.error_perm, self.client.sendcmd, 'site')
self.assertRaises(ftplib.error_perm,
self.client.sendcmd, 'site ?!?')
- self.assertRaises(ftplib.error_perm, self.client.sendcmd,
- 'site foo bar')
- self.assertRaises(ftplib.error_perm, self.client.sendcmd,
- 'sitefoo bar')
+ self.assertRaises(ftplib.error_perm, self.client.sendcmd, 'site
foo bar')
+ self.assertRaises(ftplib.error_perm, self.client.sendcmd, 'sitefoo
bar')

def test_site_help(self):
self.client.sendcmd('site help')
self.client.sendcmd('site help help')
- self.assertRaises(ftplib.error_perm, self.client.sendcmd,
- 'site help ?!?')
+ self.assertRaises(ftplib.error_perm, self.client.sendcmd, 'site
help ?!?')

def test_rest(self):
# Test error conditions only; resumed data transfers are
@@ -1074,32 +1091,25 @@
self.assertTrue('TVFS' in resp)

def test_opts_feat(self):
- self.assertRaises(
- ftplib.error_perm, self.client.sendcmd, 'opts mlst bad_fact')
- self.assertRaises(
- ftplib.error_perm, self.client.sendcmd, 'opts mlst type ;')
- self.assertRaises(ftplib.error_perm, self.client.sendcmd,
- 'opts not_mlst')
+ self.assertRaises(ftplib.error_perm, self.client.sendcmd, 'opts
mlst bad_fact')
+ self.assertRaises(ftplib.error_perm, self.client.sendcmd, 'opts
mlst type ;')
+ self.assertRaises(ftplib.error_perm, self.client.sendcmd, 'opts
not_mlst')
# utility function which used for extracting the MLST "facts"
# string from the FEAT response
-
def mlst():
resp = self.client.sendcmd('feat')
return re.search(r'^\s*MLST\s+(\S+)$', resp,
re.MULTILINE).group(1)
# we rely on "type", "perm", "size", and "modify" facts which
# are those available on all platforms
self.assertTrue('type*;perm*;size*;modify*;' in mlst())
- self.assertEqual(self.client.sendcmd(
- 'opts mlst type;'), '200 MLST OPTS type;')
- self.assertEqual(self.client.sendcmd(
- 'opts mLSt TypE;'), '200 MLST OPTS type;')
+ self.assertEqual(self.client.sendcmd('opts mlst type;'), '200 MLST
OPTS type;')
+ self.assertEqual(self.client.sendcmd('opts mLSt TypE;'), '200 MLST
OPTS type;')
self.assertTrue('type*;perm;size;modify;' in mlst())

self.assertEqual(self.client.sendcmd('opts mlst'), '200 MLST
OPTS ')
self.assertTrue(not '*' in mlst())

- self.assertEqual(
- self.client.sendcmd('opts mlst fish;cakes;'), '200 MLST OPTS ')
+ self.assertEqual(self.client.sendcmd('opts mlst
fish;cakes;'), '200 MLST OPTS ')
self.assertTrue(not '*' in mlst())
self.assertEqual(self.client.sendcmd('opts mlst fish;cakes;type;'),
'200 MLST OPTS type;')
@@ -1109,9 +1119,9 @@
class TestFtpCmdsSemantic(TestCase):
server_class = FTPd
client_class = ftplib.FTP
- arg_cmds =
['allo', 'appe', 'dele', 'eprt', 'mdtm', 'mode', 'mkd', 'opts',
- 'port', 'rest', 'retr', 'rmd', 'rnfr', 'rnto', 'site', 'size',
- 'stor', 'stru', 'type', 'user', 'xmkd', 'xrmd', 'site
chmod']
+ arg_cmds =
['allo','appe','dele','eprt','mdtm','mode','mkd','opts','port',
+ 'rest','retr','rmd','rnfr','rnto','site','size','stor','stru',
+ 'type','user','xmkd','xrmd','site chmod']

def setUp(self):
self.server = self.server_class()
@@ -1136,9 +1146,8 @@
def test_no_arg_cmds(self):
# Test commands accepting no arguments.
expected = "501 Syntax error: command does not accept arguments."
- narg_cmds = ['abor', 'cdup', 'feat', 'noop', 'pasv', 'pwd', 'quit',
- 'rein', 'syst', 'xcup', 'xpwd']
- for cmd in narg_cmds:
+ for cmd in ('abor','cdup','feat','noop','pasv','pwd','quit','rein',
+ 'syst','xcup','xpwd'):
self.client.putcmd(cmd + ' arg')
resp = self.client.getmultiline()
self.assertEqual(resp, expected)
@@ -1149,9 +1158,8 @@
self.client.sendcmd('rein')
for cmd in self.server.handler.proto_cmds:
cmd = cmd.lower()
- if cmd in
('feat', 'help', 'noop', 'user', 'pass', 'stat', 'syst',
- 'quit', 'site', 'site help', 'pbsz', 'auth', 'prot',
- 'ccc'):
+ if cmd in
('feat','help','noop','user','pass','stat','syst','quit',
+ 'site', 'site help', 'pbsz', 'auth', 'prot', 'ccc'):
continue
if cmd in self.arg_cmds:
cmd = cmd + ' arg'
@@ -1162,7 +1170,7 @@
def test_no_auth_cmds(self):
# Test those commands that do not require client to be
authenticated.
self.client.sendcmd('rein')
- for cmd in ('feat', 'help', 'noop', 'stat', 'syst', 'site help'):
+ for cmd in ('feat','help','noop','stat','syst','site help'):
self.client.sendcmd(cmd)
# STAT provided with an argument is equal to LIST hence not allowed
# if not authenticated
@@ -1172,7 +1180,6 @@


class TestFtpFsOperations(TestCase):
-
"test: PWD, CWD, CDUP, SIZE, RNFR, RNTO, DELE, MKD, RMD, MDTM, STAT"
server_class = FTPd
client_class = ftplib.FTP
@@ -1214,8 +1221,7 @@
self.client.cwd(self.tempdir)
self.assertEqual(self.client.pwd(), '/%s' % self.tempdir)
self.client.cwd(subfolder)
- self.assertEqual(self.client.pwd(),
- '/%s/%s' % (self.tempdir, subfolder))
+ self.assertEqual(self.client.pwd(), '/%s/%s' % (self.tempdir,
subfolder))
self.client.sendcmd('cdup')
self.assertEqual(self.client.pwd(), '/%s' % self.tempdir)
self.client.sendcmd('cdup')
@@ -1245,8 +1251,7 @@
self.client.rmd(self.tempdir)
self.assertRaises(ftplib.error_perm, self.client.rmd,
self.tempfile)
# make sure we can't remove the root directory
- self.assertRaisesRegex(ftplib.error_perm,
- "Can't remove root directory",
+ self.assertRaisesRegex(ftplib.error_perm, "Can't remove root
directory",
self.client.rmd, u('/'))

def test_dele(self):
@@ -1265,22 +1270,19 @@
# rnfr/rnto over non-existing paths
bogus = os.path.basename(tempfile.mktemp(dir=HOME))
self.assertRaises(ftplib.error_perm, self.client.rename,
bogus, '/x')
- self.assertRaises(
- ftplib.error_perm, self.client.rename, self.tempfile, u('/'))
+ self.assertRaises(ftplib.error_perm, self.client.rename,
self.tempfile, u('/'))
# rnto sent without first specifying the source
self.assertRaises(ftplib.error_perm, self.client.sendcmd,
'rnto ' + self.tempfile)

# make sure we can't rename root directory
- self.assertRaisesRegex(ftplib.error_perm,
- "Can't rename home directory",
+ self.assertRaisesRegex(ftplib.error_perm, "Can't rename home
directory",
self.client.rename, '/', '/x')

def test_mdtm(self):
self.client.sendcmd('mdtm ' + self.tempfile)
bogus = os.path.basename(tempfile.mktemp(dir=HOME))
- self.assertRaises(ftplib.error_perm, self.client.sendcmd,
- 'mdtm ' + bogus)
+ self.assertRaises(ftplib.error_perm, self.client.sendcmd, 'mdtm '
+ bogus)
# make sure we can't use mdtm against directories
try:
self.client.sendcmd('mdtm ' + self.tempdir)
@@ -1306,10 +1308,9 @@
_getmtime = AbstractedFS.getmtime
try:
AbstractedFS.getmtime = lambda x, y: -9000000000
- self.assertRaisesRegex(
- ftplib.error_perm,
- "550 Can't determine file's last modification time",
- self.client.sendcmd, 'mdtm ' + self.tempfile)
+ self.assertRaisesRegex(ftplib.error_perm,
+ "550 Can't determine file's last modification
time",
+ self.client.sendcmd, 'mdtm ' + self.tempfile)
# make sure client hasn't been disconnected
self.client.sendcmd('noop')
finally:
@@ -1399,7 +1400,7 @@
self.client.storbinary('stor ' + TESTFN, self.dummy_sendfile)
self.client.retrbinary('retr ' + TESTFN,
self.dummy_recvfile.write)
self.dummy_recvfile.seek(0)
- self.assertEqual(hash(data), hash(self.dummy_recvfile.read()))
+ self.assertEqual(hash(data), hash (self.dummy_recvfile.read()))
finally:
# We do not use os.remove() because file could still be
# locked by ftpd thread. If DELE through FTP fails try
@@ -1519,10 +1520,9 @@
conn.close()
# transfer finished, a 226 response is expected
self.assertEqual('226', self.client.voidresp()[:3])
- self.client.retrbinary('retr ' + filename,
- self.dummy_recvfile.write)
+ self.client.retrbinary('retr ' + filename,
self.dummy_recvfile.write)
self.dummy_recvfile.seek(0)
- self.assertEqual(hash(data), hash(self.dummy_recvfile.read()))
+ self.assertEqual(hash(data), hash (self.dummy_recvfile.read()))
finally:
# We do not use os.remove() because file could still be
# locked by ftpd thread. If DELE through FTP fails try
@@ -1552,8 +1552,7 @@
# login as a limited user in order to make STOU fail
self.client.login('anonymous', '@nopasswd')
before = os.listdir(HOME)
- self.assertRaises(ftplib.error_perm, self.client.sendcmd,
- 'stou ' + TESTFN)
+ self.assertRaises(ftplib.error_perm, self.client.sendcmd, 'stou '
+ TESTFN)
after = os.listdir(HOME)
if before != after:
for file in after:
@@ -1573,8 +1572,7 @@

self.client.retrbinary("retr " + TESTFN,
self.dummy_recvfile.write)
self.dummy_recvfile.seek(0)
- self.assertEqual(
- hash(data1 + data2), hash(self.dummy_recvfile.read()))
+ self.assertEqual(hash(data1 + data2), hash
(self.dummy_recvfile.read()))
finally:
# We do not use os.remove() because file could still be
# locked by ftpd thread. If DELE through FTP fails try
@@ -1590,7 +1588,7 @@
self.client.sendcmd('type i')
self.client.sendcmd('rest 10')
self.assertRaisesRegex(ftplib.error_temp, "Can't APPE while REST",
- self.client.sendcmd, 'appe x')
+ self.client.sendcmd, 'appe x')

def test_rest_on_stor(self):
# Test STOR preceded by REST.
@@ -1621,8 +1619,8 @@
file_size = self.client.size(TESTFN)
self.assertEqual(file_size, bytes_sent)
self.client.sendcmd('rest %s' % ((file_size + 1)))
- self.assertRaises(ftplib.error_perm, self.client.sendcmd,
- 'stor ' + TESTFN)
+ self.assertRaises(ftplib.error_perm, self.client.sendcmd, 'stor '
+ TESTFN)
+
self.client.sendcmd('rest %s' % bytes_sent)
self.client.storbinary('stor ' + TESTFN, self.dummy_sendfile)

@@ -1664,8 +1662,7 @@
# socket.error (Windows) or EOFError (Linux) exception is supposed
# to be raised in such a case.
self.client.sock.settimeout(.1)
- self.assertRaises((socket.error, EOFError),
- self.client.sendcmd, 'noop')
+ self.assertRaises((socket.error, EOFError),
self.client.sendcmd, 'noop')

def test_stor_empty_file(self):
self.client.storbinary('stor ' + TESTFN, self.dummy_sendfile)
@@ -1689,7 +1686,6 @@


class TestFtpRetrieveData(TestCase):
-
"Test RETR, REST, TYPE"
server_class = FTPd
client_class = ftplib.FTP
@@ -1724,7 +1720,7 @@
# attempt to retrieve a file which doesn't exist
bogus = os.path.basename(tempfile.mktemp(dir=HOME))
self.assertRaises(ftplib.error_perm, self.client.retrbinary,
- "retr " + bogus, lambda x: x)
+ "retr " + bogus, lambda x: x)

def test_retr_ascii(self):
# Test RETR in ASCII mode.
@@ -1777,13 +1773,13 @@
# on retr (RFC-1123)
file_size = self.client.size(TESTFN)
self.client.sendcmd('rest %s' % ((file_size + 1)))
- self.assertRaises(ftplib.error_perm, self.client.sendcmd,
- 'retr ' + TESTFN)
+ self.assertRaises(ftplib.error_perm, self.client.sendcmd, 'retr '
+ TESTFN)
+
# test resume
self.client.sendcmd('rest %s' % received_bytes)
self.client.retrbinary("retr " + TESTFN, self.dummyfile.write)
self.dummyfile.seek(0)
- self.assertEqual(hash(data), hash(self.dummyfile.read()))
+ self.assertEqual(hash(data), hash (self.dummyfile.read()))

def test_retr_empty_file(self):
self.client.retrbinary("retr " + TESTFN, self.dummyfile.write)
@@ -1839,7 +1835,7 @@
# non-existent path, 550 response is expected
bogus = os.path.basename(tempfile.mktemp(dir=HOME))
self.assertRaises(ftplib.error_perm, self.client.retrlines,
- '%s ' % cmd + bogus, lambda x: x)
+ '%s ' %cmd + bogus, lambda x: x)
# for an empty directory we excpect that the data channel is
# opened anyway and that no data is received
x = []
@@ -1867,7 +1863,7 @@
self.client.retrlines('list -la', l5.append)
tot = (l1, l2, l3, l4, l5)
for x in range(len(tot) - 1):
- self.assertEqual(tot[x], tot[x + 1])
+ self.assertEqual(tot[x], tot[x+1])

def test_mlst(self):
# utility function for extracting the line of interest
@@ -1882,8 +1878,7 @@
self.assertEqual(mlstline('mlst'), mlstline('mlst /'))
# non-existent path
bogus = os.path.basename(tempfile.mktemp(dir=HOME))
- self.assertRaises(ftplib.error_perm, self.client.sendcmd,
- 'mlst ' + bogus)
+ self.assertRaises(ftplib.error_perm,
self.client.sendcmd, 'mlst '+bogus)
# test file/dir notations
self.assertTrue('type=dir' in mlstline('mlst'))
self.assertTrue('type=file' in mlstline('mlst ' + TESTFN))
@@ -1944,8 +1939,7 @@
resp = self.client.getmultiline()
self.assertEqual(resp, '550 Globbing not supported.')
bogus = os.path.basename(tempfile.mktemp(dir=HOME))
- self.assertRaises(ftplib.error_perm, self.client.sendcmd,
- 'stat ' + bogus)
+ self.assertRaises(ftplib.error_perm, self.client.sendcmd, 'stat '
+ bogus)

def test_unforeseen_time_event(self):
# Emulate a case where the file last modification time is prior
@@ -1968,7 +1962,6 @@


class TestFtpAbort(TestCase):
-
"test: ABOR"
server_class = FTPd
client_class = ftplib.FTP
@@ -2074,11 +2067,9 @@
# overridden so that the "awake" callback is executed
# immediately; this way we won't introduce any slowdown
# and still test the code of interest
-
def _throttle_bandwidth(self, *args, **kwargs):
ThrottledDTPHandler._throttle_bandwidth(self, *args,
**kwargs)
- if (self._throttler is not None
- and not self._throttler.cancelled):
+ if self._throttler is not None and not
self._throttler.cancelled:
self._throttler.call()
self._throttler = None

@@ -2128,7 +2119,6 @@
file.close()
self.assertEqual(hash(data), hash(file_data))

-
class TestTimeouts(TestCase):
"""Test idle-timeout capabilities of control and data channels.
Some tests may fail on slow machines.
@@ -2172,8 +2162,7 @@
data = self.client.sock.recv(BUFSIZE)
self.assertEqual(data, b("421 Control connection timed out.\r\n"))
# ensure client has been kicked off
- self.assertRaises((socket.error, EOFError), self.client.sendcmd,
- 'noop')
+ self.assertRaises((socket.error, EOFError),
self.client.sendcmd, 'noop')

def test_data_timeout(self):
# Test data channel timeout. The client which does not send
@@ -2189,8 +2178,7 @@
data = self.client.sock.recv(BUFSIZE)
self.assertEqual(data, b("421 Data connection timed out.\r\n"))
# ensure client has been kicked off
- self.assertRaises((socket.error, EOFError), self.client.sendcmd,
- 'noop')
+ self.assertRaises((socket.error, EOFError),
self.client.sendcmd, 'noop')
s.close()

def test_data_timeout_not_reached(self):
@@ -2225,8 +2213,7 @@
data = self.client.sock.recv(BUFSIZE)
self.assertEqual(data, b("421 Data connection timed out.\r\n"))
# ensure client has been kicked off
- self.assertRaises((socket.error, EOFError), self.client.sendcmd,
- 'noop')
+ self.assertRaises((socket.error, EOFError),
self.client.sendcmd, 'noop')
s.close()

def test_idle_data_timeout2(self):
@@ -2243,8 +2230,7 @@
data = self.client.sock.recv(BUFSIZE)
self.assertEqual(data, b("421 Control connection timed out.\r\n"))
# ensure client has been kicked off
- self.assertRaises((socket.error, EOFError), self.client.sendcmd,
- 'noop')
+ self.assertRaises((socket.error, EOFError),
self.client.sendcmd, 'noop')
s.close()

def test_pasv_timeout(self):
@@ -2397,13 +2383,11 @@
# Test FTPHandler.max_login_attempts attribute.
self.server.handler.max_login_attempts = 1
self.server.handler._auth_failed_timeout = 0
- self.assertRaises(ftplib.error_perm, self.client.login, 'wrong',
- 'wrong')
+ self.assertRaises(ftplib.error_perm,
self.client.login, 'wrong', 'wrong')
# socket.error (Windows) or EOFError (Linux) exceptions are
# supposed to be raised when attempting to send/recv some data
# using a disconnected socket
- self.assertRaises((socket.error, EOFError), self.client.sendcmd,
- 'noop')
+ self.assertRaises((socket.error, EOFError),
self.client.sendcmd, 'noop')

def test_masquerade_address(self):
# Test FTPHandler.masquerade_address attribute
@@ -2417,7 +2401,7 @@
# Test FTPHandler.masquerade_address_map attribute
host, port = self.client.makepasv()
self.assertEqual(host, self.server.host)
- self.server.handler.masquerade_address_map = {self.server.host:
+ self.server.handler.masquerade_address_map = {self.server.host :
"128.128.128.128"}
host, port = self.client.makepasv()
self.assertEqual(host, "128.128.128.128")
@@ -2716,7 +2700,6 @@
flag = []

class TestHandler(FTPHandler):
-
def on_connect(self):
flag.append(None)

@@ -2730,7 +2713,6 @@
flag = []

class TestHandler(FTPHandler):
-
def on_disconnect(self):
flag.append(None)

@@ -2760,11 +2742,30 @@
def on_login_failed(self, username, password):
raise Exception

+
self._setUp(TestHandler)
# shut down the server to avoid race conditions
self.tearDown()
self.assertEqual(user, [USER])

+ def test_on_login_failed(self):
+ pair = []
+
+ class TestHandler(FTPHandler):
+ _auth_failed_timeout = 0
+
+ def on_login(self, username):
+ raise Exception
+
+ def on_login_failed(self, username, password):
+ pair.append((username, password))
+
+ self._setUp(TestHandler, login=False)
+ self.assertRaises(ftplib.error_perm,
self.client.login, 'foo', 'bar')
+ # shut down the server to avoid race conditions
+ self.tearDown()
+ self.assertEqual(pair, [('foo', 'bar')])
+
def test_on_login_failed(self):
pair = []

@@ -2933,17 +2934,18 @@
self.assertEqual(self.cmdresp('eprt ||'), msg)
# port > 65535
self.assertEqual(self.cmdresp('eprt |%s|%s|65536|' % (self.proto,
- self.HOST)),
msg)
+ self.HOST)),
msg)
# port < 0
self.assertEqual(self.cmdresp('eprt |%s|%s|-1|' % (self.proto,
- self.HOST)),
msg)
+ self.HOST)), msg)
# port < 1024
resp = self.cmdresp('eprt |%s|%s|222|' % (self.proto, self.HOST))
self.assertEqual(resp[:3], '501')
self.assertIn('privileged port', resp)
# proto > 2
_cmd = 'eprt |3|%s|%s|' % (self.server.host, self.server.port)
- self.assertRaises(ftplib.error_perm, self.client.sendcmd, _cmd)
+ self.assertRaises(ftplib.error_perm, self.client.sendcmd, _cmd)
+

if self.proto == '1':
# len(ip.octs) > 4
@@ -2959,7 +2961,7 @@
sock.bind((self.client.sock.getsockname()[0], 0))
sock.listen(5)
sock.settimeout(TIMEOUT)
- ip, port = sock.getsockname()[:2]
+ ip, port = sock.getsockname()[:2]
self.client.sendcmd('eprt |%s|%s|%s|' % (self.proto, ip, port))
try:
try:
@@ -2986,7 +2988,7 @@
# test connection
for cmd in ('EPSV', 'EPSV ' + self.proto):
host, port = ftplib.parse229(self.client.sendcmd(cmd),
- self.client.sock.getpeername())
+ self.client.sock.getpeername())
s = socket.socket(self.client.af, socket.SOCK_STREAM)
s.settimeout(TIMEOUT)
try:
@@ -2998,8 +3000,7 @@
def test_epsv_all(self):
self.client.sendcmd('epsv all')
self.assertRaises(ftplib.error_perm, self.client.sendcmd, 'pasv')
- self.assertRaises(ftplib.error_perm, self.client.sendport,
self.HOST,
- 2000)
+ self.assertRaises(ftplib.error_perm, self.client.sendport,
self.HOST, 2000)
self.assertRaises(ftplib.error_perm, self.client.sendcmd,
'eprt |%s|%s|%s|' % (self.proto, self.HOST,
2000))

@@ -3031,8 +3032,7 @@
ae(self.cmdresp('port 256,0,0,1,1,1'), msg) # oct > 255
ae(self.cmdresp('port 127,0,0,1,256,1'), msg) # port > 65535
ae(self.cmdresp('port 127,0,0,1,-1,0'), msg) # port < 0
- # port < 1024
- resp = self.cmdresp('port %s,1,1' % self.HOST.replace('.', ','))
+ resp = self.cmdresp('port %s,1,1' % self.HOST.replace('.',',')) #
port < 1024
self.assertEqual(resp[:3], '501')
self.assertIn('privileged port', resp)
if "1.2.3.4" != self.HOST:
@@ -3135,7 +3135,7 @@
sock.bind((self.client.sock.getsockname()[0], 0))
sock.listen(5)
sock.settimeout(2)
- ip, port = sock.getsockname()[:2]
+ ip, port = sock.getsockname()[:2]
self.client.sendcmd('eprt |1|%s|%s|' % (ip, port))
sock2 = None
try:
@@ -3155,7 +3155,7 @@
self.client.sock.settimeout(TIMEOUT)
self.client.login(USER, PASSWD)
host, port = ftplib.parse229(self.client.sendcmd('EPSV'),
- self.client.sock.getpeername())
+ self.client.sock.getpeername())
self.assertEqual('127.0.0.1', host)
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
s.settimeout(TIMEOUT)
@@ -3163,7 +3163,6 @@
self.assertTrue(mlstline('mlst /').endswith('/'))
s.close()

-
class TestCornerCases(TestCase):
"""Tests for any kind of strange situation for the server to be in,
mainly referring to bugs signaled on the bug tracker.
@@ -3194,7 +3193,7 @@
sock.bind((self.client.sock.getsockname()[0], 0))
sock.listen(5)
sock.settimeout(TIMEOUT)
- host, port = sock.getsockname()[:2]
+ host, port = sock.getsockname()[:2]

hbytes = host.split('.')
pbytes = [repr(port // 256), repr(port % 256)]
@@ -3212,8 +3211,7 @@

class TestFS(AbstractedFS):
def mkstemp(self, *args, **kwargs):
- raise IOError(errno.EEXIST,
- "No usable temporary file name found")
+ raise IOError(errno.EEXIST, "No usable temporary file name
found")

self.server.handler.abstracted_fs = TestFS
try:
@@ -3353,8 +3351,7 @@
resp = self.client.cwd(TESTFN_UNICODE)
self.assertTrue(TESTFN_UNICODE in resp)
else:
- self.assertRaises(ftplib.error_perm, self.client.cwd,
- TESTFN_UNICODE)
+ self.assertRaises(ftplib.error_perm, self.client.cwd,
TESTFN_UNICODE)

def test_mkd(self):
if self.utf8fs:
@@ -3363,15 +3360,13 @@
self.assertEqual(dirname, '/' + TESTFN_UNICODE)
self.assertTrue(os.path.isdir(TESTFN_UNICODE))
else:
- self.assertRaises(ftplib.error_perm, self.client.mkd,
- TESTFN_UNICODE)
+ self.assertRaises(ftplib.error_perm, self.client.mkd,
TESTFN_UNICODE)

def test_rmdir(self):
if self.utf8fs:
self.client.rmd(TESTFN_UNICODE)
else:
- self.assertRaises(ftplib.error_perm, self.client.rmd,
- TESTFN_UNICODE)
+ self.assertRaises(ftplib.error_perm, self.client.rmd,
TESTFN_UNICODE)

def test_rnfr_rnto(self):
if self.utf8fs:
@@ -3411,6 +3406,7 @@
self.assertRaises(ftplib.error_perm, self.client.sendcmd,
'site chmod 777 ' + TESTFN_UNICODE)

+
# --- listing cmds

def _test_listing_cmds(self, cmd):
@@ -3440,18 +3436,19 @@
# utility function for extracting the line of interest
mlstline = lambda cmd: self.client.voidcmd(cmd).split('\n')[1]
if self.utf8fs:
- self.assertTrue('type=dir' in
+ self.assertTrue('type=dir' in \
mlstline('mlst ' + TESTFN_UNICODE))
- self.assertTrue('/' + TESTFN_UNICODE in
+ self.assertTrue('/' + TESTFN_UNICODE in \
mlstline('mlst ' + TESTFN_UNICODE))
- self.assertTrue('type=file' in
+ self.assertTrue('type=file' in \
mlstline('mlst ' + TESTFN_UNICODE_2))
- self.assertTrue('/' + TESTFN_UNICODE_2 in
+ self.assertTrue('/' + TESTFN_UNICODE_2 in \
mlstline('mlst ' + TESTFN_UNICODE_2))
else:
self.assertRaises(ftplib.error_perm,
mlstline, 'mlst ' + TESTFN_UNICODE)

+
# --- file transfer

def test_stor(self):
@@ -3463,8 +3460,7 @@
dummy.seek(0)
self.client.storbinary('stor ' + TESTFN_UNICODE_2, dummy)
dummy_recv = BytesIO()
- self.client.retrbinary('retr ' + TESTFN_UNICODE_2,
- dummy_recv.write)
+ self.client.retrbinary('retr ' + TESTFN_UNICODE_2,
dummy_recv.write)
dummy_recv.seek(0)
self.assertEqual(dummy_recv.read(), data)
else:
@@ -3612,32 +3608,31 @@
logging.basicConfig(level=logging.WARNING)
remove_test_files()

***The diff for this file has been truncated for email.***
Reply all
Reply to author
Forward
0 new messages