pyftpd-0.8.5+nmu1/0000755000000000000000000000000011405442306010573 5ustar pyftpd-0.8.5+nmu1/auth_dummy_module.py0000644000000000000000000000040107126207606014670 0ustar # dummy autentification, always accept def got_user(username, session, sessions): return 230, "Welcome, user!", username, "", 1, 1 def got_pass(username, password, session, sessions): return 202, "You think I care about passwords?", 1, 1 pyftpd-0.8.5+nmu1/ban_config.py0000644000000000000000000000016207402447317013242 0ustar # list of banned ip's banlist = ['192.168.25.*', '123.123.55.*', '256.257.258.259'] banmsg = 'You are banned.' pyftpd-0.8.5+nmu1/NEWS0000644000000000000000000000221607551503132011275 0ustar 2 Dec 2001 version 0.8.1 * minor tweaks and bugfixes enabling pyftpd to run on Windows again 3 Oct 2000 version 0.8 * can run from inetd * modules renamed to saner names * separated filesystem from ftp as standalone module as a result URL- and curl- virtual filesystems appeared * moved logging to separate and configurable modules * modules can overwrite Session class * moved LIST into separate module, which made it configurable 13 Aug 2000 version 0.7 * unified auth and perm modules * auth_chroot module added (virtual root directories) 13 Jul 2000 version 0.6: * fixed NLST * fixed STOR and APPE (thanks to Remco Gerlich for finding a bug) 7 Jul 2000: version 0.5: * timeout_socket multiple socket close fixes * documentation enhancement 5 Jul 2000: version 0.4: * passive mode * posix path emulation for windows * numerous bugfixes * limit upload/download speed * tkinter configuration now finished 27 Jun 2000: version 0.3, first usable version old pyFTPdrop's history by Marco: revision history: 0.2.0 initial public release 0.2.1 more informative FTP replies pyftpd-0.8.5+nmu1/config.py0000644000000000000000000000110311405441653012411 0ustar initial_msg = 'Welcome to pyftpd. Happy downloading.' do_debug = 0 sbufsize = 16000 # size of send buffer rbufsize = 16000 # size of receive buffer modules = [ #'auth_anonymous_module', 'auth_db_module', 'ban_module', 'fs_chroot_module', # 'fs_real_module', 'cwd_module', 'speed_module', 'limit_module', 'iplimit_module', 'bsd_list_module', 'perm_acl_module', 'log_format_module' ] timeout_data = 120 # timeout for data connection timeout_session = 300 # timeout for control connection initial_wd = '/home/ftp' # initial working directory port = 2121 # default port pyftpd-0.8.5+nmu1/fs_url_module.py0000644000000000000000000000246407174620054014020 0ustar import urllib import fs_base_module class FileObject(fs_base_module.FileObject): def __init__(self, name, mode="r"): self.fo = urllib.urlopen(name[1:]) def close(self): self.fo.close() def read(self, size=None): if size==None: return self.fo.read() else: return self.fo.read(size) def write(self, s): return 0 def seek(self, offset, whence=0): return 0 class FileSystem(fs_base_module.FileSystem): def isdir(self, d): return 0 def isfile(self, f): return 1 def islink(self, d): return 0 def readlink(self, d): return "" def isexec(self, d): return 1 def chdir(self, p): return 0 def stat(self, f): return 10*(0,) def listdir(self, p): return [] def open(self, f, mode="r"): return FileObject(self.v2fs(f), mode) def rmdir(self, d): return 0 def mkdir(self, d): return 0 def unlink(self, p): return 0 class Session: def cmd_rest(self, pos): self.reply("500 Not available here") def cmd_size(self, f): self.reply("500 Not available here") def cmd_stor(self, f): self.reply("500 Not available here") pyftpd-0.8.5+nmu1/cwd_README.txt0000644000000000000000000000117107167354232013140 0ustar MODULE NAME auth_cwd_module - per user configuration of initial working directories OPTIONS none DESCRIPTION if user matches the pattern, his initial directory is set to the value of second field. This overrides initial_cwd in config.py. You can use * to match all users, or as mask, e.g. ab* matches all users beginning with ab. Order is important, first use most general patterns, then specific ones (in other words, put default cwd for * (all not explicitly mentioned users) at the first line). Warning: if you use fs_chroot_module, this directory is relative to the chrootdir. pyftpd-0.8.5+nmu1/auth_dummy_README.txt0000644000000000000000000000046407130161424014530 0ustar MODULE NAME auth_dummy_module - dummy authentification, always accept users OPTIONS none DESCRIPTION If this module is selected, everyone has access to the ftp server. NEVER USE THIS MODULE, EXCEPT FOR TESTING! SEE ALSO auth_db_module auth_anonymous_module auth_PAM_module pyftpd-0.8.5+nmu1/auth_file_config.py0000644000000000000000000000003407402467467014450 0ustar file = "/etc/pyftpd/passwd" pyftpd-0.8.5+nmu1/cwd_module.py0000644000000000000000000000050407167351212013273 0ustar from cwd_config import * from utils import myfnmatch def got_user(username, session, sessions): return 331, "Give me password", "", "", -1, 1 def got_pass(username, password, session, sessions): for i in cwdlist: if myfnmatch(username, i[0]): session.cwd = i[1] return 530, "Sorry", -1, 1 pyftpd-0.8.5+nmu1/log_format_module.py0000644000000000000000000000142007551502715014650 0ustar import time, sys import log_simple_module class Log(log_simple_module.Log): def log(self, t, user, ip, command, boe): # time, who, ip, command, beginning=0 or end=1 if command[:4] == "PASS" and user not in ("anonymous", "ftp"): command = "PASS (hidden)" event = {'t':time.ctime(t), 'user': user, 'cmd': command, 'ip': ip } if boe == 0: #begin line = '%(t)s:start "%(cmd)s" from %(user)s@%(ip)s ' % event elif boe == 1: #end line = '%(t)s:stop "%(cmd)s" from %(user)s@%(ip)s ' % event else: line = '%(t)s: "%(cmd)s" from %(user)s@%(ip)s ' % event self.logfile.write(line+"\n") self.logfile.flush() pyftpd-0.8.5+nmu1/speed_module.py0000644000000000000000000000061707167351354013632 0ustar from speed_config import * from utils import myfnmatch def got_user(username, session, sessions): return 331, "Give me password", "", "", -1, 1 def got_pass(username, password, session, sessions): for i in speedlist: if myfnmatch(username, i[0]): session.limit_retr_speed = float(i[1]) session.limit_stor_speed = float(i[2]) return 530, "Sorry", -1, 1 pyftpd-0.8.5+nmu1/mp3_list_module.py0000644000000000000000000000710107255147676014267 0ustar import string, os from stat import * import time import mp3infor def format_size(s): power = 1024 if s == 0: return "0" units = ["KB", "MB", "GB", "TB"] r = `s` + "B" for i in range(len(units)): if s/power > 9: s = s/power r = `s` + units[i] return r def format_seconds(sec): sec = long(sec) h = sec/3600 m = (sec%3600)/60 s = sec%60 if h: return "%ih%im%is" % (h, m, s) elif m: return "%im%is" % (m, s) else: return "%ss" % s def mp3info(file): try: mp3 = mp3infor.open_mp3(file) except IOError: return "" try: mp3.sync_read_header(0, 500) version = mp3.get_mpeg_version() layer = mp3.get_layer() protection_bit = mp3.get_protection() bitrate = mp3.get_bitrate() frequency = mp3.get_frequency() ch_mode = mp3.get_channel_mode() copyright = mp3.get_copyright() original_bit = mp3.get_original() seconds, minutes, seconds_remain = mp3.get_length() frame = mp3.get_framelength() s = "%5sHz %6s" % (frequency, format_seconds(seconds)) except mp3infor.NoHeaderError: s = "" return s class Session: def cmd_list(self, path): if not self.create_datasock(): return if not path: path = self.cwd path = self.joinpath(self.cwd, path) perm = self.logged and self.permcheck(path, self.user, self.group, self, "list") if not perm: self.reply("550 Sorry") return cwd = self.cwd try: isf = self.filesys.isfile(path) isd = self.filesys.isdir(path) except OSError: self.reply("550 I cannot") return print "fasz", `path` if isf: lp = [path] elif isd: try: self.filesys.chdir(path) cwd = path lp = self.filesys.listdir(path) except OSError: self.reply("550 I cannot") return else: self.reply("550 Uh, what?") return r = [] for l in lp: i = self.joinpath(cwd, l) try: appendix = "" s = "" ft = "?" if self.filesys.islink(i): dirflag = "l" appendix = " -> "+self.filesys.readlink(i) ft = "link" elif self.filesys.isdir(i): dirflag = "d" ft = "directory" else: dirflag = "-" s = mp3info(i) if s: ft = "MP3 file" else: ft = "file" status = self.filesys.stat(i) size = status[ST_SIZE] t = status[ST_CTIME] if t == -1: t = 0 ctime = time.strftime("%b %d",time.gmtime(t)) rl = "%s %10s %s %7s %s %s%s\r\n" % \ (dirflag, ft, s ,format_size(size), ctime, l, appendix) r.append(rl) except OSError: pass try: for i in r: self.sock.send(i) except socket.error: pass try: self.close_datasock() except socket.error: pass del r # to save memory self.reply("226 Wow, listing done") pyftpd-0.8.5+nmu1/fs_curl_README.txt0000644000000000000000000000134607174614722014025 0ustar MODULE NAME fs_curl_module - access to URL via curl program OPTIONS none DESCRIPTION This module is an example of fs modules flexibility. It provides access to general URL naming scheme, e.g. you can do: get /http://www.sex.com/teen.jpg from your ftp client, and pyftpd will fetch teen.jpg from www.sex.com and tranfer it to your client as if it were an ordinary file. Leading / in URL schemes is necessary in order to persuade some over-intelligent ftp clients that it is really a file. Unlike fs_url_module, this module uses external program curl (http://curl.haxx.se/) to fetch files, and can resume transfers, as long as curl can do it. SEE ALSO fs_url_module pyftpd-0.8.5+nmu1/TODO0000644000000000000000000000012207167377377011307 0ustar multiple permission modules ratios command line options to override configuration pyftpd-0.8.5+nmu1/examples-win/0000755000000000000000000000000007167356216013222 5ustar pyftpd-0.8.5+nmu1/examples-win/ban_config.py0000644000000000000000000000016007126373776015663 0ustar # list of banned ip's banlist = ['192.55.0.*', '123.123.55.*', '256.257.258.259'] banmsg = 'You are banned.' pyftpd-0.8.5+nmu1/examples-win/config.py0000644000000000000000000000104507167357342015042 0ustar initial_msg = 'Welcome to pyftpd. Happy downloading.' do_debug = 1 sbufsize = 16000 # size of send buffer rbufsize = 16000 # size of receive buffer modules = ['auth_db_module', 'auth_anonymous_module', 'ban_module', 'cwd_module', 'speed_module', 'limit_module', 'iplimit_module', 'bsd_list_module', 'perm_acl_module', 'fs_real_module', 'log_simple_module'] timeout_data = 120 # timeout for data connection timeout_session = 300 # timeout for control connection initial_wd = 'c:\\' # initial working directory port = 2121 # default port pyftpd-0.8.5+nmu1/examples-win/auth_file_config.py0000644000000000000000000000002407167356235017056 0ustar file = "c:\\passwd" pyftpd-0.8.5+nmu1/examples-win/limit_config.py0000644000000000000000000000005607130150272016220 0ustar limitlist = [('*', 100), ('anonymous', 50)] pyftpd-0.8.5+nmu1/examples-win/speed_config.py0000644000000000000000000000007407133325234016207 0ustar speedlist = [('*', 0.0, 0.0), ('anonymous', 5000.0, 0.0)] pyftpd-0.8.5+nmu1/examples-win/fs_chroot_config.py0000644000000000000000000000006307167356307017107 0ustar chrootlist = [('*', ""), ('anonymous', "c:\\tmp")] pyftpd-0.8.5+nmu1/examples-win/auth_db_config.py0000644000000000000000000000015207126377175016527 0ustar passwd = [('test', 'test', 'CY9rzUYh03PK3k6DJie09g=='), ('user', 'users', '7hHLsZBS5AsHqsDKBgwj7g==')] pyftpd-0.8.5+nmu1/examples-win/iplimit_config.py0000644000000000000000000000012507130136716016555 0ustar #user, max nr of connections from one ip limitlist = [('*', 10), ('anonymous', 2)] pyftpd-0.8.5+nmu1/examples-win/cwd_config.py0000644000000000000000000000006307167357353015700 0ustar cwdlist = [('*', 'c:\\'), ('anonymous', 'c:\\')] pyftpd-0.8.5+nmu1/examples-win/auth_anonymous_config.py0000644000000000000000000000015407125705367020171 0ustar welcome_msg = [ "Welcome to the server", "Have a nice time downloading" "", "See you" ] pyftpd-0.8.5+nmu1/examples-win/perm_acl_config.py0000644000000000000000000000126707170121612016671 0ustar LIST = [ "cwd", "list", "nlst"] GET = ["retr", "size", "mdtm"] READ = LIST+GET PUT = ["stor", "appe", "mkd"] DELETE = ["dele", "rmd"] WRITE = PUT+DELETE ALL = READ+WRITE acllist = [('*', '*', '*', '*', [ 'cwd', 'list', 'nlst', 'retr', 'size', 'mdtm'], []), ('*', '*', '*', 'c:\\ftp\\upload', ['stor', 'appe', 'mkd', 'dele', 'rmd'], []), ('anonymous', '*', '*', '*', [], [ 'cwd', 'list', 'nlst', 'retr', 'size', 'mdtm', 'stor', 'appe', 'mkd', 'dele', 'rmd']), ('anonymous', '*', '*', 'c:\\ftp', [ 'cwd', 'list', 'nlst', 'retr', 'size', 'mdtm'], []), ('anonymous', '*', '*', 'c:\\ftp\\upload', ['stor', 'appe', 'mkd'], [])] pyftpd-0.8.5+nmu1/INSTALL0000644000000000000000000000205707131412214011624 0ustar You must have python (http://www.python.org) installed first. If you want to take advantage of the graphical configuration interface, you must have functioning Tkinter. Fortunately, many (all?) recent linux distributions come with python/tkinter. Simply unpack the distribution archive in a suitable directory, set up configuration and run pyftpd.py. For unix servers: you might consider setting up a dedicate user for running pyftpd. If you want to bind pyftpd to a port below 1024, you probably need special privileges for it (which can be achieved by using sockfs, medusa, rsbac or something similar for linux; or running under root privileges - this is NOT recommended) For Windows servers: install python first. If you want graphical interface, answer Yes when the installer asks you to install tcl/tk. If you want to get rid of the MSDOS prompt window, rename pyftpd.py to pyftpd.pyw (in future, this will be default, but for now MSDOS window is useful for debug and error messages). If you want to start it automatically, add pyftpd.py to your Start Up group. pyftpd-0.8.5+nmu1/auth_anonymous_module.py0000644000000000000000000000245707267253536015613 0ustar from auth_anonymous_config import * import re def got_user(username, session, sessions): if username in ("anonymous", "ftp"): return 331, "Give me you email NOW!", "anonymous", "anonymous", 0, 1 return 331, "Give me password", "", "", -1, 1 # return "message", username, groupname, X, Y # X == 0: deny access # X == 1: grant access # X == -1: does not concern this module # Y == 1: continue with other modules # Y == 0: definitive answer def got_pass(username, password, session, sessions): if username in ("anonymous", "ftp"): if not '@' in password: return 530, "This is not a valid email", 0, 0 if re.match("IE(..)?User@.*", password, re.I): return 530, "Use proper ftp client, not Internet Explorer!", 0, 0 if password in ("mozilla@", "MOZILLA@"): return 530, "Use proper ftp client, not Netscape!", 0, 0 if password in ("NovellProxyCache@", "Squid@"): return 530, "Use proper ftp client, not www browser", 0, 0 return 230, welcome_msg, 1, 1 else: return 530, "Sorry", -1, 1 # return "message", X, Y # X == 0: deny access # X == 1: grant access # X == -1: does not concern this module # Y == 1: continue with other modules # Y == 0: definitive answer pyftpd-0.8.5+nmu1/fs_base_module.py0000644000000000000000000000165707174617060014136 0ustar class FileObject: def __init__(self, name, mode="r"): pass def close(self): pass def read(self, size=None): return "" def write(self, s): return 0 def seek(self, offset, whence=0): return 0 class FileSystem: def __init__(self, session): pass def isdir(self, d): return 0 def isfile(self, f): return 0 def isexec(self, d): return 0 def islink(self, d): return 0 def readlink(self, d): return "" def chdir(self, p): return def stat(self, f): return 10*(0,) def listdir(self, p): return [] def open(self, f, mode="r"): return FileObject(f, mode) def rmdir(self, d): return def mkdir(self, d): return def unlink(self, p): return def v2fs(self, v): return v pyftpd-0.8.5+nmu1/bsd_list_README.txt0000644000000000000000000000032307167571704014171 0ustar MODULE NAME bsd_list_module - provide traditional output for LIST command. OPTIONS none DESCRIPTION provide traditional output for LIST command ala BSD ftp servers. Has still some bugs :-) pyftpd-0.8.5+nmu1/fs_real_README.txt0000644000000000000000000000034507174614060013774 0ustar MODULE NAME fs_real_module - access to real filesystem. OPTIONS none DESCRIPTION This module makes your virtual ftp filesystem reflect the real filesystem. SEE ALSO fs_base_module, fs_chroot_module pyftpd-0.8.5+nmu1/COPYING0000644000000000000000000004311006770226140011632 0ustar GNU GENERAL PUBLIC LICENSE Version 2, June 1991 Copyright (C) 1989, 1991 Free Software Foundation, Inc. 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA Everyone is permitted to copy and distribute verbatim copies of this license document, but changing it is not allowed. Preamble The licenses for most software are designed to take away your freedom to share and change it. By contrast, the GNU General Public License is intended to guarantee your freedom to share and change free software--to make sure the software is free for all its users. This General Public License applies to most of the Free Software Foundation's software and to any other program whose authors commit to using it. (Some other Free Software Foundation software is covered by the GNU Library General Public License instead.) You can apply it to your programs, too. When we speak of free software, we are referring to freedom, not price. Our General Public Licenses are designed to make sure that you have the freedom to distribute copies of free software (and charge for this service if you wish), that you receive source code or can get it if you want it, that you can change the software or use pieces of it in new free programs; and that you know you can do these things. To protect your rights, we need to make restrictions that forbid anyone to deny you these rights or to ask you to surrender the rights. These restrictions translate to certain responsibilities for you if you distribute copies of the software, or if you modify it. For example, if you distribute copies of such a program, whether gratis or for a fee, you must give the recipients all the rights that you have. You must make sure that they, too, receive or can get the source code. And you must show them these terms so they know their rights. We protect your rights with two steps: (1) copyright the software, and (2) offer you this license which gives you legal permission to copy, distribute and/or modify the software. Also, for each author's protection and ours, we want to make certain that everyone understands that there is no warranty for this free software. If the software is modified by someone else and passed on, we want its recipients to know that what they have is not the original, so that any problems introduced by others will not reflect on the original authors' reputations. Finally, any free program is threatened constantly by software patents. We wish to avoid the danger that redistributors of a free program will individually obtain patent licenses, in effect making the program proprietary. To prevent this, we have made it clear that any patent must be licensed for everyone's free use or not licensed at all. The precise terms and conditions for copying, distribution and modification follow. GNU GENERAL PUBLIC LICENSE TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION 0. This License applies to any program or other work which contains a notice placed by the copyright holder saying it may be distributed under the terms of this General Public License. The "Program", below, refers to any such program or work, and a "work based on the Program" means either the Program or any derivative work under copyright law: that is to say, a work containing the Program or a portion of it, either verbatim or with modifications and/or translated into another language. (Hereinafter, translation is included without limitation in the term "modification".) Each licensee is addressed as "you". Activities other than copying, distribution and modification are not covered by this License; they are outside its scope. The act of running the Program is not restricted, and the output from the Program is covered only if its contents constitute a work based on the Program (independent of having been made by running the Program). Whether that is true depends on what the Program does. 1. You may copy and distribute verbatim copies of the Program's source code as you receive it, in any medium, provided that you conspicuously and appropriately publish on each copy an appropriate copyright notice and disclaimer of warranty; keep intact all the notices that refer to this License and to the absence of any warranty; and give any other recipients of the Program a copy of this License along with the Program. You may charge a fee for the physical act of transferring a copy, and you may at your option offer warranty protection in exchange for a fee. 2. You may modify your copy or copies of the Program or any portion of it, thus forming a work based on the Program, and copy and distribute such modifications or work under the terms of Section 1 above, provided that you also meet all of these conditions: a) You must cause the modified files to carry prominent notices stating that you changed the files and the date of any change. b) You must cause any work that you distribute or publish, that in whole or in part contains or is derived from the Program or any part thereof, to be licensed as a whole at no charge to all third parties under the terms of this License. c) If the modified program normally reads commands interactively when run, you must cause it, when started running for such interactive use in the most ordinary way, to print or display an announcement including an appropriate copyright notice and a notice that there is no warranty (or else, saying that you provide a warranty) and that users may redistribute the program under these conditions, and telling the user how to view a copy of this License. (Exception: if the Program itself is interactive but does not normally print such an announcement, your work based on the Program is not required to print an announcement.) These requirements apply to the modified work as a whole. If identifiable sections of that work are not derived from the Program, and can be reasonably considered independent and separate works in themselves, then this License, and its terms, do not apply to those sections when you distribute them as separate works. But when you distribute the same sections as part of a whole which is a work based on the Program, the distribution of the whole must be on the terms of this License, whose permissions for other licensees extend to the entire whole, and thus to each and every part regardless of who wrote it. Thus, it is not the intent of this section to claim rights or contest your rights to work written entirely by you; rather, the intent is to exercise the right to control the distribution of derivative or collective works based on the Program. In addition, mere aggregation of another work not based on the Program with the Program (or with a work based on the Program) on a volume of a storage or distribution medium does not bring the other work under the scope of this License. 3. You may copy and distribute the Program (or a work based on it, under Section 2) in object code or executable form under the terms of Sections 1 and 2 above provided that you also do one of the following: a) Accompany it with the complete corresponding machine-readable source code, which must be distributed under the terms of Sections 1 and 2 above on a medium customarily used for software interchange; or, b) Accompany it with a written offer, valid for at least three years, to give any third party, for a charge no more than your cost of physically performing source distribution, a complete machine-readable copy of the corresponding source code, to be distributed under the terms of Sections 1 and 2 above on a medium customarily used for software interchange; or, c) Accompany it with the information you received as to the offer to distribute corresponding source code. (This alternative is allowed only for noncommercial distribution and only if you received the program in object code or executable form with such an offer, in accord with Subsection b above.) The source code for a work means the preferred form of the work for making modifications to it. For an executable work, complete source code means all the source code for all modules it contains, plus any associated interface definition files, plus the scripts used to control compilation and installation of the executable. However, as a special exception, the source code distributed need not include anything that is normally distributed (in either source or binary form) with the major components (compiler, kernel, and so on) of the operating system on which the executable runs, unless that component itself accompanies the executable. If distribution of executable or object code is made by offering access to copy from a designated place, then offering equivalent access to copy the source code from the same place counts as distribution of the source code, even though third parties are not compelled to copy the source along with the object code. 4. You may not copy, modify, sublicense, or distribute the Program except as expressly provided under this License. Any attempt otherwise to copy, modify, sublicense or distribute the Program is void, and will automatically terminate your rights under this License. However, parties who have received copies, or rights, from you under this License will not have their licenses terminated so long as such parties remain in full compliance. 5. You are not required to accept this License, since you have not signed it. However, nothing else grants you permission to modify or distribute the Program or its derivative works. These actions are prohibited by law if you do not accept this License. Therefore, by modifying or distributing the Program (or any work based on the Program), you indicate your acceptance of this License to do so, and all its terms and conditions for copying, distributing or modifying the Program or works based on it. 6. Each time you redistribute the Program (or any work based on the Program), the recipient automatically receives a license from the original licensor to copy, distribute or modify the Program subject to these terms and conditions. You may not impose any further restrictions on the recipients' exercise of the rights granted herein. You are not responsible for enforcing compliance by third parties to this License. 7. If, as a consequence of a court judgment or allegation of patent infringement or for any other reason (not limited to patent issues), conditions are imposed on you (whether by court order, agreement or otherwise) that contradict the conditions of this License, they do not excuse you from the conditions of this License. If you cannot distribute so as to satisfy simultaneously your obligations under this License and any other pertinent obligations, then as a consequence you may not distribute the Program at all. For example, if a patent license would not permit royalty-free redistribution of the Program by all those who receive copies directly or indirectly through you, then the only way you could satisfy both it and this License would be to refrain entirely from distribution of the Program. If any portion of this section is held invalid or unenforceable under any particular circumstance, the balance of the section is intended to apply and the section as a whole is intended to apply in other circumstances. It is not the purpose of this section to induce you to infringe any patents or other property right claims or to contest validity of any such claims; this section has the sole purpose of protecting the integrity of the free software distribution system, which is implemented by public license practices. Many people have made generous contributions to the wide range of software distributed through that system in reliance on consistent application of that system; it is up to the author/donor to decide if he or she is willing to distribute software through any other system and a licensee cannot impose that choice. This section is intended to make thoroughly clear what is believed to be a consequence of the rest of this License. 8. If the distribution and/or use of the Program is restricted in certain countries either by patents or by copyrighted interfaces, the original copyright holder who places the Program under this License may add an explicit geographical distribution limitation excluding those countries, so that distribution is permitted only in or among countries not thus excluded. In such case, this License incorporates the limitation as if written in the body of this License. 9. The Free Software Foundation may publish revised and/or new versions of the General Public License from time to time. Such new versions will be similar in spirit to the present version, but may differ in detail to address new problems or concerns. Each version is given a distinguishing version number. If the Program specifies a version number of this License which applies to it and "any later version", you have the option of following the terms and conditions either of that version or of any later version published by the Free Software Foundation. If the Program does not specify a version number of this License, you may choose any version ever published by the Free Software Foundation. 10. If you wish to incorporate parts of the Program into other free programs whose distribution conditions are different, write to the author to ask for permission. For software which is copyrighted by the Free Software Foundation, write to the Free Software Foundation; we sometimes make exceptions for this. Our decision will be guided by the two goals of preserving the free status of all derivatives of our free software and of promoting the sharing and reuse of software generally. NO WARRANTY 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION. 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. END OF TERMS AND CONDITIONS How to Apply These Terms to Your New Programs If you develop a new program, and you want it to be of the greatest possible use to the public, the best way to achieve this is to make it free software which everyone can redistribute and change under these terms. To do so, attach the following notices to the program. It is safest to attach them to the start of each source file to most effectively convey the exclusion of warranty; and each file should have at least the "copyright" line and a pointer to where the full notice is found. Copyright (C) This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA Also add information on how to contact you by electronic and paper mail. If the program is interactive, make it output a short notice like this when it starts in an interactive mode: Gnomovision version 69, Copyright (C) year name of author Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'. This is free software, and you are welcome to redistribute it under certain conditions; type `show c' for details. The hypothetical commands `show w' and `show c' should show the appropriate parts of the General Public License. Of course, the commands you use may be called something other than `show w' and `show c'; they could even be mouse-clicks or menu items--whatever suits your program. You should also get your employer (if you work as a programmer) or your school, if any, to sign a "copyright disclaimer" for the program, if necessary. Here is a sample; alter the names: Yoyodyne, Inc., hereby disclaims all copyright interest in the program `Gnomovision' (which makes passes at compilers) written by James Hacker. , 1 April 1989 Ty Coon, President of Vice This General Public License does not permit incorporating your program into proprietary programs. If your program is a subroutine library, you may consider it more useful to permit linking proprietary applications with the library. If this is what you want to do, use the GNU Library General Public License instead of this License. pyftpd-0.8.5+nmu1/timeout_socket.py0000644000000000000000000001724407233251754014224 0ustar """ Sockets with timeouts. Defines a "timeout_socket" class for connections that can potentially cause the server to hang. It uses select, and is based on code originally from Scott Cotton. The following people have contributed to the development of this module: Scott Cotton Lloyd Zusman Piers Lauder Here are examples using smtplib.py and poplib.py by inheriting: class timeout_POP3(poplib.POP3): def __init__(self, host, port = poplib.POP3_PORT): self.host = host self.port = port self.sock = timeout_socket.timeout_socket() self.sock.connect(self.host, self.port) self.file = self.sock.makefile('rb') # same as poplib.POP3 self._debugging = 0 self.welcome = self._getresp() class timeout_SMTP(smtplib.SMTP): def connect(self, host='localhost', port = 0): '''Connect to a host on a given port. Override the std SMTP connect in order to use a timeout_socket. ''' if not port: i = string.find(host, ':') if i >= 0: host, port = host[:i], host[i+1:] try: port = string.atoi(port) except string.atoi_error: raise socket.error, "nonnumeric port" if not port: port = smtplib.SMTP_PORT self.sock = timeout_socket.timeout_socket() if self.debuglevel > 0: print 'connect:', (host, port) self.sock.connect(host, port) (code,msg)=self.getreply() if self.debuglevel >0 : print "connect:", msg return (code,msg) This allows one to use poplib.py & smtplib.py unchanged. """ __version__ = "1.7" __author__ = "Scott Cotton " import socket import errno import select import string _TIMEOUT = 20.0 class Timeout(Exception): pass class timeout_socket: """ Instantiate with: timeout_socket(timeout=20, sock=None) where `timeout' is the desired timeout in seconds (default 20), and `sock' is a pre-existing socket (default: one is created). """ def __init__(self, timeout=_TIMEOUT, sock=None): self.ndups = 0 self.timeout(timeout) self.inbuf = '' if sock is None: sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM) self.s = sock self.ndups = 1 self.s.setblocking(0) # destructor notes: socket.socket will close when destroyed def __getattr__(self, name): return getattr(self.s, name) # Other socket methods def accept(self): timeout = self._timeout s = self.s try: # Non-blocking mode s.setblocking(1) sa = s.accept() s.setblocking(timeout != 0) return sa except socket.error,why: if not timeout: raise s.setblocking(1) # The exception arguments can be (string) or (int,string) if len(why.args) == 1: code = 0 else: code,why = why if code not in (errno.EAGAIN, errno.EWOULDBLOCK): raise # Ok, then wait... r,w,e = select.select([s],[],[],timeout) if r: try: sa = s.accept() return sa except socket.error,why: # This can throw string or (int,string) if len(why.args) == 1: code = 0 else: code,why = why raise msg = 'accept timed out after %s seconds' % self._timeout raise Timeout(msg) def connect(self, *addr): timeout = self._timeout s = self.s try: # Non-blocking mode s.setblocking(0) apply(s.connect, addr) s.setblocking(timeout != 0) return 1 except socket.error,why: if not timeout: raise s.setblocking(1) # The exception arguments can be (string) or (int,string) if len(why.args) == 1: code = 0 else: code,why = why if code not in (errno.EINPROGRESS, errno.EALREADY, errno.EWOULDBLOCK): # WSAEWOULDBLOCK raise # Ok, then wait... r,w,e = select.select([],[s],[],timeout) if w: try: apply(s.connect, addr) return 1 except socket.error,why: # This can throw string or (int,string) if len(why.args) == 1: code = 0 else: code,why = why if code == errno.EISCONN: return 1 raise msg = 'connect to %s timed out after %s seconds' % (str(addr), self._timeout) raise Timeout(msg) def send(self, data, flags=0): next = 0 t = self._timeout total = len(data) while 1: try: r,w,e = select.select([],[self.s], [], t) except ValueError: raise Timeout("valueerror in select") if w: buf = data[next:next+8192] sent = self.s.send(buf, flags) next = next + sent if next == total: return else: raise Timeout('timeout while sending "%.20s...": %d sec' % (data, t)) def recv(self, amt, flags=0): r,w,e = select.select([self.s], [], [], self._timeout) if r: recvd = self.s.recv(amt, flags) return recvd raise Timeout("timeout while receiving from %s: %d sec" % (`self.s.getpeername()`, self._timeout)) def makefile(self, flags="r", buffsize=-1): # makefile needs parameters # Just return ourself - watch out for multiple close(), and # provide read and readline methods expected by makefile result below. self.ndups = self.ndups+1 # nubmer or dupped descriptors return self def close(self): #if self.s.fileno() >= 0: self.ndups = self.ndups-1 #print self.ndups if self.ndups == 0: #print "closing", self.s self.s.close() # New (non-standard socket) methods to support `makefile'. def read(self, amt): """ This only returns when amt has been read or socket times out """ while len(self.inbuf) < amt: self.inbuf = self.inbuf + self.recv(4096) data = self.inbuf[:amt] self.inbuf = self.inbuf[amt:] return data def readline(self): """ readine for socket - buffers data """ # Much slower than built-in method! while 1: lf = string.find(self.inbuf, '\n') if lf >= 0: break r = self.recv(4096) if not r: # connection broken # without this readline would enter infinite loop break self.inbuf = self.inbuf + r lf = lf + 1 data = self.inbuf[:lf] self.inbuf = self.inbuf[lf:] return data # Other new methods def recvpending(self, timeout=0): """ returns 1/0 """ return [] != select.select([self.s], [], [], int(timeout) or self._timeout)[0] def timeout(self, newtimo): """ change socket timeout """ self._timeout = int(newtimo) def flush(self): # TCPServer needs this pass def write(self, data): self.send(data) pyftpd-0.8.5+nmu1/fs_curl_module.py0000644000000000000000000000264307174620040014155 0ustar import ftplib, os, posixpath import fs_base_module class FileObject(fs_base_module.FileObject): def __init__(self, name, mode="r"): self.name = name[1:] self.fo = os.popen("curl -s %s" % self.name) def close(self): self.fo.close() def read(self, size=None): if size==None: return self.fo.read() else: return self.fo.read(size) def write(self, s): return 0 def seek(self, offset, whence=0): self.fo.close() assert whence == 0 self.fo = os.popen("curl -s -C %i %s" % (offset, self.name)) class FileSystem(fs_base_module.FileSystem): def __init__(self, session): global ftp self.pardir = ".." def isdir(self, d): return 0 def islink(self, d): return 0 def isfile(self, f): return 1 def readlink(self, d): return "" def isexec(self, d): return 1 def chdir(self, p): return 0 def stat(self, f): return 10*(0,) def listdir(self, p): return [] def open(self, f, mode="r"): return FileObject(self.v2fs(f), mode) def rmdir(self, d): return 0 def mkdir(self, d): return 0 def unlink(self, p): return 0 class Session: def cmd_size(self, f): self.reply("500 Not available here") pyftpd-0.8.5+nmu1/limit_README.txt0000644000000000000000000000110707167351304013475 0ustar MODULE NAME limit_module - limit maximum numbers of connections for users OPTIONS none DESCRIPTION Configure list of users, corresponding number of maximum number of users connected simultaneously. You can use * to match all users, or as mask, e.g. ab* matches all users beginning with ab. Order is important, first use most general patterns, then specific ones (in other words, put limit for * (all users) at the first line) Unlike iplimit_module, this module limits the total number of logged in users. SEE ALSO iplimit_module pyftpd-0.8.5+nmu1/README0000644000000000000000000000715407170121322011456 0ustar Documentation is not quite finished yet. pyftpd is a is a multiplatform (currently tested only on linux, hurd, solaris, hp/ux, NetBSD and windows95/98/NT/2000) ftp daemon written in python. It can work under normal user account and does not require any special rights to start. Thanks to the modular design, there are possible many different authentification schemes, most notably anonymous user authentification, authentification from internal database of users (these are virtual users, independent from the operating system users), and authentification against PAM[1]. pyftpd uses its own permission modules, and can pinpoint user permissions down to a simple file and simple ftp operation. pyftpd uses python threads; if your python interpreter was compiled without thread support, it will use forking instead, with limited functionality (limiting number of simultaneously logged users and some other options won't work). If neither threads nor fork is available, pyftpd will serve just one connection at a time. You can start pyftpd from inetd, it should automatically recognize it, with similar restrictions to forking. The main pyftpd configuration is in file config.py. Configuration for additional modules is in files modulename_config.py. You can change configuration either by editing appropriate file, or by running graphical configuration tool (Tkinter required). Configuration tool for config.py file is called conf_configure.py, configuration tools for other modules are called conf_modulename.py. When you launch conf_configure.py, in the window you are configuring only the list of authentification modules (Edit button is a bit misleading - it does not edit, but rather replace the module with another one). Global options are accessible via the Option button. You should include at least one auth_* module and exactly one fs_* module. And it is probably a good idea to include optional *list module, because the built-in LIST command gives just basic output format, which most GUI ftp clients cannot grok. To configure individual modules, you have to run appropriate conf_*.py scripts. On virtual and real paths: Path to a file or directory, as seen on the disk, is a real path. However, path as visible to ftp clients can be mangled in some ways[2]. The mangled visible path is called virtual path, and this is the path you enter as Initial dir or in cwd_module or in perm_acl_module. On performance: pyftpd, when possible, uses multiple threads, which means it manages the memory (which is the decisive factor when serving many clients) in quite an efficient way. As an example, on my Debian Linux with python 1.5.2, idle server takes 1564kB RAM, of which 588kB is shareable with eventual other python interpreter. Each connected client takes additional 44kB memory, and some more kB when up/downloading a file (this is configurable via sbufsize and rbufsize options). Compare with proftpd 1.2.0, which (in standalone mode) takes 532kB (not counting libc and other system libraries) and each connected client takes additional 312kB memory. Or with wu-ftpd, which takes 492kB memory, and each connection takes additional 344kB. This is due to multithreaded model of pyftpd, compared to forking of other daemons. On the other hand, there is a little nice single-threaded ftp server, using non blocking sockets and poll, called betaftpd (http://members.xoom.com/sneeze/betaftpd-lynx.html), which takes 220kB memory, and each connections takes additional 12kB. This shows up the efficiency of C over python :-). [1] It is currently not quite functioning. [2] Since version 0.8, you can use virtual filesystem, having nothing to do with real filesystem. pyftpd-0.8.5+nmu1/CREDITS0000644000000000000000000000050707131424254011620 0ustar Special thanks go to Peter Cagarda for selfless tedious testing on Windows, and to Pavol Domin for not being afraid to install and use first pre-alpha version, and to Tomas Stanys and Radoslav Bohm for letting me try pyftpd on their computers, and most importantly to Marco Amrein for his pyFTPdrop, on which is pyftpd based. pyftpd-0.8.5+nmu1/fs_chroot_README.txt0000644000000000000000000000150707174614274014357 0ustar MODULE NAME fs_chroot_module - access to filesystem, with the ability of per user configuration of virtual directories OPTIONS slave_fs - slave filesystem module, upon which this module operates. Usually fs_real_module. DESCRIPTION if user matches the pattern, the value of the second field is preppended to all file operations. As a result, the user will see only subdirectories, without a chance to escape out (with the exception of following symlinks). You can use * to match all users, or as mask, e.g. ab* matches all users beginning with ab. Order is important, first use most general patterns, then specific ones (in other words, put default chroot directory for * (all not explicitly mentioned users) at the first line). SEE ALSO fs_real_module pyftpd-0.8.5+nmu1/auth_file_README.txt0000644000000000000000000000035207130161436014313 0ustar MODULE NAME auth_file_module - authentificate users using text file database OPTIONS none DESCRIPTION it's obsolete. Use auth_db_module instead. SEE ALSO auth_anonymous_module auth_db_module auth_PAM_module pyftpd-0.8.5+nmu1/pyftpd.80000644000000000000000000000045210523663161012177 0ustar .TH PYFTPD 8 .SH NAME pyftpd \- ftp server in python .SH "DESCRIPTION" .B pyftpd is a ftp server written in python. .PP .SH BUGS This manual page says nothing. See README files for more information. .SH "SEE ALSO" ftp(1), ftpd(8) .SH AUTHOR Radovan Garab\('ik pyftpd-0.8.5+nmu1/iplimit_module.py0000644000000000000000000000135007167351266014176 0ustar from iplimit_config import * from utils import myfnmatch def got_user(username, session, sessions): for i in limitlist: if myfnmatch(username, i[0]): cnt = 0 for j in sessions.keys(): cs = sessions[j] if not username: print "AAAA", `username`, `cs.user` if cs.ip == session.ip and myfnmatch(username, cs.user): cnt = cnt+1 if cnt >= i[1]: # >= because we counted ourselves in return 530, "Only %i users from one IP address allowed" % i[1] , "", "", 0, 0 return 331, "Give me password", "", "", -1, 1 def got_pass(username, password, session, sessions): return 530, "Sorry", -1, 1 pyftpd-0.8.5+nmu1/conf_configure.py0000755000000000000000000002511510524174700014143 0ustar #!/usr/bin/python from utils import isdir, defaultdir initial_msg = "Welcome to pyftpd. Happy downloading" do_debug = 0 sbufsize = 16000 # size of send buffer rbufsize = 16000 # size of receive buffer modules = ["auth_db_module", "auth_anonymous_module", "ban_module", "cwd_module", "limit_module", "fs_real_module", "iplimit_module", "perm_acl_module"] timeout_data = 60 # timeout for data connection timeout_session = 60 # timeout for control connection initial_wd = defaultdir # initial working directory port = 2121 # default port try: from config import * except: pass from Tkinter import * import string, pprint, os, os.path, StringIO import tkSimpleDialog, tkMessageBox, tkFileDialog, FileDialog import TkinterConfigList config_file = "config.py" class Opts(Toplevel): def __init__(self, parent=None): Toplevel.__init__(self, parent) self.transient(parent) self.title("set options") self.parent = parent body = Frame(self) self.initial_focus = self.body(body) body.pack(padx=5, pady=5) #self.buttonbox() self.grab_set() if not self.initial_focus: self.initial_focus = self self.protocol("WM_DELETE_WINDOW", self.quit) self.geometry("+%d+%d" % (parent.winfo_rootx()+50, parent.winfo_rooty()+50)) self.initial_focus.focus_set() self.wait_window(self) def body(self, master): self.frame = master self.bdo_debug = IntVar() self.bdo_debug.set(do_debug) self.do_debugb = Checkbutton(self.frame, text="Debug messages", anchor=W, variable=self.bdo_debug) self.do_debugb.grid(row=0, column=0, sticky=W) self.inimsgb = Button(self.frame, width=16, relief=RIDGE, cursor='hand2', text="Initial message", command=self.enterinimsg) self.inimsgb.grid(row=2, column=0, sticky=W) self.inimsgv = StringVar() self.inimsgv.set(initial_msg) self.inimsgbe = Label(self.frame, textvariable=self.inimsgv, #width=16, #relief=RIDGE, cursor='hand2') self.inimsgbe.grid(row=2, column=1, sticky=W) self.sbufb = Button(self.frame, width=16, relief=RIDGE, cursor='hand2', text="Send buffer size", command=self.entersbuf) self.sbufb.grid(row=4, column=0, sticky=W) self.sbufv = IntVar() self.sbufv.set(sbufsize) self.sbufbe = Label(self.frame, textvariable=self.sbufv, #width=16, cursor='hand2') self.sbufbe.grid(row=4, column=1, sticky=W) self.rbufb = Button(self.frame, width=16, relief=RIDGE, cursor='hand2', text="Receive buffer size", command=self.enterrbuf) self.rbufb.grid(row=5, column=0, sticky=W) self.rbufv = IntVar() self.rbufv.set(rbufsize) self.rbufbe = Label(self.frame, textvariable=self.rbufv, #width=16, cursor='hand2') self.rbufbe.grid(row=5, column=1, sticky=W) self.tdatab = Button(self.frame, width=16, relief=RIDGE, cursor='hand2', text="Timeout data", command=self.entertdata) self.tdatab.grid(row=6, column=0, sticky=W) self.tdatav = IntVar() self.tdatav.set(timeout_data) self.tdatabe = Label(self.frame, textvariable=self.tdatav, #width=16, cursor='hand2') self.tdatabe.grid(row=6, column=1, sticky=W) self.tsessionb = Button(self.frame, width=16, relief=RIDGE, cursor='hand2', text="Timeout session", command=self.entertsession) self.tsessionb.grid(row=7, column=0, sticky=W) self.tsessionv = IntVar() self.tsessionv.set(timeout_session) self.tsessionbe = Label(self.frame, textvariable=self.tsessionv, #width=16, cursor='hand2') self.tsessionbe.grid(row=7, column=1, sticky=W) self.iniwdb = Button(self.frame, width=16, relief=RIDGE, cursor='hand2', text="Initial dir", command=self.enteriniwd) self.iniwdb.grid(row=8, column=0, sticky=W) self.iniwdv = StringVar() self.iniwdv.set(initial_wd) self.iniwdbe = Label(self.frame, textvariable=self.iniwdv, #width=16, #relief=RIDGE, cursor='hand2') self.iniwdbe.grid(row=8, column=1, sticky=W) self.tportb = Button(self.frame, width=16, relief=RIDGE, cursor='hand2', text="Port:", command=self.enterport) self.tportb.grid(row=10, column=0, sticky=W) self.tportv = IntVar() self.tportv.set(port) self.tportbe = Label(self.frame, textvariable=self.tportv, #width=16, cursor='hand2') self.tportbe.grid(row=10, column=1, sticky=W) self.aboutb = Button(self.frame, text="Help", width=16, relief=GROOVE, command=self.about) self.aboutb.grid(row=13, column=0, sticky=W) self.quitb = Button(self.frame, text="OK", width=7, #cursor="pirate", command=self.quit) self.quitb.grid(row=14, column=0, columnspan=2, sticky=W+E) def enterinimsg(self): global initial_msg m=tkSimpleDialog.askstring( title="initial msg", prompt="Initial message:", initialvalue=initial_msg) if m <> None: initial_msg = m self.inimsgv.set(initial_msg) def enteriniwd(self): global initial_wd pd = FileDialog.FileDialog(master=self.frame, title="Initial working directory") m = pd.go(dir_or_file=initial_wd) if m <> None: if os.path.exists(m) and not isdir(m): m = os.path.dirname(m) initial_wd = m self.iniwdv.set(m) def entersbuf(self): global sbufsize m=tkSimpleDialog.askinteger( title="sbufsize", prompt="Size of sending buffer [B]:", initialvalue=sbufsize) if m <> None: sbufsize = m self.sbufv.set(m) def enterrbuf(self): global rbufsize m=tkSimpleDialog.askinteger( title="sbufsize", prompt="Size of receiving buffer [B]:", initialvalue=rbufsize) if m <> None: rbufsize = m self.rbufv.set(m) def entertdata(self): global timeout_data m=tkSimpleDialog.askinteger( title="timeout data", prompt="Timeout for data connection [s]:", initialvalue=timeout_data) if m <> None: timeout_data = m self.tdatav.set(m) def entertsession(self): global timeout_session m=tkSimpleDialog.askinteger( title="timeout session", prompt="Timeout for session connection [s]:", initialvalue=timeout_session) if m <> None: timeout_session = m self.tsessionv.set(m) def enterport(self): global port m=tkSimpleDialog.askinteger( title="port", prompt="Port:", initialvalue=port) if m <> None: port = m self.tportv.set(m) def enterlogfile(self): global logfile "ask for the logfile" u = tkFileDialog.asksaveasfilename(defaultextension=".log", filetypes=[("log files", "*.log")], initialfile="pyftpd.log" ) if not u: return None logfile=u self.logfilev.set(u) def about(self): h = TkinterConfigList.HelpDialog(self.parent) def quit(self): global do_debug do_debug = self.bdo_debug.get() self.parent.focus_set() self.destroy() class App(TkinterConfigList.App): def askval(self, initval="auth_dummy"): "ask for the module" u = tkFileDialog.askopenfilename(defaultextension=".py", filetypes=[("modules", "*_module.py")], initialdir="." ) if not u: return None u = os.path.splitext(os.path.split(u)[1])[0] return u def opt(self): #root=Toplevel(self.frame, bd=2, relief=RAISED) opts = Opts(self.frame) #opts.title("pyftpd options") #root.mainloop() return def stringify(self, entry): return entry def prepare_for_save(self): stream = StringIO.StringIO() pprint.pprint(self.list, stream) s = stream.getvalue() stream.close() return """ initial_msg = %s do_debug = %i sbufsize = %i # size of send buffer rbufsize = %i # size of receive buffer modules = %s timeout_data = %i # timeout for data connection timeout_session = %i # timeout for control connection initial_wd = %s # initial working directory port = %i # default port """ % (repr(initial_msg), do_debug, sbufsize, rbufsize, s, timeout_data, timeout_session, repr(initial_wd), port) def quit(self): if tkMessageBox.askyesno("Save", "Save changes?"): self.save() self.frame.quit() TkinterConfigList.go(Frame=App, title="configure pyftpd", list=modules, default_config_file=config_file, hf="configure_README.txt") pyftpd-0.8.5+nmu1/conf_ban.py0000755000000000000000000000226210524174675012733 0ustar #!/usr/bin/python banlist = [] banmsg = "You are banned" try: from ban_config import * except: pass from Tkinter import * import string, pprint, os, StringIO import tkSimpleDialog, tkMessageBox, tkFileDialog, FileDialog import TkinterConfigList config_file = "ban_config.py" class App(TkinterConfigList.App): def askval(self, initval="256.257.258.259"): "ask for the ip number" u = tkSimpleDialog.askstring("IP number", "IP number:", initialvalue=initval) if u == None: return None return u def opt(self): global banmsg r = tkSimpleDialog.askstring("banmsg", "Message for the banned ones:", initialvalue=banmsg) if r: banmsg = r def stringify(self, entry): return entry def prepare_for_save(self): stream = StringIO.StringIO() pprint.pprint(self.list, stream) s = stream.getvalue() stream.close() return "# list of banned ip's \nbanlist = "+s+"\n"+ \ "banmsg = "+repr(banmsg)+"\n" TkinterConfigList.go(Frame=App, title="configure banlist", list=banlist, default_config_file=config_file, hf="ban_README.txt") pyftpd-0.8.5+nmu1/auth_PAM_config.py0000644000000000000000000000004407126175706014142 0ustar service = "ftp" #service = "passwd" pyftpd-0.8.5+nmu1/auth_PAM_README.txt0000644000000000000000000000031007130161256014003 0ustar MODULE NAME auth_PAM_module - authentificate users using PAM modules OPTIONS none DESCRIPTION it's broken. Do not use this module. SEE ALSO auth_anonymous_module auth_db_module pyftpd-0.8.5+nmu1/fs_real_module.py0000644000000000000000000000336007371021346014133 0ustar import os, sys import utils, string import fs_base_module class FileObject(fs_base_module.FileObject): def __init__(self, name, mode="r"): #check if name is valid filename self.fo = open(name, mode) def close(self): self.fo.close() def read(self, size=None): if size==None: return self.fo.read() else: return self.fo.read(size) def write(self, s): return self.fo.write(s) def seek(self, offset, whence=0): return self.fo.seek(offset, whence) class FileSystem(fs_base_module.FileSystem): def isdir(self, f): return utils.isdir(self.v2fs(f)) def isfile(self, f): return os.path.isfile(self.v2fs(f)) def isexec(self, f): return utils.isexec(self.v2fs(f)) def islink(self, f): return os.path.islink(self.v2fs(f)) def readlink(self, f): return os.readlink(self.v2fs(f)) def chdir(self, f): return os.chdir(self.v2fs(f)) def stat(self, f): return os.stat(self.v2fs(f)) def listdir(self, f): return os.listdir(self.v2fs(f)) def open(self, f, mode="r"): return FileObject(self.v2fs(f), mode) def rmdir(self, f): return os.rmdir(self.v2fs(f)) def mkdir(self, f): return os.mkdir(self.v2fs(f)) def unlink(self, f): return os.unlink(self.v2fs(f)) if sys.platform[:3] == 'win': def v2fs(self, v): print 'fasz', `v` if v[2] == ":" and v[1] in string.letters: return os.path.normpath(v[1:]) else: return "" # should not happen else: def v2fs(self, v): return os.path.normpath(v) pyftpd-0.8.5+nmu1/passwd0000644000000000000000000000005107126111414012011 0ustar test:test:test123 user:test:userpassword pyftpd-0.8.5+nmu1/limit_config.py0000644000000000000000000000007307255401364013617 0ustar limitlist = [('*', 100), ('anonymous', 5), ('roxon', 2)] pyftpd-0.8.5+nmu1/speed_config.py0000644000000000000000000000007507231075364013604 0ustar speedlist = [('*', 0.0, 0.0), ('anonymous', 50000.0, 0.0)] pyftpd-0.8.5+nmu1/ban_README.txt0000644000000000000000000000051207167351162013120 0ustar MODULE NAME ban_module - deny authentification from given IP numbers OPTIONS ban_msg - message to display to the banned users. DESCRIPTION if somebody tries to connect from one of the IP numbers in the list, he is denied access no matter what. You can use * to denote range if IP numbers, e.g. 123.4.*.* pyftpd-0.8.5+nmu1/perm_dummy_module.py0000644000000000000000000000017707135116326014702 0ustar #dummy permission, always accept def permcheck(f, user, group, ip, operation): # check permissions of file f return 1, 1 pyftpd-0.8.5+nmu1/auth_db_module.py0000644000000000000000000000241207131163054014120 0ustar # autentificate from internal database, passwords are md5-hashed import string, md5, binascii from auth_db_config import * def md5hash(s): m = md5.new() m.update(s) return string.strip(binascii.b2a_base64(m.digest())) def got_user(username, session, sessions): group = None for i in passwd: if i[0] == username: group = i[1] return 331, "Password? Password?? Password!!!", username, group, 0, 1 return 331, "Password? Password?? Password!!! (will be bad anyway)", "", "", -1, 1 # return "message", username, groupname, X, Y # X == 0: deny access # X == 1: grant access # X == -1: does not concern this module # Y == 1: continue with other modules # Y == 0: definitive answer def got_pass(username, password, session, sessions): if not username: #return 503, "Login with USER first.", 0, 0 return 530, "Bad password.", 0, 0 for i in passwd: if i[0] == username: if md5hash(password) == i[2]: return 230, "Proceed, dear "+username, 1, 1 else: return 530, "Bad password", 0, 0 return 530, "Uh oh, I am sorry...", -1, 1 pyftpd-0.8.5+nmu1/auth_file_module.py0000644000000000000000000000227207131161710014453 0ustar # autentificate from file user:group:password import string from auth_file_config import * def got_user(username, session, sessions): f = open(file, "r") group = None for i in f.readlines(): s = string.split(i, ':', 2) if s[0] == username: group = s[1] return 331, "Password? Password?? Password!!!", username, group, 0, 1 return 530, "No such user", "", "", -1, 1 # return "message", username, groupname, X, Y # X == 0: deny access # X == 1: grant access # X == -1: does not concern this module # Y == 1: continue with other modules # Y == 0: definitive answer def got_pass(username, password, session, sessions): if not username: return 503, "Login with USER first.", 0, 0 f = open(file, "r") for i in f.readlines(): s = string.split(i, ':', 2) if s[0] == username: if password == s[2][:-1]: return 230, "Proceed, dear "+username, 1, 1 else: return 530, "Bad password", 0, 0 return 530, "Uh oh, I am sorry...", -1, 1 pyftpd-0.8.5+nmu1/conf_speed.py0000755000000000000000000000244610524174716013273 0ustar #!/usr/bin/python from utils import isdir, defaultdir speedlist = [] try: from speed_config import * except: pass from Tkinter import * import string, pprint, os, StringIO import tkSimpleDialog, tkMessageBox, tkFileDialog, FileDialog import TkinterConfigList config_file = "speed_config.py" class App(TkinterConfigList.App): def askval(self, initval=("anonymous", 0., 0.)): "ask for the user and path" u = tkSimpleDialog.askstring("username", "username", initialvalue=initval[0]) if u == None: return None rs = tkSimpleDialog.askfloat("RETR limit [B/s]", "RETR limit [B/s].", initialvalue=initval[1]) if rs == None: return None ss = tkSimpleDialog.askfloat("STOR limit [B/s]", "STOR limit [B/s].", initialvalue=initval[2]) if ss == None: return None return u, rs, ss def stringify(self, tup): return "%s : %.5g, %.5g" % tup def prepare_for_save(self): stream = StringIO.StringIO() pprint.pprint(self.list, stream) s = stream.getvalue() stream.close() return "speedlist = "+s+"\n" TkinterConfigList.go(Frame=App, title="configure speed limit", list=speedlist, default_config_file=config_file, hf="speed_README.txt") pyftpd-0.8.5+nmu1/fs_chroot_config.py0000644000000000000000000000015407402467546014477 0ustar slave_fs = "fs_real_module" chrootlist = [('*', "/"), ('anonymous', "/home/ftp"), ('roxon', "/home/roxon")] pyftpd-0.8.5+nmu1/BUGS0000644000000000000000000000007307170121503011253 0ustar auth_PAM is broken, does not use pam conversation properly pyftpd-0.8.5+nmu1/conf_perm_acl.py0000755000000000000000000001610410524174714013747 0ustar #!/usr/bin/python from utils import isdir acllist = [] try: from perm_acl_config import * except: pass from Tkinter import * import string, pprint, os, StringIO import tkSimpleDialog, tkMessageBox, tkFileDialog, FileDialog import TkinterConfigList config_file = "perm_acl_config.py" class PermDialog(tkSimpleDialog.Dialog): def __init__(self, parent, title = None, initval=("", "", "", "", [], [])): self.initval = initval Toplevel.__init__(self, parent) self.transient(parent) if title: self.title(title) self.parent = parent self.result = None body = Frame(self) self.initial_focus = self.body(body) body.pack(padx=5, pady=5) self.buttonbox() self.grab_set() if not self.initial_focus: self.initial_focus = self self.protocol("WM_DELETE_WINDOW", self.cancel) self.geometry("+%d+%d" % (parent.winfo_rootx()+50, parent.winfo_rooty()+50)) self.initial_focus.focus_set() self.wait_window(self) def body(self, master): Label(master, text="User:").grid(row=0, sticky=W) Label(master, text="Group:").grid(row=1, sticky=W) Label(master, text="From IP:").grid(row=2, sticky=W) self.euser = Entry(master, width=20) self.egroup = Entry(master, width=20) self.eip = Entry(master, width=20) self.euser.insert(0,self.initval[0]) self.egroup.insert(0,self.initval[1]) self.eip.insert(0,self.initval[2]) self.euser.grid(row=0, column=1, columnspan=3, sticky=W) self.egroup.grid(row=1, column=1, columnspan=3, sticky=W) self.eip.grid(row=2, column=1, columnspan=3, sticky=W) self.pathb = Button(master, text="path:", relief=FLAT, command=self.pickpath) self.pathb.grid(row=3, sticky=W) self.pathv = StringVar() self.pathv.set(self.initval[3]) self.epathb = Button(master, textvariable=self.pathv, #relief=FLAT, command=self.pickpath) self.epathb.grid(row=3, column=1, columnspan=4, sticky=W) Label(master, text="Allow these commands:").grid(row=4, sticky=W, columnspan=4) LIST = ["cwd", "list", "nlst"] GET = ["retr", "size", "mdtm"] READ = LIST+GET self.READ=READ PUT = ["stor", "appe", "mkd"] DELETE = ["dele", "rmd"] WRITE = PUT+DELETE self.WRITE=WRITE ALL = READ+WRITE+["site"] self.ALL=ALL keys = ALL self.allowed = {} for i in range(len(keys)): v = IntVar() c = Checkbutton(master, text=keys[i], anchor=W, variable=v, indicatoron=1) c.grid(row=i/4+6, column=i%4, sticky=W) v.set(keys[i] in self.initval[4]) self.allowed[keys[i]] = c, v lastrow = len(keys)/4+7 self.bra = Button(master, text="READ", command=self.setREADallowed ) self.bra.grid(row=lastrow, column=0) self.bwa = Button(master, text="WRITE", command=self.setWRITEallowed ) self.bwa.grid(row=lastrow, column=1) self.bca = Button(master, text="CLEAR", command=self.setCLEARallowed ) self.bca.grid(row=lastrow, column=2) lastrow = lastrow+1 Label(master, text="----").grid(row=lastrow, sticky=W) lastrow = lastrow+1 Label(master, text="Deny these commands:").grid(row=lastrow, sticky=W, columnspan=4) self.denied = {} for i in range(len(keys)): v = IntVar() c = Checkbutton(master, text=keys[i], anchor=W, variable=v) c.grid(row=i/4+lastrow+1, column=i%4, sticky=W) v.set(keys[i] in self.initval[5]) self.denied[keys[i]] = c, v lastrow = len(keys)/4+lastrow+1 self.brd = Button(master, text="READ", command=self.setREADdenied ) self.brd.grid(row=lastrow, column=0) self.bwd = Button(master, text="WRITE", command=self.setWRITEdenied ) self.bwd.grid(row=lastrow, column=1) self.bcd = Button(master, text="CLEAR", command=self.setCLEARdenied ) self.bcd.grid(row=lastrow, column=2) def setREADallowed(self): for i in self.READ: self.allowed[i][1].set(1) def setWRITEallowed(self): for i in self.WRITE: self.allowed[i][1].set(1) def setCLEARallowed(self): for i in self.ALL: self.allowed[i][1].set(0) def setREADdenied(self): for i in self.READ: self.denied[i][1].set(1) def setWRITEdenied(self): for i in self.WRITE: self.denied[i][1].set(1) def setCLEARdenied(self): for i in self.ALL: self.denied[i][1].set(0) def validate(self): allowed = [] for i in self.allowed.keys(): if self.allowed[i][1].get(): allowed.append(i) denied = [] for i in self.denied.keys(): if self.denied[i][1].get(): denied.append(i) self.result = (self.euser.get(), self.egroup.get(), self.eip.get(), self.pathv.get(), allowed, denied) return 1 def pickpath(self): pd = FileDialog.FileDialog(self.parent, title="Path") path = pd.go(dir_or_file="") if path: self.pathv.set(path) class App(TkinterConfigList.App): def askval(self, initval=("*", "*", "*", "/", [], [])): d = PermDialog(self.frame, title="permissions", initval=initval) allowed = [] for i in d.allowed.keys(): if d.allowed[i][1].get(): allowed.append(i) denied = [] for i in d.denied.keys(): if d.denied[i][1].get(): denied.append(i) return d.result def stringify(self, tup): return tup[0]+":"+tup[1]+":"+tup[2]+":"+tup[3] def prepare_for_save(self): stream = StringIO.StringIO() pprint.pprint(self.list, stream) s = stream.getvalue() stream.close() return """ LIST = [ "cwd", "list", "nlst"] GET = ["retr", "size", "mdtm"] READ = LIST+GET PUT = ["stor", "appe", "mkd"] DELETE = ["dele", "rmd"] WRITE = PUT+DELETE ALL = READ+WRITE+["site"] acllist = %s\n """ % s TkinterConfigList.go(Frame=App, title="configure access", list=acllist, default_config_file=config_file, hf="perm_acl_README.txt") pyftpd-0.8.5+nmu1/auth_PAM_module.py0000644000000000000000000000406307243035152014155 0ustar import PAM, pwd, grp, os from utils import * from debug import debug from auth_PAM_config import * def got_user(username, session, sessions): try: group = grp.getgrgid(pwd.getpwnam(username)[3])[0] except KeyError: group = "" return 331, "Password? Password?? Password!", username, group, 0, 1 def got_pass(username, password, session, sessions): if not username: return "503 Login with USER first.", 0, 0 def pam_conv(auth, query_list, password=password): resp = [] for i in range(len(query_list)): query, t = query_list[i] if t == PAM.PAM_PROMPT_ECHO_ON: val = password resp.append((val, 0)) elif t == PAM.PAM_PROMPT_ECHO_OFF: val = password resp.append((val, 0)) elif t == PAM.PAM_PROMPT_ERROR_MSG or t == PAM.PAM_PROMPT_TEXT_INFO: print query resp.append(('', 0)); else: return None return resp user = username try: group = grp.getgrgid(pwd.getpwnam(username)[3])[0] uid, gid = user, group except KeyError: uid, gid = "nobody", "nogroup" auth = PAM.pam() auth.start(service) auth.set_item(PAM.PAM_USER, user) auth.set_item(PAM.PAM_CONV, pam_conv) try: auth.authenticate() auth.acct_mgmt() except PAM.error, resp: try: os.setgid(grp.getgrnam(gid)[2]) os.setuid(pwd.getpwnam(uid)[2]) except: debug("Setuid failed") return 530, "Sorry, having problems with the server...", 0, 0 return 530, "Wrong: %s" % resp, 0, 1 except: log("PAM Internal error") return 530, "Wrong: internal error", 0, 0 else: try: os.setgid(grp.getgrnam(gid)[2]) os.setuid(pwd.getpwnam(uid)[2]) except: debug("Setuid failed") return 530, "Sorry, having problems with the server...", 0, 0 return 230, "OK, logged in.", 1, 1 pyftpd-0.8.5+nmu1/log_simple_config.py0000644000000000000000000000002611405441560014623 0ustar logfile = "/dev/null" pyftpd-0.8.5+nmu1/conf_limit.py0000755000000000000000000000216610524174711013303 0ustar #!/usr/bin/python from utils import isdir limitlist = [] try: from limit_config import * except: pass from Tkinter import * import string, pprint, os, StringIO import tkSimpleDialog, tkMessageBox, tkFileDialog, FileDialog import TkinterConfigList config_file = "limit_config.py" class App(TkinterConfigList.App): def askval(self, initval=("anonymous", 10)): "ask for the user and path" u = tkSimpleDialog.askstring("username", "username", initialvalue=initval[0]) if u == None: return None val = tkSimpleDialog.askinteger("limit", "Maximum number of connections:", initialvalue=initval[1]) if val == None: return None return u, val def stringify(self, tup): return tup[0]+" : "+str(tup[1]) def prepare_for_save(self): stream = StringIO.StringIO() pprint.pprint(self.list, stream) s = stream.getvalue() stream.close() return "limitlist = "+s+"\n" TkinterConfigList.go(Frame=App, title="configure limits", list=limitlist, default_config_file=config_file, hf="limit_README.txt") pyftpd-0.8.5+nmu1/fs_base_README.txt0000644000000000000000000000040607174614156013767 0ustar MODULE NAME fs_base_module - template for filesystem modules. OPTIONS none DESCRIPTION This module is provided as teplate for other filesystem modules. Do not include this module directly. SEE ALSO fs_real_module, fs_chroot_module pyftpd-0.8.5+nmu1/speed_README.txt0000644000000000000000000000117107167351336013465 0ustar MODULE NAME speed_module - per user configuration of maximum allowed transfer speed OPTIONS none DESCRIPTION if user matches the pattern, his maximum allowed trasfer speed is set to the value of second field for downloading files, and to the third field for uploading. You can use * to match all users, or as mask, e.g. ab* matches all users beginning with ab. Order is important, first use most general patterns, then specific ones (in other words, put default limits for * (all not explicitly mentioned users) at the first line). If you set 0. as speed limit, it turns limiting off. pyftpd-0.8.5+nmu1/fs_webdav_module.py0000644000000000000000000000302107642310147014453 0ustar import ftplib, os, posixpath, davlib import fs_base_module class FileObject(fs_base_module.FileObject): def __init__(self, name, mode="r"): self.davhost = 'webtest-upload.home.nl' self.davport = 80 self.name = name self.dav = davlib.DAV(self.davhost, self.davport) self.fo = self.dav def close(self): self.fo.close() def read(self, size=None): if size==None: return self.fo.read() else: return self.fo.read(size) def write(self, s): return 0 def seek(self, offset, whence=0): self.fo.close() assert whence == 0 self.fo = os.popen("curl -s -C %i %s" % (offset, self.name)) class FileSystem(fs_base_module.FileSystem): def __init__(self, session): global ftp self.pardir = ".." def isdir(self, d): return 0 def islink(self, d): return 0 def isfile(self, f): return 1 def readlink(self, d): return "" def isexec(self, d): return 1 def chdir(self, p): return 0 def stat(self, f): return 10*(0,) def listdir(self, p): return [] def open(self, f, mode="r"): return FileObject(self.v2fs(f), mode) def rmdir(self, d): return 0 def mkdir(self, d): return 0 def unlink(self, p): return 0 class Session: def cmd_size(self, f): self.reply("500 Not available here") pyftpd-0.8.5+nmu1/pyftpd.py0000755000000000000000000006120211405434236012462 0ustar #!/usr/bin/python # maybe you need to change the above line import sys, socket, string, time, types from stat import * import timeout_socket import SocketServer import posixpath #import new from utils import * from config import * from debug import * # characters which must not appear in filenames filename_deniedchars = map(chr, range(32)) class SessionExit(Exception): pass class BasicSession: def __init__(self, rfile, wfile, client_address): self.user = "" self.group = "" self.logged = 0 self.curcmd = "just connected" self.exit_immediately = 0 self.pendingconn = 0 # if there is an open data connection - for ABOR self.last_cmd_time = self.session_create_time = time.time() self.rfile = rfile self.wfile = wfile self.restpos = 0 self.passive = 0 self.create_datasock = self.create_nonpasv_datasock self.ip = client_address[0] self.dataport = None opendebug("pyftpd["+self.ip+"]") self.replymessage(220, initial_msg) self.cwd = initial_wd self.limit_retr_speed = 0. self.limit_stor_speed = 0. self.permcheck = permcheck log.log(time.time(), "", self.ip, "CONNECT", -1) self.cmddict = { "quit": self.cmd_quit, "syst": self.cmd_syst, "user": self.cmd_user, "pass": self.cmd_pass, "port": self.cmd_port, "stor": self.cmd_stor, "appe": self.cmd_appe, "dele": self.cmd_dele, "mkd" : self.cmd_mkd, "rmd" : self.cmd_rmd, "retr": self.cmd_retr, "rest": self.cmd_rest, "size": self.cmd_size, "list": self.cmd_list, "nlst": self.cmd_nlst, "pasv": self.cmd_pasv, "pwd" : self.cmd_pwd, "cwd" : self.cmd_cwd, "cdup": self.cmd_cdup, "site": self.cmd_site, "abor": self.cmd_abor, "type": self.cmd_dummy } def joinpath(self, b, a): # join b and a, if a is absolute path, return just a r = posixpath.normpath(posixpath.join(b, a)) # make sure nobody can cheat with leading .. if r[0]<>"/": # something wrong return "" r = string.replace(r, "/../", "") return r def reply(self, x): self.wfile.write(x + "\r\n") self.wfile.flush() debug("---> "+repr(x)) def replymessage(self, n, x): # reply to the client, x is possibly tuple of lines if type(x) == types.StringType: self.reply(`n`+" "+x) else: for i in x[:-1]: self.reply(`n`+"-"+i) self.reply(`n`+" "+x[-1]) def create_nonpasv_datasock(self): if not self.dataport: self.reply("425 what about a PORT command?") return try: sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM) self.sock = t_socket(sock=sock, timeout=timeout_data) self.sock.connect((self.ip, self.dataport)) self.reply("150 dataconnection is up!") return 1 except socket.error: try: self.reply("425 your dataport sucks big time!") return None except: pass return None except timeout_socket.Timeout: return None def create_pasv_datasock(self): try: self.sock = t_socket(sock=self.sock, timeout=timeout_data) conn, addr = self.sock.accept() self.sock = t_socket(sock=conn, timeout=timeout_data) self.reply("150 dataconnection is up!") self.create_datasock = self.create_nonpasv_datasock return 1 except "socket.error": self.reply("425 cannot create socket") return None def close_datasock(self): self.sock.close() def cmd_quit(self,_): self.reply("221 Have a good one!") raise SessionExit def cmd_abor(self,_): if self.pendingconn and threading: self.abort_received = 1 while self.pendingconn: time.sleep(0.1) # prevent from taking up 100% CPU self.reply("226 Aborted") return def cmd_user(self, username): n, r, self.user, self.group, self.logged = got_user(username, self, sessions) self.replymessage(n, r) if self.logged: self.filesys = FileSystem(self) def cmd_pass(self, password): n, r, self.logged = got_pass(self.user, password, self, sessions) self.replymessage(n, r) if self.logged: self.filesys = FileSystem(self) def cmd_dummy(self, _): self.reply("200 OK (in other words: ignored)") def cmd_syst(self, _): self.reply("215 UNIX Type: L8") def cmd_pwd(self, _): self.reply('257 "%s" is where you are' % (self.cwd)) def cmd_cdup(self,_): self.cmd_cwd("..") def cmd_cwd(self, path): if path: path = self.joinpath(self.cwd, path) perm = self.logged and permcheck(path, self.user, self.group, self, "cwd") if not perm: self.reply("550 You are not allowed to") return try: self.filesys.chdir(path) except OSError: self.reply("550 You are not allowed to") return self.cwd = path self.reply("250 Ok, going there") def cmd_site(self, command): perm = self.logged and permcheck(command, self.user, self.group, self, "site") if not perm: self.reply("550 You are not allowed to") return c = string.split(command) cmd, arg = string.lower(c[0]), c[1:] if cmd == "ps": sl = [] for i in sessions.keys(): cs = sessions[i] sl.append(" %i %s[%s]@%s %% %s" % (i, cs.user, cs.group, cs.ip, cs.curcmd)) sl.append("TOTAL %i" % len(sessions)) self.replymessage(250,sl) return elif cmd == "shutdown": # does not work self.reply("250 Oh dear, shutting down server") sys.exit(0) elif cmd == "kill": try: pid2kill = int(arg[0]) except: self.reply("400 PID error") return if sessions.has_key(pid2kill): sessions[pid2kill].exit_immediately=1 else: self.reply("400 No such PID") return else: self.reply("500 Unknown SITE command") return self.reply("250 Ok, done.") def cmd_noop(self,_): self.reply("200 NOOPing") def cmd_pasv(self, _): FTP_DATA_BOTTOM = 40000 FTP_DATA_TOP = 44999 for port in xrange(FTP_DATA_BOTTOM, FTP_DATA_TOP): try: self.sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM) self.sock.bind(('', port)) break except socket.error: pass else: self.reply("425 Cannot be passive") return self.sock.listen(1) adr, self.dataport = self.sock.getsockname() #if not adr or adr == '0.0.0.0': # adr = socket.gethostname() adr = socket.gethostbyname(socket.gethostname()) adr = string.replace(adr, ".", ",") porthi, portlo = self.dataport/256, self.dataport%256 self.passive = 1 self.create_datasock = self.create_pasv_datasock self.reply("227 Entering Passive Mode (%s,%i,%i)" % (adr, porthi, portlo)) def cmd_port(self, port_id): numstr = filter(lambda x: x in "0123456789,", port_id) parts = string.split(numstr,",") try: hi = int(parts[-2]) lo = int(parts[-1]) for v in [hi,lo]: if v < 0 or v > 255: raise ValueError except IndexError: self.reply("501 are you a hacker?") return except ValueError: self.reply("501 looks like nonsense to me...") return self.dataport = (hi << 8) + lo self.reply("230 Port is " + str(self.dataport)+ " (am ignoring specified IP for security)") def stor_or_appe(self, filename, comm, mode): #if filename[0] == ".": # self.reply("553 No dot files please!") # return if filter(lambda x: x in filename_deniedchars, filename): self.reply("553 Bad characters in filename") return path = self.joinpath(self.cwd, filename) perm = self.logged and permcheck(path, self.user, self.group, self, comm) if not perm: self.reply("530 Permission denied") return r = "226 Phew, upload successfully completed" try: f = self.filesys.open(path, mode) except IOError: self.reply("553 File creation failed!") return if not self.create_datasock(): f.close() return self.abort_received = 0 self.pendingconn = 1 try: while 1: if self.abort_received: r = "426 Aborted" break if self.limit_stor_speed: timer = time.time() s = self.sock.recv(rbufsize) if len(s) == 0: break dur = time.time()-timer #speed = len(s)/dur #if speed>self.limit_stor_speed: if dur*self.limit_stor_speedself.limit_retr_speed: if dur*self.limit_retr_speed= 32 and x not in "\377\364\362",cmd) lcmd2 = string.split(cmd2, None, 1) or [""] command = string.lower(lcmd2[0]) if len(lcmd2) > 1: args = string.strip(lcmd2[1]) else: args = "" # was None, but it was breaking some commands u = self.user or "-" ip = self.ip or "-" c = cmd2 log.log(time.time(), u, ip, c, 0) if self.cmddict.has_key(command): self.curcmd = c if threading: while self.pendingconn and command<>"abor": pass self.cmddict[command](args) else: self.reply("500 I'm gonna ignore this command... maybe later...") log.log(time.time(), u, ip, c, 1) def loop(self): while not self.exit_immediately: try: self.last_cmd_time = time.time() l = self.rfile.readline() except timeout_socket.Timeout: if self.pendingconn: # if there is an ongoing connection, do not timeout for commands # there is a subtle race here - if data connection ends while readline() # is near timeout, client will get 421 Timeout as soon as the data # connections finishes, not having a chance to enter control # commands. But there is not much to be done about it. continue # continue because we do not want to do docmd(), when # readline() timeouted, l is an old value else: try: self.reply("421 timeout") break except (socket.error, timeout_socket.Timeout): break except socket.error: # but if anythin happened with control connection, go out break try: self.docmd(l) except (SessionExit, socket.error): break try: self.close_datasock() # close any transfers except: pass debug("Connection closed.") authmethods = [] permmethods = [] sessionmethods = [] for i in modules: exec("import "+i) methods = dir(eval(i)) if "got_user" in methods: # it is authentification module authmethods.append( (eval(i+".got_user"), eval(i+".got_pass")) ) if "permcheck" in methods: # permission module permmethods.append(eval(i+".permcheck")) if "FileObject" in methods: FileObject = eval(i+".FileObject") if "FileSystem" in methods: FileSystem = eval(i+".FileSystem") if "Session" in methods: sessionmethods.append(i+".Session") if "Log" in methods: Log = eval(i+".Log") sessionmethods.append("BasicSession") #this is ugly ugly UGLY exec("class Session("+string.join(sessionmethods,",")+"):\n pass\n") #sd = {} #Session = new.classobj("Session", tuple(sessionmethods), sd) def permcheck(f, user, group, session, operation): last = 0 for i in permmethods: l, c = i(f, user, group, session, operation) if l == 0: last = 0 elif l == 1: last = 1 if not c: break return last def got_user(username, session, sessions): # session points to Session class last_deny_rt = 500, "", "", "", 0, 0 # response code, response message, user, group, deny_or_grant, continue last_grant_rt = 200, "", "", "", 0, 0 last_rt = 500, "", "", "", 0, 0 last = -1 for i in authmethods: rt = n, r, u, g, l, c = i[0](username, session, sessions) if l == 0: last_deny_rt = rt last = 0 elif l == 1: last_grant_rt = rt last = 1 else: last_rt = rt if not c: break #now last is 1 for grant access, 0 for deny if last == 1: n, r, u, g, l, c = last_grant_rt elif last == 0: n, r, u, g, l, c = last_deny_rt else: n, r, u, g, l, c = last_rt return n, r, u, g, l def got_pass(username, password, session, sessions): last_deny_rt = 500, "", 0, 0 last_grant_rt = 200, "", 0, 0 last_rt = 500, "", 0, 0 last = 0 for i in authmethods: rt = n, r, l, c = i[1](username, password, session, sessions) if l == 0: last_deny_rt = rt last = 0 elif l == 1: last_grant_rt = rt last = 1 else: last_rt = rt if not c: break #now last is 1 for grant access, 0 for deny if last == 1: n, r, l, c = last_grant_rt elif last == 0: n, r, l, c = last_deny_rt else: n, r, l, c = last_rt return n, r, l def watchdog(): while 1: time.sleep(2) cur_time = time.time() print sessions for i in range(len(sessions)): print sessions[i].ip print # the main routine # ---------------- class NoneClass: pass class DummyLock: def acquire(self): pass def release(self): pass try: import thread threading = 1 except: threading = 0 forking = hasattr(os, "fork") sessions = {} t_socket = timeout_socket.timeout_socket lastpid = 1L if threading: thlock = thread.allocate_lock() else: thlock = DummyLock() #thread.start_new_thread(watchdog, ()) #test if we are running from inetd inetd = 1 inetdsock = 0 try: inetdsock = socket.fromfd(sys.stdin.fileno(), socket.AF_INET, socket.SOCK_STREAM) inetdsock.getsockname() except (socket.error, AttributeError): inetd = 0 del inetdsock log = Log() log.openlog() if inetd: client_addr = inetdsock.getpeername() session = Session(sys.stdin, sys.stdout, client_addr) pid = 1L session.pid = pid sessions[pid] = session session.loop() #thlock.acquire() if sessions.has_key(pid): del sessions[pid] #thlock.release() del session sys.exit(0) if threading: mixin = SocketServer.ThreadingMixIn elif forking: mixin = SocketServer.ForkingMixIn else: mixin = NoneClass class MyStreamRequestHandler(SocketServer.StreamRequestHandler): def finish(self): self.wfile.flush() self.wfile.close() self.rfile.close() self.request.close() class TCPServer(mixin, SocketServer.TCPServer): def server_bind(self): """Called by constructor to bind the socket. """ self.socket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1) self.socket.bind(self.server_address) def get_request(self): """Get the request and client address from the socket. """ conn, adr = self.socket.accept() conn = t_socket(sock=conn, timeout=timeout_session) return conn, adr class FTPServer(MyStreamRequestHandler): def handle(self): global lastpid session = Session(self.rfile, self.wfile, self.client_address) thlock.acquire() pid = lastpid = lastpid+1 thlock.release() session.pid = pid sessions[pid] = session session.loop() thlock.acquire() if sessions.has_key(pid): del sessions[pid] thlock.release() del session address = ('', port) server = TCPServer(address, FTPServer) server.serve_forever() pyftpd-0.8.5+nmu1/log_simple_module.py0000644000000000000000000000122411405441535014646 0ustar import time, sys from log_simple_config import * class Log: def openlog(self): global logfile if not logfile: if sys.platform[:3] == 'win': logfile = "c:/pyftpd.log" else: logfile = "/tmp/pyftpd.log" self.logfile = open(logfile, "a") def log(self, t, user, ip, command, boe): # time, who, ip, command, beginning=0 or end=1 line = "%s %s@%s %s %i\n" % (time.ctime(t), user, ip, command, boe) self.logfile.write(line) self.logfile.flush() def closelog(self): self.logfile.close() def __del__(self): self.logfile.close() pyftpd-0.8.5+nmu1/perm_acl_module.py0000644000000000000000000000125107166370516014307 0ustar from utils import myfnmatch from perm_acl_config import * def permcheck(f, user, group, session, operation): accesslist = {} #f = session.v2fs(f) for i in acllist: if ( myfnmatch(user, i[0]) and myfnmatch(group, i[1]) and myfnmatch(session.ip, i[2]) and myfnmatch(f, i[3]+"*") ): for j in i[4]: # append allowed operations accesslist[j] = 1 for j in i[5]: # and then remove denied ones if accesslist.has_key(j): del(accesslist[j]) if accesslist.has_key(operation): return 1, 1 else: return 0, 1 pyftpd-0.8.5+nmu1/bsd_list_module.py0000644000000000000000000000630607402447150014326 0ustar import string from stat import * import time, socket class Session: def cmd_list(self, args): args = string.split(args or "") if not self.create_datasock(): return paths = [] opts = "" for i in args: if i[0] == "-": opts = opts+i[1:] else: paths.append(i) if not paths: paths = [self.cwd] for path in paths: path = self.joinpath(self.cwd, path) perm = self.logged and self.permcheck(path, self.user, self.group, self, "list") if not perm: self.reply("550 Sorry") return cwd = self.cwd try: isf = self.filesys.isfile(path) isd = self.filesys.isdir(path) except OSError: self.reply("550 I cannot") return if isf or (isd and 'd' in opts): lp = [path] elif isd: try: self.filesys.chdir(path) cwd = path lp = self.filesys.listdir(path) except OSError: self.reply("550 I cannot") return #if "a" in opts: # lp.append(self.filesys.curdir) # lp.append(self.filesys.pardir) else: self.reply("550 Uh, what?") return if not '1' in opts: # long format r = [] for l in lp: i = self.joinpath(cwd, l) try: appendix = "" if self.filesys.islink(i) and not "L" in opts: dirflag = "l" appendix = " -> "+self.filesys.readlink(i) elif self.filesys.isdir(i): dirflag = "d" else: dirflag = "-" status = self.filesys.stat(i) size = status[ST_SIZE] user = str(status[ST_UID]) group = str(status[ST_GID]) t = status[ST_CTIME] if t == -1: t = 0 ctime = time.strftime("%b %d %Y",time.gmtime(t)) if self.filesys.isexec(i): perm = "rwx" else: perm = "rw-" rl = "%s%s%s%s 1 %-10s %-10s %10i %s %s%s\r\n" % \ (dirflag, perm,perm,perm, user, group, size, ctime, l, appendix) r.append(rl) except OSError: pass else: r = map(lambda x: "%s\r\n" % x, lp) try: for i in r: self.sock.send(i) except socket.error: break try: self.close_datasock() except socket.error: pass del r # to save memory self.reply("226 Wow, listing done") pyftpd-0.8.5+nmu1/auth_db_README.txt0000644000000000000000000000060507130161342013756 0ustar MODULE NAME auth_db_module - authentificate users against internal database OPTIONS none DESCRIPTION Configure list of users, corresponding groups and passwords. Passwords are kept in md5-hashed form, to enter them use configure_auth_db.py, simple editing the file auth_db_configure.py is not sufficient. SEE ALSO auth_anonymous_module auth_PAM_module pyftpd-0.8.5+nmu1/perm_acl_README.txt0000644000000000000000000000233707167354172014155 0ustar MODULE NAME perm_acl_module - access control list OPTIONS none DESCRIPTION Configure list of users, groups, IP numbers they are logged from and directories/files they are allowed to access. You can use * to match all users, or as mask, e.g. ab* matches all users beginning with ab. The same goes for groups and IP numbers. Order is important, access list is inherited from previous entries, and also from parent directories. Not checked commands are inherited (either from previous matching entry or from parent subdirectory), checked buttons (commands) are alllowed or denied explicitly. First use most general patterns, then specific ones (in other words, put default access for all users, groups, ip numbers and paths at the first line) NOTICE Slash (/) at the end of paths is important. If you allow something for path /home/ftp/, /home/ftp itself is not covered by this. For example, disabling everything for / and enabling LIST for /home/ftp/ will _not_ give you permission to do LIST in /home/ftp. You must enable LIST for /home/ftp (without trailing slash) to achieve what is probably desired behaviour. SEE ALSO perm_dummy_module pyftpd-0.8.5+nmu1/fs_chroot_module.py0000644000000000000000000000126107174620064014507 0ustar from fs_chroot_config import * from utils import myfnmatch import os exec("from "+slave_fs+" import *") slaveFileObject = FileObject slaveFileSystem = FileSystem class FileObject(slaveFileObject): pass class FileSystem(slaveFileSystem): def __init__(self, session): self.slavefs = slaveFileSystem(session) global chrootdir self.chrootdir = "" for i in chrootlist: if myfnmatch(session.user, i[0]): self.chrootdir = i[1] def v2fs(self, f): r = self.slavefs.v2fs(self.chrootdir+f) return r def readlink(self, d): return "" # because the link points to real file.. ugh pyftpd-0.8.5+nmu1/conf_cwd.py0000755000000000000000000000253710524174665012754 0ustar #!/usr/bin/python from utils import isdir, defaultdir cwdlist = [] try: from cwd_config import * except: pass from Tkinter import * import string, pprint, os, StringIO import tkSimpleDialog, tkMessageBox, tkFileDialog, FileDialog import TkinterConfigList config_file = "cwd_config.py" class App(TkinterConfigList.App): def askval(self, initval=("anonymous", defaultdir)): "ask for the user and path" u = tkSimpleDialog.askstring("username", "username", initialvalue=initval[0]) if u == None: return None pd = FileDialog.FileDialog(master=self.frame, title="Initial working directory") path = pd.go(dir_or_file=initval[1]) #path = tkSimpleDialog.askstring("initial cwd", "initial directory", initialvalue=initval[1]) if path == None: return None if os.path.exists(path) and not isdir(path): path = os.path.dirname(path) return u, path def stringify(self, tup): return tup[0]+" : "+tup[1] def prepare_for_save(self): stream = StringIO.StringIO() pprint.pprint(self.list, stream) s = stream.getvalue() stream.close() return "cwdlist = "+s+"\n" TkinterConfigList.go(Frame=App, title="configure cwd", list=cwdlist, default_config_file=config_file, hf="cwd_README.txt") pyftpd-0.8.5+nmu1/iplimit_README.txt0000644000000000000000000000127007167351246014034 0ustar MODULE NAME iplimit_module - limit maximum numbers of connections from one IP number. OPTIONS none DESCRIPTION Configure list of users, corresponding number of maximum connections they are allowed to make from one IP number (which means from one computer, if it were not for firewalls and ftp-proxies :-)) You can use * to match all users, or as mask, e.g. ab* matches all users beginning with ab. Order is important, first use most general patterns, then specific ones (in other words, put limit for * (all users) at the first line) Unlike limit_module, this module does not limit the total number of users. SEE ALSO limit_module pyftpd-0.8.5+nmu1/conf_iplimit.py0000755000000000000000000000227110524174707013636 0ustar #!/usr/bin/python from utils import isdir limitlist = [] try: from iplimit_config import * except: pass from Tkinter import * import string, pprint, os, StringIO import tkSimpleDialog, tkMessageBox, tkFileDialog, FileDialog import TkinterConfigList config_file = "iplimit_config.py" class App(TkinterConfigList.App): def askval(self, initval=("anonymous", 1)): "ask for the user and value" u = tkSimpleDialog.askstring("username", "username", initialvalue=initval[0]) if u == None: return None val = tkSimpleDialog.askinteger("limit", "Maximum number of connections from one IP number:", initialvalue=initval[1]) if val == None: return None return u, val def stringify(self, tup): return tup[0]+" : "+str(tup[1]) def prepare_for_save(self): stream = StringIO.StringIO() pprint.pprint(self.list, stream) s = stream.getvalue() stream.close() return "#user, max nr of connections from one ip\nlimitlist = "+s+"\n" TkinterConfigList.go(Frame=App, title="configure limits", list=limitlist, default_config_file=config_file, hf="iplimit_README.txt") pyftpd-0.8.5+nmu1/debug.py0000644000000000000000000000062707231077444012251 0ustar import os, os.path, fnmatch, sys, string from config import do_debug try: import syslog1 def opendebug(a): return syslog.openlog(a, 0, syslog.LOG_DAEMON) def debug(text): if do_debug: return syslog.syslog(text) closedebug = syslog.closelog except: def opendebug(a): pass def debug(text): print text def closedebug(): pass pyftpd-0.8.5+nmu1/utils.py0000644000000000000000000000155707174623232012324 0ustar import os, os.path, fnmatch, sys, string emulate_posix=0 if sys.platform[:3] == 'win': import posixpath myfnmatch = fnmatch.fnmatch defaultdir = "c:\\" def isdir(path): # kludge to recognize single drive letters as directory # path is already real fs path return os.path.isdir(path) or (len(path)==2 and path[1]==':' and path[0] in string.letters) def isexec(path): # see if path is executable return ( isdir(path) or (os.path.splitext(string.lower(path))[1] in (".exe", ".com", ".bat")) ) else: myfnmatch = fnmatch.fnmatchcase defaultdir = "/" isdir = os.path.isdir def isexec(path): try: r = os.access(path, 1) except: try: r = isdir(path) except: r = 0 return r pyftpd-0.8.5+nmu1/conf_README.txt0000644000000000000000000000462607170130702013304 0ustar NAME conf_configure.py - global pyftpd configuration OPTIONS Debug messages - if this option is selected, all the communication betweed clients and server is logged, using syslog(3) if available, or standard output if not. Initial message - welcome message to be displayed when client connects to the server. Send/Recieve buffer size - size of network buffer used when sendding/receiving files. The bigger it is, the better performance, but it takes that much memory for one connections. If you are on LAN, use value a bit smaller than you network card buffer (to accomodate for the size of TCP/IP and ethernet headers) Timeout data - the maximum time in seconds a data connection can stall for before it is terminated. Timeout session - the maximum time in seconds a control connection can take. In other words, if client is idle for this time, it is disconnected (up- or down-loading a file does not count as being idle) Initial dir - initial directory when users log in. This option is overriden if you use auth_cwd_module. This is virtual directory, e.g. use posix-like path even on Windows if you turn on posix emulation. Port - no comment Emulate posix - on Windows, paths have form drive:\path\file, e.g. c:\temp\file.ext This can be confusing to some ftp clients, so you if you turn this option on, visible paths will look like /drive:/path/file, e.g. /c:/temp/file.ext If you change this option, be sure to change directories in auth_cwd_module and Initial dir above to appropriate forms. Paths in perm_acl_module are always real filesystem paths and are not affected by this. This has no effect on UNIX machines, since those are posix anyway. DESCRIPTION Configure list of pluggable modules. See relevant *_README.txt files for each module, and run configure_*.py for each module, or edit *_config.py configuration file. Include authentification and permition modules. Permission modules - modules responsible for deciding which users have access to which files. Use perm_dummy_module to allow everybody everything, or perm_acl_module for sophisticated access control list. pyftpd-0.8.5+nmu1/TkinterConfigList.py0000644000000000000000000001317110524174623014557 0ustar #!/usr/bin/python from Tkinter import * import string import tkSimpleDialog, tkMessageBox, tkFileDialog, ScrolledText class HelpDialog(tkSimpleDialog.Dialog): def body(self, master): global helpfile # ugly text = ScrolledText.ScrolledText(master) text.pack({'expand': 1, 'fill': 'both'}) # Expand into available space for i in open(helpfile, "r").readlines(): text.insert('end', i) class App(Frame): def __init__(self, master=None, list=[], default_config_file="generic_config.py", hf="generic_README.txt"): global helpfile self.master = master helpfile = hf self.default_config_file=default_config_file self.list=list # list of tuples (user, cwd) self.frame=Frame(master) self.frame.pack() scrollbar = Scrollbar(self.frame, orient=VERTICAL) self.listbox = Listbox(self.frame, width=35, yscrollcommand=scrollbar.set) self.listbox.bind("", self.edit) scrollbar.config(command=self.listbox.yview) scrollbar.pack(side=LEFT, fill=Y) self.listbox.pack(side=LEFT, fill=BOTH, expand=1) for i in self.list: self.listbox.insert(END, self.stringify(i)) self.optb = Button(self.frame, text="Options", width=7, command=self.opt) self.optb.pack(side=TOP) self.saveb = Button(self.frame, text="Save", width=7, command=self.save) self.saveb.pack(side=TOP) self.saveasb = Button(self.frame, text="Save As", width=7, command=self.saveas) self.saveasb.pack(side=TOP) self.addb = Button(self.frame, text="Add", width=7, command=self.add) self.addb.pack(side=TOP) self.copyb = Button(self.frame, text="Copy", width=7, command=self.copy) self.copyb.pack(side=TOP) self.delb = Button(self.frame, text="Delete", width=7, command=self.delete) self.delb.pack(side=TOP) self.editb = Button(self.frame, text="Edit", width=7, command=self.edit) self.editb.pack(side=TOP) self.quitb = Button(self.frame, text="Quit", width=7, cursor="pirate", command=self.quit) self.quitb.pack(side=BOTTOM) self.helpb = Button(self.frame, text="Help", width=7, relief=GROOVE, command=self.help) self.helpb.pack(side=BOTTOM) def opt(self): """to be overriden set different options""" def askval(self, initval="default"): """to be overriden ask for one entry""" u = tkSimpleDialog.askstring("entry", "entry:", initialvalue=initval) return u def stringify(self, entry): """to be overriden change list entry to string for displaying""" return str(entry) def getpos(self): csel = self.listbox.curselection() if csel <> (): return int(csel[0]) else: return None def delete(self): pos = self.getpos() if pos <> None: self.list = self.list[:pos]+self.list[pos+1:] self.listbox.delete(ACTIVE) def prepare_for_save(self): """to be overriden return asciified representation for saving""" return "list = " + repr(self.list) def save(self): self.do_save(fname=self.default_config_file) def do_save(self, fname): if fname: f = open(fname, "w") f.write(self.prepare_for_save()) f.write("\n") f.close() def saveas(self): fname = tkFileDialog.asksaveasfilename(initialfile=self.default_config_file, title="Save as", filetypes=[("configuration files","*_config.py")]) if fname: self.save(fname) def add(self): dr = self.askval() if dr <> None: self.list.append(dr) self.listbox.insert(END, self.stringify(dr)) def copy(self): pos = self.getpos() if pos <> None: self.list.append(self.list[pos]) self.listbox.insert(END, self.stringify(self.list[pos])) def edit(self, event=None): pos = self.getpos() if pos <> None: dr = self.askval(initval = self.list[pos]) if dr <> None: self.list[pos] = dr self.listbox.delete(pos) self.listbox.insert(pos, self.stringify(self.list[pos])) def about(self): tkMessageBox.showinfo("About pyftpd", "pyftpd" + "\n\nby Radovan Garabik") def help(self): h = HelpDialog(self.master) def quit(self): if tkMessageBox.askyesno("Save", "Save changes?"): self.save() self.frame.quit() def go(Frame, title="configure generic", list=[], default_config_file="generic_config.py", hf="generic_README.txt"): root = Tk() app = Frame(root, list, default_config_file, hf) root.title(title) root.mainloop() pyftpd-0.8.5+nmu1/fs_url_README.txt0000644000000000000000000000126007167572156013663 0ustar MODULE NAME fs_url_module - access to URL OPTIONS none DESCRIPTION This module is an example of fs modules flexibility. It provides access to general URL naming scheme, e.g. you can do: get /http://www.playboy.com/babe.jpg from your ftp client, and pyftpd will fetch babe.jpg from www.playboy.com and tranfer it to your client as if it were an ordinary file. Leading / in URL schemes is necessary in order to persuade some over-intelligent ftp clients that it is really a file. This module provides just bare minumum, no directory listing, no transfer resume, no error checking, no nothing. SEE ALSO fs_curl_module pyftpd-0.8.5+nmu1/perm_dummy_README.txt0000644000000000000000000000070107131432062014524 0ustar MODULE NAME perm_dummy_module - dummy permissions, always allow acces OPTIONS none DESCRIPTION always allow access to files. This means that the normal OS permissions apply (everything allowed in Windows 95/98, standard permissions under UNIX) While this module can be sufficient for operating systems with UNIX-like permissions, it is strongly recommended to use perm_acl_module. SEE ALSO perm_acl_module pyftpd-0.8.5+nmu1/conf_auth_db.py0000755000000000000000000000361510524174672013601 0ustar #!/usr/bin/python from utils import isdir passwd = [] try: from auth_db_config import * except: pass from auth_db_module import md5hash import Tkinter from Tkinter import * import string, pprint, os, StringIO import tkSimpleDialog, tkMessageBox, tkFileDialog, FileDialog import TkinterConfigList class _QueryDialog(tkSimpleDialog._QueryDialog): def body(self, master): w = Label(master, text=self.prompt, justify=LEFT) w.grid(row=0, padx=5, sticky=W) self.entry = Entry(master, name="entry", show="*") self.entry.grid(row=1, padx=5, sticky=W+E) if self.initialvalue: self.entry.insert(0, self.initialvalue) self.entry.select_range(0, END) return self.entry class _QueryString(_QueryDialog): def getresult(self): return self.entry.get() def askpass(title, prompt, **kw): d = apply(_QueryString, (title, prompt), kw) return d.result config_file = "auth_db_config.py" class App(TkinterConfigList.App): def askval(self, initval=("user", "users", "")): "ask for the user, group and password" u = tkSimpleDialog.askstring("username", "username", initialvalue=initval[0]) if u == None: return None g = tkSimpleDialog.askstring("group", "group", initialvalue=initval[1]) if g == None: return None p = askpass("password", "password", initialvalue="") if p == None: return None return u, g, md5hash(p) def stringify(self, tup): return tup[0]+":"+tup[1]+":x" def prepare_for_save(self): stream = StringIO.StringIO() pprint.pprint(self.list, stream) s = stream.getvalue() stream.close() return "passwd = "+s+"\n" TkinterConfigList.go(Frame=App, title="configure users", list=passwd, default_config_file=config_file, hf="auth_db_README.txt") pyftpd-0.8.5+nmu1/auth_db_config.py0000644000000000000000000000036711405436344014113 0ustar passwd = [ # commented out by default - use /usr/share/pyftpd/conf_auth_db.py to change/add users #('test', 'test', 'CY9rzUYh03PK4k6DJie09g=='), # ('user', 'users', '7hHLsZBS6AsHqsDKBgwj7g=='), # ('roxon', 'users', 'ItZ2pB7rPmzFV6hrtdnZ7A==') ] pyftpd-0.8.5+nmu1/iplimit_config.py0000644000000000000000000000014307255401354014145 0ustar #user, max nr of connections from one ip limitlist = [('*', 10), ('anonymous', 1), ('roxon', 2)] pyftpd-0.8.5+nmu1/cwd_config.py0000644000000000000000000000005507136055406013256 0ustar cwdlist = [('*', '/'), ('anonymous', '/')] pyftpd-0.8.5+nmu1/limit_module.py0000644000000000000000000000131707167351322013641 0ustar from limit_config import * from utils import myfnmatch def got_user(username, session, sessions): for i in limitlist: if myfnmatch(username, i[0]): cnt = 0 for j in sessions.keys(): # [1] try: cs = sessions[j] # [2] if myfnmatch(username, cs.user): cnt = cnt+1 except KeyError: # if session was deleted between [1] and [2] pass if cnt >= i[1]: return 530, "Only %i users allowed" % i[1] , "", "", 0, 0 return 331, "Give me password", "", "", -1, 1 def got_pass(username, password, session, sessions): return 530, "Sorry", -1, 1 pyftpd-0.8.5+nmu1/auth_anonymous_config.py0000644000000000000000000000015407125714406015552 0ustar welcome_msg = [ "Welcome to the server", "Have a nice time downloading" "", "See you" ] pyftpd-0.8.5+nmu1/debian/0000755000000000000000000000000012165777772012043 5ustar pyftpd-0.8.5+nmu1/debian/pyftpd.sh0000755000000000000000000000006507402445736013677 0ustar #!/bin/sh exec python /usr/share/pyftpd/pyftpd.py & pyftpd-0.8.5+nmu1/debian/compat0000644000000000000000000000000210523655776013234 0ustar 5 pyftpd-0.8.5+nmu1/debian/rules0000755000000000000000000000367012165777745013131 0ustar #!/usr/bin/make -f # Sample debian/rules that uses debhelper. # GNU copyright 1997 by Joey Hess. # # This version is for a hypothetical package that builds an # architecture-dependant package, as well as an architecture-independent # package. # Uncomment this to turn on verbose mode. #export DH_VERBOSE=1 # This has to be exported to make some magic below work. export DH_OPTIONS build: build-stamp build-stamp: dh_testdir # Add here commands to compile the package. #$(MAKE) touch build-stamp clean: dh_testdir dh_testroot rm -f build-stamp # Add here commands to clean up after the build process. #-$(MAKE) clean rm -rf *.pyc dh_clean #install: DH_OPTIONS= install: build dh_testdir dh_testroot dh_clean -k dh_installdirs # Add here commands to install the package into debian/tmp. #$(MAKE) install DESTDIR=`pwd`/debian/tmp mkdir -p debian/pyftpd/usr/share/pyftpd cp *.py debian/pyftpd/usr/share/pyftpd/ mv debian/pyftpd/usr/share/pyftpd/*_config.py debian/pyftpd/etc/pyftpd/ mv debian/pyftpd/usr/share/pyftpd/config.py debian/pyftpd/etc/pyftpd/ cp debian/pyftpd.sh debian/pyftpd/usr/sbin/pyftpd #cp passwd debian/tmp/etc/pyftpd/ # dh_movefiles # Build architecture-independent files here. # Pass -i to all debhelper commands in this target to reduce clutter. binary-indep: DH_OPTIONS=-i binary-indep: build install #binary-arch: build install # Need this version of debhelper for DH_OPTIONS to work. # dh_testversion 1.1.17 dh_testdir dh_testroot # dh_installdebconf dh_installdocs dh_installexamples # dh_installemacsen dh_installpam # dh_installinit dh_installcron dh_installman pyftpd.8 dh_installinfo # dh_undocumented dh_installchangelogs dh_link dh_compress dh_fixperms # You may want to make some executables suid here. # dh_suidregister dh_python2 dh_installdeb # dh_perl dh_gencontrol dh_md5sums dh_builddeb binary: binary-indep binary-arch .PHONY: build clean binary-indep binary-arch binary install pyftpd-0.8.5+nmu1/debian/docs0000644000000000000000000000003107131411620012656 0ustar BUGS NEWS README CREDITS pyftpd-0.8.5+nmu1/debian/control0000644000000000000000000000104512166000043013411 0ustar Source: pyftpd Section: net Priority: extra Maintainer: Radovan Garabík Standards-Version: 3.7.2 Build-Depends: debhelper (>= 5.0.37.2), python (>= 2.6.6-3~) Package: pyftpd Architecture: all Depends: ${python:Depends}, ${misc:Depends} Recommends: python-tk Description: ftp daemon with advanced features Multithreaded ftp daemon written in Python, featuring advanced permission scheme, upload/download speed throttling, GUI configuration, internal database of users and more. Does not need to run as root. pyftpd-0.8.5+nmu1/debian/init0000755000000000000000000000231307130672077012717 0ustar #!/bin/sh # # Start the pyftpd daemon. run_pyftpd=1 pyftpd_options="" PATH=/bin:/usr/bin:/sbin:/usr/sbin DAEMON=/usr/sbin/pyftpd NAME=pyftpd FLAGS="defaults 50" trap "" 1 trap "" 15 test -f $DAEMON || exit 0 case "$1" in start) if [ $run_pyftpd = 1 ] then #update-inetd --disable ftp echo -n "Starting pyftp daemon: " if start-stop-daemon --start --quiet --pidfile /var/run/$NAME.pid \ --exec $DAEMON -- $pyftpd_options then echo "$NAME." else echo fi fi ;; stop) if [ $run_pyftpd = 1 ] then echo -n "Stopping pyftp daemon: " start-stop-daemon --stop --quiet --pidfile /var/run/$NAME.pid fi ;; reload) $0 restart ;; restart) $0 force-reload ;; force-reload) echo -n "Restarting $NAME daemon." /etc/init.d/$NAME stop > /dev/null 2>&1 echo -n "." sleep 1 echo -n "." if start-stop-daemon --start --quiet --pidfile /var/run/$NAME.pid \ --exec $DAEMON -- $pyftpd_options then echo "done." fi ;; *) echo "Usage: /etc/init.d/$NAME {start|stop|reload|restart|force-reload}" exit 1 ;; esac exit 0 pyftpd-0.8.5+nmu1/debian/examples0000644000000000000000000000001507126167014013557 0ustar examples-win pyftpd-0.8.5+nmu1/debian/changelog0000644000000000000000000001047312165777745013722 0ustar pyftpd (0.8.5+nmu1) unstable; urgency=low * Non-maintainer upload. * Convert to dh_python2. (Closes: #616964) -- Andrea Colangelo Wed, 03 Jul 2013 00:14:51 +0200 pyftpd (0.8.5) unstable; urgency=high * get rid of one last forgotten string exception (closes: #585275) * SECURITY: change default configuration - do not include any default users, disable anonymous access (closes: #585776) * SECURITY: change default logging file to /dev/null (closes: #585773) -- Radovan Garabík Mon, 14 Jun 2010 16:09:53 +0200 pyftpd (0.8.4.6) unstable; urgency=low * remove non-free rfc959.txt.gz (closes: #480376) * get rid of string exception -- Radovan Garabík Sun, 27 Jul 2008 14:29:18 +0300 pyftpd (0.8.4.5) unstable; urgency=low * pre-depend on python-central (closes:#408277) -- Radovan Garabík Sun, 30 Sep 2007 17:28:00 +0200 pyftpd (0.8.4.4) unstable; urgency=low * use python-central (closes: #380904) -- Radovan Garabík Mon, 6 Nov 2006 16:54:21 +0100 pyftpd (0.8.4.3) unstable; urgency=low * now REALLY make it work with python2.3. Even if there is no python2.2 installed . (closes: #233195) -- Radovan Garabík Tue, 20 Apr 2004 16:14:45 +0200 pyftpd (0.8.4.2) unstable; urgency=low * modified debian dependencies to work with python2.3 (closes: #207338) * removed python-pam suggestion since it was buggy anyway -- Radovan Garabík Wed, 10 Sep 2003 18:54:05 +0200 pyftpd (0.8.4.1) unstable; urgency=low * NMU * Explicitely use python2.2 (closes: #206649). -- Matthias Klose Tue, 26 Aug 2003 08:02:57 +0200 pyftpd (0.8.4) unstable; urgency=low * the previously mentioned typo is really fixed now :-) thanks to Jarosław Bylina for pointing this out * introduced UTF-8 characters into debian/changelog and debian/control (yeah :-)) -- Radovan Garabík Tue, 1 Jul 2003 13:41:53 +0200 pyftpd (0.8.3) unstable; urgency=low * fixed silly typo causing exception when deleting a directory (closes: #173886) -- Radovan Garabik Wed, 5 Feb 2003 09:54:24 +0100 pyftpd (0.8.2) unstable; urgency=low * upgraded to work with python2.2 (closes: #161558) -- Radovan Garabik Fri, 11 Oct 2002 10:00:00 +0200 pyftpd (0.8.1) unstable; urgency=low * python dependency according to new policy (closes: #119208) * clean bytecompiled modules after purge (closes: #117804) * minor tweaks -- Radovan Garabik Sun, 2 Dec 2001 16:16:28 +0100 pyftpd (0.8) unstable; urgency=low * can run from inetd * modules renamed to saner names * separated filesystem from ftp as standalone module as a result URL- and curl- virtual filesystems appeared * moved logging to separate and configurable modules * modules can overwrite Session class * moved LIST into separate module, which made it configurable -- Radovan Garabik Tue, 3 Oct 2000 17:43:22 +0400 pyftpd (0.7) unstable; urgency=low * unified auth and perm modules * auth_chroot module added * few more try... except clauses around critical places -- Radovan Garabik Sun, 13 Aug 2000 20:55:45 +0200 pyftpd (0.6) unstable; urgency=low * fixed NLST * fixed STOR and APPE (thanks to Remco Gerlich for finding a bug) -- Radovan Garabik Mon, 10 Jul 2000 16:53:14 +0200 pyftpd (0.5) unstable; urgency=low * timeout_socket multiple socket close fixes * documentation enhancement -- Radovan Garabik Fri, 7 Jul 2000 11:42:16 +0200 pyftpd (0.4) unstable; urgency=low * passive mode * posix path emulation for windows * numerous bugfixes * limit upload/download speed * tkinter configuration now finished -- Radovan Garabik Wed, 5 Jul 2000 18:06:17 +0200 pyftpd (0.3) unstable; urgency=low * Initial Release. -- Radovan Garabik Tue, 27 Jun 2000 12:48:41 +0200 Local variables: mode: debian-changelog End: pyftpd-0.8.5+nmu1/debian/dirs0000644000000000000000000000006411405434626012707 0ustar usr/share/pyftpd usr/sbin etc/pyftpd var/log/pyftpd pyftpd-0.8.5+nmu1/debian/README.Debian0000644000000000000000000000124011405437766014071 0ustar based on pyFTPdrop by Marco Amrein. pyftpd is a bit dated package - coming from the days where anonymous ftp was still a widespread way of sharing files. As such, it is included in Debian in hope it still could be useful - it might be removed in the future. Without extensive configuring according to your needs, the package is probably unusable. Note that pyftpd does not run by default - you have to run /usr/sbin/pyftpd under a suitable user (root is strongly not recommended). If you want logging, create the directory /var/log/pyftpd/, writable by the user pyftpd runs as. -- Radovan Garabík Mon, 14 Jun 2010 16:09:53 +0200 pyftpd-0.8.5+nmu1/debian/copyright0000644000000000000000000000026110523655754013764 0ustar This package was based on pyFTPdrop by Marco Amrein. Copyright: GPL, see /usr/share/common-licenses/GPL Radovan Garabík pyftpd-0.8.5+nmu1/debian/links0000644000000000000000000000152207402445156013070 0ustar /etc/pyftpd/auth_PAM_config.py /usr/share/pyftpd/auth_PAM_config.py /etc/pyftpd/auth_anonymous_config.py /usr/share/pyftpd/auth_anonymous_config.py /etc/pyftpd/ban_config.py /usr/share/pyftpd/ban_config.py /etc/pyftpd/cwd_config.py /usr/share/pyftpd/cwd_config.py /etc/pyftpd/auth_db_config.py /usr/share/pyftpd/auth_db_config.py /etc/pyftpd/auth_file_config.py /usr/share/pyftpd/auth_file_config.py /etc/pyftpd/iplimit_config.py /usr/share/pyftpd/iplimit_config.py /etc/pyftpd/limit_config.py /usr/share/pyftpd/limit_config.py /etc/pyftpd/speed_config.py /usr/share/pyftpd/speed_config.py /etc/pyftpd/fs_chroot_config.py /usr/share/pyftpd/fs_chroot_config.py /etc/pyftpd/config.py /usr/share/pyftpd/config.py /etc/pyftpd/perm_acl_config.py /usr/share/pyftpd/perm_acl_config.py /etc/pyftpd/log_simple_config.py /usr/share/pyftpd/log_simple_config.py pyftpd-0.8.5+nmu1/conf_fs_chroot.py0000755000000000000000000000251110524174670014151 0ustar #!/usr/bin/python from utils import isdir, defaultdir chrootlist = [] try: from fs_chroot_config import * except: pass from Tkinter import * import string, pprint, os, StringIO import tkSimpleDialog, tkMessageBox, tkFileDialog, FileDialog import TkinterConfigList config_file = "fs_chroot_config.py" class App(TkinterConfigList.App): def askval(self, initval=("anonymous", defaultdir)): "ask for the user and path" u = tkSimpleDialog.askstring("username", "username", initialvalue=initval[0]) if u == None: return None pd = FileDialog.FileDialog(master=self.frame, title="Chroot directory") path = pd.go(dir_or_file=initval[1]) if path == None: return None if os.path.exists(path) and not isdir(path): path = os.path.dirname(path) return u, path def stringify(self, tup): return tup[0]+" : "+tup[1] def prepare_for_save(self): stream = StringIO.StringIO() pprint.pprint(self.list, stream) s = stream.getvalue() stream.close() return """ slave_fs = "%s" chrootlist = %s """ % (slavefs, s) TkinterConfigList.go(Frame=App, title="configure fs_chroot", list=chrootlist, default_config_file=config_file, hf="fs_chroot_README.txt") pyftpd-0.8.5+nmu1/mp3infor.py0000644000000000000000000002653107014333774012722 0ustar # # Python MPEG Information Module # # (c)Eduardo Roldan, Under the GPL License # e-mail: trazor@adinet.com.uy # # Check the included README.TXT for instructions (no many '#' comments here) import string info = 'v 0.2 291099' mpeg_strings = ['2.5', '', '2', '1'] layers = ['', 'III', 'II', 'I'] bitrates_mpeg1 = [ [], [-1, 32, 40, 48, 56, 64, 80, 96, 112, 128, 160, 192, 224, 256, 320], [-1, 32, 48, 56, 64, 80, 96, 112, 128, 160, 192, 224, 256, 320, 384], [-1, 32, 64, 96, 128, 160, 192, 224, 256, 288, 320, 352, 384, 416, 448] ] # !! -1 = free bitrate bitrates_mpeg2 = [ [], [-1, 8, 16, 24, 32, 40, 48, 56, 64, 80, 96, 112, 128, 144, 160], [-1, 8, 16, 24, 32, 40, 48, 56, 64, 80, 96, 112, 128, 144, 160], [-1, 32, 48, 56, 64, 80, 96, 112, 128, 144, 160, 176, 192, 224, 256] ] frequency_strings = [ [11025, 12000, 8000], [], [22050, 24000, 16000,], [44100, 48000, 32000,] ] channel_mode_strings= ['Stereo', 'Joint Stereo', 'Dual Channel', 'Single Channel'] InvalidHeaderError = 'Can\'t read an invalid header' NoHeaderError = 'No header present' NoTagError = 'No id3 tag present' #Extracted from mpg123 0.59q genre_list = [ "Blues", "Classic Rock", "Country", "Dance", "Disco", "Funk", "Grunge", "Hip-Hop", "Jazz", "Metal", "New Age", "Oldies", "Other", "Pop", "R&B", "Rap", "Reggae", "Rock", "Techno", "Industrial", "Alternative", "Ska", "Death Metal", "Pranks", "Soundtrack", "Euro-Techno", "Ambient", "Trip-Hop", "Vocal", "Jazz+Funk", "Fusion", "Trance", "Classical", "Instrumental", "Acid", "House", "Game", "Sound Clip", "Gospel", "Noise", "Alt", "Bass", "Soul", "Punk", "Space", "Meditative", "Instrumental Pop", "Instrumental Rock", "Ethnic", "Gothic", "Darkwave", "Techno-Industrial", "Electronic", "Pop-Folk", "Eurodance", "Dream", "Southern Rock", "Comedy", "Cult", "Gangsta Rap", "Top 40", "Christian Rap", "Pop/Funk", "Jungle", "Native American", "Cabaret", "New Wave", "Psychedelic", "Rave", "Showtunes", "Trailer", "Lo-Fi", "Tribal", "Acid Punk", "Acid Jazz", "Polka", "Retro", "Musical", "Rock & Roll", "Hard Rock", "Folk", "Folk/Rock", "National Folk", "Swing", "Fast-Fusion", "Bebob", "Latin", "Revival", "Celtic", "Bluegrass", "Avantgarde", "Gothic Rock", "Progressive Rock", "Psychedelic Rock", "Symphonic Rock", "Slow Rock", "Big Band", "Chorus", "Easy Listening", "Acoustic", "Humour", "Speech", "Chanson", "Opera", "Chamber Music", "Sonata", "Symphony", "Booty Bass", "Primus", "Porn Groove", "Satire", "Slow Jam", "Club", "Tango", "Samba", "Folklore", "Ballad", "Power Ballad", "Rhythmic Soul", "Freestyle", "Duet", "Punk Rock", "Drum Solo", "A Cappella", "Euro-House", "Dance Hall", "Goa", "Drum & Bass", "Club-House", "Hardcore", "Terror", "Indie", "BritPop", "Negerpunk", "Polsk Punk", "Beat", "Christian Gangsta Rap", "Heavy Metal", "Black Metal", "Crossover", "Contemporary Christian", "Christian Rock", "Merengue", "Salsa", "Thrash Metal", "Anime", "JPop", "Synthpop" ] class open_mp3: genre_no = 0 bitrate = 0 ### Id3 ### def __init__(self, file_to_open): if type(file_to_open) == type(''): self.file = open(file_to_open) else: self.file = file_to_open self.file.seek(0, 2) self.filesize = self.file.tell() def __del__(self): self.file.close() def __call__(self): self.sync_read_header(0, 1024) mpeg = self.get_mpeg_version() layer = self.get_layer() protection = self.get_protection() bitrate = `self.get_bitrate()` frequency = `self.get_frequency()` channel_mode = self.get_channel_mode() copyright = self.get_copyright() original = self.get_original() length = string.zfill(self.get_length()[1], 2) + ':' + string.zfill(self.get_length()[2], 2) framelength = `self.get_framelength()` try: self.read_tag() genre = self.get_genre() songname = self.songname artist = self.artist album = self.album year = self.year comment = self.comment except NoTagError: genre = songname = artist = album = year = comment = '' return {'mpeg':mpeg, 'layer':layer, 'protection':protection, 'bitrate':bitrate, 'frequency':frequency, 'channel_mode':channel_mode, 'copyright':copyright, 'original':original, 'length':length, 'framelength':framelength, 'genre':genre, 'songname':songname, 'artist':artist, 'album':album, 'year':year, 'comment':comment, 'filesize':self.filesize} def read_tag(self): if self.filesize <= 128: raise NoTagError self.file.seek(-128, 2) if self.file.read(3) == 'TAG': self.songname = self.file.read(30) self.artist = self.file.read(30) self.album = self.file.read(30) self.year = self.file.read(4) self.comment = self.file.read(30) self.genre_no = ord(self.file.read(1)) return 1 else: raise NoTagError def get_genre(self): try: return genre_list[self.genre_no] except IndexError: return 'Unknown' ### Header ### def sync_read_header(self, offset = 0, depth = 0): if depth == 0 or depth > self.filesize: depth = self.filesize self.file.seek(offset, 0) while 1: while self.file.tell() < depth: if ord(self.file.read(1)) != 0xff: break second_byte = ord(self.file.read(1)) if second_byte >> 5 != 0x07: self.file.seek(-1, 1) break self.sync_byte = 255 self.mpeg_version = (second_byte & 0x18) >> 3 # B self.layer = (second_byte & 0x06) >> 1 # C third_byte = ord(self.file.read(1)) self.bitrate = (third_byte & 0xf0) >> 4 # E self.frequency = (third_byte & 0x0c) >> 2 # F self.padding = (third_byte & 0x02) >> 1 # G fourth_byte = ord(self.file.read(1)) self.emphasis = (fourth_byte & 0x03) # M try: self.check_header() except InvalidHeaderError: self.file.seek(-3, 1) break ctuple1 = self.mpeg_version, self.layer, self.frequency, self.emphasis step_1_ = self.get_framelength() self.file.seek(step_1_-4, 1) try: self.read_header(self.file.tell()) except InvalidHeaderError: self.file.seek(-(step_1_+3), 1) break ctuple2 = self.mpeg_version, self.layer, self.frequency, self.emphasis if ctuple1 == ctuple2: return self.file.tell() - (step_1_+4) else: self.file.seek(-(step_1_+3), 1) break else: raise NoHeaderError def read_header(self, offset = 0): # AAAAAAAA AAABBCCD EEEEFFGH IIJJKLMM self.file.seek(offset, 0) self.sync_byte = ord(self.file.read(1)) second_byte = ord(self.file.read(1)) self.mpeg_version = (second_byte & 0x18) >> 3 # B self.layer = (second_byte & 0x06) >> 1 # C self.protection = (second_byte & 0x01) # D third_byte = ord(self.file.read(1)) self.bitrate = (third_byte & 0xf0) >> 4 # E self.frequency = (third_byte & 0x0c) >> 2 # F self.padding = (third_byte & 0x02) >> 1 # G self.private = (third_byte & 0x01) # H fourth_byte = ord(self.file.read(1)) self.channel_mode = (fourth_byte & 0xc0) >> 6 # I self.mode_ext = (fourth_byte & 0x30) >> 4 # J self.copyright = (fourth_byte & 0x08) >> 3 # K self.original = (fourth_byte & 0x04) >> 2 # L self.emphasis = (fourth_byte & 0x03) # M self.check_header() return 1 def check_header(self): if self.sync_byte != 255 or self.mpeg_version == 1 or self.layer == 0\ or self.bitrate == 15 or self.frequency == 3 or self.emphasis == 2 : raise InvalidHeaderError def get_mpeg_version(self): return mpeg_strings[self.mpeg_version] def get_layer(self): return layers[self.layer] def get_protection(self): if self.protection: return 'No' else: return 'Yes' def get_bitrate(self): if self.mpeg_version == 3: return bitrates_mpeg1[self.layer][self.bitrate] elif self.mpeg_version == 2 or self.mpeg_version == 0: return bitrates_mpeg2[self.layer][self.bitrate] def get_frequency(self): return frequency_strings[self.mpeg_version][self.frequency] def get_channel_mode(self): return channel_mode_strings[self.channel_mode] def get_copyright(self): if self.copyright: return 'Yes' else: return 'No' def get_original(self): if self.original: return 'Yes' else: return 'No' def get_length(self): if self.bitrate == 0: return 0, 0, 0 else: const = self.get_bitrate() * 125 seconds = self.filesize / const minutes = seconds / 60 seconds_part = seconds % 60 return seconds, minutes, seconds_part def get_framelength(self): if self.layer == 1 or self.layer == 2: return 144 * self.get_bitrate() * 1000 / self.get_frequency() + self.padding else: return 12 * self.get_bitrate() * 1000 / self.get_frequency() + self.padding pyftpd-0.8.5+nmu1/perm_acl_config.py0000644000000000000000000000203107231074475014262 0ustar LIST = [ "cwd", "list", "nlst"] GET = ["retr", "size", "mdtm"] READ = LIST+GET PUT = ["stor", "appe", "mkd"] DELETE = ["dele", "rmd"] WRITE = PUT+DELETE ALL = READ+WRITE+["site"] acllist = [('*', '*', '*', '/', ['cwd', 'list', 'nlst', 'retr', 'size', 'mdtm'], []), ('*', '*', '*', '/etc', ['appe', 'retr'], ['size', 'mdtm', 'retr']), ('*', '*', '*', '/tmp', ['stor', 'appe', 'mkd', 'dele', 'rmd'], []), ('*', '*', '*', '/home', ['stor', 'appe', 'mkd', 'dele', 'rmd'], []), ('anonymous', '*', '*', '/', [], ['cwd', 'list', 'nlst', 'retr', 'size', 'mdtm', 'stor', 'appe', 'mkd', 'dele', 'rmd']), ('anonymous', '*', '*', '/', ['cwd', 'list', 'nlst', 'retr', 'size', 'mdtm'], []), ('anonymous', '*', '*', '/home/ftp/bin', [], ['size', 'mdtm', 'retr']), ('anonymous', '*', '*', '', ['site'], []), ('anonymous', '*', '*', '/incoming', ['stor', 'appe', 'mkd'], []), ('roxon', '*', '*', '/', ['size', 'mdtm', 'cwd', 'nlst', 'retr', 'list'], ['appe', 'rmd', 'stor', 'dele', 'mkd'])] pyftpd-0.8.5+nmu1/ban_module.py0000644000000000000000000000052107167351154013262 0ustar from utils import myfnmatch from ban_config import * def got_user(username, session, sessions): for i in banlist: if myfnmatch(session.ip, i): return 530, banmsg, "", "", 0, 0 return 331, "Give me password", "", "", -1, 1 def got_pass(username, password, session, sessions): return 530, "Sorry", -1, 1