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

Fred L. Drake, Jr. fred@zope.com
Tue, 18 Mar 2003 16:38:20 -0500


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

Added Files:
	TextBlockFormatter.py __init__.py lock_file.py zpasswd.py 
Log Message:
Merge startup code from the new-install-branch.

=== Zope/lib/python/Zope/Startup/misc/TextBlockFormatter.py 1.3 => 1.4 ===
--- /dev/null	Tue Mar 18 16:38:20 2003
+++ Zope/lib/python/Zope/Startup/misc/TextBlockFormatter.py	Tue Mar 18 16:37:49 2003
@@ -0,0 +1,128 @@
+##############################################################################
+#
+# 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$
+"""
+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)
+
+
+


=== Zope/lib/python/Zope/Startup/misc/__init__.py 1.3 => 1.4 ===
--- /dev/null	Tue Mar 18 16:38:20 2003
+++ Zope/lib/python/Zope/Startup/misc/__init__.py	Tue Mar 18 16:37:49 2003
@@ -0,0 +1 @@
+""" Placeholder module file """


=== Zope/lib/python/Zope/Startup/misc/lock_file.py 1.3 => 1.4 ===
--- /dev/null	Tue Mar 18 16:38:20 2003
+++ Zope/lib/python/Zope/Startup/misc/lock_file.py	Tue Mar 18 16:37:49 2003
@@ -0,0 +1,40 @@
+##############################################################################
+#
+# 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.
+#
+##############################################################################
+
+"""Utility function for file locking.
+
+This module provides a platform-specific function which uses the
+best-available strategy for locking a file object.
+"""
+
+try:
+    import fcntl
+except ImportError:
+    # Try windows-specific code:
+    try:
+        # We expect this module to exist, but the LockFile function may not.
+        from ZODB.winlock import LockFile
+    except ImportError:
+        # we don't understand any kind of locking, forget it
+        def lock_file(file):
+            pass
+    else:
+        # Windows
+        def lock_file(file):
+            un = file.fileno()
+            LockFile(un, 0, 0, 1, 0) # just lock the first byte, who cares
+else:
+    # Unix-specific locking:
+    def lock_file(file):
+        fcntl.flock(file, fcntl.LOCK_EX | fcntl.LOCK_NB)


=== Zope/lib/python/Zope/Startup/misc/zpasswd.py 1.3 => 1.4 ===
--- /dev/null	Tue Mar 18 16:38:20 2003
+++ Zope/lib/python/Zope/Startup/misc/zpasswd.py	Tue Mar 18 16:37:49 2003
@@ -0,0 +1,200 @@
+#!/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$ '[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)