[Zope-Checkins] CVS: Zope/lib/python/Zope/Startup/misc - TextBlockFormatter.py:1.1.2.1 TextBlockFormatter.pyc:1.1.2.1 __init__.py:1.1.2.1 __init__.pyc:1.1.2.1 factory.py:1.1.2.1 factory.pyc:1.1.2.1 lock_file.py:1.1.2.1 lock_file.pyc:1.1.2.1 zpasswd.py:1.1.2.1

Chris McDonough chrism@zope.com
Sat, 4 Jan 2003 23:01:44 -0500


Update of /cvs-repository/Zope/lib/python/Zope/Startup/misc
In directory cvs.zope.org:/tmp/cvs-serv4133/misc

Added Files:
      Tag: chrism-install-branch
	TextBlockFormatter.py TextBlockFormatter.pyc __init__.py 
	__init__.pyc factory.py factory.pyc lock_file.py lock_file.pyc 
	zpasswd.py 
Log Message:
Adding Startup to Zope package (moved from software_home).


=== Added File Zope/lib/python/Zope/Startup/misc/TextBlockFormatter.py ===
##############################################################################
#
# Copyright (c) 2001, 2002 Zope Corporation and Contributors.
# All Rights Reserved.
# 
# This software is subject to the provisions of the Zope Public License,
# Version 2.0 (ZPL).  A copy of the ZPL should accompany this distribution.
# THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED
# WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
# WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS
# FOR A PARTICULAR PURPOSE
# 
##############################################################################
"""
Revision information:
$Id: TextBlockFormatter.py,v 1.1.2.1 2003/01/05 04:01:39 chrism Exp $
"""
import string, math

def format(text, max_width=80, indent=0, trailing_lines_indent_more=0):
    text = string.expandtabs(string.replace(text, '\r', ''))
    lines = string.split(text, '\n')
    aggregate = []
    for line in lines:
        if len(line) <= max_width-1:
            # this line is short enough to output
            aggregate.append(line)
        else:
            lines = splitlongline(line, max_width)
            aggregate.extend(lines)
    out = []
    i = 0
    for line in aggregate:
        spaces = ' ' * indent
        if i != 0 and trailing_lines_indent_more:
            spaces = spaces + (' ' * trailing_lines_indent_more)
        out.append('%s%s' % (spaces, line))
        i = i + 1
    return string.join(out, '\n')

def splitword(word, max_width=80, linepos=0):
    # some lines may have single words that exceed the max_width
    # We want to break apart long words into as many chunks as necessary
    if len(word) <= max_width:
        return [word]
    first_chunk_len = max_width-1-linepos
    firstchunk = word[:first_chunk_len]
    word = word[first_chunk_len:]
    numchunks = int(math.ceil(len(word) / float(max_width-1)))
    index = 0
    tmp = [firstchunk]
    for chunknum in range(numchunks):
        chunk = word[index:index+max_width-1]
        tmp.append(chunk)
        index = index + max_width-1
    return tmp
    
def splitlongline(line, max_width=80):
    # split a "long" line defined by max_width into a list of lines
    line = string.strip(line)
    words = string.split(line, ' ')
    wordnum = 0
    # iterate over all the words in the line, extending the word list
    # necessary for too-long words
                
    aggregate = []
    linelen = 0
    wordnum = 0
    while words:
        word = words.pop(0)
        if not word: continue
        if len(word) > max_width:
            new_words = splitword(word, max_width, linelen)
            word = new_words[0]
            for new_word in new_words[1:]:
                words.insert(wordnum, new_word)
                wordnum = wordnum + 1
        if words:
            next_word = words[0]
        else:
            next_word = None
        if next_word is None:
            aggregate.append(word)
            wordnum = wordnum + 1
            continue
        maybe_len = linelen + len(word) + len(next_word)
        if maybe_len >= max_width-1:
            aggregate.append(word)
            aggregate.append(None)
            linelen = 0
        else:
            aggregate.append(word)
            linelen = linelen + len(word) + 1
        wordnum = wordnum + 1

    s = ""
    last = None
    for item in aggregate:
        if item is None:
            s = '%s\n' % s
        elif last is None:
            s = '%s%s' % (s, item)
        else:
            s = '%s %s' % (s, item)
        last = item
    return string.split(s, '\n')

long = """
To turn a component into a product you must fulfill many contracts. For the most part these contracts are not yet defined in terms of interfaces. Instead you must subclass from base classes that implement the contracts. This makes building products confusing, and this is an area that we are actively working on improving.  Hereisalonglinethatshouldbecaughtandbrokenupbytheformatter,hopefullyitllgetsplitupwhenirunitthroughthere."""
long2 = """
Hereisalonglinethatshouldbecaughtandbrokenupbytheformatter,hopefullyitllgetsplitupwhenirunitthroughthere."""
long3 = """
To turn a component into a product you must fulfill many contracts. For the most part these contracts are not yet defined in terms of interfaces.

Instead you must subclass from base classes that implement the contracts. This makes building products confusing, and this is an area that we are

actively working on improving.  Hereisalonglinethatshouldbecaughtandbrokenupbytheformatter,hopefullyitllgetsplitupwhenirunitthroughthere."""

if __name__ == '__main__':
    print format(long, 60, 10)
    print format(long)
    print format(long2, 60, 10)
    print format(long2)
    print format(long3, 60, 10)
    print format(long3)





=== Added File Zope/lib/python/Zope/Startup/misc/TextBlockFormatter.pyc ===
  <Binary-ish file>

=== Added File Zope/lib/python/Zope/Startup/misc/__init__.py ===
""" Placeholder module file """


=== Added File Zope/lib/python/Zope/Startup/misc/__init__.pyc ===
  <Binary-ish file>

=== Added File Zope/lib/python/Zope/Startup/misc/factory.py ===
_marker = []

def importer(name):
    components = name.split('.')
    start = components[0]
    g = globals()
    package = __import__(start, g, g)
    modulenames = [start]
    for component in components[1:]:
        modulenames.append(component)
        try:
            package = getattr(package, component)
        except AttributeError:
            name = '.'.join(modulenames)
            package = __import__(name, g, g, component)
    return package

class Factory:
    """
    A generic wrapper for instance construction and function calling used
    to delay construction/call until necessary.  The class path is the dotted
    name to the class or function, args are the positional args, kw are the
    keyword args. If it is specified, 'callback' is a function which will be
    called back after constructing an instance or calling a function.  It must
    take the instance (or the result of the function) as a single argument.
    """
    def __init__(self, class_path, callback, *args, **kw):
        self.class_path = class_path
        self.callback = callback
        self.setArgs(list(args), kw)
        self.resolved = _marker

    def __repr__(self):
        return ('<Factory instance for class "%s" with positional args "%s" '
                'and keword args "%s"' % (self.class_path, self.args, self.kw))

    __str__ = __repr__

    def __call__(self):
        if self.resolved is _marker:
            package = importer(self.class_path)
            inst = package(*self.args, **self.kw)
            if self.callback:
                self.callback(inst)
            self.resolved = inst
        return self.resolved

    def setArgs(self, args, kw):
        self.args = args
        self.kw = kw

    def getArgs(self):
        return (self.args, self.kw)


=== Added File Zope/lib/python/Zope/Startup/misc/factory.pyc ===
  <Binary-ish file>

=== Added File Zope/lib/python/Zope/Startup/misc/lock_file.py ===
##############################################################################
#
# Copyright (c) 2001, 2002 Zope Corporation and Contributors.
# All Rights Reserved.
#
# This software is subject to the provisions of the Zope Public License,
# Version 2.0 (ZPL).  A copy of the ZPL should accompany this distribution.
# THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED
# WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
# WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS
# FOR A PARTICULAR PURPOSE
#
##############################################################################
""" Utilities for file locking """
try:
    import fcntl
    def lock_file(file):
        un=file.fileno()
        fcntl.flock(un, fcntl.LOCK_EX | fcntl.LOCK_NB)
except:
    # Try windows-specific code:
    try:
        from ZODB.winlock import LockFile
        def lock_file(file):
            un=file.fileno()
            LockFile(un,0,0,1,0) # just lock the first byte, who cares
    except:
        # we don't understand any kind of locking, forget it
        def lock_file(file):
            pass


=== Added File Zope/lib/python/Zope/Startup/misc/lock_file.pyc ===
  <Binary-ish file>

=== Added File Zope/lib/python/Zope/Startup/misc/zpasswd.py ===
#!/usr/bin/env python
##############################################################################
#
# Copyright (c) 2001 Zope Corporation and Contributors. All Rights Reserved.
#
# This software is subject to the provisions of the Zope Public License,
# Version 2.0 (ZPL).  A copy of the ZPL should accompany this distribution.
# THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED
# WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
# WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS
# FOR A PARTICULAR PURPOSE
#
##############################################################################
"""Zope user bootstrap system"""

__version__='$Revision: 1.1.2.1 $ '[11:-2]

import sys,  sha, binascii, random, getopt, getpass, os

try:
    from crypt import crypt
except ImportError:
    crypt = None

def generate_salt():
    """Generate a salt value for the crypt function."""
    salt_choices = ("ABCDEFGHIJKLMNOPQRSTUVWXYZ"
                    "abcdefghijklmnopqrstuvwxyz"
                    "0123456789./")
    return random.choice(salt_choices)+random.choice(salt_choices)

def generate_passwd(password, encoding):
    encoding=encoding.upper()
    if encoding == 'SHA':
        pw = '{SHA}' + binascii.b2a_base64(sha.new(password).digest())[:-1]
    elif encoding == 'CRYPT':
        pw = '{CRYPT}' + crypt(password, generate_salt())
    elif encoding == 'CLEARTEXT':
        pw = password

    return pw

def write_generated_password(home, ac_path, username):
    pw_choices = ("ABCDEFGHIJKLMNOPQRSTUVWXYZ"
                  "abcdefghijklmnopqrstuvwxyz"
                  "0123456789!")
    acfile=open(ac_path, 'w')
    pw = ''
    for i in range(8):
        pw = pw + random.choice(pw_choices)
    acfile.write('%s:%s' % (username, generate_passwd(pw, 'SHA')))
    acfile.close()
    os.system('chmod 644 %s' % ac_path)
    return pw

def write_access(home, user='', group=''):
    ac_path=os.path.join(home, 'access')
    if not os.path.exists(ac_path):
        print '-'*78
        print 'creating default access file'
        pw = write_generated_password(home, ac_path, 'emergency')
        print """Note:
        The emergency user name and password are 'emergency'
        and '%s'.

        You can change the emergency name and password with the
        zpasswd script.  To find out more, type:

        %s zpasswd.py
        """ % (pw, sys.executable)

        import do; do.ch(ac_path, user, group)

def write_inituser(home, user='', group=''):
    ac_path=os.path.join(home, 'inituser')
    if not os.path.exists(ac_path):
        print '-'*78
        print 'creating default inituser file'
        pw = write_generated_password(home, ac_path, 'admin')
        print """Note:
        The initial user name and password are 'admin'
        and '%s'.

        You can change the name and password through the web
        interface or using the 'zpasswd.py' script.
        """ % pw

        import do; do.ch(ac_path, user, group)


def main(argv):
    short_options = ':u:p:e:d:'
    long_options = ['username=',
                    'password=',
                    'encoding=',
                    'domains=']

    usage = """Usage: %s [options] filename
If this program is called without command-line options, it will prompt
for all necessary information.  The available options are:

    -u / --username=
    Set the username to be used for the initial user or the emergency user

    -p / --password=
    Set the password

    -e / --encoding=
    Set the encryption/encoding rules.  Defaults to SHA-1. OPTIONAL

    -d / --domains=
    Set the domain names that the user user can log in from.  Defaults to
    any. OPTIONAL.

    Filename is required and should be the name of the file to store the
    information in (usually "inituser" or "access").

Copyright (C) 1999, 2000 Digital Creations, Inc.
""" % argv[0]

    try:
        if len(argv) < 2:
            raise "CommandLineError"

        optlist, args = getopt.getopt(sys.argv[1:], short_options, long_options)

        if len(args) != 1:
            raise "CommandLineError"

        access_file = open(args[0], 'w')

        if len(optlist) > 0:
            # Set the sane defaults
            username = ''
            encoding = 'SHA'
            domains = ''

            for opt in optlist:
                if (opt[0] == '-u') or (opt[0] == '--username'):
                    username = opt[1]
                elif (opt[0] == '-p') or (opt[0] == '--password'):
                    password = opt[1]
                elif (opt[0] == '-e') or (opt[0] == '--encoding'):
                    encoding = opt[1]
                elif (opt[0] == '-d') or (opt[0] == '--domains'):
                    domains = ":" + opt[1]

            # Verify that we got what we need
            if not username or not password:
                raise "CommandLineError"

            access_file.write(username + ':' +
                              generate_passwd(password, encoding) +
                              domains)

        else:
            # Run through the prompts
            while 1:
                username = raw_input("Username: ")
                if username != '':
                    break

            while 1:
                password = getpass.getpass("Password: ")
                verify = getpass.getpass("Verify password: ")
                if verify == password:
                    break
                else:
                    password = verify = ''
                    print "Password mismatch, please try again..."

            while 1:
                print """
Please choose a format from:

SHA - SHA-1 hashed password (default)
CRYPT - UNIX-style crypt password
CLEARTEXT - no protection
"""
                encoding = raw_input("Encoding: ")
                if encoding == '':
                    encoding = 'SHA'
                    break
                if encoding.upper() in ['SHA', 'CRYPT', 'CLEARTEXT']:
                    break

            domains = raw_input("Domain restrictions: ")
            if domains: domains = ":" + domains

            access_file.write(username + ":" +
                              generate_passwd(password, encoding) +
                              domains)

    except "CommandLineError":
        sys.stderr.write(usage)
        sys.exit(1)


# If called from the command line
if __name__=='__main__': main(sys.argv)