#!/usr/bin/python

# upload_cgi.py
#       --copyright--                   Copyright 2011 (C) Tranzoa, Co. All rights reserved.    Warranty: You're free and on your own here. This code is not necessarily up-to-date or of public quality.
#       --url--                         http://www.tranzoa.net/tzpython/
#       --email--                       pycode is the name to send to. tranzoa.com is the place to send to.
#       --bodstamps--
#       April 17, 2011          bar
#       April 18, 2011          bar     don't allow leading dots in uploaded file names
#                                       change the checksum file name to checksums.txt
#                                       occasionally add any files in the dir to the checksums if the file is not there
#       May 27, 2012            bar     doxygen namespace
#       --eodstamps--
##      \file
#       \namespace              tzpython.upload_cgi
#
#
#       Manage uploading files.
#       Allow the other end to query whether a file already exists by md5.
#       Put the uploaded files in the same directory as this script is in.
#
#       Note:
#           .htaccess wants:
#               Options         +ExecCGI
#               Options         -Indexes
#               AddType         application/x-httpd-cgi py
#               AddHandler      cgi-script             .py
#               Options -FollowSymLinks
#               SetEnv  PYTHONPATH  /home/_______USER_______/tzpython
#               <files ~ "\.txt$">
#               Deny from all
#               </files>
#
#

import  cgi
import  glob
import  os
import  random
import  re
import  stat

try :
    import  hashlib
except      ImportError       :
    import  md5
    import  sha
    class   a_hashlib(object) :
        def md5(me, s  = "")  :
            return(md5.new(s))
        def sha1(me, s = "")  :
            return(sha.new(s))
        pass
    hashlib = a_hashlib()


# import cgitb; cgitb.enable()
# or
#   import cgitb; cgitb.enable(display=0, logdir="/tmp")
# or
import  sys
sys.stderr = sys.stdout
#
#

try :
    import  tzlib
except ImportError :
    if  os.path.isdir(  "/home/alex/tzpython") :
        sys.path.append("/home/alex/tzpython")
    elif os.path.isdir( "I:/tzpython") :
        sys.path.append("I:/tzpython")
    elif os.path.isdir(       "../tzpython") :
        sys.path.append(      "../tzpython")
    elif os.path.isdir(    "../../tzpython") :
        sys.path.append(   "../../tzpython")
    elif os.path.isdir( "../../../tzpython") :
        sys.path.append("../../../tzpython")
    import  tzlib



CS_FILE_NAME    =   "checksums.txt"


def string_md5(str) :
    """ Return the lower case, hex, SHA1 of the given string. """

    return(hashlib.md5(str).hexdigest().lower())



def file_md5(fname) :
    """ Return the lower case, hex, SHA1 of the given file. """

    return(string_md5(tzlib.read_whole_binary_file(fname)))



all_cs          = {}
all_files       = {}

def remember_cs(fn, cs) :
    all_cs[cs]      = fn
    all_files[fn]   = cs

    fo  = open(CS_FILE_NAME, "a")
    fo.write("md5 %s %s\n" % ( cs, fn ) )
    fo.close()
    os.chmod(CS_FILE_NAME, stat.S_IREAD | stat.S_IWRITE | stat.S_IRUSR | stat.S_IWUSR | stat.S_IRGRP | stat.S_IWGRP | stat.S_IROTH | stat.S_IWOTH)



def get_cs_file_name(cs) :
    """ Is the given checksum in the checksum file? Return the file name from the checksum file, if so. Or None, if not. """

    if  not len(all_cs) :
        sd      = tzlib.safe_read_whole_text_file(CS_FILE_NAME)
        rx      = re.compile(r"^md5\s+([0-9a-f]{32})\s+(.*)$", re.MULTILINE + re.IGNORECASE)
        for fcs, fn in rx.findall(sd or '') :
            fn              = fn.rstrip()
            all_cs[fcs]     = fn
            all_files[fn]   = fcs

        if  random.random() >= 0.9 :
            fns     = glob.glob("*")
            for fn in fns   :
                if  (fn not in all_files) and (not fn.startswith('.')) and (fn != CS_FILE_NAME) and (not fn.endswith("~")) and (not fn.lower().endswith(".bak")) and (fn != os.path.basename(sys.argv[0])) and os.path.isfile(fn) :
                    fd      = tzlib.safe_read_whole_binary_file(fn)
                    if  fd  :
                        fcs = string_md5(fd)
                        if  fcs not in all_cs :
                            remember_cs(fn, fcs)
                        pass
                    pass
                pass
            pass
        pass

    return(all_cs.get(cs, None))



def store_file(fn, fd) :
    """ Store the file, returning the name or '' if we already have the file. Cheesie database of a text file with md5's of every file we've uploaded. """

    if  fn and len(fd) :
        rfn = fn
        dcs = string_md5(fd)
        if  get_cs_file_name(dcs) != None :
            rfn = ''
        else    :
            while   True :
                rfn = fn + "_%08x" % random.randint(0, 0x7fffFFFF)
                if  not os.path.exists(rfn) :
                    break
                pass
            tzlib.write_whole_binary_file(rfn, fd)
            os.chmod(rfn, stat.S_IREAD | stat.S_IWRITE | stat.S_IRUSR | stat.S_IWUSR | stat.S_IRGRP | stat.S_IWGRP | stat.S_IROTH | stat.S_IWOTH)

            remember_cs(rfn, dcs)
        pass

    return(rfn)




if  __name__ == '__main__' :

    program_name    = sys.argv[0]

    print "Content-Type: text/html"
    print

    form    = cgi.FieldStorage(keep_blank_values = 1)

    fd      = ''
    fn      = None
    if  'upload_file' in form :
        fd  = form.getfirst('upload_file')
        if  len(fd) :
            try :
                fn  = tzlib.file_name_able(form['upload_file'].filename)
            except (AttributeError, ValueError, TypeError ) :
                fn  = None
            if  fn  :
                bmsg    = ' of %u bytes' % len(fd)
                dmsg    = ''

                if  fn.startswith('.') :
                    fn  = "_" + fn[1:]
                rfn     = store_file(fn, fd)
                if  fn != rfn :
                    if  not rfn :
                        dmsg    = ' (duplicate)'
                    pass
                pass
            pass
        pass

    cs_msg  = ''
    if  'md5' in form :
        cs_msg  = "<p>\n"
        for cs in form.getlist('md5') :
            if  get_cs_file_name(cs) :
                cs_msg += "Known: <MD5_OK>%s</MD5_OK><br>\n" % cs
            else        :
                cs_msg += "Unknown: <MD5_UNK>%s</MD5_UNK><br>\n" % cs
            pass
        cs_msg += "<p>\n"


    if  os.path.isfile("upload.htm") :
        htm = tzlib.read_whole_text_file("upload.htm")
    else    :
        htm =   tzlib.multiline_flush_left("""
                <HTML>
                  <HEAD>

                    <SCRIPT LANGUAGE="JavaScript">

                      <!--

                        function check_input()
                        {
                          if (document.uplform.upload_file.value.length <= 0) {
                            alert('Please tell me a file name to upload!');

                            return(false);
                            }

                          return(true);
                        }

                        -->

                        </SCRIPT>


                    <TITLE>File Uploader</TITLE>

                    </HEAD>

                  <BODY BGCOLOR="#e4e4d0">

                    <A NAME="top" HREF="#top"></A>

                    <CENTER><H2>File Uploader</H2></CENTER>

                    <P>
                    <HR NOSHADE RAISED SIZE=10 COLOR=#808080>
                    <P>

                    <!-- MESSAGE_INSERT

                    <P>
                    <HR NOSHADE RAISED SIZE=10 COLOR=#808080>
                    <P>

                      -- END_MESSAGE -->

                    <P>

                    <FORM NAME="uplform" METHOD="POST" ACTION="%s" ENCTYPE="multipart/form-data" onSubmit="return check_input()" >

                      <input type="hidden" name="mode" value="upload">

                      <P>

                      <TABLE CELLSPACING=20>
                        <TR>
                          <TD>
                            Select file to be uploaded:

                            </TD>

                          <TD>
                            <input type="file" name="upload_file" value="fill me in" size="50" maxlength="80">

                            </TD>
                          </TR>



                        <TR>
                          <TD>
                            <input type="reset" name=".reset" value="Reset Input">

                            </TD>

                          <TD>
                            <input type="submit" name="submit" value="Upload Selected File Now!">

                            </TD>
                          </TR>

                        </TABLE>


                      </FORM>

                    <P>

                    <HR NOSHADE>
                    <HR NOSHADE>

                    </BODY>

                  </HTML>
                """)
    htm = htm % os.getenv('SCRIPT_NAME', program_name).replace("\\", "/")

    if  fn  :
        htm = htm.replace("<!-- MESSAGE_INSERT", "Uploaded [%s]%s%s%s" % ( fn, bmsg, dmsg, cs_msg, ) )
        htm = htm.replace("-- END_MESSAGE -->", "")

    print htm

#
#
# eof
