Start to move m_lib to git.
--- /dev/null
+ defenc.py - get default encoding.
+
+ flog.py - simple file logger.
+
+ lazy/ - lazy evaluation modules - lazy dictionary and lazy import.
+
+ mcrypt.py - crypt module supplement function gen_salt().
+
+ metaclasses.py - borrowed from Michele Simionato (mis6@pitt.edu)
+ to solve "TypeError: metatype conflict among bases".
+
+ md5wrapper.py - just an MD5 wrapper.
+
+ m_path.py - simple convenient get_homedir().
+
+ m_shutil.py - additional shell utilities (currently only mkhier
+ function).
+
+ net/ftp/ - modules related to FTP - ftpparse (pure-python parser of LIST
+ command output) and ftpscan - recursive scanner of FTP directories.
+
+ net/sms.py - Send SMS to subscribers of Moscow operators (Beeline, MTS,
+ Megafone) using their Web or SMTP gateways.
+
+ net/www/ - modules related to Web/HTTP/HTML/XML/DTML/etc.
+
+ opdate.py - additional date/time manipulation routines
+ In this module Date is a number of days since 1/1/1600 (up to 31 Dec 3999)
+ I am not sure about how easy it might be to extend the modules beyond
+ these bounds. Time is just a number of seconds since midnight. User can
+ add/subtract dates and times, calculate diffs ("how many days, months
+ and years passed since 21 Dec 1967?") and so on. DateTime <==> UTC (GMT)
+ conversion routines provided, too.
+ This module required flognat's strptime.py.
+
+ opstring.py - additional string manipulation routines
+ (character padding, encoding conversions).
+
+ rus/ - work with russian cyrillic - convert text to/from translit.
+
+ tty_menu.py - extremely dumb text-mode menues.
--- /dev/null
+"Broytman Library for Python, Copyright (C) 1996-2001 PhiloSoft Design"
--- /dev/null
+"Broytman Clock Library for Python, Copyright (C) 1996-2001 PhiloSoft Design"
--- /dev/null
+#! /usr/bin/env python
+"""
+ Test if current interpreter do not have clock() and define it as need
+
+ Written by Broytman, Jul 1997. Copyright (C) 1997 PhiloSoft Design
+"""
+
+import sys, time
+
+print "Testing...",
+sys.stdout.flush()
+
+time.sleep(3)
+print '\n' + " "*len("Testing...") + '\n',
+
+need_clock = time.clock() <> 3
+
+outfile = open("clock.py", 'w')
+
+if need_clock:
+ print "Generaing clock.py with custom clock()"
+ outfile.write("""\"\"\"
+ Define clock() for systems that do not have it
+\"\"\"
+
+from time import time
+_clock = time()
+
+def clock():
+ return int(time() - _clock)
+""")
+else:
+ print "Generaing clock.py with standard clock()"
+ outfile.write("""\"\"\"
+ Define clock() shim
+\"\"\"
+
+from time import clock
+ """)
--- /dev/null
+#! /usr/bin/env python
+"""
+ Define clock() for systems that do not have it
+
+ Written by Broytman, Jul 1997. Copyright (C) 1997 PhiloSoft Design
+"""
+
+from clock import clock
+from time import sleep
+
+print "Testing..."
+sleep(3)
+print "Clock:", clock()
--- /dev/null
+#! /usr/bin/env python
+"""Get default encoding
+
+ Written by Oleg Broytman. Copyright (C) 2007, 2008 PhiloSoft Design.
+"""
+
+__all__ = ['default_encoding']
+
+import sys
+
+try:
+ import locale
+ use_locale = True
+except ImportError:
+ use_locale = False
+
+if use_locale:
+ # Get the default charset.
+ try:
+ lcAll = locale.getdefaultlocale()
+ except locale.Error, err:
+ print >>sys.stderr, "WARNING:", err
+ lcAll = []
+
+ if len(lcAll) == 2:
+ default_encoding = lcAll[1]
+ else:
+ try:
+ default_encoding = locale.getpreferredencoding()
+ except locale.Error, err:
+ print >>sys.stderr, "WARNING:", err
+ default_encoding = sys.getdefaultencoding()
+else:
+ default_encoding = sys.getdefaultencoding()
+
+default_encoding = default_encoding.lower()
+
+if __name__ == "__main__":
+ print default_encoding
--- /dev/null
+#! /usr/bin/env python
+"""File logger
+
+ Written by Broytman, Dec 1997. Copyright (C) 1997 PhiloSoft Design.
+"""
+
+
+from time import *
+
+
+class FLog:
+ def __init__(self, f, overwrite = 0, timeformat = "%a %d %b %Y %T"):
+ if type(f) == type(''): # If f is string - use it as file's name
+ if overwrite:
+ mode = 'w'
+ else:
+ mode = 'a'
+ self.outfile = open(f, mode)
+ else:
+ self.outfile = f # else assume it is opened file (fileobject) or
+ # "compatible" object (must has write() method)
+ self.f = f
+ self.timeformat = timeformat
+
+
+ def __del__(self):
+ self.close()
+
+
+ def close(self):
+ if type(self.f) == type(''): # If f was opened - close it
+ self.outfile.close()
+
+
+ def log(self, str):
+ self.outfile.write("%s %s\n" % (strftime(self.timeformat, localtime(time())), str))
+
+
+ __call__ = log
+
+
+ def flush(self):
+ self.outfile.flush()
+
+
+def makelog(f):
+ return FLog(f, 1)
+
+
+def openlog(f):
+ return FLog(f)
+
+
+def test():
+ log = makelog("test.log")
+ log.log("Log opened")
+ log("Log closed")
+ log.close()
+
+if __name__ == "__main__":
+ test()
--- /dev/null
+"Broytman WWW Library for Python, Copyright (C) 1996-2003 PhiloSoft Design"
+
+
+__all__ = [
+ "dict", "imports"
+]
--- /dev/null
+"Lazy dictionaries calculate self content upon first access"
+
+class LazyDict:
+ "Abstract parent of all lazy dictionaries"
+
+ def _init(self):
+ raise NotImplementedError
+
+ def __getattr__(self, attr):
+ if self.data is None: self._init()
+ return getattr(self.data, attr)
+
+ def __getitem__(self, key):
+ if self.data is None: self._init()
+ return self.data[key]
+
+ def __setitem__(self, key, value):
+ if self.data is None: self._init()
+ self.data[key] = value
+
+
+class LazyDictInitFunc(LazyDict):
+ "Lazy dict that initializes itself by calling supplied init function"
+
+ def __init__(self, init=None, *args, **kw):
+ self.init = init
+ self.data = None
+ self.args = args
+ self.kw = kw
+
+ def _init(self):
+ init = self.init
+ if init is None:
+ data = {} # just a new empty dict
+ else:
+ data = init(*self.args, **self.kw)
+ self.data = data
--- /dev/null
+"Lazy imoport - import the module upon first request"
+
+
+try:
+ from mx.Misc import LazyModule
+
+except ImportError:
+
+ class LazyModule:
+ def __init__(self, module_name, locals, globals=None):
+ self.module = None
+ self.module_name = module_name
+ self.locals = locals
+ if globals is None:
+ globals = locals
+ self.globals = globals
+
+ def __getattr__(self, attr):
+ if self.module is None:
+ self.module = module = __import__(self.module_name, self.globals, self.locals)
+ else:
+ module = self.module
+ return getattr(module, attr)
--- /dev/null
+#! /usr/bin/env python
+
+#
+# useful function(s) for manipulating paths
+#
+# Author: Oleg Broytman <phd@phd.pp.ru>
+# Copyright (C) 2002 PhiloSoft Design
+#
+
+
+_homedir = None
+
+def get_homedir():
+ global _homedir
+ if _homedir is None:
+ import sys, os
+ _homedir = os.path.abspath(os.path.dirname(sys.argv[0]))
+ return _homedir
+
+
+def test():
+ print get_homedir()
+
+
+if __name__ == "__main__":
+ test()
--- /dev/null
+#! /usr/bin/env python
+"""Broytman's shell utilities. Additional to shutil.py (standard library module)
+
+ Written by Broytman, Oct 1997. Copyright (C) 1997 PhiloSoft Design.
+"""
+
+
+import os, string
+
+
+mkhier_error = "m_shutil.mkhier_error"
+
+def mkhier(path): # Python implementation of UNIX' mkdir -p /path/to/dir
+ if os.path.isdir(path):
+ return # It's Ok to have the directory already created
+
+ if os.path.exists(path):
+ raise mkhier_error, "`%s' is file" % path
+
+ list_dirs = string.split(path, os.sep)
+ #print list_dirs
+ for i in range(0, len(list_dirs)):
+ new_path = string.join(list_dirs[0:i+1], os.sep)
+ if (new_path <> '') and (not os.path.exists(new_path)):
+ #print "Making", new_path
+ os.mkdir(new_path)
+
+
+def mcd(dir):
+ os.mkdir(dir)
+ os.chdir(dir)
+
+
+def test():
+ mkhier("I.AM/creating/TEST/dir")
+
+if __name__ == "__main__":
+ test()
--- /dev/null
+#! /usr/bin/env python
+
+#
+# useful function(s) for module crypt
+#
+# Author: Oleg Broytman <phd@phd.pp.ru>
+# Copyright (C) 1998-1999 PhiloSoft Design
+#
+
+
+import random, crypt
+
+
+saltchars = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcedfghijklmnopqrstuvwxyz0123456789./"
+len_salt = len(saltchars)
+
+def gen_salt():
+ """
+ There are some difference among modern unicies. BSD/OS, for example,
+ uses MD5 hash, and ignores salt completely. FreeBSD uses 3 different
+ versions of crypt() - with standard salt, with extended 9-byte salt,
+ and MD5 (again, ignoring salt at all).
+ This function generates salt for standard "Broken DES"-based crypt().
+ """
+ r1 = random.randint(0, len_salt-1)
+ r2 = random.randint(0, len_salt-1)
+ return "%s%s" % (saltchars[r1], saltchars[r2])
+
+
+def test():
+ passwd = raw_input("Enter password: ")
+ salt = gen_salt()
+ encrypted = crypt.crypt(passwd, salt)
+
+ pwd_file = open("test.pwd", 'w')
+ pwd_file.write("%s:%s" % ("user", encrypted))
+ pwd_file.close()
+ print "Password file written"
+
+ import string
+ pwd_file = open("test.pwd", 'r')
+ username, encrypted = string.split(pwd_file.readline()[:-1], ':')
+ pwd_file.close()
+
+ if crypt.crypt(encrypted, encrypted):
+ print "Password verified Ok"
+ else:
+ print "BAD password"
+
+if __name__ == "__main__":
+ test()
--- /dev/null
+#! /usr/bin/env python
+"""User wrapper for md5 builtin object"""
+
+__author__ = "Oleg Broytman <phd@phdru.name>"
+__copyright__ = "Copyright (C) 1997-2012 PhiloSoft Design"
+__license__ = "GNU GPL"
+
+__all__ = ['md5wrapper']
+
+import sys
+if sys.version < '2.5':
+ from md5 import md5
+else:
+ from hashlib import md5
+
+class md5wrapper:
+ def __init__(self, init=None):
+ if init:
+ self._md5 = md5(init)
+ else:
+ self._md5 = md5()
+
+ def update(self, data):
+ self._md5.update(data)
+
+ def digest(self):
+ return self._md5.digest()
+
+ def __repr__(self):
+ str = self.digest()
+ return "%02x"*len(str) % tuple(map(ord, str))
+ # This nice was suggested by Guido
+
+ def md5file(self, f):
+ if type(f) == type(''): # If f is string - use it as file's name
+ infile = open(f, 'r')
+ else:
+ infile = f # else assume it is file or file-like object
+
+ try:
+ while 1:
+ buf = infile.read(16*1024)
+ if not buf: break
+ self.update(buf)
+
+ finally:
+ if type(f) == type(''): # If f was opened - close it
+ infile.close()
+
+if __name__ == "__main__":
+ print "This must print exactly the string"
+ print "Test: 900150983cd24fb0d6963f7d28e17f72"
+ print "Test:", md5wrapper("abc")
--- /dev/null
+#! /usr/bin/env python
+
+
+# From: Michele Simionato (mis6@pitt.edu)
+# http://groups.google.com/groups?selm=2259b0e2.0304250413.4be8ee45%40posting.google.com
+# To solve "TypeError: metatype conflict among bases"
+
+
+def _generatemetaclass(bases, metas):
+ "Internal function called by child"
+
+ if metas == (type,): # trivial metaclass
+ metabases = (); metaname = "_"
+ else: # non-trivial metaclass
+ metabases = metas
+ metaname = "_"+''.join([m.__name__ for m in metas])
+ trivial = lambda m: m in metabases or m is type
+
+ for b in bases:
+ meta_b = type(b)
+ if not trivial(meta_b):
+ metabases += (meta_b,)
+ metaname += meta_b.__name__
+
+ if not metabases: # trivial metabase
+ return type
+ elif len(metabases) == 1: # single metabase
+ return metabases[0]
+ else: # multiple metabases
+ return type(metaname, metabases, {}) # creates a new metaclass
+ #shifting the possible conflict to meta-metaclasses
+
+
+def child(*bases, **options):
+ """Class factory avoiding metatype conflicts: if the base classes have
+ metaclasses conflicting within themselves or with the given metaclass,
+ it automatically generates a compatible metaclass and instantiate the
+ child class from it. The recognized keywords in the option dictionary
+ are name, dic and meta."""
+ name = options.get('name', ''.join([b.__name__ for b in bases])+'_')
+ dic = options.get('dic', {})
+ metas = options.get('metas', (type,))
+ return _generatemetaclass(bases, metas)(name, bases, dic)
+
+
+
+def test():
+ class M_A(type): pass
+ class M_B(type): pass
+ A = M_A('A', (), {})
+ B = M_B('B', (), {})
+
+ try:
+ class C(A, B): pass
+ except TypeError:
+ pass
+ else:
+ raise RuntimeError
+
+ C = child(A, B, name='C')
+
+
+if __name__ == "__main__":
+ test()
--- /dev/null
+"Broytman Net Library for Python, Copyright (C) 1996-2003 PhiloSoft Design"
+
+
+__all__ = [
+ "ftp", "www", "sms"
+]
--- /dev/null
+"Broytman WWW Library for Python, Copyright (C) 1996-2003 PhiloSoft Design"
+
+
+__all__ = [
+ "ftpparse", "ftpscan", "TelnetFTP"
+]
+
+
+from ftplib import FTP
+from telnetlib import IAC
+
+
+class TelnetFTP(FTP):
+ def putline(self, line):
+ line = line.replace(IAC, IAC+IAC)
+ FTP.putline(self, line)
--- /dev/null
+#! /usr/bin/env python
+"""Parse output of FTP LIST command.
+ Pure python implementation.
+ Author: Oleg Broytman <phd@phd.pp.ru>.
+ Copyright (C) 2003-2005 PhiloSoft Design.
+ License: GPL.
+ See http://cr.yp.to/ftpparse.html, http://effbot.org/downloads#ftpparse,
+ http://c0re.23.nu/c0de/ftpparsemodule/ and http://www.ocgy.ubc.ca/~tang/treeftp
+
+Currently covered formats:
+ UNIX ls, with or without gid;
+ Windoze FTP Servers;
+ VMS;
+ WFTPD;
+ NetPresenz (Mac);
+ NetWare;
+ DOS;
+
+Definitely not covered:
+ EPLF (show me an FTP server first...);
+ Long VMS filenames, with information split across two lines;
+ NCSA Telnet FTP server. Has LIST = NLST (and bad NLST for directories).
+"""
+
+
+__version__ = "1.1.2"
+__author__ = "Oleg Broytman <phd@phd.pp.ru>"
+__copyright__ = "Copyright (C) 2003-2005 PhiloSoft Design"
+
+
+# ChangeLog:
+# 2005-04-26 version 1.1.2 [phd] Changed some comments and URLs.
+# 2003-07-23 version 1.1.1 [phd] Upgrade to Python 2.2: 0/1 => False/True.
+# 2003-07-17 version 1.1.0 [phd] Great renaming.
+# 2003-07-17 version 1.0.1 [phd] Preserve leading spaces in UNIX format.
+# 2003-02-17 version 1.0.0 [phd] First public version.
+# 2003-02-07 version 0.0.1 [phd] started the project.
+
+
+try:
+ from mx import DateTime
+except ImportError:
+ _parse_datetime = False
+else:
+ _parse_datetime = True
+
+
+class parse_error(Exception): pass
+
+
+class entry:
+ def __init__(self, name=None, perm=None, nlink=None, user=None, group=None, \
+ size=None, mtime=None, links_to=None, file_type=None):
+
+ if mtime:
+ mtime = ' '.join(mtime)
+ if _parse_datetime:
+ try:
+ mtime = DateTime.DateTimeFrom(mtime)
+ except DateTime.Error:
+ pass
+
+ self.name = name
+ self.perm = perm
+ self.nlink = nlink
+ self.user = user
+ self.group = group
+ self.size = size
+ self.mtime = mtime
+ self.links_to = links_to
+ self.file_type = file_type # f - regular file, d - directory, l - symlink
+
+
+ def __str__(self):
+ return """<%s: name=%s, perm=%s, nlink=%s, user=%s, group=%s, size=%s, mtime=%s, links-to=%s, type=%s at 0x%x>""" % (
+ self.__class__.__name__, self.name, self.perm, self.nlink,
+ self.user, self.group, self.size, self.mtime,
+ self.links_to, self.file_type, id(self))
+
+
+def _parse_unix(line, parts):
+ name = None
+ perm = None
+ nlink = None
+ user = None
+ group = None
+ size = None
+ mtime = None
+ links_to = None
+ file_type = None
+
+ perm = parts[0]
+
+ if parts[1][0] == '[': # NetWare
+ if perm[0] == 'd':
+ file_type = 'd'
+ elif perm[0] == '-':
+ file_type = 'f'
+ else:
+ return None
+ perm = perm + ' ' + parts[1]
+ user = parts[2]
+ size = parts[3]
+ mtime = parts[4:7]
+
+ parts = line.split(None, 7) # resplit the original line...
+ name = parts[7] # ...in case the filename contains whitespaces
+
+ elif parts[1] == "folder": # NetPresenz for the Mac
+ file_type = 'd'
+ size = parts[2]
+ mtime = parts[3:6]
+
+ parts = line.split(None, 6)
+ name = parts[6]
+
+ elif parts[0][0] == 'l': # symlink
+ file_type = 'l'
+ nlink = int(parts[1])
+ user = parts[2]
+ group = parts[3]
+ size = parts[4]
+ mtime = parts[5:8]
+
+ parts = line.split(None, 8)
+ link = parts[8]
+ parts = link.split(" -> ")
+ name = parts[0]
+ links_to = parts[1]
+
+ elif len(parts) > 8:
+ if perm[0] == 'd':
+ file_type = 'd'
+ elif perm[0] == '-':
+ file_type = 'f'
+ else:
+ return None
+ nlink = int(parts[1])
+ user = parts[2]
+ group = parts[3]
+ size = parts[4]
+ mtime = parts[5:8]
+
+ parts = line.split(None, 7)
+ name = parts[7]
+ parts = name.split(' ')[1:]
+ name = ' '.join(parts)
+
+ else:
+ if parts[2].isdigit(): # NetPresenz
+ file_type = 'f'
+ size = parts[3]
+ mtime = parts[4:7]
+ else:
+ if perm[0] == 'd':
+ file_type = 'd'
+ elif perm[0] == '-':
+ file_type = 'f'
+ else:
+ return None
+ nlink = int(parts[1])
+ user = parts[2]
+ size = parts[3]
+ mtime = parts[4:7]
+
+ parts = line.split(None, 7)
+ name = parts[7]
+
+ return entry(name, perm, nlink, user, group, size, mtime, links_to, file_type)
+
+
+def _parse_vms(parts):
+ name = parts[0]
+ perm = parts[5]
+ nlink = parts[1]
+ user = parts[4][1:-1]
+ group = None
+ size = None
+ mtime = parts[2:4]
+ links_to = None
+ file_type = None
+
+ if ',' in user:
+ parts = user.split(',')
+ user = parts[0]
+ group = parts[1]
+
+ return entry(name, perm, nlink, user, group, size, mtime, links_to, file_type)
+
+
+def _parse_dos(parts):
+ name = parts[3]
+ perm = None
+ nlink = None
+ user = None
+ group = None
+ size = None
+ links_to = None
+
+ if _parse_datetime:
+ # Change %m-%d-%y format to %y-%m-%d
+ date = parts[0]
+ date_parts = date.split('-')
+ date = "%s-%s-%s" % (date_parts[2], date_parts[0], date_parts[1])
+ time = parts[1]
+ mtime = [date, time]
+ else:
+ mtime = parts[0:2]
+
+ if parts[2] == "<DIR>":
+ file_type = 'd'
+ else:
+ file_type = 'f'
+ size = int(parts[2])
+
+ return entry(name, perm, nlink, user, group, size, mtime, links_to, file_type)
+
+
+def ftpparse(line):
+ parts = line.split()
+ c = parts[0][0]
+
+ if c == '+': # EPLF format is not supported
+ return None
+
+ if c in ('b', 'c', 'd', 'l', 'p', 's', '-'): # UNIX-like
+ return _parse_unix(line, parts)
+
+ if ';' in parts[0]: # VMS
+ return _parse_vms(parts)
+
+ if '0' <= c <= '9': # DOS
+ return _parse_dos(parts)
+
+
+ #Some useless lines, safely ignored:
+ #"Total of 11 Files, 10966 Blocks." (VMS)
+ #"total 14786" (UNIX)
+ #"DISK$ANONFTP:[ANONYMOUS]" (VMS)
+ #"Directory DISK$PCSA:[ANONYM]" (VMS)
+
+ return None
+
+
+def test():
+ #UNIX-style listing, without inum and without blocks
+ print ftpparse("-rw-r--r-- 1 root other 531 Jan 29 03:26 README")
+ print ftpparse("dr-xr-xr-x 2 root other 512 Apr 8 1994 etc")
+ print ftpparse("dr-xr-xr-x 2 root 512 Apr 8 1994 etc")
+ print ftpparse("lrwxrwxrwx 1 root other 7 Jan 25 00:17 bin -> usr/bin")
+ #FTP servers for Windoze:
+ print ftpparse("---------- 1 owner group 1803128 Jul 10 10:18 ls-lR.Z")
+ print ftpparse("d--------- 1 owner group 0 May 9 19:45 Softlib")
+ #WFTPD for DOS:
+ print ftpparse("-rwxrwxrwx 1 noone nogroup 322 Aug 19 1996 message.ftp")
+ #NetWare:
+ print ftpparse("d [R----F--] supervisor 512 Jan 16 18:53 login")
+ print ftpparse("- [R----F--] rhesus 214059 Oct 20 15:27 cx.exe")
+ #NetPresenz for the Mac:
+ print ftpparse("-------r-- 326 1391972 1392298 Nov 22 1995 MegaPhone.sit")
+ print ftpparse("drwxrwxr-x folder 2 May 10 1996 network")
+
+ #MultiNet (some spaces removed from examples)
+ print ftpparse("00README.TXT;1 2 30-DEC-1996 17:44 [SYSTEM] (RWED,RWED,RE,RE)")
+ print ftpparse("CORE.DIR;1 1 8-SEP-1996 16:09 [SYSTEM] (RWE,RWE,RE,RE)")
+ #non-MutliNet VMS:
+ print ftpparse("CII-MANUAL.TEX;1 213/216 29-JAN-1996 03:33:12 [ANONYMOU,ANONYMOUS] (RWED,RWED,,)")
+
+ #DOS format
+ print ftpparse("04-27-00 09:09PM <DIR> licensed")
+ print ftpparse("07-18-00 10:16AM <DIR> pub")
+ print ftpparse("04-14-00 03:47PM 589 readme.htm")
+
+ print ftpparse("-rw-r--r-- 1 root other 531 Jan 29 03:26 READ ME")
+ print ftpparse("-rw-r--r-- 1 root other 531 Jan 29 03:26 DO NOT READ ME ")
+
+if __name__ == "__main__":
+ test()
--- /dev/null
+#! /usr/bin/env python
+"""Recursive FTP scanners"""
+
+
+import ftplib
+from m_lib.net.ftp.ftpparse import ftpparse
+
+
+class FtpScanError(Exception): pass
+ftpscan_error_mark = object() # error marker
+
+
+class GetFiles:
+ def __init__(self):
+ self.entries = []
+
+ def __call__(self, line):
+ entry = ftpparse(line)
+ if entry:
+ self.entries.append(entry)
+
+ def filter(self, file_type):
+ return filter(lambda e, file_type=file_type: e.file_type == file_type,
+ self.entries)
+
+ def files(self):
+ return self.filter('f')
+
+ def directories(self):
+ return filter(lambda e: e.name not in (".", ".."), self.filter('d'))
+
+
+class ReconnectingFTPCallWrapper:
+ retries = 10 # retries per function call
+
+ def __init__(self, wrapper, func):
+ self.wrapper = wrapper
+ self.func = func
+
+ def __call__(self, *params, **kw):
+ wrapper = self.wrapper
+ func = self.func
+
+ for retry in range(self.retries):
+ try:
+ return func(*params, **kw)
+ except EOFError:
+ pass
+
+ ftp_dir = wrapper._ftp_dir
+ wrapper._tree.append((ftpscan_error_mark, "Connection reset by peer at directory `%s'. Reconnecting..." % ftp_dir))
+
+ ftp = wrapper._ftp
+ ftp.close()
+
+ ftp.connect(wrapper._ftp_server, wrapper._ftp_port)
+ ftp.login(wrapper._login, wrapper._password)
+ ftp.cwd(ftp_dir)
+
+class ReconnectingFTPWrapper:
+ ReconnectingFTPCallWrapperClass = ReconnectingFTPCallWrapper
+
+ def __init__(self, ftp, ftp_server, ftp_port=None, login=None, password=None, ftp_dir='/', tree=None):
+ self._ftp = ftp
+ self._ftp_server = ftp_server
+ self._ftp_port = ftp_port
+ self._login = login
+ self._password = password
+ ftp_dir = [''] + [name for name in ftp_dir.split('/') if name] # remove double slashes //
+ self._ftp_dir = '/'.join(ftp_dir)
+ self._tree = tree
+
+ def cwd(self, new_cwd, do_ftp=True):
+ ftp_dir = self._ftp_dir.split('/')
+ if new_cwd == "..":
+ del ftp_dir[-1]
+ else:
+ ftp_dir.append(new_cwd)
+ self._ftp_dir = '/'.join(ftp_dir)
+ if do_ftp: self._wrap(self._ftp.cwd)(new_cwd)
+
+ def __getattr__(self, attr):
+ value = getattr(self._ftp, attr)
+ if callable(value):
+ return self._wrap(value)
+ return value
+
+ def _wrap(self, func):
+ return self.ReconnectingFTPCallWrapperClass(self, func)
+
+
+def _traverse_ftp(ftp, tree, ftp_dir):
+ get_files = GetFiles()
+ try:
+ ftp.dir(get_files)
+ except ftplib.all_errors, msg:
+ tree.append((ftpscan_error_mark, "Cannot list directory `%s': %s" % (ftp_dir, msg)))
+ return
+ files = get_files.files()
+ directories = get_files.directories()
+
+ if ftp_dir and ftp_dir[-1] == '/':
+ ftp_dir = ftp_dir[:-1] # Prevent paths to contain double slashes //
+
+ tree.append((ftp_dir, files))
+
+ for d in directories:
+ name = d.name
+ full_path = ftp_dir + '/' + name
+ try:
+ ftp.cwd(name)
+ except ftplib.error_perm, msg:
+ tree.append((ftpscan_error_mark, "Cannot enter directory `%s': %s" % (full_path, msg)))
+ if isinstance(ftp, ReconnectingFTPWrapper):
+ ftp.cwd("..", False)
+ except ftplib.all_errors, msg:
+ tree.append((ftpscan_error_mark, "Cannot enter directory `%s': %s" % (full_path, msg)))
+ else:
+ _traverse_ftp(ftp, tree, full_path)
+ ftp.cwd("..")
+
+
+def ftpscan1(ftp_server, ftp_port=None, login=None, password=None,
+ ftp_dir='/', passive=None, FTPClass=ftplib.FTP, reconnect=False,
+ ReconnectingFTPWrapperClass=ReconnectingFTPWrapper):
+ """Recursive FTP scan using one-by-one directory traversing. It is slow
+ but robust - it works with all but very broken FTP servers.
+ """
+ tree = []
+ ftp = FTPClass()
+ if passive is not None:
+ ftp.set_pasv(passive)
+ if reconnect:
+ ftp = ReconnectingFTPWrapperClass(ftp, ftp_server, ftp_port, login, password, ftp_dir, tree)
+ ftp.connect(ftp_server, ftp_port)
+ ftp.login(login, password)
+ if ftp_dir <> '/':
+ ftp.cwd(ftp_dir)
+
+ _traverse_ftp(ftp, tree, ftp_dir)
+ ftp.quit()
+
+ return tree
+
+
+def ftpscanrecursive(ftp_server, ftp_port=None, login=None, password=None,
+ ftp_dir='/', passive=None, FTPClass=ftplib.FTP, reconnect=False):
+ """
+ Recursive FTP scan using fast LIST -R command. Not all servers supports
+ this, though.
+ """
+ ftp = FTPClass()
+ if passive is not None:
+ ftp.set_pasv(passive)
+ ftp.connect(ftp_server, ftp_port)
+ ftp.login(login, password)
+ if ftp_dir <> '/':
+ ftp.cwd(ftp_dir)
+
+ lines = []
+ try:
+ ftp.dir("-R", lines.append)
+ except ftplib.error_perm:
+ # The server does not implement LIST -R and
+ # treats -R as a name of a directory (-:
+ ftp.quit()
+ raise FtpScanError, "the server does not implement recursive listing"
+ ftp.quit()
+
+ tree = []
+ current_dir = ftp_dir
+ files = []
+
+ for line in lines:
+ if line:
+ if line[-1] == ':' and not line.startswith("-rw-"): # directory
+ tree.append((current_dir, files))
+ if line[:2] == "./":
+ line = line[1:] # remove leading dot
+ elif line[0] <> '/':
+ line = '/' + line
+ current_dir = line[:-1]
+ files = []
+ else:
+ if not line.startswith("total "):
+ entry = ftpparse(line)
+ if entry:
+ if entry.file_type == 'f':
+ files.append(entry)
+ else:
+ tree.append((ftpscan_error_mark, "Unrecognised line: `%s'" % line))
+ tree.append((current_dir, files))
+
+ if len(tree) == 1:
+ raise FtpScanError, "the server ignores -R in LIST"
+
+ return tree
+
+
+def ftpscan(ftp_server, ftp_port=None, login=None, password=None,
+ ftp_dir='/', passive=None, FTPClass=ftplib.FTP):
+ try:
+ return ftpscanrecursive(ftp_server, ftp_port, login, password, ftp_dir, passive, FTPClass)
+ except FtpScanError:
+ try:
+ return ftpscan1(ftp_server, ftp_port, login, password, ftp_dir, passive, FTPClass)
+ except EOFError:
+ return ftpscan1(ftp_server, ftp_port, login, password, ftp_dir, passive, FTPClass, True)
+ except EOFError:
+ return ftpscan1(ftp_server, ftp_port, login, password, ftp_dir, passive, FTPClass, True)
+
+
+def test(ftp_server, func, passive=None, reconnect=False):
+ from time import time
+ start_time = time()
+
+ tree = func(ftp_server, passive=passive, reconnect=reconnect)
+
+ stop_time = time()
+ print stop_time - start_time
+
+ logfname = "%s.list" % ftp_server
+ log = open(logfname, 'w')
+
+ for ftp_dir, files in tree:
+ if ftp_dir == ftpscan_error_mark:
+ log.write("Error:\n")
+ log.write(files)
+ log.write('\n')
+ else:
+ log.write(ftp_dir + '\n')
+ for _file in files:
+ log.write(" ")
+ log.write(_file.name + '\n')
+
+
+def usage(code=0):
+ sys.stderr.write("Usage: %s [-a|-p] [hostname]\n" % sys.argv[0])
+ sys.exit(code)
+
+if __name__ == "__main__":
+ import sys
+ from getopt import getopt, GetoptError
+
+ try:
+ options, arguments = getopt(sys.argv[1:], "hap",
+ ["help", "active", "passive"])
+ except GetoptError:
+ usage(1)
+
+ passive = None
+
+ for option, value in options:
+ if option in ("-h", "--help"):
+ usage()
+ elif option in ("-a", "--active"):
+ passive = False
+ elif option in ("-p", "--passive"):
+ passive = True
+ else:
+ usage(2)
+
+ l = len(arguments)
+ if (l == 0):
+ ftp_server = "localhost"
+ elif l > 1:
+ usage()
+ else:
+ ftp_server = arguments[0]
+
+ print "Scanning", ftp_server
+ try:
+ test(ftp_server, ftpscanrecursive, passive)
+ except FtpScanError, msg:
+ print "Rescanning due to the error:", msg
+ try:
+ test(ftp_server, ftpscan1, passive)
+ except EOFError:
+ print "Rescanning due to the error: connection reset by peer"
+ test(ftp_server, ftpscan1, passive, True)
+ except EOFError:
+ print "Rescanning due to the error: connection reset by peer"
+ test(ftp_server, ftpscan1, passive, True)
--- /dev/null
+"SMS Transports by phd, corvin, r_sabitov"
+# -*- coding: koi8-r -*-
+
+
+import socket
+
+from m_lib.opstring import koi2win
+from m_lib.rus.rus2lat import rus2lat
+
+
+debug_level = 0
+use_syslog = 0
+
+def debug(level, message):
+ if level <= debug_level:
+ if use_syslog:
+ import syslog
+ syslog.syslog(message)
+ import sys
+ sys.stderr.write("%s\n" % message)
+
+
+smsSize = 160
+
+#
+# Base transport classes
+#
+
+class Transport(object):
+ def __init__(self, phone, message):
+ self.phone = phone
+ self.message = message
+
+ def calcText(self, maxsize=smsSize, transFunction=None):
+ "Recode text on demand and truncate the result"
+
+ text = self.message
+ if transFunction:
+ text = transFunction(text)
+ text = text[:maxsize - 1]
+ return text
+
+ def calcExpiration(self):
+ from mx import DateTime
+ return DateTime.now() + DateTime.DateTimeDelta(0, 4)
+
+class CP1251(object):
+ "Mixin that converts input text from koi8 to cp1251"
+
+ def calcText(self, maxsize=smsSize):
+ text = super(CP1251, self).calcText(maxsize=maxsize)
+ return koi2win(text)
+
+#
+# HTTP transport
+#
+
+class HTTPTransport(Transport):
+ "Base class for HTTP transport"
+
+ HTTPHeaders = {
+ 'User-Agent': 'Mozilla/5.0 (X11; U; Linux 2.2.19 i686; en-US; rv:0.9) Gecko/20010507',
+ 'Accept': '*/*'
+ }
+
+ referer = ''
+
+ def post(self, dict):
+ import urllib, urlparse
+ _none, host, uri, _none, _none, _none = urlparse.urlparse(self.url)
+ postdata = urllib.urlencode(dict)
+
+ methodFunc = getattr(self, self.method)
+
+ try:
+ reply = methodFunc(postdata, host, uri)
+ except socket.error:
+ ret = 0
+ else:
+ html = reply[2].fp.read()
+ debug(2, html)
+
+ if html.find(self.ok_match) >= 0:
+ ret = 1
+ else:
+ ret = 0
+
+
+ _debug = 'msg to %s via %s (%s)' % \
+ (self.phone, self.__class__.__name__, ret)
+ debug(1, _debug)
+
+ return ret
+
+ def GET(self, postdata, host, uri, port=80, proxy_host=None, proxy_port=None):
+ "HTTP method GET"
+
+ if postdata:
+ uri = uri + "?" + postdata
+
+ import httplib
+ if proxy_host:
+ http = httplib.HTTP(proxy_host, proxy_port)
+ http.set_debuglevel(debug_level)
+ http.putrequest("GET", 'http://%s%s' % (host, uri))
+ else:
+ http = httplib.HTTP(host, port)
+ http.set_debuglevel(debug_level)
+ http.putrequest("GET", uri)
+
+ http.putheader("Host", host)
+ if self.referer:
+ http.putheader("Referer", self.referer)
+
+ for name, val in self.HTTPHeaders.items():
+ http.putheader(name, val)
+
+ http.endheaders()
+
+ reply = http.getreply()
+ reply[2].fp = http.getfile()
+ return reply
+
+ def POST(self, postdata, host, uri, port=80, proxy_host=None, proxy_port=None):
+ "HTTP method POST"
+
+ import httplib
+ if proxy_host:
+ http = httplib.HTTP(proxy_host, proxy_port)
+ http.set_debuglevel(debug_level)
+ http.putrequest("POST", 'http://%s%s' % (host, uri))
+ else:
+ http = httplib.HTTP(host, port)
+ http.set_debuglevel(debug_level)
+ http.putrequest("POST", uri)
+
+ http.putheader("Host", host)
+ if self.referer:
+ http.putheader("Referer", self.referer)
+ http.putheader('Content-Type', 'application/x-www-form-urlencoded')
+ http.putheader('Content-Length', str(len(postdata)))
+
+ for name, val in self.HTTPHeaders.items():
+ http.putheader(name, val)
+
+ http.endheaders()
+ http.send(postdata)
+
+ reply = http.getreply()
+ reply[2].fp = http.getfile()
+ return reply
+
+class CP1251HTTPTransport(CP1251, HTTPTransport):
+ pass
+
+#
+# SNPP transport
+#
+
+class SNPPTransport(Transport):
+ "Base class for SNPP transport"
+
+ host = 'localhost'
+ port = 444
+ ok_match = '250 Message Sent Successfully'
+
+ def post(self, dict):
+ # raw snpp hack w/o error checking
+ try:
+ sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
+ sock.connect(('www.extel-gsm.com', 4444))
+ sock_file = sock.makefile('r')
+
+ # 220 Extel Mobile Communications. SNPP Gateway v.0.2.15; Wed May 30 00:43:56 2001
+ # 220 SNPP Gateway Ready
+ debug(2, sock_file.readline())
+ debug(2, sock_file.readline())
+
+ sock.send('PAGE %s\r\n' % dict['phone'])
+ # 250 Pager ID Accepted
+ debug(2, sock_file.readline())
+
+ sock.send('DATA\r\n')
+ # 354 Begin Input; End with <CRLF>'.'<CRLF>
+ debug(2, sock_file.readline())
+
+ sock.send('%s\r\n' % dict['message'])
+ sock.send('.\r\n')
+ # 250 Message OK
+ debug(2, sock_file.readline())
+
+ sock.send('SEND\r\n')
+ # 250 Message Sent Successfully. Msg ID = 692274
+ reply = sock_file.readline()
+ debug(2, reply)
+
+ sock.send('QUIT\r\n')
+ sock.close()
+ except:
+ ret = 0
+ else:
+ if reply.find(self.ok_match) >= 0:
+ ret = 1
+ else:
+ ret = 0
+
+ _debug = 'msg to %s via %s (%s)' % \
+ (self.phone, self.__class__.__name__, ret)
+ debug(1, _debug)
+ return ret
+
+#
+# E-mail trnsport
+#
+
+class EMailTransport(Transport):
+ def __init__(self, phone, message, mail_from=None, gate=None):
+ Transport.__init__(self, phone, message)
+
+ if not mail_from:
+ import getpass
+ try:
+ realuser = getpass.getuser()
+ except AttributeError:
+ # Not all systems have os.environ or getpw...
+ realuser = 'nobody'
+ thishost = socket.getfqdn()
+ mail_from = "%s@%s" % (realuser, thishost)
+ debug(1, mail_from)
+ self.mail_from = mail_from
+
+ self.gate = gate
+
+ def send(self):
+ message = """\
+From: %s
+To: %s@%s
+Subject: SMS
+
+%s
+""" % (self.mail_from, self.phone, self.gate, self.message)
+ self.post(message)
+ debug(2, message)
+ return 1
+
+class SendmailTransport(EMailTransport):
+ def __init__(self, phone, message, mail_from=None, gate=None):
+ EMailTransport.__init__(self, phone, message, mail_from, gate)
+
+ sendmail = self.find_sendmail()
+ if not sendmail:
+ raise ValueError, "cannot find sendmail binary"
+ self.sendmail = sendmail
+
+ def find_sendmail(self):
+ import os
+ for sendmail in ("/usr/lib/sendmail", "/usr/sbin/sendmail"):
+ if os.path.exists(sendmail):
+ return sendmail
+ return None
+
+ def post(self, message):
+ cmd = "%s -oem -oi '%s@%s'" % (self.sendmail, self.phone, self.gate)
+ debug(1, cmd)
+ import os
+ sendmail = os.popen(cmd, 'w')
+ sendmail.write(message)
+ sendmail.close()
+
+class SMTPTransport(EMailTransport):
+ def __init__(self, phone, message, mail_from=None, gate=None, mail_relay="localhost"):
+ EMailTransport.__init__(self, phone, message, mail_from, gate)
+ self.mail_relay = mail_relay
+
+ def post(self, message):
+ debug(1, self.mail_relay)
+ import smtplib
+ smtp = smtplib.SMTP(self.mail_relay)
+ smtp.sendmail(self.mail_from, "%s@%s" % (self.phone, self.gate), message)
+ smtp.close()
+
+
+#
+# Real transports
+#
+
+class MTSru(HTTPTransport):
+ "Russian provider MTS.ru"
+
+ method = "POST"
+ url = "http://www.mts.ru:5051/cgi-bin/cgi.exe"
+ referer = ""
+ ok_match = 'Ваше сообщение отправлено'
+
+ def send(self):
+ dict = {}
+ text = self.calcText()
+ time = self.calcExpiration()
+
+ dict['function'] = 'sms_send'
+ #dict['MMObjectType'] = '0'
+ #dict['MMObjectID'] = ''
+ phone = self.phone
+ if phone[:4] == "8902":
+ phone = "8916%s" % phone[4:]
+ dict['To'] = phone
+ dict['Msg'] = text
+ dict['Hour'] = time.strftime('%k').strip()
+ dict['Min'] = time.strftime('%M')
+ dict['Day'] = time.strftime('%e').strip()
+ dict['Mon'] = str(time.month)
+ dict['Year'] = time.strftime('%Y')
+ dict['count'] = str(len(text))
+ dict['Lang'] = '2'
+
+ return self.post(dict)
+
+ def POST(self, postdata, host, uri, port=80, proxy_host=None, proxy_port=None):
+ postdata = "MMObjectType=0&MMObjectID=&" + postdata
+ return HTTPTransport.POST(self, postdata, host, uri, port, proxy_host, proxy_port)
+
+
+class MTSGSMcom(HTTPTransport):
+ url = "http://www.mtsgsm.com/sms/sent.html"
+ referer = "http://www.mtsgsm.com/sms/"
+ method = "GET"
+ ok_match = 'Ваше сообщение отправлено'
+
+ def send(self):
+ dict = {}
+ text = self.calcText()
+ time = self.calcExpiration()
+
+ dict['Posted'] = '1'
+ dict['To'] = self.phone
+ dict['Msg'] = text
+ dict['count'] = str(len(text))
+ dict['SMSHour'] = time.strftime('%k').strip()
+ dict['SMSMinute'] = time.strftime('%M')
+ dict['SMSDay'] = time.strftime('%e').strip()
+ dict['SMSMonth'] = str(time.month - 1)
+ dict['SMSYear'] = time.strftime('%Y')
+
+ return self.post(dict)
+
+
+class BeeOnLine(CP1251HTTPTransport):
+ "Russian provider BeeOnLine.ru"
+
+ method = "POST"
+ url = "http://www.beeonline.ru/portal/comm/send_sms/simple_send_sms.sms"
+ referer = "http://www.beeonline.ru/portal/comm/send_sms/simple_send_sms.sms"
+ crap = 'BOL '
+ ok_match = koi2win('Ваше сообщение отправлено')
+
+ def __init__(self, phone, message,
+ mode="GSM", # mode can be either GSM or DAMPS
+ transliterate=1, # turn transliteration of/off
+ reply_to=''): # send reply to this e-mail
+ Transport.__init__(self, phone, message)
+ if mode not in ('GSM', 'DAMPS'):
+ raise ValueError, "mode (%s) must be either 'GSM' or 'DAMPS'" % mode
+ self.mode = mode
+ self.transliterate = transliterate
+ self.reply_to = reply_to
+
+ def send(self):
+ dict = {}
+ text = self.calcText(smsSize - len(self.crap))
+
+ # hidden
+ #dict['deferto'] = ''
+ #dict['adv_year'] = ''
+ dict['send'] = 'send'
+ dict['destination_number_from'] = 'ordinary' # number, not a BEEpost
+
+ prf = self.phone[:4]
+ if self.mode == "GSM":
+ dict['network_code'] = '3'
+ elif prf == '7095':
+ dict['network_code'] = '2'
+ elif prf == '7901':
+ dict['network_code'] = '1'
+ else:
+ raise RuntimeError, "incorrect combination of mode (%s) and prefix (%s)" % (self.mode, prf)
+
+ dict['phone'] = self.phone[4:]
+ dict['message'] = text
+ dict['mlength'] = str(smsSize - len(self.crap) - len(text))
+
+ if self.mode == "GSM" and not self.transliterate:
+ dict['translit'] = '1' # turn transliteration OFF! :)
+
+ if self.reply_to:
+ dict['send_email'] = '1'
+ dict['reply_addr'] = self.reply_to
+
+ return self.post(dict)
+
+class BeeOnLineSMS(BeeOnLine):
+ "Russian provider BeeOnLine.ru"
+
+ url = "http://www.beeonline.ru/servlet/send/sms/"
+
+ def send(self):
+ dict = {}
+ text = self.calcText(smsSize - len(self.crap))
+
+ dict['prf'] = self.phone[:4]
+ dict['phone'] = self.phone[4:]
+ dict['termtype'] = self.mode[0]
+ dict['message'] = text
+
+ if self.mode == "GSM" and not self.transliterate:
+ dict['translit'] = '1' # turn transliteration OFF! :)
+
+ dict['number_sms'] = "number_sms_send"
+ return self.post(dict)
+
+
+class Pcom(HTTPTransport):
+ "Russian provider PCOM.ru"
+
+ method = "POST"
+ url = "http://www.pcom.ru/online.phtml"
+ referer = "http://www.pcom.ru/online.phtml"
+ crap = ''
+ ok_match = koi2win('поставлено в очередь')
+
+ def calcText(self, maxsize=smsSize):
+ "force translitertaion"
+
+ return HTTPTransport.calcText(self, maxsize=maxsize,
+ transFunction=rus2lat)
+
+ def send(self):
+ dict = {}
+ text = self.calcText(120 - len(self.crap))
+ expiration = self.calcExpiration()
+ from mx import DateTime
+ now = DateTime.now()
+
+ dict['ACTION'] = 'SENDSMS'
+
+ dict['SMS_START_HOUR'] = now.strftime('%H')
+ dict['SMS_START_MINUTE'] = now.strftime('%M')
+ dict['SMS_START_DAY'] = now.strftime('%d')
+ dict['SMS_START_MONTH'] = now.strftime('%m')
+ dict['SMS_START_YEAR'] = now.strftime('%Y')
+
+ dict['SMS_STOP_HOUR'] = expiration.strftime('%H')
+ dict['SMS_STOP_MINUTE'] = expiration.strftime('%M')
+ dict['SMS_STOP_DAY'] = expiration.strftime('%d')
+ dict['SMS_STOP_MONTH'] = expiration.strftime('%m')
+ dict['SMS_STOP_YEAR'] = expiration.strftime('%Y')
+
+ dict['prefix'] = self.phone[1:4]
+ dict['DN'] = self.phone[4:]
+ dict['MSG'] = text
+
+ return self.post(dict)
+
+
+class ExtelGsmCom(SNPPTransport):
+ "Russian provider Extel-GSM.com"
+
+ host = 'www.extel-gsm.com'
+ port = 4444
+ ok_match = '250 Message Sent Successfully'
+ crap = ''
+
+ prefix = '0119'
+
+ def calcText(self, maxsize=smsSize):
+ "force translitertaion"
+
+ return SNPPTransport.calcText(self, maxsize=maxsize,
+ transFunction=rus2lat)
+
+ def send(self):
+ dict = {}
+ text = self.calcText(smsSize - len(self.crap))
+ phone = self.phone[5:]
+
+ # 0112 (city code) must be replaced with (0119)
+ dict['phone'] = '+7%s%s' % (self.prefix, phone)
+ dict['message'] = text
+
+ return self.post(dict)
+
+
+class MegafoneMoscow(CP1251HTTPTransport):
+ "Rissian provider megafonmoscow.ru"
+
+ method = "POST"
+ url = "http://www.megafonmoscow.ru/rus/sms.xpml"
+ referer = "http://www.megafonmoscow.ru/rus/sms.xpml"
+ ok_match = koi2win('Сообщение успешно отправлено')
+
+ def send(self):
+ dict = {}
+ text = self.calcText()
+
+ dict['prefix'] = self.phone[:4]
+ dict['addr'] = self.phone[4:]
+ dict['message'] = text
+ dict['send'] = " Send "
+
+ if hasattr(self, 'transliterate') and self.transliterate:
+ dict['transliterate'] = '1'
+
+ return self.post(dict)
+
+
+class SkyLinkMsk(CP1251HTTPTransport):
+ "Russian provider SkyLink.Msk.ru"
+
+ method = "POST"
+ url = "http://skylink.msk.ru/inc/sendsms.php"
+ ok_match = koi2win('Сообщение было отправлено')
+
+ def send(self):
+ dict = {}
+ text = self.calcText()
+
+ phone = self.phone
+ if phone[0] == '7':
+ phone = '8' + phone[1:]
+ dict['dest'] = phone
+ dict['smsbody'] = text
+
+ return self.post(dict)
+
+
+BEELINE = BeeOnLineSMS
+#BEELINE = Whizdiary
+MTS = MTSru
+#MTS = MTSGSMcom
+#MTS = Whizdiary
+SONET = Pcom
+EXTEL = ExtelGsmCom
+Megafone = MegafoneMoscow
+SkyLink = SkyLinkMsk
+
+
+Prefix2Provider = {
+ "903": BEELINE,
+ "905": BEELINE,
+ "906": BEELINE,
+
+ "910": MTS,
+ "916": MTS,
+
+ "926": Megafone
+}
+
+
+class SMSError(Exception): pass
+
+def Phone2Provider(phone):
+ prefix = phone[1:4]
+
+ if prefix in ("095", "901"): # 901 is being used by Beeline and SkyLink
+ raise SMSError, "unknown provider for phone %s" % phone
+
+ if Prefix2Provider.has_key(prefix):
+ return Prefix2Provider[prefix]
+
+ raise SMSError, "bad prefix for phone %s" % phone
--- /dev/null
+"Broytman WWW Library for Python, Copyright (C) 1996-2002 PhiloSoft Design"
+
+
+__all__ = [
+ "util", "dtml", "url_lib", "html", "xml", "serverpush"
+]
--- /dev/null
+"DTML utilities"
+
+class standard_html: # Base class for using with ZTemplates
+ def __init__(self, title):
+ self.standard_html_header = """<HTML>
+ <HEAD>
+ <TITLE>
+ %s
+ </TITLE>
+ </HEAD>
+
+<BODY>""" % title
+
+ self.standard_html_footer = """</BODY>
+</HTML>"""
+
+ self.title = title
--- /dev/null
+"HTML parsers"
+
+
+from HTMLParser import HTMLParser as _HTMLParser
+from htmlentitydefs import entitydefs
+
+
+def join_attrs(attrs):
+ attr_list = ['']
+ for attrname, value in attrs:
+ if value is None:
+ attr_list.append('%s' % attrname)
+ else:
+ attr_list.append('%s="%s"' % (attrname, value.strip()))
+
+ return ' '.join(attr_list)
+
+
+class HTMLParser(_HTMLParser):
+
+
+ def __init__(self):
+ _HTMLParser.__init__(self)
+ self.accumulator = ""
+
+
+ def handle_starttag(self, tag, attrs):
+ try:
+ method = getattr(self, 'start_' + tag)
+ except AttributeError:
+ try:
+ method = getattr(self, 'do_' + tag)
+ except AttributeError:
+ self.unknown_starttag(tag, attrs)
+ else:
+ method(attrs)
+ else:
+ method(attrs)
+
+ def handle_endtag(self, tag):
+ try:
+ method = getattr(self, 'end_' + tag)
+ except AttributeError:
+ self.unknown_endtag(tag)
+ else:
+ method()
+
+
+ def handle_data(self, data):
+ if data:
+ self.accumulator = "%s%s" % (self.accumulator, data)
+
+ def handle_comment(self, data):
+ if data:
+ self.accumulator = "%s<!--%s-->" % (self.accumulator, data)
+
+
+ def handle_charref(self, name):
+ self.accumulator = "%s&#%s;" % (self.accumulator, name)
+
+ def handle_entityref(self, name):
+ if entitydefs.has_key(name): # If it is one of the standard SGML entities - close it with semicolon
+ x = ';'
+ else:
+ x = ''
+ self.accumulator = "%s&%s%s" % (self.accumulator, name, x)
+
+
+ # Pass other tags unmodified
+ def unknown_starttag(self, tag, attrs):
+ self.accumulator = "%s<%s%s>" % (self.accumulator, tag, join_attrs(attrs))
+
+ def unknown_endtag(self, tag):
+ self.accumulator = "%s</%s>" % (self.accumulator, tag)
+
+
+# Additional classes for filters
+
+class _allowStartTag:
+ def __init__(self, filter, tag):
+ self.filter = filter
+ self.tag = tag
+
+ def __call__(self, attrs):
+ filter = self.filter
+ filter.accumulator = "%s<%s%s>" % (filter.accumulator, self.tag, join_attrs(attrs))
+
+class _allowEndTag:
+ def __init__(self, filter, tag):
+ self.filter = filter
+ self.tag = tag
+
+ def __call__(self):
+ filter = self.filter
+ filter.accumulator = "%s</%s>" % (filter.accumulator, self.tag)
+
+
+class HTMLFilter(HTMLParser):
+ allowStartTagClass = _allowStartTag
+ allowEndTagClass = _allowEndTag
+
+ def handle_comment(self, data):
+ pass
+
+ # Filter out all tags
+ def unknown_starttag(self, tag, attrs):
+ pass
+
+ def unknown_endtag(self, tag):
+ pass
+
+
+ def allow_startTag(self, tag):
+ setattr(self, "start_%s" % tag, self.allowStartTagClass(self, tag))
+
+ def allow_endTag(self, tag):
+ setattr(self, "end_%s" % tag, self.allowEndTagClass(self, tag))
+
+
+# how to use them:
+
+#class DocHTMLFilter(HTMLFilter):
+# def __init__(self):
+# HTMLFilter.__init__(self)
+#
+# # allow tags <table>, <tr>, <td>
+# # ... and closing tags
+#
+# self.allow_startTag('table')
+# self.allow_endTag('table')
+#
+# self.allow_startTag('tr')
+# self.allow_endTag('tr')
+#
+# self.allow_startTag('td')
+# self.allow_endTag('td')
+
+def filter_html(str, filter=None):
+ "Process HTML using some HTML parser/filter"
+
+ if filter is None:
+ filter = HTMLFilter()
+
+ filter.feed(str)
+ return filter.accumulator
--- /dev/null
+"Server Push"
+
+
+import sys, mimetools
+
+class ServerPush:
+ def __init__(self, out=sys.stdout):
+ self.out = out
+ self.output = out.write
+ self.boundary = mimetools.choose_boundary()
+
+
+ def start(self):
+ self.output("""\
+Content-type: multipart/x-mixed-replace;boundary=%s
+
+""" % self.boundary)
+
+
+ def next(self, content_type="text/html"):
+ self.output("""\
+--%s
+Content-type: %s
+""" % (self.boundary, content_type))
+
+
+ def stop(self):
+ self.output("--%s--" % self.boundary)
--- /dev/null
+"url_lib"
+
+
+from urllib import FancyURLopener
+
+class NoAsk_URLopener(FancyURLopener):
+ "URL opener that does not ask for a password interactively"
+
+ def __init__(self, username, password):
+ FancyURLopener.__init__(self)
+
+ self.username = username
+ self.password = password
+ self._asked = 0
+
+ def prompt_user_passwd(self, host, realm):
+ if self._asked:
+ return None, None
+
+ self._asked = 1
+ return self.username, self.password
--- /dev/null
+"Common WWW/CGI utilities"
+
+
+import sys, os
+
+
+def exception(str = ""):
+ if sys.exc_type == SystemExit: # pass exit() normally
+ return
+
+ print "Content-Type: text/html"
+ print # Terminate HTTP headers
+
+ import html
+ print html.exception()
+
+ if str:
+ print str
+
+ sys.exit(1)
+
+
+def error(err_str):
+ if not err_str:
+ err_str = "Unknown error"
+
+ print "Content-Type: text/html"
+ print # Terminate HTTP headers
+
+ print str(err_str)
+ sys.exit(1)
+
+
+def get_redirect(_str = ""):
+ server_name = os.environ["SERVER_NAME"]
+ server_port = os.environ["SERVER_PORT"]
+
+ if server_port == "80":
+ server_port = ""
+ else:
+ server_port = ":" + server_port
+
+ return "http://" + server_name + server_port + _str
+
+
+def convert_empty(estr):
+ if estr:
+ _str = str(estr)
+ else:
+ _str = " "
+ return _str
+
+
+def gen_html(title, body):
+ print """
+ <HTML>
+ <HEAD>
+ <TITLE>
+ %s
+ </TITLE>
+ </HEAD>
+
+ <BODY>
+ %s
+ </BODY>
+ </HTML>
+ """ % (title, body)
+
+
+def mkexpires(hours=1, minutes=0, seconds=0):
+ from datetime import datetime, timedelta
+ expire = datetime.now() + timedelta(hours=hours, minutes=minutes, seconds=seconds)
+ return "Expires: %s" % expire.strftime("%a, %d %b %Y %H:%M:%S GMT")
+
+
+def parse_time(t):
+ import time
+ for format in ("%a, %d %b %Y %H:%M:%S GMT", "%A, %d-%b-%y %H:%M:%S GMT", "%A, %d-%b-%Y %H:%M:%S GMT"):
+ try:
+ return time.mktime(time.strptime(t, format)) - time.timezone
+ except (ValueError, OverflowError):
+ pass
+
+ return None
--- /dev/null
+"XML parsers"
+
+
+import re, xmllib
+illegal = re.compile('[^\t\r\n -\377]') # illegal chars in content
+xmllib.illegal = illegal # allow cyrillic characters in XML
+
+
+def join_xml_attrs(attrs):
+ attr_list = ['']
+ for attrname, value in attrs.items():
+ attr_list.append('%s="%s"' % (attrname, string.strip(value)))
+
+ return string.join(attr_list, " ")
+
+
+class XMLParser(xmllib.XMLParser):
+ def __init__(self):
+ xmllib.XMLParser.__init__(self)
+ self.accumulator = ""
+
+
+ def handle_data(self, data):
+ if data:
+ self.accumulator = "%s%s" % (self.accumulator, data)
+
+ def handle_comment(self, data):
+ if data:
+ self.accumulator = "%s<!--%s-->" % (self.accumulator, data)
+
+
+ # Pass other tags unmodified
+ def unknown_starttag(self, tag, attrs):
+ self.accumulator = "%s<%s%s>" % (self.accumulator, tag, join_xml_attrs(attrs))
+
+ def unknown_endtag(self, tag):
+ self.accumulator = "%s</%s>" % (self.accumulator, tag)
+
+
+class XMLFilter(XMLParser):
+ def handle_comment(self, data):
+ pass
+
+ # Filter out all tags
+ def unknown_starttag(self, tag, attrs):
+ pass
+
+ def unknown_endtag(self, tag):
+ pass
+
+def filter_xml(str, filter=None):
+ "Process XML using some XML parser/filter"
+
+ if filter is None:
+ filter = XMLFilter()
+
+ filter.feed(str)
+ return filter.accumulator
--- /dev/null
+#! /usr/bin/env python
+# -*- coding: koi8-r -*-
+
+#
+# opDate - date/time manipulation routines
+# Some ideas came from Turbo Professional/Object Professional (t/o)pDate.PAS
+#
+# Written by Broytman, Nov 1997 - Dec 2003
+# Copyright (C) 1997-2003 PhiloSoft Design
+#
+
+
+from string import *
+from time import *
+from calendar import *
+from opstring import *
+
+
+MinYear = 1600
+MaxYear = 3999
+MinDate = 0x00000000 # = 01/01/1600
+MaxDate = 0x000D6025 # = 12/31/3999
+Date1900 = 0x0001AC05 # = 01/01/1900
+Date1980 = 0x00021E28 # = 01/01/1980
+Date2000 = 0x00023AB1 # = 01/01/2000
+BadDate = 0x7FFFFFFF
+
+Threshold2000 = 1900
+
+MinTime = 0 # = 00:00:00 am
+MaxTime = 86399 # = 23:59:59 pm
+BadTime = 0x7FFFFFFF
+
+SecondsInDay = 86400 # number of seconds in a day
+SecondsInHour = 3600 # number of seconds in an hour
+SecondsInMinute = 60 # number of seconds in a minute
+HoursInDay = 24 # number of hours in a day
+MinutesInHour = 60 # number of minutes in an hour
+
+First2Months = 59 # 1600 was a leap year
+FirstDayOfWeek = 5 # 01/01/1600 was a Saturday
+
+
+# Errors
+class opdate_error(Exception):
+ pass
+
+
+#
+### Date manipulation routines
+#
+
+def IsLeapYear(Year):
+ if ( (Year % 4 == 0) and (Year % 4000 <> 0) and ((Year % 100 <> 0) or (Year % 400 == 0)) ):
+ return True
+ return False
+
+
+def _setYear(Year):
+ # Internal function
+ if Year < 100:
+ Year = Year + 1900
+ if Year < Threshold2000:
+ Year = Year + 100
+ return Year
+
+
+def DaysInMonth(Month, Year):
+ """ Return the number of days in the specified month of a given year """
+ if Month in [1, 3, 5, 7, 8, 10, 12]:
+ return 31
+
+ elif Month in [4, 6, 9, 11]:
+ return 30
+
+ elif Month == 2:
+ return 28+IsLeapYear(_setYear(Year))
+
+ else:
+ raise opdate_error, "bad month `%s'" % str(Month)
+
+
+def ValidDate(Day, Month, Year):
+ """ Verify that day, month, year is a valid date """
+ Year = _setYear(Year)
+
+ if (Day < 1) or (Year < MinYear) or (Year > MaxYear):
+ return False
+ elif (Month >= 1) and (Month <= 12):
+ return Day <= DaysInMonth(Month, Year)
+ else:
+ return False
+
+
+def DMYtoDate(Day, Month, Year):
+ """ Convert from day, month, year to a julian date """
+ Year = _setYear(Year)
+
+ if not ValidDate(Day, Month, Year):
+ return BadDate
+
+ if (Year == MinYear) and (Month < 3):
+ if Month == 1:
+ return Day-1
+ else:
+ return Day+30
+ else:
+ if Month > 2:
+ Month = Month - 3
+ else:
+ Month = Month + 9
+ Year = Year - 1
+ Year = Year - MinYear
+
+ return (((Year / 100)*146097) / 4) + (((Year % 100)*1461) / 4) + (((153*Month)+2) / 5)+Day+First2Months
+
+
+def DateToDMY(Julian):
+ """ Convert from a julian date to day, month, year """
+ if Julian == BadDate:
+ return 0, 0, 0
+
+ if Julian <= First2Months:
+ Year = MinYear
+ if Julian <= 30:
+ Month = 1
+ Day = Julian + 1
+ else:
+ Month = 2
+ Day = Julian-30
+ else:
+ I = (4*(Julian-First2Months))-1
+ J = (4*((I % 146097) / 4))+3
+ Year = (100*(I / 146097))+(J / 1461)
+ I = (5*(((J % 1461)+4) / 4))-3
+ Month = I / 153
+ Day = ((I % 153)+5) / 5
+ if Month < 10:
+ Month = Month + 3
+ else:
+ Month = Month - 9
+ Year = Year + 1
+ Year = Year + MinYear
+
+ return Day, Month, Year
+
+
+def IncDate(Julian, Days, Months, Years):
+ """ Add (or subtract) the number of months, days, and years to a date.
+ Months and years are added before days. No overflow/underflow checks are made
+ """
+ Day, Month, Year = DateToDMY(Julian)
+ Day28Delta = Day-28
+ if Day28Delta < 0:
+ Day28Delta = 0
+ else:
+ Day = 28
+
+ Year = Year + Years
+ Year = Year + Months / 12
+ Month = Month + Months % 12
+ if Month < 1:
+ Month = Month + 12
+ Year = Year - 1
+ elif Month > 12:
+ Month = Month - 12
+ Year = Year + 1
+
+ Julian = DMYtoDate(Day, Month, Year)
+ if Julian <> BadDate:
+ Julian = Julian + Days + Day28Delta
+
+ return Julian
+
+
+def IncDateTrunc(Julian, Months, Years):
+ """ Add (or subtract) the specified number of months and years to a date """
+ Day, Month, Year = DateToDMY(Julian)
+ Day28Delta = Day-28
+ if Day28Delta < 0:
+ Day28Delta = 0
+ else:
+ Day = 28
+
+ Year = Year + Years
+ Year = Year + Months / 12
+ Month = Month + Months % 12
+ if Month < 1:
+ Month = Month + 12
+ Year = Year - 1
+ elif Month > 12:
+ Month = Month - 12
+ Year = Year + 1
+
+ Julian = DMYtoDate(Day, Month, Year)
+ if Julian <> BadDate:
+ MaxDay = DaysInMonth(Month, Year)
+ if Day+Day28Delta > MaxDay:
+ Julian = Julian + MaxDay-Day
+ else:
+ Julian = Julian + Day28Delta
+
+ return Julian
+
+
+def DateDiff(Date1, Date2):
+ """ Return the difference in days,months,years between two valid julian dates """
+ #we want Date2 > Date1
+ if Date1 > Date2:
+ _tmp = Date1
+ Date1 = Date2
+ Date2 = _tmp
+
+ #convert dates to day,month,year
+ Day1, Month1, Year1 = DateToDMY(Date1)
+ Day2, Month2, Year2 = DateToDMY(Date2)
+
+ #days first
+ if Day2 < Day1:
+ Month2 = Month2 - 1
+ if Month2 == 0:
+ Month2 = 12
+ Year2 = Year2 - 1
+ Day2 = Day2 + DaysInMonth(Month2, Year2)
+ Days = abs(Day2-Day1)
+
+ #now months and years
+ if Month2 < Month1:
+ Month2 = Month2 + 12
+ Year2 = Year2 - 1
+ Months = Month2-Month1
+ Years = Year2-Year1
+
+ return Days, Months, Years
+
+
+def DayOfWeek(Julian):
+ """ Return the day of the week for the date. Returns DayType(7) if Julian == BadDate. """
+ if Julian == BadDate:
+ raise opdate_error, "bad date `%s'" % str(Julian)
+ else:
+ return (Julian+FirstDayOfWeek) % 7
+
+
+def DayOfWeekDMY(Day, Month, Year):
+ """ Return the day of the week for the day, month, year """
+ return DayOfWeek( DMYtoDate(Day, Month, Year) )
+
+
+#def MonthStringToMonth(MSt):
+# """ Convert the month name in MSt to a month (1..12) or -1 on error """
+# lmn = strptime.LongMonthNames[strptime.LANGUAGE]
+# smn = strptime.ShortMonthNames[strptime.LANGUAGE]
+# lmna = LongMonthNamesA
+#
+# I = FindStr(MSt, lmn)+1 or FindStr(MSt, smn)+1 or \
+# FindStrUC(MSt, lmn)+1 or FindStrUC(MSt, smn)+1 or \
+# FindStr(MSt, lmna)+1 or FindStrUC(MSt, lmna)+1
+#
+# return I-1
+
+
+def Today():
+ """ Returns today's date as a julian """
+ Year, Month, Day = localtime(time())[0:3]
+ return DMYtoDate(Day, Month, Year)
+
+#
+### Time manipulation routines
+#
+
+def TimeToHMS(T):
+ """ Convert a Time variable to Hours, Minutes, Seconds """
+ if T == BadTime:
+ return 0, 0, 0
+
+ else:
+ Hours = T / SecondsInHour
+ T = T - Hours*SecondsInHour
+ Minutes = T / SecondsInMinute
+ T = T - Minutes*SecondsInMinute
+ Seconds = T
+
+ return Hours, Minutes, Seconds
+
+
+def HMStoTime(Hours, Minutes, Seconds):
+ """ Convert Hours, Minutes, Seconds to a Time variable """
+ Hours = Hours % HoursInDay
+ T = Hours*SecondsInHour + Minutes*SecondsInMinute + Seconds
+
+ return T % SecondsInDay
+
+
+def ValidTime(Hours, Minutes, Seconds):
+ """ Return true if Hours:Minutes:Seconds is a valid time """
+ return (0 <= Hours < 24) and (0 <= Minutes < 60) and (0 <= Seconds < 60)
+
+
+def CurrentTime():
+ """ Returns current time in seconds since midnight """
+ Hours, Minutes, Seconds = localtime(time())[3:6]
+ return HMStoTime(Hours, Minutes, Seconds)
+
+
+def TimeDiff(Time1, Time2):
+ """ Return the difference in hours,minutes,seconds between two times """
+ if Time1 > Time2:
+ T = Time1-Time2
+ else:
+ T = Time2-Time1
+
+ Hours, Minutes, Seconds = TimeToHMS(T)
+ return Hours, Minutes, Seconds
+
+
+def IncTime(T, Hours, Minutes, Seconds):
+ """ Add the specified hours,minutes,seconds to T and return the result """
+ T = T + HMStoTime(Hours, Minutes, Seconds)
+ return T % SecondsInDay
+
+
+def DecTime(T, Hours, Minutes, Seconds):
+ """ Subtract the specified hours,minutes,seconds from T and return the result """
+ Hours = Hours % HoursInDay
+ T = T - HMStoTime(Hours, Minutes, Seconds)
+ if T < 0:
+ return T+SecondsInDay
+ else:
+ return T
+
+
+def RoundToNearestHour(T, Truncate = False):
+ """ Round T to the nearest hour, or Truncate minutes and seconds from T """
+ Hours, Minutes, Seconds = TimeToHMS(T)
+ Seconds = 0
+
+ if not Truncate:
+ if Minutes >= (MinutesInHour / 2):
+ Hours = Hours + 1
+
+ Minutes = 0
+ return HMStoTime(Hours, Minutes, Seconds)
+
+
+def RoundToNearestMinute(T, Truncate = False):
+ """ Round T to the nearest minute, or Truncate seconds from T """
+ Hours, Minutes, Seconds = TimeToHMS(T)
+
+ if not Truncate:
+ if Seconds >= (SecondsInMinute / 2):
+ Minutes = Minutes + 1
+
+ Seconds = 0
+ return HMStoTime(Hours, Minutes, Seconds)
+
+
+def DateTimeDiff(DT1, DT2):
+ """ Return the difference in days,seconds between two points in time """
+ # swap if DT1 later than DT2
+ if (DT1[0] > DT2[0]) or ((DT1[0] == DT2[0]) and (DT1[1] > DT2[1])):
+ _tmp = DT1
+ DT1 = DT2
+ DT2 = _tmp
+
+ # the difference in days is easy
+ Days = DT2[0]-DT1[0]
+
+ # difference in seconds
+ if DT2[1] < DT1[1]:
+ # subtract one day, add 24 hours
+ Days = Days - 1
+ DT2[1] = DT2[1] + SecondsInDay
+
+ Secs = DT2[1]-DT1[1]
+ return Days, Secs
+
+
+def IncDateTime(DT1, Days, Secs):
+ """ Increment (or decrement) DT1 by the specified number of days and seconds
+ and put the result in DT2 """
+ DT2 = DT1[:]
+
+ # date first
+ DT2[0] = DT2[0] + Days
+
+ if Secs < 0:
+ # change the sign
+ Secs = -Secs
+
+ # adjust the date
+ DT2[0] = DT2[0] - Secs / SecondsInDay
+ Secs = Secs % SecondsInDay
+
+ if Secs > DT2[1]:
+ # subtract a day from DT2[0] and add a day's worth of seconds to DT2[1]
+ DT2[0] = DT2[0] - 1
+ DT2[1] = DT2[1] + SecondsInDay
+
+ # now subtract the seconds
+ DT2[1] = DT2[1] - Secs
+
+ else:
+ # increment the seconds
+ DT2[1] = DT2[1] + Secs
+
+ # adjust date if necessary
+ DT2[0] = DT2[0] + DT2[1] / SecondsInDay
+
+ # force time to 0..SecondsInDay-1 range
+ DT2[1] = DT2[1] % SecondsInDay
+
+ return DT2
+
+
+#
+### UTC (GMT) stuff
+#
+
+UTC_0Date = DMYtoDate(1, 1, 1970)
+
+
+def DateTimeToGMT(Date, Time = False):
+ Date = Date - UTC_0Date
+ return Date*SecondsInDay + Time
+
+
+def GMTtoDateTime(GMT):
+ q, r = divmod(GMT, SecondsInDay)
+ return q + UTC_0Date, r
+
+
+#
+### Cyrillic stuff
+#
+
+LongMonthNamesA = ['Января', 'Февраля', 'Марта', 'Апреля', 'Мая', 'Июня',
+ 'Июля', 'Августа', 'Сентября', 'Октября', 'Ноября', 'Декабря']
+
+
+#
+### Test stuff
+#
+
+def test():
+ print "Is 1984 leap year?", IsLeapYear(1984)
+ print "Is 1990 leap year?", IsLeapYear(1990)
+
+ print "Days in month 8 year 1996:", DaysInMonth(8, 1996)
+
+ print "Is date 8/12/1996 valid?", ValidDate(8, 12, 1996)
+ print "Is date 40/11/1996 valid?", ValidDate(40, 11, 1996)
+ print "Is date 8/14/1996 valid?", ValidDate(8, 14, 1996)
+
+ print "Date->DMY for 138219:", DateToDMY(138219)
+
+ diff = DateDiff(DMYtoDate(12, 10, 1996), DMYtoDate(12, 10, 1997))
+ print "Date 12/10/1996 and date 12/10/1997 diff: %d years, %d months, %d days" % (diff[2], diff[1], diff[0])
+
+ diff = DateDiff(DMYtoDate(12, 10, 1996), DMYtoDate(12, 11, 1997))
+ print "Date 12/10/1996 and date 12/11/1997 diff: %d years, %d months, %d days" % (diff[2], diff[1], diff[0])
+
+ diff = DateDiff(DMYtoDate(31, 1, 1996), DMYtoDate(1, 3, 1996))
+ print "Date 31/01/1996 and date 01/03/1996 diff: %d years, %d months, %d days" % (diff[2], diff[1], diff[0])
+
+
+ #print "November is %dth month" % MonthStringToMonth("November")
+
+ print "Today is", Today()
+ print "Now is", CurrentTime()
+
+ print "My birthday 21 Dec 1967 is (must be Thursday):", day_name[DayOfWeekDMY(21, 12, 67)]
+
+ gmt = DateTimeToGMT(DMYtoDate(21, 12, 1967), HMStoTime(23, 45, 0))
+ print "21 Dec 1967, 23:45:00 --", gmtime(gmt) # DOS version of gmtime has error processing dates before 1/1/1970 :(
+ D, T = GMTtoDateTime(gmt)
+ print "(gmt) --", DateToDMY(D), TimeToHMS(T)
+
+if __name__ == "__main__":
+ test()
--- /dev/null
+#! /usr/bin/env python
+# -*- coding: koi8-r -*-
+
+#
+# opString - string/pathnames manipulation routines
+# Some ideas came from Turbo Professional/Object Professional (t/o)pString.PAS
+#
+# Written by Broytman, Nov 1997. Copyright (C) 1997 PhiloSoft Design
+#
+
+
+from string import *
+
+
+#
+### Int/string conversion routines
+#
+
+
+def bin(i):
+ """
+ Convert integer to binary string.
+ """
+ s = ''
+ q = i
+ while (1):
+ q, r = divmod(q, 2)
+ s = digits[r] + s
+ if q == 0: break
+
+ return s
+
+
+#
+### String manipulation routines
+#
+
+
+def PadCh(S, Ch, Len):
+ """ Return a string right-padded to length Len with Ch """
+ if len(S) >= Len:
+ return S
+ else:
+ return S + Ch*(Len - len(S))
+
+
+def Pad(S, Len):
+ """ Return a string right-padded to length len with blanks """
+ return PadCh(S, ' ', Len)
+
+
+def LeftPadCh(S, Ch, Len):
+ """ Return a string left-padded to length len with ch """
+ if len(S) >= Len:
+ return S
+ else:
+ return Ch*(Len - len(S)) + S
+
+
+def LeftPad(S, Len):
+ """ Return a string left-padded to length len with blanks """
+ return LeftPadCh(S, ' ', Len)
+
+
+def CenterCh(S, Ch, Width):
+ """ Return a string centered in a string of Ch with specified width """
+ if len(S) >= Width:
+ return S
+ else:
+ l = (Width - len(S)) / 2
+ r = Width - len(S) - l
+ return Ch*l + S + Ch*r
+
+
+def Center(S, Width):
+ """ Return a string centered in a blank string of specified width """
+ return CenterCh(S, ' ', Width)
+
+
+def FindStr(str, list):
+ """ Find given string in the list of strings """
+ for i in range(len(list)):
+ if str == list[i]:
+ return i
+
+ return -1
+
+
+def FindStrUC(str, list):
+ """ Find string ignoring case """
+ str = upper(str)
+ for i in range(len(list)):
+ if str == upper(list[i]):
+ return i
+
+ return -1
+
+
+# Склонения
+
+transl_adict = {
+ "day" : ["день", "дня", "дней"],
+ "week" : ["неделя", "недели", "недель"],
+ "month": ["месяц", "месяца", "месяцев"],
+ "year" : ["год", "года", "лет"]
+}
+
+transl_adict["days"] = transl_adict["day"]
+transl_adict["weeks"] = transl_adict["week"]
+transl_adict["months"] = transl_adict["month"]
+transl_adict["years"] = transl_adict["year"]
+
+transl_vdict = {
+ 1: 0,
+ 2: 1, 3: 1, 4: 1,
+ 5: 2, 6: 2, 7: 2, 8: 2, 9: 2, 0: 2
+}
+
+def translate_a(val, id):
+ if not transl_adict.has_key(id):
+ return ''
+
+ if 5 <= (val % 100) <= 20:
+ val = 2
+ else:
+ val = transl_vdict[val % 10]
+ return transl_adict[id][val]
+
+
+# Encodings, especially cyrillic. Requires Unicode, hence Python 2.0+
+
+def recode(s, from_encoding, to_encoding, errors = "strict"):
+ return unicode(s, from_encoding, errors).encode(to_encoding, errors)
+
+
+def win2koi(s, errors = "strict"):
+ return recode(s, "cp1251", "koi8-r", errors)
+
+def koi2win(s, errors = "strict"):
+ return recode(s, "koi8-r", "cp1251", errors)
+
+
+#
+### Test stuff
+#
+
+def test():
+ print "bin(0x6) =", bin(0x6)
+ print "bin(0xC) =", bin(0xC)
+
+ print "'Test' left-padded :", LeftPad("Test", 20)
+ print "'Test' right-padded:", PadCh("Test", '*', 20)
+ print "'Test' centered :", CenterCh("Test", '=', 20)
+
+ print "'Олег':", koi2win(win2koi("Олег"))
+
+if __name__ == "__main__":
+ test()
--- /dev/null
+"Broytman WWW Library for Python, Copyright (C) 1996-2003 PhiloSoft Design"
+
+
+__all__ = [
+ "rus2lat", "lat2rus"
+]
--- /dev/null
+#! /usr/bin/env python
+# -*- coding: koi8-r -*-
+
+#
+# Lat -> Rus translation
+# Written by Broytman. Copyright (C) 2002 PhiloSoft Design
+#
+
+lat2koi_d = {
+ "q": "й",
+ "w": "ц",
+ "e": "у",
+ "r": "к",
+ "t": "е",
+ "y": "н",
+ "u": "г",
+ "i": "ш",
+ "o": "щ",
+ "p": "з",
+ "[": "х",
+ "]": "ъ",
+ "a": "ф",
+ "s": "ы",
+ "d": "в",
+ "f": "а",
+ "g": "п",
+ "h": "р",
+ "j": "о",
+ "k": "л",
+ "l": "д",
+ ";": "ж",
+ "'": "э",
+ "z": "я",
+ "x": "ч",
+ "c": "с",
+ "v": "м",
+ "b": "и",
+ "n": "т",
+ "m": "ь",
+ ",": "б",
+ ".": "ю",
+ "Q": "Й",
+ "W": "Ц",
+ "E": "У",
+ "R": "К",
+ "T": "Е",
+ "Y": "Н",
+ "U": "Г",
+ "I": "Ш",
+ "O": "Щ",
+ "P": "З",
+ "{": "Х",
+ "}": "Ъ",
+ "A": "Ф",
+ "S": "Ы",
+ "D": "В",
+ "F": "А",
+ "G": "П",
+ "H": "Р",
+ "J": "О",
+ "K": "Л",
+ "L": "Д",
+ ":": "Ж",
+ "\"": "Э",
+ "Z": "Я",
+ "X": "Ч",
+ "C": "С",
+ "V": "М",
+ "B": "И",
+ "N": "Т",
+ "M": "Ь",
+ "<": "Б",
+ ">": "Ю",
+ "`": "ё",
+ "~": "Ё",
+ "!": "!",
+ "@": "\"",
+ "#": "#",
+ "$": "*",
+ "%": ":",
+ "^": ",",
+ "&": ".",
+ "*": ";"
+}
+
+
+def make_lat2xxx(encoding="cp1251"):
+ d = {}
+ for k, v in lat2koi_d.items():
+ v = unicode(v, "koi8-r").encode(encoding)
+ d[k] = v
+ return d
+
+
+from m_lib.lazy.dict import LazyDictInitFunc
+lat2win_d = LazyDictInitFunc(make_lat2xxx, encoding="cp1251")
+
+
+def lat2rus(instr, lat2rus_d = lat2koi_d):
+ out = []
+ for c in instr:
+ out.append(lat2rus_d.get(c, c))
+ return ''.join(out)
+
+
+lat2koi = lat2rus
+
+def lat2win(instr):
+ return lat2rus(instr, lat2win_d)
+
+
+if __name__ == "__main__":
+ Test = "Ghbdtn nt,t^ ghtrhfcysq vbh!"
+ print "Test:", Test
+ print "Тест:", lat2koi(Test)
+ print "Тест:", unicode(lat2win(Test), "cp1251").encode("koi8-r")
--- /dev/null
+#! /usr/bin/env python
+# -*- coding: koi8-r -*-
+
+#
+# Rus -> Lat transliteration (koi2lat and win2lat)
+# Written by Broytman. Copyright (C) 1997-2002 PhiloSoft Design
+#
+
+koi2lat_d = {
+ "А": "A",
+ "Б": "B",
+ "В": "V",
+ "Г": "G",
+ "Д": "D",
+ "Е": "E",
+ "Ж": "Zh",
+ "З": "Z",
+ "И": "I",
+ "Й": "Y",
+ "К": "K",
+ "Л": "L",
+ "М": "M",
+ "Н": "N",
+ "О": "O",
+ "П": "P",
+ "Р": "R",
+ "С": "S",
+ "Т": "T",
+ "У": "U",
+ "Ф": "F",
+ "Х": "H",
+ "Ц": "Ts",
+ "Ч": "Ch",
+ "Ш": "Sh",
+ "Щ": "Sh",
+ "Ъ": "'",
+ "Ь": "'",
+ "Ы": "Y",
+ "Э": "E",
+ "Ю": "Yu",
+ "Я": "Ya",
+ "а": "a",
+ "б": "b",
+ "в": "v",
+ "г": "g",
+ "д": "d",
+ "е": "e",
+ "ж": "zh",
+ "з": "z",
+ "и": "i",
+ "й": "y",
+ "к": "k",
+ "л": "l",
+ "м": "m",
+ "н": "n",
+ "о": "o",
+ "п": "p",
+ "р": "r",
+ "с": "s",
+ "т": "t",
+ "у": "u",
+ "ф": "f",
+ "х": "h",
+ "ц": "ts",
+ "ч": "ch",
+ "ш": "sh",
+ "щ": "sh",
+ "ъ": "'",
+ "ь": "'",
+ "ы": "y",
+ "э": "e",
+ "ю": "yu",
+ "я": "ya"
+}
+
+def make_xxx2lat(encoding="cp1251"):
+ d = {}
+ for k, v in koi2lat_d.items():
+ k = unicode(k, "koi8-r").encode(encoding)
+ d[k] = v
+ return d
+
+
+from m_lib.lazy.dict import LazyDictInitFunc
+win2lat_d = LazyDictInitFunc(make_xxx2lat, encoding="cp1251")
+
+
+def rus2lat(instr, rus2lat_d = koi2lat_d):
+ out = []
+ for c in instr:
+ out.append(rus2lat_d.get(c, c))
+ return ''.join(out)
+
+
+koi2lat = rus2lat
+
+def win2lat(instr):
+ return rus2lat(instr, win2lat_d)
+
+
+if __name__ == "__main__":
+ Test = "Щербаков Игорь Григорьевич. АБВ xyz абв ЬЬЭЮЯ ъьэюя"
+ print "Test:", Test
+ print "Тест:", koi2lat(Test)
+ print "Тест:", win2lat(unicode(Test, "koi8-r").encode("cp1251"))
--- /dev/null
+#! /usr/bin/env python
+"""tty menus
+
+ Written by Broytman, Mar 1998. Copyright (C) 1997 PhiloSoft Design.
+"""
+
+
+import string
+
+
+def hmenu(prompt, astring):
+ """
+ Writes prompt and read result
+ until result[0] is one of allowed characters (from astring),
+ and returns the character
+ """
+ while 1:
+ result = raw_input(prompt)
+ if len(result) > 0:
+ c = result[0]
+ if c in astring:
+ return c
+
+
+def vmenu(item_list, prompt, format = "%d. %s"):
+ """
+ Prints numbered list of items and allow user to select one,
+ returns selected number. Returns -1, if user enter non-numeric string.
+ """
+ for i in range(len(item_list)):
+ print format % (i, item_list[i])
+ print
+
+ result = raw_input(prompt)
+
+ try:
+ result = string.atoi(result)
+ except string.atoi_error:
+ result = -1
+
+ return result
+
+
+def test():
+ result = hmenu("Select: d)aily, w)eekly, m)onthly, c)ancel: ", "dwmc")
+ print "Answer is '%s'\n" % result
+
+ os_list = ["DOS", "Windows", "UNIX"]
+ result = vmenu(os_list, "Select OS: ")
+ if 0 <= result < len(os_list):
+ print "Answer is '%s'\n" % os_list[result]
+ else:
+ print "Wrong selection"
+
+if __name__ == "__main__":
+ test()
--- /dev/null
+#! /usr/bin/env python
+
+from distutils.core import setup
+
+setup(name = "m_lib",
+ version = "1.4",
+ description = "Broytman Library for Python",
+ long_description = "Broytman Library for Python, Copyright (C) 1996-2012 PhiloSoft Design",
+ author = "Oleg Broytman",
+ author_email = "phd@phdru.name",
+ url = "http://phdru.name/Software/Python/#m_lib",
+ license = "GPL",
+ platforms = "All",
+ packages = ["m_lib", "m_lib.clock", "m_lib.lazy",
+ "m_lib.net", "m_lib.net.ftp", "m_lib.net.www", "m_lib.rus"]
+)