[pysignup] / trunk / cgi / functions.py Repository:
ViewVC logotype

View of /trunk/cgi/functions.py

Parent Directory Parent Directory | Revision Log Revision Log


Revision 99 - (download) (as text) (annotate)
Sun Oct 5 14:10:10 2008 UTC (23 months ago) by pinky
File size: 10924 byte(s)
making functions.createsqlite only create table if it doesn't exist yet
"""Generic functions used by PySignup.

@copyright: 2008 by Nathaniel Herman
@license: GNU GPLv3, see COPYING for more details
"""

###
# Copyright (C) 2008 Nathaniel Herman
#
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation; either version 3 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
# GNU General Public License for more details.

# You should have received a copy of the GNU General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
###


import string, random, sys, cgi, re
import smtplib
import cPickle as pickle
from configobj import ConfigObj

# if its py2.5 or higher, import hashlib, else import md5
# this isn't going to work if it starts using sha1, etc. so a different method
# will have to be used
if sys.version_info >= (2, 5, 0):
    import hashlib as md5
else:
    import md5

def save(file, data):
    """Appends given data to a given file.

    If it cannot write to the file, returns None, otherwise, returns True."""
    try:
        f = open(file, 'a')
    except IOError:
        return
    f.write(data)
    f.close()
    return True

def write(file, data):
    """Overwrites a given file with the given data.

    If it cannot write to the file, returns None, otherwise returns True."""
    try:
        f = open(file, 'w')
    except IOError:
        return
    f.write(data)
    f.close()
    return True

def read(file):
    """Attempts to open given file and return its contents.

    If the file doesn't exist, returns False"""

    try:
        f = open(file, 'r')
        contents = f.read()
        f.close()
    except IOError:
        contents = False
    return contents

def checkboolean(string):
    """Returns the boolean value of a given string if it were evaluated."""
    truvalues = ("on", "1", "true", "yes")
    falvalues = ("off", "0", "false", "no")   
    if string.lower() in truvalues:
        return True
    elif string.lower() in falvalues:
        return False
    elif not string:
        # if the string is blank, return false
        return False
    else:
        # they could've put anything thats not specifically true or false
        # so we'll assume true
        return True 

def createhtaccess(config):
    """Creates a .htaccess file if it doesn't already exist."""
    createhtaccess = config["required"]["createhtaccess"]
    datafile = config["file"]["data-file"]
    sessionfile = config["file"]["session-file"]
    sqlitedb = config['sqlite']['sqlitedb']
    # if they don't want to make a .htaccess file, just exit
    if not checkboolean(createhtaccess):
        return
    # Don't do anything if .htaccess already exists
    try:
        f = open(".htaccess", "r")
        if f.read():
            return
    except IOError:
        pass
    # now make sure .htaccess is writable by the web server
    try:
        f = open(".htaccess", "w")
        htaccess = """\
DirectoryIndex index.py

<FilesMatch ^(.*settings.conf.*|%s|%s|%s)$>
  deny from all
</FilesMatch>

RedirectMatch 403 /backups(/|$)""" % (datafile, sessionfile, sqlitedb)

        f.write(htaccess)
    except IOError:
        print "<p><b>Unable to write to .htaccess file!</b></p>"
        print "<p><b>Check permissions.</b></p>"

def _makesalt(length):
    """Returns a random salt of given length."""
    chars = string.letters + string.digits
    salt = random.sample(chars, length)
    return ''.join(salt)

def _md5hash(password, usesalt=False, saltlength=5):
    """Returns the md5 hash of a given string.

    usesalt determines whether it should use a salt with the string, and 
    saltlength is the length of the salt if usesalt is true. If usesalt is 
    true, the password hash, followed by the salt will be returned."""

    # initialize hash
    hash = md5.md5()
    if usesalt:
        salt = _makesalt(saltlength)
        hash.update(salt)
        hash.update(password)
        return hash.hexdigest(), salt
    else:
        hash.update(password)
        return hash.hexdigest()

def makehash(password, saltlength=5):
    """Returns a hash of the given password.

    Currently only makes md5 hashes."""

    # this allows the use of different hashing techniques in the future,
    # by using a different letter for the type (i.e. sha1, etc.)
    type = 'A'
    hash, salt = _md5hash(password, True, saltlength)
    return '%s:%s:%s' % (type, salt, hash)

def checkhash(hash, password):
    """Checks the given password against the given hash.

    Returns True if they match, False if they don't."""

    try:
        # seperate hash by each ":"
        type, salt, realhash = hash.split(':')
    # the hash is somehow invalid
    except:
        print "Invalid hash"
        return
        
    # this way we could add a "B" type hash, etc. which might use sha1 for
    # encryption as an example
    if type == 'A':
        checkhash = md5.md5()
        checkhash.update(salt)
        checkhash.update(password)
        return realhash == checkhash.hexdigest()
    else:
        print "Invalid hash"
        return

def makesessionid():
    """Returns a random, 20 character session id."""
    # a salt is basically a bunch of random characters, which will work fine
    # for a session id as well
    id = _makesalt(20)
    return id

def redirect(url):
    """Redirects browser to the given url."""
    print 'Status: 302 Moved'
    print 'Location: %s' % url
    print

def loadtheme(themename):
    """Returns the HTML to load the given theme name."""
    # none of the skins use JavaScript yet, but it still includes main.js
    # and user.js for future skin-specific JavaScript
    html = """
<link rel="stylesheet" type="text/css" href="skins/%(theme)s/main.css" />
<script type="text/javascript" src="skins/%(theme)s/main.js"></script>

<link rel="stylesheet" type="text/css" href="skins/%(theme)s/user.css" />
<script type="text/javascript" src="skins/%(theme)s/user.js"></script>
""" % {'theme': cgi.escape(themename)}

    return html

def escapebackslash(str):
    """Returns given string with all backslashes escaped.

    re.escape will do this as well, however re.escape also escapes all other
    non-alphanumeric characters."""

    newstr = ''
    for char in str:
        if char == '\\':
            char = '\\' + char
        newstr += char
    return newstr

def stripquotes(s):
    """Strips opening and closing quotes from given string."""
    if s[0] == s[-1] and s[0] == '"':
        s = s[1:-1]
    return s

def parselist(str):
    """Parses a comma seperated string into a Python list."""
    try:
        # looks for anything in quotes not followed by a comma
        nolistreg = re.match(r'(".*?")\s*(?!,)', str.strip()).group(1)
    except AttributeError:
        nolistreg = None
    if ',' not in str:
        return stripquotes(str)
    elif '"' not in str:
        return [item.strip() for item in str.split(',')]
    elif nolistreg == str.strip():
        return stripquotes(nolistreg)

    # listregex.findall will then return a list of each comma seperated entry
    listregex = re.compile(r'''
        (
            (?:".*?")|           # double quotes
            (?:[^",\#].*?)       # unquoted
        )
        (?:\s*,\s*|\s*$)         # comma or the end of the string
        ''',
        re.VERBOSE)

    list = listregex.findall(str.strip())

    list = [stripquotes(item) for item in list]

    return list

def parsedict(str):
    """Parses the given dictionary-like string into a list of tuples.

    The given string should look something like: "key1: value1, key2: value2" 
    Returns the result, which will be
    [('key1', 'value1'), ('key2', 'value2')]"""

    keys = []
    values = []

    # disabled in favor of a regex
    """
    if '"' in str:
        pos = 0
        list = []
        quoted = False
        while 1:
            try:
                char = str[pos]
            except IndexError:
                list.append(str)
                break
            if char == '"':
                if quoted:
                    quoted = False
                else:
                    quoted = True
                # basically removes the current " mark from str
                str = str[:pos] + str[pos + 1:]
                continue
            elif char == ',' and not quoted:
                list.append(str[:pos])
                str = str[pos + 1:]
                pos = 0
                continue
            pos += 1
    else: # if str doesn't contain any " marks, just split it by comma
        list = str.split(',')
    """

    # the first dictregex group will match everything on the left side of a
    # colon, i.e. "foo" in "foo: bar", and the second group will match 
    # everything on the right side until a comma or the end of the string.
    dictregex = re.compile(r'''
        (
            (?:".*?")|          # double quotes
            (?:[^":\#].*?)     # unquoted
        )
        (?:\s*:\s*)     # colon
        (
            (?:".*?")| # quotes
            (?:[^",\#].*?) # no quotes
        )
        (?:\s*,\s*|\s*$) # comma or end of string
        ''',
        re.VERBOSE)

    dict = dictregex.findall(str)
    return dict

    for entry in list:
        entrylist = dictregex.findall(entry)
        print entrylist
        key = entrylist[0].strip()
        value = entrylist[1].strip()
        keys.append(key)
        values.append(value)
    return zip(keys, values)

def mail(server, fromaddr, toaddr, subject, msg, user='', passwd=''):
    '''Sends an email using smtplib and the given information.''' 
    smtp = smtplib.SMTP(server)
    if user:
        smtp.login(user, passwd)
    body = "To: %s\nFrom: %s\nSubject: %s\n\n%s" % (toaddr, fromaddr, subject,
                                                    msg)
    smtp.sendmail(fromaddr, parselist(toaddr), body)
    
def createsqlite(conn, datafields=False):
    '''Creates the required tables at the given SQLite database connection.
    
    conn should be a connection to an SQLite database 
    i.e. conn = sqlite.connect('filename')
    datafields should be a tuple of all the datafields. If datafields is 
    passed, the signup table will be created, otherwise, the sessions table
    will be created.'''
    
    c = conn.cursor()
    
    if not datafields:
        c.execute('create table if not exists sessions (sessionid text)')
        c.close()
        conn.commit()
        return
        
    columns = '%s text, ' * len(datafields)
    statement = 'create table if not exists signup (status text, %s passwd \
text)' % columns
    statement %= datafields
    c.execute(statement)
    c.close()
    conn.commit()

Report a bug
ViewVC Help
Powered by ViewVC 1.0.5