clean up for installation
authorOliver Maurhart <oliver.maurhart@ait.ac.at>
Mon, 24 Feb 2014 12:36:53 +0100
changeset 77e48c45225df1
parent 76 6cf4ca255d98
child 80 f6c0b8f48ca8
clean up for installation
OpenSecurity.iss
OpenSecurity/install/OpenSecurity.reg
OpenSecurity/install/content.txt
OpenSecurity/install/fix_cygwin_paths.bat
OpenSecurity/install/initial_vm.bat
OpenSecurity/install/initial_vm.sh
OpenSecurity/install/shadowrun.exe
OpenSecurity/install/vbox_lookup.vbs
OpenSecurity/install/web.py-0.37/PKG-INFO
OpenSecurity/install/web.py-0.37/build/lib/web/__init__.py
OpenSecurity/install/web.py-0.37/build/lib/web/application.py
OpenSecurity/install/web.py-0.37/build/lib/web/browser.py
OpenSecurity/install/web.py-0.37/build/lib/web/contrib/__init__.py
OpenSecurity/install/web.py-0.37/build/lib/web/contrib/template.py
OpenSecurity/install/web.py-0.37/build/lib/web/db.py
OpenSecurity/install/web.py-0.37/build/lib/web/debugerror.py
OpenSecurity/install/web.py-0.37/build/lib/web/form.py
OpenSecurity/install/web.py-0.37/build/lib/web/http.py
OpenSecurity/install/web.py-0.37/build/lib/web/httpserver.py
OpenSecurity/install/web.py-0.37/build/lib/web/net.py
OpenSecurity/install/web.py-0.37/build/lib/web/python23.py
OpenSecurity/install/web.py-0.37/build/lib/web/session.py
OpenSecurity/install/web.py-0.37/build/lib/web/template.py
OpenSecurity/install/web.py-0.37/build/lib/web/test.py
OpenSecurity/install/web.py-0.37/build/lib/web/utils.py
OpenSecurity/install/web.py-0.37/build/lib/web/webapi.py
OpenSecurity/install/web.py-0.37/build/lib/web/webopenid.py
OpenSecurity/install/web.py-0.37/build/lib/web/wsgi.py
OpenSecurity/install/web.py-0.37/build/lib/web/wsgiserver/__init__.py
OpenSecurity/install/web.py-0.37/build/lib/web/wsgiserver/ssl_builtin.py
OpenSecurity/install/web.py-0.37/build/lib/web/wsgiserver/ssl_pyopenssl.py
OpenSecurity/install/web.py-0.37/setup.py
OpenSecurity/install/web.py-0.37/web/__init__.py
OpenSecurity/install/web.py-0.37/web/__init__.pyc
OpenSecurity/install/web.py-0.37/web/application.py
OpenSecurity/install/web.py-0.37/web/application.pyc
OpenSecurity/install/web.py-0.37/web/browser.py
OpenSecurity/install/web.py-0.37/web/browser.pyc
OpenSecurity/install/web.py-0.37/web/contrib/__init__.py
OpenSecurity/install/web.py-0.37/web/contrib/template.py
OpenSecurity/install/web.py-0.37/web/db.py
OpenSecurity/install/web.py-0.37/web/db.pyc
OpenSecurity/install/web.py-0.37/web/debugerror.py
OpenSecurity/install/web.py-0.37/web/debugerror.pyc
OpenSecurity/install/web.py-0.37/web/form.py
OpenSecurity/install/web.py-0.37/web/form.pyc
OpenSecurity/install/web.py-0.37/web/http.py
OpenSecurity/install/web.py-0.37/web/http.pyc
OpenSecurity/install/web.py-0.37/web/httpserver.py
OpenSecurity/install/web.py-0.37/web/httpserver.pyc
OpenSecurity/install/web.py-0.37/web/net.py
OpenSecurity/install/web.py-0.37/web/net.pyc
OpenSecurity/install/web.py-0.37/web/python23.py
OpenSecurity/install/web.py-0.37/web/session.py
OpenSecurity/install/web.py-0.37/web/session.pyc
OpenSecurity/install/web.py-0.37/web/template.py
OpenSecurity/install/web.py-0.37/web/template.pyc
OpenSecurity/install/web.py-0.37/web/test.py
OpenSecurity/install/web.py-0.37/web/utils.py
OpenSecurity/install/web.py-0.37/web/utils.pyc
OpenSecurity/install/web.py-0.37/web/webapi.py
OpenSecurity/install/web.py-0.37/web/webapi.pyc
OpenSecurity/install/web.py-0.37/web/webopenid.py
OpenSecurity/install/web.py-0.37/web/webopenid.pyc
OpenSecurity/install/web.py-0.37/web/wsgi.py
OpenSecurity/install/web.py-0.37/web/wsgi.pyc
OpenSecurity/install/web.py-0.37/web/wsgiserver/__init__.py
OpenSecurity/install/web.py-0.37/web/wsgiserver/ssl_builtin.py
OpenSecurity/install/web.py-0.37/web/wsgiserver/ssl_pyopenssl.py
     1.1 --- a/OpenSecurity.iss	Thu Feb 20 15:40:48 2014 +0100
     1.2 +++ b/OpenSecurity.iss	Mon Feb 24 12:36:53 2014 +0100
     1.3 @@ -21,24 +21,29 @@
     1.4  
     1.5  [Files]
     1.6  ; Files to copy
     1.7 -Source: "OpenSecurity\bin\*"; Excludes: "*.pyc"; DestDir: "{app}\bin"; Flags: recursesubdirs;
     1.8 -Source: "OpenSecurity\cygwin64\*"; DestDir: "{app}\cygwin64"; Flags: recursesubdirs;
     1.9 -Source: "OpenSecurity\gfx\*"; DestDir: "{app}\gfx"; Flags: recursesubdirs;
    1.10 -Source: "OpenSecurity\install\*"; DestDir: "{app}\install"; Flags: recursesubdirs;
    1.11 -Source: "OpenSecurity\python27\*"; DestDir: "{app}\python27"; Flags: recursesubdirs;
    1.12 +Source: "OpenSecurity\bin\*"; Excludes: "*.pyc"; DestDir: "{app}\bin"; Flags: recursesubdirs createallsubdirs;
    1.13 +Source: "OpenSecurity\cygwin64\*"; DestDir: "{app}\cygwin64"; Flags: recursesubdirs createallsubdirs;
    1.14 +Source: "OpenSecurity\gfx\*"; DestDir: "{app}\gfx"; Flags: recursesubdirs createallsubdirs;
    1.15 +Source: "OpenSecurity\install\*"; DestDir: "{app}\install"; Flags: recursesubdirs createallsubdirs;
    1.16 +Source: "OpenSecurity\python27\*"; DestDir: "{app}\python27"; Flags: recursesubdirs createallsubdirs;
    1.17  
    1.18  [Registry]
    1.19  ; Registry entries to set
    1.20 -Root: HKLM; Subkey: "SOFTWARE\Microsoft\Windows\CurrentVersion\Run"; ValueName: "OpenSecurity Daemon"; ValueType: string; ValueData: "{app}\python27\pythonw.exe ""{app}\bin\opensecurityd.pyw"""; Flags: uninsdeletevalue
    1.21 +Root: HKCU; Subkey: "SOFTWARE\Microsoft\Windows\CurrentVersion\Run"; ValueName: "OpenSecurity Daemon"; ValueType: string; ValueData: "{app}\python27\pythonw.exe ""{app}\bin\opensecurityd.pyw"""; Flags: uninsdeletevalue
    1.22  Root: HKCU; Subkey: "SOFTWARE\Microsoft\Windows\CurrentVersion\Run"; ValueName: "OpenSecurity Tray Icon"; ValueType: string; ValueData: "{app}\python27\pythonw.exe ""{app}\bin\opensecurity_tray.pyw"""; Flags: uninsdeletevalue
    1.23  
    1.24  [Icons]
    1.25  ; Program Icons in start menu
    1.26  Name: "{group}\OpenSecurity Tray Icon"; Filename: "{app}\python27\pythonw.exe"; Parameters: """{app}\bin\opensecurity_tray.pyw"""; WorkingDir: "{userappdata}"; Comment: "The OpenSecurity Tray Icon"; IconFilename: "{app}\gfx\OpenSecurity.ico"
    1.27  Name: "{group}\OpenSecurity Server"; Filename: "{app}\python27\pythonw.exe"; Parameters: """{app}\bin\opensecurityd.pyw"""; WorkingDir: "{userappdata}"; Comment: "The OpenSecurity VM System Orchestrating Server"; IconFilename: "{app}\gfx\OpenSecurity.ico"
    1.28 +Name: "{group}\Initial VM import"; Filename: "{app}\install\initial_vm.bat"; WorkingDir: "{userappdata}"; Comment: "The OpenSecurity VM System Orchestrating Server"; IconFilename: "{app}\gfx\OpenSecurity.ico"
    1.29  Name: "{group}\Uninstall OpenSecurity"; Filename: "{uninstallexe}"
    1.30  
    1.31  [Run]
    1.32  ; Run after installment
    1.33 +Filename: "{app}\cygwin64\bin\dash.exe"; Parameters: "/bin/rebaseall"; Description: "Rebasing background system"; WorkingDir: "{app}"; StatusMsg: "Rebasing background system..."; Flags: runascurrentuser
    1.34 +Filename: "{app}\install\fix_cygwin_paths.bat"; Description: "Fixing Cygwin paths"; WorkingDir: "{app}\install"; StatusMsg: "Fixing Cygwin Paths..";
    1.35  Filename: "{app}\install\initial_vm.bat"; Description: "Loading initial VM"; WorkingDir: "{app}\install"; StatusMsg: "Setting up initial VM..."; Flags: runasoriginaluser
    1.36  
    1.37 +[UninstallDelete]
    1.38 +Type: filesandordirs; Name: "{app}"
     2.1 Binary file OpenSecurity/install/OpenSecurity.reg has changed
     3.1 --- a/OpenSecurity/install/content.txt	Thu Feb 20 15:40:48 2014 +0100
     3.2 +++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
     3.3 @@ -1,19 +0,0 @@
     3.4 -Content of Opensecurity/install
     3.5 -===============================
     3.6 -
     3.7 -Additonally to the files already present in this folder, we need:
     3.8 -
     3.9 -- PyQt4-4.10.3-gpl-Py2.7-Qt4.8.5-x32.exe
    3.10 -- PyQt4-4.10.3-gpl-Py2.7-Qt4.8.5-x64.exe
    3.11 -    (--> http://www.riverbankcomputing.com/software/pyqt/download)
    3.12 -
    3.13 -- python-2.7.6.amd64.msi
    3.14 -- python-2.7.6.msi
    3.15 -    (--> http://www.python.org/download)
    3.16 -
    3.17 -- pywin32-218.win32-py2.7.exe
    3.18 -- pywin32-218.win-amd64-py2.7.exe    
    3.19 -    (--> http://sourceforge.net/projects/pywin32/files/pywin32)
    3.20 -    
    3.21 -- VirtualBox-4.3.4-91027-Win.exe
    3.22 -    (--> https://virtualbox.org/wiki/Downloads)
     4.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
     4.2 +++ b/OpenSecurity/install/fix_cygwin_paths.bat	Mon Feb 24 12:36:53 2014 +0100
     4.3 @@ -0,0 +1,5 @@
     4.4 +@echo off
     4.5 +cd %0%\..\..
     4.6 +echo %windir%\Temp /tmp ntfs binary,auto 1 1 >> cygwin64/etc/fstab
     4.7 +echo %HOMEDRIVE%/Users /home ntfs binary,auto 1 1 >> cygwin64/etc/fstab
     4.8 +copy install\initial_vm.sh cygwin64\usr\local\bin
     5.1 --- a/OpenSecurity/install/initial_vm.bat	Thu Feb 20 15:40:48 2014 +0100
     5.2 +++ b/OpenSecurity/install/initial_vm.bat	Mon Feb 24 12:36:53 2014 +0100
     5.3 @@ -1,115 +1,3 @@
     5.4  @echo off
     5.5 -
     5.6 -cd "%0%\.."
     5.7 -SET WGET=%0%\..\..\cygwin64\bin\wget.exe
     5.8 -SET OS_ADMIN_PORT=8080
     5.9 -
    5.10 -rem ------------------------------------------------------------
    5.11 -rem install initial OpenSecuity VM image
    5.12 -rem ------------------------------------------------------------
    5.13 -
    5.14 -SET OVA_TEMPLATE_URL="http://service.x-net.at/opensecurity/OsecVM_latest.ova"
    5.15 -
    5.16 -
    5.17 -:ENSURE_VBOX_READY
    5.18 -rem ensure environment is ready
    5.19 -rem This gets up VirtualBox VBoxSVC.exe COM Server right
    5.20 -rem and thus helps us not to run in any timing problems
    5.21 -rem due to a lazy VBox COM Server ... o.O
    5.22 -rem
    5.23 -rem 1. locate the VBoxManage.exe
    5.24 -rem 2. check version (ability to call)
    5.25 -rem 3. enumerate vms (ensure VBoxSVC.exe feels good)
    5.26 -
    5.27 -CSCRIPT //NoLogo vbox_lookup.vbs >%TEMP%\vbox_lookup
    5.28 -SET /P VBM_DIR=<%TEMP%\vbox_lookup
    5.29 -SET VBM="%VBM_DIR%VBoxManage.exe"
    5.30 -IF NOT EXIST %VBM% GOTO VBM_MISSING
    5.31 -%VBM% -version >%TEMP%\vbox_version
    5.32 -IF NOT ERRORLEVEL 0 GOTO VBM_ERROR
    5.33 -%VBM% list vms > %TEMP%\VMS.txt
    5.34 -del %TEMP%\VMS.txt
    5.35 -
    5.36 -
    5.37 -:PREPARE_VBOX_FOLDER
    5.38 -rem Now, VBox should feel good within the Windows OS space
    5.39 -rem switch into the User's VBox setup folders 
    5.40 -rem
    5.41 -rem 1. create Virtual box folder (if not already existing)
    5.42 -rem 2. download OVA template
    5.43 -rem 3. import OVS template in VBox
    5.44 -
    5.45 -SET VBP=%HOMEDRIVE%%HOMEPATH%\VirtualBox VMs
    5.46 -IF NOT EXIST "%VBP%" MKDIR "%VBP%"
    5.47 -
    5.48 -:DOWNLOAD_TEMPLATE_VM
    5.49 -rem Download the OSec.ova template from the X-Net servers
    5.50 -
    5.51 -IF EXIST OsecVM.ova GOTO COPY_TEMPLATE_VM
    5.52 -echo download initial OpenSecurity VM
    5.53 -"%WGET%" %OVA_TEMPLATE_URL%
    5.54 -IF NOT ERRORLEVEL 0 GOTO OVA_DOWNLOAD_ERROR
    5.55 -RENAME OsecVM_latest.ova OsecVM.ova
    5.56 -echo.
    5.57 -echo.
    5.58 -
    5.59 -:COPY_TEMPLATE_VM
    5.60 -echo copying template VM into Virtual Box's realm (this may take a while ... relax ...)
    5.61 -%HOMEDRIVE%
    5.62 -cd "%VBP%"
    5.63 -COPY %0%\..\OSecVM.ova .
    5.64 -echo.
    5.65 -echo.
    5.66 -
    5.67 -
    5.68 -:IMPORT_TEMPLATE_VM
    5.69 -echo importing OpenSecurity VM image ...
    5.70 -%VBM% import OsecVM.ova --vsys 0 --vmname SecurityDVM --unit 12 --disk SecurityDVM\SecurityDVM.vmdk
    5.71 -IF NOT ERRORLEVEL 0 GOTO VBM_IMPORT_ERROR
    5.72 -echo.
    5.73 -echo.
    5.74 -echo turning template image immutable
    5.75 -%VBM% storageattach SecurityDVM --storagectl SATA --port 0 --medium none
    5.76 -%VBM% storageattach SecurityDVM --storagectl SATA --port 0 --device 0 --type hdd --mtype writethrough --medium SecurityDVM\SecurityDVM.vmdk
    5.77 -%VBM% storageattach SecurityDVM --storagectl SATA --port 0 --medium none
    5.78 -%VBM% storageattach SecurityDVM --storagectl SATA --port 0 --device 0 --type hdd --mtype immutable --medium SecurityDVM\SecurityDVM.vmdk
    5.79 -echo OpenSecurity VM Image ready.
    5.80 -echo.
    5.81 -echo.
    5.82 -
    5.83 -:UPDATE_TEMPLATE
    5.84 -rem we have imported the Template
    5.85 -rem now, we need to update the template before first use
    5.86 -
    5.87 -cd %0%\..\..
    5.88 -SET PATH=%CD%\python27;%PATH%
    5.89 -START /I python27\python bin\opensecurityd.pyw
    5.90 -echo Waiting for OpenSecurity Admin Daemon to start
    5.91 -PAUSE
    5.92 -rem trigger update
    5.93 -echo %WGET% http://localhost:%OS_ADMIN_PORT%/update_template
    5.94 -%WGET% http://localhost:%OS_ADMIN_PORT%/update_template
    5.95 -GOTO END
    5.96 -
    5.97 -
    5.98 -:OVA_DOWNLOAD_ERROR
    5.99 -echo "Failed to download OVA initial image."
   5.100 -GOTO END
   5.101 -
   5.102 -
   5.103 -:VBM_ERROR
   5.104 -echo "Error calling VBoxManage.exe"
   5.105 -GOTO END
   5.106 -
   5.107 -
   5.108 -:VBM_MISSING
   5.109 -echo "Could not locate VBoxManage.exe --> Is VirtualBox installed?"
   5.110 -GOTO END
   5.111 -
   5.112 -
   5.113 -:VBM_IMPORT_ERROR
   5.114 -echo "Failed to import OVA template into VBox."
   5.115 -GOTO END
   5.116 -
   5.117 -
   5.118 -:END
   5.119 +CD %0%\..
   5.120 +CMD /C ..\cygwin64\bin\bash.exe -l -i -c initial_vm.sh
     6.1 --- a/OpenSecurity/install/initial_vm.sh	Thu Feb 20 15:40:48 2014 +0100
     6.2 +++ b/OpenSecurity/install/initial_vm.sh	Mon Feb 24 12:36:53 2014 +0100
     6.3 @@ -13,7 +13,8 @@
     6.4  # against humanity and should be punished by jail.
     6.5  #
     6.6  # To be called with the OpenSecurity installation folder
     6.7 -# like C:\Program Files\OpenSecurity
     6.8 +# like C:\Program Files\OpenSecurity as first argument
     6.9 +#
    6.10  #
    6.11  # (C)opyright 2014, AIT Austrian Instiitute of Technology
    6.12  # ------------------------------------------------------------
    6.13 @@ -26,24 +27,22 @@
    6.14  OPENSECURITY_PORT=8080
    6.15  OVA_TEMPLATE_URL="http://service.x-net.at/opensecurity/OsecVM_latest.ova"
    6.16  
    6.17 -# we try to call VBox commans several times
    6.18 -#
    6.19 -# rational: on windows VirtualBox relies on the
    6.20 -#           VBoxSVC.exe DCOM service
    6.21 -#           which to interact with the VirtualBox
    6.22 -#           images.
    6.23 -#
    6.24 -#           Sadly, this pretty buggy ...
    6.25 -#
    6.26 -# number of tries we do when interacting with vbox
    6.27 -VBOX_RETRY_COUNT="5"
    6.28 -
    6.29  
    6.30  # ------------------------------------------------------------
    6.31  # code
    6.32  
    6.33  
    6.34  # ------------------------------
    6.35 +# press a key message to the user
    6.36 +# 
    6.37 +function press_a_key() {
    6.38 +    echo "press a key ..."
    6.39 +    read -n 1 X
    6.40 +    echo
    6.41 +}
    6.42 +
    6.43 +
    6.44 +# ------------------------------
    6.45  # read a value from the windows regsitry
    6.46  #
    6.47  #   $1  ...     full registry key
    6.48 @@ -75,32 +74,15 @@
    6.49  
    6.50  
    6.51  # ------------------------------
    6.52 -# vbox command hammer
    6.53 -#
    6.54 -# retry the vbox command some times
    6.55 -# until sucess ... or death
    6.56 -#
    6.57 -#   $1  ...     full command to execute
    6.58 -#   stdout      the result
    6.59 -#
    6.60 -function vbox_command() {
    6.61 -
    6.62 -    echo "command failed ... :("
    6.63 -    exit 1
    6.64 -}
    6.65 -
    6.66 -
    6.67 -# ------------------------------
    6.68  # main ...
    6.69  #
    6.70  
    6.71  # check opensecurity folder
    6.72  #
    6.73  OPENSECURITY_DIR=$(sanitize_path "${OPENSECURITY_DIR}")
    6.74 +OSECVM_IMAGE="${OPENSECURITY_DIR}/install/OsecVM.ova"
    6.75  if [ ! -d "${OPENSECURITY_DIR}" ]; then
    6.76 -    echo "please specify a valid path to the OpenSecurity"
    6.77 -    echo "installation folder --> not a valid directory"
    6.78 -    exit 1
    6.79 +    echo "warning: no valid installation folder specified"
    6.80  fi
    6.81  
    6.82  # look up VirtulBox installation
    6.83 @@ -110,84 +92,127 @@
    6.84  if [ ! -x "${VBOX_MANAGER}" ]; then
    6.85      echo "can't execute VBoxManage.exe - is VirtualBox installed?"
    6.86      echo "looked at: "$(cygpath -w ${VBOX_MANAGER})""
    6.87 +    press_a_key
    6.88      exit 1
    6.89  fi
    6.90  
    6.91 -# enforce VirtualBox to "feel good" by calling some functions
    6.92 +# enforce VirtualBox to "feel good" by calling a function
    6.93 +# (that is to "warm up" VirtualBox DCOM server ...)
    6.94  #
    6.95  VBOX_VERSION=$("${VBOX_MANAGER}" -version)
    6.96  "${VBOX_MANAGER}" list vms &> /dev/null
    6.97  
    6.98  # download OSec.VM
    6.99  #
   6.100 -OSECVM_IMAGE="${OPENSECURITY_DIR}/install/OsecVM.ova"
   6.101  if [ ! -e "${OSECVM_IMAGE}" ]; then
   6.102 -    echo "downloading OSecVM.ova image"
   6.103 -    wget -O "${OSECVM_IMAGE}" "${OVA_TEMPLATE_URL}" 
   6.104 -    if [ "${?}" != "0" ]; then
   6.105 -        echo "failed to download OsecVM.ova"
   6.106 +    
   6.107 +    echo "checking for local OsecVM.ova image instance ..."
   6.108 +
   6.109 +    # we would like to place the OsecVM.ova image
   6.110 +    # inside the installation folder:
   6.111 +    #
   6.112 +    #       C:\Program Files\OpenSecurity
   6.113 +    #
   6.114 +    # BUT: this script also works in deep with
   6.115 +    #      VirtualBox and this one has a very
   6.116 +    #      picky understanding of user/admin
   6.117 +    #      permissions on Windows
   6.118 +    #
   6.119 +    # So, in oder to place any files under
   6.120 +    # C:\Program Files\OpenSecurity we need
   6.121 +    # this script running with elevated
   6.122 +    # user rights.
   6.123 +    #
   6.124 +    # ... which make VirtualBox managed VMs
   6.125 +    # by the install user impossible, since
   6.126 +    # VirtualBox gets confused by the User
   6.127 +    # privileges setup here in Windows
   6.128 +    # (Well, who doesn't anyway?).
   6.129 +
   6.130 +    # user space place of OsecVM.ova image
   6.131 +    # ---> USER/AppData/Roaming/OpenSecurity
   6.132 +   
   6.133 +    mkdir -p $(cygpath -O)/../AppData/Roaming/OpenSecurity &> /dev/null
   6.134 +    if [ ! "${?}" = "0" ]; then
   6.135 +        echo "failed to create folder $(cygpath -O)/../AppData/Roaming/OpenSecurity"
   6.136 +        echo "unable to proceed ..."
   6.137 +        press_a_key
   6.138          exit 1
   6.139      fi
   6.140 +
   6.141 +    OSECVM_IMAGE=$(cygpath -O)/../AppData/Roaming/OpenSecurity/OsecVM.ova
   6.142 +    if [ ! -e "${OSECVM_IMAGE}" ]; then
   6.143 +        echo "downloading OSecVM.ova image ..."
   6.144 +        wget -O "${OSECVM_IMAGE}" "${OVA_TEMPLATE_URL}" 
   6.145 +        if [ "${?}" != "0" ]; then
   6.146 +            echo "failed to download OsecVM.ova"
   6.147 +            press_a_key
   6.148 +            exit 1
   6.149 +        fi    
   6.150 +    fi
   6.151  fi
   6.152 +echo "using $(cygpath -w ${OSECVM_IMAGE}) as inital VM image to import ..."
   6.153 +
   6.154 +
   6.155 +# the next operations to produce some file I/O
   6.156 +# (especially SecurityDVM/SecurityDVM,vmdk)
   6.157 +# we therefore switch into the user's home
   6.158 +pushd  $(cygpath -O)/.. &> /dev/null
   6.159 +
   6.160 +# VirtualBox works on the User's home ...
   6.161 +# which is different in cygwin and pure windows
   6.162 +# yet another idiosyncrasy ... -.-
   6.163 +export HOME="$(cygpath -w "$(cygpath -O)/..")"
   6.164 +echo "HOME: ${HOME}"
   6.165 +
   6.166 +VDISK_IMAGE="VirtualBox VMs\SecurityDVM\SecurityDVM.vmdk"
   6.167  
   6.168  # import VM 
   6.169  #
   6.170 -for (( i=1; i<=${VBOX_RETRY_COUNT}; i++ )); do
   6.171 +"${VBOX_MANAGER}" list vms | grep SecurityDVM &> /dev/null
   6.172 +if [ ! "${?}" = "0" ]; then
   6.173 +    echo "importing VM: ${OSECVM_IMAGE}"
   6.174 +    "${VBOX_MANAGER}" import "$(cygpath -w "${OSECVM_IMAGE}")" --vsys 0 --vmname SecurityDVM --unit 12 --disk "${VDISK_IMAGE}"
   6.175 +else
   6.176 +    echo "found SecurityDVM already present in VBox reusing it."
   6.177 +    echo "if you want a complete new import please remove the VM first."
   6.178 +fi
   6.179  
   6.180 -    "${VBOX_MANAGER}" import "$(cygpath -w "${OSECVM_IMAGE}")" --vsys 0 --vmname SecurityDVM --unit 12 --disk "SecurityDVM\SecurityDVM.vmdk"
   6.181 -    test "${?}" -eq "0" && return
   6.182 -    echo "next try ..."
   6.183 -    sleep 1
   6.184 -done
   6.185 +# grab VM storage controller and port 
   6.186 +#
   6.187 +VDISK_SETUP=$("${VBOX_MANAGER}" showvminfo SecurityDVM | grep SecurityDVM.vmdk | cut -d ':' -f 1 | tr '(),' '   ')
   6.188 +VDISK_CONTROLLER=$(echo ${VDISK_SETUP} | gawk '{print $1;}')
   6.189 +VDISK_PORT=$(echo ${VDISK_SETUP} | gawk '{print $2;}')
   6.190 +VDISK_DEVICE=$(echo ${VDISK_SETUP} | gawk '{print $3;}')
   6.191 +if [ -z "${VDISK_CONTROLLER}" ]; then
   6.192 +    echo "unable to grab virtual disk controller in VM."
   6.193 +    echo "this shouldn't happen. It's a bug."
   6.194 +    press_a_key
   6.195 +    popd
   6.196 +    exit 1
   6.197 +fi
   6.198  
   6.199  # detach disk image
   6.200  #
   6.201 -for (( i=1; i<=${VBOX_RETRY_COUNT}; i++ )); do
   6.202 -
   6.203 -    "${VBOX_MANAGER}" storageattach SecurityDVM --storagectl SATA --port 0 --medium none
   6.204 -    test "${?}" -eq "0" && break
   6.205 -    echo "next try ..."
   6.206 -    sleep 1
   6.207 -done
   6.208 +echo "detaching disk image ..."
   6.209 +"${VBOX_MANAGER}" storageattach SecurityDVM --storagectl ${VDISK_CONTROLLER} --port ${VDISK_PORT} --medium none
   6.210  
   6.211  # turn disk image into writethrough
   6.212  #
   6.213 -for (( i=1; i<=${VBOX_RETRY_COUNT}; i++ )); do
   6.214 +echo "turning disk image into writetrough ..."
   6.215 +"${VBOX_MANAGER}" storageattach SecurityDVM --storagectl ${VDISK_CONTROLLER} --port ${VDISK_PORT} --device ${VDISK_DEVICE} --type hdd --mtype writethrough --medium "${VDISK_IMAGE}" 
   6.216  
   6.217 -    "${VBOX_MANAGER}" storageattach SecurityDVM --storagectl SATA --port 0 --device 0 --type hdd --mtype writethrough --medium 'SecurityDVM\SecurityDVM.vmdk'
   6.218 -    test "${?}" -eq "0" && break
   6.219 -    echo "next try ..."
   6.220 -    sleep 1
   6.221 -done
   6.222 +# detach disk image
   6.223 +#
   6.224 +echo "detaching disk image ..."
   6.225 +"${VBOX_MANAGER}" storageattach SecurityDVM --storagectl ${VDISK_CONTROLLER} --port ${VDISK_PORT} --medium none
   6.226  
   6.227 -# attach disk again
   6.228 +# immutablize disk
   6.229  #
   6.230 -for (( i=1; i<=${VBOX_RETRY_COUNT}; i++ )); do
   6.231 +echo "reattach immutable disk image ..."
   6.232 +"${VBOX_MANAGER}" storageattach SecurityDVM --storagectl ${VDISK_CONTROLLER} --port ${VDISK_PORT} --device ${VDISK_DEVICE} --type hdd --mtype immutable --medium "${VDISK_IMAGE}"
   6.233  
   6.234 -    "${VBOX_MANAGER}" storageattach SecurityDVM --storagectl SATA --port 0 --device 0 --type hdd --mtype immutable --medium 'SecurityDVM\SecurityDVM.vmdk'
   6.235 -    test "${?}" -eq "0" && break
   6.236 -    echo "next try ..."
   6.237 -    sleep 1
   6.238 -done
   6.239 +# return to current working dir
   6.240 +popd &> /dev/null
   6.241  
   6.242 -
   6.243 -
   6.244 -
   6.245 -#echo turning template image immutable
   6.246 -#echo step 1: detach image
   6.247 -#"${VBOX_MANAGER}" storageattach SecurityDVM --storagectl SATA --port 0 --medium none
   6.248 -#echo step 2: writethrough image
   6.249 -#echo.
   6.250 -#"${VBOX_MANAGER}" storageattach SecurityDVM --storagectl SATA --port 0 --device 0 --type hdd --mtype writethrough --medium SecurityDVM\SecurityDVM.vmdk
   6.251 -#echo step 3: detach image
   6.252 -#echo.
   6.253 -#"${VBOX_MANAGER}" storageattach SecurityDVM --storagectl SATA --port 0 --medium none
   6.254 -#echo step 4: imuteable
   6.255 -#echo.
   6.256 -#"${VBOX_MANAGER}" storageattach SecurityDVM --storagectl SATA --port 0 --device 0 --type hdd --mtype immutable --medium SecurityDVM\SecurityDVM.vmdk
   6.257 -#echo OpenSecurity VM Image ready.
   6.258 -#echo.
   6.259 -
   6.260 -
   6.261 -
   6.262 -
   6.263 +echo "imported initial OsecVM.ova image"
     7.1 Binary file OpenSecurity/install/shadowrun.exe has changed
     8.1 --- a/OpenSecurity/install/vbox_lookup.vbs	Thu Feb 20 15:40:48 2014 +0100
     8.2 +++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
     8.3 @@ -1,3 +0,0 @@
     8.4 -Set cShell = CreateObject("WScript.Shell")
     8.5 -WScript.Echo cShell.RegRead("HKLM\SOFTWARE\Oracle\VirtualBox\InstallDir")
     8.6 -
     9.1 --- a/OpenSecurity/install/web.py-0.37/PKG-INFO	Thu Feb 20 15:40:48 2014 +0100
     9.2 +++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
     9.3 @@ -1,10 +0,0 @@
     9.4 -Metadata-Version: 1.0
     9.5 -Name: web.py
     9.6 -Version: 0.37
     9.7 -Summary: web.py: makes web apps
     9.8 -Home-page:  http://webpy.org/
     9.9 -Author: Anand Chitipothu
    9.10 -Author-email: anandology@gmail.com
    9.11 -License: Public domain
    9.12 -Description: Think about the ideal way to write a web app. Write the code to make it happen.
    9.13 -Platform: any
    10.1 --- a/OpenSecurity/install/web.py-0.37/build/lib/web/__init__.py	Thu Feb 20 15:40:48 2014 +0100
    10.2 +++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
    10.3 @@ -1,33 +0,0 @@
    10.4 -#!/usr/bin/env python
    10.5 -"""web.py: makes web apps (http://webpy.org)"""
    10.6 -
    10.7 -from __future__ import generators
    10.8 -
    10.9 -__version__ = "0.37"
   10.10 -__author__ = [
   10.11 -    "Aaron Swartz <me@aaronsw.com>",
   10.12 -    "Anand Chitipothu <anandology@gmail.com>"
   10.13 -]
   10.14 -__license__ = "public domain"
   10.15 -__contributors__ = "see http://webpy.org/changes"
   10.16 -
   10.17 -import utils, db, net, wsgi, http, webapi, httpserver, debugerror
   10.18 -import template, form
   10.19 -
   10.20 -import session
   10.21 -
   10.22 -from utils import *
   10.23 -from db import *
   10.24 -from net import *
   10.25 -from wsgi import *
   10.26 -from http import *
   10.27 -from webapi import *
   10.28 -from httpserver import *
   10.29 -from debugerror import *
   10.30 -from application import *
   10.31 -from browser import *
   10.32 -try:
   10.33 -    import webopenid as openid
   10.34 -except ImportError:
   10.35 -    pass # requires openid module
   10.36 -
    11.1 --- a/OpenSecurity/install/web.py-0.37/build/lib/web/application.py	Thu Feb 20 15:40:48 2014 +0100
    11.2 +++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
    11.3 @@ -1,687 +0,0 @@
    11.4 -"""
    11.5 -Web application
    11.6 -(from web.py)
    11.7 -"""
    11.8 -import webapi as web
    11.9 -import webapi, wsgi, utils
   11.10 -import debugerror
   11.11 -import httpserver
   11.12 -
   11.13 -from utils import lstrips, safeunicode
   11.14 -import sys
   11.15 -
   11.16 -import urllib
   11.17 -import traceback
   11.18 -import itertools
   11.19 -import os
   11.20 -import types
   11.21 -from exceptions import SystemExit
   11.22 -
   11.23 -try:
   11.24 -    import wsgiref.handlers
   11.25 -except ImportError:
   11.26 -    pass # don't break people with old Pythons
   11.27 -
   11.28 -__all__ = [
   11.29 -    "application", "auto_application",
   11.30 -    "subdir_application", "subdomain_application", 
   11.31 -    "loadhook", "unloadhook",
   11.32 -    "autodelegate"
   11.33 -]
   11.34 -
   11.35 -class application:
   11.36 -    """
   11.37 -    Application to delegate requests based on path.
   11.38 -    
   11.39 -        >>> urls = ("/hello", "hello")
   11.40 -        >>> app = application(urls, globals())
   11.41 -        >>> class hello:
   11.42 -        ...     def GET(self): return "hello"
   11.43 -        >>>
   11.44 -        >>> app.request("/hello").data
   11.45 -        'hello'
   11.46 -    """
   11.47 -    def __init__(self, mapping=(), fvars={}, autoreload=None):
   11.48 -        if autoreload is None:
   11.49 -            autoreload = web.config.get('debug', False)
   11.50 -        self.init_mapping(mapping)
   11.51 -        self.fvars = fvars
   11.52 -        self.processors = []
   11.53 -        
   11.54 -        self.add_processor(loadhook(self._load))
   11.55 -        self.add_processor(unloadhook(self._unload))
   11.56 -        
   11.57 -        if autoreload:
   11.58 -            def main_module_name():
   11.59 -                mod = sys.modules['__main__']
   11.60 -                file = getattr(mod, '__file__', None) # make sure this works even from python interpreter
   11.61 -                return file and os.path.splitext(os.path.basename(file))[0]
   11.62 -
   11.63 -            def modname(fvars):
   11.64 -                """find name of the module name from fvars."""
   11.65 -                file, name = fvars.get('__file__'), fvars.get('__name__')
   11.66 -                if file is None or name is None:
   11.67 -                    return None
   11.68 -
   11.69 -                if name == '__main__':
   11.70 -                    # Since the __main__ module can't be reloaded, the module has 
   11.71 -                    # to be imported using its file name.                    
   11.72 -                    name = main_module_name()
   11.73 -                return name
   11.74 -                
   11.75 -            mapping_name = utils.dictfind(fvars, mapping)
   11.76 -            module_name = modname(fvars)
   11.77 -            
   11.78 -            def reload_mapping():
   11.79 -                """loadhook to reload mapping and fvars."""
   11.80 -                mod = __import__(module_name, None, None, [''])
   11.81 -                mapping = getattr(mod, mapping_name, None)
   11.82 -                if mapping:
   11.83 -                    self.fvars = mod.__dict__
   11.84 -                    self.init_mapping(mapping)
   11.85 -
   11.86 -            self.add_processor(loadhook(Reloader()))
   11.87 -            if mapping_name and module_name:
   11.88 -                self.add_processor(loadhook(reload_mapping))
   11.89 -
   11.90 -            # load __main__ module usings its filename, so that it can be reloaded.
   11.91 -            if main_module_name() and '__main__' in sys.argv:
   11.92 -                try:
   11.93 -                    __import__(main_module_name())
   11.94 -                except ImportError:
   11.95 -                    pass
   11.96 -                    
   11.97 -    def _load(self):
   11.98 -        web.ctx.app_stack.append(self)
   11.99 -        
  11.100 -    def _unload(self):
  11.101 -        web.ctx.app_stack = web.ctx.app_stack[:-1]
  11.102 -        
  11.103 -        if web.ctx.app_stack:
  11.104 -            # this is a sub-application, revert ctx to earlier state.
  11.105 -            oldctx = web.ctx.get('_oldctx')
  11.106 -            if oldctx:
  11.107 -                web.ctx.home = oldctx.home
  11.108 -                web.ctx.homepath = oldctx.homepath
  11.109 -                web.ctx.path = oldctx.path
  11.110 -                web.ctx.fullpath = oldctx.fullpath
  11.111 -                
  11.112 -    def _cleanup(self):
  11.113 -        # Threads can be recycled by WSGI servers.
  11.114 -        # Clearing up all thread-local state to avoid interefereing with subsequent requests.
  11.115 -        utils.ThreadedDict.clear_all()
  11.116 -
  11.117 -    def init_mapping(self, mapping):
  11.118 -        self.mapping = list(utils.group(mapping, 2))
  11.119 -
  11.120 -    def add_mapping(self, pattern, classname):
  11.121 -        self.mapping.append((pattern, classname))
  11.122 -
  11.123 -    def add_processor(self, processor):
  11.124 -        """
  11.125 -        Adds a processor to the application. 
  11.126 -        
  11.127 -            >>> urls = ("/(.*)", "echo")
  11.128 -            >>> app = application(urls, globals())
  11.129 -            >>> class echo:
  11.130 -            ...     def GET(self, name): return name
  11.131 -            ...
  11.132 -            >>>
  11.133 -            >>> def hello(handler): return "hello, " +  handler()
  11.134 -            ...
  11.135 -            >>> app.add_processor(hello)
  11.136 -            >>> app.request("/web.py").data
  11.137 -            'hello, web.py'
  11.138 -        """
  11.139 -        self.processors.append(processor)
  11.140 -
  11.141 -    def request(self, localpart='/', method='GET', data=None,
  11.142 -                host="0.0.0.0:8080", headers=None, https=False, **kw):
  11.143 -        """Makes request to this application for the specified path and method.
  11.144 -        Response will be a storage object with data, status and headers.
  11.145 -
  11.146 -            >>> urls = ("/hello", "hello")
  11.147 -            >>> app = application(urls, globals())
  11.148 -            >>> class hello:
  11.149 -            ...     def GET(self): 
  11.150 -            ...         web.header('Content-Type', 'text/plain')
  11.151 -            ...         return "hello"
  11.152 -            ...
  11.153 -            >>> response = app.request("/hello")
  11.154 -            >>> response.data
  11.155 -            'hello'
  11.156 -            >>> response.status
  11.157 -            '200 OK'
  11.158 -            >>> response.headers['Content-Type']
  11.159 -            'text/plain'
  11.160 -
  11.161 -        To use https, use https=True.
  11.162 -
  11.163 -            >>> urls = ("/redirect", "redirect")
  11.164 -            >>> app = application(urls, globals())
  11.165 -            >>> class redirect:
  11.166 -            ...     def GET(self): raise web.seeother("/foo")
  11.167 -            ...
  11.168 -            >>> response = app.request("/redirect")
  11.169 -            >>> response.headers['Location']
  11.170 -            'http://0.0.0.0:8080/foo'
  11.171 -            >>> response = app.request("/redirect", https=True)
  11.172 -            >>> response.headers['Location']
  11.173 -            'https://0.0.0.0:8080/foo'
  11.174 -
  11.175 -        The headers argument specifies HTTP headers as a mapping object
  11.176 -        such as a dict.
  11.177 -
  11.178 -            >>> urls = ('/ua', 'uaprinter')
  11.179 -            >>> class uaprinter:
  11.180 -            ...     def GET(self):
  11.181 -            ...         return 'your user-agent is ' + web.ctx.env['HTTP_USER_AGENT']
  11.182 -            ... 
  11.183 -            >>> app = application(urls, globals())
  11.184 -            >>> app.request('/ua', headers = {
  11.185 -            ...      'User-Agent': 'a small jumping bean/1.0 (compatible)'
  11.186 -            ... }).data
  11.187 -            'your user-agent is a small jumping bean/1.0 (compatible)'
  11.188 -
  11.189 -        """
  11.190 -        path, maybe_query = urllib.splitquery(localpart)
  11.191 -        query = maybe_query or ""
  11.192 -        
  11.193 -        if 'env' in kw:
  11.194 -            env = kw['env']
  11.195 -        else:
  11.196 -            env = {}
  11.197 -        env = dict(env, HTTP_HOST=host, REQUEST_METHOD=method, PATH_INFO=path, QUERY_STRING=query, HTTPS=str(https))
  11.198 -        headers = headers or {}
  11.199 -
  11.200 -        for k, v in headers.items():
  11.201 -            env['HTTP_' + k.upper().replace('-', '_')] = v
  11.202 -
  11.203 -        if 'HTTP_CONTENT_LENGTH' in env:
  11.204 -            env['CONTENT_LENGTH'] = env.pop('HTTP_CONTENT_LENGTH')
  11.205 -
  11.206 -        if 'HTTP_CONTENT_TYPE' in env:
  11.207 -            env['CONTENT_TYPE'] = env.pop('HTTP_CONTENT_TYPE')
  11.208 -
  11.209 -        if method not in ["HEAD", "GET"]:
  11.210 -            data = data or ''
  11.211 -            import StringIO
  11.212 -            if isinstance(data, dict):
  11.213 -                q = urllib.urlencode(data)
  11.214 -            else:
  11.215 -                q = data
  11.216 -            env['wsgi.input'] = StringIO.StringIO(q)
  11.217 -            if not env.get('CONTENT_TYPE', '').lower().startswith('multipart/') and 'CONTENT_LENGTH' not in env:
  11.218 -                env['CONTENT_LENGTH'] = len(q)
  11.219 -        response = web.storage()
  11.220 -        def start_response(status, headers):
  11.221 -            response.status = status
  11.222 -            response.headers = dict(headers)
  11.223 -            response.header_items = headers
  11.224 -        response.data = "".join(self.wsgifunc()(env, start_response))
  11.225 -        return response
  11.226 -
  11.227 -    def browser(self):
  11.228 -        import browser
  11.229 -        return browser.AppBrowser(self)
  11.230 -
  11.231 -    def handle(self):
  11.232 -        fn, args = self._match(self.mapping, web.ctx.path)
  11.233 -        return self._delegate(fn, self.fvars, args)
  11.234 -        
  11.235 -    def handle_with_processors(self):
  11.236 -        def process(processors):
  11.237 -            try:
  11.238 -                if processors:
  11.239 -                    p, processors = processors[0], processors[1:]
  11.240 -                    return p(lambda: process(processors))
  11.241 -                else:
  11.242 -                    return self.handle()
  11.243 -            except web.HTTPError:
  11.244 -                raise
  11.245 -            except (KeyboardInterrupt, SystemExit):
  11.246 -                raise
  11.247 -            except:
  11.248 -                print >> web.debug, traceback.format_exc()
  11.249 -                raise self.internalerror()
  11.250 -        
  11.251 -        # processors must be applied in the resvere order. (??)
  11.252 -        return process(self.processors)
  11.253 -                        
  11.254 -    def wsgifunc(self, *middleware):
  11.255 -        """Returns a WSGI-compatible function for this application."""
  11.256 -        def peep(iterator):
  11.257 -            """Peeps into an iterator by doing an iteration
  11.258 -            and returns an equivalent iterator.
  11.259 -            """
  11.260 -            # wsgi requires the headers first
  11.261 -            # so we need to do an iteration
  11.262 -            # and save the result for later
  11.263 -            try:
  11.264 -                firstchunk = iterator.next()
  11.265 -            except StopIteration:
  11.266 -                firstchunk = ''
  11.267 -
  11.268 -            return itertools.chain([firstchunk], iterator)    
  11.269 -                                
  11.270 -        def is_generator(x): return x and hasattr(x, 'next')
  11.271 -        
  11.272 -        def wsgi(env, start_resp):
  11.273 -            # clear threadlocal to avoid inteference of previous requests
  11.274 -            self._cleanup()
  11.275 -
  11.276 -            self.load(env)
  11.277 -            try:
  11.278 -                # allow uppercase methods only
  11.279 -                if web.ctx.method.upper() != web.ctx.method:
  11.280 -                    raise web.nomethod()
  11.281 -
  11.282 -                result = self.handle_with_processors()
  11.283 -                if is_generator(result):
  11.284 -                    result = peep(result)
  11.285 -                else:
  11.286 -                    result = [result]
  11.287 -            except web.HTTPError, e:
  11.288 -                result = [e.data]
  11.289 -
  11.290 -            result = web.safestr(iter(result))
  11.291 -
  11.292 -            status, headers = web.ctx.status, web.ctx.headers
  11.293 -            start_resp(status, headers)
  11.294 -            
  11.295 -            def cleanup():
  11.296 -                self._cleanup()
  11.297 -                yield '' # force this function to be a generator
  11.298 -                            
  11.299 -            return itertools.chain(result, cleanup())
  11.300 -
  11.301 -        for m in middleware: 
  11.302 -            wsgi = m(wsgi)
  11.303 -
  11.304 -        return wsgi
  11.305 -
  11.306 -    def run(self, *middleware):
  11.307 -        """
  11.308 -        Starts handling requests. If called in a CGI or FastCGI context, it will follow
  11.309 -        that protocol. If called from the command line, it will start an HTTP
  11.310 -        server on the port named in the first command line argument, or, if there
  11.311 -        is no argument, on port 8080.
  11.312 -        
  11.313 -        `middleware` is a list of WSGI middleware which is applied to the resulting WSGI
  11.314 -        function.
  11.315 -        """
  11.316 -        return wsgi.runwsgi(self.wsgifunc(*middleware))
  11.317 -
  11.318 -    def stop(self):
  11.319 -        """Stops the http server started by run.
  11.320 -        """
  11.321 -        if httpserver.server:
  11.322 -            httpserver.server.stop()
  11.323 -            httpserver.server = None
  11.324 -    
  11.325 -    def cgirun(self, *middleware):
  11.326 -        """
  11.327 -        Return a CGI handler. This is mostly useful with Google App Engine.
  11.328 -        There you can just do:
  11.329 -        
  11.330 -            main = app.cgirun()
  11.331 -        """
  11.332 -        wsgiapp = self.wsgifunc(*middleware)
  11.333 -
  11.334 -        try:
  11.335 -            from google.appengine.ext.webapp.util import run_wsgi_app
  11.336 -            return run_wsgi_app(wsgiapp)
  11.337 -        except ImportError:
  11.338 -            # we're not running from within Google App Engine
  11.339 -            return wsgiref.handlers.CGIHandler().run(wsgiapp)
  11.340 -    
  11.341 -    def load(self, env):
  11.342 -        """Initializes ctx using env."""
  11.343 -        ctx = web.ctx
  11.344 -        ctx.clear()
  11.345 -        ctx.status = '200 OK'
  11.346 -        ctx.headers = []
  11.347 -        ctx.output = ''
  11.348 -        ctx.environ = ctx.env = env
  11.349 -        ctx.host = env.get('HTTP_HOST')
  11.350 -
  11.351 -        if env.get('wsgi.url_scheme') in ['http', 'https']:
  11.352 -            ctx.protocol = env['wsgi.url_scheme']
  11.353 -        elif env.get('HTTPS', '').lower() in ['on', 'true', '1']:
  11.354 -            ctx.protocol = 'https'
  11.355 -        else:
  11.356 -            ctx.protocol = 'http'
  11.357 -        ctx.homedomain = ctx.protocol + '://' + env.get('HTTP_HOST', '[unknown]')
  11.358 -        ctx.homepath = os.environ.get('REAL_SCRIPT_NAME', env.get('SCRIPT_NAME', ''))
  11.359 -        ctx.home = ctx.homedomain + ctx.homepath
  11.360 -        #@@ home is changed when the request is handled to a sub-application.
  11.361 -        #@@ but the real home is required for doing absolute redirects.
  11.362 -        ctx.realhome = ctx.home
  11.363 -        ctx.ip = env.get('REMOTE_ADDR')
  11.364 -        ctx.method = env.get('REQUEST_METHOD')
  11.365 -        ctx.path = env.get('PATH_INFO')
  11.366 -        # http://trac.lighttpd.net/trac/ticket/406 requires:
  11.367 -        if env.get('SERVER_SOFTWARE', '').startswith('lighttpd/'):
  11.368 -            ctx.path = lstrips(env.get('REQUEST_URI').split('?')[0], ctx.homepath)
  11.369 -            # Apache and CherryPy webservers unquote the url but lighttpd doesn't. 
  11.370 -            # unquote explicitly for lighttpd to make ctx.path uniform across all servers.
  11.371 -            ctx.path = urllib.unquote(ctx.path)
  11.372 -
  11.373 -        if env.get('QUERY_STRING'):
  11.374 -            ctx.query = '?' + env.get('QUERY_STRING', '')
  11.375 -        else:
  11.376 -            ctx.query = ''
  11.377 -
  11.378 -        ctx.fullpath = ctx.path + ctx.query
  11.379 -        
  11.380 -        for k, v in ctx.iteritems():
  11.381 -            # convert all string values to unicode values and replace 
  11.382 -            # malformed data with a suitable replacement marker.
  11.383 -            if isinstance(v, str):
  11.384 -                ctx[k] = v.decode('utf-8', 'replace') 
  11.385 -
  11.386 -        # status must always be str
  11.387 -        ctx.status = '200 OK'
  11.388 -        
  11.389 -        ctx.app_stack = []
  11.390 -
  11.391 -    def _delegate(self, f, fvars, args=[]):
  11.392 -        def handle_class(cls):
  11.393 -            meth = web.ctx.method
  11.394 -            if meth == 'HEAD' and not hasattr(cls, meth):
  11.395 -                meth = 'GET'
  11.396 -            if not hasattr(cls, meth):
  11.397 -                raise web.nomethod(cls)
  11.398 -            tocall = getattr(cls(), meth)
  11.399 -            return tocall(*args)
  11.400 -            
  11.401 -        def is_class(o): return isinstance(o, (types.ClassType, type))
  11.402 -            
  11.403 -        if f is None:
  11.404 -            raise web.notfound()
  11.405 -        elif isinstance(f, application):
  11.406 -            return f.handle_with_processors()
  11.407 -        elif is_class(f):
  11.408 -            return handle_class(f)
  11.409 -        elif isinstance(f, basestring):
  11.410 -            if f.startswith('redirect '):
  11.411 -                url = f.split(' ', 1)[1]
  11.412 -                if web.ctx.method == "GET":
  11.413 -                    x = web.ctx.env.get('QUERY_STRING', '')
  11.414 -                    if x:
  11.415 -                        url += '?' + x
  11.416 -                raise web.redirect(url)
  11.417 -            elif '.' in f:
  11.418 -                mod, cls = f.rsplit('.', 1)
  11.419 -                mod = __import__(mod, None, None, [''])
  11.420 -                cls = getattr(mod, cls)
  11.421 -            else:
  11.422 -                cls = fvars[f]
  11.423 -            return handle_class(cls)
  11.424 -        elif hasattr(f, '__call__'):
  11.425 -            return f()
  11.426 -        else:
  11.427 -            return web.notfound()
  11.428 -
  11.429 -    def _match(self, mapping, value):
  11.430 -        for pat, what in mapping:
  11.431 -            if isinstance(what, application):
  11.432 -                if value.startswith(pat):
  11.433 -                    f = lambda: self._delegate_sub_application(pat, what)
  11.434 -                    return f, None
  11.435 -                else:
  11.436 -                    continue
  11.437 -            elif isinstance(what, basestring):
  11.438 -                what, result = utils.re_subm('^' + pat + '$', what, value)
  11.439 -            else:
  11.440 -                result = utils.re_compile('^' + pat + '$').match(value)
  11.441 -                
  11.442 -            if result: # it's a match
  11.443 -                return what, [x for x in result.groups()]
  11.444 -        return None, None
  11.445 -        
  11.446 -    def _delegate_sub_application(self, dir, app):
  11.447 -        """Deletes request to sub application `app` rooted at the directory `dir`.
  11.448 -        The home, homepath, path and fullpath values in web.ctx are updated to mimic request
  11.449 -        to the subapp and are restored after it is handled. 
  11.450 -        
  11.451 -        @@Any issues with when used with yield?
  11.452 -        """
  11.453 -        web.ctx._oldctx = web.storage(web.ctx)
  11.454 -        web.ctx.home += dir
  11.455 -        web.ctx.homepath += dir
  11.456 -        web.ctx.path = web.ctx.path[len(dir):]
  11.457 -        web.ctx.fullpath = web.ctx.fullpath[len(dir):]
  11.458 -        return app.handle_with_processors()
  11.459 -            
  11.460 -    def get_parent_app(self):
  11.461 -        if self in web.ctx.app_stack:
  11.462 -            index = web.ctx.app_stack.index(self)
  11.463 -            if index > 0:
  11.464 -                return web.ctx.app_stack[index-1]
  11.465 -        
  11.466 -    def notfound(self):
  11.467 -        """Returns HTTPError with '404 not found' message"""
  11.468 -        parent = self.get_parent_app()
  11.469 -        if parent:
  11.470 -            return parent.notfound()
  11.471 -        else:
  11.472 -            return web._NotFound()
  11.473 -            
  11.474 -    def internalerror(self):
  11.475 -        """Returns HTTPError with '500 internal error' message"""
  11.476 -        parent = self.get_parent_app()
  11.477 -        if parent:
  11.478 -            return parent.internalerror()
  11.479 -        elif web.config.get('debug'):
  11.480 -            import debugerror
  11.481 -            return debugerror.debugerror()
  11.482 -        else:
  11.483 -            return web._InternalError()
  11.484 -
  11.485 -class auto_application(application):
  11.486 -    """Application similar to `application` but urls are constructed 
  11.487 -    automatiacally using metaclass.
  11.488 -
  11.489 -        >>> app = auto_application()
  11.490 -        >>> class hello(app.page):
  11.491 -        ...     def GET(self): return "hello, world"
  11.492 -        ...
  11.493 -        >>> class foo(app.page):
  11.494 -        ...     path = '/foo/.*'
  11.495 -        ...     def GET(self): return "foo"
  11.496 -        >>> app.request("/hello").data
  11.497 -        'hello, world'
  11.498 -        >>> app.request('/foo/bar').data
  11.499 -        'foo'
  11.500 -    """
  11.501 -    def __init__(self):
  11.502 -        application.__init__(self)
  11.503 -
  11.504 -        class metapage(type):
  11.505 -            def __init__(klass, name, bases, attrs):
  11.506 -                type.__init__(klass, name, bases, attrs)
  11.507 -                path = attrs.get('path', '/' + name)
  11.508 -
  11.509 -                # path can be specified as None to ignore that class
  11.510 -                # typically required to create a abstract base class.
  11.511 -                if path is not None:
  11.512 -                    self.add_mapping(path, klass)
  11.513 -
  11.514 -        class page:
  11.515 -            path = None
  11.516 -            __metaclass__ = metapage
  11.517 -
  11.518 -        self.page = page
  11.519 -
  11.520 -# The application class already has the required functionality of subdir_application
  11.521 -subdir_application = application
  11.522 -                
  11.523 -class subdomain_application(application):
  11.524 -    """
  11.525 -    Application to delegate requests based on the host.
  11.526 -
  11.527 -        >>> urls = ("/hello", "hello")
  11.528 -        >>> app = application(urls, globals())
  11.529 -        >>> class hello:
  11.530 -        ...     def GET(self): return "hello"
  11.531 -        >>>
  11.532 -        >>> mapping = (r"hello\.example\.com", app)
  11.533 -        >>> app2 = subdomain_application(mapping)
  11.534 -        >>> app2.request("/hello", host="hello.example.com").data
  11.535 -        'hello'
  11.536 -        >>> response = app2.request("/hello", host="something.example.com")
  11.537 -        >>> response.status
  11.538 -        '404 Not Found'
  11.539 -        >>> response.data
  11.540 -        'not found'
  11.541 -    """
  11.542 -    def handle(self):
  11.543 -        host = web.ctx.host.split(':')[0] #strip port
  11.544 -        fn, args = self._match(self.mapping, host)
  11.545 -        return self._delegate(fn, self.fvars, args)
  11.546 -        
  11.547 -    def _match(self, mapping, value):
  11.548 -        for pat, what in mapping:
  11.549 -            if isinstance(what, basestring):
  11.550 -                what, result = utils.re_subm('^' + pat + '$', what, value)
  11.551 -            else:
  11.552 -                result = utils.re_compile('^' + pat + '$').match(value)
  11.553 -
  11.554 -            if result: # it's a match
  11.555 -                return what, [x for x in result.groups()]
  11.556 -        return None, None
  11.557 -        
  11.558 -def loadhook(h):
  11.559 -    """
  11.560 -    Converts a load hook into an application processor.
  11.561 -    
  11.562 -        >>> app = auto_application()
  11.563 -        >>> def f(): "something done before handling request"
  11.564 -        ...
  11.565 -        >>> app.add_processor(loadhook(f))
  11.566 -    """
  11.567 -    def processor(handler):
  11.568 -        h()
  11.569 -        return handler()
  11.570 -        
  11.571 -    return processor
  11.572 -    
  11.573 -def unloadhook(h):
  11.574 -    """
  11.575 -    Converts an unload hook into an application processor.
  11.576 -    
  11.577 -        >>> app = auto_application()
  11.578 -        >>> def f(): "something done after handling request"
  11.579 -        ...
  11.580 -        >>> app.add_processor(unloadhook(f))    
  11.581 -    """
  11.582 -    def processor(handler):
  11.583 -        try:
  11.584 -            result = handler()
  11.585 -            is_generator = result and hasattr(result, 'next')
  11.586 -        except:
  11.587 -            # run the hook even when handler raises some exception
  11.588 -            h()
  11.589 -            raise
  11.590 -
  11.591 -        if is_generator:
  11.592 -            return wrap(result)
  11.593 -        else:
  11.594 -            h()
  11.595 -            return result
  11.596 -            
  11.597 -    def wrap(result):
  11.598 -        def next():
  11.599 -            try:
  11.600 -                return result.next()
  11.601 -            except:
  11.602 -                # call the hook at the and of iterator
  11.603 -                h()
  11.604 -                raise
  11.605 -
  11.606 -        result = iter(result)
  11.607 -        while True:
  11.608 -            yield next()
  11.609 -            
  11.610 -    return processor
  11.611 -
  11.612 -def autodelegate(prefix=''):
  11.613 -    """
  11.614 -    Returns a method that takes one argument and calls the method named prefix+arg,
  11.615 -    calling `notfound()` if there isn't one. Example:
  11.616 -
  11.617 -        urls = ('/prefs/(.*)', 'prefs')
  11.618 -
  11.619 -        class prefs:
  11.620 -            GET = autodelegate('GET_')
  11.621 -            def GET_password(self): pass
  11.622 -            def GET_privacy(self): pass
  11.623 -
  11.624 -    `GET_password` would get called for `/prefs/password` while `GET_privacy` for 
  11.625 -    `GET_privacy` gets called for `/prefs/privacy`.
  11.626 -    
  11.627 -    If a user visits `/prefs/password/change` then `GET_password(self, '/change')`
  11.628 -    is called.
  11.629 -    """
  11.630 -    def internal(self, arg):
  11.631 -        if '/' in arg:
  11.632 -            first, rest = arg.split('/', 1)
  11.633 -            func = prefix + first
  11.634 -            args = ['/' + rest]
  11.635 -        else:
  11.636 -            func = prefix + arg
  11.637 -            args = []
  11.638 -        
  11.639 -        if hasattr(self, func):
  11.640 -            try:
  11.641 -                return getattr(self, func)(*args)
  11.642 -            except TypeError:
  11.643 -                raise web.notfound()
  11.644 -        else:
  11.645 -            raise web.notfound()
  11.646 -    return internal
  11.647 -
  11.648 -class Reloader:
  11.649 -    """Checks to see if any loaded modules have changed on disk and, 
  11.650 -    if so, reloads them.
  11.651 -    """
  11.652 -
  11.653 -    """File suffix of compiled modules."""
  11.654 -    if sys.platform.startswith('java'):
  11.655 -        SUFFIX = '$py.class'
  11.656 -    else:
  11.657 -        SUFFIX = '.pyc'
  11.658 -    
  11.659 -    def __init__(self):
  11.660 -        self.mtimes = {}
  11.661 -
  11.662 -    def __call__(self):
  11.663 -        for mod in sys.modules.values():
  11.664 -            self.check(mod)
  11.665 -
  11.666 -    def check(self, mod):
  11.667 -        # jython registers java packages as modules but they either
  11.668 -        # don't have a __file__ attribute or its value is None
  11.669 -        if not (mod and hasattr(mod, '__file__') and mod.__file__):
  11.670 -            return
  11.671 -
  11.672 -        try: 
  11.673 -            mtime = os.stat(mod.__file__).st_mtime
  11.674 -        except (OSError, IOError):
  11.675 -            return
  11.676 -        if mod.__file__.endswith(self.__class__.SUFFIX) and os.path.exists(mod.__file__[:-1]):
  11.677 -            mtime = max(os.stat(mod.__file__[:-1]).st_mtime, mtime)
  11.678 -            
  11.679 -        if mod not in self.mtimes:
  11.680 -            self.mtimes[mod] = mtime
  11.681 -        elif self.mtimes[mod] < mtime:
  11.682 -            try: 
  11.683 -                reload(mod)
  11.684 -                self.mtimes[mod] = mtime
  11.685 -            except ImportError: 
  11.686 -                pass
  11.687 -                
  11.688 -if __name__ == "__main__":
  11.689 -    import doctest
  11.690 -    doctest.testmod()
    12.1 --- a/OpenSecurity/install/web.py-0.37/build/lib/web/browser.py	Thu Feb 20 15:40:48 2014 +0100
    12.2 +++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
    12.3 @@ -1,236 +0,0 @@
    12.4 -"""Browser to test web applications.
    12.5 -(from web.py)
    12.6 -"""
    12.7 -from utils import re_compile
    12.8 -from net import htmlunquote
    12.9 -
   12.10 -import httplib, urllib, urllib2
   12.11 -import copy
   12.12 -from StringIO import StringIO
   12.13 -
   12.14 -DEBUG = False
   12.15 -
   12.16 -__all__ = [
   12.17 -    "BrowserError",
   12.18 -    "Browser", "AppBrowser",
   12.19 -    "AppHandler"
   12.20 -]
   12.21 -
   12.22 -class BrowserError(Exception):
   12.23 -    pass
   12.24 -
   12.25 -class Browser:
   12.26 -    def __init__(self):
   12.27 -        import cookielib
   12.28 -        self.cookiejar = cookielib.CookieJar()
   12.29 -        self._cookie_processor = urllib2.HTTPCookieProcessor(self.cookiejar)
   12.30 -        self.form = None
   12.31 -
   12.32 -        self.url = "http://0.0.0.0:8080/"
   12.33 -        self.path = "/"
   12.34 -        
   12.35 -        self.status = None
   12.36 -        self.data = None
   12.37 -        self._response = None
   12.38 -        self._forms = None
   12.39 -
   12.40 -    def reset(self):
   12.41 -        """Clears all cookies and history."""
   12.42 -        self.cookiejar.clear()
   12.43 -
   12.44 -    def build_opener(self):
   12.45 -        """Builds the opener using urllib2.build_opener. 
   12.46 -        Subclasses can override this function to prodive custom openers.
   12.47 -        """
   12.48 -        return urllib2.build_opener()
   12.49 -
   12.50 -    def do_request(self, req):
   12.51 -        if DEBUG:
   12.52 -            print 'requesting', req.get_method(), req.get_full_url()
   12.53 -        opener = self.build_opener()
   12.54 -        opener.add_handler(self._cookie_processor)
   12.55 -        try:
   12.56 -            self._response = opener.open(req)
   12.57 -        except urllib2.HTTPError, e:
   12.58 -            self._response = e
   12.59 -
   12.60 -        self.url = self._response.geturl()
   12.61 -        self.path = urllib2.Request(self.url).get_selector()
   12.62 -        self.data = self._response.read()
   12.63 -        self.status = self._response.code
   12.64 -        self._forms = None
   12.65 -        self.form = None
   12.66 -        return self.get_response()
   12.67 -
   12.68 -    def open(self, url, data=None, headers={}):
   12.69 -        """Opens the specified url."""
   12.70 -        url = urllib.basejoin(self.url, url)
   12.71 -        req = urllib2.Request(url, data, headers)
   12.72 -        return self.do_request(req)
   12.73 -
   12.74 -    def show(self):
   12.75 -        """Opens the current page in real web browser."""
   12.76 -        f = open('page.html', 'w')
   12.77 -        f.write(self.data)
   12.78 -        f.close()
   12.79 -
   12.80 -        import webbrowser, os
   12.81 -        url = 'file://' + os.path.abspath('page.html')
   12.82 -        webbrowser.open(url)
   12.83 -
   12.84 -    def get_response(self):
   12.85 -        """Returns a copy of the current response."""
   12.86 -        return urllib.addinfourl(StringIO(self.data), self._response.info(), self._response.geturl())
   12.87 -
   12.88 -    def get_soup(self):
   12.89 -        """Returns beautiful soup of the current document."""
   12.90 -        import BeautifulSoup
   12.91 -        return BeautifulSoup.BeautifulSoup(self.data)
   12.92 -
   12.93 -    def get_text(self, e=None):
   12.94 -        """Returns content of e or the current document as plain text."""
   12.95 -        e = e or self.get_soup()
   12.96 -        return ''.join([htmlunquote(c) for c in e.recursiveChildGenerator() if isinstance(c, unicode)])
   12.97 -
   12.98 -    def _get_links(self):
   12.99 -        soup = self.get_soup()
  12.100 -        return [a for a in soup.findAll(name='a')]
  12.101 -        
  12.102 -    def get_links(self, text=None, text_regex=None, url=None, url_regex=None, predicate=None):
  12.103 -        """Returns all links in the document."""
  12.104 -        return self._filter_links(self._get_links(),
  12.105 -            text=text, text_regex=text_regex, url=url, url_regex=url_regex, predicate=predicate)
  12.106 -
  12.107 -    def follow_link(self, link=None, text=None, text_regex=None, url=None, url_regex=None, predicate=None):
  12.108 -        if link is None:
  12.109 -            links = self._filter_links(self.get_links(),
  12.110 -                text=text, text_regex=text_regex, url=url, url_regex=url_regex, predicate=predicate)
  12.111 -            link = links and links[0]
  12.112 -            
  12.113 -        if link:
  12.114 -            return self.open(link['href'])
  12.115 -        else:
  12.116 -            raise BrowserError("No link found")
  12.117 -            
  12.118 -    def find_link(self, text=None, text_regex=None, url=None, url_regex=None, predicate=None):
  12.119 -        links = self._filter_links(self.get_links(), 
  12.120 -            text=text, text_regex=text_regex, url=url, url_regex=url_regex, predicate=predicate)
  12.121 -        return links and links[0] or None
  12.122 -            
  12.123 -    def _filter_links(self, links, 
  12.124 -            text=None, text_regex=None,
  12.125 -            url=None, url_regex=None,
  12.126 -            predicate=None):
  12.127 -        predicates = []
  12.128 -        if text is not None:
  12.129 -            predicates.append(lambda link: link.string == text)
  12.130 -        if text_regex is not None:
  12.131 -            predicates.append(lambda link: re_compile(text_regex).search(link.string or ''))
  12.132 -        if url is not None:
  12.133 -            predicates.append(lambda link: link.get('href') == url)
  12.134 -        if url_regex is not None:
  12.135 -            predicates.append(lambda link: re_compile(url_regex).search(link.get('href', '')))
  12.136 -        if predicate:
  12.137 -            predicate.append(predicate)
  12.138 -
  12.139 -        def f(link):
  12.140 -            for p in predicates:
  12.141 -                if not p(link):
  12.142 -                    return False
  12.143 -            return True
  12.144 -
  12.145 -        return [link for link in links if f(link)]
  12.146 -
  12.147 -    def get_forms(self):
  12.148 -        """Returns all forms in the current document.
  12.149 -        The returned form objects implement the ClientForm.HTMLForm interface.
  12.150 -        """
  12.151 -        if self._forms is None:
  12.152 -            import ClientForm
  12.153 -            self._forms = ClientForm.ParseResponse(self.get_response(), backwards_compat=False)
  12.154 -        return self._forms
  12.155 -
  12.156 -    def select_form(self, name=None, predicate=None, index=0):
  12.157 -        """Selects the specified form."""
  12.158 -        forms = self.get_forms()
  12.159 -
  12.160 -        if name is not None:
  12.161 -            forms = [f for f in forms if f.name == name]
  12.162 -        if predicate:
  12.163 -            forms = [f for f in forms if predicate(f)]
  12.164 -            
  12.165 -        if forms:
  12.166 -            self.form = forms[index]
  12.167 -            return self.form
  12.168 -        else:
  12.169 -            raise BrowserError("No form selected.")
  12.170 -        
  12.171 -    def submit(self, **kw):
  12.172 -        """submits the currently selected form."""
  12.173 -        if self.form is None:
  12.174 -            raise BrowserError("No form selected.")
  12.175 -        req = self.form.click(**kw)
  12.176 -        return self.do_request(req)
  12.177 -
  12.178 -    def __getitem__(self, key):
  12.179 -        return self.form[key]
  12.180 -
  12.181 -    def __setitem__(self, key, value):
  12.182 -        self.form[key] = value
  12.183 -
  12.184 -class AppBrowser(Browser):
  12.185 -    """Browser interface to test web.py apps.
  12.186 -    
  12.187 -        b = AppBrowser(app)
  12.188 -        b.open('/')
  12.189 -        b.follow_link(text='Login')
  12.190 -        
  12.191 -        b.select_form(name='login')
  12.192 -        b['username'] = 'joe'
  12.193 -        b['password'] = 'secret'
  12.194 -        b.submit()
  12.195 -
  12.196 -        assert b.path == '/'
  12.197 -        assert 'Welcome joe' in b.get_text()
  12.198 -    """
  12.199 -    def __init__(self, app):
  12.200 -        Browser.__init__(self)
  12.201 -        self.app = app
  12.202 -
  12.203 -    def build_opener(self):
  12.204 -        return urllib2.build_opener(AppHandler(self.app))
  12.205 -
  12.206 -class AppHandler(urllib2.HTTPHandler):
  12.207 -    """urllib2 handler to handle requests using web.py application."""
  12.208 -    handler_order = 100
  12.209 -
  12.210 -    def __init__(self, app):
  12.211 -        self.app = app
  12.212 -
  12.213 -    def http_open(self, req):
  12.214 -        result = self.app.request(
  12.215 -            localpart=req.get_selector(),
  12.216 -            method=req.get_method(),
  12.217 -            host=req.get_host(),
  12.218 -            data=req.get_data(),
  12.219 -            headers=dict(req.header_items()),
  12.220 -            https=req.get_type() == "https"
  12.221 -        )
  12.222 -        return self._make_response(result, req.get_full_url())
  12.223 -
  12.224 -    def https_open(self, req):
  12.225 -        return self.http_open(req)
  12.226 -    
  12.227 -    try:
  12.228 -        https_request = urllib2.HTTPHandler.do_request_
  12.229 -    except AttributeError:
  12.230 -        # for python 2.3
  12.231 -        pass
  12.232 -
  12.233 -    def _make_response(self, result, url):
  12.234 -        data = "\r\n".join(["%s: %s" % (k, v) for k, v in result.header_items])
  12.235 -        headers = httplib.HTTPMessage(StringIO(data))
  12.236 -        response = urllib.addinfourl(StringIO(result.data), headers, url)
  12.237 -        code, msg = result.status.split(None, 1)
  12.238 -        response.code, response.msg = int(code), msg
  12.239 -        return response
    13.1 --- a/OpenSecurity/install/web.py-0.37/build/lib/web/contrib/template.py	Thu Feb 20 15:40:48 2014 +0100
    13.2 +++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
    13.3 @@ -1,131 +0,0 @@
    13.4 -"""
    13.5 -Interface to various templating engines.
    13.6 -"""
    13.7 -import os.path
    13.8 -
    13.9 -__all__ = [
   13.10 -    "render_cheetah", "render_genshi", "render_mako",
   13.11 -    "cache", 
   13.12 -]
   13.13 -
   13.14 -class render_cheetah:
   13.15 -    """Rendering interface to Cheetah Templates.
   13.16 -
   13.17 -    Example:
   13.18 -
   13.19 -        render = render_cheetah('templates')
   13.20 -        render.hello(name="cheetah")
   13.21 -    """
   13.22 -    def __init__(self, path):
   13.23 -        # give error if Chetah is not installed
   13.24 -        from Cheetah.Template import Template
   13.25 -        self.path = path
   13.26 -
   13.27 -    def __getattr__(self, name):
   13.28 -        from Cheetah.Template import Template
   13.29 -        path = os.path.join(self.path, name + ".html")
   13.30 -        
   13.31 -        def template(**kw):
   13.32 -            t = Template(file=path, searchList=[kw])
   13.33 -            return t.respond()
   13.34 -
   13.35 -        return template
   13.36 -    
   13.37 -class render_genshi:
   13.38 -    """Rendering interface genshi templates.
   13.39 -    Example:
   13.40 -
   13.41 -    for xml/html templates.
   13.42 -
   13.43 -        render = render_genshi(['templates/'])
   13.44 -        render.hello(name='genshi')
   13.45 -
   13.46 -    For text templates:
   13.47 -
   13.48 -        render = render_genshi(['templates/'], type='text')
   13.49 -        render.hello(name='genshi')
   13.50 -    """
   13.51 -
   13.52 -    def __init__(self, *a, **kwargs):
   13.53 -        from genshi.template import TemplateLoader
   13.54 -
   13.55 -        self._type = kwargs.pop('type', None)
   13.56 -        self._loader = TemplateLoader(*a, **kwargs)
   13.57 -
   13.58 -    def __getattr__(self, name):
   13.59 -        # Assuming all templates are html
   13.60 -        path = name + ".html"
   13.61 -
   13.62 -        if self._type == "text":
   13.63 -            from genshi.template import TextTemplate
   13.64 -            cls = TextTemplate
   13.65 -            type = "text"
   13.66 -        else:
   13.67 -            cls = None
   13.68 -            type = None
   13.69 -
   13.70 -        t = self._loader.load(path, cls=cls)
   13.71 -        def template(**kw):
   13.72 -            stream = t.generate(**kw)
   13.73 -            if type:
   13.74 -                return stream.render(type)
   13.75 -            else:
   13.76 -                return stream.render()
   13.77 -        return template
   13.78 -
   13.79 -class render_jinja:
   13.80 -    """Rendering interface to Jinja2 Templates
   13.81 -    
   13.82 -    Example:
   13.83 -
   13.84 -        render= render_jinja('templates')
   13.85 -        render.hello(name='jinja2')
   13.86 -    """
   13.87 -    def __init__(self, *a, **kwargs):
   13.88 -        extensions = kwargs.pop('extensions', [])
   13.89 -        globals = kwargs.pop('globals', {})
   13.90 -
   13.91 -        from jinja2 import Environment,FileSystemLoader
   13.92 -        self._lookup = Environment(loader=FileSystemLoader(*a, **kwargs), extensions=extensions)
   13.93 -        self._lookup.globals.update(globals)
   13.94 -        
   13.95 -    def __getattr__(self, name):
   13.96 -        # Assuming all templates end with .html
   13.97 -        path = name + '.html'
   13.98 -        t = self._lookup.get_template(path)
   13.99 -        return t.render
  13.100 -        
  13.101 -class render_mako:
  13.102 -    """Rendering interface to Mako Templates.
  13.103 -
  13.104 -    Example:
  13.105 -
  13.106 -        render = render_mako(directories=['templates'])
  13.107 -        render.hello(name="mako")
  13.108 -    """
  13.109 -    def __init__(self, *a, **kwargs):
  13.110 -        from mako.lookup import TemplateLookup
  13.111 -        self._lookup = TemplateLookup(*a, **kwargs)
  13.112 -
  13.113 -    def __getattr__(self, name):
  13.114 -        # Assuming all templates are html
  13.115 -        path = name + ".html"
  13.116 -        t = self._lookup.get_template(path)
  13.117 -        return t.render
  13.118 -
  13.119 -class cache:
  13.120 -    """Cache for any rendering interface.
  13.121 -    
  13.122 -    Example:
  13.123 -
  13.124 -        render = cache(render_cheetah("templates/"))
  13.125 -        render.hello(name='cache')
  13.126 -    """
  13.127 -    def __init__(self, render):
  13.128 -        self._render = render
  13.129 -        self._cache = {}
  13.130 -
  13.131 -    def __getattr__(self, name):
  13.132 -        if name not in self._cache:
  13.133 -            self._cache[name] = getattr(self._render, name)
  13.134 -        return self._cache[name]
    14.1 --- a/OpenSecurity/install/web.py-0.37/build/lib/web/db.py	Thu Feb 20 15:40:48 2014 +0100
    14.2 +++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
    14.3 @@ -1,1237 +0,0 @@
    14.4 -"""
    14.5 -Database API
    14.6 -(part of web.py)
    14.7 -"""
    14.8 -
    14.9 -__all__ = [
   14.10 -  "UnknownParamstyle", "UnknownDB", "TransactionError", 
   14.11 -  "sqllist", "sqlors", "reparam", "sqlquote",
   14.12 -  "SQLQuery", "SQLParam", "sqlparam",
   14.13 -  "SQLLiteral", "sqlliteral",
   14.14 -  "database", 'DB',
   14.15 -]
   14.16 -
   14.17 -import time
   14.18 -try:
   14.19 -    import datetime
   14.20 -except ImportError:
   14.21 -    datetime = None
   14.22 -
   14.23 -try: set
   14.24 -except NameError:
   14.25 -    from sets import Set as set
   14.26 -    
   14.27 -from utils import threadeddict, storage, iters, iterbetter, safestr, safeunicode
   14.28 -
   14.29 -try:
   14.30 -    # db module can work independent of web.py
   14.31 -    from webapi import debug, config
   14.32 -except:
   14.33 -    import sys
   14.34 -    debug = sys.stderr
   14.35 -    config = storage()
   14.36 -
   14.37 -class UnknownDB(Exception):
   14.38 -    """raised for unsupported dbms"""
   14.39 -    pass
   14.40 -
   14.41 -class _ItplError(ValueError): 
   14.42 -    def __init__(self, text, pos):
   14.43 -        ValueError.__init__(self)
   14.44 -        self.text = text
   14.45 -        self.pos = pos
   14.46 -    def __str__(self):
   14.47 -        return "unfinished expression in %s at char %d" % (
   14.48 -            repr(self.text), self.pos)
   14.49 -
   14.50 -class TransactionError(Exception): pass
   14.51 -
   14.52 -class UnknownParamstyle(Exception): 
   14.53 -    """
   14.54 -    raised for unsupported db paramstyles
   14.55 -
   14.56 -    (currently supported: qmark, numeric, format, pyformat)
   14.57 -    """
   14.58 -    pass
   14.59 -    
   14.60 -class SQLParam(object):
   14.61 -    """
   14.62 -    Parameter in SQLQuery.
   14.63 -    
   14.64 -        >>> q = SQLQuery(["SELECT * FROM test WHERE name=", SQLParam("joe")])
   14.65 -        >>> q
   14.66 -        <sql: "SELECT * FROM test WHERE name='joe'">
   14.67 -        >>> q.query()
   14.68 -        'SELECT * FROM test WHERE name=%s'
   14.69 -        >>> q.values()
   14.70 -        ['joe']
   14.71 -    """
   14.72 -    __slots__ = ["value"]
   14.73 -
   14.74 -    def __init__(self, value):
   14.75 -        self.value = value
   14.76 -        
   14.77 -    def get_marker(self, paramstyle='pyformat'):
   14.78 -        if paramstyle == 'qmark':
   14.79 -            return '?'
   14.80 -        elif paramstyle == 'numeric':
   14.81 -            return ':1'
   14.82 -        elif paramstyle is None or paramstyle in ['format', 'pyformat']:
   14.83 -            return '%s'
   14.84 -        raise UnknownParamstyle, paramstyle
   14.85 -        
   14.86 -    def sqlquery(self): 
   14.87 -        return SQLQuery([self])
   14.88 -        
   14.89 -    def __add__(self, other):
   14.90 -        return self.sqlquery() + other
   14.91 -        
   14.92 -    def __radd__(self, other):
   14.93 -        return other + self.sqlquery() 
   14.94 -            
   14.95 -    def __str__(self): 
   14.96 -        return str(self.value)
   14.97 -    
   14.98 -    def __repr__(self):
   14.99 -        return '<param: %s>' % repr(self.value)
  14.100 -
  14.101 -sqlparam =  SQLParam
  14.102 -
  14.103 -class SQLQuery(object):
  14.104 -    """
  14.105 -    You can pass this sort of thing as a clause in any db function.
  14.106 -    Otherwise, you can pass a dictionary to the keyword argument `vars`
  14.107 -    and the function will call reparam for you.
  14.108 -
  14.109 -    Internally, consists of `items`, which is a list of strings and
  14.110 -    SQLParams, which get concatenated to produce the actual query.
  14.111 -    """
  14.112 -    __slots__ = ["items"]
  14.113 -
  14.114 -    # tested in sqlquote's docstring
  14.115 -    def __init__(self, items=None):
  14.116 -        r"""Creates a new SQLQuery.
  14.117 -        
  14.118 -            >>> SQLQuery("x")
  14.119 -            <sql: 'x'>
  14.120 -            >>> q = SQLQuery(['SELECT * FROM ', 'test', ' WHERE x=', SQLParam(1)])
  14.121 -            >>> q
  14.122 -            <sql: 'SELECT * FROM test WHERE x=1'>
  14.123 -            >>> q.query(), q.values()
  14.124 -            ('SELECT * FROM test WHERE x=%s', [1])
  14.125 -            >>> SQLQuery(SQLParam(1))
  14.126 -            <sql: '1'>
  14.127 -        """
  14.128 -        if items is None:
  14.129 -            self.items = []
  14.130 -        elif isinstance(items, list):
  14.131 -            self.items = items
  14.132 -        elif isinstance(items, SQLParam):
  14.133 -            self.items = [items]
  14.134 -        elif isinstance(items, SQLQuery):
  14.135 -            self.items = list(items.items)
  14.136 -        else:
  14.137 -            self.items = [items]
  14.138 -            
  14.139 -        # Take care of SQLLiterals
  14.140 -        for i, item in enumerate(self.items):
  14.141 -            if isinstance(item, SQLParam) and isinstance(item.value, SQLLiteral):
  14.142 -                self.items[i] = item.value.v
  14.143 -
  14.144 -    def append(self, value):
  14.145 -        self.items.append(value)
  14.146 -
  14.147 -    def __add__(self, other):
  14.148 -        if isinstance(other, basestring):
  14.149 -            items = [other]
  14.150 -        elif isinstance(other, SQLQuery):
  14.151 -            items = other.items
  14.152 -        else:
  14.153 -            return NotImplemented
  14.154 -        return SQLQuery(self.items + items)
  14.155 -
  14.156 -    def __radd__(self, other):
  14.157 -        if isinstance(other, basestring):
  14.158 -            items = [other]
  14.159 -        else:
  14.160 -            return NotImplemented
  14.161 -            
  14.162 -        return SQLQuery(items + self.items)
  14.163 -
  14.164 -    def __iadd__(self, other):
  14.165 -        if isinstance(other, (basestring, SQLParam)):
  14.166 -            self.items.append(other)
  14.167 -        elif isinstance(other, SQLQuery):
  14.168 -            self.items.extend(other.items)
  14.169 -        else:
  14.170 -            return NotImplemented
  14.171 -        return self
  14.172 -
  14.173 -    def __len__(self):
  14.174 -        return len(self.query())
  14.175 -        
  14.176 -    def query(self, paramstyle=None):
  14.177 -        """
  14.178 -        Returns the query part of the sql query.
  14.179 -            >>> q = SQLQuery(["SELECT * FROM test WHERE name=", SQLParam('joe')])
  14.180 -            >>> q.query()
  14.181 -            'SELECT * FROM test WHERE name=%s'
  14.182 -            >>> q.query(paramstyle='qmark')
  14.183 -            'SELECT * FROM test WHERE name=?'
  14.184 -        """
  14.185 -        s = []
  14.186 -        for x in self.items:
  14.187 -            if isinstance(x, SQLParam):
  14.188 -                x = x.get_marker(paramstyle)
  14.189 -                s.append(safestr(x))
  14.190 -            else:
  14.191 -                x = safestr(x)
  14.192 -                # automatically escape % characters in the query
  14.193 -                # For backward compatability, ignore escaping when the query looks already escaped
  14.194 -                if paramstyle in ['format', 'pyformat']:
  14.195 -                    if '%' in x and '%%' not in x:
  14.196 -                        x = x.replace('%', '%%')
  14.197 -                s.append(x)
  14.198 -        return "".join(s)
  14.199 -    
  14.200 -    def values(self):
  14.201 -        """
  14.202 -        Returns the values of the parameters used in the sql query.
  14.203 -            >>> q = SQLQuery(["SELECT * FROM test WHERE name=", SQLParam('joe')])
  14.204 -            >>> q.values()
  14.205 -            ['joe']
  14.206 -        """
  14.207 -        return [i.value for i in self.items if isinstance(i, SQLParam)]
  14.208 -        
  14.209 -    def join(items, sep=' ', prefix=None, suffix=None, target=None):
  14.210 -        """
  14.211 -        Joins multiple queries.
  14.212 -        
  14.213 -        >>> SQLQuery.join(['a', 'b'], ', ')
  14.214 -        <sql: 'a, b'>
  14.215 -
  14.216 -        Optinally, prefix and suffix arguments can be provided.
  14.217 -
  14.218 -        >>> SQLQuery.join(['a', 'b'], ', ', prefix='(', suffix=')')
  14.219 -        <sql: '(a, b)'>
  14.220 -
  14.221 -        If target argument is provided, the items are appended to target instead of creating a new SQLQuery.
  14.222 -        """
  14.223 -        if target is None:
  14.224 -            target = SQLQuery()
  14.225 -
  14.226 -        target_items = target.items
  14.227 -
  14.228 -        if prefix:
  14.229 -            target_items.append(prefix)
  14.230 -
  14.231 -        for i, item in enumerate(items):
  14.232 -            if i != 0:
  14.233 -                target_items.append(sep)
  14.234 -            if isinstance(item, SQLQuery):
  14.235 -                target_items.extend(item.items)
  14.236 -            else:
  14.237 -                target_items.append(item)
  14.238 -
  14.239 -        if suffix:
  14.240 -            target_items.append(suffix)
  14.241 -        return target
  14.242 -    
  14.243 -    join = staticmethod(join)
  14.244 -    
  14.245 -    def _str(self):
  14.246 -        try:
  14.247 -            return self.query() % tuple([sqlify(x) for x in self.values()])            
  14.248 -        except (ValueError, TypeError):
  14.249 -            return self.query()
  14.250 -        
  14.251 -    def __str__(self):
  14.252 -        return safestr(self._str())
  14.253 -        
  14.254 -    def __unicode__(self):
  14.255 -        return safeunicode(self._str())
  14.256 -
  14.257 -    def __repr__(self):
  14.258 -        return '<sql: %s>' % repr(str(self))
  14.259 -
  14.260 -class SQLLiteral: 
  14.261 -    """
  14.262 -    Protects a string from `sqlquote`.
  14.263 -
  14.264 -        >>> sqlquote('NOW()')
  14.265 -        <sql: "'NOW()'">
  14.266 -        >>> sqlquote(SQLLiteral('NOW()'))
  14.267 -        <sql: 'NOW()'>
  14.268 -    """
  14.269 -    def __init__(self, v): 
  14.270 -        self.v = v
  14.271 -
  14.272 -    def __repr__(self): 
  14.273 -        return self.v
  14.274 -
  14.275 -sqlliteral = SQLLiteral
  14.276 -
  14.277 -def _sqllist(values):
  14.278 -    """
  14.279 -        >>> _sqllist([1, 2, 3])
  14.280 -        <sql: '(1, 2, 3)'>
  14.281 -    """
  14.282 -    items = []
  14.283 -    items.append('(')
  14.284 -    for i, v in enumerate(values):
  14.285 -        if i != 0:
  14.286 -            items.append(', ')
  14.287 -        items.append(sqlparam(v))
  14.288 -    items.append(')')
  14.289 -    return SQLQuery(items)
  14.290 -
  14.291 -def reparam(string_, dictionary): 
  14.292 -    """
  14.293 -    Takes a string and a dictionary and interpolates the string
  14.294 -    using values from the dictionary. Returns an `SQLQuery` for the result.
  14.295 -
  14.296 -        >>> reparam("s = $s", dict(s=True))
  14.297 -        <sql: "s = 't'">
  14.298 -        >>> reparam("s IN $s", dict(s=[1, 2]))
  14.299 -        <sql: 's IN (1, 2)'>
  14.300 -    """
  14.301 -    dictionary = dictionary.copy() # eval mucks with it
  14.302 -    vals = []
  14.303 -    result = []
  14.304 -    for live, chunk in _interpolate(string_):
  14.305 -        if live:
  14.306 -            v = eval(chunk, dictionary)
  14.307 -            result.append(sqlquote(v))
  14.308 -        else: 
  14.309 -            result.append(chunk)
  14.310 -    return SQLQuery.join(result, '')
  14.311 -
  14.312 -def sqlify(obj): 
  14.313 -    """
  14.314 -    converts `obj` to its proper SQL version
  14.315 -
  14.316 -        >>> sqlify(None)
  14.317 -        'NULL'
  14.318 -        >>> sqlify(True)
  14.319 -        "'t'"
  14.320 -        >>> sqlify(3)
  14.321 -        '3'
  14.322 -    """
  14.323 -    # because `1 == True and hash(1) == hash(True)`
  14.324 -    # we have to do this the hard way...
  14.325 -
  14.326 -    if obj is None:
  14.327 -        return 'NULL'
  14.328 -    elif obj is True:
  14.329 -        return "'t'"
  14.330 -    elif obj is False:
  14.331 -        return "'f'"
  14.332 -    elif datetime and isinstance(obj, datetime.datetime):
  14.333 -        return repr(obj.isoformat())
  14.334 -    else:
  14.335 -        if isinstance(obj, unicode): obj = obj.encode('utf8')
  14.336 -        return repr(obj)
  14.337 -
  14.338 -def sqllist(lst): 
  14.339 -    """
  14.340 -    Converts the arguments for use in something like a WHERE clause.
  14.341 -    
  14.342 -        >>> sqllist(['a', 'b'])
  14.343 -        'a, b'
  14.344 -        >>> sqllist('a')
  14.345 -        'a'
  14.346 -        >>> sqllist(u'abc')
  14.347 -        u'abc'
  14.348 -    """
  14.349 -    if isinstance(lst, basestring): 
  14.350 -        return lst
  14.351 -    else:
  14.352 -        return ', '.join(lst)
  14.353 -
  14.354 -def sqlors(left, lst):
  14.355 -    """
  14.356 -    `left is a SQL clause like `tablename.arg = ` 
  14.357 -    and `lst` is a list of values. Returns a reparam-style
  14.358 -    pair featuring the SQL that ORs together the clause
  14.359 -    for each item in the lst.
  14.360 -
  14.361 -        >>> sqlors('foo = ', [])
  14.362 -        <sql: '1=2'>
  14.363 -        >>> sqlors('foo = ', [1])
  14.364 -        <sql: 'foo = 1'>
  14.365 -        >>> sqlors('foo = ', 1)
  14.366 -        <sql: 'foo = 1'>
  14.367 -        >>> sqlors('foo = ', [1,2,3])
  14.368 -        <sql: '(foo = 1 OR foo = 2 OR foo = 3 OR 1=2)'>
  14.369 -    """
  14.370 -    if isinstance(lst, iters):
  14.371 -        lst = list(lst)
  14.372 -        ln = len(lst)
  14.373 -        if ln == 0:
  14.374 -            return SQLQuery("1=2")
  14.375 -        if ln == 1:
  14.376 -            lst = lst[0]
  14.377 -
  14.378 -    if isinstance(lst, iters):
  14.379 -        return SQLQuery(['('] + 
  14.380 -          sum([[left, sqlparam(x), ' OR '] for x in lst], []) +
  14.381 -          ['1=2)']
  14.382 -        )
  14.383 -    else:
  14.384 -        return left + sqlparam(lst)
  14.385 -        
  14.386 -def sqlwhere(dictionary, grouping=' AND '): 
  14.387 -    """
  14.388 -    Converts a `dictionary` to an SQL WHERE clause `SQLQuery`.
  14.389 -    
  14.390 -        >>> sqlwhere({'cust_id': 2, 'order_id':3})
  14.391 -        <sql: 'order_id = 3 AND cust_id = 2'>
  14.392 -        >>> sqlwhere({'cust_id': 2, 'order_id':3}, grouping=', ')
  14.393 -        <sql: 'order_id = 3, cust_id = 2'>
  14.394 -        >>> sqlwhere({'a': 'a', 'b': 'b'}).query()
  14.395 -        'a = %s AND b = %s'
  14.396 -    """
  14.397 -    return SQLQuery.join([k + ' = ' + sqlparam(v) for k, v in dictionary.items()], grouping)
  14.398 -
  14.399 -def sqlquote(a): 
  14.400 -    """
  14.401 -    Ensures `a` is quoted properly for use in a SQL query.
  14.402 -
  14.403 -        >>> 'WHERE x = ' + sqlquote(True) + ' AND y = ' + sqlquote(3)
  14.404 -        <sql: "WHERE x = 't' AND y = 3">
  14.405 -        >>> 'WHERE x = ' + sqlquote(True) + ' AND y IN ' + sqlquote([2, 3])
  14.406 -        <sql: "WHERE x = 't' AND y IN (2, 3)">
  14.407 -    """
  14.408 -    if isinstance(a, list):
  14.409 -        return _sqllist(a)
  14.410 -    else:
  14.411 -        return sqlparam(a).sqlquery()
  14.412 -
  14.413 -class Transaction:
  14.414 -    """Database transaction."""
  14.415 -    def __init__(self, ctx):
  14.416 -        self.ctx = ctx
  14.417 -        self.transaction_count = transaction_count = len(ctx.transactions)
  14.418 -
  14.419 -        class transaction_engine:
  14.420 -            """Transaction Engine used in top level transactions."""
  14.421 -            def do_transact(self):
  14.422 -                ctx.commit(unload=False)
  14.423 -
  14.424 -            def do_commit(self):
  14.425 -                ctx.commit()
  14.426 -
  14.427 -            def do_rollback(self):
  14.428 -                ctx.rollback()
  14.429 -
  14.430 -        class subtransaction_engine:
  14.431 -            """Transaction Engine used in sub transactions."""
  14.432 -            def query(self, q):
  14.433 -                db_cursor = ctx.db.cursor()
  14.434 -                ctx.db_execute(db_cursor, SQLQuery(q % transaction_count))
  14.435 -
  14.436 -            def do_transact(self):
  14.437 -                self.query('SAVEPOINT webpy_sp_%s')
  14.438 -
  14.439 -            def do_commit(self):
  14.440 -                self.query('RELEASE SAVEPOINT webpy_sp_%s')
  14.441 -
  14.442 -            def do_rollback(self):
  14.443 -                self.query('ROLLBACK TO SAVEPOINT webpy_sp_%s')
  14.444 -
  14.445 -        class dummy_engine:
  14.446 -            """Transaction Engine used instead of subtransaction_engine 
  14.447 -            when sub transactions are not supported."""
  14.448 -            do_transact = do_commit = do_rollback = lambda self: None
  14.449 -
  14.450 -        if self.transaction_count:
  14.451 -            # nested transactions are not supported in some databases
  14.452 -            if self.ctx.get('ignore_nested_transactions'):
  14.453 -                self.engine = dummy_engine()
  14.454 -            else:
  14.455 -                self.engine = subtransaction_engine()
  14.456 -        else:
  14.457 -            self.engine = transaction_engine()
  14.458 -
  14.459 -        self.engine.do_transact()
  14.460 -        self.ctx.transactions.append(self)
  14.461 -
  14.462 -    def __enter__(self):
  14.463 -        return self
  14.464 -
  14.465 -    def __exit__(self, exctype, excvalue, traceback):
  14.466 -        if exctype is not None:
  14.467 -            self.rollback()
  14.468 -        else:
  14.469 -            self.commit()
  14.470 -
  14.471 -    def commit(self):
  14.472 -        if len(self.ctx.transactions) > self.transaction_count:
  14.473 -            self.engine.do_commit()
  14.474 -            self.ctx.transactions = self.ctx.transactions[:self.transaction_count]
  14.475 -
  14.476 -    def rollback(self):
  14.477 -        if len(self.ctx.transactions) > self.transaction_count:
  14.478 -            self.engine.do_rollback()
  14.479 -            self.ctx.transactions = self.ctx.transactions[:self.transaction_count]
  14.480 -
  14.481 -class DB: 
  14.482 -    """Database"""
  14.483 -    def __init__(self, db_module, keywords):
  14.484 -        """Creates a database.
  14.485 -        """
  14.486 -        # some DB implementaions take optional paramater `driver` to use a specific driver modue
  14.487 -        # but it should not be passed to connect
  14.488 -        keywords.pop('driver', None)
  14.489 -
  14.490 -        self.db_module = db_module
  14.491 -        self.keywords = keywords
  14.492 -
  14.493 -        self._ctx = threadeddict()
  14.494 -        # flag to enable/disable printing queries
  14.495 -        self.printing = config.get('debug_sql', config.get('debug', False))
  14.496 -        self.supports_multiple_insert = False
  14.497 -        
  14.498 -        try:
  14.499 -            import DBUtils
  14.500 -            # enable pooling if DBUtils module is available.
  14.501 -            self.has_pooling = True
  14.502 -        except ImportError:
  14.503 -            self.has_pooling = False
  14.504 -            
  14.505 -        # Pooling can be disabled by passing pooling=False in the keywords.
  14.506 -        self.has_pooling = self.keywords.pop('pooling', True) and self.has_pooling
  14.507 -            
  14.508 -    def _getctx(self): 
  14.509 -        if not self._ctx.get('db'):
  14.510 -            self._load_context(self._ctx)
  14.511 -        return self._ctx
  14.512 -    ctx = property(_getctx)
  14.513 -    
  14.514 -    def _load_context(self, ctx):
  14.515 -        ctx.dbq_count = 0
  14.516 -        ctx.transactions = [] # stack of transactions
  14.517 -        
  14.518 -        if self.has_pooling:
  14.519 -            ctx.db = self._connect_with_pooling(self.keywords)
  14.520 -        else:
  14.521 -            ctx.db = self._connect(self.keywords)
  14.522 -        ctx.db_execute = self._db_execute
  14.523 -        
  14.524 -        if not hasattr(ctx.db, 'commit'):
  14.525 -            ctx.db.commit = lambda: None
  14.526 -
  14.527 -        if not hasattr(ctx.db, 'rollback'):
  14.528 -            ctx.db.rollback = lambda: None
  14.529 -            
  14.530 -        def commit(unload=True):
  14.531 -            # do db commit and release the connection if pooling is enabled.            
  14.532 -            ctx.db.commit()
  14.533 -            if unload and self.has_pooling:
  14.534 -                self._unload_context(self._ctx)
  14.535 -                
  14.536 -        def rollback():
  14.537 -            # do db rollback and release the connection if pooling is enabled.
  14.538 -            ctx.db.rollback()
  14.539 -            if self.has_pooling:
  14.540 -                self._unload_context(self._ctx)
  14.541 -                
  14.542 -        ctx.commit = commit
  14.543 -        ctx.rollback = rollback
  14.544 -            
  14.545 -    def _unload_context(self, ctx):
  14.546 -        del ctx.db
  14.547 -            
  14.548 -    def _connect(self, keywords):
  14.549 -        return self.db_module.connect(**keywords)
  14.550 -        
  14.551 -    def _connect_with_pooling(self, keywords):
  14.552 -        def get_pooled_db():
  14.553 -            from DBUtils import PooledDB
  14.554 -
  14.555 -            # In DBUtils 0.9.3, `dbapi` argument is renamed as `creator`
  14.556 -            # see Bug#122112
  14.557 -            
  14.558 -            if PooledDB.__version__.split('.') < '0.9.3'.split('.'):
  14.559 -                return PooledDB.PooledDB(dbapi=self.db_module, **keywords)
  14.560 -            else:
  14.561 -                return PooledDB.PooledDB(creator=self.db_module, **keywords)
  14.562 -        
  14.563 -        if getattr(self, '_pooleddb', None) is None:
  14.564 -            self._pooleddb = get_pooled_db()
  14.565 -        
  14.566 -        return self._pooleddb.connection()
  14.567 -        
  14.568 -    def _db_cursor(self):
  14.569 -        return self.ctx.db.cursor()
  14.570 -
  14.571 -    def _param_marker(self):
  14.572 -        """Returns parameter marker based on paramstyle attribute if this database."""
  14.573 -        style = getattr(self, 'paramstyle', 'pyformat')
  14.574 -
  14.575 -        if style == 'qmark':
  14.576 -            return '?'
  14.577 -        elif style == 'numeric':
  14.578 -            return ':1'
  14.579 -        elif style in ['format', 'pyformat']:
  14.580 -            return '%s'
  14.581 -        raise UnknownParamstyle, style
  14.582 -
  14.583 -    def _db_execute(self, cur, sql_query): 
  14.584 -        """executes an sql query"""
  14.585 -        self.ctx.dbq_count += 1
  14.586 -        
  14.587 -        try:
  14.588 -            a = time.time()
  14.589 -            query, params = self._process_query(sql_query)
  14.590 -            out = cur.execute(query, params)
  14.591 -            b = time.time()
  14.592 -        except:
  14.593 -            if self.printing:
  14.594 -                print >> debug, 'ERR:', str(sql_query)
  14.595 -            if self.ctx.transactions:
  14.596 -                self.ctx.transactions[-1].rollback()
  14.597 -            else:
  14.598 -                self.ctx.rollback()
  14.599 -            raise
  14.600 -
  14.601 -        if self.printing:
  14.602 -            print >> debug, '%s (%s): %s' % (round(b-a, 2), self.ctx.dbq_count, str(sql_query))
  14.603 -        return out
  14.604 -
  14.605 -    def _process_query(self, sql_query):
  14.606 -        """Takes the SQLQuery object and returns query string and parameters.
  14.607 -        """
  14.608 -        paramstyle = getattr(self, 'paramstyle', 'pyformat')
  14.609 -        query = sql_query.query(paramstyle)
  14.610 -        params = sql_query.values()
  14.611 -        return query, params
  14.612 -    
  14.613 -    def _where(self, where, vars): 
  14.614 -        if isinstance(where, (int, long)):
  14.615 -            where = "id = " + sqlparam(where)
  14.616 -        #@@@ for backward-compatibility
  14.617 -        elif isinstance(where, (list, tuple)) and len(where) == 2:
  14.618 -            where = SQLQuery(where[0], where[1])
  14.619 -        elif isinstance(where, SQLQuery):
  14.620 -            pass
  14.621 -        else:
  14.622 -            where = reparam(where, vars)        
  14.623 -        return where
  14.624 -    
  14.625 -    def query(self, sql_query, vars=None, processed=False, _test=False): 
  14.626 -        """
  14.627 -        Execute SQL query `sql_query` using dictionary `vars` to interpolate it.
  14.628 -        If `processed=True`, `vars` is a `reparam`-style list to use 
  14.629 -        instead of interpolating.
  14.630 -        
  14.631 -            >>> db = DB(None, {})
  14.632 -            >>> db.query("SELECT * FROM foo", _test=True)
  14.633 -            <sql: 'SELECT * FROM foo'>
  14.634 -            >>> db.query("SELECT * FROM foo WHERE x = $x", vars=dict(x='f'), _test=True)
  14.635 -            <sql: "SELECT * FROM foo WHERE x = 'f'">
  14.636 -            >>> db.query("SELECT * FROM foo WHERE x = " + sqlquote('f'), _test=True)
  14.637 -            <sql: "SELECT * FROM foo WHERE x = 'f'">
  14.638 -        """
  14.639 -        if vars is None: vars = {}
  14.640 -        
  14.641 -        if not processed and not isinstance(sql_query, SQLQuery):
  14.642 -            sql_query = reparam(sql_query, vars)
  14.643 -        
  14.644 -        if _test: return sql_query
  14.645 -        
  14.646 -        db_cursor = self._db_cursor()
  14.647 -        self._db_execute(db_cursor, sql_query)
  14.648 -        
  14.649 -        if db_cursor.description:
  14.650 -            names = [x[0] for x in db_cursor.description]
  14.651 -            def iterwrapper():
  14.652 -                row = db_cursor.fetchone()
  14.653 -                while row:
  14.654 -                    yield storage(dict(zip(names, row)))
  14.655 -                    row = db_cursor.fetchone()
  14.656 -            out = iterbetter(iterwrapper())
  14.657 -            out.__len__ = lambda: int(db_cursor.rowcount)
  14.658 -            out.list = lambda: [storage(dict(zip(names, x))) \
  14.659 -                               for x in db_cursor.fetchall()]
  14.660 -        else:
  14.661 -            out = db_cursor.rowcount
  14.662 -        
  14.663 -        if not self.ctx.transactions: 
  14.664 -            self.ctx.commit()
  14.665 -        return out
  14.666 -    
  14.667 -    def select(self, tables, vars=None, what='*', where=None, order=None, group=None, 
  14.668 -               limit=None, offset=None, _test=False): 
  14.669 -        """
  14.670 -        Selects `what` from `tables` with clauses `where`, `order`, 
  14.671 -        `group`, `limit`, and `offset`. Uses vars to interpolate. 
  14.672 -        Otherwise, each clause can be a SQLQuery.
  14.673 -        
  14.674 -            >>> db = DB(None, {})
  14.675 -            >>> db.select('foo', _test=True)
  14.676 -            <sql: 'SELECT * FROM foo'>
  14.677 -            >>> db.select(['foo', 'bar'], where="foo.bar_id = bar.id", limit=5, _test=True)
  14.678 -            <sql: 'SELECT * FROM foo, bar WHERE foo.bar_id = bar.id LIMIT 5'>
  14.679 -        """
  14.680 -        if vars is None: vars = {}
  14.681 -        sql_clauses = self.sql_clauses(what, tables, where, group, order, limit, offset)
  14.682 -        clauses = [self.gen_clause(sql, val, vars) for sql, val in sql_clauses if val is not None]
  14.683 -        qout = SQLQuery.join(clauses)
  14.684 -        if _test: return qout
  14.685 -        return self.query(qout, processed=True)
  14.686 -    
  14.687 -    def where(self, table, what='*', order=None, group=None, limit=None, 
  14.688 -              offset=None, _test=False, **kwargs):
  14.689 -        """
  14.690 -        Selects from `table` where keys are equal to values in `kwargs`.
  14.691 -        
  14.692 -            >>> db = DB(None, {})
  14.693 -            >>> db.where('foo', bar_id=3, _test=True)
  14.694 -            <sql: 'SELECT * FROM foo WHERE bar_id = 3'>
  14.695 -            >>> db.where('foo', source=2, crust='dewey', _test=True)
  14.696 -            <sql: "SELECT * FROM foo WHERE source = 2 AND crust = 'dewey'">
  14.697 -            >>> db.where('foo', _test=True)
  14.698 -            <sql: 'SELECT * FROM foo'>
  14.699 -        """
  14.700 -        where_clauses = []
  14.701 -        for k, v in kwargs.iteritems():
  14.702 -            where_clauses.append(k + ' = ' + sqlquote(v))
  14.703 -            
  14.704 -        if where_clauses:
  14.705 -            where = SQLQuery.join(where_clauses, " AND ")
  14.706 -        else:
  14.707 -            where = None
  14.708 -            
  14.709 -        return self.select(table, what=what, order=order, 
  14.710 -               group=group, limit=limit, offset=offset, _test=_test, 
  14.711 -               where=where)
  14.712 -    
  14.713 -    def sql_clauses(self, what, tables, where, group, order, limit, offset): 
  14.714 -        return (
  14.715 -            ('SELECT', what),
  14.716 -            ('FROM', sqllist(tables)),
  14.717 -            ('WHERE', where),
  14.718 -            ('GROUP BY', group),
  14.719 -            ('ORDER BY', order),
  14.720 -            ('LIMIT', limit),
  14.721 -            ('OFFSET', offset))
  14.722 -    
  14.723 -    def gen_clause(self, sql, val, vars): 
  14.724 -        if isinstance(val, (int, long)):
  14.725 -            if sql == 'WHERE':
  14.726 -                nout = 'id = ' + sqlquote(val)
  14.727 -            else:
  14.728 -                nout = SQLQuery(val)
  14.729 -        #@@@
  14.730 -        elif isinstance(val, (list, tuple)) and len(val) == 2:
  14.731 -            nout = SQLQuery(val[0], val[1]) # backwards-compatibility
  14.732 -        elif isinstance(val, SQLQuery):
  14.733 -            nout = val
  14.734 -        else:
  14.735 -            nout = reparam(val, vars)
  14.736 -
  14.737 -        def xjoin(a, b):
  14.738 -            if a and b: return a + ' ' + b
  14.739 -            else: return a or b
  14.740 -
  14.741 -        return xjoin(sql, nout)
  14.742 -
  14.743 -    def insert(self, tablename, seqname=None, _test=False, **values): 
  14.744 -        """
  14.745 -        Inserts `values` into `tablename`. Returns current sequence ID.
  14.746 -        Set `seqname` to the ID if it's not the default, or to `False`
  14.747 -        if there isn't one.
  14.748 -        
  14.749 -            >>> db = DB(None, {})
  14.750 -            >>> q = db.insert('foo', name='bob', age=2, created=SQLLiteral('NOW()'), _test=True)
  14.751 -            >>> q
  14.752 -            <sql: "INSERT INTO foo (age, name, created) VALUES (2, 'bob', NOW())">
  14.753 -            >>> q.query()
  14.754 -            'INSERT INTO foo (age, name, created) VALUES (%s, %s, NOW())'
  14.755 -            >>> q.values()
  14.756 -            [2, 'bob']
  14.757 -        """
  14.758 -        def q(x): return "(" + x + ")"
  14.759 -        
  14.760 -        if values:
  14.761 -            _keys = SQLQuery.join(values.keys(), ', ')
  14.762 -            _values = SQLQuery.join([sqlparam(v) for v in values.values()], ', ')
  14.763 -            sql_query = "INSERT INTO %s " % tablename + q(_keys) + ' VALUES ' + q(_values)
  14.764 -        else:
  14.765 -            sql_query = SQLQuery(self._get_insert_default_values_query(tablename))
  14.766 -
  14.767 -        if _test: return sql_query
  14.768 -        
  14.769 -        db_cursor = self._db_cursor()
  14.770 -        if seqname is not False: 
  14.771 -            sql_query = self._process_insert_query(sql_query, tablename, seqname)
  14.772 -
  14.773 -        if isinstance(sql_query, tuple):
  14.774 -            # for some databases, a separate query has to be made to find 
  14.775 -            # the id of the inserted row.
  14.776 -            q1, q2 = sql_query
  14.777 -            self._db_execute(db_cursor, q1)
  14.778 -            self._db_execute(db_cursor, q2)
  14.779 -        else:
  14.780 -            self._db_execute(db_cursor, sql_query)
  14.781 -
  14.782 -        try: 
  14.783 -            out = db_cursor.fetchone()[0]
  14.784 -        except Exception: 
  14.785 -            out = None
  14.786 -        
  14.787 -        if not self.ctx.transactions: 
  14.788 -            self.ctx.commit()
  14.789 -        return out
  14.790 -        
  14.791 -    def _get_insert_default_values_query(self, table):
  14.792 -        return "INSERT INTO %s DEFAULT VALUES" % table
  14.793 -
  14.794 -    def multiple_insert(self, tablename, values, seqname=None, _test=False):
  14.795 -        """
  14.796 -        Inserts multiple rows into `tablename`. The `values` must be a list of dictioanries, 
  14.797 -        one for each row to be inserted, each with the same set of keys.
  14.798 -        Returns the list of ids of the inserted rows.        
  14.799 -        Set `seqname` to the ID if it's not the default, or to `False`
  14.800 -        if there isn't one.
  14.801 -        
  14.802 -            >>> db = DB(None, {})
  14.803 -            >>> db.supports_multiple_insert = True
  14.804 -            >>> values = [{"name": "foo", "email": "foo@example.com"}, {"name": "bar", "email": "bar@example.com"}]
  14.805 -            >>> db.multiple_insert('person', values=values, _test=True)
  14.806 -            <sql: "INSERT INTO person (name, email) VALUES ('foo', 'foo@example.com'), ('bar', 'bar@example.com')">
  14.807 -        """        
  14.808 -        if not values:
  14.809 -            return []
  14.810 -            
  14.811 -        if not self.supports_multiple_insert:
  14.812 -            out = [self.insert(tablename, seqname=seqname, _test=_test, **v) for v in values]
  14.813 -            if seqname is False:
  14.814 -                return None
  14.815 -            else:
  14.816 -                return out
  14.817 -                
  14.818 -        keys = values[0].keys()
  14.819 -        #@@ make sure all keys are valid
  14.820 -
  14.821 -        # make sure all rows have same keys.
  14.822 -        for v in values:
  14.823 -            if v.keys() != keys:
  14.824 -                raise ValueError, 'Bad data'
  14.825 -
  14.826 -        sql_query = SQLQuery('INSERT INTO %s (%s) VALUES ' % (tablename, ', '.join(keys)))
  14.827 -
  14.828 -        for i, row in enumerate(values):
  14.829 -            if i != 0:
  14.830 -                sql_query.append(", ")
  14.831 -            SQLQuery.join([SQLParam(row[k]) for k in keys], sep=", ", target=sql_query, prefix="(", suffix=")")
  14.832 -        
  14.833 -        if _test: return sql_query
  14.834 -
  14.835 -        db_cursor = self._db_cursor()
  14.836 -        if seqname is not False: 
  14.837 -            sql_query = self._process_insert_query(sql_query, tablename, seqname)
  14.838 -
  14.839 -        if isinstance(sql_query, tuple):
  14.840 -            # for some databases, a separate query has to be made to find 
  14.841 -            # the id of the inserted row.
  14.842 -            q1, q2 = sql_query
  14.843 -            self._db_execute(db_cursor, q1)
  14.844 -            self._db_execute(db_cursor, q2)
  14.845 -        else:
  14.846 -            self._db_execute(db_cursor, sql_query)
  14.847 -
  14.848 -        try: 
  14.849 -            out = db_cursor.fetchone()[0]
  14.850 -            out = range(out-len(values)+1, out+1)        
  14.851 -        except Exception: 
  14.852 -            out = None
  14.853 -
  14.854 -        if not self.ctx.transactions: 
  14.855 -            self.ctx.commit()
  14.856 -        return out
  14.857 -
  14.858 -    
  14.859 -    def update(self, tables, where, vars=None, _test=False, **values): 
  14.860 -        """
  14.861 -        Update `tables` with clause `where` (interpolated using `vars`)
  14.862 -        and setting `values`.
  14.863 -
  14.864 -            >>> db = DB(None, {})
  14.865 -            >>> name = 'Joseph'
  14.866 -            >>> q = db.update('foo', where='name = $name', name='bob', age=2,
  14.867 -            ...     created=SQLLiteral('NOW()'), vars=locals(), _test=True)
  14.868 -            >>> q
  14.869 -            <sql: "UPDATE foo SET age = 2, name = 'bob', created = NOW() WHERE name = 'Joseph'">
  14.870 -            >>> q.query()
  14.871 -            'UPDATE foo SET age = %s, name = %s, created = NOW() WHERE name = %s'
  14.872 -            >>> q.values()
  14.873 -            [2, 'bob', 'Joseph']
  14.874 -        """
  14.875 -        if vars is None: vars = {}
  14.876 -        where = self._where(where, vars)
  14.877 -
  14.878 -        query = (
  14.879 -          "UPDATE " + sqllist(tables) + 
  14.880 -          " SET " + sqlwhere(values, ', ') + 
  14.881 -          " WHERE " + where)
  14.882 -
  14.883 -        if _test: return query
  14.884 -        
  14.885 -        db_cursor = self._db_cursor()
  14.886 -        self._db_execute(db_cursor, query)
  14.887 -        if not self.ctx.transactions: 
  14.888 -            self.ctx.commit()
  14.889 -        return db_cursor.rowcount
  14.890 -    
  14.891 -    def delete(self, table, where, using=None, vars=None, _test=False): 
  14.892 -        """
  14.893 -        Deletes from `table` with clauses `where` and `using`.
  14.894 -
  14.895 -            >>> db = DB(None, {})
  14.896 -            >>> name = 'Joe'
  14.897 -            >>> db.delete('foo', where='name = $name', vars=locals(), _test=True)
  14.898 -            <sql: "DELETE FROM foo WHERE name = 'Joe'">
  14.899 -        """
  14.900 -        if vars is None: vars = {}
  14.901 -        where = self._where(where, vars)
  14.902 -
  14.903 -        q = 'DELETE FROM ' + table
  14.904 -        if using: q += ' USING ' + sqllist(using)
  14.905 -        if where: q += ' WHERE ' + where
  14.906 -
  14.907 -        if _test: return q
  14.908 -
  14.909 -        db_cursor = self._db_cursor()
  14.910 -        self._db_execute(db_cursor, q)
  14.911 -        if not self.ctx.transactions: 
  14.912 -            self.ctx.commit()
  14.913 -        return db_cursor.rowcount
  14.914 -
  14.915 -    def _process_insert_query(self, query, tablename, seqname):
  14.916 -        return query
  14.917 -
  14.918 -    def transaction(self): 
  14.919 -        """Start a transaction."""
  14.920 -        return Transaction(self.ctx)
  14.921 -    
  14.922 -class PostgresDB(DB): 
  14.923 -    """Postgres driver."""
  14.924 -    def __init__(self, **keywords):
  14.925 -        if 'pw' in keywords:
  14.926 -            keywords['password'] = keywords.pop('pw')
  14.927 -            
  14.928 -        db_module = import_driver(["psycopg2", "psycopg", "pgdb"], preferred=keywords.pop('driver', None))
  14.929 -        if db_module.__name__ == "psycopg2":
  14.930 -            import psycopg2.extensions
  14.931 -            psycopg2.extensions.register_type(psycopg2.extensions.UNICODE)
  14.932 -
  14.933 -        # if db is not provided postgres driver will take it from PGDATABASE environment variable
  14.934 -        if 'db' in keywords:
  14.935 -            keywords['database'] = keywords.pop('db')
  14.936 -        
  14.937 -        self.dbname = "postgres"
  14.938 -        self.paramstyle = db_module.paramstyle
  14.939 -        DB.__init__(self, db_module, keywords)
  14.940 -        self.supports_multiple_insert = True
  14.941 -        self._sequences = None
  14.942 -        
  14.943 -    def _process_insert_query(self, query, tablename, seqname):
  14.944 -        if seqname is None:
  14.945 -            # when seqname is not provided guess the seqname and make sure it exists
  14.946 -            seqname = tablename + "_id_seq"
  14.947 -            if seqname not in self._get_all_sequences():
  14.948 -                seqname = None
  14.949 -        
  14.950 -        if seqname:
  14.951 -            query += "; SELECT currval('%s')" % seqname
  14.952 -            
  14.953 -        return query
  14.954 -    
  14.955 -    def _get_all_sequences(self):
  14.956 -        """Query postgres to find names of all sequences used in this database."""
  14.957 -        if self._sequences is None:
  14.958 -            q = "SELECT c.relname FROM pg_class c WHERE c.relkind = 'S'"
  14.959 -            self._sequences = set([c.relname for c in self.query(q)])
  14.960 -        return self._sequences
  14.961 -
  14.962 -    def _connect(self, keywords):
  14.963 -        conn = DB._connect(self, keywords)
  14.964 -        try:
  14.965 -            conn.set_client_encoding('UTF8')
  14.966 -        except AttributeError:
  14.967 -            # fallback for pgdb driver
  14.968 -            conn.cursor().execute("set client_encoding to 'UTF-8'")
  14.969 -        return conn
  14.970 -        
  14.971 -    def _connect_with_pooling(self, keywords):
  14.972 -        conn = DB._connect_with_pooling(self, keywords)
  14.973 -        conn._con._con.set_client_encoding('UTF8')
  14.974 -        return conn
  14.975 -
  14.976 -class MySQLDB(DB): 
  14.977 -    def __init__(self, **keywords):
  14.978 -        import MySQLdb as db
  14.979 -        if 'pw' in keywords:
  14.980 -            keywords['passwd'] = keywords['pw']
  14.981 -            del keywords['pw']
  14.982 -
  14.983 -        if 'charset' not in keywords:
  14.984 -            keywords['charset'] = 'utf8'
  14.985 -        elif keywords['charset'] is None:
  14.986 -            del keywords['charset']
  14.987 -
  14.988 -        self.paramstyle = db.paramstyle = 'pyformat' # it's both, like psycopg
  14.989 -        self.dbname = "mysql"
  14.990 -        DB.__init__(self, db, keywords)
  14.991 -        self.supports_multiple_insert = True
  14.992 -        
  14.993 -    def _process_insert_query(self, query, tablename, seqname):
  14.994 -        return query, SQLQuery('SELECT last_insert_id();')
  14.995 -        
  14.996 -    def _get_insert_default_values_query(self, table):
  14.997 -        return "INSERT INTO %s () VALUES()" % table
  14.998 -
  14.999 -def import_driver(drivers, preferred=None):
 14.1000 -    """Import the first available driver or preferred driver.
 14.1001 -    """
 14.1002 -    if preferred:
 14.1003 -        drivers = [preferred]
 14.1004 -
 14.1005 -    for d in drivers:
 14.1006 -        try:
 14.1007 -            return __import__(d, None, None, ['x'])
 14.1008 -        except ImportError:
 14.1009 -            pass
 14.1010 -    raise ImportError("Unable to import " + " or ".join(drivers))
 14.1011 -
 14.1012 -class SqliteDB(DB): 
 14.1013 -    def __init__(self, **keywords):
 14.1014 -        db = import_driver(["sqlite3", "pysqlite2.dbapi2", "sqlite"], preferred=keywords.pop('driver', None))
 14.1015 -
 14.1016 -        if db.__name__ in ["sqlite3", "pysqlite2.dbapi2"]:
 14.1017 -            db.paramstyle = 'qmark'
 14.1018 -            
 14.1019 -        # sqlite driver doesn't create datatime objects for timestamp columns unless `detect_types` option is passed.
 14.1020 -        # It seems to be supported in sqlite3 and pysqlite2 drivers, not surte about sqlite.
 14.1021 -        keywords.setdefault('detect_types', db.PARSE_DECLTYPES)
 14.1022 -
 14.1023 -        self.paramstyle = db.paramstyle
 14.1024 -        keywords['database'] = keywords.pop('db')
 14.1025 -        keywords['pooling'] = False # sqlite don't allows connections to be shared by threads
 14.1026 -        self.dbname = "sqlite"        
 14.1027 -        DB.__init__(self, db, keywords)
 14.1028 -
 14.1029 -    def _process_insert_query(self, query, tablename, seqname):
 14.1030 -        return query, SQLQuery('SELECT last_insert_rowid();')
 14.1031 -    
 14.1032 -    def query(self, *a, **kw):
 14.1033 -        out = DB.query(self, *a, **kw)
 14.1034 -        if isinstance(out, iterbetter):
 14.1035 -            del out.__len__
 14.1036 -        return out
 14.1037 -
 14.1038 -class FirebirdDB(DB):
 14.1039 -    """Firebird Database.
 14.1040 -    """
 14.1041 -    def __init__(self, **keywords):
 14.1042 -        try:
 14.1043 -            import kinterbasdb as db
 14.1044 -        except Exception:
 14.1045 -            db = None
 14.1046 -            pass
 14.1047 -        if 'pw' in keywords:
 14.1048 -            keywords['passwd'] = keywords['pw']
 14.1049 -            del keywords['pw']
 14.1050 -        keywords['database'] = keywords['db']
 14.1051 -        del keywords['db']
 14.1052 -        DB.__init__(self, db, keywords)
 14.1053 -        
 14.1054 -    def delete(self, table, where=None, using=None, vars=None, _test=False):
 14.1055 -        # firebird doesn't support using clause
 14.1056 -        using=None
 14.1057 -        return DB.delete(self, table, where, using, vars, _test)
 14.1058 -
 14.1059 -    def sql_clauses(self, what, tables, where, group, order, limit, offset):
 14.1060 -        return (
 14.1061 -            ('SELECT', ''),
 14.1062 -            ('FIRST', limit),
 14.1063 -            ('SKIP', offset),
 14.1064 -            ('', what),
 14.1065 -            ('FROM', sqllist(tables)),
 14.1066 -            ('WHERE', where),
 14.1067 -            ('GROUP BY', group),
 14.1068 -            ('ORDER BY', order)
 14.1069 -        )
 14.1070 -
 14.1071 -class MSSQLDB(DB):
 14.1072 -    def __init__(self, **keywords):
 14.1073 -        import pymssql as db    
 14.1074 -        if 'pw' in keywords:
 14.1075 -            keywords['password'] = keywords.pop('pw')
 14.1076 -        keywords['database'] = keywords.pop('db')
 14.1077 -        self.dbname = "mssql"
 14.1078 -        DB.__init__(self, db, keywords)
 14.1079 -
 14.1080 -    def _process_query(self, sql_query):
 14.1081 -        """Takes the SQLQuery object and returns query string and parameters.
 14.1082 -        """
 14.1083 -        # MSSQLDB expects params to be a tuple. 
 14.1084 -        # Overwriting the default implementation to convert params to tuple.
 14.1085 -        paramstyle = getattr(self, 'paramstyle', 'pyformat')
 14.1086 -        query = sql_query.query(paramstyle)
 14.1087 -        params = sql_query.values()
 14.1088 -        return query, tuple(params)
 14.1089 -
 14.1090 -    def sql_clauses(self, what, tables, where, group, order, limit, offset): 
 14.1091 -        return (
 14.1092 -            ('SELECT', what),
 14.1093 -            ('TOP', limit),
 14.1094 -            ('FROM', sqllist(tables)),
 14.1095 -            ('WHERE', where),
 14.1096 -            ('GROUP BY', group),
 14.1097 -            ('ORDER BY', order),
 14.1098 -            ('OFFSET', offset))
 14.1099 -            
 14.1100 -    def _test(self):
 14.1101 -        """Test LIMIT.
 14.1102 -
 14.1103 -            Fake presence of pymssql module for running tests.
 14.1104 -            >>> import sys
 14.1105 -            >>> sys.modules['pymssql'] = sys.modules['sys']
 14.1106 -            
 14.1107 -            MSSQL has TOP clause instead of LIMIT clause.
 14.1108 -            >>> db = MSSQLDB(db='test', user='joe', pw='secret')
 14.1109 -            >>> db.select('foo', limit=4, _test=True)
 14.1110 -            <sql: 'SELECT * TOP 4 FROM foo'>
 14.1111 -        """
 14.1112 -        pass
 14.1113 -
 14.1114 -class OracleDB(DB): 
 14.1115 -    def __init__(self, **keywords): 
 14.1116 -        import cx_Oracle as db 
 14.1117 -        if 'pw' in keywords: 
 14.1118 -            keywords['password'] = keywords.pop('pw') 
 14.1119 -
 14.1120 -        #@@ TODO: use db.makedsn if host, port is specified 
 14.1121 -        keywords['dsn'] = keywords.pop('db') 
 14.1122 -        self.dbname = 'oracle' 
 14.1123 -        db.paramstyle = 'numeric' 
 14.1124 -        self.paramstyle = db.paramstyle
 14.1125 -
 14.1126 -        # oracle doesn't support pooling 
 14.1127 -        keywords.pop('pooling', None) 
 14.1128 -        DB.__init__(self, db, keywords) 
 14.1129 -
 14.1130 -    def _process_insert_query(self, query, tablename, seqname): 
 14.1131 -        if seqname is None: 
 14.1132 -            # It is not possible to get seq name from table name in Oracle
 14.1133 -            return query
 14.1134 -        else:
 14.1135 -            return query + "; SELECT %s.currval FROM dual" % seqname 
 14.1136 -
 14.1137 -_databases = {}
 14.1138 -def database(dburl=None, **params):
 14.1139 -    """Creates appropriate database using params.
 14.1140 -    
 14.1141 -    Pooling will be enabled if DBUtils module is available. 
 14.1142 -    Pooling can be disabled by passing pooling=False in params.
 14.1143 -    """
 14.1144 -    dbn = params.pop('dbn')
 14.1145 -    if dbn in _databases:
 14.1146 -        return _databases[dbn](**params)
 14.1147 -    else:
 14.1148 -        raise UnknownDB, dbn
 14.1149 -
 14.1150 -def register_database(name, clazz):
 14.1151 -    """
 14.1152 -    Register a database.
 14.1153 -
 14.1154 -        >>> class LegacyDB(DB): 
 14.1155 -        ...     def __init__(self, **params): 
 14.1156 -        ...        pass 
 14.1157 -        ...
 14.1158 -        >>> register_database('legacy', LegacyDB)
 14.1159 -        >>> db = database(dbn='legacy', db='test', user='joe', passwd='secret') 
 14.1160 -    """
 14.1161 -    _databases[name] = clazz
 14.1162 -
 14.1163 -register_database('mysql', MySQLDB)
 14.1164 -register_database('postgres', PostgresDB)
 14.1165 -register_database('sqlite', SqliteDB)
 14.1166 -register_database('firebird', FirebirdDB)
 14.1167 -register_database('mssql', MSSQLDB)
 14.1168 -register_database('oracle', OracleDB)
 14.1169 -
 14.1170 -def _interpolate(format): 
 14.1171 -    """
 14.1172 -    Takes a format string and returns a list of 2-tuples of the form
 14.1173 -    (boolean, string) where boolean says whether string should be evaled
 14.1174 -    or not.
 14.1175 -
 14.1176 -    from <http://lfw.org/python/Itpl.py> (public domain, Ka-Ping Yee)
 14.1177 -    """
 14.1178 -    from tokenize import tokenprog
 14.1179 -
 14.1180 -    def matchorfail(text, pos):
 14.1181 -        match = tokenprog.match(text, pos)
 14.1182 -        if match is None:
 14.1183 -            raise _ItplError(text, pos)
 14.1184 -        return match, match.end()
 14.1185 -
 14.1186 -    namechars = "abcdefghijklmnopqrstuvwxyz" \
 14.1187 -        "ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789_";
 14.1188 -    chunks = []
 14.1189 -    pos = 0
 14.1190 -
 14.1191 -    while 1:
 14.1192 -        dollar = format.find("$", pos)
 14.1193 -        if dollar < 0: 
 14.1194 -            break
 14.1195 -        nextchar = format[dollar + 1]
 14.1196 -
 14.1197 -        if nextchar == "{":
 14.1198 -            chunks.append((0, format[pos:dollar]))
 14.1199 -            pos, level = dollar + 2, 1
 14.1200 -            while level:
 14.1201 -                match, pos = matchorfail(format, pos)
 14.1202 -                tstart, tend = match.regs[3]
 14.1203 -                token = format[tstart:tend]
 14.1204 -                if token == "{": 
 14.1205 -                    level = level + 1
 14.1206 -                elif token == "}":  
 14.1207 -                    level = level - 1
 14.1208 -            chunks.append((1, format[dollar + 2:pos - 1]))
 14.1209 -
 14.1210 -        elif nextchar in namechars:
 14.1211 -            chunks.append((0, format[pos:dollar]))
 14.1212 -            match, pos = matchorfail(format, dollar + 1)
 14.1213 -            while pos < len(format):
 14.1214 -                if format[pos] == "." and \
 14.1215 -                    pos + 1 < len(format) and format[pos + 1] in namechars:
 14.1216 -                    match, pos = matchorfail(format, pos + 1)
 14.1217 -                elif format[pos] in "([":
 14.1218 -                    pos, level = pos + 1, 1
 14.1219 -                    while level:
 14.1220 -                        match, pos = matchorfail(format, pos)
 14.1221 -                        tstart, tend = match.regs[3]
 14.1222 -                        token = format[tstart:tend]
 14.1223 -                        if token[0] in "([": 
 14.1224 -                            level = level + 1
 14.1225 -                        elif token[0] in ")]":  
 14.1226 -                            level = level - 1
 14.1227 -                else: 
 14.1228 -                    break
 14.1229 -            chunks.append((1, format[dollar + 1:pos]))
 14.1230 -        else:
 14.1231 -            chunks.append((0, format[pos:dollar + 1]))
 14.1232 -            pos = dollar + 1 + (nextchar == "$")
 14.1233 -
 14.1234 -    if pos < len(format): 
 14.1235 -        chunks.append((0, format[pos:]))
 14.1236 -    return chunks
 14.1237 -
 14.1238 -if __name__ == "__main__":
 14.1239 -    import doctest
 14.1240 -    doctest.testmod()
    15.1 --- a/OpenSecurity/install/web.py-0.37/build/lib/web/debugerror.py	Thu Feb 20 15:40:48 2014 +0100
    15.2 +++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
    15.3 @@ -1,354 +0,0 @@
    15.4 -"""
    15.5 -pretty debug errors
    15.6 -(part of web.py)
    15.7 -
    15.8 -portions adapted from Django <djangoproject.com> 
    15.9 -Copyright (c) 2005, the Lawrence Journal-World
   15.10 -Used under the modified BSD license:
   15.11 -http://www.xfree86.org/3.3.6/COPYRIGHT2.html#5
   15.12 -"""
   15.13 -
   15.14 -__all__ = ["debugerror", "djangoerror", "emailerrors"]
   15.15 -
   15.16 -import sys, urlparse, pprint, traceback
   15.17 -from template import Template
   15.18 -from net import websafe
   15.19 -from utils import sendmail, safestr
   15.20 -import webapi as web
   15.21 -
   15.22 -import os, os.path
   15.23 -whereami = os.path.join(os.getcwd(), __file__)
   15.24 -whereami = os.path.sep.join(whereami.split(os.path.sep)[:-1])
   15.25 -djangoerror_t = """\
   15.26 -$def with (exception_type, exception_value, frames)
   15.27 -<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
   15.28 -<html lang="en">
   15.29 -<head>
   15.30 -  <meta http-equiv="content-type" content="text/html; charset=utf-8" />
   15.31 -  <meta name="robots" content="NONE,NOARCHIVE" />
   15.32 -  <title>$exception_type at $ctx.path</title>
   15.33 -  <style type="text/css">
   15.34 -    html * { padding:0; margin:0; }
   15.35 -    body * { padding:10px 20px; }
   15.36 -    body * * { padding:0; }
   15.37 -    body { font:small sans-serif; }
   15.38 -    body>div { border-bottom:1px solid #ddd; }
   15.39 -    h1 { font-weight:normal; }
   15.40 -    h2 { margin-bottom:.8em; }
   15.41 -    h2 span { font-size:80%; color:#666; font-weight:normal; }
   15.42 -    h3 { margin:1em 0 .5em 0; }
   15.43 -    h4 { margin:0 0 .5em 0; font-weight: normal; }
   15.44 -    table { 
   15.45 -        border:1px solid #ccc; border-collapse: collapse; background:white; }
   15.46 -    tbody td, tbody th { vertical-align:top; padding:2px 3px; }
   15.47 -    thead th { 
   15.48 -        padding:1px 6px 1px 3px; background:#fefefe; text-align:left; 
   15.49 -        font-weight:normal; font-size:11px; border:1px solid #ddd; }
   15.50 -    tbody th { text-align:right; color:#666; padding-right:.5em; }
   15.51 -    table.vars { margin:5px 0 2px 40px; }
   15.52 -    table.vars td, table.req td { font-family:monospace; }
   15.53 -    table td.code { width:100%;}
   15.54 -    table td.code div { overflow:hidden; }
   15.55 -    table.source th { color:#666; }
   15.56 -    table.source td { 
   15.57 -        font-family:monospace; white-space:pre; border-bottom:1px solid #eee; }
   15.58 -    ul.traceback { list-style-type:none; }
   15.59 -    ul.traceback li.frame { margin-bottom:1em; }
   15.60 -    div.context { margin: 10px 0; }
   15.61 -    div.context ol { 
   15.62 -        padding-left:30px; margin:0 10px; list-style-position: inside; }
   15.63 -    div.context ol li { 
   15.64 -        font-family:monospace; white-space:pre; color:#666; cursor:pointer; }
   15.65 -    div.context ol.context-line li { color:black; background-color:#ccc; }
   15.66 -    div.context ol.context-line li span { float: right; }
   15.67 -    div.commands { margin-left: 40px; }
   15.68 -    div.commands a { color:black; text-decoration:none; }
   15.69 -    #summary { background: #ffc; }
   15.70 -    #summary h2 { font-weight: normal; color: #666; }
   15.71 -    #explanation { background:#eee; }
   15.72 -    #template, #template-not-exist { background:#f6f6f6; }
   15.73 -    #template-not-exist ul { margin: 0 0 0 20px; }
   15.74 -    #traceback { background:#eee; }
   15.75 -    #requestinfo { background:#f6f6f6; padding-left:120px; }
   15.76 -    #summary table { border:none; background:transparent; }
   15.77 -    #requestinfo h2, #requestinfo h3 { position:relative; margin-left:-100px; }
   15.78 -    #requestinfo h3 { margin-bottom:-1em; }
   15.79 -    .error { background: #ffc; }
   15.80 -    .specific { color:#cc3300; font-weight:bold; }
   15.81 -  </style>
   15.82 -  <script type="text/javascript">
   15.83 -  //<!--
   15.84 -    function getElementsByClassName(oElm, strTagName, strClassName){
   15.85 -        // Written by Jonathan Snook, http://www.snook.ca/jon; 
   15.86 -        // Add-ons by Robert Nyman, http://www.robertnyman.com
   15.87 -        var arrElements = (strTagName == "*" && document.all)? document.all :
   15.88 -        oElm.getElementsByTagName(strTagName);
   15.89 -        var arrReturnElements = new Array();
   15.90 -        strClassName = strClassName.replace(/\-/g, "\\-");
   15.91 -        var oRegExp = new RegExp("(^|\\s)" + strClassName + "(\\s|$$)");
   15.92 -        var oElement;
   15.93 -        for(var i=0; i<arrElements.length; i++){
   15.94 -            oElement = arrElements[i];
   15.95 -            if(oRegExp.test(oElement.className)){
   15.96 -                arrReturnElements.push(oElement);
   15.97 -            }
   15.98 -        }
   15.99 -        return (arrReturnElements)
  15.100 -    }
  15.101 -    function hideAll(elems) {
  15.102 -      for (var e = 0; e < elems.length; e++) {
  15.103 -        elems[e].style.display = 'none';
  15.104 -      }
  15.105 -    }
  15.106 -    window.onload = function() {
  15.107 -      hideAll(getElementsByClassName(document, 'table', 'vars'));
  15.108 -      hideAll(getElementsByClassName(document, 'ol', 'pre-context'));
  15.109 -      hideAll(getElementsByClassName(document, 'ol', 'post-context'));
  15.110 -    }
  15.111 -    function toggle() {
  15.112 -      for (var i = 0; i < arguments.length; i++) {
  15.113 -        var e = document.getElementById(arguments[i]);
  15.114 -        if (e) {
  15.115 -          e.style.display = e.style.display == 'none' ? 'block' : 'none';
  15.116 -        }
  15.117 -      }
  15.118 -      return false;
  15.119 -    }
  15.120 -    function varToggle(link, id) {
  15.121 -      toggle('v' + id);
  15.122 -      var s = link.getElementsByTagName('span')[0];
  15.123 -      var uarr = String.fromCharCode(0x25b6);
  15.124 -      var darr = String.fromCharCode(0x25bc);
  15.125 -      s.innerHTML = s.innerHTML == uarr ? darr : uarr;
  15.126 -      return false;
  15.127 -    }
  15.128 -    //-->
  15.129 -  </script>
  15.130 -</head>
  15.131 -<body>
  15.132 -
  15.133 -$def dicttable (d, kls='req', id=None):
  15.134 -    $ items = d and d.items() or []
  15.135 -    $items.sort()
  15.136 -    $:dicttable_items(items, kls, id)
  15.137 -        
  15.138 -$def dicttable_items(items, kls='req', id=None):
  15.139 -    $if items:
  15.140 -        <table class="$kls"
  15.141 -        $if id: id="$id"
  15.142 -        ><thead><tr><th>Variable</th><th>Value</th></tr></thead>
  15.143 -        <tbody>
  15.144 -        $for k, v in items:
  15.145 -            <tr><td>$k</td><td class="code"><div>$prettify(v)</div></td></tr>
  15.146 -        </tbody>
  15.147 -        </table>
  15.148 -    $else:
  15.149 -        <p>No data.</p>
  15.150 -
  15.151 -<div id="summary">
  15.152 -  <h1>$exception_type at $ctx.path</h1>
  15.153 -  <h2>$exception_value</h2>
  15.154 -  <table><tr>
  15.155 -    <th>Python</th>
  15.156 -    <td>$frames[0].filename in $frames[0].function, line $frames[0].lineno</td>
  15.157 -  </tr><tr>
  15.158 -    <th>Web</th>
  15.159 -    <td>$ctx.method $ctx.home$ctx.path</td>
  15.160 -  </tr></table>
  15.161 -</div>
  15.162 -<div id="traceback">
  15.163 -<h2>Traceback <span>(innermost first)</span></h2>
  15.164 -<ul class="traceback">
  15.165 -$for frame in frames:
  15.166 -    <li class="frame">
  15.167 -    <code>$frame.filename</code> in <code>$frame.function</code>
  15.168 -    $if frame.context_line is not None:
  15.169 -        <div class="context" id="c$frame.id">
  15.170 -        $if frame.pre_context:
  15.171 -            <ol start="$frame.pre_context_lineno" class="pre-context" id="pre$frame.id">
  15.172 -            $for line in frame.pre_context:
  15.173 -                <li onclick="toggle('pre$frame.id', 'post$frame.id')">$line</li>
  15.174 -            </ol>
  15.175 -            <ol start="$frame.lineno" class="context-line"><li onclick="toggle('pre$frame.id', 'post$frame.id')">$frame.context_line <span>...</span></li></ol>
  15.176 -        $if frame.post_context:
  15.177 -            <ol start='${frame.lineno + 1}' class="post-context" id="post$frame.id">
  15.178 -            $for line in frame.post_context:
  15.179 -                <li onclick="toggle('pre$frame.id', 'post$frame.id')">$line</li>
  15.180 -            </ol>
  15.181 -      </div>
  15.182 -    
  15.183 -    $if frame.vars:
  15.184 -        <div class="commands">
  15.185 -        <a href='#' onclick="return varToggle(this, '$frame.id')"><span>&#x25b6;</span> Local vars</a>
  15.186 -        $# $inspect.formatargvalues(*inspect.getargvalues(frame['tb'].tb_frame))
  15.187 -        </div>
  15.188 -        $:dicttable(frame.vars, kls='vars', id=('v' + str(frame.id)))
  15.189 -      </li>
  15.190 -  </ul>
  15.191 -</div>
  15.192 -
  15.193 -<div id="requestinfo">
  15.194 -$if ctx.output or ctx.headers:
  15.195 -    <h2>Response so far</h2>
  15.196 -    <h3>HEADERS</h3>
  15.197 -    $:dicttable_items(ctx.headers)
  15.198 -
  15.199 -    <h3>BODY</h3>
  15.200 -    <p class="req" style="padding-bottom: 2em"><code>
  15.201 -    $ctx.output
  15.202 -    </code></p>
  15.203 -  
  15.204 -<h2>Request information</h2>
  15.205 -
  15.206 -<h3>INPUT</h3>
  15.207 -$:dicttable(web.input(_unicode=False))
  15.208 -
  15.209 -<h3 id="cookie-info">COOKIES</h3>
  15.210 -$:dicttable(web.cookies())
  15.211 -
  15.212 -<h3 id="meta-info">META</h3>
  15.213 -$ newctx = [(k, v) for (k, v) in ctx.iteritems() if not k.startswith('_') and not isinstance(v, dict)]
  15.214 -$:dicttable(dict(newctx))
  15.215 -
  15.216 -<h3 id="meta-info">ENVIRONMENT</h3>
  15.217 -$:dicttable(ctx.env)
  15.218 -</div>
  15.219 -
  15.220 -<div id="explanation">
  15.221 -  <p>
  15.222 -    You're seeing this error because you have <code>web.config.debug</code>
  15.223 -    set to <code>True</code>. Set that to <code>False</code> if you don't want to see this.
  15.224 -  </p>
  15.225 -</div>
  15.226 -
  15.227 -</body>
  15.228 -</html>
  15.229 -"""
  15.230 -
  15.231 -djangoerror_r = None
  15.232 -
  15.233 -def djangoerror():
  15.234 -    def _get_lines_from_file(filename, lineno, context_lines):
  15.235 -        """
  15.236 -        Returns context_lines before and after lineno from file.
  15.237 -        Returns (pre_context_lineno, pre_context, context_line, post_context).
  15.238 -        """
  15.239 -        try:
  15.240 -            source = open(filename).readlines()
  15.241 -            lower_bound = max(0, lineno - context_lines)
  15.242 -            upper_bound = lineno + context_lines
  15.243 -
  15.244 -            pre_context = \
  15.245 -                [line.strip('\n') for line in source[lower_bound:lineno]]
  15.246 -            context_line = source[lineno].strip('\n')
  15.247 -            post_context = \
  15.248 -                [line.strip('\n') for line in source[lineno + 1:upper_bound]]
  15.249 -
  15.250 -            return lower_bound, pre_context, context_line, post_context
  15.251 -        except (OSError, IOError, IndexError):
  15.252 -            return None, [], None, []    
  15.253 -    
  15.254 -    exception_type, exception_value, tback = sys.exc_info()
  15.255 -    frames = []
  15.256 -    while tback is not None:
  15.257 -        filename = tback.tb_frame.f_code.co_filename
  15.258 -        function = tback.tb_frame.f_code.co_name
  15.259 -        lineno = tback.tb_lineno - 1
  15.260 -
  15.261 -        # hack to get correct line number for templates
  15.262 -        lineno += tback.tb_frame.f_locals.get("__lineoffset__", 0)
  15.263 -        
  15.264 -        pre_context_lineno, pre_context, context_line, post_context = \
  15.265 -            _get_lines_from_file(filename, lineno, 7)
  15.266 -
  15.267 -        if '__hidetraceback__' not in tback.tb_frame.f_locals:
  15.268 -            frames.append(web.storage({
  15.269 -                'tback': tback,
  15.270 -                'filename': filename,
  15.271 -                'function': function,
  15.272 -                'lineno': lineno,
  15.273 -                'vars': tback.tb_frame.f_locals,
  15.274 -                'id': id(tback),
  15.275 -                'pre_context': pre_context,
  15.276 -                'context_line': context_line,
  15.277 -                'post_context': post_context,
  15.278 -                'pre_context_lineno': pre_context_lineno,
  15.279 -            }))
  15.280 -        tback = tback.tb_next
  15.281 -    frames.reverse()
  15.282 -    urljoin = urlparse.urljoin
  15.283 -    def prettify(x):
  15.284 -        try: 
  15.285 -            out = pprint.pformat(x)
  15.286 -        except Exception, e: 
  15.287 -            out = '[could not display: <' + e.__class__.__name__ + \
  15.288 -                  ': '+str(e)+'>]'
  15.289 -        return out
  15.290 -        
  15.291 -    global djangoerror_r
  15.292 -    if djangoerror_r is None:
  15.293 -        djangoerror_r = Template(djangoerror_t, filename=__file__, filter=websafe)
  15.294 -        
  15.295 -    t = djangoerror_r
  15.296 -    globals = {'ctx': web.ctx, 'web':web, 'dict':dict, 'str':str, 'prettify': prettify}
  15.297 -    t.t.func_globals.update(globals)
  15.298 -    return t(exception_type, exception_value, frames)
  15.299 -
  15.300 -def debugerror():
  15.301 -    """
  15.302 -    A replacement for `internalerror` that presents a nice page with lots
  15.303 -    of debug information for the programmer.
  15.304 -
  15.305 -    (Based on the beautiful 500 page from [Django](http://djangoproject.com/), 
  15.306 -    designed by [Wilson Miner](http://wilsonminer.com/).)
  15.307 -    """
  15.308 -    return web._InternalError(djangoerror())
  15.309 -
  15.310 -def emailerrors(to_address, olderror, from_address=None):
  15.311 -    """
  15.312 -    Wraps the old `internalerror` handler (pass as `olderror`) to 
  15.313 -    additionally email all errors to `to_address`, to aid in
  15.314 -    debugging production websites.
  15.315 -    
  15.316 -    Emails contain a normal text traceback as well as an
  15.317 -    attachment containing the nice `debugerror` page.
  15.318 -    """
  15.319 -    from_address = from_address or to_address
  15.320 -
  15.321 -    def emailerrors_internal():
  15.322 -        error = olderror()
  15.323 -        tb = sys.exc_info()
  15.324 -        error_name = tb[0]
  15.325 -        error_value = tb[1]
  15.326 -        tb_txt = ''.join(traceback.format_exception(*tb))
  15.327 -        path = web.ctx.path
  15.328 -        request = web.ctx.method + ' ' + web.ctx.home + web.ctx.fullpath
  15.329 -        
  15.330 -        message = "\n%s\n\n%s\n\n" % (request, tb_txt)
  15.331 -        
  15.332 -        sendmail(
  15.333 -            "your buggy site <%s>" % from_address,
  15.334 -            "the bugfixer <%s>" % to_address,
  15.335 -            "bug: %(error_name)s: %(error_value)s (%(path)s)" % locals(),
  15.336 -            message,
  15.337 -            attachments=[
  15.338 -                dict(filename="bug.html", content=safestr(djangoerror()))
  15.339 -            ],
  15.340 -        )
  15.341 -        return error
  15.342 -    
  15.343 -    return emailerrors_internal
  15.344 -
  15.345 -if __name__ == "__main__":
  15.346 -    urls = (
  15.347 -        '/', 'index'
  15.348 -    )
  15.349 -    from application import application
  15.350 -    app = application(urls, globals())
  15.351 -    app.internalerror = debugerror
  15.352 -    
  15.353 -    class index:
  15.354 -        def GET(self):
  15.355 -            thisdoesnotexist
  15.356 -
  15.357 -    app.run()
    16.1 --- a/OpenSecurity/install/web.py-0.37/build/lib/web/form.py	Thu Feb 20 15:40:48 2014 +0100
    16.2 +++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
    16.3 @@ -1,410 +0,0 @@
    16.4 -"""
    16.5 -HTML forms
    16.6 -(part of web.py)
    16.7 -"""
    16.8 -
    16.9 -import copy, re
   16.10 -import webapi as web
   16.11 -import utils, net
   16.12 -
   16.13 -def attrget(obj, attr, value=None):
   16.14 -    try:
   16.15 -        if hasattr(obj, 'has_key') and obj.has_key(attr): 
   16.16 -            return obj[attr]
   16.17 -    except TypeError:
   16.18 -        # Handle the case where has_key takes different number of arguments.
   16.19 -        # This is the case with Model objects on appengine. See #134
   16.20 -        pass
   16.21 -    if hasattr(obj, attr):
   16.22 -        return getattr(obj, attr)
   16.23 -    return value
   16.24 -
   16.25 -class Form(object):
   16.26 -    r"""
   16.27 -    HTML form.
   16.28 -    
   16.29 -        >>> f = Form(Textbox("x"))
   16.30 -        >>> f.render()
   16.31 -        u'<table>\n    <tr><th><label for="x">x</label></th><td><input type="text" id="x" name="x"/></td></tr>\n</table>'
   16.32 -    """
   16.33 -    def __init__(self, *inputs, **kw):
   16.34 -        self.inputs = inputs
   16.35 -        self.valid = True
   16.36 -        self.note = None
   16.37 -        self.validators = kw.pop('validators', [])
   16.38 -
   16.39 -    def __call__(self, x=None):
   16.40 -        o = copy.deepcopy(self)
   16.41 -        if x: o.validates(x)
   16.42 -        return o
   16.43 -    
   16.44 -    def render(self):
   16.45 -        out = ''
   16.46 -        out += self.rendernote(self.note)
   16.47 -        out += '<table>\n'
   16.48 -        
   16.49 -        for i in self.inputs:
   16.50 -            html = utils.safeunicode(i.pre) + i.render() + self.rendernote(i.note) + utils.safeunicode(i.post)
   16.51 -            if i.is_hidden():
   16.52 -                out += '    <tr style="display: none;"><th></th><td>%s</td></tr>\n' % (html)
   16.53 -            else:
   16.54 -                out += '    <tr><th><label for="%s">%s</label></th><td>%s</td></tr>\n' % (i.id, net.websafe(i.description), html)
   16.55 -        out += "</table>"
   16.56 -        return out
   16.57 -        
   16.58 -    def render_css(self): 
   16.59 -        out = [] 
   16.60 -        out.append(self.rendernote(self.note)) 
   16.61 -        for i in self.inputs:
   16.62 -            if not i.is_hidden():
   16.63 -                out.append('<label for="%s">%s</label>' % (i.id, net.websafe(i.description))) 
   16.64 -            out.append(i.pre)
   16.65 -            out.append(i.render()) 
   16.66 -            out.append(self.rendernote(i.note))
   16.67 -            out.append(i.post) 
   16.68 -            out.append('\n')
   16.69 -        return ''.join(out) 
   16.70 -        
   16.71 -    def rendernote(self, note):
   16.72 -        if note: return '<strong class="wrong">%s</strong>' % net.websafe(note)
   16.73 -        else: return ""
   16.74 -    
   16.75 -    def validates(self, source=None, _validate=True, **kw):
   16.76 -        source = source or kw or web.input()
   16.77 -        out = True
   16.78 -        for i in self.inputs:
   16.79 -            v = attrget(source, i.name)
   16.80 -            if _validate:
   16.81 -                out = i.validate(v) and out
   16.82 -            else:
   16.83 -                i.set_value(v)
   16.84 -        if _validate:
   16.85 -            out = out and self._validate(source)
   16.86 -            self.valid = out
   16.87 -        return out
   16.88 -
   16.89 -    def _validate(self, value):
   16.90 -        self.value = value
   16.91 -        for v in self.validators:
   16.92 -            if not v.valid(value):
   16.93 -                self.note = v.msg
   16.94 -                return False
   16.95 -        return True
   16.96 -
   16.97 -    def fill(self, source=None, **kw):
   16.98 -        return self.validates(source, _validate=False, **kw)
   16.99 -    
  16.100 -    def __getitem__(self, i):
  16.101 -        for x in self.inputs:
  16.102 -            if x.name == i: return x
  16.103 -        raise KeyError, i
  16.104 -
  16.105 -    def __getattr__(self, name):
  16.106 -        # don't interfere with deepcopy
  16.107 -        inputs = self.__dict__.get('inputs') or []
  16.108 -        for x in inputs:
  16.109 -            if x.name == name: return x
  16.110 -        raise AttributeError, name
  16.111 -    
  16.112 -    def get(self, i, default=None):
  16.113 -        try:
  16.114 -            return self[i]
  16.115 -        except KeyError:
  16.116 -            return default
  16.117 -            
  16.118 -    def _get_d(self): #@@ should really be form.attr, no?
  16.119 -        return utils.storage([(i.name, i.get_value()) for i in self.inputs])
  16.120 -    d = property(_get_d)
  16.121 -
  16.122 -class Input(object):
  16.123 -    def __init__(self, name, *validators, **attrs):
  16.124 -        self.name = name
  16.125 -        self.validators = validators
  16.126 -        self.attrs = attrs = AttributeList(attrs)
  16.127 -        
  16.128 -        self.description = attrs.pop('description', name)
  16.129 -        self.value = attrs.pop('value', None)
  16.130 -        self.pre = attrs.pop('pre', "")
  16.131 -        self.post = attrs.pop('post', "")
  16.132 -        self.note = None
  16.133 -        
  16.134 -        self.id = attrs.setdefault('id', self.get_default_id())
  16.135 -        
  16.136 -        if 'class_' in attrs:
  16.137 -            attrs['class'] = attrs['class_']
  16.138 -            del attrs['class_']
  16.139 -        
  16.140 -    def is_hidden(self):
  16.141 -        return False
  16.142 -        
  16.143 -    def get_type(self):
  16.144 -        raise NotImplementedError
  16.145 -        
  16.146 -    def get_default_id(self):
  16.147 -        return self.name
  16.148 -
  16.149 -    def validate(self, value):
  16.150 -        self.set_value(value)
  16.151 -
  16.152 -        for v in self.validators:
  16.153 -            if not v.valid(value):
  16.154 -                self.note = v.msg
  16.155 -                return False
  16.156 -        return True
  16.157 -
  16.158 -    def set_value(self, value):
  16.159 -        self.value = value
  16.160 -
  16.161 -    def get_value(self):
  16.162 -        return self.value
  16.163 -
  16.164 -    def render(self):
  16.165 -        attrs = self.attrs.copy()
  16.166 -        attrs['type'] = self.get_type()
  16.167 -        if self.value is not None:
  16.168 -            attrs['value'] = self.value
  16.169 -        attrs['name'] = self.name
  16.170 -        return '<input %s/>' % attrs
  16.171 -
  16.172 -    def rendernote(self, note):
  16.173 -        if note: return '<strong class="wrong">%s</strong>' % net.websafe(note)
  16.174 -        else: return ""
  16.175 -        
  16.176 -    def addatts(self):
  16.177 -        # add leading space for backward-compatibility
  16.178 -        return " " + str(self.attrs)
  16.179 -
  16.180 -class AttributeList(dict):
  16.181 -    """List of atributes of input.
  16.182 -    
  16.183 -    >>> a = AttributeList(type='text', name='x', value=20)
  16.184 -    >>> a
  16.185 -    <attrs: 'type="text" name="x" value="20"'>
  16.186 -    """
  16.187 -    def copy(self):
  16.188 -        return AttributeList(self)
  16.189 -        
  16.190 -    def __str__(self):
  16.191 -        return " ".join(['%s="%s"' % (k, net.websafe(v)) for k, v in self.items()])
  16.192 -        
  16.193 -    def __repr__(self):
  16.194 -        return '<attrs: %s>' % repr(str(self))
  16.195 -
  16.196 -class Textbox(Input):
  16.197 -    """Textbox input.
  16.198 -    
  16.199 -        >>> Textbox(name='foo', value='bar').render()
  16.200 -        u'<input type="text" id="foo" value="bar" name="foo"/>'
  16.201 -        >>> Textbox(name='foo', value=0).render()
  16.202 -        u'<input type="text" id="foo" value="0" name="foo"/>'
  16.203 -    """        
  16.204 -    def get_type(self):
  16.205 -        return 'text'
  16.206 -
  16.207 -class Password(Input):
  16.208 -    """Password input.
  16.209 -
  16.210 -        >>> Password(name='password', value='secret').render()
  16.211 -        u'<input type="password" id="password" value="secret" name="password"/>'
  16.212 -    """
  16.213 -    
  16.214 -    def get_type(self):
  16.215 -        return 'password'
  16.216 -
  16.217 -class Textarea(Input):
  16.218 -    """Textarea input.
  16.219 -    
  16.220 -        >>> Textarea(name='foo', value='bar').render()
  16.221 -        u'<textarea id="foo" name="foo">bar</textarea>'
  16.222 -    """
  16.223 -    def render(self):
  16.224 -        attrs = self.attrs.copy()
  16.225 -        attrs['name'] = self.name
  16.226 -        value = net.websafe(self.value or '')
  16.227 -        return '<textarea %s>%s</textarea>' % (attrs, value)
  16.228 -
  16.229 -class Dropdown(Input):
  16.230 -    r"""Dropdown/select input.
  16.231 -    
  16.232 -        >>> Dropdown(name='foo', args=['a', 'b', 'c'], value='b').render()
  16.233 -        u'<select id="foo" name="foo">\n  <option value="a">a</option>\n  <option selected="selected" value="b">b</option>\n  <option value="c">c</option>\n</select>\n'
  16.234 -        >>> Dropdown(name='foo', args=[('a', 'aa'), ('b', 'bb'), ('c', 'cc')], value='b').render()
  16.235 -        u'<select id="foo" name="foo">\n  <option value="a">aa</option>\n  <option selected="selected" value="b">bb</option>\n  <option value="c">cc</option>\n</select>\n'
  16.236 -    """
  16.237 -    def __init__(self, name, args, *validators, **attrs):
  16.238 -        self.args = args
  16.239 -        super(Dropdown, self).__init__(name, *validators, **attrs)
  16.240 -
  16.241 -    def render(self):
  16.242 -        attrs = self.attrs.copy()
  16.243 -        attrs['name'] = self.name
  16.244 -        
  16.245 -        x = '<select %s>\n' % attrs
  16.246 -        
  16.247 -        for arg in self.args:
  16.248 -            x += self._render_option(arg)
  16.249 -
  16.250 -        x += '</select>\n'
  16.251 -        return x
  16.252 -
  16.253 -    def _render_option(self, arg, indent='  '):
  16.254 -        if isinstance(arg, (tuple, list)):
  16.255 -            value, desc= arg
  16.256 -        else:
  16.257 -            value, desc = arg, arg 
  16.258 -
  16.259 -        if self.value == value or (isinstance(self.value, list) and value in self.value):
  16.260 -            select_p = ' selected="selected"'
  16.261 -        else:
  16.262 -            select_p = ''
  16.263 -        return indent + '<option%s value="%s">%s</option>\n' % (select_p, net.websafe(value), net.websafe(desc))
  16.264 -        
  16.265 -
  16.266 -class GroupedDropdown(Dropdown):
  16.267 -    r"""Grouped Dropdown/select input.
  16.268 -    
  16.269 -        >>> GroupedDropdown(name='car_type', args=(('Swedish Cars', ('Volvo', 'Saab')), ('German Cars', ('Mercedes', 'Audi'))), value='Audi').render()
  16.270 -        u'<select id="car_type" name="car_type">\n  <optgroup label="Swedish Cars">\n    <option value="Volvo">Volvo</option>\n    <option value="Saab">Saab</option>\n  </optgroup>\n  <optgroup label="German Cars">\n    <option value="Mercedes">Mercedes</option>\n    <option selected="selected" value="Audi">Audi</option>\n  </optgroup>\n</select>\n'
  16.271 -        >>> GroupedDropdown(name='car_type', args=(('Swedish Cars', (('v', 'Volvo'), ('s', 'Saab'))), ('German Cars', (('m', 'Mercedes'), ('a', 'Audi')))), value='a').render()
  16.272 -        u'<select id="car_type" name="car_type">\n  <optgroup label="Swedish Cars">\n    <option value="v">Volvo</option>\n    <option value="s">Saab</option>\n  </optgroup>\n  <optgroup label="German Cars">\n    <option value="m">Mercedes</option>\n    <option selected="selected" value="a">Audi</option>\n  </optgroup>\n</select>\n'
  16.273 -
  16.274 -    """
  16.275 -    def __init__(self, name, args, *validators, **attrs):
  16.276 -        self.args = args
  16.277 -        super(Dropdown, self).__init__(name, *validators, **attrs)
  16.278 -
  16.279 -    def render(self):
  16.280 -        attrs = self.attrs.copy()
  16.281 -        attrs['name'] = self.name
  16.282 -        
  16.283 -        x = '<select %s>\n' % attrs
  16.284 -        
  16.285 -        for label, options in self.args:
  16.286 -            x += '  <optgroup label="%s">\n' % net.websafe(label)
  16.287 -            for arg in options:
  16.288 -                x += self._render_option(arg, indent = '    ')
  16.289 -            x +=  '  </optgroup>\n'
  16.290 -            
  16.291 -        x += '</select>\n'
  16.292 -        return x
  16.293 -
  16.294 -class Radio(Input):
  16.295 -    def __init__(self, name, args, *validators, **attrs):
  16.296 -        self.args = args
  16.297 -        super(Radio, self).__init__(name, *validators, **attrs)
  16.298 -
  16.299 -    def render(self):
  16.300 -        x = '<span>'
  16.301 -        for arg in self.args:
  16.302 -            if isinstance(arg, (tuple, list)):
  16.303 -                value, desc= arg
  16.304 -            else:
  16.305 -                value, desc = arg, arg 
  16.306 -            attrs = self.attrs.copy()
  16.307 -            attrs['name'] = self.name
  16.308 -            attrs['type'] = 'radio'
  16.309 -            attrs['value'] = value
  16.310 -            if self.value == value:
  16.311 -                attrs['checked'] = 'checked'
  16.312 -            x += '<input %s/> %s' % (attrs, net.websafe(desc))
  16.313 -        x += '</span>'
  16.314 -        return x
  16.315 -
  16.316 -class Checkbox(Input):
  16.317 -    """Checkbox input.
  16.318 -
  16.319 -    >>> Checkbox('foo', value='bar', checked=True).render()
  16.320 -    u'<input checked="checked" type="checkbox" id="foo_bar" value="bar" name="foo"/>'
  16.321 -    >>> Checkbox('foo', value='bar').render()
  16.322 -    u'<input type="checkbox" id="foo_bar" value="bar" name="foo"/>'
  16.323 -    >>> c = Checkbox('foo', value='bar')
  16.324 -    >>> c.validate('on')
  16.325 -    True
  16.326 -    >>> c.render()
  16.327 -    u'<input checked="checked" type="checkbox" id="foo_bar" value="bar" name="foo"/>'
  16.328 -    """
  16.329 -    def __init__(self, name, *validators, **attrs):
  16.330 -        self.checked = attrs.pop('checked', False)
  16.331 -        Input.__init__(self, name, *validators, **attrs)
  16.332 -        
  16.333 -    def get_default_id(self):
  16.334 -        value = utils.safestr(self.value or "")
  16.335 -        return self.name + '_' + value.replace(' ', '_')
  16.336 -
  16.337 -    def render(self):
  16.338 -        attrs = self.attrs.copy()
  16.339 -        attrs['type'] = 'checkbox'
  16.340 -        attrs['name'] = self.name
  16.341 -        attrs['value'] = self.value
  16.342 -
  16.343 -        if self.checked:
  16.344 -            attrs['checked'] = 'checked'            
  16.345 -        return '<input %s/>' % attrs
  16.346 -
  16.347 -    def set_value(self, value):
  16.348 -        self.checked = bool(value)
  16.349 -
  16.350 -    def get_value(self):
  16.351 -        return self.checked
  16.352 -
  16.353 -class Button(Input):
  16.354 -    """HTML Button.
  16.355 -    
  16.356 -    >>> Button("save").render()
  16.357 -    u'<button id="save" name="save">save</button>'
  16.358 -    >>> Button("action", value="save", html="<b>Save Changes</b>").render()
  16.359 -    u'<button id="action" value="save" name="action"><b>Save Changes</b></button>'
  16.360 -    """
  16.361 -    def __init__(self, name, *validators, **attrs):
  16.362 -        super(Button, self).__init__(name, *validators, **attrs)
  16.363 -        self.description = ""
  16.364 -
  16.365 -    def render(self):
  16.366 -        attrs = self.attrs.copy()
  16.367 -        attrs['name'] = self.name
  16.368 -        if self.value is not None:
  16.369 -            attrs['value'] = self.value
  16.370 -        html = attrs.pop('html', None) or net.websafe(self.name)
  16.371 -        return '<button %s>%s</button>' % (attrs, html)
  16.372 -
  16.373 -class Hidden(Input):
  16.374 -    """Hidden Input.
  16.375 -    
  16.376 -        >>> Hidden(name='foo', value='bar').render()
  16.377 -        u'<input type="hidden" id="foo" value="bar" name="foo"/>'
  16.378 -    """
  16.379 -    def is_hidden(self):
  16.380 -        return True
  16.381 -        
  16.382 -    def get_type(self):
  16.383 -        return 'hidden'
  16.384 -
  16.385 -class File(Input):
  16.386 -    """File input.
  16.387 -    
  16.388 -        >>> File(name='f').render()
  16.389 -        u'<input type="file" id="f" name="f"/>'
  16.390 -    """
  16.391 -    def get_type(self):
  16.392 -        return 'file'
  16.393 -    
  16.394 -class Validator:
  16.395 -    def __deepcopy__(self, memo): return copy.copy(self)
  16.396 -    def __init__(self, msg, test, jstest=None): utils.autoassign(self, locals())
  16.397 -    def valid(self, value): 
  16.398 -        try: return self.test(value)
  16.399 -        except: return False
  16.400 -
  16.401 -notnull = Validator("Required", bool)
  16.402 -
  16.403 -class regexp(Validator):
  16.404 -    def __init__(self, rexp, msg):
  16.405 -        self.rexp = re.compile(rexp)
  16.406 -        self.msg = msg
  16.407 -    
  16.408 -    def valid(self, value):
  16.409 -        return bool(self.rexp.match(value))
  16.410 -
  16.411 -if __name__ == "__main__":
  16.412 -    import doctest
  16.413 -    doctest.testmod()
    17.1 --- a/OpenSecurity/install/web.py-0.37/build/lib/web/http.py	Thu Feb 20 15:40:48 2014 +0100
    17.2 +++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
    17.3 @@ -1,150 +0,0 @@
    17.4 -"""
    17.5 -HTTP Utilities
    17.6 -(from web.py)
    17.7 -"""
    17.8 -
    17.9 -__all__ = [
   17.10 -  "expires", "lastmodified", 
   17.11 -  "prefixurl", "modified", 
   17.12 -  "changequery", "url",
   17.13 -  "profiler",
   17.14 -]
   17.15 -
   17.16 -import sys, os, threading, urllib, urlparse
   17.17 -try: import datetime
   17.18 -except ImportError: pass
   17.19 -import net, utils, webapi as web
   17.20 -
   17.21 -def prefixurl(base=''):
   17.22 -    """
   17.23 -    Sorry, this function is really difficult to explain.
   17.24 -    Maybe some other time.
   17.25 -    """
   17.26 -    url = web.ctx.path.lstrip('/')
   17.27 -    for i in xrange(url.count('/')): 
   17.28 -        base += '../'
   17.29 -    if not base: 
   17.30 -        base = './'
   17.31 -    return base
   17.32 -
   17.33 -def expires(delta):
   17.34 -    """
   17.35 -    Outputs an `Expires` header for `delta` from now. 
   17.36 -    `delta` is a `timedelta` object or a number of seconds.
   17.37 -    """
   17.38 -    if isinstance(delta, (int, long)):
   17.39 -        delta = datetime.timedelta(seconds=delta)
   17.40 -    date_obj = datetime.datetime.utcnow() + delta
   17.41 -    web.header('Expires', net.httpdate(date_obj))
   17.42 -
   17.43 -def lastmodified(date_obj):
   17.44 -    """Outputs a `Last-Modified` header for `datetime`."""
   17.45 -    web.header('Last-Modified', net.httpdate(date_obj))
   17.46 -
   17.47 -def modified(date=None, etag=None):
   17.48 -    """
   17.49 -    Checks to see if the page has been modified since the version in the
   17.50 -    requester's cache.
   17.51 -    
   17.52 -    When you publish pages, you can include `Last-Modified` and `ETag`
   17.53 -    with the date the page was last modified and an opaque token for
   17.54 -    the particular version, respectively. When readers reload the page, 
   17.55 -    the browser sends along the modification date and etag value for
   17.56 -    the version it has in its cache. If the page hasn't changed, 
   17.57 -    the server can just return `304 Not Modified` and not have to 
   17.58 -    send the whole page again.
   17.59 -    
   17.60 -    This function takes the last-modified date `date` and the ETag `etag`
   17.61 -    and checks the headers to see if they match. If they do, it returns 
   17.62 -    `True`, or otherwise it raises NotModified error. It also sets 
   17.63 -    `Last-Modified` and `ETag` output headers.
   17.64 -    """
   17.65 -    try:
   17.66 -        from __builtin__ import set
   17.67 -    except ImportError:
   17.68 -        # for python 2.3
   17.69 -        from sets import Set as set
   17.70 -
   17.71 -    n = set([x.strip('" ') for x in web.ctx.env.get('HTTP_IF_NONE_MATCH', '').split(',')])
   17.72 -    m = net.parsehttpdate(web.ctx.env.get('HTTP_IF_MODIFIED_SINCE', '').split(';')[0])
   17.73 -    validate = False
   17.74 -    if etag:
   17.75 -        if '*' in n or etag in n:
   17.76 -            validate = True
   17.77 -    if date and m:
   17.78 -        # we subtract a second because 
   17.79 -        # HTTP dates don't have sub-second precision
   17.80 -        if date-datetime.timedelta(seconds=1) <= m:
   17.81 -            validate = True
   17.82 -    
   17.83 -    if date: lastmodified(date)
   17.84 -    if etag: web.header('ETag', '"' + etag + '"')
   17.85 -    if validate:
   17.86 -        raise web.notmodified()
   17.87 -    else:
   17.88 -        return True
   17.89 -
   17.90 -def urlencode(query, doseq=0):
   17.91 -    """
   17.92 -    Same as urllib.urlencode, but supports unicode strings.
   17.93 -    
   17.94 -        >>> urlencode({'text':'foo bar'})
   17.95 -        'text=foo+bar'
   17.96 -        >>> urlencode({'x': [1, 2]}, doseq=True)
   17.97 -        'x=1&x=2'
   17.98 -    """
   17.99 -    def convert(value, doseq=False):
  17.100 -        if doseq and isinstance(value, list):
  17.101 -            return [convert(v) for v in value]
  17.102 -        else:
  17.103 -            return utils.safestr(value)
  17.104 -        
  17.105 -    query = dict([(k, convert(v, doseq)) for k, v in query.items()])
  17.106 -    return urllib.urlencode(query, doseq=doseq)
  17.107 -
  17.108 -def changequery(query=None, **kw):
  17.109 -    """
  17.110 -    Imagine you're at `/foo?a=1&b=2`. Then `changequery(a=3)` will return
  17.111 -    `/foo?a=3&b=2` -- the same URL but with the arguments you requested
  17.112 -    changed.
  17.113 -    """
  17.114 -    if query is None:
  17.115 -        query = web.rawinput(method='get')
  17.116 -    for k, v in kw.iteritems():
  17.117 -        if v is None:
  17.118 -            query.pop(k, None)
  17.119 -        else:
  17.120 -            query[k] = v
  17.121 -    out = web.ctx.path
  17.122 -    if query:
  17.123 -        out += '?' + urlencode(query, doseq=True)
  17.124 -    return out
  17.125 -
  17.126 -def url(path=None, doseq=False, **kw):
  17.127 -    """
  17.128 -    Makes url by concatenating web.ctx.homepath and path and the 
  17.129 -    query string created using the arguments.
  17.130 -    """
  17.131 -    if path is None:
  17.132 -        path = web.ctx.path
  17.133 -    if path.startswith("/"):
  17.134 -        out = web.ctx.homepath + path
  17.135 -    else:
  17.136 -        out = path
  17.137 -
  17.138 -    if kw:
  17.139 -        out += '?' + urlencode(kw, doseq=doseq)
  17.140 -    
  17.141 -    return out
  17.142 -
  17.143 -def profiler(app):
  17.144 -    """Outputs basic profiling information at the bottom of each response."""
  17.145 -    from utils import profile
  17.146 -    def profile_internal(e, o):
  17.147 -        out, result = profile(app)(e, o)
  17.148 -        return list(out) + ['<pre>' + net.websafe(result) + '</pre>']
  17.149 -    return profile_internal
  17.150 -
  17.151 -if __name__ == "__main__":
  17.152 -    import doctest
  17.153 -    doctest.testmod()
    18.1 --- a/OpenSecurity/install/web.py-0.37/build/lib/web/httpserver.py	Thu Feb 20 15:40:48 2014 +0100
    18.2 +++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
    18.3 @@ -1,319 +0,0 @@
    18.4 -__all__ = ["runsimple"]
    18.5 -
    18.6 -import sys, os
    18.7 -from SimpleHTTPServer import SimpleHTTPRequestHandler
    18.8 -import urllib
    18.9 -import posixpath
   18.10 -
   18.11 -import webapi as web
   18.12 -import net
   18.13 -import utils
   18.14 -
   18.15 -def runbasic(func, server_address=("0.0.0.0", 8080)):
   18.16 -    """
   18.17 -    Runs a simple HTTP server hosting WSGI app `func`. The directory `static/` 
   18.18 -    is hosted statically.
   18.19 -
   18.20 -    Based on [WsgiServer][ws] from [Colin Stewart][cs].
   18.21 -    
   18.22 -  [ws]: http://www.owlfish.com/software/wsgiutils/documentation/wsgi-server-api.html
   18.23 -  [cs]: http://www.owlfish.com/
   18.24 -    """
   18.25 -    # Copyright (c) 2004 Colin Stewart (http://www.owlfish.com/)
   18.26 -    # Modified somewhat for simplicity
   18.27 -    # Used under the modified BSD license:
   18.28 -    # http://www.xfree86.org/3.3.6/COPYRIGHT2.html#5
   18.29 -
   18.30 -    import SimpleHTTPServer, SocketServer, BaseHTTPServer, urlparse
   18.31 -    import socket, errno
   18.32 -    import traceback
   18.33 -
   18.34 -    class WSGIHandler(SimpleHTTPServer.SimpleHTTPRequestHandler):
   18.35 -        def run_wsgi_app(self):
   18.36 -            protocol, host, path, parameters, query, fragment = \
   18.37 -                urlparse.urlparse('http://dummyhost%s' % self.path)
   18.38 -
   18.39 -            # we only use path, query
   18.40 -            env = {'wsgi.version': (1, 0)
   18.41 -                   ,'wsgi.url_scheme': 'http'
   18.42 -                   ,'wsgi.input': self.rfile
   18.43 -                   ,'wsgi.errors': sys.stderr
   18.44 -                   ,'wsgi.multithread': 1
   18.45 -                   ,'wsgi.multiprocess': 0
   18.46 -                   ,'wsgi.run_once': 0
   18.47 -                   ,'REQUEST_METHOD': self.command
   18.48 -                   ,'REQUEST_URI': self.path
   18.49 -                   ,'PATH_INFO': path
   18.50 -                   ,'QUERY_STRING': query
   18.51 -                   ,'CONTENT_TYPE': self.headers.get('Content-Type', '')
   18.52 -                   ,'CONTENT_LENGTH': self.headers.get('Content-Length', '')
   18.53 -                   ,'REMOTE_ADDR': self.client_address[0]
   18.54 -                   ,'SERVER_NAME': self.server.server_address[0]
   18.55 -                   ,'SERVER_PORT': str(self.server.server_address[1])
   18.56 -                   ,'SERVER_PROTOCOL': self.request_version
   18.57 -                   }
   18.58 -
   18.59 -            for http_header, http_value in self.headers.items():
   18.60 -                env ['HTTP_%s' % http_header.replace('-', '_').upper()] = \
   18.61 -                    http_value
   18.62 -
   18.63 -            # Setup the state
   18.64 -            self.wsgi_sent_headers = 0
   18.65 -            self.wsgi_headers = []
   18.66 -
   18.67 -            try:
   18.68 -                # We have there environment, now invoke the application
   18.69 -                result = self.server.app(env, self.wsgi_start_response)
   18.70 -                try:
   18.71 -                    try:
   18.72 -                        for data in result:
   18.73 -                            if data: 
   18.74 -                                self.wsgi_write_data(data)
   18.75 -                    finally:
   18.76 -                        if hasattr(result, 'close'): 
   18.77 -                            result.close()
   18.78 -                except socket.error, socket_err:
   18.79 -                    # Catch common network errors and suppress them
   18.80 -                    if (socket_err.args[0] in \
   18.81 -                       (errno.ECONNABORTED, errno.EPIPE)): 
   18.82 -                        return
   18.83 -                except socket.timeout, socket_timeout: 
   18.84 -                    return
   18.85 -            except:
   18.86 -                print >> web.debug, traceback.format_exc(),
   18.87 -
   18.88 -            if (not self.wsgi_sent_headers):
   18.89 -                # We must write out something!
   18.90 -                self.wsgi_write_data(" ")
   18.91 -            return
   18.92 -
   18.93 -        do_POST = run_wsgi_app
   18.94 -        do_PUT = run_wsgi_app
   18.95 -        do_DELETE = run_wsgi_app
   18.96 -
   18.97 -        def do_GET(self):
   18.98 -            if self.path.startswith('/static/'):
   18.99 -                SimpleHTTPServer.SimpleHTTPRequestHandler.do_GET(self)
  18.100 -            else:
  18.101 -                self.run_wsgi_app()
  18.102 -
  18.103 -        def wsgi_start_response(self, response_status, response_headers, 
  18.104 -                              exc_info=None):
  18.105 -            if (self.wsgi_sent_headers):
  18.106 -                raise Exception \
  18.107 -                      ("Headers already sent and start_response called again!")
  18.108 -            # Should really take a copy to avoid changes in the application....
  18.109 -            self.wsgi_headers = (response_status, response_headers)
  18.110 -            return self.wsgi_write_data
  18.111 -
  18.112 -        def wsgi_write_data(self, data):
  18.113 -            if (not self.wsgi_sent_headers):
  18.114 -                status, headers = self.wsgi_headers
  18.115 -                # Need to send header prior to data
  18.116 -                status_code = status[:status.find(' ')]
  18.117 -                status_msg = status[status.find(' ') + 1:]
  18.118 -                self.send_response(int(status_code), status_msg)
  18.119 -                for header, value in headers:
  18.120 -                    self.send_header(header, value)
  18.121 -                self.end_headers()
  18.122 -                self.wsgi_sent_headers = 1
  18.123 -            # Send the data
  18.124 -            self.wfile.write(data)
  18.125 -
  18.126 -    class WSGIServer(SocketServer.ThreadingMixIn, BaseHTTPServer.HTTPServer):
  18.127 -        def __init__(self, func, server_address):
  18.128 -            BaseHTTPServer.HTTPServer.__init__(self, 
  18.129 -                                               server_address, 
  18.130 -                                               WSGIHandler)
  18.131 -            self.app = func
  18.132 -            self.serverShuttingDown = 0
  18.133 -
  18.134 -    print "http://%s:%d/" % server_address
  18.135 -    WSGIServer(func, server_address).serve_forever()
  18.136 -
  18.137 -# The WSGIServer instance. 
  18.138 -# Made global so that it can be stopped in embedded mode.
  18.139 -server = None
  18.140 -
  18.141 -def runsimple(func, server_address=("0.0.0.0", 8080)):
  18.142 -    """
  18.143 -    Runs [CherryPy][cp] WSGI server hosting WSGI app `func`. 
  18.144 -    The directory `static/` is hosted statically.
  18.145 -
  18.146 -    [cp]: http://www.cherrypy.org
  18.147 -    """
  18.148 -    global server
  18.149 -    func = StaticMiddleware(func)
  18.150 -    func = LogMiddleware(func)
  18.151 -    
  18.152 -    server = WSGIServer(server_address, func)
  18.153 -
  18.154 -    if server.ssl_adapter:
  18.155 -        print "https://%s:%d/" % server_address
  18.156 -    else:
  18.157 -        print "http://%s:%d/" % server_address
  18.158 -
  18.159 -    try:
  18.160 -        server.start()
  18.161 -    except (KeyboardInterrupt, SystemExit):
  18.162 -        server.stop()
  18.163 -        server = None
  18.164 -
  18.165 -def WSGIServer(server_address, wsgi_app):
  18.166 -    """Creates CherryPy WSGI server listening at `server_address` to serve `wsgi_app`.
  18.167 -    This function can be overwritten to customize the webserver or use a different webserver.
  18.168 -    """
  18.169 -    import wsgiserver
  18.170 -    
  18.171 -    # Default values of wsgiserver.ssl_adapters uses cherrypy.wsgiserver
  18.172 -    # prefix. Overwriting it make it work with web.wsgiserver.
  18.173 -    wsgiserver.ssl_adapters = {
  18.174 -        'builtin': 'web.wsgiserver.ssl_builtin.BuiltinSSLAdapter',
  18.175 -        'pyopenssl': 'web.wsgiserver.ssl_pyopenssl.pyOpenSSLAdapter',
  18.176 -    }
  18.177 -    
  18.178 -    server = wsgiserver.CherryPyWSGIServer(server_address, wsgi_app, server_name="localhost")
  18.179 -        
  18.180 -    def create_ssl_adapter(cert, key):
  18.181 -        # wsgiserver tries to import submodules as cherrypy.wsgiserver.foo.
  18.182 -        # That doesn't work as not it is web.wsgiserver. 
  18.183 -        # Patching sys.modules temporarily to make it work.
  18.184 -        import types
  18.185 -        cherrypy = types.ModuleType('cherrypy')
  18.186 -        cherrypy.wsgiserver = wsgiserver
  18.187 -        sys.modules['cherrypy'] = cherrypy
  18.188 -        sys.modules['cherrypy.wsgiserver'] = wsgiserver
  18.189 -        
  18.190 -        from wsgiserver.ssl_pyopenssl import pyOpenSSLAdapter
  18.191 -        adapter = pyOpenSSLAdapter(cert, key)
  18.192 -        
  18.193 -        # We are done with our work. Cleanup the patches.
  18.194 -        del sys.modules['cherrypy']
  18.195 -        del sys.modules['cherrypy.wsgiserver']
  18.196 -
  18.197 -        return adapter
  18.198 -
  18.199 -    # SSL backward compatibility
  18.200 -    if (server.ssl_adapter is None and
  18.201 -        getattr(server, 'ssl_certificate', None) and
  18.202 -        getattr(server, 'ssl_private_key', None)):
  18.203 -        server.ssl_adapter = create_ssl_adapter(server.ssl_certificate, server.ssl_private_key)
  18.204 -
  18.205 -    server.nodelay = not sys.platform.startswith('java') # TCP_NODELAY isn't supported on the JVM
  18.206 -    return server
  18.207 -
  18.208 -class StaticApp(SimpleHTTPRequestHandler):
  18.209 -    """WSGI application for serving static files."""
  18.210 -    def __init__(self, environ, start_response):
  18.211 -        self.headers = []
  18.212 -        self.environ = environ
  18.213 -        self.start_response = start_response
  18.214 -
  18.215 -    def send_response(self, status, msg=""):
  18.216 -        self.status = str(status) + " " + msg
  18.217 -
  18.218 -    def send_header(self, name, value):
  18.219 -        self.headers.append((name, value))
  18.220 -
  18.221 -    def end_headers(self):
  18.222 -        pass
  18.223 -
  18.224 -    def log_message(*a): pass
  18.225 -
  18.226 -    def __iter__(self):
  18.227 -        environ = self.environ
  18.228 -
  18.229 -        self.path = environ.get('PATH_INFO', '')
  18.230 -        self.client_address = environ.get('REMOTE_ADDR','-'), \
  18.231 -                              environ.get('REMOTE_PORT','-')
  18.232 -        self.command = environ.get('REQUEST_METHOD', '-')
  18.233 -
  18.234 -        from cStringIO import StringIO
  18.235 -        self.wfile = StringIO() # for capturing error
  18.236 -
  18.237 -        try:
  18.238 -            path = self.translate_path(self.path)
  18.239 -            etag = '"%s"' % os.path.getmtime(path)
  18.240 -            client_etag = environ.get('HTTP_IF_NONE_MATCH')
  18.241 -            self.send_header('ETag', etag)
  18.242 -            if etag == client_etag:
  18.243 -                self.send_response(304, "Not Modified")
  18.244 -                self.start_response(self.status, self.headers)
  18.245 -                raise StopIteration
  18.246 -        except OSError:
  18.247 -            pass # Probably a 404
  18.248 -
  18.249 -        f = self.send_head()
  18.250 -        self.start_response(self.status, self.headers)
  18.251 -
  18.252 -        if f:
  18.253 -            block_size = 16 * 1024
  18.254 -            while True:
  18.255 -                buf = f.read(block_size)
  18.256 -                if not buf:
  18.257 -                    break
  18.258 -                yield buf
  18.259 -            f.close()
  18.260 -        else:
  18.261 -            value = self.wfile.getvalue()
  18.262 -            yield value
  18.263 -
  18.264 -class StaticMiddleware:
  18.265 -    """WSGI middleware for serving static files."""
  18.266 -    def __init__(self, app, prefix='/static/'):
  18.267 -        self.app = app
  18.268 -        self.prefix = prefix
  18.269 -        
  18.270 -    def __call__(self, environ, start_response):
  18.271 -        path = environ.get('PATH_INFO', '')
  18.272 -        path = self.normpath(path)
  18.273 -
  18.274 -        if path.startswith(self.prefix):
  18.275 -            return StaticApp(environ, start_response)
  18.276 -        else:
  18.277 -            return self.app(environ, start_response)
  18.278 -
  18.279 -    def normpath(self, path):
  18.280 -        path2 = posixpath.normpath(urllib.unquote(path))
  18.281 -        if path.endswith("/"):
  18.282 -            path2 += "/"
  18.283 -        return path2
  18.284 -
  18.285 -    
  18.286 -class LogMiddleware:
  18.287 -    """WSGI middleware for logging the status."""
  18.288 -    def __init__(self, app):
  18.289 -        self.app = app
  18.290 -        self.format = '%s - - [%s] "%s %s %s" - %s'
  18.291 -    
  18.292 -        from BaseHTTPServer import BaseHTTPRequestHandler
  18.293 -        import StringIO
  18.294 -        f = StringIO.StringIO()
  18.295 -        
  18.296 -        class FakeSocket:
  18.297 -            def makefile(self, *a):
  18.298 -                return f
  18.299 -        
  18.300 -        # take log_date_time_string method from BaseHTTPRequestHandler
  18.301 -        self.log_date_time_string = BaseHTTPRequestHandler(FakeSocket(), None, None).log_date_time_string
  18.302 -        
  18.303 -    def __call__(self, environ, start_response):
  18.304 -        def xstart_response(status, response_headers, *args):
  18.305 -            out = start_response(status, response_headers, *args)
  18.306 -            self.log(status, environ)
  18.307 -            return out
  18.308 -
  18.309 -        return self.app(environ, xstart_response)
  18.310 -             
  18.311 -    def log(self, status, environ):
  18.312 -        outfile = environ.get('wsgi.errors', web.debug)
  18.313 -        req = environ.get('PATH_INFO', '_')
  18.314 -        protocol = environ.get('ACTUAL_SERVER_PROTOCOL', '-')
  18.315 -        method = environ.get('REQUEST_METHOD', '-')
  18.316 -        host = "%s:%s" % (environ.get('REMOTE_ADDR','-'), 
  18.317 -                          environ.get('REMOTE_PORT','-'))
  18.318 -
  18.319 -        time = self.log_date_time_string()
  18.320 -
  18.321 -        msg = self.format % (host, time, protocol, method, req, status)
  18.322 -        print >> outfile, utils.safestr(msg)
    19.1 --- a/OpenSecurity/install/web.py-0.37/build/lib/web/net.py	Thu Feb 20 15:40:48 2014 +0100
    19.2 +++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
    19.3 @@ -1,193 +0,0 @@
    19.4 -"""
    19.5 -Network Utilities
    19.6 -(from web.py)
    19.7 -"""
    19.8 -
    19.9 -__all__ = [
   19.10 -  "validipaddr", "validipport", "validip", "validaddr", 
   19.11 -  "urlquote",
   19.12 -  "httpdate", "parsehttpdate", 
   19.13 -  "htmlquote", "htmlunquote", "websafe",
   19.14 -]
   19.15 -
   19.16 -import urllib, time
   19.17 -try: import datetime
   19.18 -except ImportError: pass
   19.19 -
   19.20 -def validipaddr(address):
   19.21 -    """
   19.22 -    Returns True if `address` is a valid IPv4 address.
   19.23 -    
   19.24 -        >>> validipaddr('192.168.1.1')
   19.25 -        True
   19.26 -        >>> validipaddr('192.168.1.800')
   19.27 -        False
   19.28 -        >>> validipaddr('192.168.1')
   19.29 -        False
   19.30 -    """
   19.31 -    try:
   19.32 -        octets = address.split('.')
   19.33 -        if len(octets) != 4:
   19.34 -            return False
   19.35 -        for x in octets:
   19.36 -            if not (0 <= int(x) <= 255):
   19.37 -                return False
   19.38 -    except ValueError:
   19.39 -        return False
   19.40 -    return True
   19.41 -
   19.42 -def validipport(port):
   19.43 -    """
   19.44 -    Returns True if `port` is a valid IPv4 port.
   19.45 -    
   19.46 -        >>> validipport('9000')
   19.47 -        True
   19.48 -        >>> validipport('foo')
   19.49 -        False
   19.50 -        >>> validipport('1000000')
   19.51 -        False
   19.52 -    """
   19.53 -    try:
   19.54 -        if not (0 <= int(port) <= 65535):
   19.55 -            return False
   19.56 -    except ValueError:
   19.57 -        return False
   19.58 -    return True
   19.59 -
   19.60 -def validip(ip, defaultaddr="0.0.0.0", defaultport=8080):
   19.61 -    """Returns `(ip_address, port)` from string `ip_addr_port`"""
   19.62 -    addr = defaultaddr
   19.63 -    port = defaultport
   19.64 -    
   19.65 -    ip = ip.split(":", 1)
   19.66 -    if len(ip) == 1:
   19.67 -        if not ip[0]:
   19.68 -            pass
   19.69 -        elif validipaddr(ip[0]):
   19.70 -            addr = ip[0]
   19.71 -        elif validipport(ip[0]):
   19.72 -            port = int(ip[0])
   19.73 -        else:
   19.74 -            raise ValueError, ':'.join(ip) + ' is not a valid IP address/port'
   19.75 -    elif len(ip) == 2:
   19.76 -        addr, port = ip
   19.77 -        if not validipaddr(addr) and validipport(port):
   19.78 -            raise ValueError, ':'.join(ip) + ' is not a valid IP address/port'
   19.79 -        port = int(port)
   19.80 -    else:
   19.81 -        raise ValueError, ':'.join(ip) + ' is not a valid IP address/port'
   19.82 -    return (addr, port)
   19.83 -
   19.84 -def validaddr(string_):
   19.85 -    """
   19.86 -    Returns either (ip_address, port) or "/path/to/socket" from string_
   19.87 -    
   19.88 -        >>> validaddr('/path/to/socket')
   19.89 -        '/path/to/socket'
   19.90 -        >>> validaddr('8000')
   19.91 -        ('0.0.0.0', 8000)
   19.92 -        >>> validaddr('127.0.0.1')
   19.93 -        ('127.0.0.1', 8080)
   19.94 -        >>> validaddr('127.0.0.1:8000')
   19.95 -        ('127.0.0.1', 8000)
   19.96 -        >>> validaddr('fff')
   19.97 -        Traceback (most recent call last):
   19.98 -            ...
   19.99 -        ValueError: fff is not a valid IP address/port
  19.100 -    """
  19.101 -    if '/' in string_:
  19.102 -        return string_
  19.103 -    else:
  19.104 -        return validip(string_)
  19.105 -
  19.106 -def urlquote(val):
  19.107 -    """
  19.108 -    Quotes a string for use in a URL.
  19.109 -    
  19.110 -        >>> urlquote('://?f=1&j=1')
  19.111 -        '%3A//%3Ff%3D1%26j%3D1'
  19.112 -        >>> urlquote(None)
  19.113 -        ''
  19.114 -        >>> urlquote(u'\u203d')
  19.115 -        '%E2%80%BD'
  19.116 -    """
  19.117 -    if val is None: return ''
  19.118 -    if not isinstance(val, unicode): val = str(val)
  19.119 -    else: val = val.encode('utf-8')
  19.120 -    return urllib.quote(val)
  19.121 -
  19.122 -def httpdate(date_obj):
  19.123 -    """
  19.124 -    Formats a datetime object for use in HTTP headers.
  19.125 -    
  19.126 -        >>> import datetime
  19.127 -        >>> httpdate(datetime.datetime(1970, 1, 1, 1, 1, 1))
  19.128 -        'Thu, 01 Jan 1970 01:01:01 GMT'
  19.129 -    """
  19.130 -    return date_obj.strftime("%a, %d %b %Y %H:%M:%S GMT")
  19.131 -
  19.132 -def parsehttpdate(string_):
  19.133 -    """
  19.134 -    Parses an HTTP date into a datetime object.
  19.135 -
  19.136 -        >>> parsehttpdate('Thu, 01 Jan 1970 01:01:01 GMT')
  19.137 -        datetime.datetime(1970, 1, 1, 1, 1, 1)
  19.138 -    """
  19.139 -    try:
  19.140 -        t = time.strptime(string_, "%a, %d %b %Y %H:%M:%S %Z")
  19.141 -    except ValueError:
  19.142 -        return None
  19.143 -    return datetime.datetime(*t[:6])
  19.144 -
  19.145 -def htmlquote(text):
  19.146 -    r"""
  19.147 -    Encodes `text` for raw use in HTML.
  19.148 -    
  19.149 -        >>> htmlquote(u"<'&\">")
  19.150 -        u'&lt;&#39;&amp;&quot;&gt;'
  19.151 -    """
  19.152 -    text = text.replace(u"&", u"&amp;") # Must be done first!
  19.153 -    text = text.replace(u"<", u"&lt;")
  19.154 -    text = text.replace(u">", u"&gt;")
  19.155 -    text = text.replace(u"'", u"&#39;")
  19.156 -    text = text.replace(u'"', u"&quot;")
  19.157 -    return text
  19.158 -
  19.159 -def htmlunquote(text):
  19.160 -    r"""
  19.161 -    Decodes `text` that's HTML quoted.
  19.162 -
  19.163 -        >>> htmlunquote(u'&lt;&#39;&amp;&quot;&gt;')
  19.164 -        u'<\'&">'
  19.165 -    """
  19.166 -    text = text.replace(u"&quot;", u'"')
  19.167 -    text = text.replace(u"&#39;", u"'")
  19.168 -    text = text.replace(u"&gt;", u">")
  19.169 -    text = text.replace(u"&lt;", u"<")
  19.170 -    text = text.replace(u"&amp;", u"&") # Must be done last!
  19.171 -    return text
  19.172 -    
  19.173 -def websafe(val):
  19.174 -    r"""Converts `val` so that it is safe for use in Unicode HTML.
  19.175 -
  19.176 -        >>> websafe("<'&\">")
  19.177 -        u'&lt;&#39;&amp;&quot;&gt;'
  19.178 -        >>> websafe(None)
  19.179 -        u''
  19.180 -        >>> websafe(u'\u203d')
  19.181 -        u'\u203d'
  19.182 -        >>> websafe('\xe2\x80\xbd')
  19.183 -        u'\u203d'
  19.184 -    """
  19.185 -    if val is None:
  19.186 -        return u''
  19.187 -    elif isinstance(val, str):
  19.188 -        val = val.decode('utf-8')
  19.189 -    elif not isinstance(val, unicode):
  19.190 -        val = unicode(val)
  19.191 -        
  19.192 -    return htmlquote(val)
  19.193 -
  19.194 -if __name__ == "__main__":
  19.195 -    import doctest
  19.196 -    doctest.testmod()
    20.1 --- a/OpenSecurity/install/web.py-0.37/build/lib/web/python23.py	Thu Feb 20 15:40:48 2014 +0100
    20.2 +++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
    20.3 @@ -1,46 +0,0 @@
    20.4 -"""Python 2.3 compatabilty"""
    20.5 -import threading
    20.6 -
    20.7 -class threadlocal(object):
    20.8 -    """Implementation of threading.local for python2.3.
    20.9 -    """
   20.10 -    def __getattribute__(self, name):
   20.11 -        if name == "__dict__":
   20.12 -            return threadlocal._getd(self)
   20.13 -        else:
   20.14 -            try:
   20.15 -                return object.__getattribute__(self, name)
   20.16 -            except AttributeError:
   20.17 -                try:
   20.18 -                    return self.__dict__[name]
   20.19 -                except KeyError:
   20.20 -                    raise AttributeError, name
   20.21 -            
   20.22 -    def __setattr__(self, name, value):
   20.23 -        self.__dict__[name] = value
   20.24 -        
   20.25 -    def __delattr__(self, name):
   20.26 -        try:
   20.27 -            del self.__dict__[name]
   20.28 -        except KeyError:
   20.29 -            raise AttributeError, name
   20.30 -    
   20.31 -    def _getd(self):
   20.32 -        t = threading.currentThread()
   20.33 -        if not hasattr(t, '_d'):
   20.34 -            # using __dict__ of thread as thread local storage
   20.35 -            t._d = {}
   20.36 -        
   20.37 -        _id = id(self)
   20.38 -        # there could be multiple instances of threadlocal.
   20.39 -        # use id(self) as key
   20.40 -        if _id not in t._d:
   20.41 -            t._d[_id] = {}
   20.42 -        return t._d[_id]
   20.43 -        
   20.44 -if __name__ == '__main__':
   20.45 -     d = threadlocal()
   20.46 -     d.x = 1
   20.47 -     print d.__dict__
   20.48 -     print d.x
   20.49 -     
   20.50 \ No newline at end of file
    21.1 --- a/OpenSecurity/install/web.py-0.37/build/lib/web/session.py	Thu Feb 20 15:40:48 2014 +0100
    21.2 +++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
    21.3 @@ -1,358 +0,0 @@
    21.4 -"""
    21.5 -Session Management
    21.6 -(from web.py)
    21.7 -"""
    21.8 -
    21.9 -import os, time, datetime, random, base64
   21.10 -import os.path
   21.11 -from copy import deepcopy
   21.12 -try:
   21.13 -    import cPickle as pickle
   21.14 -except ImportError:
   21.15 -    import pickle
   21.16 -try:
   21.17 -    import hashlib
   21.18 -    sha1 = hashlib.sha1
   21.19 -except ImportError:
   21.20 -    import sha
   21.21 -    sha1 = sha.new
   21.22 -
   21.23 -import utils
   21.24 -import webapi as web
   21.25 -
   21.26 -__all__ = [
   21.27 -    'Session', 'SessionExpired',
   21.28 -    'Store', 'DiskStore', 'DBStore',
   21.29 -]
   21.30 -
   21.31 -web.config.session_parameters = utils.storage({
   21.32 -    'cookie_name': 'webpy_session_id',
   21.33 -    'cookie_domain': None,
   21.34 -    'cookie_path' : None,
   21.35 -    'timeout': 86400, #24 * 60 * 60, # 24 hours in seconds
   21.36 -    'ignore_expiry': True,
   21.37 -    'ignore_change_ip': True,
   21.38 -    'secret_key': 'fLjUfxqXtfNoIldA0A0J',
   21.39 -    'expired_message': 'Session expired',
   21.40 -    'httponly': True,
   21.41 -    'secure': False
   21.42 -})
   21.43 -
   21.44 -class SessionExpired(web.HTTPError): 
   21.45 -    def __init__(self, message):
   21.46 -        web.HTTPError.__init__(self, '200 OK', {}, data=message)
   21.47 -
   21.48 -class Session(object):
   21.49 -    """Session management for web.py
   21.50 -    """
   21.51 -    __slots__ = [
   21.52 -        "store", "_initializer", "_last_cleanup_time", "_config", "_data", 
   21.53 -        "__getitem__", "__setitem__", "__delitem__"
   21.54 -    ]
   21.55 -
   21.56 -    def __init__(self, app, store, initializer=None):
   21.57 -        self.store = store
   21.58 -        self._initializer = initializer
   21.59 -        self._last_cleanup_time = 0
   21.60 -        self._config = utils.storage(web.config.session_parameters)
   21.61 -        self._data = utils.threadeddict()
   21.62 -        
   21.63 -        self.__getitem__ = self._data.__getitem__
   21.64 -        self.__setitem__ = self._data.__setitem__
   21.65 -        self.__delitem__ = self._data.__delitem__
   21.66 -
   21.67 -        if app:
   21.68 -            app.add_processor(self._processor)
   21.69 -
   21.70 -    def __contains__(self, name):
   21.71 -        return name in self._data
   21.72 -
   21.73 -    def __getattr__(self, name):
   21.74 -        return getattr(self._data, name)
   21.75 -    
   21.76 -    def __setattr__(self, name, value):
   21.77 -        if name in self.__slots__:
   21.78 -            object.__setattr__(self, name, value)
   21.79 -        else:
   21.80 -            setattr(self._data, name, value)
   21.81 -        
   21.82 -    def __delattr__(self, name):
   21.83 -        delattr(self._data, name)
   21.84 -
   21.85 -    def _processor(self, handler):
   21.86 -        """Application processor to setup session for every request"""
   21.87 -        self._cleanup()
   21.88 -        self._load()
   21.89 -
   21.90 -        try:
   21.91 -            return handler()
   21.92 -        finally:
   21.93 -            self._save()
   21.94 -
   21.95 -    def _load(self):
   21.96 -        """Load the session from the store, by the id from cookie"""
   21.97 -        cookie_name = self._config.cookie_name
   21.98 -        cookie_domain = self._config.cookie_domain
   21.99 -        cookie_path = self._config.cookie_path
  21.100 -        httponly = self._config.httponly
  21.101 -        self.session_id = web.cookies().get(cookie_name)
  21.102 -
  21.103 -        # protection against session_id tampering
  21.104 -        if self.session_id and not self._valid_session_id(self.session_id):
  21.105 -            self.session_id = None
  21.106 -
  21.107 -        self._check_expiry()
  21.108 -        if self.session_id:
  21.109 -            d = self.store[self.session_id]
  21.110 -            self.update(d)
  21.111 -            self._validate_ip()
  21.112 -        
  21.113 -        if not self.session_id:
  21.114 -            self.session_id = self._generate_session_id()
  21.115 -
  21.116 -            if self._initializer:
  21.117 -                if isinstance(self._initializer, dict):
  21.118 -                    self.update(deepcopy(self._initializer))
  21.119 -                elif hasattr(self._initializer, '__call__'):
  21.120 -                    self._initializer()
  21.121 - 
  21.122 -        self.ip = web.ctx.ip
  21.123 -
  21.124 -    def _check_expiry(self):
  21.125 -        # check for expiry
  21.126 -        if self.session_id and self.session_id not in self.store:
  21.127 -            if self._config.ignore_expiry:
  21.128 -                self.session_id = None
  21.129 -            else:
  21.130 -                return self.expired()
  21.131 -
  21.132 -    def _validate_ip(self):
  21.133 -        # check for change of IP
  21.134 -        if self.session_id and self.get('ip', None) != web.ctx.ip:
  21.135 -            if not self._config.ignore_change_ip:
  21.136 -               return self.expired() 
  21.137 -    
  21.138 -    def _save(self):
  21.139 -        if not self.get('_killed'):
  21.140 -            self._setcookie(self.session_id)
  21.141 -            self.store[self.session_id] = dict(self._data)
  21.142 -        else:
  21.143 -            self._setcookie(self.session_id, expires=-1)
  21.144 -            
  21.145 -    def _setcookie(self, session_id, expires='', **kw):
  21.146 -        cookie_name = self._config.cookie_name
  21.147 -        cookie_domain = self._config.cookie_domain
  21.148 -        cookie_path = self._config.cookie_path
  21.149 -        httponly = self._config.httponly
  21.150 -        secure = self._config.secure
  21.151 -        web.setcookie(cookie_name, session_id, expires=expires, domain=cookie_domain, httponly=httponly, secure=secure, path=cookie_path)
  21.152 -    
  21.153 -    def _generate_session_id(self):
  21.154 -        """Generate a random id for session"""
  21.155 -
  21.156 -        while True:
  21.157 -            rand = os.urandom(16)
  21.158 -            now = time.time()
  21.159 -            secret_key = self._config.secret_key
  21.160 -            session_id = sha1("%s%s%s%s" %(rand, now, utils.safestr(web.ctx.ip), secret_key))
  21.161 -            session_id = session_id.hexdigest()
  21.162 -            if session_id not in self.store:
  21.163 -                break
  21.164 -        return session_id
  21.165 -
  21.166 -    def _valid_session_id(self, session_id):
  21.167 -        rx = utils.re_compile('^[0-9a-fA-F]+$')
  21.168 -        return rx.match(session_id)
  21.169 -        
  21.170 -    def _cleanup(self):
  21.171 -        """Cleanup the stored sessions"""
  21.172 -        current_time = time.time()
  21.173 -        timeout = self._config.timeout
  21.174 -        if current_time - self._last_cleanup_time > timeout:
  21.175 -            self.store.cleanup(timeout)
  21.176 -            self._last_cleanup_time = current_time
  21.177 -
  21.178 -    def expired(self):
  21.179 -        """Called when an expired session is atime"""
  21.180 -        self._killed = True
  21.181 -        self._save()
  21.182 -        raise SessionExpired(self._config.expired_message)
  21.183 - 
  21.184 -    def kill(self):
  21.185 -        """Kill the session, make it no longer available"""
  21.186 -        del self.store[self.session_id]
  21.187 -        self._killed = True
  21.188 -
  21.189 -class Store:
  21.190 -    """Base class for session stores"""
  21.191 -
  21.192 -    def __contains__(self, key):
  21.193 -        raise NotImplementedError
  21.194 -
  21.195 -    def __getitem__(self, key):
  21.196 -        raise NotImplementedError
  21.197 -
  21.198 -    def __setitem__(self, key, value):
  21.199 -        raise NotImplementedError
  21.200 -
  21.201 -    def cleanup(self, timeout):
  21.202 -        """removes all the expired sessions"""
  21.203 -        raise NotImplementedError
  21.204 -
  21.205 -    def encode(self, session_dict):
  21.206 -        """encodes session dict as a string"""
  21.207 -        pickled = pickle.dumps(session_dict)
  21.208 -        return base64.encodestring(pickled)
  21.209 -
  21.210 -    def decode(self, session_data):
  21.211 -        """decodes the data to get back the session dict """
  21.212 -        pickled = base64.decodestring(session_data)
  21.213 -        return pickle.loads(pickled)
  21.214 -
  21.215 -class DiskStore(Store):
  21.216 -    """
  21.217 -    Store for saving a session on disk.
  21.218 -
  21.219 -        >>> import tempfile
  21.220 -        >>> root = tempfile.mkdtemp()
  21.221 -        >>> s = DiskStore(root)
  21.222 -        >>> s['a'] = 'foo'
  21.223 -        >>> s['a']
  21.224 -        'foo'
  21.225 -        >>> time.sleep(0.01)
  21.226 -        >>> s.cleanup(0.01)
  21.227 -        >>> s['a']
  21.228 -        Traceback (most recent call last):
  21.229 -            ...
  21.230 -        KeyError: 'a'
  21.231 -    """
  21.232 -    def __init__(self, root):
  21.233 -        # if the storage root doesn't exists, create it.
  21.234 -        if not os.path.exists(root):
  21.235 -            os.makedirs(
  21.236 -                    os.path.abspath(root)
  21.237 -                    )
  21.238 -        self.root = root
  21.239 -
  21.240 -    def _get_path(self, key):
  21.241 -        if os.path.sep in key: 
  21.242 -            raise ValueError, "Bad key: %s" % repr(key)
  21.243 -        return os.path.join(self.root, key)
  21.244 -    
  21.245 -    def __contains__(self, key):
  21.246 -        path = self._get_path(key)
  21.247 -        return os.path.exists(path)
  21.248 -
  21.249 -    def __getitem__(self, key):
  21.250 -        path = self._get_path(key)
  21.251 -        if os.path.exists(path): 
  21.252 -            pickled = open(path).read()
  21.253 -            return self.decode(pickled)
  21.254 -        else:
  21.255 -            raise KeyError, key
  21.256 -
  21.257 -    def __setitem__(self, key, value):
  21.258 -        path = self._get_path(key)
  21.259 -        pickled = self.encode(value)    
  21.260 -        try:
  21.261 -            f = open(path, 'w')
  21.262 -            try:
  21.263 -                f.write(pickled)
  21.264 -            finally: 
  21.265 -                f.close()
  21.266 -        except IOError:
  21.267 -            pass
  21.268 -
  21.269 -    def __delitem__(self, key):
  21.270 -        path = self._get_path(key)
  21.271 -        if os.path.exists(path):
  21.272 -            os.remove(path)
  21.273 -    
  21.274 -    def cleanup(self, timeout):
  21.275 -        now = time.time()
  21.276 -        for f in os.listdir(self.root):
  21.277 -            path = self._get_path(f)
  21.278 -            atime = os.stat(path).st_atime
  21.279 -            if now - atime > timeout :
  21.280 -                os.remove(path)
  21.281 -
  21.282 -class DBStore(Store):
  21.283 -    """Store for saving a session in database
  21.284 -    Needs a table with the following columns:
  21.285 -
  21.286 -        session_id CHAR(128) UNIQUE NOT NULL,
  21.287 -        atime DATETIME NOT NULL default current_timestamp,
  21.288 -        data TEXT
  21.289 -    """
  21.290 -    def __init__(self, db, table_name):
  21.291 -        self.db = db
  21.292 -        self.table = table_name
  21.293 -    
  21.294 -    def __contains__(self, key):
  21.295 -        data = self.db.select(self.table, where="session_id=$key", vars=locals())
  21.296 -        return bool(list(data)) 
  21.297 -
  21.298 -    def __getitem__(self, key):
  21.299 -        now = datetime.datetime.now()
  21.300 -        try:
  21.301 -            s = self.db.select(self.table, where="session_id=$key", vars=locals())[0]
  21.302 -            self.db.update(self.table, where="session_id=$key", atime=now, vars=locals())
  21.303 -        except IndexError:
  21.304 -            raise KeyError
  21.305 -        else:
  21.306 -            return self.decode(s.data)
  21.307 -
  21.308 -    def __setitem__(self, key, value):
  21.309 -        pickled = self.encode(value)
  21.310 -        now = datetime.datetime.now()
  21.311 -        if key in self:
  21.312 -            self.db.update(self.table, where="session_id=$key", data=pickled, vars=locals())
  21.313 -        else:
  21.314 -            self.db.insert(self.table, False, session_id=key, data=pickled )
  21.315 -                
  21.316 -    def __delitem__(self, key):
  21.317 -        self.db.delete(self.table, where="session_id=$key", vars=locals())
  21.318 -
  21.319 -    def cleanup(self, timeout):
  21.320 -        timeout = datetime.timedelta(timeout/(24.0*60*60)) #timedelta takes numdays as arg
  21.321 -        last_allowed_time = datetime.datetime.now() - timeout
  21.322 -        self.db.delete(self.table, where="$last_allowed_time > atime", vars=locals())
  21.323 -
  21.324 -class ShelfStore:
  21.325 -    """Store for saving session using `shelve` module.
  21.326 -
  21.327 -        import shelve
  21.328 -        store = ShelfStore(shelve.open('session.shelf'))
  21.329 -
  21.330 -    XXX: is shelve thread-safe?
  21.331 -    """
  21.332 -    def __init__(self, shelf):
  21.333 -        self.shelf = shelf
  21.334 -
  21.335 -    def __contains__(self, key):
  21.336 -        return key in self.shelf
  21.337 -
  21.338 -    def __getitem__(self, key):
  21.339 -        atime, v = self.shelf[key]
  21.340 -        self[key] = v # update atime
  21.341 -        return v
  21.342 -
  21.343 -    def __setitem__(self, key, value):
  21.344 -        self.shelf[key] = time.time(), value
  21.345 -        
  21.346 -    def __delitem__(self, key):
  21.347 -        try:
  21.348 -            del self.shelf[key]
  21.349 -        except KeyError:
  21.350 -            pass
  21.351 -
  21.352 -    def cleanup(self, timeout):
  21.353 -        now = time.time()
  21.354 -        for k in self.shelf.keys():
  21.355 -            atime, v = self.shelf[k]
  21.356 -            if now - atime > timeout :
  21.357 -                del self[k]
  21.358 -
  21.359 -if __name__ == '__main__' :
  21.360 -    import doctest
  21.361 -    doctest.testmod()
    22.1 --- a/OpenSecurity/install/web.py-0.37/build/lib/web/template.py	Thu Feb 20 15:40:48 2014 +0100
    22.2 +++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
    22.3 @@ -1,1515 +0,0 @@
    22.4 -"""
    22.5 -simple, elegant templating
    22.6 -(part of web.py)
    22.7 -
    22.8 -Template design:
    22.9 -
   22.10 -Template string is split into tokens and the tokens are combined into nodes. 
   22.11 -Parse tree is a nodelist. TextNode and ExpressionNode are simple nodes and 
   22.12 -for-loop, if-loop etc are block nodes, which contain multiple child nodes. 
   22.13 -
   22.14 -Each node can emit some python string. python string emitted by the 
   22.15 -root node is validated for safeeval and executed using python in the given environment.
   22.16 -
   22.17 -Enough care is taken to make sure the generated code and the template has line to line match, 
   22.18 -so that the error messages can point to exact line number in template. (It doesn't work in some cases still.)
   22.19 -
   22.20 -Grammar:
   22.21 -
   22.22 -    template -> defwith sections 
   22.23 -    defwith -> '$def with (' arguments ')' | ''
   22.24 -    sections -> section*
   22.25 -    section -> block | assignment | line
   22.26 -
   22.27 -    assignment -> '$ ' <assignment expression>
   22.28 -    line -> (text|expr)*
   22.29 -    text -> <any characters other than $>
   22.30 -    expr -> '$' pyexpr | '$(' pyexpr ')' | '${' pyexpr '}'
   22.31 -    pyexpr -> <python expression>
   22.32 -"""
   22.33 -
   22.34 -__all__ = [
   22.35 -    "Template",
   22.36 -    "Render", "render", "frender",
   22.37 -    "ParseError", "SecurityError",
   22.38 -    "test"
   22.39 -]
   22.40 -
   22.41 -import tokenize
   22.42 -import os
   22.43 -import sys
   22.44 -import glob
   22.45 -import re
   22.46 -from UserDict import DictMixin
   22.47 -import warnings
   22.48 -
   22.49 -from utils import storage, safeunicode, safestr, re_compile
   22.50 -from webapi import config
   22.51 -from net import websafe
   22.52 -
   22.53 -def splitline(text):
   22.54 -    r"""
   22.55 -    Splits the given text at newline.
   22.56 -    
   22.57 -        >>> splitline('foo\nbar')
   22.58 -        ('foo\n', 'bar')
   22.59 -        >>> splitline('foo')
   22.60 -        ('foo', '')
   22.61 -        >>> splitline('')
   22.62 -        ('', '')
   22.63 -    """
   22.64 -    index = text.find('\n') + 1
   22.65 -    if index:
   22.66 -        return text[:index], text[index:]
   22.67 -    else:
   22.68 -        return text, ''
   22.69 -
   22.70 -class Parser:
   22.71 -    """Parser Base.
   22.72 -    """
   22.73 -    def __init__(self):
   22.74 -        self.statement_nodes = STATEMENT_NODES
   22.75 -        self.keywords = KEYWORDS
   22.76 -
   22.77 -    def parse(self, text, name="<template>"):
   22.78 -        self.text = text
   22.79 -        self.name = name
   22.80 -        
   22.81 -        defwith, text = self.read_defwith(text)
   22.82 -        suite = self.read_suite(text)
   22.83 -        return DefwithNode(defwith, suite)
   22.84 -
   22.85 -    def read_defwith(self, text):
   22.86 -        if text.startswith('$def with'):
   22.87 -            defwith, text = splitline(text)
   22.88 -            defwith = defwith[1:].strip() # strip $ and spaces
   22.89 -            return defwith, text
   22.90 -        else:
   22.91 -            return '', text
   22.92 -    
   22.93 -    def read_section(self, text):
   22.94 -        r"""Reads one section from the given text.
   22.95 -        
   22.96 -        section -> block | assignment | line
   22.97 -        
   22.98 -            >>> read_section = Parser().read_section
   22.99 -            >>> read_section('foo\nbar\n')
  22.100 -            (<line: [t'foo\n']>, 'bar\n')
  22.101 -            >>> read_section('$ a = b + 1\nfoo\n')
  22.102 -            (<assignment: 'a = b + 1'>, 'foo\n')
  22.103 -            
  22.104 -        read_section('$for in range(10):\n    hello $i\nfoo)
  22.105 -        """
  22.106 -        if text.lstrip(' ').startswith('$'):
  22.107 -            index = text.index('$')
  22.108 -            begin_indent, text2 = text[:index], text[index+1:]
  22.109 -            ahead = self.python_lookahead(text2)
  22.110 -            
  22.111 -            if ahead == 'var':
  22.112 -                return self.read_var(text2)
  22.113 -            elif ahead in self.statement_nodes:
  22.114 -                return self.read_block_section(text2, begin_indent)
  22.115 -            elif ahead in self.keywords:
  22.116 -                return self.read_keyword(text2)
  22.117 -            elif ahead.strip() == '':
  22.118 -                # assignments starts with a space after $
  22.119 -                # ex: $ a = b + 2
  22.120 -                return self.read_assignment(text2)
  22.121 -        return self.readline(text)
  22.122 -        
  22.123 -    def read_var(self, text):
  22.124 -        r"""Reads a var statement.
  22.125 -        
  22.126 -            >>> read_var = Parser().read_var
  22.127 -            >>> read_var('var x=10\nfoo')
  22.128 -            (<var: x = 10>, 'foo')
  22.129 -            >>> read_var('var x: hello $name\nfoo')
  22.130 -            (<var: x = join_(u'hello ', escape_(name, True))>, 'foo')
  22.131 -        """
  22.132 -        line, text = splitline(text)
  22.133 -        tokens = self.python_tokens(line)
  22.134 -        if len(tokens) < 4:
  22.135 -            raise SyntaxError('Invalid var statement')
  22.136 -            
  22.137 -        name = tokens[1]
  22.138 -        sep = tokens[2]
  22.139 -        value = line.split(sep, 1)[1].strip()
  22.140 -        
  22.141 -        if sep == '=':
  22.142 -            pass # no need to process value
  22.143 -        elif sep == ':': 
  22.144 -            #@@ Hack for backward-compatability
  22.145 -            if tokens[3] == '\n': # multi-line var statement
  22.146 -                block, text = self.read_indented_block(text, '    ')
  22.147 -                lines = [self.readline(x)[0] for x in block.splitlines()]
  22.148 -                nodes = []
  22.149 -                for x in lines:
  22.150 -                    nodes.extend(x.nodes)
  22.151 -                    nodes.append(TextNode('\n'))         
  22.152 -            else: # single-line var statement
  22.153 -                linenode, _ = self.readline(value)
  22.154 -                nodes = linenode.nodes                
  22.155 -            parts = [node.emit('') for node in nodes]
  22.156 -            value = "join_(%s)" % ", ".join(parts)
  22.157 -        else:
  22.158 -            raise SyntaxError('Invalid var statement')
  22.159 -        return VarNode(name, value), text
  22.160 -                    
  22.161 -    def read_suite(self, text):
  22.162 -        r"""Reads section by section till end of text.
  22.163 -        
  22.164 -            >>> read_suite = Parser().read_suite
  22.165 -            >>> read_suite('hello $name\nfoo\n')
  22.166 -            [<line: [t'hello ', $name, t'\n']>, <line: [t'foo\n']>]
  22.167 -        """
  22.168 -        sections = []
  22.169 -        while text:
  22.170 -            section, text = self.read_section(text)
  22.171 -            sections.append(section)
  22.172 -        return SuiteNode(sections)
  22.173 -    
  22.174 -    def readline(self, text):
  22.175 -        r"""Reads one line from the text. Newline is supressed if the line ends with \.
  22.176 -        
  22.177 -            >>> readline = Parser().readline
  22.178 -            >>> readline('hello $name!\nbye!')
  22.179 -            (<line: [t'hello ', $name, t'!\n']>, 'bye!')
  22.180 -            >>> readline('hello $name!\\\nbye!')
  22.181 -            (<line: [t'hello ', $name, t'!']>, 'bye!')
  22.182 -            >>> readline('$f()\n\n')
  22.183 -            (<line: [$f(), t'\n']>, '\n')
  22.184 -        """
  22.185 -        line, text = splitline(text)
  22.186 -
  22.187 -        # supress new line if line ends with \
  22.188 -        if line.endswith('\\\n'):
  22.189 -            line = line[:-2]
  22.190 -                
  22.191 -        nodes = []
  22.192 -        while line:
  22.193 -            node, line = self.read_node(line)
  22.194 -            nodes.append(node)
  22.195 -            
  22.196 -        return LineNode(nodes), text
  22.197 -
  22.198 -    def read_node(self, text):
  22.199 -        r"""Reads a node from the given text and returns the node and remaining text.
  22.200 -
  22.201 -            >>> read_node = Parser().read_node
  22.202 -            >>> read_node('hello $name')
  22.203 -            (t'hello ', '$name')
  22.204 -            >>> read_node('$name')
  22.205 -            ($name, '')
  22.206 -        """
  22.207 -        if text.startswith('$$'):
  22.208 -            return TextNode('$'), text[2:]
  22.209 -        elif text.startswith('$#'): # comment
  22.210 -            line, text = splitline(text)
  22.211 -            return TextNode('\n'), text
  22.212 -        elif text.startswith('$'):
  22.213 -            text = text[1:] # strip $
  22.214 -            if text.startswith(':'):
  22.215 -                escape = False
  22.216 -                text = text[1:] # strip :
  22.217 -            else:
  22.218 -                escape = True
  22.219 -            return self.read_expr(text, escape=escape)
  22.220 -        else:
  22.221 -            return self.read_text(text)
  22.222 -    
  22.223 -    def read_text(self, text):
  22.224 -        r"""Reads a text node from the given text.
  22.225 -        
  22.226 -            >>> read_text = Parser().read_text
  22.227 -            >>> read_text('hello $name')
  22.228 -            (t'hello ', '$name')
  22.229 -        """
  22.230 -        index = text.find('$')
  22.231 -        if index < 0:
  22.232 -            return TextNode(text), ''
  22.233 -        else:
  22.234 -            return TextNode(text[:index]), text[index:]
  22.235 -            
  22.236 -    def read_keyword(self, text):
  22.237 -        line, text = splitline(text)
  22.238 -        return StatementNode(line.strip() + "\n"), text
  22.239 -
  22.240 -    def read_expr(self, text, escape=True):
  22.241 -        """Reads a python expression from the text and returns the expression and remaining text.
  22.242 -
  22.243 -        expr -> simple_expr | paren_expr
  22.244 -        simple_expr -> id extended_expr
  22.245 -        extended_expr -> attr_access | paren_expr extended_expr | ''
  22.246 -        attr_access -> dot id extended_expr
  22.247 -        paren_expr -> [ tokens ] | ( tokens ) | { tokens }
  22.248 -     
  22.249 -            >>> read_expr = Parser().read_expr
  22.250 -            >>> read_expr("name")
  22.251 -            ($name, '')
  22.252 -            >>> read_expr("a.b and c")
  22.253 -            ($a.b, ' and c')
  22.254 -            >>> read_expr("a. b")
  22.255 -            ($a, '. b')
  22.256 -            >>> read_expr("name</h1>")
  22.257 -            ($name, '</h1>')
  22.258 -            >>> read_expr("(limit)ing")
  22.259 -            ($(limit), 'ing')
  22.260 -            >>> read_expr('a[1, 2][:3].f(1+2, "weird string[).", 3 + 4) done.')
  22.261 -            ($a[1, 2][:3].f(1+2, "weird string[).", 3 + 4), ' done.')
  22.262 -        """
  22.263 -        def simple_expr():
  22.264 -            identifier()
  22.265 -            extended_expr()
  22.266 -        
  22.267 -        def identifier():
  22.268 -            tokens.next()
  22.269 -        
  22.270 -        def extended_expr():
  22.271 -            lookahead = tokens.lookahead()
  22.272 -            if lookahead is None:
  22.273 -                return
  22.274 -            elif lookahead.value == '.':
  22.275 -                attr_access()
  22.276 -            elif lookahead.value in parens:
  22.277 -                paren_expr()
  22.278 -                extended_expr()
  22.279 -            else:
  22.280 -                return
  22.281 -        
  22.282 -        def attr_access():
  22.283 -            from token import NAME # python token constants
  22.284 -            dot = tokens.lookahead()
  22.285 -            if tokens.lookahead2().type == NAME:
  22.286 -                tokens.next() # consume dot
  22.287 -                identifier()
  22.288 -                extended_expr()
  22.289 -        
  22.290 -        def paren_expr():
  22.291 -            begin = tokens.next().value
  22.292 -            end = parens[begin]
  22.293 -            while True:
  22.294 -                if tokens.lookahead().value in parens:
  22.295 -                    paren_expr()
  22.296 -                else:
  22.297 -                    t = tokens.next()
  22.298 -                    if t.value == end:
  22.299 -                        break
  22.300 -            return
  22.301 -
  22.302 -        parens = {
  22.303 -            "(": ")",
  22.304 -            "[": "]",
  22.305 -            "{": "}"
  22.306 -        }
  22.307 -        
  22.308 -        def get_tokens(text):
  22.309 -            """tokenize text using python tokenizer.
  22.310 -            Python tokenizer ignores spaces, but they might be important in some cases. 
  22.311 -            This function introduces dummy space tokens when it identifies any ignored space.
  22.312 -            Each token is a storage object containing type, value, begin and end.
  22.313 -            """
  22.314 -            readline = iter([text]).next
  22.315 -            end = None
  22.316 -            for t in tokenize.generate_tokens(readline):
  22.317 -                t = storage(type=t[0], value=t[1], begin=t[2], end=t[3])
  22.318 -                if end is not None and end != t.begin:
  22.319 -                    _, x1 = end
  22.320 -                    _, x2 = t.begin
  22.321 -                    yield storage(type=-1, value=text[x1:x2], begin=end, end=t.begin)
  22.322 -                end = t.end
  22.323 -                yield t
  22.324 -                
  22.325 -        class BetterIter:
  22.326 -            """Iterator like object with 2 support for 2 look aheads."""
  22.327 -            def __init__(self, items):
  22.328 -                self.iteritems = iter(items)
  22.329 -                self.items = []
  22.330 -                self.position = 0
  22.331 -                self.current_item = None
  22.332 -            
  22.333 -            def lookahead(self):
  22.334 -                if len(self.items) <= self.position:
  22.335 -                    self.items.append(self._next())
  22.336 -                return self.items[self.position]
  22.337 -
  22.338 -            def _next(self):
  22.339 -                try:
  22.340 -                    return self.iteritems.next()
  22.341 -                except StopIteration:
  22.342 -                    return None
  22.343 -                
  22.344 -            def lookahead2(self):
  22.345 -                if len(self.items) <= self.position+1:
  22.346 -                    self.items.append(self._next())
  22.347 -                return self.items[self.position+1]
  22.348 -                    
  22.349 -            def next(self):
  22.350 -                self.current_item = self.lookahead()
  22.351 -                self.position += 1
  22.352 -                return self.current_item
  22.353 -
  22.354 -        tokens = BetterIter(get_tokens(text))
  22.355 -                
  22.356 -        if tokens.lookahead().value in parens:
  22.357 -            paren_expr()
  22.358 -        else:
  22.359 -            simple_expr()
  22.360 -        row, col = tokens.current_item.end
  22.361 -        return ExpressionNode(text[:col], escape=escape), text[col:]    
  22.362 -
  22.363 -    def read_assignment(self, text):
  22.364 -        r"""Reads assignment statement from text.
  22.365 -    
  22.366 -            >>> read_assignment = Parser().read_assignment
  22.367 -            >>> read_assignment('a = b + 1\nfoo')
  22.368 -            (<assignment: 'a = b + 1'>, 'foo')
  22.369 -        """
  22.370 -        line, text = splitline(text)
  22.371 -        return AssignmentNode(line.strip()), text
  22.372 -    
  22.373 -    def python_lookahead(self, text):
  22.374 -        """Returns the first python token from the given text.
  22.375 -        
  22.376 -            >>> python_lookahead = Parser().python_lookahead
  22.377 -            >>> python_lookahead('for i in range(10):')
  22.378 -            'for'
  22.379 -            >>> python_lookahead('else:')
  22.380 -            'else'
  22.381 -            >>> python_lookahead(' x = 1')
  22.382 -            ' '
  22.383 -        """
  22.384 -        readline = iter([text]).next
  22.385 -        tokens = tokenize.generate_tokens(readline)
  22.386 -        return tokens.next()[1]
  22.387 -        
  22.388 -    def python_tokens(self, text):
  22.389 -        readline = iter([text]).next
  22.390 -        tokens = tokenize.generate_tokens(readline)
  22.391 -        return [t[1] for t in tokens]
  22.392 -        
  22.393 -    def read_indented_block(self, text, indent):
  22.394 -        r"""Read a block of text. A block is what typically follows a for or it statement.
  22.395 -        It can be in the same line as that of the statement or an indented block.
  22.396 -
  22.397 -            >>> read_indented_block = Parser().read_indented_block
  22.398 -            >>> read_indented_block('  a\n  b\nc', '  ')
  22.399 -            ('a\nb\n', 'c')
  22.400 -            >>> read_indented_block('  a\n    b\n  c\nd', '  ')
  22.401 -            ('a\n  b\nc\n', 'd')
  22.402 -            >>> read_indented_block('  a\n\n    b\nc', '  ')
  22.403 -            ('a\n\n  b\n', 'c')
  22.404 -        """
  22.405 -        if indent == '':
  22.406 -            return '', text
  22.407 -            
  22.408 -        block = ""
  22.409 -        while text:
  22.410 -            line, text2 = splitline(text)
  22.411 -            if line.strip() == "":
  22.412 -                block += '\n'
  22.413 -            elif line.startswith(indent):
  22.414 -                block += line[len(indent):]
  22.415 -            else:
  22.416 -                break
  22.417 -            text = text2
  22.418 -        return block, text
  22.419 -
  22.420 -    def read_statement(self, text):
  22.421 -        r"""Reads a python statement.
  22.422 -        
  22.423 -            >>> read_statement = Parser().read_statement
  22.424 -            >>> read_statement('for i in range(10): hello $name')
  22.425 -            ('for i in range(10):', ' hello $name')
  22.426 -        """
  22.427 -        tok = PythonTokenizer(text)
  22.428 -        tok.consume_till(':')
  22.429 -        return text[:tok.index], text[tok.index:]
  22.430 -        
  22.431 -    def read_block_section(self, text, begin_indent=''):
  22.432 -        r"""
  22.433 -            >>> read_block_section = Parser().read_block_section
  22.434 -            >>> read_block_section('for i in range(10): hello $i\nfoo')
  22.435 -            (<block: 'for i in range(10):', [<line: [t'hello ', $i, t'\n']>]>, 'foo')
  22.436 -            >>> read_block_section('for i in range(10):\n        hello $i\n    foo', begin_indent='    ')
  22.437 -            (<block: 'for i in range(10):', [<line: [t'hello ', $i, t'\n']>]>, '    foo')
  22.438 -            >>> read_block_section('for i in range(10):\n  hello $i\nfoo')
  22.439 -            (<block: 'for i in range(10):', [<line: [t'hello ', $i, t'\n']>]>, 'foo')
  22.440 -        """
  22.441 -        line, text = splitline(text)
  22.442 -        stmt, line = self.read_statement(line)
  22.443 -        keyword = self.python_lookahead(stmt)
  22.444 -        
  22.445 -        # if there is some thing left in the line
  22.446 -        if line.strip():
  22.447 -            block = line.lstrip()
  22.448 -        else:
  22.449 -            def find_indent(text):
  22.450 -                rx = re_compile('  +')
  22.451 -                match = rx.match(text)    
  22.452 -                first_indent = match and match.group(0)
  22.453 -                return first_indent or ""
  22.454 -
  22.455 -            # find the indentation of the block by looking at the first line
  22.456 -            first_indent = find_indent(text)[len(begin_indent):]
  22.457 -
  22.458 -            #TODO: fix this special case
  22.459 -            if keyword == "code":
  22.460 -                indent = begin_indent + first_indent
  22.461 -            else:
  22.462 -                indent = begin_indent + min(first_indent, INDENT)
  22.463 -            
  22.464 -            block, text = self.read_indented_block(text, indent)
  22.465 -            
  22.466 -        return self.create_block_node(keyword, stmt, block, begin_indent), text
  22.467 -        
  22.468 -    def create_block_node(self, keyword, stmt, block, begin_indent):
  22.469 -        if keyword in self.statement_nodes:
  22.470 -            return self.statement_nodes[keyword](stmt, block, begin_indent)
  22.471 -        else:
  22.472 -            raise ParseError, 'Unknown statement: %s' % repr(keyword)
  22.473 -        
  22.474 -class PythonTokenizer:
  22.475 -    """Utility wrapper over python tokenizer."""
  22.476 -    def __init__(self, text):
  22.477 -        self.text = text
  22.478 -        readline = iter([text]).next
  22.479 -        self.tokens = tokenize.generate_tokens(readline)
  22.480 -        self.index = 0
  22.481 -        
  22.482 -    def consume_till(self, delim):        
  22.483 -        """Consumes tokens till colon.
  22.484 -        
  22.485 -            >>> tok = PythonTokenizer('for i in range(10): hello $i')
  22.486 -            >>> tok.consume_till(':')
  22.487 -            >>> tok.text[:tok.index]
  22.488 -            'for i in range(10):'
  22.489 -            >>> tok.text[tok.index:]
  22.490 -            ' hello $i'
  22.491 -        """
  22.492 -        try:
  22.493 -            while True:
  22.494 -                t = self.next()
  22.495 -                if t.value == delim:
  22.496 -                    break
  22.497 -                elif t.value == '(':
  22.498 -                    self.consume_till(')')
  22.499 -                elif t.value == '[':
  22.500 -                    self.consume_till(']')
  22.501 -                elif t.value == '{':
  22.502 -                    self.consume_till('}')
  22.503 -
  22.504 -                # if end of line is found, it is an exception.
  22.505 -                # Since there is no easy way to report the line number,
  22.506 -                # leave the error reporting to the python parser later  
  22.507 -                #@@ This should be fixed.
  22.508 -                if t.value == '\n':
  22.509 -                    break
  22.510 -        except:
  22.511 -            #raise ParseError, "Expected %s, found end of line." % repr(delim)
  22.512 -
  22.513 -            # raising ParseError doesn't show the line number. 
  22.514 -            # if this error is ignored, then it will be caught when compiling the python code.
  22.515 -            return
  22.516 -    
  22.517 -    def next(self):
  22.518 -        type, t, begin, end, line = self.tokens.next()
  22.519 -        row, col = end
  22.520 -        self.index = col
  22.521 -        return storage(type=type, value=t, begin=begin, end=end)
  22.522 -        
  22.523 -class DefwithNode:
  22.524 -    def __init__(self, defwith, suite):
  22.525 -        if defwith:
  22.526 -            self.defwith = defwith.replace('with', '__template__') + ':'
  22.527 -            # offset 4 lines. for encoding, __lineoffset__, loop and self.
  22.528 -            self.defwith += "\n    __lineoffset__ = -4"
  22.529 -        else:
  22.530 -            self.defwith = 'def __template__():'
  22.531 -            # offset 4 lines for encoding, __template__, __lineoffset__, loop and self.
  22.532 -            self.defwith += "\n    __lineoffset__ = -5"
  22.533 -
  22.534 -        self.defwith += "\n    loop = ForLoop()"
  22.535 -        self.defwith += "\n    self = TemplateResult(); extend_ = self.extend"
  22.536 -        self.suite = suite
  22.537 -        self.end = "\n    return self"
  22.538 -
  22.539 -    def emit(self, indent):
  22.540 -        encoding = "# coding: utf-8\n"
  22.541 -        return encoding + self.defwith + self.suite.emit(indent + INDENT) + self.end
  22.542 -
  22.543 -    def __repr__(self):
  22.544 -        return "<defwith: %s, %s>" % (self.defwith, self.suite)
  22.545 -
  22.546 -class TextNode:
  22.547 -    def __init__(self, value):
  22.548 -        self.value = value
  22.549 -
  22.550 -    def emit(self, indent, begin_indent=''):
  22.551 -        return repr(safeunicode(self.value))
  22.552 -        
  22.553 -    def __repr__(self):
  22.554 -        return 't' + repr(self.value)
  22.555 -
  22.556 -class ExpressionNode:
  22.557 -    def __init__(self, value, escape=True):
  22.558 -        self.value = value.strip()
  22.559 -        
  22.560 -        # convert ${...} to $(...)
  22.561 -        if value.startswith('{') and value.endswith('}'):
  22.562 -            self.value = '(' + self.value[1:-1] + ')'
  22.563 -            
  22.564 -        self.escape = escape
  22.565 -
  22.566 -    def emit(self, indent, begin_indent=''):
  22.567 -        return 'escape_(%s, %s)' % (self.value, bool(self.escape))
  22.568 -        
  22.569 -    def __repr__(self):
  22.570 -        if self.escape:
  22.571 -            escape = ''
  22.572 -        else:
  22.573 -            escape = ':'
  22.574 -        return "$%s%s" % (escape, self.value)
  22.575 -        
  22.576 -class AssignmentNode:
  22.577 -    def __init__(self, code):
  22.578 -        self.code = code
  22.579 -        
  22.580 -    def emit(self, indent, begin_indent=''):
  22.581 -        return indent + self.code + "\n"
  22.582 -        
  22.583 -    def __repr__(self):
  22.584 -        return "<assignment: %s>" % repr(self.code)
  22.585 -        
  22.586 -class LineNode:
  22.587 -    def __init__(self, nodes):
  22.588 -        self.nodes = nodes
  22.589 -        
  22.590 -    def emit(self, indent, text_indent='', name=''):
  22.591 -        text = [node.emit('') for node in self.nodes]
  22.592 -        if text_indent:
  22.593 -            text = [repr(text_indent)] + text
  22.594 -
  22.595 -        return indent + "extend_([%s])\n" % ", ".join(text)        
  22.596 -    
  22.597 -    def __repr__(self):
  22.598 -        return "<line: %s>" % repr(self.nodes)
  22.599 -
  22.600 -INDENT = '    ' # 4 spaces
  22.601 -        
  22.602 -class BlockNode:
  22.603 -    def __init__(self, stmt, block, begin_indent=''):
  22.604 -        self.stmt = stmt
  22.605 -        self.suite = Parser().read_suite(block)
  22.606 -        self.begin_indent = begin_indent
  22.607 -
  22.608 -    def emit(self, indent, text_indent=''):
  22.609 -        text_indent = self.begin_indent + text_indent
  22.610 -        out = indent + self.stmt + self.suite.emit(indent + INDENT, text_indent)
  22.611 -        return out
  22.612 -        
  22.613 -    def __repr__(self):
  22.614 -        return "<block: %s, %s>" % (repr(self.stmt), repr(self.suite))
  22.615 -
  22.616 -class ForNode(BlockNode):
  22.617 -    def __init__(self, stmt, block, begin_indent=''):
  22.618 -        self.original_stmt = stmt
  22.619 -        tok = PythonTokenizer(stmt)
  22.620 -        tok.consume_till('in')
  22.621 -        a = stmt[:tok.index] # for i in
  22.622 -        b = stmt[tok.index:-1] # rest of for stmt excluding :
  22.623 -        stmt = a + ' loop.setup(' + b.strip() + '):'
  22.624 -        BlockNode.__init__(self, stmt, block, begin_indent)
  22.625 -        
  22.626 -    def __repr__(self):
  22.627 -        return "<block: %s, %s>" % (repr(self.original_stmt), repr(self.suite))
  22.628 -
  22.629 -class CodeNode:
  22.630 -    def __init__(self, stmt, block, begin_indent=''):
  22.631 -        # compensate one line for $code:
  22.632 -        self.code = "\n" + block
  22.633 -        
  22.634 -    def emit(self, indent, text_indent=''):
  22.635 -        import re
  22.636 -        rx = re.compile('^', re.M)
  22.637 -        return rx.sub(indent, self.code).rstrip(' ')
  22.638 -        
  22.639 -    def __repr__(self):
  22.640 -        return "<code: %s>" % repr(self.code)
  22.641 -        
  22.642 -class StatementNode:
  22.643 -    def __init__(self, stmt):
  22.644 -        self.stmt = stmt
  22.645 -        
  22.646 -    def emit(self, indent, begin_indent=''):
  22.647 -        return indent + self.stmt
  22.648 -        
  22.649 -    def __repr__(self):
  22.650 -        return "<stmt: %s>" % repr(self.stmt)
  22.651 -        
  22.652 -class IfNode(BlockNode):
  22.653 -    pass
  22.654 -
  22.655 -class ElseNode(BlockNode):
  22.656 -    pass
  22.657 -
  22.658 -class ElifNode(BlockNode):
  22.659 -    pass
  22.660 -
  22.661 -class DefNode(BlockNode):
  22.662 -    def __init__(self, *a, **kw):
  22.663 -        BlockNode.__init__(self, *a, **kw)
  22.664 -
  22.665 -        code = CodeNode("", "")
  22.666 -        code.code = "self = TemplateResult(); extend_ = self.extend\n"
  22.667 -        self.suite.sections.insert(0, code)
  22.668 -
  22.669 -        code = CodeNode("", "")
  22.670 -        code.code = "return self\n"
  22.671 -        self.suite.sections.append(code)
  22.672 -        
  22.673 -    def emit(self, indent, text_indent=''):
  22.674 -        text_indent = self.begin_indent + text_indent
  22.675 -        out = indent + self.stmt + self.suite.emit(indent + INDENT, text_indent)
  22.676 -        return indent + "__lineoffset__ -= 3\n" + out
  22.677 -
  22.678 -class VarNode:
  22.679 -    def __init__(self, name, value):
  22.680 -        self.name = name
  22.681 -        self.value = value
  22.682 -        
  22.683 -    def emit(self, indent, text_indent):
  22.684 -        return indent + "self[%s] = %s\n" % (repr(self.name), self.value)
  22.685 -        
  22.686 -    def __repr__(self):
  22.687 -        return "<var: %s = %s>" % (self.name, self.value)
  22.688 -
  22.689 -class SuiteNode:
  22.690 -    """Suite is a list of sections."""
  22.691 -    def __init__(self, sections):
  22.692 -        self.sections = sections
  22.693 -        
  22.694 -    def emit(self, indent, text_indent=''):
  22.695 -        return "\n" + "".join([s.emit(indent, text_indent) for s in self.sections])
  22.696 -        
  22.697 -    def __repr__(self):
  22.698 -        return repr(self.sections)
  22.699 -
  22.700 -STATEMENT_NODES = {
  22.701 -    'for': ForNode,
  22.702 -    'while': BlockNode,
  22.703 -    'if': IfNode,
  22.704 -    'elif': ElifNode,
  22.705 -    'else': ElseNode,
  22.706 -    'def': DefNode,
  22.707 -    'code': CodeNode
  22.708 -}
  22.709 -
  22.710 -KEYWORDS = [
  22.711 -    "pass",
  22.712 -    "break",
  22.713 -    "continue",
  22.714 -    "return"
  22.715 -]
  22.716 -
  22.717 -TEMPLATE_BUILTIN_NAMES = [
  22.718 -    "dict", "enumerate", "float", "int", "bool", "list", "long", "reversed", 
  22.719 -    "set", "slice", "tuple", "xrange",
  22.720 -    "abs", "all", "any", "callable", "chr", "cmp", "divmod", "filter", "hex", 
  22.721 -    "id", "isinstance", "iter", "len", "max", "min", "oct", "ord", "pow", "range",
  22.722 -    "True", "False",
  22.723 -    "None",
  22.724 -    "__import__", # some c-libraries like datetime requires __import__ to present in the namespace
  22.725 -]
  22.726 -
  22.727 -import __builtin__
  22.728 -TEMPLATE_BUILTINS = dict([(name, getattr(__builtin__, name)) for name in TEMPLATE_BUILTIN_NAMES if name in __builtin__.__dict__])
  22.729 -
  22.730 -class ForLoop:
  22.731 -    """
  22.732 -    Wrapper for expression in for stament to support loop.xxx helpers.
  22.733 -    
  22.734 -        >>> loop = ForLoop()
  22.735 -        >>> for x in loop.setup(['a', 'b', 'c']):
  22.736 -        ...     print loop.index, loop.revindex, loop.parity, x
  22.737 -        ...
  22.738 -        1 3 odd a
  22.739 -        2 2 even b
  22.740 -        3 1 odd c
  22.741 -        >>> loop.index
  22.742 -        Traceback (most recent call last):
  22.743 -            ...
  22.744 -        AttributeError: index
  22.745 -    """
  22.746 -    def __init__(self):
  22.747 -        self._ctx = None
  22.748 -        
  22.749 -    def __getattr__(self, name):
  22.750 -        if self._ctx is None:
  22.751 -            raise AttributeError, name
  22.752 -        else:
  22.753 -            return getattr(self._ctx, name)
  22.754 -        
  22.755 -    def setup(self, seq):        
  22.756 -        self._push()
  22.757 -        return self._ctx.setup(seq)
  22.758 -        
  22.759 -    def _push(self):
  22.760 -        self._ctx = ForLoopContext(self, self._ctx)
  22.761 -        
  22.762 -    def _pop(self):
  22.763 -        self._ctx = self._ctx.parent
  22.764 -                
  22.765 -class ForLoopContext:
  22.766 -    """Stackable context for ForLoop to support nested for loops.
  22.767 -    """
  22.768 -    def __init__(self, forloop, parent):
  22.769 -        self._forloop = forloop
  22.770 -        self.parent = parent
  22.771 -        
  22.772 -    def setup(self, seq):
  22.773 -        try:
  22.774 -            self.length = len(seq)
  22.775 -        except:
  22.776 -            self.length = 0
  22.777 -
  22.778 -        self.index = 0
  22.779 -        for a in seq:
  22.780 -            self.index += 1
  22.781 -            yield a
  22.782 -        self._forloop._pop()
  22.783 -            
  22.784 -    index0 = property(lambda self: self.index-1)
  22.785 -    first = property(lambda self: self.index == 1)
  22.786 -    last = property(lambda self: self.index == self.length)
  22.787 -    odd = property(lambda self: self.index % 2 == 1)
  22.788 -    even = property(lambda self: self.index % 2 == 0)
  22.789 -    parity = property(lambda self: ['odd', 'even'][self.even])
  22.790 -    revindex0 = property(lambda self: self.length - self.index)
  22.791 -    revindex = property(lambda self: self.length - self.index + 1)
  22.792 -        
  22.793 -class BaseTemplate:
  22.794 -    def __init__(self, code, filename, filter, globals, builtins):
  22.795 -        self.filename = filename
  22.796 -        self.filter = filter
  22.797 -        self._globals = globals
  22.798 -        self._builtins = builtins
  22.799 -        if code:
  22.800 -            self.t = self._compile(code)
  22.801 -        else:
  22.802 -            self.t = lambda: ''
  22.803 -        
  22.804 -    def _compile(self, code):
  22.805 -        env = self.make_env(self._globals or {}, self._builtins)
  22.806 -        exec(code, env)
  22.807 -        return env['__template__']
  22.808 -
  22.809 -    def __call__(self, *a, **kw):
  22.810 -        __hidetraceback__ = True
  22.811 -        return self.t(*a, **kw)
  22.812 -
  22.813 -    def make_env(self, globals, builtins):
  22.814 -        return dict(globals,
  22.815 -            __builtins__=builtins, 
  22.816 -            ForLoop=ForLoop,
  22.817 -            TemplateResult=TemplateResult,
  22.818 -            escape_=self._escape,
  22.819 -            join_=self._join
  22.820 -        )
  22.821 -    def _join(self, *items):
  22.822 -        return u"".join(items)
  22.823 -            
  22.824 -    def _escape(self, value, escape=False):
  22.825 -        if value is None: 
  22.826 -            value = ''
  22.827 -            
  22.828 -        value = safeunicode(value)
  22.829 -        if escape and self.filter:
  22.830 -            value = self.filter(value)
  22.831 -        return value
  22.832 -
  22.833 -class Template(BaseTemplate):
  22.834 -    CONTENT_TYPES = {
  22.835 -        '.html' : 'text/html; charset=utf-8',
  22.836 -        '.xhtml' : 'application/xhtml+xml; charset=utf-8',
  22.837 -        '.txt' : 'text/plain',
  22.838 -    }
  22.839 -    FILTERS = {
  22.840 -        '.html': websafe,
  22.841 -        '.xhtml': websafe,
  22.842 -        '.xml': websafe
  22.843 -    }
  22.844 -    globals = {}
  22.845 -    
  22.846 -    def __init__(self, text, filename='<template>', filter=None, globals=None, builtins=None, extensions=None):
  22.847 -        self.extensions = extensions or []
  22.848 -        text = Template.normalize_text(text)
  22.849 -        code = self.compile_template(text, filename)
  22.850 -                
  22.851 -        _, ext = os.path.splitext(filename)
  22.852 -        filter = filter or self.FILTERS.get(ext, None)
  22.853 -        self.content_type = self.CONTENT_TYPES.get(ext, None)
  22.854 -
  22.855 -        if globals is None:
  22.856 -            globals = self.globals
  22.857 -        if builtins is None:
  22.858 -            builtins = TEMPLATE_BUILTINS
  22.859 -                
  22.860 -        BaseTemplate.__init__(self, code=code, filename=filename, filter=filter, globals=globals, builtins=builtins)
  22.861 -        
  22.862 -    def normalize_text(text):
  22.863 -        """Normalizes template text by correcting \r\n, tabs and BOM chars."""
  22.864 -        text = text.replace('\r\n', '\n').replace('\r', '\n').expandtabs()
  22.865 -        if not text.endswith('\n'):
  22.866 -            text += '\n'
  22.867 -
  22.868 -        # ignore BOM chars at the begining of template
  22.869 -        BOM = '\xef\xbb\xbf'
  22.870 -        if isinstance(text, str) and text.startswith(BOM):
  22.871 -            text = text[len(BOM):]
  22.872 -        
  22.873 -        # support fort \$ for backward-compatibility 
  22.874 -        text = text.replace(r'\$', '$$')
  22.875 -        return text
  22.876 -    normalize_text = staticmethod(normalize_text)
  22.877 -                
  22.878 -    def __call__(self, *a, **kw):
  22.879 -        __hidetraceback__ = True
  22.880 -        import webapi as web
  22.881 -        if 'headers' in web.ctx and self.content_type:
  22.882 -            web.header('Content-Type', self.content_type, unique=True)
  22.883 -            
  22.884 -        return BaseTemplate.__call__(self, *a, **kw)
  22.885 -        
  22.886 -    def generate_code(text, filename, parser=None):
  22.887 -        # parse the text
  22.888 -        parser = parser or Parser()
  22.889 -        rootnode = parser.parse(text, filename)
  22.890 -                
  22.891 -        # generate python code from the parse tree
  22.892 -        code = rootnode.emit(indent="").strip()
  22.893 -        return safestr(code)
  22.894 -        
  22.895 -    generate_code = staticmethod(generate_code)
  22.896 -    
  22.897 -    def create_parser(self):
  22.898 -        p = Parser()
  22.899 -        for ext in self.extensions:
  22.900 -            p = ext(p)
  22.901 -        return p
  22.902 -                
  22.903 -    def compile_template(self, template_string, filename):
  22.904 -        code = Template.generate_code(template_string, filename, parser=self.create_parser())
  22.905 -
  22.906 -        def get_source_line(filename, lineno):
  22.907 -            try:
  22.908 -                lines = open(filename).read().splitlines()
  22.909 -                return lines[lineno]
  22.910 -            except:
  22.911 -                return None
  22.912 -        
  22.913 -        try:
  22.914 -            # compile the code first to report the errors, if any, with the filename
  22.915 -            compiled_code = compile(code, filename, 'exec')
  22.916 -        except SyntaxError, e:
  22.917 -            # display template line that caused the error along with the traceback.
  22.918 -            try:
  22.919 -                e.msg += '\n\nTemplate traceback:\n    File %s, line %s\n        %s' % \
  22.920 -                    (repr(e.filename), e.lineno, get_source_line(e.filename, e.lineno-1))
  22.921 -            except: 
  22.922 -                pass
  22.923 -            raise
  22.924 -        
  22.925 -        # make sure code is safe - but not with jython, it doesn't have a working compiler module
  22.926 -        if not sys.platform.startswith('java'):
  22.927 -            try:
  22.928 -                import compiler
  22.929 -                ast = compiler.parse(code)
  22.930 -                SafeVisitor().walk(ast, filename)
  22.931 -            except ImportError:
  22.932 -                warnings.warn("Unabled to import compiler module. Unable to check templates for safety.")
  22.933 -        else:
  22.934 -            warnings.warn("SECURITY ISSUE: You are using Jython, which does not support checking templates for safety. Your templates can execute arbitrary code.")
  22.935 -
  22.936 -        return compiled_code
  22.937 -        
  22.938 -class CompiledTemplate(Template):
  22.939 -    def __init__(self, f, filename):
  22.940 -        Template.__init__(self, '', filename)
  22.941 -        self.t = f
  22.942 -        
  22.943 -    def compile_template(self, *a):
  22.944 -        return None
  22.945 -    
  22.946 -    def _compile(self, *a):
  22.947 -        return None
  22.948 -                
  22.949 -class Render:
  22.950 -    """The most preferred way of using templates.
  22.951 -    
  22.952 -        render = web.template.render('templates')
  22.953 -        print render.foo()
  22.954 -        
  22.955 -    Optional parameter can be `base` can be used to pass output of 
  22.956 -    every template through the base template.
  22.957 -    
  22.958 -        render = web.template.render('templates', base='layout')
  22.959 -    """
  22.960 -    def __init__(self, loc='templates', cache=None, base=None, **keywords):
  22.961 -        self._loc = loc
  22.962 -        self._keywords = keywords
  22.963 -
  22.964 -        if cache is None:
  22.965 -            cache = not config.get('debug', False)
  22.966 -        
  22.967 -        if cache:
  22.968 -            self._cache = {}
  22.969 -        else:
  22.970 -            self._cache = None
  22.971 -        
  22.972 -        if base and not hasattr(base, '__call__'):
  22.973 -            # make base a function, so that it can be passed to sub-renders
  22.974 -            self._base = lambda page: self._template(base)(page)
  22.975 -        else:
  22.976 -            self._base = base
  22.977 -    
  22.978 -    def _add_global(self, obj, name=None):
  22.979 -        """Add a global to this rendering instance."""
  22.980 -        if 'globals' not in self._keywords: self._keywords['globals'] = {}
  22.981 -        if not name:
  22.982 -            name = obj.__name__
  22.983 -        self._keywords['globals'][name] = obj
  22.984 -    
  22.985 -    def _lookup(self, name):
  22.986 -        path = os.path.join(self._loc, name)
  22.987 -        if os.path.isdir(path):
  22.988 -            return 'dir', path
  22.989 -        else:
  22.990 -            path = self._findfile(path)
  22.991 -            if path:
  22.992 -                return 'file', path
  22.993 -            else:
  22.994 -                return 'none', None
  22.995 -        
  22.996 -    def _load_template(self, name):
  22.997 -        kind, path = self._lookup(name)
  22.998 -        
  22.999 -        if kind == 'dir':
 22.1000 -            return Render(path, cache=self._cache is not None, base=self._base, **self._keywords)
 22.1001 -        elif kind == 'file':
 22.1002 -            return Template(open(path).read(), filename=path, **self._keywords)
 22.1003 -        else:
 22.1004 -            raise AttributeError, "No template named " + name            
 22.1005 -
 22.1006 -    def _findfile(self, path_prefix): 
 22.1007 -        p = [f for f in glob.glob(path_prefix + '.*') if not f.endswith('~')] # skip backup files
 22.1008 -        p.sort() # sort the matches for deterministic order
 22.1009 -        return p and p[0]
 22.1010 -            
 22.1011 -    def _template(self, name):
 22.1012 -        if self._cache is not None:
 22.1013 -            if name not in self._cache:
 22.1014 -                self._cache[name] = self._load_template(name)
 22.1015 -            return self._cache[name]
 22.1016 -        else:
 22.1017 -            return self._load_template(name)
 22.1018 -        
 22.1019 -    def __getattr__(self, name):
 22.1020 -        t = self._template(name)
 22.1021 -        if self._base and isinstance(t, Template):
 22.1022 -            def template(*a, **kw):
 22.1023 -                return self._base(t(*a, **kw))
 22.1024 -            return template
 22.1025 -        else:
 22.1026 -            return self._template(name)
 22.1027 -
 22.1028 -class GAE_Render(Render):
 22.1029 -    # Render gets over-written. make a copy here.
 22.1030 -    super = Render
 22.1031 -    def __init__(self, loc, *a, **kw):
 22.1032 -        GAE_Render.super.__init__(self, loc, *a, **kw)
 22.1033 -        
 22.1034 -        import types
 22.1035 -        if isinstance(loc, types.ModuleType):
 22.1036 -            self.mod = loc
 22.1037 -        else:
 22.1038 -            name = loc.rstrip('/').replace('/', '.')
 22.1039 -            self.mod = __import__(name, None, None, ['x'])
 22.1040 -
 22.1041 -        self.mod.__dict__.update(kw.get('builtins', TEMPLATE_BUILTINS))
 22.1042 -        self.mod.__dict__.update(Template.globals)
 22.1043 -        self.mod.__dict__.update(kw.get('globals', {}))
 22.1044 -
 22.1045 -    def _load_template(self, name):
 22.1046 -        t = getattr(self.mod, name)
 22.1047 -        import types
 22.1048 -        if isinstance(t, types.ModuleType):
 22.1049 -            return GAE_Render(t, cache=self._cache is not None, base=self._base, **self._keywords)
 22.1050 -        else:
 22.1051 -            return t
 22.1052 -
 22.1053 -render = Render
 22.1054 -# setup render for Google App Engine.
 22.1055 -try:
 22.1056 -    from google import appengine
 22.1057 -    render = Render = GAE_Render
 22.1058 -except ImportError:
 22.1059 -    pass
 22.1060 -        
 22.1061 -def frender(path, **keywords):
 22.1062 -    """Creates a template from the given file path.
 22.1063 -    """
 22.1064 -    return Template(open(path).read(), filename=path, **keywords)
 22.1065 -    
 22.1066 -def compile_templates(root):
 22.1067 -    """Compiles templates to python code."""
 22.1068 -    re_start = re_compile('^', re.M)
 22.1069 -    
 22.1070 -    for dirpath, dirnames, filenames in os.walk(root):
 22.1071 -        filenames = [f for f in filenames if not f.startswith('.') and not f.endswith('~') and not f.startswith('__init__.py')]
 22.1072 -
 22.1073 -        for d in dirnames[:]:
 22.1074 -            if d.startswith('.'):
 22.1075 -                dirnames.remove(d) # don't visit this dir
 22.1076 -
 22.1077 -        out = open(os.path.join(dirpath, '__init__.py'), 'w')
 22.1078 -        out.write('from web.template import CompiledTemplate, ForLoop, TemplateResult\n\n')
 22.1079 -        if dirnames:
 22.1080 -            out.write("import " + ", ".join(dirnames))
 22.1081 -        out.write("\n")
 22.1082 -
 22.1083 -        for f in filenames:
 22.1084 -            path = os.path.join(dirpath, f)
 22.1085 -
 22.1086 -            if '.' in f:
 22.1087 -                name, _ = f.split('.', 1)
 22.1088 -            else:
 22.1089 -                name = f
 22.1090 -                
 22.1091 -            text = open(path).read()
 22.1092 -            text = Template.normalize_text(text)
 22.1093 -            code = Template.generate_code(text, path)
 22.1094 -
 22.1095 -            code = code.replace("__template__", name, 1)
 22.1096 -            
 22.1097 -            out.write(code)
 22.1098 -
 22.1099 -            out.write('\n\n')
 22.1100 -            out.write('%s = CompiledTemplate(%s, %s)\n' % (name, name, repr(path)))
 22.1101 -            out.write("join_ = %s._join; escape_ = %s._escape\n\n" % (name, name))
 22.1102 -
 22.1103 -            # create template to make sure it compiles
 22.1104 -            t = Template(open(path).read(), path)
 22.1105 -        out.close()
 22.1106 -                
 22.1107 -class ParseError(Exception):
 22.1108 -    pass
 22.1109 -    
 22.1110 -class SecurityError(Exception):
 22.1111 -    """The template seems to be trying to do something naughty."""
 22.1112 -    pass
 22.1113 -
 22.1114 -# Enumerate all the allowed AST nodes
 22.1115 -ALLOWED_AST_NODES = [
 22.1116 -    "Add", "And",
 22.1117 -#   "AssAttr",
 22.1118 -    "AssList", "AssName", "AssTuple",
 22.1119 -#   "Assert",
 22.1120 -    "Assign", "AugAssign",
 22.1121 -#   "Backquote",
 22.1122 -    "Bitand", "Bitor", "Bitxor", "Break",
 22.1123 -    "CallFunc","Class", "Compare", "Const", "Continue",
 22.1124 -    "Decorators", "Dict", "Discard", "Div",
 22.1125 -    "Ellipsis", "EmptyNode",
 22.1126 -#   "Exec",
 22.1127 -    "Expression", "FloorDiv", "For",
 22.1128 -#   "From",
 22.1129 -    "Function", 
 22.1130 -    "GenExpr", "GenExprFor", "GenExprIf", "GenExprInner",
 22.1131 -    "Getattr", 
 22.1132 -#   "Global", 
 22.1133 -    "If", "IfExp",
 22.1134 -#   "Import",
 22.1135 -    "Invert", "Keyword", "Lambda", "LeftShift",
 22.1136 -    "List", "ListComp", "ListCompFor", "ListCompIf", "Mod",
 22.1137 -    "Module",
 22.1138 -    "Mul", "Name", "Not", "Or", "Pass", "Power",
 22.1139 -#   "Print", "Printnl", "Raise",
 22.1140 -    "Return", "RightShift", "Slice", "Sliceobj",
 22.1141 -    "Stmt", "Sub", "Subscript",
 22.1142 -#   "TryExcept", "TryFinally",
 22.1143 -    "Tuple", "UnaryAdd", "UnarySub",
 22.1144 -    "While", "With", "Yield",
 22.1145 -]
 22.1146 -
 22.1147 -class SafeVisitor(object):
 22.1148 -    """
 22.1149 -    Make sure code is safe by walking through the AST.
 22.1150 -    
 22.1151 -    Code considered unsafe if:
 22.1152 -        * it has restricted AST nodes
 22.1153 -        * it is trying to access resricted attributes   
 22.1154 -        
 22.1155 -    Adopted from http://www.zafar.se/bkz/uploads/safe.txt (public domain, Babar K. Zafar)
 22.1156 -    """
 22.1157 -    def __init__(self):
 22.1158 -        "Initialize visitor by generating callbacks for all AST node types."
 22.1159 -        self.errors = []
 22.1160 -
 22.1161 -    def walk(self, ast, filename):
 22.1162 -        "Validate each node in AST and raise SecurityError if the code is not safe."
 22.1163 -        self.filename = filename
 22.1164 -        self.visit(ast)
 22.1165 -        
 22.1166 -        if self.errors:        
 22.1167 -            raise SecurityError, '\n'.join([str(err) for err in self.errors])
 22.1168 -        
 22.1169 -    def visit(self, node, *args):
 22.1170 -        "Recursively validate node and all of its children."
 22.1171 -        def classname(obj):
 22.1172 -            return obj.__class__.__name__
 22.1173 -        nodename = classname(node)
 22.1174 -        fn = getattr(self, 'visit' + nodename, None)
 22.1175 -        
 22.1176 -        if fn:
 22.1177 -            fn(node, *args)
 22.1178 -        else:
 22.1179 -            if nodename not in ALLOWED_AST_NODES:
 22.1180 -                self.fail(node, *args)
 22.1181 -            
 22.1182 -        for child in node.getChildNodes():
 22.1183 -            self.visit(child, *args)
 22.1184 -
 22.1185 -    def visitName(self, node, *args):
 22.1186 -        "Disallow any attempts to access a restricted attr."
 22.1187 -        #self.assert_attr(node.getChildren()[0], node)
 22.1188 -        pass
 22.1189 -        
 22.1190 -    def visitGetattr(self, node, *args):
 22.1191 -        "Disallow any attempts to access a restricted attribute."
 22.1192 -        self.assert_attr(node.attrname, node)
 22.1193 -            
 22.1194 -    def assert_attr(self, attrname, node):
 22.1195 -        if self.is_unallowed_attr(attrname):
 22.1196 -            lineno = self.get_node_lineno(node)
 22.1197 -            e = SecurityError("%s:%d - access to attribute '%s' is denied" % (self.filename, lineno, attrname))
 22.1198 -            self.errors.append(e)
 22.1199 -
 22.1200 -    def is_unallowed_attr(self, name):
 22.1201 -        return name.startswith('_') \
 22.1202 -            or name.startswith('func_') \
 22.1203 -            or name.startswith('im_')
 22.1204 -            
 22.1205 -    def get_node_lineno(self, node):
 22.1206 -        return (node.lineno) and node.lineno or 0
 22.1207 -        
 22.1208 -    def fail(self, node, *args):
 22.1209 -        "Default callback for unallowed AST nodes."
 22.1210 -        lineno = self.get_node_lineno(node)
 22.1211 -        nodename = node.__class__.__name__
 22.1212 -        e = SecurityError("%s:%d - execution of '%s' statements is denied" % (self.filename, lineno, nodename))
 22.1213 -        self.errors.append(e)
 22.1214 -
 22.1215 -class TemplateResult(object, DictMixin):
 22.1216 -    """Dictionary like object for storing template output.
 22.1217 -    
 22.1218 -    The result of a template execution is usally a string, but sometimes it
 22.1219 -    contains attributes set using $var. This class provides a simple
 22.1220 -    dictionary like interface for storing the output of the template and the
 22.1221 -    attributes. The output is stored with a special key __body__. Convering
 22.1222 -    the the TemplateResult to string or unicode returns the value of __body__.
 22.1223 -    
 22.1224 -    When the template is in execution, the output is generated part by part
 22.1225 -    and those parts are combined at the end. Parts are added to the
 22.1226 -    TemplateResult by calling the `extend` method and the parts are combined
 22.1227 -    seemlessly when __body__ is accessed.
 22.1228 -    
 22.1229 -        >>> d = TemplateResult(__body__='hello, world', x='foo')
 22.1230 -        >>> d
 22.1231 -        <TemplateResult: {'__body__': 'hello, world', 'x': 'foo'}>
 22.1232 -        >>> print d
 22.1233 -        hello, world
 22.1234 -        >>> d.x
 22.1235 -        'foo'
 22.1236 -        >>> d = TemplateResult()
 22.1237 -        >>> d.extend([u'hello', u'world'])
 22.1238 -        >>> d
 22.1239 -        <TemplateResult: {'__body__': u'helloworld'}>
 22.1240 -    """
 22.1241 -    def __init__(self, *a, **kw):
 22.1242 -        self.__dict__["_d"] = dict(*a, **kw)
 22.1243 -        self._d.setdefault("__body__", u'')
 22.1244 -        
 22.1245 -        self.__dict__['_parts'] = []
 22.1246 -        self.__dict__["extend"] = self._parts.extend
 22.1247 -        
 22.1248 -        self._d.setdefault("__body__", None)
 22.1249 -    
 22.1250 -    def keys(self):
 22.1251 -        return self._d.keys()
 22.1252 -        
 22.1253 -    def _prepare_body(self):
 22.1254 -        """Prepare value of __body__ by joining parts.
 22.1255 -        """
 22.1256 -        if self._parts:
 22.1257 -            value = u"".join(self._parts)
 22.1258 -            self._parts[:] = []
 22.1259 -            body = self._d.get('__body__')
 22.1260 -            if body:
 22.1261 -                self._d['__body__'] = body + value
 22.1262 -            else:
 22.1263 -                self._d['__body__'] = value
 22.1264 -                
 22.1265 -    def __getitem__(self, name):
 22.1266 -        if name == "__body__":
 22.1267 -            self._prepare_body()
 22.1268 -        return self._d[name]
 22.1269 -        
 22.1270 -    def __setitem__(self, name, value):
 22.1271 -        if name == "__body__":
 22.1272 -            self._prepare_body()
 22.1273 -        return self._d.__setitem__(name, value)
 22.1274 -        
 22.1275 -    def __delitem__(self, name):
 22.1276 -        if name == "__body__":
 22.1277 -            self._prepare_body()
 22.1278 -        return self._d.__delitem__(name)
 22.1279 -
 22.1280 -    def __getattr__(self, key): 
 22.1281 -        try:
 22.1282 -            return self[key]
 22.1283 -        except KeyError, k:
 22.1284 -            raise AttributeError, k
 22.1285 -
 22.1286 -    def __setattr__(self, key, value): 
 22.1287 -        self[key] = value
 22.1288 -
 22.1289 -    def __delattr__(self, key):
 22.1290 -        try:
 22.1291 -            del self[key]
 22.1292 -        except KeyError, k:
 22.1293 -            raise AttributeError, k
 22.1294 -        
 22.1295 -    def __unicode__(self):
 22.1296 -        self._prepare_body()
 22.1297 -        return self["__body__"]
 22.1298 -    
 22.1299 -    def __str__(self):
 22.1300 -        self._prepare_body()
 22.1301 -        return self["__body__"].encode('utf-8')
 22.1302 -        
 22.1303 -    def __repr__(self):
 22.1304 -        self._prepare_body()
 22.1305 -        return "<TemplateResult: %s>" % self._d
 22.1306 -
 22.1307 -def test():
 22.1308 -    r"""Doctest for testing template module.
 22.1309 -
 22.1310 -    Define a utility function to run template test.
 22.1311 -    
 22.1312 -        >>> class TestResult:
 22.1313 -        ...     def __init__(self, t): self.t = t
 22.1314 -        ...     def __getattr__(self, name): return getattr(self.t, name)
 22.1315 -        ...     def __repr__(self): return repr(unicode(self))
 22.1316 -        ...
 22.1317 -        >>> def t(code, **keywords):
 22.1318 -        ...     tmpl = Template(code, **keywords)
 22.1319 -        ...     return lambda *a, **kw: TestResult(tmpl(*a, **kw))
 22.1320 -        ...
 22.1321 -    
 22.1322 -    Simple tests.
 22.1323 -    
 22.1324 -        >>> t('1')()
 22.1325 -        u'1\n'
 22.1326 -        >>> t('$def with ()\n1')()
 22.1327 -        u'1\n'
 22.1328 -        >>> t('$def with (a)\n$a')(1)
 22.1329 -        u'1\n'
 22.1330 -        >>> t('$def with (a=0)\n$a')(1)
 22.1331 -        u'1\n'
 22.1332 -        >>> t('$def with (a=0)\n$a')(a=1)
 22.1333 -        u'1\n'
 22.1334 -    
 22.1335 -    Test complicated expressions.
 22.1336 -        
 22.1337 -        >>> t('$def with (x)\n$x.upper()')('hello')
 22.1338 -        u'HELLO\n'
 22.1339 -        >>> t('$(2 * 3 + 4 * 5)')()
 22.1340 -        u'26\n'
 22.1341 -        >>> t('${2 * 3 + 4 * 5}')()
 22.1342 -        u'26\n'
 22.1343 -        >>> t('$def with (limit)\nkeep $(limit)ing.')('go')
 22.1344 -        u'keep going.\n'
 22.1345 -        >>> t('$def with (a)\n$a.b[0]')(storage(b=[1]))
 22.1346 -        u'1\n'
 22.1347 -        
 22.1348 -    Test html escaping.
 22.1349 -    
 22.1350 -        >>> t('$def with (x)\n$x', filename='a.html')('<html>')
 22.1351 -        u'&lt;html&gt;\n'
 22.1352 -        >>> t('$def with (x)\n$x', filename='a.txt')('<html>')
 22.1353 -        u'<html>\n'
 22.1354 -                
 22.1355 -    Test if, for and while.
 22.1356 -    
 22.1357 -        >>> t('$if 1: 1')()
 22.1358 -        u'1\n'
 22.1359 -        >>> t('$if 1:\n    1')()
 22.1360 -        u'1\n'
 22.1361 -        >>> t('$if 1:\n    1\\')()
 22.1362 -        u'1'
 22.1363 -        >>> t('$if 0: 0\n$elif 1: 1')()
 22.1364 -        u'1\n'
 22.1365 -        >>> t('$if 0: 0\n$elif None: 0\n$else: 1')()
 22.1366 -        u'1\n'
 22.1367 -        >>> t('$if 0 < 1 and 1 < 2: 1')()
 22.1368 -        u'1\n'
 22.1369 -        >>> t('$for x in [1, 2, 3]: $x')()
 22.1370 -        u'1\n2\n3\n'
 22.1371 -        >>> t('$def with (d)\n$for k, v in d.iteritems(): $k')({1: 1})
 22.1372 -        u'1\n'
 22.1373 -        >>> t('$for x in [1, 2, 3]:\n\t$x')()
 22.1374 -        u'    1\n    2\n    3\n'
 22.1375 -        >>> t('$def with (a)\n$while a and a.pop():1')([1, 2, 3])
 22.1376 -        u'1\n1\n1\n'
 22.1377 -
 22.1378 -    The space after : must be ignored.
 22.1379 -    
 22.1380 -        >>> t('$if True: foo')()
 22.1381 -        u'foo\n'
 22.1382 -    
 22.1383 -    Test loop.xxx.
 22.1384 -
 22.1385 -        >>> t("$for i in range(5):$loop.index, $loop.parity")()
 22.1386 -        u'1, odd\n2, even\n3, odd\n4, even\n5, odd\n'
 22.1387 -        >>> t("$for i in range(2):\n    $for j in range(2):$loop.parent.parity $loop.parity")()
 22.1388 -        u'odd odd\nodd even\neven odd\neven even\n'
 22.1389 -        
 22.1390 -    Test assignment.
 22.1391 -    
 22.1392 -        >>> t('$ a = 1\n$a')()
 22.1393 -        u'1\n'
 22.1394 -        >>> t('$ a = [1]\n$a[0]')()
 22.1395 -        u'1\n'
 22.1396 -        >>> t('$ a = {1: 1}\n$a.keys()[0]')()
 22.1397 -        u'1\n'
 22.1398 -        >>> t('$ a = []\n$if not a: 1')()
 22.1399 -        u'1\n'
 22.1400 -        >>> t('$ a = {}\n$if not a: 1')()
 22.1401 -        u'1\n'
 22.1402 -        >>> t('$ a = -1\n$a')()
 22.1403 -        u'-1\n'
 22.1404 -        >>> t('$ a = "1"\n$a')()
 22.1405 -        u'1\n'
 22.1406 -
 22.1407 -    Test comments.
 22.1408 -    
 22.1409 -        >>> t('$# 0')()
 22.1410 -        u'\n'
 22.1411 -        >>> t('hello$#comment1\nhello$#comment2')()
 22.1412 -        u'hello\nhello\n'
 22.1413 -        >>> t('$#comment0\nhello$#comment1\nhello$#comment2')()
 22.1414 -        u'\nhello\nhello\n'
 22.1415 -        
 22.1416 -    Test unicode.
 22.1417 -    
 22.1418 -        >>> t('$def with (a)\n$a')(u'\u203d')
 22.1419 -        u'\u203d\n'
 22.1420 -        >>> t('$def with (a)\n$a')(u'\u203d'.encode('utf-8'))
 22.1421 -        u'\u203d\n'
 22.1422 -        >>> t(u'$def with (a)\n$a $:a')(u'\u203d')
 22.1423 -        u'\u203d \u203d\n'
 22.1424 -        >>> t(u'$def with ()\nfoo')()
 22.1425 -        u'foo\n'
 22.1426 -        >>> def f(x): return x
 22.1427 -        ...
 22.1428 -        >>> t(u'$def with (f)\n$:f("x")')(f)
 22.1429 -        u'x\n'
 22.1430 -        >>> t('$def with (f)\n$:f("x")')(f)
 22.1431 -        u'x\n'
 22.1432 -    
 22.1433 -    Test dollar escaping.
 22.1434 -    
 22.1435 -        >>> t("Stop, $$money isn't evaluated.")()
 22.1436 -        u"Stop, $money isn't evaluated.\n"
 22.1437 -        >>> t("Stop, \$money isn't evaluated.")()
 22.1438 -        u"Stop, $money isn't evaluated.\n"
 22.1439 -        
 22.1440 -    Test space sensitivity.
 22.1441 -    
 22.1442 -        >>> t('$def with (x)\n$x')(1)
 22.1443 -        u'1\n'
 22.1444 -        >>> t('$def with(x ,y)\n$x')(1, 1)
 22.1445 -        u'1\n'
 22.1446 -        >>> t('$(1 + 2*3 + 4)')()
 22.1447 -        u'11\n'
 22.1448 -        
 22.1449 -    Make sure globals are working.
 22.1450 -            
 22.1451 -        >>> t('$x')()
 22.1452 -        Traceback (most recent call last):
 22.1453 -            ...
 22.1454 -        NameError: global name 'x' is not defined
 22.1455 -        >>> t('$x', globals={'x': 1})()
 22.1456 -        u'1\n'
 22.1457 -        
 22.1458 -    Can't change globals.
 22.1459 -    
 22.1460 -        >>> t('$ x = 2\n$x', globals={'x': 1})()
 22.1461 -        u'2\n'
 22.1462 -        >>> t('$ x = x + 1\n$x', globals={'x': 1})()
 22.1463 -        Traceback (most recent call last):
 22.1464 -            ...
 22.1465 -        UnboundLocalError: local variable 'x' referenced before assignment
 22.1466 -    
 22.1467 -    Make sure builtins are customizable.
 22.1468 -    
 22.1469 -        >>> t('$min(1, 2)')()
 22.1470 -        u'1\n'
 22.1471 -        >>> t('$min(1, 2)', builtins={})()
 22.1472 -        Traceback (most recent call last):
 22.1473 -            ...
 22.1474 -        NameError: global name 'min' is not defined
 22.1475 -        
 22.1476 -    Test vars.
 22.1477 -    
 22.1478 -        >>> x = t('$var x: 1')()
 22.1479 -        >>> x.x
 22.1480 -        u'1'
 22.1481 -        >>> x = t('$var x = 1')()
 22.1482 -        >>> x.x
 22.1483 -        1
 22.1484 -        >>> x = t('$var x:  \n    foo\n    bar')()
 22.1485 -        >>> x.x
 22.1486 -        u'foo\nbar\n'
 22.1487 -
 22.1488 -    Test BOM chars.
 22.1489 -
 22.1490 -        >>> t('\xef\xbb\xbf$def with(x)\n$x')('foo')
 22.1491 -        u'foo\n'
 22.1492 -
 22.1493 -    Test for with weird cases.
 22.1494 -
 22.1495 -        >>> t('$for i in range(10)[1:5]:\n    $i')()
 22.1496 -        u'1\n2\n3\n4\n'
 22.1497 -        >>> t("$for k, v in {'a': 1, 'b': 2}.items():\n    $k $v")()
 22.1498 -        u'a 1\nb 2\n'
 22.1499 -        >>> t("$for k, v in ({'a': 1, 'b': 2}.items():\n    $k $v")()
 22.1500 -        Traceback (most recent call last):
 22.1501 -            ...
 22.1502 -        SyntaxError: invalid syntax
 22.1503 -
 22.1504 -    Test datetime.
 22.1505 -
 22.1506 -        >>> import datetime
 22.1507 -        >>> t("$def with (date)\n$date.strftime('%m %Y')")(datetime.datetime(2009, 1, 1))
 22.1508 -        u'01 2009\n'
 22.1509 -    """
 22.1510 -    pass
 22.1511 -            
 22.1512 -if __name__ == "__main__":
 22.1513 -    import sys
 22.1514 -    if '--compile' in sys.argv:
 22.1515 -        compile_templates(sys.argv[2])
 22.1516 -    else:
 22.1517 -        import doctest
 22.1518 -        doctest.testmod()
    23.1 --- a/OpenSecurity/install/web.py-0.37/build/lib/web/test.py	Thu Feb 20 15:40:48 2014 +0100
    23.2 +++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
    23.3 @@ -1,51 +0,0 @@
    23.4 -"""test utilities
    23.5 -(part of web.py)
    23.6 -"""
    23.7 -import unittest
    23.8 -import sys, os
    23.9 -import web
   23.10 -
   23.11 -TestCase = unittest.TestCase
   23.12 -TestSuite = unittest.TestSuite
   23.13 -
   23.14 -def load_modules(names):
   23.15 -    return [__import__(name, None, None, "x") for name in names]
   23.16 -
   23.17 -def module_suite(module, classnames=None):
   23.18 -    """Makes a suite from a module."""
   23.19 -    if classnames:
   23.20 -        return unittest.TestLoader().loadTestsFromNames(classnames, module)
   23.21 -    elif hasattr(module, 'suite'):
   23.22 -        return module.suite()
   23.23 -    else:
   23.24 -        return unittest.TestLoader().loadTestsFromModule(module)
   23.25 -
   23.26 -def doctest_suite(module_names):
   23.27 -    """Makes a test suite from doctests."""
   23.28 -    import doctest
   23.29 -    suite = TestSuite()
   23.30 -    for mod in load_modules(module_names):
   23.31 -        suite.addTest(doctest.DocTestSuite(mod))
   23.32 -    return suite
   23.33 -    
   23.34 -def suite(module_names):
   23.35 -    """Creates a suite from multiple modules."""
   23.36 -    suite = TestSuite()
   23.37 -    for mod in load_modules(module_names):
   23.38 -        suite.addTest(module_suite(mod))
   23.39 -    return suite
   23.40 -
   23.41 -def runTests(suite):
   23.42 -    runner = unittest.TextTestRunner()
   23.43 -    return runner.run(suite)
   23.44 -
   23.45 -def main(suite=None):
   23.46 -    if not suite:
   23.47 -        main_module = __import__('__main__')
   23.48 -        # allow command line switches
   23.49 -        args = [a for a in sys.argv[1:] if not a.startswith('-')]
   23.50 -        suite = module_suite(main_module, args or None)
   23.51 -
   23.52 -    result = runTests(suite)
   23.53 -    sys.exit(not result.wasSuccessful())
   23.54 -
    24.1 --- a/OpenSecurity/install/web.py-0.37/build/lib/web/utils.py	Thu Feb 20 15:40:48 2014 +0100
    24.2 +++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
    24.3 @@ -1,1526 +0,0 @@
    24.4 -#!/usr/bin/env python
    24.5 -"""
    24.6 -General Utilities
    24.7 -(part of web.py)
    24.8 -"""
    24.9 -
   24.10 -__all__ = [
   24.11 -  "Storage", "storage", "storify", 
   24.12 -  "Counter", "counter",
   24.13 -  "iters", 
   24.14 -  "rstrips", "lstrips", "strips", 
   24.15 -  "safeunicode", "safestr", "utf8",
   24.16 -  "TimeoutError", "timelimit",
   24.17 -  "Memoize", "memoize",
   24.18 -  "re_compile", "re_subm",
   24.19 -  "group", "uniq", "iterview",
   24.20 -  "IterBetter", "iterbetter",
   24.21 -  "safeiter", "safewrite",
   24.22 -  "dictreverse", "dictfind", "dictfindall", "dictincr", "dictadd",
   24.23 -  "requeue", "restack",
   24.24 -  "listget", "intget", "datestr",
   24.25 -  "numify", "denumify", "commify", "dateify",
   24.26 -  "nthstr", "cond",
   24.27 -  "CaptureStdout", "capturestdout", "Profile", "profile",
   24.28 -  "tryall",
   24.29 -  "ThreadedDict", "threadeddict",
   24.30 -  "autoassign",
   24.31 -  "to36",
   24.32 -  "safemarkdown",
   24.33 -  "sendmail"
   24.34 -]
   24.35 -
   24.36 -import re, sys, time, threading, itertools, traceback, os
   24.37 -
   24.38 -try:
   24.39 -    import subprocess
   24.40 -except ImportError: 
   24.41 -    subprocess = None
   24.42 -
   24.43 -try: import datetime
   24.44 -except ImportError: pass
   24.45 -
   24.46 -try: set
   24.47 -except NameError:
   24.48 -    from sets import Set as set
   24.49 -    
   24.50 -try:
   24.51 -    from threading import local as threadlocal
   24.52 -except ImportError:
   24.53 -    from python23 import threadlocal
   24.54 -
   24.55 -class Storage(dict):
   24.56 -    """
   24.57 -    A Storage object is like a dictionary except `obj.foo` can be used
   24.58 -    in addition to `obj['foo']`.
   24.59 -    
   24.60 -        >>> o = storage(a=1)
   24.61 -        >>> o.a
   24.62 -        1
   24.63 -        >>> o['a']
   24.64 -        1
   24.65 -        >>> o.a = 2
   24.66 -        >>> o['a']
   24.67 -        2
   24.68 -        >>> del o.a
   24.69 -        >>> o.a
   24.70 -        Traceback (most recent call last):
   24.71 -            ...
   24.72 -        AttributeError: 'a'
   24.73 -    
   24.74 -    """
   24.75 -    def __getattr__(self, key): 
   24.76 -        try:
   24.77 -            return self[key]
   24.78 -        except KeyError, k:
   24.79 -            raise AttributeError, k
   24.80 -    
   24.81 -    def __setattr__(self, key, value): 
   24.82 -        self[key] = value
   24.83 -    
   24.84 -    def __delattr__(self, key):
   24.85 -        try:
   24.86 -            del self[key]
   24.87 -        except KeyError, k:
   24.88 -            raise AttributeError, k
   24.89 -    
   24.90 -    def __repr__(self):     
   24.91 -        return '<Storage ' + dict.__repr__(self) + '>'
   24.92 -
   24.93 -storage = Storage
   24.94 -
   24.95 -def storify(mapping, *requireds, **defaults):
   24.96 -    """
   24.97 -    Creates a `storage` object from dictionary `mapping`, raising `KeyError` if
   24.98 -    d doesn't have all of the keys in `requireds` and using the default 
   24.99 -    values for keys found in `defaults`.
  24.100 -
  24.101 -    For example, `storify({'a':1, 'c':3}, b=2, c=0)` will return the equivalent of
  24.102 -    `storage({'a':1, 'b':2, 'c':3})`.
  24.103 -    
  24.104 -    If a `storify` value is a list (e.g. multiple values in a form submission), 
  24.105 -    `storify` returns the last element of the list, unless the key appears in 
  24.106 -    `defaults` as a list. Thus:
  24.107 -    
  24.108 -        >>> storify({'a':[1, 2]}).a
  24.109 -        2
  24.110 -        >>> storify({'a':[1, 2]}, a=[]).a
  24.111 -        [1, 2]
  24.112 -        >>> storify({'a':1}, a=[]).a
  24.113 -        [1]
  24.114 -        >>> storify({}, a=[]).a
  24.115 -        []
  24.116 -    
  24.117 -    Similarly, if the value has a `value` attribute, `storify will return _its_
  24.118 -    value, unless the key appears in `defaults` as a dictionary.
  24.119 -    
  24.120 -        >>> storify({'a':storage(value=1)}).a
  24.121 -        1
  24.122 -        >>> storify({'a':storage(value=1)}, a={}).a
  24.123 -        <Storage {'value': 1}>
  24.124 -        >>> storify({}, a={}).a
  24.125 -        {}
  24.126 -        
  24.127 -    Optionally, keyword parameter `_unicode` can be passed to convert all values to unicode.
  24.128 -    
  24.129 -        >>> storify({'x': 'a'}, _unicode=True)
  24.130 -        <Storage {'x': u'a'}>
  24.131 -        >>> storify({'x': storage(value='a')}, x={}, _unicode=True)
  24.132 -        <Storage {'x': <Storage {'value': 'a'}>}>
  24.133 -        >>> storify({'x': storage(value='a')}, _unicode=True)
  24.134 -        <Storage {'x': u'a'}>
  24.135 -    """
  24.136 -    _unicode = defaults.pop('_unicode', False)
  24.137 -
  24.138 -    # if _unicode is callable object, use it convert a string to unicode.
  24.139 -    to_unicode = safeunicode
  24.140 -    if _unicode is not False and hasattr(_unicode, "__call__"):
  24.141 -        to_unicode = _unicode
  24.142 -    
  24.143 -    def unicodify(s):
  24.144 -        if _unicode and isinstance(s, str): return to_unicode(s)
  24.145 -        else: return s
  24.146 -        
  24.147 -    def getvalue(x):
  24.148 -        if hasattr(x, 'file') and hasattr(x, 'value'):
  24.149 -            return x.value
  24.150 -        elif hasattr(x, 'value'):
  24.151 -            return unicodify(x.value)
  24.152 -        else:
  24.153 -            return unicodify(x)
  24.154 -    
  24.155 -    stor = Storage()
  24.156 -    for key in requireds + tuple(mapping.keys()):
  24.157 -        value = mapping[key]
  24.158 -        if isinstance(value, list):
  24.159 -            if isinstance(defaults.get(key), list):
  24.160 -                value = [getvalue(x) for x in value]
  24.161 -            else:
  24.162 -                value = value[-1]
  24.163 -        if not isinstance(defaults.get(key), dict):
  24.164 -            value = getvalue(value)
  24.165 -        if isinstance(defaults.get(key), list) and not isinstance(value, list):
  24.166 -            value = [value]
  24.167 -        setattr(stor, key, value)
  24.168 -
  24.169 -    for (key, value) in defaults.iteritems():
  24.170 -        result = value
  24.171 -        if hasattr(stor, key): 
  24.172 -            result = stor[key]
  24.173 -        if value == () and not isinstance(result, tuple): 
  24.174 -            result = (result,)
  24.175 -        setattr(stor, key, result)
  24.176 -    
  24.177 -    return stor
  24.178 -
  24.179 -class Counter(storage):
  24.180 -    """Keeps count of how many times something is added.
  24.181 -        
  24.182 -        >>> c = counter()
  24.183 -        >>> c.add('x')
  24.184 -        >>> c.add('x')
  24.185 -        >>> c.add('x')
  24.186 -        >>> c.add('x')
  24.187 -        >>> c.add('x')
  24.188 -        >>> c.add('y')
  24.189 -        >>> c
  24.190 -        <Counter {'y': 1, 'x': 5}>
  24.191 -        >>> c.most()
  24.192 -        ['x']
  24.193 -    """
  24.194 -    def add(self, n):
  24.195 -        self.setdefault(n, 0)
  24.196 -        self[n] += 1
  24.197 -    
  24.198 -    def most(self):
  24.199 -        """Returns the keys with maximum count."""
  24.200 -        m = max(self.itervalues())
  24.201 -        return [k for k, v in self.iteritems() if v == m]
  24.202 -        
  24.203 -    def least(self):
  24.204 -        """Returns the keys with mininum count."""
  24.205 -        m = min(self.itervalues())
  24.206 -        return [k for k, v in self.iteritems() if v == m]
  24.207 -
  24.208 -    def percent(self, key):
  24.209 -       """Returns what percentage a certain key is of all entries.
  24.210 -
  24.211 -           >>> c = counter()
  24.212 -           >>> c.add('x')
  24.213 -           >>> c.add('x')
  24.214 -           >>> c.add('x')
  24.215 -           >>> c.add('y')
  24.216 -           >>> c.percent('x')
  24.217 -           0.75
  24.218 -           >>> c.percent('y')
  24.219 -           0.25
  24.220 -       """
  24.221 -       return float(self[key])/sum(self.values())
  24.222 -             
  24.223 -    def sorted_keys(self):
  24.224 -        """Returns keys sorted by value.
  24.225 -             
  24.226 -             >>> c = counter()
  24.227 -             >>> c.add('x')
  24.228 -             >>> c.add('x')
  24.229 -             >>> c.add('y')
  24.230 -             >>> c.sorted_keys()
  24.231 -             ['x', 'y']
  24.232 -        """
  24.233 -        return sorted(self.keys(), key=lambda k: self[k], reverse=True)
  24.234 -    
  24.235 -    def sorted_values(self):
  24.236 -        """Returns values sorted by value.
  24.237 -            
  24.238 -            >>> c = counter()
  24.239 -            >>> c.add('x')
  24.240 -            >>> c.add('x')
  24.241 -            >>> c.add('y')
  24.242 -            >>> c.sorted_values()
  24.243 -            [2, 1]
  24.244 -        """
  24.245 -        return [self[k] for k in self.sorted_keys()]
  24.246 -    
  24.247 -    def sorted_items(self):
  24.248 -        """Returns items sorted by value.
  24.249 -            
  24.250 -            >>> c = counter()
  24.251 -            >>> c.add('x')
  24.252 -            >>> c.add('x')
  24.253 -            >>> c.add('y')
  24.254 -            >>> c.sorted_items()
  24.255 -            [('x', 2), ('y', 1)]
  24.256 -        """
  24.257 -        return [(k, self[k]) for k in self.sorted_keys()]
  24.258 -    
  24.259 -    def __repr__(self):
  24.260 -        return '<Counter ' + dict.__repr__(self) + '>'
  24.261 -       
  24.262 -counter = Counter
  24.263 -
  24.264 -iters = [list, tuple]
  24.265 -import __builtin__
  24.266 -if hasattr(__builtin__, 'set'):
  24.267 -    iters.append(set)
  24.268 -if hasattr(__builtin__, 'frozenset'):
  24.269 -    iters.append(set)
  24.270 -if sys.version_info < (2,6): # sets module deprecated in 2.6
  24.271 -    try:
  24.272 -        from sets import Set
  24.273 -        iters.append(Set)
  24.274 -    except ImportError: 
  24.275 -        pass
  24.276 -    
  24.277 -class _hack(tuple): pass
  24.278 -iters = _hack(iters)
  24.279 -iters.__doc__ = """
  24.280 -A list of iterable items (like lists, but not strings). Includes whichever
  24.281 -of lists, tuples, sets, and Sets are available in this version of Python.
  24.282 -"""
  24.283 -
  24.284 -def _strips(direction, text, remove):
  24.285 -    if isinstance(remove, iters):
  24.286 -        for subr in remove:
  24.287 -            text = _strips(direction, text, subr)
  24.288 -        return text
  24.289 -    
  24.290 -    if direction == 'l': 
  24.291 -        if text.startswith(remove): 
  24.292 -            return text[len(remove):]
  24.293 -    elif direction == 'r':
  24.294 -        if text.endswith(remove):   
  24.295 -            return text[:-len(remove)]
  24.296 -    else: 
  24.297 -        raise ValueError, "Direction needs to be r or l."
  24.298 -    return text
  24.299 -
  24.300 -def rstrips(text, remove):
  24.301 -    """
  24.302 -    removes the string `remove` from the right of `text`
  24.303 -
  24.304 -        >>> rstrips("foobar", "bar")
  24.305 -        'foo'
  24.306 -    
  24.307 -    """
  24.308 -    return _strips('r', text, remove)
  24.309 -
  24.310 -def lstrips(text, remove):
  24.311 -    """
  24.312 -    removes the string `remove` from the left of `text`
  24.313 -    
  24.314 -        >>> lstrips("foobar", "foo")
  24.315 -        'bar'
  24.316 -        >>> lstrips('http://foo.org/', ['http://', 'https://'])
  24.317 -        'foo.org/'
  24.318 -        >>> lstrips('FOOBARBAZ', ['FOO', 'BAR'])
  24.319 -        'BAZ'
  24.320 -        >>> lstrips('FOOBARBAZ', ['BAR', 'FOO'])
  24.321 -        'BARBAZ'
  24.322 -    
  24.323 -    """
  24.324 -    return _strips('l', text, remove)
  24.325 -
  24.326 -def strips(text, remove):
  24.327 -    """
  24.328 -    removes the string `remove` from the both sides of `text`
  24.329 -
  24.330 -        >>> strips("foobarfoo", "foo")
  24.331 -        'bar'
  24.332 -    
  24.333 -    """
  24.334 -    return rstrips(lstrips(text, remove), remove)
  24.335 -
  24.336 -def safeunicode(obj, encoding='utf-8'):
  24.337 -    r"""
  24.338 -    Converts any given object to unicode string.
  24.339 -    
  24.340 -        >>> safeunicode('hello')
  24.341 -        u'hello'
  24.342 -        >>> safeunicode(2)
  24.343 -        u'2'
  24.344 -        >>> safeunicode('\xe1\x88\xb4')
  24.345 -        u'\u1234'
  24.346 -    """
  24.347 -    t = type(obj)
  24.348 -    if t is unicode:
  24.349 -        return obj
  24.350 -    elif t is str:
  24.351 -        return obj.decode(encoding)
  24.352 -    elif t in [int, float, bool]:
  24.353 -        return unicode(obj)
  24.354 -    elif hasattr(obj, '__unicode__') or isinstance(obj, unicode):
  24.355 -        return unicode(obj)
  24.356 -    else:
  24.357 -        return str(obj).decode(encoding)
  24.358 -    
  24.359 -def safestr(obj, encoding='utf-8'):
  24.360 -    r"""
  24.361 -    Converts any given object to utf-8 encoded string. 
  24.362 -    
  24.363 -        >>> safestr('hello')
  24.364 -        'hello'
  24.365 -        >>> safestr(u'\u1234')
  24.366 -        '\xe1\x88\xb4'
  24.367 -        >>> safestr(2)
  24.368 -        '2'
  24.369 -    """
  24.370 -    if isinstance(obj, unicode):
  24.371 -        return obj.encode(encoding)
  24.372 -    elif isinstance(obj, str):
  24.373 -        return obj
  24.374 -    elif hasattr(obj, 'next'): # iterator
  24.375 -        return itertools.imap(safestr, obj)
  24.376 -    else:
  24.377 -        return str(obj)
  24.378 -
  24.379 -# for backward-compatibility
  24.380 -utf8 = safestr
  24.381 -    
  24.382 -class TimeoutError(Exception): pass
  24.383 -def timelimit(timeout):
  24.384 -    """
  24.385 -    A decorator to limit a function to `timeout` seconds, raising `TimeoutError`
  24.386 -    if it takes longer.
  24.387 -    
  24.388 -        >>> import time
  24.389 -        >>> def meaningoflife():
  24.390 -        ...     time.sleep(.2)
  24.391 -        ...     return 42
  24.392 -        >>> 
  24.393 -        >>> timelimit(.1)(meaningoflife)()
  24.394 -        Traceback (most recent call last):
  24.395 -            ...
  24.396 -        TimeoutError: took too long
  24.397 -        >>> timelimit(1)(meaningoflife)()
  24.398 -        42
  24.399 -
  24.400 -    _Caveat:_ The function isn't stopped after `timeout` seconds but continues 
  24.401 -    executing in a separate thread. (There seems to be no way to kill a thread.)
  24.402 -
  24.403 -    inspired by <http://aspn.activestate.com/ASPN/Cookbook/Python/Recipe/473878>
  24.404 -    """
  24.405 -    def _1(function):
  24.406 -        def _2(*args, **kw):
  24.407 -            class Dispatch(threading.Thread):
  24.408 -                def __init__(self):
  24.409 -                    threading.Thread.__init__(self)
  24.410 -                    self.result = None
  24.411 -                    self.error = None
  24.412 -
  24.413 -                    self.setDaemon(True)
  24.414 -                    self.start()
  24.415 -
  24.416 -                def run(self):
  24.417 -                    try:
  24.418 -                        self.result = function(*args, **kw)
  24.419 -                    except:
  24.420 -                        self.error = sys.exc_info()
  24.421 -
  24.422 -            c = Dispatch()
  24.423 -            c.join(timeout)
  24.424 -            if c.isAlive():
  24.425 -                raise TimeoutError, 'took too long'
  24.426 -            if c.error:
  24.427 -                raise c.error[0], c.error[1]
  24.428 -            return c.result
  24.429 -        return _2
  24.430 -    return _1
  24.431 -
  24.432 -class Memoize:
  24.433 -    """
  24.434 -    'Memoizes' a function, caching its return values for each input.
  24.435 -    If `expires` is specified, values are recalculated after `expires` seconds.
  24.436 -    If `background` is specified, values are recalculated in a separate thread.
  24.437 -    
  24.438 -        >>> calls = 0
  24.439 -        >>> def howmanytimeshaveibeencalled():
  24.440 -        ...     global calls
  24.441 -        ...     calls += 1
  24.442 -        ...     return calls
  24.443 -        >>> fastcalls = memoize(howmanytimeshaveibeencalled)
  24.444 -        >>> howmanytimeshaveibeencalled()
  24.445 -        1
  24.446 -        >>> howmanytimeshaveibeencalled()
  24.447 -        2
  24.448 -        >>> fastcalls()
  24.449 -        3
  24.450 -        >>> fastcalls()
  24.451 -        3
  24.452 -        >>> import time
  24.453 -        >>> fastcalls = memoize(howmanytimeshaveibeencalled, .1, background=False)
  24.454 -        >>> fastcalls()
  24.455 -        4
  24.456 -        >>> fastcalls()
  24.457 -        4
  24.458 -        >>> time.sleep(.2)
  24.459 -        >>> fastcalls()
  24.460 -        5
  24.461 -        >>> def slowfunc():
  24.462 -        ...     time.sleep(.1)
  24.463 -        ...     return howmanytimeshaveibeencalled()
  24.464 -        >>> fastcalls = memoize(slowfunc, .2, background=True)
  24.465 -        >>> fastcalls()
  24.466 -        6
  24.467 -        >>> timelimit(.05)(fastcalls)()
  24.468 -        6
  24.469 -        >>> time.sleep(.2)
  24.470 -        >>> timelimit(.05)(fastcalls)()
  24.471 -        6
  24.472 -        >>> timelimit(.05)(fastcalls)()
  24.473 -        6
  24.474 -        >>> time.sleep(.2)
  24.475 -        >>> timelimit(.05)(fastcalls)()
  24.476 -        7
  24.477 -        >>> fastcalls = memoize(slowfunc, None, background=True)
  24.478 -        >>> threading.Thread(target=fastcalls).start()
  24.479 -        >>> time.sleep(.01)
  24.480 -        >>> fastcalls()
  24.481 -        9
  24.482 -    """
  24.483 -    def __init__(self, func, expires=None, background=True): 
  24.484 -        self.func = func
  24.485 -        self.cache = {}
  24.486 -        self.expires = expires
  24.487 -        self.background = background
  24.488 -        self.running = {}
  24.489 -    
  24.490 -    def __call__(self, *args, **keywords):
  24.491 -        key = (args, tuple(keywords.items()))
  24.492 -        if not self.running.get(key):
  24.493 -            self.running[key] = threading.Lock()
  24.494 -        def update(block=False):
  24.495 -            if self.running[key].acquire(block):
  24.496 -                try:
  24.497 -                    self.cache[key] = (self.func(*args, **keywords), time.time())
  24.498 -                finally:
  24.499 -                    self.running[key].release()
  24.500 -        
  24.501 -        if key not in self.cache: 
  24.502 -            update(block=True)
  24.503 -        elif self.expires and (time.time() - self.cache[key][1]) > self.expires:
  24.504 -            if self.background:
  24.505 -                threading.Thread(target=update).start()
  24.506 -            else:
  24.507 -                update()
  24.508 -        return self.cache[key][0]
  24.509 -
  24.510 -memoize = Memoize
  24.511 -
  24.512 -re_compile = memoize(re.compile) #@@ threadsafe?
  24.513 -re_compile.__doc__ = """
  24.514 -A memoized version of re.compile.
  24.515 -"""
  24.516 -
  24.517 -class _re_subm_proxy:
  24.518 -    def __init__(self): 
  24.519 -        self.match = None
  24.520 -    def __call__(self, match): 
  24.521 -        self.match = match
  24.522 -        return ''
  24.523 -
  24.524 -def re_subm(pat, repl, string):
  24.525 -    """
  24.526 -    Like re.sub, but returns the replacement _and_ the match object.
  24.527 -    
  24.528 -        >>> t, m = re_subm('g(oo+)fball', r'f\\1lish', 'goooooofball')
  24.529 -        >>> t
  24.530 -        'foooooolish'
  24.531 -        >>> m.groups()
  24.532 -        ('oooooo',)
  24.533 -    """
  24.534 -    compiled_pat = re_compile(pat)
  24.535 -    proxy = _re_subm_proxy()
  24.536 -    compiled_pat.sub(proxy.__call__, string)
  24.537 -    return compiled_pat.sub(repl, string), proxy.match
  24.538 -
  24.539 -def group(seq, size): 
  24.540 -    """
  24.541 -    Returns an iterator over a series of lists of length size from iterable.
  24.542 -
  24.543 -        >>> list(group([1,2,3,4], 2))
  24.544 -        [[1, 2], [3, 4]]
  24.545 -        >>> list(group([1,2,3,4,5], 2))
  24.546 -        [[1, 2], [3, 4], [5]]
  24.547 -    """
  24.548 -    def take(seq, n):
  24.549 -        for i in xrange(n):
  24.550 -            yield seq.next()
  24.551 -
  24.552 -    if not hasattr(seq, 'next'):  
  24.553 -        seq = iter(seq)
  24.554 -    while True: 
  24.555 -        x = list(take(seq, size))
  24.556 -        if x:
  24.557 -            yield x
  24.558 -        else:
  24.559 -            break
  24.560 -
  24.561 -def uniq(seq, key=None):
  24.562 -    """
  24.563 -    Removes duplicate elements from a list while preserving the order of the rest.
  24.564 -
  24.565 -        >>> uniq([9,0,2,1,0])
  24.566 -        [9, 0, 2, 1]
  24.567 -
  24.568 -    The value of the optional `key` parameter should be a function that
  24.569 -    takes a single argument and returns a key to test the uniqueness.
  24.570 -
  24.571 -        >>> uniq(["Foo", "foo", "bar"], key=lambda s: s.lower())
  24.572 -        ['Foo', 'bar']
  24.573 -    """
  24.574 -    key = key or (lambda x: x)
  24.575 -    seen = set()
  24.576 -    result = []
  24.577 -    for v in seq:
  24.578 -        k = key(v)
  24.579 -        if k in seen:
  24.580 -            continue
  24.581 -        seen.add(k)
  24.582 -        result.append(v)
  24.583 -    return result
  24.584 -
  24.585 -def iterview(x):
  24.586 -   """
  24.587 -   Takes an iterable `x` and returns an iterator over it
  24.588 -   which prints its progress to stderr as it iterates through.
  24.589 -   """
  24.590 -   WIDTH = 70
  24.591 -
  24.592 -   def plainformat(n, lenx):
  24.593 -       return '%5.1f%% (%*d/%d)' % ((float(n)/lenx)*100, len(str(lenx)), n, lenx)
  24.594 -
  24.595 -   def bars(size, n, lenx):
  24.596 -       val = int((float(n)*size)/lenx + 0.5)
  24.597 -       if size - val:
  24.598 -           spacing = ">" + (" "*(size-val))[1:]
  24.599 -       else:
  24.600 -           spacing = ""
  24.601 -       return "[%s%s]" % ("="*val, spacing)
  24.602 -
  24.603 -   def eta(elapsed, n, lenx):
  24.604 -       if n == 0:
  24.605 -           return '--:--:--'
  24.606 -       if n == lenx:
  24.607 -           secs = int(elapsed)
  24.608 -       else:
  24.609 -           secs = int((elapsed/n) * (lenx-n))
  24.610 -       mins, secs = divmod(secs, 60)
  24.611 -       hrs, mins = divmod(mins, 60)
  24.612 -
  24.613 -       return '%02d:%02d:%02d' % (hrs, mins, secs)
  24.614 -
  24.615 -   def format(starttime, n, lenx):
  24.616 -       out = plainformat(n, lenx) + ' '
  24.617 -       if n == lenx:
  24.618 -           end = '     '
  24.619 -       else:
  24.620 -           end = ' ETA '
  24.621 -       end += eta(time.time() - starttime, n, lenx)
  24.622 -       out += bars(WIDTH - len(out) - len(end), n, lenx)
  24.623 -       out += end
  24.624 -       return out
  24.625 -
  24.626 -   starttime = time.time()
  24.627 -   lenx = len(x)
  24.628 -   for n, y in enumerate(x):
  24.629 -       sys.stderr.write('\r' + format(starttime, n, lenx))
  24.630 -       yield y
  24.631 -   sys.stderr.write('\r' + format(starttime, n+1, lenx) + '\n')
  24.632 -
  24.633 -class IterBetter:
  24.634 -    """
  24.635 -    Returns an object that can be used as an iterator 
  24.636 -    but can also be used via __getitem__ (although it 
  24.637 -    cannot go backwards -- that is, you cannot request 
  24.638 -    `iterbetter[0]` after requesting `iterbetter[1]`).
  24.639 -    
  24.640 -        >>> import itertools
  24.641 -        >>> c = iterbetter(itertools.count())
  24.642 -        >>> c[1]
  24.643 -        1
  24.644 -        >>> c[5]
  24.645 -        5
  24.646 -        >>> c[3]
  24.647 -        Traceback (most recent call last):
  24.648 -            ...
  24.649 -        IndexError: already passed 3
  24.650 -
  24.651 -    For boolean test, IterBetter peeps at first value in the itertor without effecting the iteration.
  24.652 -
  24.653 -        >>> c = iterbetter(iter(range(5)))
  24.654 -        >>> bool(c)
  24.655 -        True
  24.656 -        >>> list(c)
  24.657 -        [0, 1, 2, 3, 4]
  24.658 -        >>> c = iterbetter(iter([]))
  24.659 -        >>> bool(c)
  24.660 -        False
  24.661 -        >>> list(c)
  24.662 -        []
  24.663 -    """
  24.664 -    def __init__(self, iterator): 
  24.665 -        self.i, self.c = iterator, 0
  24.666 -
  24.667 -    def __iter__(self): 
  24.668 -        if hasattr(self, "_head"):
  24.669 -            yield self._head
  24.670 -
  24.671 -        while 1:    
  24.672 -            yield self.i.next()
  24.673 -            self.c += 1
  24.674 -
  24.675 -    def __getitem__(self, i):
  24.676 -        #todo: slices
  24.677 -        if i < self.c: 
  24.678 -            raise IndexError, "already passed "+str(i)
  24.679 -        try:
  24.680 -            while i > self.c: 
  24.681 -                self.i.next()
  24.682 -                self.c += 1
  24.683 -            # now self.c == i
  24.684 -            self.c += 1
  24.685 -            return self.i.next()
  24.686 -        except StopIteration: 
  24.687 -            raise IndexError, str(i)
  24.688 -            
  24.689 -    def __nonzero__(self):
  24.690 -        if hasattr(self, "__len__"):
  24.691 -            return len(self) != 0
  24.692 -        elif hasattr(self, "_head"):
  24.693 -            return True
  24.694 -        else:
  24.695 -            try:
  24.696 -                self._head = self.i.next()
  24.697 -            except StopIteration:
  24.698 -                return False
  24.699 -            else:
  24.700 -                return True
  24.701 -
  24.702 -iterbetter = IterBetter
  24.703 -
  24.704 -def safeiter(it, cleanup=None, ignore_errors=True):
  24.705 -    """Makes an iterator safe by ignoring the exceptions occured during the iteration.
  24.706 -    """
  24.707 -    def next():
  24.708 -        while True:
  24.709 -            try:
  24.710 -                return it.next()
  24.711 -            except StopIteration:
  24.712 -                raise
  24.713 -            except:
  24.714 -                traceback.print_exc()
  24.715 -
  24.716 -    it = iter(it)
  24.717 -    while True:
  24.718 -        yield next()
  24.719 -
  24.720 -def safewrite(filename, content):
  24.721 -    """Writes the content to a temp file and then moves the temp file to 
  24.722 -    given filename to avoid overwriting the existing file in case of errors.
  24.723 -    """
  24.724 -    f = file(filename + '.tmp', 'w')
  24.725 -    f.write(content)
  24.726 -    f.close()
  24.727 -    os.rename(f.name, filename)
  24.728 -
  24.729 -def dictreverse(mapping):
  24.730 -    """
  24.731 -    Returns a new dictionary with keys and values swapped.
  24.732 -    
  24.733 -        >>> dictreverse({1: 2, 3: 4})
  24.734 -        {2: 1, 4: 3}
  24.735 -    """
  24.736 -    return dict([(value, key) for (key, value) in mapping.iteritems()])
  24.737 -
  24.738 -def dictfind(dictionary, element):
  24.739 -    """
  24.740 -    Returns a key whose value in `dictionary` is `element` 
  24.741 -    or, if none exists, None.
  24.742 -    
  24.743 -        >>> d = {1:2, 3:4}
  24.744 -        >>> dictfind(d, 4)
  24.745 -        3
  24.746 -        >>> dictfind(d, 5)
  24.747 -    """
  24.748 -    for (key, value) in dictionary.iteritems():
  24.749 -        if element is value: 
  24.750 -            return key
  24.751 -
  24.752 -def dictfindall(dictionary, element):
  24.753 -    """
  24.754 -    Returns the keys whose values in `dictionary` are `element`
  24.755 -    or, if none exists, [].
  24.756 -    
  24.757 -        >>> d = {1:4, 3:4}
  24.758 -        >>> dictfindall(d, 4)
  24.759 -        [1, 3]
  24.760 -        >>> dictfindall(d, 5)
  24.761 -        []
  24.762 -    """
  24.763 -    res = []
  24.764 -    for (key, value) in dictionary.iteritems():
  24.765 -        if element is value:
  24.766 -            res.append(key)
  24.767 -    return res
  24.768 -
  24.769 -def dictincr(dictionary, element):
  24.770 -    """
  24.771 -    Increments `element` in `dictionary`, 
  24.772 -    setting it to one if it doesn't exist.
  24.773 -    
  24.774 -        >>> d = {1:2, 3:4}
  24.775 -        >>> dictincr(d, 1)
  24.776 -        3
  24.777 -        >>> d[1]
  24.778 -        3
  24.779 -        >>> dictincr(d, 5)
  24.780 -        1
  24.781 -        >>> d[5]
  24.782 -        1
  24.783 -    """
  24.784 -    dictionary.setdefault(element, 0)
  24.785 -    dictionary[element] += 1
  24.786 -    return dictionary[element]
  24.787 -
  24.788 -def dictadd(*dicts):
  24.789 -    """
  24.790 -    Returns a dictionary consisting of the keys in the argument dictionaries.
  24.791 -    If they share a key, the value from the last argument is used.
  24.792 -    
  24.793 -        >>> dictadd({1: 0, 2: 0}, {2: 1, 3: 1})
  24.794 -        {1: 0, 2: 1, 3: 1}
  24.795 -    """
  24.796 -    result = {}
  24.797 -    for dct in dicts:
  24.798 -        result.update(dct)
  24.799 -    return result
  24.800 -
  24.801 -def requeue(queue, index=-1):
  24.802 -    """Returns the element at index after moving it to the beginning of the queue.
  24.803 -
  24.804 -        >>> x = [1, 2, 3, 4]
  24.805 -        >>> requeue(x)
  24.806 -        4
  24.807 -        >>> x
  24.808 -        [4, 1, 2, 3]
  24.809 -    """
  24.810 -    x = queue.pop(index)
  24.811 -    queue.insert(0, x)
  24.812 -    return x
  24.813 -
  24.814 -def restack(stack, index=0):
  24.815 -    """Returns the element at index after moving it to the top of stack.
  24.816 -
  24.817 -           >>> x = [1, 2, 3, 4]
  24.818 -           >>> restack(x)
  24.819 -           1
  24.820 -           >>> x
  24.821 -           [2, 3, 4, 1]
  24.822 -    """
  24.823 -    x = stack.pop(index)
  24.824 -    stack.append(x)
  24.825 -    return x
  24.826 -
  24.827 -def listget(lst, ind, default=None):
  24.828 -    """
  24.829 -    Returns `lst[ind]` if it exists, `default` otherwise.
  24.830 -    
  24.831 -        >>> listget(['a'], 0)
  24.832 -        'a'
  24.833 -        >>> listget(['a'], 1)
  24.834 -        >>> listget(['a'], 1, 'b')
  24.835 -        'b'
  24.836 -    """
  24.837 -    if len(lst)-1 < ind: 
  24.838 -        return default
  24.839 -    return lst[ind]
  24.840 -
  24.841 -def intget(integer, default=None):
  24.842 -    """
  24.843 -    Returns `integer` as an int or `default` if it can't.
  24.844 -    
  24.845 -        >>> intget('3')
  24.846 -        3
  24.847 -        >>> intget('3a')
  24.848 -        >>> intget('3a', 0)
  24.849 -        0
  24.850 -    """
  24.851 -    try:
  24.852 -        return int(integer)
  24.853 -    except (TypeError, ValueError):
  24.854 -        return default
  24.855 -
  24.856 -def datestr(then, now=None):
  24.857 -    """
  24.858 -    Converts a (UTC) datetime object to a nice string representation.
  24.859 -    
  24.860 -        >>> from datetime import datetime, timedelta
  24.861 -        >>> d = datetime(1970, 5, 1)
  24.862 -        >>> datestr(d, now=d)
  24.863 -        '0 microseconds ago'
  24.864 -        >>> for t, v in {
  24.865 -        ...   timedelta(microseconds=1): '1 microsecond ago',
  24.866 -        ...   timedelta(microseconds=2): '2 microseconds ago',
  24.867 -        ...   -timedelta(microseconds=1): '1 microsecond from now',
  24.868 -        ...   -timedelta(microseconds=2): '2 microseconds from now',
  24.869 -        ...   timedelta(microseconds=2000): '2 milliseconds ago',
  24.870 -        ...   timedelta(seconds=2): '2 seconds ago',
  24.871 -        ...   timedelta(seconds=2*60): '2 minutes ago',
  24.872 -        ...   timedelta(seconds=2*60*60): '2 hours ago',
  24.873 -        ...   timedelta(days=2): '2 days ago',
  24.874 -        ... }.iteritems():
  24.875 -        ...     assert datestr(d, now=d+t) == v
  24.876 -        >>> datestr(datetime(1970, 1, 1), now=d)
  24.877 -        'January  1'
  24.878 -        >>> datestr(datetime(1969, 1, 1), now=d)
  24.879 -        'January  1, 1969'
  24.880 -        >>> datestr(datetime(1970, 6, 1), now=d)
  24.881 -        'June  1, 1970'
  24.882 -        >>> datestr(None)
  24.883 -        ''
  24.884 -    """
  24.885 -    def agohence(n, what, divisor=None):
  24.886 -        if divisor: n = n // divisor
  24.887 -
  24.888 -        out = str(abs(n)) + ' ' + what       # '2 day'
  24.889 -        if abs(n) != 1: out += 's'           # '2 days'
  24.890 -        out += ' '                           # '2 days '
  24.891 -        if n < 0:
  24.892 -            out += 'from now'
  24.893 -        else:
  24.894 -            out += 'ago'
  24.895 -        return out                           # '2 days ago'
  24.896 -
  24.897 -    oneday = 24 * 60 * 60
  24.898 -
  24.899 -    if not then: return ""
  24.900 -    if not now: now = datetime.datetime.utcnow()
  24.901 -    if type(now).__name__ == "DateTime":
  24.902 -        now = datetime.datetime.fromtimestamp(now)
  24.903 -    if type(then).__name__ == "DateTime":
  24.904 -        then = datetime.datetime.fromtimestamp(then)
  24.905 -    elif type(then).__name__ == "date":
  24.906 -        then = datetime.datetime(then.year, then.month, then.day)
  24.907 -
  24.908 -    delta = now - then
  24.909 -    deltaseconds = int(delta.days * oneday + delta.seconds + delta.microseconds * 1e-06)
  24.910 -    deltadays = abs(deltaseconds) // oneday
  24.911 -    if deltaseconds < 0: deltadays *= -1 # fix for oddity of floor
  24.912 -
  24.913 -    if deltadays:
  24.914 -        if abs(deltadays) < 4:
  24.915 -            return agohence(deltadays, 'day')
  24.916 -
  24.917 -        try:
  24.918 -            out = then.strftime('%B %e') # e.g. 'June  3'
  24.919 -        except ValueError:
  24.920 -            # %e doesn't work on Windows.
  24.921 -            out = then.strftime('%B %d') # e.g. 'June 03'
  24.922 -
  24.923 -        if then.year != now.year or deltadays < 0:
  24.924 -            out += ', %s' % then.year
  24.925 -        return out
  24.926 -
  24.927 -    if int(deltaseconds):
  24.928 -        if abs(deltaseconds) > (60 * 60):
  24.929 -            return agohence(deltaseconds, 'hour', 60 * 60)
  24.930 -        elif abs(deltaseconds) > 60:
  24.931 -            return agohence(deltaseconds, 'minute', 60)
  24.932 -        else:
  24.933 -            return agohence(deltaseconds, 'second')
  24.934 -
  24.935 -    deltamicroseconds = delta.microseconds
  24.936 -    if delta.days: deltamicroseconds = int(delta.microseconds - 1e6) # datetime oddity
  24.937 -    if abs(deltamicroseconds) > 1000:
  24.938 -        return agohence(deltamicroseconds, 'millisecond', 1000)
  24.939 -
  24.940 -    return agohence(deltamicroseconds, 'microsecond')
  24.941 -
  24.942 -def numify(string):
  24.943 -    """
  24.944 -    Removes all non-digit characters from `string`.
  24.945 -    
  24.946 -        >>> numify('800-555-1212')
  24.947 -        '8005551212'
  24.948 -        >>> numify('800.555.1212')
  24.949 -        '8005551212'
  24.950 -    
  24.951 -    """
  24.952 -    return ''.join([c for c in str(string) if c.isdigit()])
  24.953 -
  24.954 -def denumify(string, pattern):
  24.955 -    """
  24.956 -    Formats `string` according to `pattern`, where the letter X gets replaced
  24.957 -    by characters from `string`.
  24.958 -    
  24.959 -        >>> denumify("8005551212", "(XXX) XXX-XXXX")
  24.960 -        '(800) 555-1212'
  24.961 -    
  24.962 -    """
  24.963 -    out = []
  24.964 -    for c in pattern:
  24.965 -        if c == "X":
  24.966 -            out.append(string[0])
  24.967 -            string = string[1:]
  24.968 -        else:
  24.969 -            out.append(c)
  24.970 -    return ''.join(out)
  24.971 -
  24.972 -def commify(n):
  24.973 -    """
  24.974 -    Add commas to an integer `n`.
  24.975 -
  24.976 -        >>> commify(1)
  24.977 -        '1'
  24.978 -        >>> commify(123)
  24.979 -        '123'
  24.980 -        >>> commify(1234)
  24.981 -        '1,234'
  24.982 -        >>> commify(1234567890)
  24.983 -        '1,234,567,890'
  24.984 -        >>> commify(123.0)
  24.985 -        '123.0'
  24.986 -        >>> commify(1234.5)
  24.987 -        '1,234.5'
  24.988 -        >>> commify(1234.56789)
  24.989 -        '1,234.56789'
  24.990 -        >>> commify('%.2f' % 1234.5)
  24.991 -        '1,234.50'
  24.992 -        >>> commify(None)
  24.993 -        >>>
  24.994 -
  24.995 -    """
  24.996 -    if n is None: return None
  24.997 -    n = str(n)
  24.998 -    if '.' in n:
  24.999 -        dollars, cents = n.split('.')
 24.1000 -    else:
 24.1001 -        dollars, cents = n, None
 24.1002 -
 24.1003 -    r = []
 24.1004 -    for i, c in enumerate(str(dollars)[::-1]):
 24.1005 -        if i and (not (i % 3)):
 24.1006 -            r.insert(0, ',')
 24.1007 -        r.insert(0, c)
 24.1008 -    out = ''.join(r)
 24.1009 -    if cents:
 24.1010 -        out += '.' + cents
 24.1011 -    return out
 24.1012 -
 24.1013 -def dateify(datestring):
 24.1014 -    """
 24.1015 -    Formats a numified `datestring` properly.
 24.1016 -    """
 24.1017 -    return denumify(datestring, "XXXX-XX-XX XX:XX:XX")
 24.1018 -
 24.1019 -
 24.1020 -def nthstr(n):
 24.1021 -    """
 24.1022 -    Formats an ordinal.
 24.1023 -    Doesn't handle negative numbers.
 24.1024 -
 24.1025 -        >>> nthstr(1)
 24.1026 -        '1st'
 24.1027 -        >>> nthstr(0)
 24.1028 -        '0th'
 24.1029 -        >>> [nthstr(x) for x in [2, 3, 4, 5, 10, 11, 12, 13, 14, 15]]
 24.1030 -        ['2nd', '3rd', '4th', '5th', '10th', '11th', '12th', '13th', '14th', '15th']
 24.1031 -        >>> [nthstr(x) for x in [91, 92, 93, 94, 99, 100, 101, 102]]
 24.1032 -        ['91st', '92nd', '93rd', '94th', '99th', '100th', '101st', '102nd']
 24.1033 -        >>> [nthstr(x) for x in [111, 112, 113, 114, 115]]
 24.1034 -        ['111th', '112th', '113th', '114th', '115th']
 24.1035 -
 24.1036 -    """
 24.1037 -    
 24.1038 -    assert n >= 0
 24.1039 -    if n % 100 in [11, 12, 13]: return '%sth' % n
 24.1040 -    return {1: '%sst', 2: '%snd', 3: '%srd'}.get(n % 10, '%sth') % n
 24.1041 -
 24.1042 -def cond(predicate, consequence, alternative=None):
 24.1043 -    """
 24.1044 -    Function replacement for if-else to use in expressions.
 24.1045 -        
 24.1046 -        >>> x = 2
 24.1047 -        >>> cond(x % 2 == 0, "even", "odd")
 24.1048 -        'even'
 24.1049 -        >>> cond(x % 2 == 0, "even", "odd") + '_row'
 24.1050 -        'even_row'
 24.1051 -    """
 24.1052 -    if predicate:
 24.1053 -        return consequence
 24.1054 -    else:
 24.1055 -        return alternative
 24.1056 -
 24.1057 -class CaptureStdout:
 24.1058 -    """
 24.1059 -    Captures everything `func` prints to stdout and returns it instead.
 24.1060 -    
 24.1061 -        >>> def idiot():
 24.1062 -        ...     print "foo"
 24.1063 -        >>> capturestdout(idiot)()
 24.1064 -        'foo\\n'
 24.1065 -    
 24.1066 -    **WARNING:** Not threadsafe!
 24.1067 -    """
 24.1068 -    def __init__(self, func): 
 24.1069 -        self.func = func
 24.1070 -    def __call__(self, *args, **keywords):
 24.1071 -        from cStringIO import StringIO
 24.1072 -        # Not threadsafe!
 24.1073 -        out = StringIO()
 24.1074 -        oldstdout = sys.stdout
 24.1075 -        sys.stdout = out
 24.1076 -        try: 
 24.1077 -            self.func(*args, **keywords)
 24.1078 -        finally: 
 24.1079 -            sys.stdout = oldstdout
 24.1080 -        return out.getvalue()
 24.1081 -
 24.1082 -capturestdout = CaptureStdout
 24.1083 -
 24.1084 -class Profile:
 24.1085 -    """
 24.1086 -    Profiles `func` and returns a tuple containing its output
 24.1087 -    and a string with human-readable profiling information.
 24.1088 -        
 24.1089 -        >>> import time
 24.1090 -        >>> out, inf = profile(time.sleep)(.001)
 24.1091 -        >>> out
 24.1092 -        >>> inf[:10].strip()
 24.1093 -        'took 0.0'
 24.1094 -    """
 24.1095 -    def __init__(self, func): 
 24.1096 -        self.func = func
 24.1097 -    def __call__(self, *args): ##, **kw):   kw unused
 24.1098 -        import hotshot, hotshot.stats, os, tempfile ##, time already imported
 24.1099 -        f, filename = tempfile.mkstemp()
 24.1100 -        os.close(f)
 24.1101 -        
 24.1102 -        prof = hotshot.Profile(filename)
 24.1103 -
 24.1104 -        stime = time.time()
 24.1105 -        result = prof.runcall(self.func, *args)
 24.1106 -        stime = time.time() - stime
 24.1107 -        prof.close()
 24.1108 -
 24.1109 -        import cStringIO
 24.1110 -        out = cStringIO.StringIO()
 24.1111 -        stats = hotshot.stats.load(filename)
 24.1112 -        stats.stream = out
 24.1113 -        stats.strip_dirs()
 24.1114 -        stats.sort_stats('time', 'calls')
 24.1115 -        stats.print_stats(40)
 24.1116 -        stats.print_callers()
 24.1117 -
 24.1118 -        x =  '\n\ntook '+ str(stime) + ' seconds\n'
 24.1119 -        x += out.getvalue()
 24.1120 -
 24.1121 -        # remove the tempfile
 24.1122 -        try:
 24.1123 -            os.remove(filename)
 24.1124 -        except IOError:
 24.1125 -            pass
 24.1126 -            
 24.1127 -        return result, x
 24.1128 -
 24.1129 -profile = Profile
 24.1130 -
 24.1131 -
 24.1132 -import traceback
 24.1133 -# hack for compatibility with Python 2.3:
 24.1134 -if not hasattr(traceback, 'format_exc'):
 24.1135 -    from cStringIO import StringIO
 24.1136 -    def format_exc(limit=None):
 24.1137 -        strbuf = StringIO()
 24.1138 -        traceback.print_exc(limit, strbuf)
 24.1139 -        return strbuf.getvalue()
 24.1140 -    traceback.format_exc = format_exc
 24.1141 -
 24.1142 -def tryall(context, prefix=None):
 24.1143 -    """
 24.1144 -    Tries a series of functions and prints their results. 
 24.1145 -    `context` is a dictionary mapping names to values; 
 24.1146 -    the value will only be tried if it's callable.
 24.1147 -    
 24.1148 -        >>> tryall(dict(j=lambda: True))
 24.1149 -        j: True
 24.1150 -        ----------------------------------------
 24.1151 -        results:
 24.1152 -           True: 1
 24.1153 -
 24.1154 -    For example, you might have a file `test/stuff.py` 
 24.1155 -    with a series of functions testing various things in it. 
 24.1156 -    At the bottom, have a line:
 24.1157 -
 24.1158 -        if __name__ == "__main__": tryall(globals())
 24.1159 -
 24.1160 -    Then you can run `python test/stuff.py` and get the results of 
 24.1161 -    all the tests.
 24.1162 -    """
 24.1163 -    context = context.copy() # vars() would update
 24.1164 -    results = {}
 24.1165 -    for (key, value) in context.iteritems():
 24.1166 -        if not hasattr(value, '__call__'): 
 24.1167 -            continue
 24.1168 -        if prefix and not key.startswith(prefix): 
 24.1169 -            continue
 24.1170 -        print key + ':',
 24.1171 -        try:
 24.1172 -            r = value()
 24.1173 -            dictincr(results, r)
 24.1174 -            print r
 24.1175 -        except:
 24.1176 -            print 'ERROR'
 24.1177 -            dictincr(results, 'ERROR')
 24.1178 -            print '   ' + '\n   '.join(traceback.format_exc().split('\n'))
 24.1179 -        
 24.1180 -    print '-'*40
 24.1181 -    print 'results:'
 24.1182 -    for (key, value) in results.iteritems():
 24.1183 -        print ' '*2, str(key)+':', value
 24.1184 -        
 24.1185 -class ThreadedDict(threadlocal):
 24.1186 -    """
 24.1187 -    Thread local storage.
 24.1188 -    
 24.1189 -        >>> d = ThreadedDict()
 24.1190 -        >>> d.x = 1
 24.1191 -        >>> d.x
 24.1192 -        1
 24.1193 -        >>> import threading
 24.1194 -        >>> def f(): d.x = 2
 24.1195 -        ...
 24.1196 -        >>> t = threading.Thread(target=f)
 24.1197 -        >>> t.start()
 24.1198 -        >>> t.join()
 24.1199 -        >>> d.x
 24.1200 -        1
 24.1201 -    """
 24.1202 -    _instances = set()
 24.1203 -    
 24.1204 -    def __init__(self):
 24.1205 -        ThreadedDict._instances.add(self)
 24.1206 -        
 24.1207 -    def __del__(self):
 24.1208 -        ThreadedDict._instances.remove(self)
 24.1209 -        
 24.1210 -    def __hash__(self):
 24.1211 -        return id(self)
 24.1212 -    
 24.1213 -    def clear_all():
 24.1214 -        """Clears all ThreadedDict instances.
 24.1215 -        """
 24.1216 -        for t in list(ThreadedDict._instances):
 24.1217 -            t.clear()
 24.1218 -    clear_all = staticmethod(clear_all)
 24.1219 -    
 24.1220 -    # Define all these methods to more or less fully emulate dict -- attribute access
 24.1221 -    # is built into threading.local.
 24.1222 -
 24.1223 -    def __getitem__(self, key):
 24.1224 -        return self.__dict__[key]
 24.1225 -
 24.1226 -    def __setitem__(self, key, value):
 24.1227 -        self.__dict__[key] = value
 24.1228 -
 24.1229 -    def __delitem__(self, key):
 24.1230 -        del self.__dict__[key]
 24.1231 -
 24.1232 -    def __contains__(self, key):
 24.1233 -        return key in self.__dict__
 24.1234 -
 24.1235 -    has_key = __contains__
 24.1236 -        
 24.1237 -    def clear(self):
 24.1238 -        self.__dict__.clear()
 24.1239 -
 24.1240 -    def copy(self):
 24.1241 -        return self.__dict__.copy()
 24.1242 -
 24.1243 -    def get(self, key, default=None):
 24.1244 -        return self.__dict__.get(key, default)
 24.1245 -
 24.1246 -    def items(self):
 24.1247 -        return self.__dict__.items()
 24.1248 -
 24.1249 -    def iteritems(self):
 24.1250 -        return self.__dict__.iteritems()
 24.1251 -
 24.1252 -    def keys(self):
 24.1253 -        return self.__dict__.keys()
 24.1254 -
 24.1255 -    def iterkeys(self):
 24.1256 -        return self.__dict__.iterkeys()
 24.1257 -
 24.1258 -    iter = iterkeys
 24.1259 -
 24.1260 -    def values(self):
 24.1261 -        return self.__dict__.values()
 24.1262 -
 24.1263 -    def itervalues(self):
 24.1264 -        return self.__dict__.itervalues()
 24.1265 -
 24.1266 -    def pop(self, key, *args):
 24.1267 -        return self.__dict__.pop(key, *args)
 24.1268 -
 24.1269 -    def popitem(self):
 24.1270 -        return self.__dict__.popitem()
 24.1271 -
 24.1272 -    def setdefault(self, key, default=None):
 24.1273 -        return self.__dict__.setdefault(key, default)
 24.1274 -
 24.1275 -    def update(self, *args, **kwargs):
 24.1276 -        self.__dict__.update(*args, **kwargs)
 24.1277 -
 24.1278 -    def __repr__(self):
 24.1279 -        return '<ThreadedDict %r>' % self.__dict__
 24.1280 -
 24.1281 -    __str__ = __repr__
 24.1282 -    
 24.1283 -threadeddict = ThreadedDict
 24.1284 -
 24.1285 -def autoassign(self, locals):
 24.1286 -    """
 24.1287 -    Automatically assigns local variables to `self`.
 24.1288 -    
 24.1289 -        >>> self = storage()
 24.1290 -        >>> autoassign(self, dict(a=1, b=2))
 24.1291 -        >>> self
 24.1292 -        <Storage {'a': 1, 'b': 2}>
 24.1293 -    
 24.1294 -    Generally used in `__init__` methods, as in:
 24.1295 -
 24.1296 -        def __init__(self, foo, bar, baz=1): autoassign(self, locals())
 24.1297 -    """
 24.1298 -    for (key, value) in locals.iteritems():
 24.1299 -        if key == 'self': 
 24.1300 -            continue
 24.1301 -        setattr(self, key, value)
 24.1302 -
 24.1303 -def to36(q):
 24.1304 -    """
 24.1305 -    Converts an integer to base 36 (a useful scheme for human-sayable IDs).
 24.1306 -    
 24.1307 -        >>> to36(35)
 24.1308 -        'z'
 24.1309 -        >>> to36(119292)
 24.1310 -        '2k1o'
 24.1311 -        >>> int(to36(939387374), 36)
 24.1312 -        939387374
 24.1313 -        >>> to36(0)
 24.1314 -        '0'
 24.1315 -        >>> to36(-393)
 24.1316 -        Traceback (most recent call last):
 24.1317 -            ... 
 24.1318 -        ValueError: must supply a positive integer
 24.1319 -    
 24.1320 -    """
 24.1321 -    if q < 0: raise ValueError, "must supply a positive integer"
 24.1322 -    letters = "0123456789abcdefghijklmnopqrstuvwxyz"
 24.1323 -    converted = []
 24.1324 -    while q != 0:
 24.1325 -        q, r = divmod(q, 36)
 24.1326 -        converted.insert(0, letters[r])
 24.1327 -    return "".join(converted) or '0'
 24.1328 -
 24.1329 -
 24.1330 -r_url = re_compile('(?<!\()(http://(\S+))')
 24.1331 -def safemarkdown(text):
 24.1332 -    """
 24.1333 -    Converts text to HTML following the rules of Markdown, but blocking any
 24.1334 -    outside HTML input, so that only the things supported by Markdown
 24.1335 -    can be used. Also converts raw URLs to links.
 24.1336 -
 24.1337 -    (requires [markdown.py](http://webpy.org/markdown.py))
 24.1338 -    """
 24.1339 -    from markdown import markdown
 24.1340 -    if text:
 24.1341 -        text = text.replace('<', '&lt;')
 24.1342 -        # TODO: automatically get page title?
 24.1343 -        text = r_url.sub(r'<\1>', text)
 24.1344 -        text = markdown(text)
 24.1345 -        return text
 24.1346 -
 24.1347 -def sendmail(from_address, to_address, subject, message, headers=None, **kw):
 24.1348 -    """
 24.1349 -    Sends the email message `message` with mail and envelope headers
 24.1350 -    for from `from_address_` to `to_address` with `subject`. 
 24.1351 -    Additional email headers can be specified with the dictionary 
 24.1352 -    `headers.
 24.1353 -    
 24.1354 -    Optionally cc, bcc and attachments can be specified as keyword arguments.
 24.1355 -    Attachments must be an iterable and each attachment can be either a 
 24.1356 -    filename or a file object or a dictionary with filename, content and 
 24.1357 -    optionally content_type keys.
 24.1358 -
 24.1359 -    If `web.config.smtp_server` is set, it will send the message
 24.1360 -    to that SMTP server. Otherwise it will look for 
 24.1361 -    `/usr/sbin/sendmail`, the typical location for the sendmail-style
 24.1362 -    binary. To use sendmail from a different path, set `web.config.sendmail_path`.
 24.1363 -    """
 24.1364 -    attachments = kw.pop("attachments", [])
 24.1365 -    mail = _EmailMessage(from_address, to_address, subject, message, headers, **kw)
 24.1366 -
 24.1367 -    for a in attachments:
 24.1368 -        if isinstance(a, dict):
 24.1369 -            mail.attach(a['filename'], a['content'], a.get('content_type'))
 24.1370 -        elif hasattr(a, 'read'): # file
 24.1371 -            filename = os.path.basename(getattr(a, "name", ""))
 24.1372 -            content_type = getattr(a, 'content_type', None)
 24.1373 -            mail.attach(filename, a.read(), content_type)
 24.1374 -        elif isinstance(a, basestring):
 24.1375 -            f = open(a, 'rb')
 24.1376 -            content = f.read()
 24.1377 -            f.close()
 24.1378 -            filename = os.path.basename(a)
 24.1379 -            mail.attach(filename, content, None)
 24.1380 -        else:
 24.1381 -            raise ValueError, "Invalid attachment: %s" % repr(a)
 24.1382 -            
 24.1383 -    mail.send()
 24.1384 -
 24.1385 -class _EmailMessage:
 24.1386 -    def __init__(self, from_address, to_address, subject, message, headers=None, **kw):
 24.1387 -        def listify(x):
 24.1388 -            if not isinstance(x, list):
 24.1389 -                return [safestr(x)]
 24.1390 -            else:
 24.1391 -                return [safestr(a) for a in x]
 24.1392 -    
 24.1393 -        subject = safestr(subject)
 24.1394 -        message = safestr(message)
 24.1395 -
 24.1396 -        from_address = safestr(from_address)
 24.1397 -        to_address = listify(to_address)    
 24.1398 -        cc = listify(kw.get('cc', []))
 24.1399 -        bcc = listify(kw.get('bcc', []))
 24.1400 -        recipients = to_address + cc + bcc
 24.1401 -
 24.1402 -        import email.Utils
 24.1403 -        self.from_address = email.Utils.parseaddr(from_address)[1]
 24.1404 -        self.recipients = [email.Utils.parseaddr(r)[1] for r in recipients]        
 24.1405 -    
 24.1406 -        self.headers = dictadd({
 24.1407 -          'From': from_address,
 24.1408 -          'To': ", ".join(to_address),
 24.1409 -          'Subject': subject
 24.1410 -        }, headers or {})
 24.1411 -
 24.1412 -        if cc:
 24.1413 -            self.headers['Cc'] = ", ".join(cc)
 24.1414 -    
 24.1415 -        self.message = self.new_message()
 24.1416 -        self.message.add_header("Content-Transfer-Encoding", "7bit")
 24.1417 -        self.message.add_header("Content-Disposition", "inline")
 24.1418 -        self.message.add_header("MIME-Version", "1.0")
 24.1419 -        self.message.set_payload(message, 'utf-8')
 24.1420 -        self.multipart = False
 24.1421 -        
 24.1422 -    def new_message(self):
 24.1423 -        from email.Message import Message
 24.1424 -        return Message()
 24.1425 -        
 24.1426 -    def attach(self, filename, content, content_type=None):
 24.1427 -        if not self.multipart:
 24.1428 -            msg = self.new_message()
 24.1429 -            msg.add_header("Content-Type", "multipart/mixed")
 24.1430 -            msg.attach(self.message)
 24.1431 -            self.message = msg
 24.1432 -            self.multipart = True
 24.1433 -                        
 24.1434 -        import mimetypes
 24.1435 -        try:
 24.1436 -            from email import encoders
 24.1437 -        except:
 24.1438 -            from email import Encoders as encoders
 24.1439 -            
 24.1440 -        content_type = content_type or mimetypes.guess_type(filename)[0] or "applcation/octet-stream"
 24.1441 -        
 24.1442 -        msg = self.new_message()
 24.1443 -        msg.set_payload(content)
 24.1444 -        msg.add_header('Content-Type', content_type)
 24.1445 -        msg.add_header('Content-Disposition', 'attachment', filename=filename)
 24.1446 -        
 24.1447 -        if not content_type.startswith("text/"):
 24.1448 -            encoders.encode_base64(msg)
 24.1449 -            
 24.1450 -        self.message.attach(msg)
 24.1451 -
 24.1452 -    def prepare_message(self):
 24.1453 -        for k, v in self.headers.iteritems():
 24.1454 -            if k.lower() == "content-type":
 24.1455 -                self.message.set_type(v)
 24.1456 -            else:
 24.1457 -                self.message.add_header(k, v)
 24.1458 -
 24.1459 -        self.headers = {}
 24.1460 -
 24.1461 -    def send(self):
 24.1462 -        try:
 24.1463 -            import webapi
 24.1464 -        except ImportError:
 24.1465 -            webapi = Storage(config=Storage())
 24.1466 -
 24.1467 -        self.prepare_message()
 24.1468 -        message_text = self.message.as_string()
 24.1469 -    
 24.1470 -        if webapi.config.get('smtp_server'):
 24.1471 -            server = webapi.config.get('smtp_server')
 24.1472 -            port = webapi.config.get('smtp_port', 0)
 24.1473 -            username = webapi.config.get('smtp_username') 
 24.1474 -            password = webapi.config.get('smtp_password')
 24.1475 -            debug_level = webapi.config.get('smtp_debuglevel', None)
 24.1476 -            starttls = webapi.config.get('smtp_starttls', False)
 24.1477 -
 24.1478 -            import smtplib
 24.1479 -            smtpserver = smtplib.SMTP(server, port)
 24.1480 -
 24.1481 -            if debug_level:
 24.1482 -                smtpserver.set_debuglevel(debug_level)
 24.1483 -
 24.1484 -            if starttls:
 24.1485 -                smtpserver.ehlo()
 24.1486 -                smtpserver.starttls()
 24.1487 -                smtpserver.ehlo()
 24.1488 -
 24.1489 -            if username and password:
 24.1490 -                smtpserver.login(username, password)
 24.1491 -
 24.1492 -            smtpserver.sendmail(self.from_address, self.recipients, message_text)
 24.1493 -            smtpserver.quit()
 24.1494 -        elif webapi.config.get('email_engine') == 'aws':
 24.1495 -            import boto.ses
 24.1496 -            c = boto.ses.SESConnection(
 24.1497 -              aws_access_key_id=webapi.config.get('aws_access_key_id'),
 24.1498 -              aws_secret_access_key=web.api.config.get('aws_secret_access_key'))
 24.1499 -            c.send_raw_email(self.from_address, message_text, self.from_recipients)
 24.1500 -        else:
 24.1501 -            sendmail = webapi.config.get('sendmail_path', '/usr/sbin/sendmail')
 24.1502 -        
 24.1503 -            assert not self.from_address.startswith('-'), 'security'
 24.1504 -            for r in self.recipients:
 24.1505 -                assert not r.startswith('-'), 'security'
 24.1506 -                
 24.1507 -            cmd = [sendmail, '-f', self.from_address] + self.recipients
 24.1508 -
 24.1509 -            if subprocess:
 24.1510 -                p = subprocess.Popen(cmd, stdin=subprocess.PIPE)
 24.1511 -                p.stdin.write(message_text)
 24.1512 -                p.stdin.close()
 24.1513 -                p.wait()
 24.1514 -            else:
 24.1515 -                i, o = os.popen2(cmd)
 24.1516 -                i.write(message)
 24.1517 -                i.close()
 24.1518 -                o.close()
 24.1519 -                del i, o
 24.1520 -                
 24.1521 -    def __repr__(self):
 24.1522 -        return "<EmailMessage>"
 24.1523 -    
 24.1524 -    def __str__(self):
 24.1525 -        return self.message.as_string()
 24.1526 -
 24.1527 -if __name__ == "__main__":
 24.1528 -    import doctest
 24.1529 -    doctest.testmod()
    25.1 --- a/OpenSecurity/install/web.py-0.37/build/lib/web/webapi.py	Thu Feb 20 15:40:48 2014 +0100
    25.2 +++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
    25.3 @@ -1,525 +0,0 @@
    25.4 -"""
    25.5 -Web API (wrapper around WSGI)
    25.6 -(from web.py)
    25.7 -"""
    25.8 -
    25.9 -__all__ = [
   25.10 -    "config",
   25.11 -    "header", "debug",
   25.12 -    "input", "data",
   25.13 -    "setcookie", "cookies",
   25.14 -    "ctx", 
   25.15 -    "HTTPError", 
   25.16 -
   25.17 -    # 200, 201, 202
   25.18 -    "OK", "Created", "Accepted",    
   25.19 -    "ok", "created", "accepted",
   25.20 -    
   25.21 -    # 301, 302, 303, 304, 307
   25.22 -    "Redirect", "Found", "SeeOther", "NotModified", "TempRedirect", 
   25.23 -    "redirect", "found", "seeother", "notmodified", "tempredirect",
   25.24 -
   25.25 -    # 400, 401, 403, 404, 405, 406, 409, 410, 412, 415
   25.26 -    "BadRequest", "Unauthorized", "Forbidden", "NotFound", "NoMethod", "NotAcceptable", "Conflict", "Gone", "PreconditionFailed", "UnsupportedMediaType",
   25.27 -    "badrequest", "unauthorized", "forbidden", "notfound", "nomethod", "notacceptable", "conflict", "gone", "preconditionfailed", "unsupportedmediatype",
   25.28 -
   25.29 -    # 500
   25.30 -    "InternalError", 
   25.31 -    "internalerror",
   25.32 -]
   25.33 -
   25.34 -import sys, cgi, Cookie, pprint, urlparse, urllib
   25.35 -from utils import storage, storify, threadeddict, dictadd, intget, safestr
   25.36 -
   25.37 -config = storage()
   25.38 -config.__doc__ = """
   25.39 -A configuration object for various aspects of web.py.
   25.40 -
   25.41 -`debug`
   25.42 -   : when True, enables reloading, disabled template caching and sets internalerror to debugerror.
   25.43 -"""
   25.44 -
   25.45 -class HTTPError(Exception):
   25.46 -    def __init__(self, status, headers={}, data=""):
   25.47 -        ctx.status = status
   25.48 -        for k, v in headers.items():
   25.49 -            header(k, v)
   25.50 -        self.data = data
   25.51 -        Exception.__init__(self, status)
   25.52 -        
   25.53 -def _status_code(status, data=None, classname=None, docstring=None):
   25.54 -    if data is None:
   25.55 -        data = status.split(" ", 1)[1]
   25.56 -    classname = status.split(" ", 1)[1].replace(' ', '') # 304 Not Modified -> NotModified    
   25.57 -    docstring = docstring or '`%s` status' % status
   25.58 -
   25.59 -    def __init__(self, data=data, headers={}):
   25.60 -        HTTPError.__init__(self, status, headers, data)
   25.61 -        
   25.62 -    # trick to create class dynamically with dynamic docstring.
   25.63 -    return type(classname, (HTTPError, object), {
   25.64 -        '__doc__': docstring,
   25.65 -        '__init__': __init__
   25.66 -    })
   25.67 -
   25.68 -ok = OK = _status_code("200 OK", data="")
   25.69 -created = Created = _status_code("201 Created")
   25.70 -accepted = Accepted = _status_code("202 Accepted")
   25.71 -
   25.72 -class Redirect(HTTPError):
   25.73 -    """A `301 Moved Permanently` redirect."""
   25.74 -    def __init__(self, url, status='301 Moved Permanently', absolute=False):
   25.75 -        """
   25.76 -        Returns a `status` redirect to the new URL. 
   25.77 -        `url` is joined with the base URL so that things like 
   25.78 -        `redirect("about") will work properly.
   25.79 -        """
   25.80 -        newloc = urlparse.urljoin(ctx.path, url)
   25.81 -
   25.82 -        if newloc.startswith('/'):
   25.83 -            if absolute:
   25.84 -                home = ctx.realhome
   25.85 -            else:
   25.86 -                home = ctx.home
   25.87 -            newloc = home + newloc
   25.88 -
   25.89 -        headers = {
   25.90 -            'Content-Type': 'text/html',
   25.91 -            'Location': newloc
   25.92 -        }
   25.93 -        HTTPError.__init__(self, status, headers, "")
   25.94 -
   25.95 -redirect = Redirect
   25.96 -
   25.97 -class Found(Redirect):
   25.98 -    """A `302 Found` redirect."""
   25.99 -    def __init__(self, url, absolute=False):
  25.100 -        Redirect.__init__(self, url, '302 Found', absolute=absolute)
  25.101 -
  25.102 -found = Found
  25.103 -
  25.104 -class SeeOther(Redirect):
  25.105 -    """A `303 See Other` redirect."""
  25.106 -    def __init__(self, url, absolute=False):
  25.107 -        Redirect.__init__(self, url, '303 See Other', absolute=absolute)
  25.108 -    
  25.109 -seeother = SeeOther
  25.110 -
  25.111 -class NotModified(HTTPError):
  25.112 -    """A `304 Not Modified` status."""
  25.113 -    def __init__(self):
  25.114 -        HTTPError.__init__(self, "304 Not Modified")
  25.115 -
  25.116 -notmodified = NotModified
  25.117 -
  25.118 -class TempRedirect(Redirect):
  25.119 -    """A `307 Temporary Redirect` redirect."""
  25.120 -    def __init__(self, url, absolute=False):
  25.121 -        Redirect.__init__(self, url, '307 Temporary Redirect', absolute=absolute)
  25.122 -
  25.123 -tempredirect = TempRedirect
  25.124 -
  25.125 -class BadRequest(HTTPError):
  25.126 -    """`400 Bad Request` error."""
  25.127 -    message = "bad request"
  25.128 -    def __init__(self, message=None):
  25.129 -        status = "400 Bad Request"
  25.130 -        headers = {'Content-Type': 'text/html'}
  25.131 -        HTTPError.__init__(self, status, headers, message or self.message)
  25.132 -
  25.133 -badrequest = BadRequest
  25.134 -
  25.135 -class Unauthorized(HTTPError):
  25.136 -    """`401 Unauthorized` error."""
  25.137 -    message = "unauthorized"
  25.138 -    def __init__(self):
  25.139 -        status = "401 Unauthorized"
  25.140 -        headers = {'Content-Type': 'text/html'}
  25.141 -        HTTPError.__init__(self, status, headers, self.message)
  25.142 -
  25.143 -unauthorized = Unauthorized
  25.144 -
  25.145 -class Forbidden(HTTPError):
  25.146 -    """`403 Forbidden` error."""
  25.147 -    message = "forbidden"
  25.148 -    def __init__(self):
  25.149 -        status = "403 Forbidden"
  25.150 -        headers = {'Content-Type': 'text/html'}
  25.151 -        HTTPError.__init__(self, status, headers, self.message)
  25.152 -
  25.153 -forbidden = Forbidden
  25.154 -
  25.155 -class _NotFound(HTTPError):
  25.156 -    """`404 Not Found` error."""
  25.157 -    message = "not found"
  25.158 -    def __init__(self, message=None):
  25.159 -        status = '404 Not Found'
  25.160 -        headers = {'Content-Type': 'text/html'}
  25.161 -        HTTPError.__init__(self, status, headers, message or self.message)
  25.162 -
  25.163 -def NotFound(message=None):
  25.164 -    """Returns HTTPError with '404 Not Found' error from the active application.
  25.165 -    """
  25.166 -    if message:
  25.167 -        return _NotFound(message)
  25.168 -    elif ctx.get('app_stack'):
  25.169 -        return ctx.app_stack[-1].notfound()
  25.170 -    else:
  25.171 -        return _NotFound()
  25.172 -
  25.173 -notfound = NotFound
  25.174 -
  25.175 -class NoMethod(HTTPError):
  25.176 -    """A `405 Method Not Allowed` error."""
  25.177 -    def __init__(self, cls=None):
  25.178 -        status = '405 Method Not Allowed'
  25.179 -        headers = {}
  25.180 -        headers['Content-Type'] = 'text/html'
  25.181 -        
  25.182 -        methods = ['GET', 'HEAD', 'POST', 'PUT', 'DELETE']
  25.183 -        if cls:
  25.184 -            methods = [method for method in methods if hasattr(cls, method)]
  25.185 -
  25.186 -        headers['Allow'] = ', '.join(methods)
  25.187 -        data = None
  25.188 -        HTTPError.__init__(self, status, headers, data)
  25.189 -        
  25.190 -nomethod = NoMethod
  25.191 -
  25.192 -class NotAcceptable(HTTPError):
  25.193 -    """`406 Not Acceptable` error."""
  25.194 -    message = "not acceptable"
  25.195 -    def __init__(self):
  25.196 -        status = "406 Not Acceptable"
  25.197 -        headers = {'Content-Type': 'text/html'}
  25.198 -        HTTPError.__init__(self, status, headers, self.message)
  25.199 -
  25.200 -notacceptable = NotAcceptable
  25.201 -
  25.202 -class Conflict(HTTPError):
  25.203 -    """`409 Conflict` error."""
  25.204 -    message = "conflict"
  25.205 -    def __init__(self):
  25.206 -        status = "409 Conflict"
  25.207 -        headers = {'Content-Type': 'text/html'}
  25.208 -        HTTPError.__init__(self, status, headers, self.message)
  25.209 -
  25.210 -conflict = Conflict
  25.211 -
  25.212 -class Gone(HTTPError):
  25.213 -    """`410 Gone` error."""
  25.214 -    message = "gone"
  25.215 -    def __init__(self):
  25.216 -        status = '410 Gone'
  25.217 -        headers = {'Content-Type': 'text/html'}
  25.218 -        HTTPError.__init__(self, status, headers, self.message)
  25.219 -
  25.220 -gone = Gone
  25.221 -
  25.222 -class PreconditionFailed(HTTPError):
  25.223 -    """`412 Precondition Failed` error."""
  25.224 -    message = "precondition failed"
  25.225 -    def __init__(self):
  25.226 -        status = "412 Precondition Failed"
  25.227 -        headers = {'Content-Type': 'text/html'}
  25.228 -        HTTPError.__init__(self, status, headers, self.message)
  25.229 -
  25.230 -preconditionfailed = PreconditionFailed
  25.231 -
  25.232 -class UnsupportedMediaType(HTTPError):
  25.233 -    """`415 Unsupported Media Type` error."""
  25.234 -    message = "unsupported media type"
  25.235 -    def __init__(self):
  25.236 -        status = "415 Unsupported Media Type"
  25.237 -        headers = {'Content-Type': 'text/html'}
  25.238 -        HTTPError.__init__(self, status, headers, self.message)
  25.239 -
  25.240 -unsupportedmediatype = UnsupportedMediaType
  25.241 -
  25.242 -class _InternalError(HTTPError):
  25.243 -    """500 Internal Server Error`."""
  25.244 -    message = "internal server error"
  25.245 -    
  25.246 -    def __init__(self, message=None):
  25.247 -        status = '500 Internal Server Error'
  25.248 -        headers = {'Content-Type': 'text/html'}
  25.249 -        HTTPError.__init__(self, status, headers, message or self.message)
  25.250 -
  25.251 -def InternalError(message=None):
  25.252 -    """Returns HTTPError with '500 internal error' error from the active application.
  25.253 -    """
  25.254 -    if message:
  25.255 -        return _InternalError(message)
  25.256 -    elif ctx.get('app_stack'):
  25.257 -        return ctx.app_stack[-1].internalerror()
  25.258 -    else:
  25.259 -        return _InternalError()
  25.260 -
  25.261 -internalerror = InternalError
  25.262 -
  25.263 -def header(hdr, value, unique=False):
  25.264 -    """
  25.265 -    Adds the header `hdr: value` with the response.
  25.266 -    
  25.267 -    If `unique` is True and a header with that name already exists,
  25.268 -    it doesn't add a new one. 
  25.269 -    """
  25.270 -    hdr, value = safestr(hdr), safestr(value)
  25.271 -    # protection against HTTP response splitting attack
  25.272 -    if '\n' in hdr or '\r' in hdr or '\n' in value or '\r' in value:
  25.273 -        raise ValueError, 'invalid characters in header'
  25.274 -        
  25.275 -    if unique is True:
  25.276 -        for h, v in ctx.headers:
  25.277 -            if h.lower() == hdr.lower(): return
  25.278 -    
  25.279 -    ctx.headers.append((hdr, value))
  25.280 -    
  25.281 -def rawinput(method=None):
  25.282 -    """Returns storage object with GET or POST arguments.
  25.283 -    """
  25.284 -    method = method or "both"
  25.285 -    from cStringIO import StringIO
  25.286 -
  25.287 -    def dictify(fs): 
  25.288 -        # hack to make web.input work with enctype='text/plain.
  25.289 -        if fs.list is None:
  25.290 -            fs.list = [] 
  25.291 -
  25.292 -        return dict([(k, fs[k]) for k in fs.keys()])
  25.293 -    
  25.294 -    e = ctx.env.copy()
  25.295 -    a = b = {}
  25.296 -    
  25.297 -    if method.lower() in ['both', 'post', 'put']:
  25.298 -        if e['REQUEST_METHOD'] in ['POST', 'PUT']:
  25.299 -            if e.get('CONTENT_TYPE', '').lower().startswith('multipart/'):
  25.300 -                # since wsgi.input is directly passed to cgi.FieldStorage, 
  25.301 -                # it can not be called multiple times. Saving the FieldStorage
  25.302 -                # object in ctx to allow calling web.input multiple times.
  25.303 -                a = ctx.get('_fieldstorage')
  25.304 -                if not a:
  25.305 -                    fp = e['wsgi.input']
  25.306 -                    a = cgi.FieldStorage(fp=fp, environ=e, keep_blank_values=1)
  25.307 -                    ctx._fieldstorage = a
  25.308 -            else:
  25.309 -                fp = StringIO(data())
  25.310 -                a = cgi.FieldStorage(fp=fp, environ=e, keep_blank_values=1)
  25.311 -            a = dictify(a)
  25.312 -
  25.313 -    if method.lower() in ['both', 'get']:
  25.314 -        e['REQUEST_METHOD'] = 'GET'
  25.315 -        b = dictify(cgi.FieldStorage(environ=e, keep_blank_values=1))
  25.316 -
  25.317 -    def process_fieldstorage(fs):
  25.318 -        if isinstance(fs, list):
  25.319 -            return [process_fieldstorage(x) for x in fs]
  25.320 -        elif fs.filename is None:
  25.321 -            return fs.value
  25.322 -        else:
  25.323 -            return fs
  25.324 -
  25.325 -    return storage([(k, process_fieldstorage(v)) for k, v in dictadd(b, a).items()])
  25.326 -
  25.327 -def input(*requireds, **defaults):
  25.328 -    """
  25.329 -    Returns a `storage` object with the GET and POST arguments. 
  25.330 -    See `storify` for how `requireds` and `defaults` work.
  25.331 -    """
  25.332 -    _method = defaults.pop('_method', 'both')
  25.333 -    out = rawinput(_method)
  25.334 -    try:
  25.335 -        defaults.setdefault('_unicode', True) # force unicode conversion by default.
  25.336 -        return storify(out, *requireds, **defaults)
  25.337 -    except KeyError:
  25.338 -        raise badrequest()
  25.339 -
  25.340 -def data():
  25.341 -    """Returns the data sent with the request."""
  25.342 -    if 'data' not in ctx:
  25.343 -        cl = intget(ctx.env.get('CONTENT_LENGTH'), 0)
  25.344 -        ctx.data = ctx.env['wsgi.input'].read(cl)
  25.345 -    return ctx.data
  25.346 -
  25.347 -def setcookie(name, value, expires='', domain=None,
  25.348 -              secure=False, httponly=False, path=None):
  25.349 -    """Sets a cookie."""
  25.350 -    morsel = Cookie.Morsel()
  25.351 -    name, value = safestr(name), safestr(value)
  25.352 -    morsel.set(name, value, urllib.quote(value))
  25.353 -    if expires < 0:
  25.354 -        expires = -1000000000
  25.355 -    morsel['expires'] = expires
  25.356 -    morsel['path'] = path or ctx.homepath+'/'
  25.357 -    if domain:
  25.358 -        morsel['domain'] = domain
  25.359 -    if secure:
  25.360 -        morsel['secure'] = secure
  25.361 -    value = morsel.OutputString()
  25.362 -    if httponly:
  25.363 -        value += '; httponly'
  25.364 -    header('Set-Cookie', value)
  25.365 -        
  25.366 -def decode_cookie(value):
  25.367 -    r"""Safely decodes a cookie value to unicode. 
  25.368 -    
  25.369 -    Tries us-ascii, utf-8 and io8859 encodings, in that order.
  25.370 -
  25.371 -    >>> decode_cookie('')
  25.372 -    u''
  25.373 -    >>> decode_cookie('asdf')
  25.374 -    u'asdf'
  25.375 -    >>> decode_cookie('foo \xC3\xA9 bar')
  25.376 -    u'foo \xe9 bar'
  25.377 -    >>> decode_cookie('foo \xE9 bar')
  25.378 -    u'foo \xe9 bar'
  25.379 -    """
  25.380 -    try:
  25.381 -        # First try plain ASCII encoding
  25.382 -        return unicode(value, 'us-ascii')
  25.383 -    except UnicodeError:
  25.384 -        # Then try UTF-8, and if that fails, ISO8859
  25.385 -        try:
  25.386 -            return unicode(value, 'utf-8')
  25.387 -        except UnicodeError:
  25.388 -            return unicode(value, 'iso8859', 'ignore')
  25.389 -
  25.390 -def parse_cookies(http_cookie):
  25.391 -    r"""Parse a HTTP_COOKIE header and return dict of cookie names and decoded values.
  25.392 -        
  25.393 -    >>> sorted(parse_cookies('').items())
  25.394 -    []
  25.395 -    >>> sorted(parse_cookies('a=1').items())
  25.396 -    [('a', '1')]
  25.397 -    >>> sorted(parse_cookies('a=1%202').items())
  25.398 -    [('a', '1 2')]
  25.399 -    >>> sorted(parse_cookies('a=Z%C3%A9Z').items())
  25.400 -    [('a', 'Z\xc3\xa9Z')]
  25.401 -    >>> sorted(parse_cookies('a=1; b=2; c=3').items())
  25.402 -    [('a', '1'), ('b', '2'), ('c', '3')]
  25.403 -    >>> sorted(parse_cookies('a=1; b=w("x")|y=z; c=3').items())
  25.404 -    [('a', '1'), ('b', 'w('), ('c', '3')]
  25.405 -    >>> sorted(parse_cookies('a=1; b=w(%22x%22)|y=z; c=3').items())
  25.406 -    [('a', '1'), ('b', 'w("x")|y=z'), ('c', '3')]
  25.407 -
  25.408 -    >>> sorted(parse_cookies('keebler=E=mc2').items())
  25.409 -    [('keebler', 'E=mc2')]
  25.410 -    >>> sorted(parse_cookies(r'keebler="E=mc2; L=\"Loves\"; fudge=\012;"').items())
  25.411 -    [('keebler', 'E=mc2; L="Loves"; fudge=\n;')]
  25.412 -    """
  25.413 -    #print "parse_cookies"
  25.414 -    if '"' in http_cookie:
  25.415 -        # HTTP_COOKIE has quotes in it, use slow but correct cookie parsing
  25.416 -        cookie = Cookie.SimpleCookie()
  25.417 -        try:
  25.418 -            cookie.load(http_cookie)
  25.419 -        except Cookie.CookieError:
  25.420 -            # If HTTP_COOKIE header is malformed, try at least to load the cookies we can by
  25.421 -            # first splitting on ';' and loading each attr=value pair separately
  25.422 -            cookie = Cookie.SimpleCookie()
  25.423 -            for attr_value in http_cookie.split(';'):
  25.424 -                try:
  25.425 -                    cookie.load(attr_value)
  25.426 -                except Cookie.CookieError:
  25.427 -                    pass
  25.428 -        cookies = dict((k, urllib.unquote(v.value)) for k, v in cookie.iteritems())
  25.429 -    else:
  25.430 -        # HTTP_COOKIE doesn't have quotes, use fast cookie parsing
  25.431 -        cookies = {}
  25.432 -        for key_value in http_cookie.split(';'):
  25.433 -            key_value = key_value.split('=', 1)
  25.434 -            if len(key_value) == 2:
  25.435 -                key, value = key_value
  25.436 -                cookies[key.strip()] = urllib.unquote(value.strip())
  25.437 -    return cookies
  25.438 -
  25.439 -def cookies(*requireds, **defaults):
  25.440 -    r"""Returns a `storage` object with all the request cookies in it.
  25.441 -    
  25.442 -    See `storify` for how `requireds` and `defaults` work.
  25.443 -
  25.444 -    This is forgiving on bad HTTP_COOKIE input, it tries to parse at least
  25.445 -    the cookies it can.
  25.446 -    
  25.447 -    The values are converted to unicode if _unicode=True is passed.
  25.448 -    """
  25.449 -    # If _unicode=True is specified, use decode_cookie to convert cookie value to unicode 
  25.450 -    if defaults.get("_unicode") is True:
  25.451 -        defaults['_unicode'] = decode_cookie
  25.452 -        
  25.453 -    # parse cookie string and cache the result for next time.
  25.454 -    if '_parsed_cookies' not in ctx:
  25.455 -        http_cookie = ctx.env.get("HTTP_COOKIE", "")
  25.456 -        ctx._parsed_cookies = parse_cookies(http_cookie)
  25.457 -
  25.458 -    try:
  25.459 -        return storify(ctx._parsed_cookies, *requireds, **defaults)
  25.460 -    except KeyError:
  25.461 -        badrequest()
  25.462 -        raise StopIteration
  25.463 -
  25.464 -def debug(*args):
  25.465 -    """
  25.466 -    Prints a prettyprinted version of `args` to stderr.
  25.467 -    """
  25.468 -    try: 
  25.469 -        out = ctx.environ['wsgi.errors']
  25.470 -    except: 
  25.471 -        out = sys.stderr
  25.472 -    for arg in args:
  25.473 -        print >> out, pprint.pformat(arg)
  25.474 -    return ''
  25.475 -
  25.476 -def _debugwrite(x):
  25.477 -    try: 
  25.478 -        out = ctx.environ['wsgi.errors']
  25.479 -    except: 
  25.480 -        out = sys.stderr
  25.481 -    out.write(x)
  25.482 -debug.write = _debugwrite
  25.483 -
  25.484 -ctx = context = threadeddict()
  25.485 -
  25.486 -ctx.__doc__ = """
  25.487 -A `storage` object containing various information about the request:
  25.488 -  
  25.489 -`environ` (aka `env`)
  25.490 -   : A dictionary containing the standard WSGI environment variables.
  25.491 -
  25.492 -`host`
  25.493 -   : The domain (`Host` header) requested by the user.
  25.494 -
  25.495 -`home`
  25.496 -   : The base path for the application.
  25.497 -
  25.498 -`ip`
  25.499 -   : The IP address of the requester.
  25.500 -
  25.501 -`method`
  25.502 -   : The HTTP method used.
  25.503 -
  25.504 -`path`
  25.505 -   : The path request.
  25.506 -   
  25.507 -`query`
  25.508 -   : If there are no query arguments, the empty string. Otherwise, a `?` followed
  25.509 -     by the query string.
  25.510 -
  25.511 -`fullpath`
  25.512 -   : The full path requested, including query arguments (`== path + query`).
  25.513 -
  25.514 -### Response Data
  25.515 -
  25.516 -`status` (default: "200 OK")
  25.517 -   : The status code to be used in the response.
  25.518 -
  25.519 -`headers`
  25.520 -   : A list of 2-tuples to be used in the response.
  25.521 -
  25.522 -`output`
  25.523 -   : A string to be used as the response.
  25.524 -"""
  25.525 -
  25.526 -if __name__ == "__main__":
  25.527 -    import doctest
  25.528 -    doctest.testmod()
  25.529 \ No newline at end of file
    26.1 --- a/OpenSecurity/install/web.py-0.37/build/lib/web/webopenid.py	Thu Feb 20 15:40:48 2014 +0100
    26.2 +++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
    26.3 @@ -1,115 +0,0 @@
    26.4 -"""openid.py: an openid library for web.py
    26.5 -
    26.6 -Notes:
    26.7 -
    26.8 - - This will create a file called .openid_secret_key in the 
    26.9 -   current directory with your secret key in it. If someone 
   26.10 -   has access to this file they can log in as any user. And 
   26.11 -   if the app can't find this file for any reason (e.g. you 
   26.12 -   moved the app somewhere else) then each currently logged 
   26.13 -   in user will get logged out.
   26.14 -
   26.15 - - State must be maintained through the entire auth process 
   26.16 -   -- this means that if you have multiple web.py processes 
   26.17 -   serving one set of URLs or if you restart your app often 
   26.18 -   then log ins will fail. You have to replace sessions and 
   26.19 -   store for things to work.
   26.20 -
   26.21 - - We set cookies starting with "openid_".
   26.22 -
   26.23 -"""
   26.24 -
   26.25 -import os
   26.26 -import random
   26.27 -import hmac
   26.28 -import __init__ as web
   26.29 -import openid.consumer.consumer
   26.30 -import openid.store.memstore
   26.31 -
   26.32 -sessions = {}
   26.33 -store = openid.store.memstore.MemoryStore()
   26.34 -
   26.35 -def _secret():
   26.36 -    try:
   26.37 -        secret = file('.openid_secret_key').read()
   26.38 -    except IOError:
   26.39 -        # file doesn't exist
   26.40 -        secret = os.urandom(20)
   26.41 -        file('.openid_secret_key', 'w').write(secret)
   26.42 -    return secret
   26.43 -
   26.44 -def _hmac(identity_url):
   26.45 -    return hmac.new(_secret(), identity_url).hexdigest()
   26.46 -
   26.47 -def _random_session():
   26.48 -    n = random.random()
   26.49 -    while n in sessions:
   26.50 -        n = random.random()
   26.51 -    n = str(n)
   26.52 -    return n
   26.53 -
   26.54 -def status():
   26.55 -    oid_hash = web.cookies().get('openid_identity_hash', '').split(',', 1)
   26.56 -    if len(oid_hash) > 1:
   26.57 -        oid_hash, identity_url = oid_hash
   26.58 -        if oid_hash == _hmac(identity_url):
   26.59 -            return identity_url
   26.60 -    return None
   26.61 -
   26.62 -def form(openid_loc):
   26.63 -    oid = status()
   26.64 -    if oid:
   26.65 -        return '''
   26.66 -        <form method="post" action="%s">
   26.67 -          <img src="http://openid.net/login-bg.gif" alt="OpenID" />
   26.68 -          <strong>%s</strong>
   26.69 -          <input type="hidden" name="action" value="logout" />
   26.70 -          <input type="hidden" name="return_to" value="%s" />
   26.71 -          <button type="submit">log out</button>
   26.72 -        </form>''' % (openid_loc, oid, web.ctx.fullpath)
   26.73 -    else:
   26.74 -        return '''
   26.75 -        <form method="post" action="%s">
   26.76 -          <input type="text" name="openid" value="" 
   26.77 -            style="background: url(http://openid.net/login-bg.gif) no-repeat; padding-left: 18px; background-position: 0 50%%;" />
   26.78 -          <input type="hidden" name="return_to" value="%s" />
   26.79 -          <button type="submit">log in</button>
   26.80 -        </form>''' % (openid_loc, web.ctx.fullpath)
   26.81 -
   26.82 -def logout():
   26.83 -    web.setcookie('openid_identity_hash', '', expires=-1)
   26.84 -
   26.85 -class host:
   26.86 -    def POST(self):
   26.87 -        # unlike the usual scheme of things, the POST is actually called
   26.88 -        # first here
   26.89 -        i = web.input(return_to='/')
   26.90 -        if i.get('action') == 'logout':
   26.91 -            logout()
   26.92 -            return web.redirect(i.return_to)
   26.93 -
   26.94 -        i = web.input('openid', return_to='/')
   26.95 -
   26.96 -        n = _random_session()
   26.97 -        sessions[n] = {'webpy_return_to': i.return_to}
   26.98 -        
   26.99 -        c = openid.consumer.consumer.Consumer(sessions[n], store)
  26.100 -        a = c.begin(i.openid)
  26.101 -        f = a.redirectURL(web.ctx.home, web.ctx.home + web.ctx.fullpath)
  26.102 -
  26.103 -        web.setcookie('openid_session_id', n)
  26.104 -        return web.redirect(f)
  26.105 -
  26.106 -    def GET(self):
  26.107 -        n = web.cookies('openid_session_id').openid_session_id
  26.108 -        web.setcookie('openid_session_id', '', expires=-1)
  26.109 -        return_to = sessions[n]['webpy_return_to']
  26.110 -
  26.111 -        c = openid.consumer.consumer.Consumer(sessions[n], store)
  26.112 -        a = c.complete(web.input(), web.ctx.home + web.ctx.fullpath)
  26.113 -
  26.114 -        if a.status.lower() == 'success':
  26.115 -            web.setcookie('openid_identity_hash', _hmac(a.identity_url) + ',' + a.identity_url)
  26.116 -
  26.117 -        del sessions[n]
  26.118 -        return web.redirect(return_to)
    27.1 --- a/OpenSecurity/install/web.py-0.37/build/lib/web/wsgi.py	Thu Feb 20 15:40:48 2014 +0100
    27.2 +++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
    27.3 @@ -1,70 +0,0 @@
    27.4 -"""
    27.5 -WSGI Utilities
    27.6 -(from web.py)
    27.7 -"""
    27.8 -
    27.9 -import os, sys
   27.10 -
   27.11 -import http
   27.12 -import webapi as web
   27.13 -from utils import listget
   27.14 -from net import validaddr, validip
   27.15 -import httpserver
   27.16 -    
   27.17 -def runfcgi(func, addr=('localhost', 8000)):
   27.18 -    """Runs a WSGI function as a FastCGI server."""
   27.19 -    import flup.server.fcgi as flups
   27.20 -    return flups.WSGIServer(func, multiplexed=True, bindAddress=addr, debug=False).run()
   27.21 -
   27.22 -def runscgi(func, addr=('localhost', 4000)):
   27.23 -    """Runs a WSGI function as an SCGI server."""
   27.24 -    import flup.server.scgi as flups
   27.25 -    return flups.WSGIServer(func, bindAddress=addr, debug=False).run()
   27.26 -
   27.27 -def runwsgi(func):
   27.28 -    """
   27.29 -    Runs a WSGI-compatible `func` using FCGI, SCGI, or a simple web server,
   27.30 -    as appropriate based on context and `sys.argv`.
   27.31 -    """
   27.32 -    
   27.33 -    if os.environ.has_key('SERVER_SOFTWARE'): # cgi
   27.34 -        os.environ['FCGI_FORCE_CGI'] = 'Y'
   27.35 -
   27.36 -    if (os.environ.has_key('PHP_FCGI_CHILDREN') #lighttpd fastcgi
   27.37 -      or os.environ.has_key('SERVER_SOFTWARE')):
   27.38 -        return runfcgi(func, None)
   27.39 -    
   27.40 -    if 'fcgi' in sys.argv or 'fastcgi' in sys.argv:
   27.41 -        args = sys.argv[1:]
   27.42 -        if 'fastcgi' in args: args.remove('fastcgi')
   27.43 -        elif 'fcgi' in args: args.remove('fcgi')
   27.44 -        if args:
   27.45 -            return runfcgi(func, validaddr(args[0]))
   27.46 -        else:
   27.47 -            return runfcgi(func, None)
   27.48 -    
   27.49 -    if 'scgi' in sys.argv:
   27.50 -        args = sys.argv[1:]
   27.51 -        args.remove('scgi')
   27.52 -        if args:
   27.53 -            return runscgi(func, validaddr(args[0]))
   27.54 -        else:
   27.55 -            return runscgi(func)
   27.56 -    
   27.57 -    return httpserver.runsimple(func, validip(listget(sys.argv, 1, '')))
   27.58 -    
   27.59 -def _is_dev_mode():
   27.60 -    # Some embedded python interpreters won't have sys.arv
   27.61 -    # For details, see https://github.com/webpy/webpy/issues/87
   27.62 -    argv = getattr(sys, "argv", [])
   27.63 -
   27.64 -    # quick hack to check if the program is running in dev mode.
   27.65 -    if os.environ.has_key('SERVER_SOFTWARE') \
   27.66 -        or os.environ.has_key('PHP_FCGI_CHILDREN') \
   27.67 -        or 'fcgi' in argv or 'fastcgi' in argv \
   27.68 -        or 'mod_wsgi' in argv:
   27.69 -            return False
   27.70 -    return True
   27.71 -
   27.72 -# When running the builtin-server, enable debug mode if not already set.
   27.73 -web.config.setdefault('debug', _is_dev_mode())
    28.1 --- a/OpenSecurity/install/web.py-0.37/build/lib/web/wsgiserver/__init__.py	Thu Feb 20 15:40:48 2014 +0100
    28.2 +++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
    28.3 @@ -1,2219 +0,0 @@
    28.4 -"""A high-speed, production ready, thread pooled, generic HTTP server.
    28.5 -
    28.6 -Simplest example on how to use this module directly
    28.7 -(without using CherryPy's application machinery)::
    28.8 -
    28.9 -    from cherrypy import wsgiserver
   28.10 -    
   28.11 -    def my_crazy_app(environ, start_response):
   28.12 -        status = '200 OK'
   28.13 -        response_headers = [('Content-type','text/plain')]
   28.14 -        start_response(status, response_headers)
   28.15 -        return ['Hello world!']
   28.16 -    
   28.17 -    server = wsgiserver.CherryPyWSGIServer(
   28.18 -                ('0.0.0.0', 8070), my_crazy_app,
   28.19 -                server_name='www.cherrypy.example')
   28.20 -    server.start()
   28.21 -    
   28.22 -The CherryPy WSGI server can serve as many WSGI applications 
   28.23 -as you want in one instance by using a WSGIPathInfoDispatcher::
   28.24 -    
   28.25 -    d = WSGIPathInfoDispatcher({'/': my_crazy_app, '/blog': my_blog_app})
   28.26 -    server = wsgiserver.CherryPyWSGIServer(('0.0.0.0', 80), d)
   28.27 -    
   28.28 -Want SSL support? Just set server.ssl_adapter to an SSLAdapter instance.
   28.29 -
   28.30 -This won't call the CherryPy engine (application side) at all, only the
   28.31 -HTTP server, which is independent from the rest of CherryPy. Don't
   28.32 -let the name "CherryPyWSGIServer" throw you; the name merely reflects
   28.33 -its origin, not its coupling.
   28.34 -
   28.35 -For those of you wanting to understand internals of this module, here's the
   28.36 -basic call flow. The server's listening thread runs a very tight loop,
   28.37 -sticking incoming connections onto a Queue::
   28.38 -
   28.39 -    server = CherryPyWSGIServer(...)
   28.40 -    server.start()
   28.41 -    while True:
   28.42 -        tick()
   28.43 -        # This blocks until a request comes in:
   28.44 -        child = socket.accept()
   28.45 -        conn = HTTPConnection(child, ...)
   28.46 -        server.requests.put(conn)
   28.47 -
   28.48 -Worker threads are kept in a pool and poll the Queue, popping off and then
   28.49 -handling each connection in turn. Each connection can consist of an arbitrary
   28.50 -number of requests and their responses, so we run a nested loop::
   28.51 -
   28.52 -    while True:
   28.53 -        conn = server.requests.get()
   28.54 -        conn.communicate()
   28.55 -        ->  while True:
   28.56 -                req = HTTPRequest(...)
   28.57 -                req.parse_request()
   28.58 -                ->  # Read the Request-Line, e.g. "GET /page HTTP/1.1"
   28.59 -                    req.rfile.readline()
   28.60 -                    read_headers(req.rfile, req.inheaders)
   28.61 -                req.respond()
   28.62 -                ->  response = app(...)
   28.63 -                    try:
   28.64 -                        for chunk in response:
   28.65 -                            if chunk:
   28.66 -                                req.write(chunk)
   28.67 -                    finally:
   28.68 -                        if hasattr(response, "close"):
   28.69 -                            response.close()
   28.70 -                if req.close_connection:
   28.71 -                    return
   28.72 -"""
   28.73 -
   28.74 -CRLF = '\r\n'
   28.75 -import os
   28.76 -import Queue
   28.77 -import re
   28.78 -quoted_slash = re.compile("(?i)%2F")
   28.79 -import rfc822
   28.80 -import socket
   28.81 -import sys
   28.82 -if 'win' in sys.platform and not hasattr(socket, 'IPPROTO_IPV6'):
   28.83 -    socket.IPPROTO_IPV6 = 41
   28.84 -try:
   28.85 -    import cStringIO as StringIO
   28.86 -except ImportError:
   28.87 -    import StringIO
   28.88 -DEFAULT_BUFFER_SIZE = -1
   28.89 -
   28.90 -_fileobject_uses_str_type = isinstance(socket._fileobject(None)._rbuf, basestring)
   28.91 -
   28.92 -import threading
   28.93 -import time
   28.94 -import traceback
   28.95 -def format_exc(limit=None):
   28.96 -    """Like print_exc() but return a string. Backport for Python 2.3."""
   28.97 -    try:
   28.98 -        etype, value, tb = sys.exc_info()
   28.99 -        return ''.join(traceback.format_exception(etype, value, tb, limit))
  28.100 -    finally:
  28.101 -        etype = value = tb = None
  28.102 -
  28.103 -
  28.104 -from urllib import unquote
  28.105 -from urlparse import urlparse
  28.106 -import warnings
  28.107 -
  28.108 -import errno
  28.109 -
  28.110 -def plat_specific_errors(*errnames):
  28.111 -    """Return error numbers for all errors in errnames on this platform.
  28.112 -    
  28.113 -    The 'errno' module contains different global constants depending on
  28.114 -    the specific platform (OS). This function will return the list of
  28.115 -    numeric values for a given list of potential names.
  28.116 -    """
  28.117 -    errno_names = dir(errno)
  28.118 -    nums = [getattr(errno, k) for k in errnames if k in errno_names]
  28.119 -    # de-dupe the list
  28.120 -    return dict.fromkeys(nums).keys()
  28.121 -
  28.122 -socket_error_eintr = plat_specific_errors("EINTR", "WSAEINTR")
  28.123 -
  28.124 -socket_errors_to_ignore = plat_specific_errors(
  28.125 -    "EPIPE",
  28.126 -    "EBADF", "WSAEBADF",
  28.127 -    "ENOTSOCK", "WSAENOTSOCK",
  28.128 -    "ETIMEDOUT", "WSAETIMEDOUT",
  28.129 -    "ECONNREFUSED", "WSAECONNREFUSED",
  28.130 -    "ECONNRESET", "WSAECONNRESET",
  28.131 -    "ECONNABORTED", "WSAECONNABORTED",
  28.132 -    "ENETRESET", "WSAENETRESET",
  28.133 -    "EHOSTDOWN", "EHOSTUNREACH",
  28.134 -    )
  28.135 -socket_errors_to_ignore.append("timed out")
  28.136 -socket_errors_to_ignore.append("The read operation timed out")
  28.137 -
  28.138 -socket_errors_nonblocking = plat_specific_errors(
  28.139 -    'EAGAIN', 'EWOULDBLOCK', 'WSAEWOULDBLOCK')
  28.140 -
  28.141 -comma_separated_headers = ['Accept', 'Accept-Charset', 'Accept-Encoding',
  28.142 -    'Accept-Language', 'Accept-Ranges', 'Allow', 'Cache-Control',
  28.143 -    'Connection', 'Content-Encoding', 'Content-Language', 'Expect',
  28.144 -    'If-Match', 'If-None-Match', 'Pragma', 'Proxy-Authenticate', 'TE',
  28.145 -    'Trailer', 'Transfer-Encoding', 'Upgrade', 'Vary', 'Via', 'Warning',
  28.146 -    'WWW-Authenticate']
  28.147 -
  28.148 -
  28.149 -import logging
  28.150 -if not hasattr(logging, 'statistics'): logging.statistics = {}
  28.151 -
  28.152 -
  28.153 -def read_headers(rfile, hdict=None):
  28.154 -    """Read headers from the given stream into the given header dict.
  28.155 -    
  28.156 -    If hdict is None, a new header dict is created. Returns the populated
  28.157 -    header dict.
  28.158 -    
  28.159 -    Headers which are repeated are folded together using a comma if their
  28.160 -    specification so dictates.
  28.161 -    
  28.162 -    This function raises ValueError when the read bytes violate the HTTP spec.
  28.163 -    You should probably return "400 Bad Request" if this happens.
  28.164 -    """
  28.165 -    if hdict is None:
  28.166 -        hdict = {}
  28.167 -    
  28.168 -    while True:
  28.169 -        line = rfile.readline()
  28.170 -        if not line:
  28.171 -            # No more data--illegal end of headers
  28.172 -            raise ValueError("Illegal end of headers.")
  28.173 -        
  28.174 -        if line == CRLF:
  28.175 -            # Normal end of headers
  28.176 -            break
  28.177 -        if not line.endswith(CRLF):
  28.178 -            raise ValueError("HTTP requires CRLF terminators")
  28.179 -        
  28.180 -        if line[0] in ' \t':
  28.181 -            # It's a continuation line.
  28.182 -            v = line.strip()
  28.183 -        else:
  28.184 -            try:
  28.185 -                k, v = line.split(":", 1)
  28.186 -            except ValueError:
  28.187 -                raise ValueError("Illegal header line.")
  28.188 -            # TODO: what about TE and WWW-Authenticate?
  28.189 -            k = k.strip().title()
  28.190 -            v = v.strip()
  28.191 -            hname = k
  28.192 -        
  28.193 -        if k in comma_separated_headers:
  28.194 -            existing = hdict.get(hname)
  28.195 -            if existing:
  28.196 -                v = ", ".join((existing, v))
  28.197 -        hdict[hname] = v
  28.198 -    
  28.199 -    return hdict
  28.200 -
  28.201 -
  28.202 -class MaxSizeExceeded(Exception):
  28.203 -    pass
  28.204 -
  28.205 -class SizeCheckWrapper(object):
  28.206 -    """Wraps a file-like object, raising MaxSizeExceeded if too large."""
  28.207 -    
  28.208 -    def __init__(self, rfile, maxlen):
  28.209 -        self.rfile = rfile
  28.210 -        self.maxlen = maxlen
  28.211 -        self.bytes_read = 0
  28.212 -    
  28.213 -    def _check_length(self):
  28.214 -        if self.maxlen and self.bytes_read > self.maxlen:
  28.215 -            raise MaxSizeExceeded()
  28.216 -    
  28.217 -    def read(self, size=None):
  28.218 -        data = self.rfile.read(size)
  28.219 -        self.bytes_read += len(data)
  28.220 -        self._check_length()
  28.221 -        return data
  28.222 -    
  28.223 -    def readline(self, size=None):
  28.224 -        if size is not None:
  28.225 -            data = self.rfile.readline(size)
  28.226 -            self.bytes_read += len(data)
  28.227 -            self._check_length()
  28.228 -            return data
  28.229 -        
  28.230 -        # User didn't specify a size ...
  28.231 -        # We read the line in chunks to make sure it's not a 100MB line !
  28.232 -        res = []
  28.233 -        while True:
  28.234 -            data = self.rfile.readline(256)
  28.235 -            self.bytes_read += len(data)
  28.236 -            self._check_length()
  28.237 -            res.append(data)
  28.238 -            # See http://www.cherrypy.org/ticket/421
  28.239 -            if len(data) < 256 or data[-1:] == "\n":
  28.240 -                return ''.join(res)
  28.241 -    
  28.242 -    def readlines(self, sizehint=0):
  28.243 -        # Shamelessly stolen from StringIO
  28.244 -        total = 0
  28.245 -        lines = []
  28.246 -        line = self.readline()
  28.247 -        while line:
  28.248 -            lines.append(line)
  28.249 -            total += len(line)
  28.250 -            if 0 < sizehint <= total:
  28.251 -                break
  28.252 -            line = self.readline()
  28.253 -        return lines
  28.254 -    
  28.255 -    def close(self):
  28.256 -        self.rfile.close()
  28.257 -    
  28.258 -    def __iter__(self):
  28.259 -        return self
  28.260 -    
  28.261 -    def next(self):
  28.262 -        data = self.rfile.next()
  28.263 -        self.bytes_read += len(data)
  28.264 -        self._check_length()
  28.265 -        return data
  28.266 -
  28.267 -
  28.268 -class KnownLengthRFile(object):
  28.269 -    """Wraps a file-like object, returning an empty string when exhausted."""
  28.270 -    
  28.271 -    def __init__(self, rfile, content_length):
  28.272 -        self.rfile = rfile
  28.273 -        self.remaining = content_length
  28.274 -    
  28.275 -    def read(self, size=None):
  28.276 -        if self.remaining == 0:
  28.277 -            return ''
  28.278 -        if size is None:
  28.279 -            size = self.remaining
  28.280 -        else:
  28.281 -            size = min(size, self.remaining)
  28.282 -        
  28.283 -        data = self.rfile.read(size)
  28.284 -        self.remaining -= len(data)
  28.285 -        return data
  28.286 -    
  28.287 -    def readline(self, size=None):
  28.288 -        if self.remaining == 0:
  28.289 -            return ''
  28.290 -        if size is None:
  28.291 -            size = self.remaining
  28.292 -        else:
  28.293 -            size = min(size, self.remaining)
  28.294 -        
  28.295 -        data = self.rfile.readline(size)
  28.296 -        self.remaining -= len(data)
  28.297 -        return data
  28.298 -    
  28.299 -    def readlines(self, sizehint=0):
  28.300 -        # Shamelessly stolen from StringIO
  28.301 -        total = 0
  28.302 -        lines = []
  28.303 -        line = self.readline(sizehint)
  28.304 -        while line:
  28.305 -            lines.append(line)
  28.306 -            total += len(line)
  28.307 -            if 0 < sizehint <= total:
  28.308 -                break
  28.309 -            line = self.readline(sizehint)
  28.310 -        return lines
  28.311 -    
  28.312 -    def close(self):
  28.313 -        self.rfile.close()
  28.314 -    
  28.315 -    def __iter__(self):
  28.316 -        return self
  28.317 -    
  28.318 -    def __next__(self):
  28.319 -        data = next(self.rfile)
  28.320 -        self.remaining -= len(data)
  28.321 -        return data
  28.322 -
  28.323 -
  28.324 -class ChunkedRFile(object):
  28.325 -    """Wraps a file-like object, returning an empty string when exhausted.
  28.326 -    
  28.327 -    This class is intended to provide a conforming wsgi.input value for
  28.328 -    request entities that have been encoded with the 'chunked' transfer
  28.329 -    encoding.
  28.330 -    """
  28.331 -    
  28.332 -    def __init__(self, rfile, maxlen, bufsize=8192):
  28.333 -        self.rfile = rfile
  28.334 -        self.maxlen = maxlen
  28.335 -        self.bytes_read = 0
  28.336 -        self.buffer = ''
  28.337 -        self.bufsize = bufsize
  28.338 -        self.closed = False
  28.339 -    
  28.340 -    def _fetch(self):
  28.341 -        if self.closed:
  28.342 -            return
  28.343 -        
  28.344 -        line = self.rfile.readline()
  28.345 -        self.bytes_read += len(line)
  28.346 -        
  28.347 -        if self.maxlen and self.bytes_read > self.maxlen:
  28.348 -            raise MaxSizeExceeded("Request Entity Too Large", self.maxlen)
  28.349 -        
  28.350 -        line = line.strip().split(";", 1)
  28.351 -        
  28.352 -        try:
  28.353 -            chunk_size = line.pop(0)
  28.354 -            chunk_size = int(chunk_size, 16)
  28.355 -        except ValueError:
  28.356 -            raise ValueError("Bad chunked transfer size: " + repr(chunk_size))
  28.357 -        
  28.358 -        if chunk_size <= 0:
  28.359 -            self.closed = True
  28.360 -            return
  28.361 -        
  28.362 -##            if line: chunk_extension = line[0]
  28.363 -        
  28.364 -        if self.maxlen and self.bytes_read + chunk_size > self.maxlen:
  28.365 -            raise IOError("Request Entity Too Large")
  28.366 -        
  28.367 -        chunk = self.rfile.read(chunk_size)
  28.368 -        self.bytes_read += len(chunk)
  28.369 -        self.buffer += chunk
  28.370 -        
  28.371 -        crlf = self.rfile.read(2)
  28.372 -        if crlf != CRLF:
  28.373 -            raise ValueError(
  28.374 -                 "Bad chunked transfer coding (expected '\\r\\n', "
  28.375 -                 "got " + repr(crlf) + ")")
  28.376 -    
  28.377 -    def read(self, size=None):
  28.378 -        data = ''
  28.379 -        while True:
  28.380 -            if size and len(data) >= size:
  28.381 -                return data
  28.382 -            
  28.383 -            if not self.buffer:
  28.384 -                self._fetch()
  28.385 -                if not self.buffer:
  28.386 -                    # EOF
  28.387 -                    return data
  28.388 -            
  28.389 -            if size:
  28.390 -                remaining = size - len(data)
  28.391 -                data += self.buffer[:remaining]
  28.392 -                self.buffer = self.buffer[remaining:]
  28.393 -            else:
  28.394 -                data += self.buffer
  28.395 -    
  28.396 -    def readline(self, size=None):
  28.397 -        data = ''
  28.398 -        while True:
  28.399 -            if size and len(data) >= size:
  28.400 -                return data
  28.401 -            
  28.402 -            if not self.buffer:
  28.403 -                self._fetch()
  28.404 -                if not self.buffer:
  28.405 -                    # EOF
  28.406 -                    return data
  28.407 -            
  28.408 -            newline_pos = self.buffer.find('\n')
  28.409 -            if size:
  28.410 -                if newline_pos == -1:
  28.411 -                    remaining = size - len(data)
  28.412 -                    data += self.buffer[:remaining]
  28.413 -                    self.buffer = self.buffer[remaining:]
  28.414 -                else:
  28.415 -                    remaining = min(size - len(data), newline_pos)
  28.416 -                    data += self.buffer[:remaining]
  28.417 -                    self.buffer = self.buffer[remaining:]
  28.418 -            else:
  28.419 -                if newline_pos == -1:
  28.420 -                    data += self.buffer
  28.421 -                else:
  28.422 -                    data += self.buffer[:newline_pos]
  28.423 -                    self.buffer = self.buffer[newline_pos:]
  28.424 -    
  28.425 -    def readlines(self, sizehint=0):
  28.426 -        # Shamelessly stolen from StringIO
  28.427 -        total = 0
  28.428 -        lines = []
  28.429 -        line = self.readline(sizehint)
  28.430 -        while line:
  28.431 -            lines.append(line)
  28.432 -            total += len(line)
  28.433 -            if 0 < sizehint <= total:
  28.434 -                break
  28.435 -            line = self.readline(sizehint)
  28.436 -        return lines
  28.437 -    
  28.438 -    def read_trailer_lines(self):
  28.439 -        if not self.closed:
  28.440 -            raise ValueError(
  28.441 -                "Cannot read trailers until the request body has been read.")
  28.442 -        
  28.443 -        while True:
  28.444 -            line = self.rfile.readline()
  28.445 -            if not line:
  28.446 -                # No more data--illegal end of headers
  28.447 -                raise ValueError("Illegal end of headers.")
  28.448 -            
  28.449 -            self.bytes_read += len(line)
  28.450 -            if self.maxlen and self.bytes_read > self.maxlen:
  28.451 -                raise IOError("Request Entity Too Large")
  28.452 -            
  28.453 -            if line == CRLF:
  28.454 -                # Normal end of headers
  28.455 -                break
  28.456 -            if not line.endswith(CRLF):
  28.457 -                raise ValueError("HTTP requires CRLF terminators")
  28.458 -            
  28.459 -            yield line
  28.460 -    
  28.461 -    def close(self):
  28.462 -        self.rfile.close()
  28.463 -    
  28.464 -    def __iter__(self):
  28.465 -        # Shamelessly stolen from StringIO
  28.466 -        total = 0
  28.467 -        line = self.readline(sizehint)
  28.468 -        while line:
  28.469 -            yield line
  28.470 -            total += len(line)
  28.471 -            if 0 < sizehint <= total:
  28.472 -                break
  28.473 -            line = self.readline(sizehint)
  28.474 -
  28.475 -
  28.476 -class HTTPRequest(object):
  28.477 -    """An HTTP Request (and response).
  28.478 -    
  28.479 -    A single HTTP connection may consist of multiple request/response pairs.
  28.480 -    """
  28.481 -    
  28.482 -    server = None
  28.483 -    """The HTTPServer object which is receiving this request."""
  28.484 -    
  28.485 -    conn = None
  28.486 -    """The HTTPConnection object on which this request connected."""
  28.487 -    
  28.488 -    inheaders = {}
  28.489 -    """A dict of request headers."""
  28.490 -    
  28.491 -    outheaders = []
  28.492 -    """A list of header tuples to write in the response."""
  28.493 -    
  28.494 -    ready = False
  28.495 -    """When True, the request has been parsed and is ready to begin generating
  28.496 -    the response. When False, signals the calling Connection that the response
  28.497 -    should not be generated and the connection should close."""
  28.498 -    
  28.499 -    close_connection = False
  28.500 -    """Signals the calling Connection that the request should close. This does
  28.501 -    not imply an error! The client and/or server may each request that the
  28.502 -    connection be closed."""
  28.503 -    
  28.504 -    chunked_write = False
  28.505 -    """If True, output will be encoded with the "chunked" transfer-coding.
  28.506 -    
  28.507 -    This value is set automatically inside send_headers."""
  28.508 -    
  28.509 -    def __init__(self, server, conn):
  28.510 -        self.server= server
  28.511 -        self.conn = conn
  28.512 -        
  28.513 -        self.ready = False
  28.514 -        self.started_request = False
  28.515 -        self.scheme = "http"
  28.516 -        if self.server.ssl_adapter is not None:
  28.517 -            self.scheme = "https"
  28.518 -        # Use the lowest-common protocol in case read_request_line errors.
  28.519 -        self.response_protocol = 'HTTP/1.0'
  28.520 -        self.inheaders = {}
  28.521 -        
  28.522 -        self.status = ""
  28.523 -        self.outheaders = []
  28.524 -        self.sent_headers = False
  28.525 -        self.close_connection = self.__class__.close_connection
  28.526 -        self.chunked_read = False
  28.527 -        self.chunked_write = self.__class__.chunked_write
  28.528 -    
  28.529 -    def parse_request(self):
  28.530 -        """Parse the next HTTP request start-line and message-headers."""
  28.531 -        self.rfile = SizeCheckWrapper(self.conn.rfile,
  28.532 -                                      self.server.max_request_header_size)
  28.533 -        try:
  28.534 -            self.read_request_line()
  28.535 -        except MaxSizeExceeded:
  28.536 -            self.simple_response("414 Request-URI Too Long",
  28.537 -                "The Request-URI sent with the request exceeds the maximum "
  28.538 -                "allowed bytes.")
  28.539 -            return
  28.540 -        
  28.541 -        try:
  28.542 -            success = self.read_request_headers()
  28.543 -        except MaxSizeExceeded:
  28.544 -            self.simple_response("413 Request Entity Too Large",
  28.545 -                "The headers sent with the request exceed the maximum "
  28.546 -                "allowed bytes.")
  28.547 -            return
  28.548 -        else:
  28.549 -            if not success:
  28.550 -                return
  28.551 -        
  28.552 -        self.ready = True
  28.553 -    
  28.554 -    def read_request_line(self):
  28.555 -        # HTTP/1.1 connections are persistent by default. If a client
  28.556 -        # requests a page, then idles (leaves the connection open),
  28.557 -        # then rfile.readline() will raise socket.error("timed out").
  28.558 -        # Note that it does this based on the value given to settimeout(),
  28.559 -        # and doesn't need the client to request or acknowledge the close
  28.560 -        # (although your TCP stack might suffer for it: cf Apache's history
  28.561 -        # with FIN_WAIT_2).
  28.562 -        request_line = self.rfile.readline()
  28.563 -        
  28.564 -        # Set started_request to True so communicate() knows to send 408
  28.565 -        # from here on out.
  28.566 -        self.started_request = True
  28.567 -        if not request_line:
  28.568 -            # Force self.ready = False so the connection will close.
  28.569 -            self.ready = False
  28.570 -            return
  28.571 -        
  28.572 -        if request_line == CRLF:
  28.573 -            # RFC 2616 sec 4.1: "...if the server is reading the protocol
  28.574 -            # stream at the beginning of a message and receives a CRLF
  28.575 -            # first, it should ignore the CRLF."
  28.576 -            # But only ignore one leading line! else we enable a DoS.
  28.577 -            request_line = self.rfile.readline()
  28.578 -            if not request_line:
  28.579 -                self.ready = False
  28.580 -                return
  28.581 -        
  28.582 -        if not request_line.endswith(CRLF):
  28.583 -            self.simple_response("400 Bad Request", "HTTP requires CRLF terminators")
  28.584 -            return
  28.585 -        
  28.586 -        try:
  28.587 -            method, uri, req_protocol = request_line.strip().split(" ", 2)
  28.588 -            rp = int(req_protocol[5]), int(req_protocol[7])
  28.589 -        except (ValueError, IndexError):
  28.590 -            self.simple_response("400 Bad Request", "Malformed Request-Line")
  28.591 -            return
  28.592 -        
  28.593 -        self.uri = uri
  28.594 -        self.method = method
  28.595 -        
  28.596 -        # uri may be an abs_path (including "http://host.domain.tld");
  28.597 -        scheme, authority, path = self.parse_request_uri(uri)
  28.598 -        if '#' in path:
  28.599 -            self.simple_response("400 Bad Request",
  28.600 -                                 "Illegal #fragment in Request-URI.")
  28.601 -            return
  28.602 -        
  28.603 -        if scheme:
  28.604 -            self.scheme = scheme
  28.605 -        
  28.606 -        qs = ''
  28.607 -        if '?' in path:
  28.608 -            path, qs = path.split('?', 1)
  28.609 -        
  28.610 -        # Unquote the path+params (e.g. "/this%20path" -> "/this path").
  28.611 -        # http://www.w3.org/Protocols/rfc2616/rfc2616-sec5.html#sec5.1.2
  28.612 -        #
  28.613 -        # But note that "...a URI must be separated into its components
  28.614 -        # before the escaped characters within those components can be
  28.615 -        # safely decoded." http://www.ietf.org/rfc/rfc2396.txt, sec 2.4.2
  28.616 -        # Therefore, "/this%2Fpath" becomes "/this%2Fpath", not "/this/path".
  28.617 -        try:
  28.618 -            atoms = [unquote(x) for x in quoted_slash.split(path)]
  28.619 -        except ValueError, ex:
  28.620 -            self.simple_response("400 Bad Request", ex.args[0])
  28.621 -            return
  28.622 -        path = "%2F".join(atoms)
  28.623 -        self.path = path
  28.624 -        
  28.625 -        # Note that, like wsgiref and most other HTTP servers,
  28.626 -        # we "% HEX HEX"-unquote the path but not the query string.
  28.627 -        self.qs = qs
  28.628 -        
  28.629 -        # Compare request and server HTTP protocol versions, in case our
  28.630 -        # server does not support the requested protocol. Limit our output
  28.631 -        # to min(req, server). We want the following output:
  28.632 -        #     request    server     actual written   supported response
  28.633 -        #     protocol   protocol  response protocol    feature set
  28.634 -        # a     1.0        1.0           1.0                1.0
  28.635 -        # b     1.0        1.1           1.1                1.0
  28.636 -        # c     1.1        1.0           1.0                1.0
  28.637 -        # d     1.1        1.1           1.1                1.1
  28.638 -        # Notice that, in (b), the response will be "HTTP/1.1" even though
  28.639 -        # the client only understands 1.0. RFC 2616 10.5.6 says we should
  28.640 -        # only return 505 if the _major_ version is different.
  28.641 -        sp = int(self.server.protocol[5]), int(self.server.protocol[7])
  28.642 -        
  28.643 -        if sp[0] != rp[0]:
  28.644 -            self.simple_response("505 HTTP Version Not Supported")
  28.645 -            return
  28.646 -        self.request_protocol = req_protocol
  28.647 -        self.response_protocol = "HTTP/%s.%s" % min(rp, sp)
  28.648 -    
  28.649 -    def read_request_headers(self):
  28.650 -        """Read self.rfile into self.inheaders. Return success."""
  28.651 -        
  28.652 -        # then all the http headers
  28.653 -        try:
  28.654 -            read_headers(self.rfile, self.inheaders)
  28.655 -        except ValueError, ex:
  28.656 -            self.simple_response("400 Bad Request", ex.args[0])
  28.657 -            return False
  28.658 -        
  28.659 -        mrbs = self.server.max_request_body_size
  28.660 -        if mrbs and int(self.inheaders.get("Content-Length", 0)) > mrbs:
  28.661 -            self.simple_response("413 Request Entity Too Large",
  28.662 -                "The entity sent with the request exceeds the maximum "
  28.663 -                "allowed bytes.")
  28.664 -            return False
  28.665 -        
  28.666 -        # Persistent connection support
  28.667 -        if self.response_protocol == "HTTP/1.1":
  28.668 -            # Both server and client are HTTP/1.1
  28.669 -            if self.inheaders.get("Connection", "") == "close":
  28.670 -                self.close_connection = True
  28.671 -        else:
  28.672 -            # Either the server or client (or both) are HTTP/1.0
  28.673 -            if self.inheaders.get("Connection", "") != "Keep-Alive":
  28.674 -                self.close_connection = True
  28.675 -        
  28.676 -        # Transfer-Encoding support
  28.677 -        te = None
  28.678 -        if self.response_protocol == "HTTP/1.1":
  28.679 -            te = self.inheaders.get("Transfer-Encoding")
  28.680 -            if te:
  28.681 -                te = [x.strip().lower() for x in te.split(",") if x.strip()]
  28.682 -        
  28.683 -        self.chunked_read = False
  28.684 -        
  28.685 -        if te:
  28.686 -            for enc in te:
  28.687 -                if enc == "chunked":
  28.688 -                    self.chunked_read = True
  28.689 -                else:
  28.690 -                    # Note that, even if we see "chunked", we must reject
  28.691 -                    # if there is an extension we don't recognize.
  28.692 -                    self.simple_response("501 Unimplemented")
  28.693 -                    self.close_connection = True
  28.694 -                    return False
  28.695 -        
  28.696 -        # From PEP 333:
  28.697 -        # "Servers and gateways that implement HTTP 1.1 must provide
  28.698 -        # transparent support for HTTP 1.1's "expect/continue" mechanism.
  28.699 -        # This may be done in any of several ways:
  28.700 -        #   1. Respond to requests containing an Expect: 100-continue request
  28.701 -        #      with an immediate "100 Continue" response, and proceed normally.
  28.702 -        #   2. Proceed with the request normally, but provide the application
  28.703 -        #      with a wsgi.input stream that will send the "100 Continue"
  28.704 -        #      response if/when the application first attempts to read from
  28.705 -        #      the input stream. The read request must then remain blocked
  28.706 -        #      until the client responds.
  28.707 -        #   3. Wait until the client decides that the server does not support
  28.708 -        #      expect/continue, and sends the request body on its own.
  28.709 -        #      (This is suboptimal, and is not recommended.)
  28.710 -        #
  28.711 -        # We used to do 3, but are now doing 1. Maybe we'll do 2 someday,
  28.712 -        # but it seems like it would be a big slowdown for such a rare case.
  28.713 -        if self.inheaders.get("Expect", "") == "100-continue":
  28.714 -            # Don't use simple_response here, because it emits headers
  28.715 -            # we don't want. See http://www.cherrypy.org/ticket/951
  28.716 -            msg = self.server.protocol + " 100 Continue\r\n\r\n"
  28.717 -            try:
  28.718 -                self.conn.wfile.sendall(msg)
  28.719 -            except socket.error, x:
  28.720 -                if x.args[0] not in socket_errors_to_ignore:
  28.721 -                    raise
  28.722 -        return True
  28.723 -    
  28.724 -    def parse_request_uri(self, uri):
  28.725 -        """Parse a Request-URI into (scheme, authority, path).
  28.726 -        
  28.727 -        Note that Request-URI's must be one of::
  28.728 -            
  28.729 -            Request-URI    = "*" | absoluteURI | abs_path | authority
  28.730 -        
  28.731 -        Therefore, a Request-URI which starts with a double forward-slash
  28.732 -        cannot be a "net_path"::
  28.733 -        
  28.734 -            net_path      = "//" authority [ abs_path ]
  28.735 -        
  28.736 -        Instead, it must be interpreted as an "abs_path" with an empty first
  28.737 -        path segment::
  28.738 -        
  28.739 -            abs_path      = "/"  path_segments
  28.740 -            path_segments = segment *( "/" segment )
  28.741 -            segment       = *pchar *( ";" param )
  28.742 -            param         = *pchar
  28.743 -        """
  28.744 -        if uri == "*":
  28.745 -            return None, None, uri
  28.746 -        
  28.747 -        i = uri.find('://')
  28.748 -        if i > 0 and '?' not in uri[:i]:
  28.749 -            # An absoluteURI.
  28.750 -            # If there's a scheme (and it must be http or https), then:
  28.751 -            # http_URL = "http:" "//" host [ ":" port ] [ abs_path [ "?" query ]]
  28.752 -            scheme, remainder = uri[:i].lower(), uri[i + 3:]
  28.753 -            authority, path = remainder.split("/", 1)
  28.754 -            return scheme, authority, path
  28.755 -        
  28.756 -        if uri.startswith('/'):
  28.757 -            # An abs_path.
  28.758 -            return None, None, uri
  28.759 -        else:
  28.760 -            # An authority.
  28.761 -            return None, uri, None
  28.762 -    
  28.763 -    def respond(self):
  28.764 -        """Call the gateway and write its iterable output."""
  28.765 -        mrbs = self.server.max_request_body_size
  28.766 -        if self.chunked_read:
  28.767 -            self.rfile = ChunkedRFile(self.conn.rfile, mrbs)
  28.768 -        else:
  28.769 -            cl = int(self.inheaders.get("Content-Length", 0))
  28.770 -            if mrbs and mrbs < cl:
  28.771 -                if not self.sent_headers:
  28.772 -                    self.simple_response("413 Request Entity Too Large",
  28.773 -                        "The entity sent with the request exceeds the maximum "
  28.774 -                        "allowed bytes.")
  28.775 -                return
  28.776 -            self.rfile = KnownLengthRFile(self.conn.rfile, cl)
  28.777 -        
  28.778 -        self.server.gateway(self).respond()
  28.779 -        
  28.780 -        if (self.ready and not self.sent_headers):
  28.781 -            self.sent_headers = True
  28.782 -            self.send_headers()
  28.783 -        if self.chunked_write:
  28.784 -            self.conn.wfile.sendall("0\r\n\r\n")
  28.785 -    
  28.786 -    def simple_response(self, status, msg=""):
  28.787 -        """Write a simple response back to the client."""
  28.788 -        status = str(status)
  28.789 -        buf = [self.server.protocol + " " +
  28.790 -               status + CRLF,
  28.791 -               "Content-Length: %s\r\n" % len(msg),
  28.792 -               "Content-Type: text/plain\r\n"]
  28.793 -        
  28.794 -        if status[:3] in ("413", "414"):
  28.795 -            # Request Entity Too Large / Request-URI Too Long
  28.796 -            self.close_connection = True
  28.797 -            if self.response_protocol == 'HTTP/1.1':
  28.798 -                # This will not be true for 414, since read_request_line
  28.799 -                # usually raises 414 before reading the whole line, and we
  28.800 -                # therefore cannot know the proper response_protocol.
  28.801 -                buf.append("Connection: close\r\n")
  28.802 -            else:
  28.803 -                # HTTP/1.0 had no 413/414 status nor Connection header.
  28.804 -                # Emit 400 instead and trust the message body is enough.
  28.805 -                status = "400 Bad Request"
  28.806 -        
  28.807 -        buf.append(CRLF)
  28.808 -        if msg:
  28.809 -            if isinstance(msg, unicode):
  28.810 -                msg = msg.encode("ISO-8859-1")
  28.811 -            buf.append(msg)
  28.812 -        
  28.813 -        try:
  28.814 -            self.conn.wfile.sendall("".join(buf))
  28.815 -        except socket.error, x:
  28.816 -            if x.args[0] not in socket_errors_to_ignore:
  28.817 -                raise
  28.818 -    
  28.819 -    def write(self, chunk):
  28.820 -        """Write unbuffered data to the client."""
  28.821 -        if self.chunked_write and chunk:
  28.822 -            buf = [hex(len(chunk))[2:], CRLF, chunk, CRLF]
  28.823 -            self.conn.wfile.sendall("".join(buf))
  28.824 -        else:
  28.825 -            self.conn.wfile.sendall(chunk)
  28.826 -    
  28.827 -    def send_headers(self):
  28.828 -        """Assert, process, and send the HTTP response message-headers.
  28.829 -        
  28.830 -        You must set self.status, and self.outheaders before calling this.
  28.831 -        """
  28.832 -        hkeys = [key.lower() for key, value in self.outheaders]
  28.833 -        status = int(self.status[:3])
  28.834 -        
  28.835 -        if status == 413:
  28.836 -            # Request Entity Too Large. Close conn to avoid garbage.
  28.837 -            self.close_connection = True
  28.838 -        elif "content-length" not in hkeys:
  28.839 -            # "All 1xx (informational), 204 (no content),
  28.840 -            # and 304 (not modified) responses MUST NOT
  28.841 -            # include a message-body." So no point chunking.
  28.842 -            if status < 200 or status in (204, 205, 304):
  28.843 -                pass
  28.844 -            else:
  28.845 -                if (self.response_protocol == 'HTTP/1.1'
  28.846 -                    and self.method != 'HEAD'):
  28.847 -                    # Use the chunked transfer-coding
  28.848 -                    self.chunked_write = True
  28.849 -                    self.outheaders.append(("Transfer-Encoding", "chunked"))
  28.850 -                else:
  28.851 -                    # Closing the conn is the only way to determine len.
  28.852 -                    self.close_connection = True
  28.853 -        
  28.854 -        if "connection" not in hkeys:
  28.855 -            if self.response_protocol == 'HTTP/1.1':
  28.856 -                # Both server and client are HTTP/1.1 or better
  28.857 -                if self.close_connection:
  28.858 -                    self.outheaders.append(("Connection", "close"))
  28.859 -            else:
  28.860 -                # Server and/or client are HTTP/1.0
  28.861 -                if not self.close_connection:
  28.862 -                    self.outheaders.append(("Connection", "Keep-Alive"))
  28.863 -        
  28.864 -        if (not self.close_connection) and (not self.chunked_read):
  28.865 -            # Read any remaining request body data on the socket.
  28.866 -            # "If an origin server receives a request that does not include an
  28.867 -            # Expect request-header field with the "100-continue" expectation,
  28.868 -            # the request includes a request body, and the server responds
  28.869 -            # with a final status code before reading the entire request body
  28.870 -            # from the transport connection, then the server SHOULD NOT close
  28.871 -            # the transport connection until it has read the entire request,
  28.872 -            # or until the client closes the connection. Otherwise, the client
  28.873 -            # might not reliably receive the response message. However, this
  28.874 -            # requirement is not be construed as preventing a server from
  28.875 -            # defending itself against denial-of-service attacks, or from
  28.876 -            # badly broken client implementations."
  28.877 -            remaining = getattr(self.rfile, 'remaining', 0)
  28.878 -            if remaining > 0:
  28.879 -                self.rfile.read(remaining)
  28.880 -        
  28.881 -        if "date" not in hkeys:
  28.882 -            self.outheaders.append(("Date", rfc822.formatdate()))
  28.883 -        
  28.884 -        if "server" not in hkeys:
  28.885 -            self.outheaders.append(("Server", self.server.server_name))
  28.886 -        
  28.887 -        buf = [self.server.protocol + " " + self.status + CRLF]
  28.888 -        for k, v in self.outheaders:
  28.889 -            buf.append(k + ": " + v + CRLF)
  28.890 -        buf.append(CRLF)
  28.891 -        self.conn.wfile.sendall("".join(buf))
  28.892 -
  28.893 -
  28.894 -class NoSSLError(Exception):
  28.895 -    """Exception raised when a client speaks HTTP to an HTTPS socket."""
  28.896 -    pass
  28.897 -
  28.898 -
  28.899 -class FatalSSLAlert(Exception):
  28.900 -    """Exception raised when the SSL implementation signals a fatal alert."""
  28.901 -    pass
  28.902 -
  28.903 -
  28.904 -class CP_fileobject(socket._fileobject):
  28.905 -    """Faux file object attached to a socket object."""
  28.906 -
  28.907 -    def __init__(self, *args, **kwargs):
  28.908 -        self.bytes_read = 0
  28.909 -        self.bytes_written = 0
  28.910 -        socket._fileobject.__init__(self, *args, **kwargs)
  28.911 -    
  28.912 -    def sendall(self, data):
  28.913 -        """Sendall for non-blocking sockets."""
  28.914 -        while data:
  28.915 -            try:
  28.916 -                bytes_sent = self.send(data)
  28.917 -                data = data[bytes_sent:]
  28.918 -            except socket.error, e:
  28.919 -                if e.args[0] not in socket_errors_nonblocking:
  28.920 -                    raise
  28.921 -
  28.922 -    def send(self, data):
  28.923 -        bytes_sent = self._sock.send(data)
  28.924 -        self.bytes_written += bytes_sent
  28.925 -        return bytes_sent
  28.926 -
  28.927 -    def flush(self):
  28.928 -        if self._wbuf:
  28.929 -            buffer = "".join(self._wbuf)
  28.930 -            self._wbuf = []
  28.931 -            self.sendall(buffer)
  28.932 -
  28.933 -    def recv(self, size):
  28.934 -        while True:
  28.935 -            try:
  28.936 -                data = self._sock.recv(size)
  28.937 -                self.bytes_read += len(data)
  28.938 -                return data
  28.939 -            except socket.error, e:
  28.940 -                if (e.args[0] not in socket_errors_nonblocking
  28.941 -                    and e.args[0] not in socket_error_eintr):
  28.942 -                    raise
  28.943 -
  28.944 -    if not _fileobject_uses_str_type:
  28.945 -        def read(self, size=-1):
  28.946 -            # Use max, disallow tiny reads in a loop as they are very inefficient.
  28.947 -            # We never leave read() with any leftover data from a new recv() call
  28.948 -            # in our internal buffer.
  28.949 -            rbufsize = max(self._rbufsize, self.default_bufsize)
  28.950 -            # Our use of StringIO rather than lists of string objects returned by
  28.951 -            # recv() minimizes memory usage and fragmentation that occurs when
  28.952 -            # rbufsize is large compared to the typical return value of recv().
  28.953 -            buf = self._rbuf
  28.954 -            buf.seek(0, 2)  # seek end
  28.955 -            if size < 0:
  28.956 -                # Read until EOF
  28.957 -                self._rbuf = StringIO.StringIO()  # reset _rbuf.  we consume it via buf.
  28.958 -                while True:
  28.959 -                    data = self.recv(rbufsize)
  28.960 -                    if not data:
  28.961 -                        break
  28.962 -                    buf.write(data)
  28.963 -                return buf.getvalue()
  28.964 -            else:
  28.965 -                # Read until size bytes or EOF seen, whichever comes first
  28.966 -                buf_len = buf.tell()
  28.967 -                if buf_len >= size:
  28.968 -                    # Already have size bytes in our buffer?  Extract and return.
  28.969 -                    buf.seek(0)
  28.970 -                    rv = buf.read(size)
  28.971 -                    self._rbuf = StringIO.StringIO()
  28.972 -                    self._rbuf.write(buf.read())
  28.973 -                    return rv
  28.974 -
  28.975 -                self._rbuf = StringIO.StringIO()  # reset _rbuf.  we consume it via buf.
  28.976 -                while True:
  28.977 -                    left = size - buf_len
  28.978 -                    # recv() will malloc the amount of memory given as its
  28.979 -                    # parameter even though it often returns much less data
  28.980 -                    # than that.  The returned data string is short lived
  28.981 -                    # as we copy it into a StringIO and free it.  This avoids
  28.982 -                    # fragmentation issues on many platforms.
  28.983 -                    data = self.recv(left)
  28.984 -                    if not data:
  28.985 -                        break
  28.986 -                    n = len(data)
  28.987 -                    if n == size and not buf_len:
  28.988 -                        # Shortcut.  Avoid buffer data copies when:
  28.989 -                        # - We have no data in our buffer.
  28.990 -                        # AND
  28.991 -                        # - Our call to recv returned exactly the
  28.992 -                        #   number of bytes we were asked to read.
  28.993 -                        return data
  28.994 -                    if n == left:
  28.995 -                        buf.write(data)
  28.996 -                        del data  # explicit free
  28.997 -                        break
  28.998 -                    assert n <= left, "recv(%d) returned %d bytes" % (left, n)
  28.999 -                    buf.write(data)
 28.1000 -                    buf_len += n
 28.1001 -                    del data  # explicit free
 28.1002 -                    #assert buf_len == buf.tell()
 28.1003 -                return buf.getvalue()
 28.1004 -
 28.1005 -        def readline(self, size=-1):
 28.1006 -            buf = self._rbuf
 28.1007 -            buf.seek(0, 2)  # seek end
 28.1008 -            if buf.tell() > 0:
 28.1009 -                # check if we already have it in our buffer
 28.1010 -                buf.seek(0)
 28.1011 -                bline = buf.readline(size)
 28.1012 -                if bline.endswith('\n') or len(bline) == size:
 28.1013 -                    self._rbuf = StringIO.StringIO()
 28.1014 -                    self._rbuf.write(buf.read())
 28.1015 -                    return bline
 28.1016 -                del bline
 28.1017 -            if size < 0:
 28.1018 -                # Read until \n or EOF, whichever comes first
 28.1019 -                if self._rbufsize <= 1:
 28.1020 -                    # Speed up unbuffered case
 28.1021 -                    buf.seek(0)
 28.1022 -                    buffers = [buf.read()]
 28.1023 -                    self._rbuf = StringIO.StringIO()  # reset _rbuf.  we consume it via buf.
 28.1024 -                    data = None
 28.1025 -                    recv = self.recv
 28.1026 -                    while data != "\n":
 28.1027 -                        data = recv(1)
 28.1028 -                        if not data:
 28.1029 -                            break
 28.1030 -                        buffers.append(data)
 28.1031 -                    return "".join(buffers)
 28.1032 -
 28.1033 -                buf.seek(0, 2)  # seek end
 28.1034 -                self._rbuf = StringIO.StringIO()  # reset _rbuf.  we consume it via buf.
 28.1035 -                while True:
 28.1036 -                    data = self.recv(self._rbufsize)
 28.1037 -                    if not data:
 28.1038 -                        break
 28.1039 -                    nl = data.find('\n')
 28.1040 -                    if nl >= 0:
 28.1041 -                        nl += 1
 28.1042 -                        buf.write(data[:nl])
 28.1043 -                        self._rbuf.write(data[nl:])
 28.1044 -                        del data
 28.1045 -                        break
 28.1046 -                    buf.write(data)
 28.1047 -                return buf.getvalue()
 28.1048 -            else:
 28.1049 -                # Read until size bytes or \n or EOF seen, whichever comes first
 28.1050 -                buf.seek(0, 2)  # seek end
 28.1051 -                buf_len = buf.tell()
 28.1052 -                if buf_len >= size:
 28.1053 -                    buf.seek(0)
 28.1054 -                    rv = buf.read(size)
 28.1055 -                    self._rbuf = StringIO.StringIO()
 28.1056 -                    self._rbuf.write(buf.read())
 28.1057 -                    return rv
 28.1058 -                self._rbuf = StringIO.StringIO()  # reset _rbuf.  we consume it via buf.
 28.1059 -                while True:
 28.1060 -                    data = self.recv(self._rbufsize)
 28.1061 -                    if not data:
 28.1062 -                        break
 28.1063 -                    left = size - buf_len
 28.1064 -                    # did we just receive a newline?
 28.1065 -                    nl = data.find('\n', 0, left)
 28.1066 -                    if nl >= 0:
 28.1067 -                        nl += 1
 28.1068 -                        # save the excess data to _rbuf
 28.1069 -                        self._rbuf.write(data[nl:])
 28.1070 -                        if buf_len:
 28.1071 -                            buf.write(data[:nl])
 28.1072 -                            break
 28.1073 -                        else:
 28.1074 -                            # Shortcut.  Avoid data copy through buf when returning
 28.1075 -                            # a substring of our first recv().
 28.1076 -                            return data[:nl]
 28.1077 -                    n = len(data)
 28.1078 -                    if n == size and not buf_len:
 28.1079 -                        # Shortcut.  Avoid data copy through buf when
 28.1080 -                        # returning exactly all of our first recv().
 28.1081 -                        return data
 28.1082 -                    if n >= left:
 28.1083 -                        buf.write(data[:left])
 28.1084 -                        self._rbuf.write(data[left:])
 28.1085 -                        break
 28.1086 -                    buf.write(data)
 28.1087 -                    buf_len += n
 28.1088 -                    #assert buf_len == buf.tell()
 28.1089 -                return buf.getvalue()
 28.1090 -    else:
 28.1091 -        def read(self, size=-1):
 28.1092 -            if size < 0:
 28.1093 -                # Read until EOF
 28.1094 -                buffers = [self._rbuf]
 28.1095 -                self._rbuf = ""
 28.1096 -                if self._rbufsize <= 1:
 28.1097 -                    recv_size = self.default_bufsize
 28.1098 -                else:
 28.1099 -                    recv_size = self._rbufsize
 28.1100 -
 28.1101 -                while True:
 28.1102 -                    data = self.recv(recv_size)
 28.1103 -                    if not data:
 28.1104 -                        break
 28.1105 -                    buffers.append(data)
 28.1106 -                return "".join(buffers)
 28.1107 -            else:
 28.1108 -                # Read until size bytes or EOF seen, whichever comes first
 28.1109 -                data = self._rbuf
 28.1110 -                buf_len = len(data)
 28.1111 -                if buf_len >= size:
 28.1112 -                    self._rbuf = data[size:]
 28.1113 -                    return data[:size]
 28.1114 -                buffers = []
 28.1115 -                if data:
 28.1116 -                    buffers.append(data)
 28.1117 -                self._rbuf = ""
 28.1118 -                while True:
 28.1119 -                    left = size - buf_len
 28.1120 -                    recv_size = max(self._rbufsize, left)
 28.1121 -                    data = self.recv(recv_size)
 28.1122 -                    if not data:
 28.1123 -                        break
 28.1124 -                    buffers.append(data)
 28.1125 -                    n = len(data)
 28.1126 -                    if n >= left:
 28.1127 -                        self._rbuf = data[left:]
 28.1128 -                        buffers[-1] = data[:left]
 28.1129 -                        break
 28.1130 -                    buf_len += n
 28.1131 -                return "".join(buffers)
 28.1132 -
 28.1133 -        def readline(self, size=-1):
 28.1134 -            data = self._rbuf
 28.1135 -            if size < 0:
 28.1136 -                # Read until \n or EOF, whichever comes first
 28.1137 -                if self._rbufsize <= 1:
 28.1138 -                    # Speed up unbuffered case
 28.1139 -                    assert data == ""
 28.1140 -                    buffers = []
 28.1141 -                    while data != "\n":
 28.1142 -                        data = self.recv(1)
 28.1143 -                        if not data:
 28.1144 -                            break
 28.1145 -                        buffers.append(data)
 28.1146 -                    return "".join(buffers)
 28.1147 -                nl = data.find('\n')
 28.1148 -                if nl >= 0:
 28.1149 -                    nl += 1
 28.1150 -                    self._rbuf = data[nl:]
 28.1151 -                    return data[:nl]
 28.1152 -                buffers = []
 28.1153 -                if data:
 28.1154 -                    buffers.append(data)
 28.1155 -                self._rbuf = ""
 28.1156 -                while True:
 28.1157 -                    data = self.recv(self._rbufsize)
 28.1158 -                    if not data:
 28.1159 -                        break
 28.1160 -                    buffers.append(data)
 28.1161 -                    nl = data.find('\n')
 28.1162 -                    if nl >= 0:
 28.1163 -                        nl += 1
 28.1164 -                        self._rbuf = data[nl:]
 28.1165 -                        buffers[-1] = data[:nl]
 28.1166 -                        break
 28.1167 -                return "".join(buffers)
 28.1168 -            else:
 28.1169 -                # Read until size bytes or \n or EOF seen, whichever comes first
 28.1170 -                nl = data.find('\n', 0, size)
 28.1171 -                if nl >= 0:
 28.1172 -                    nl += 1
 28.1173 -                    self._rbuf = data[nl:]
 28.1174 -                    return data[:nl]
 28.1175 -                buf_len = len(data)
 28.1176 -                if buf_len >= size:
 28.1177 -                    self._rbuf = data[size:]
 28.1178 -                    return data[:size]
 28.1179 -                buffers = []
 28.1180 -                if data:
 28.1181 -                    buffers.append(data)
 28.1182 -                self._rbuf = ""
 28.1183 -                while True:
 28.1184 -                    data = self.recv(self._rbufsize)
 28.1185 -                    if not data:
 28.1186 -                        break
 28.1187 -                    buffers.append(data)
 28.1188 -                    left = size - buf_len
 28.1189 -                    nl = data.find('\n', 0, left)
 28.1190 -                    if nl >= 0:
 28.1191 -                        nl += 1
 28.1192 -                        self._rbuf = data[nl:]
 28.1193 -                        buffers[-1] = data[:nl]
 28.1194 -                        break
 28.1195 -                    n = len(data)
 28.1196 -                    if n >= left:
 28.1197 -                        self._rbuf = data[left:]
 28.1198 -                        buffers[-1] = data[:left]
 28.1199 -                        break
 28.1200 -                    buf_len += n
 28.1201 -                return "".join(buffers)
 28.1202 -
 28.1203 -
 28.1204 -class HTTPConnection(object):
 28.1205 -    """An HTTP connection (active socket).
 28.1206 -    
 28.1207 -    server: the Server object which received this connection.
 28.1208 -    socket: the raw socket object (usually TCP) for this connection.
 28.1209 -    makefile: a fileobject class for reading from the socket.
 28.1210 -    """
 28.1211 -    
 28.1212 -    remote_addr = None
 28.1213 -    remote_port = None
 28.1214 -    ssl_env = None
 28.1215 -    rbufsize = DEFAULT_BUFFER_SIZE
 28.1216 -    wbufsize = DEFAULT_BUFFER_SIZE
 28.1217 -    RequestHandlerClass = HTTPRequest
 28.1218 -    
 28.1219 -    def __init__(self, server, sock, makefile=CP_fileobject):
 28.1220 -        self.server = server
 28.1221 -        self.socket = sock
 28.1222 -        self.rfile = makefile(sock, "rb", self.rbufsize)
 28.1223 -        self.wfile = makefile(sock, "wb", self.wbufsize)
 28.1224 -        self.requests_seen = 0
 28.1225 -    
 28.1226 -    def communicate(self):
 28.1227 -        """Read each request and respond appropriately."""
 28.1228 -        request_seen = False
 28.1229 -        try:
 28.1230 -            while True:
 28.1231 -                # (re)set req to None so that if something goes wrong in
 28.1232 -                # the RequestHandlerClass constructor, the error doesn't
 28.1233 -                # get written to the previous request.
 28.1234 -                req = None
 28.1235 -                req = self.RequestHandlerClass(self.server, self)
 28.1236 -                
 28.1237 -                # This order of operations should guarantee correct pipelining.
 28.1238 -                req.parse_request()
 28.1239 -                if self.server.stats['Enabled']:
 28.1240 -                    self.requests_seen += 1
 28.1241 -                if not req.ready:
 28.1242 -                    # Something went wrong in the parsing (and the server has
 28.1243 -                    # probably already made a simple_response). Return and
 28.1244 -                    # let the conn close.
 28.1245 -                    return
 28.1246 -                
 28.1247 -                request_seen = True
 28.1248 -                req.respond()
 28.1249 -                if req.close_connection:
 28.1250 -                    return
 28.1251 -        except socket.error, e:
 28.1252 -            errnum = e.args[0]
 28.1253 -            # sadly SSL sockets return a different (longer) time out string
 28.1254 -            if errnum == 'timed out' or errnum == 'The read operation timed out':
 28.1255 -                # Don't error if we're between requests; only error
 28.1256 -                # if 1) no request has been started at all, or 2) we're
 28.1257 -                # in the middle of a request.
 28.1258 -                # See http://www.cherrypy.org/ticket/853
 28.1259 -                if (not request_seen) or (req and req.started_request):
 28.1260 -                    # Don't bother writing the 408 if the response
 28.1261 -                    # has already started being written.
 28.1262 -                    if req and not req.sent_headers:
 28.1263 -                        try:
 28.1264 -                            req.simple_response("408 Request Timeout")
 28.1265 -                        except FatalSSLAlert:
 28.1266 -                            # Close the connection.
 28.1267 -                            return
 28.1268 -            elif errnum not in socket_errors_to_ignore:
 28.1269 -                if req and not req.sent_headers:
 28.1270 -                    try:
 28.1271 -                        req.simple_response("500 Internal Server Error",
 28.1272 -                                            format_exc())
 28.1273 -                    except FatalSSLAlert:
 28.1274 -                        # Close the connection.
 28.1275 -                        return
 28.1276 -            return
 28.1277 -        except (KeyboardInterrupt, SystemExit):
 28.1278 -            raise
 28.1279 -        except FatalSSLAlert:
 28.1280 -            # Close the connection.
 28.1281 -            return
 28.1282 -        except NoSSLError:
 28.1283 -            if req and not req.sent_headers:
 28.1284 -                # Unwrap our wfile
 28.1285 -                self.wfile = CP_fileobject(self.socket._sock, "wb", self.wbufsize)
 28.1286 -                req.simple_response("400 Bad Request",
 28.1287 -                    "The client sent a plain HTTP request, but "
 28.1288 -                    "this server only speaks HTTPS on this port.")
 28.1289 -                self.linger = True
 28.1290 -        except Exception:
 28.1291 -            if req and not req.sent_headers:
 28.1292 -                try:
 28.1293 -                    req.simple_response("500 Internal Server Error", format_exc())
 28.1294 -                except FatalSSLAlert:
 28.1295 -                    # Close the connection.
 28.1296 -                    return
 28.1297 -    
 28.1298 -    linger = False
 28.1299 -    
 28.1300 -    def close(self):
 28.1301 -        """Close the socket underlying this connection."""
 28.1302 -        self.rfile.close()
 28.1303 -        
 28.1304 -        if not self.linger:
 28.1305 -            # Python's socket module does NOT call close on the kernel socket
 28.1306 -            # when you call socket.close(). We do so manually here because we
 28.1307 -            # want this server to send a FIN TCP segment immediately. Note this
 28.1308 -            # must be called *before* calling socket.close(), because the latter
 28.1309 -            # drops its reference to the kernel socket.
 28.1310 -            if hasattr(self.socket, '_sock'):
 28.1311 -                self.socket._sock.close()
 28.1312 -            self.socket.close()
 28.1313 -        else:
 28.1314 -            # On the other hand, sometimes we want to hang around for a bit
 28.1315 -            # to make sure the client has a chance to read our entire
 28.1316 -            # response. Skipping the close() calls here delays the FIN
 28.1317 -            # packet until the socket object is garbage-collected later.
 28.1318 -            # Someday, perhaps, we'll do the full lingering_close that
 28.1319 -            # Apache does, but not today.
 28.1320 -            pass
 28.1321 -
 28.1322 -
 28.1323 -_SHUTDOWNREQUEST = None
 28.1324 -
 28.1325 -class WorkerThread(threading.Thread):
 28.1326 -    """Thread which continuously polls a Queue for Connection objects.
 28.1327 -    
 28.1328 -    Due to the timing issues of polling a Queue, a WorkerThread does not
 28.1329 -    check its own 'ready' flag after it has started. To stop the thread,
 28.1330 -    it is necessary to stick a _SHUTDOWNREQUEST object onto the Queue
 28.1331 -    (one for each running WorkerThread).
 28.1332 -    """
 28.1333 -    
 28.1334 -    conn = None
 28.1335 -    """The current connection pulled off the Queue, or None."""
 28.1336 -    
 28.1337 -    server = None
 28.1338 -    """The HTTP Server which spawned this thread, and which owns the
 28.1339 -    Queue and is placing active connections into it."""
 28.1340 -    
 28.1341 -    ready = False
 28.1342 -    """A simple flag for the calling server to know when this thread
 28.1343 -    has begun polling the Queue."""
 28.1344 -    
 28.1345 -    
 28.1346 -    def __init__(self, server):
 28.1347 -        self.ready = False
 28.1348 -        self.server = server
 28.1349 -        
 28.1350 -        self.requests_seen = 0
 28.1351 -        self.bytes_read = 0
 28.1352 -        self.bytes_written = 0
 28.1353 -        self.start_time = None
 28.1354 -        self.work_time = 0
 28.1355 -        self.stats = {
 28.1356 -            'Requests': lambda s: self.requests_seen + ((self.start_time is None) and 0 or self.conn.requests_seen),
 28.1357 -            'Bytes Read': lambda s: self.bytes_read + ((self.start_time is None) and 0 or self.conn.rfile.bytes_read),
 28.1358 -            'Bytes Written': lambda s: self.bytes_written + ((self.start_time is None) and 0 or self.conn.wfile.bytes_written),
 28.1359 -            'Work Time': lambda s: self.work_time + ((self.start_time is None) and 0 or time.time() - self.start_time),
 28.1360 -            'Read Throughput': lambda s: s['Bytes Read'](s) / (s['Work Time'](s) or 1e-6),
 28.1361 -            'Write Throughput': lambda s: s['Bytes Written'](s) / (s['Work Time'](s) or 1e-6),
 28.1362 -        }
 28.1363 -        threading.Thread.__init__(self)
 28.1364 -    
 28.1365 -    def run(self):
 28.1366 -        self.server.stats['Worker Threads'][self.getName()] = self.stats
 28.1367 -        try:
 28.1368 -            self.ready = True
 28.1369 -            while True:
 28.1370 -                conn = self.server.requests.get()
 28.1371 -                if conn is _SHUTDOWNREQUEST:
 28.1372 -                    return
 28.1373 -                
 28.1374 -                self.conn = conn
 28.1375 -                if self.server.stats['Enabled']:
 28.1376 -                    self.start_time = time.time()
 28.1377 -                try:
 28.1378 -                    conn.communicate()
 28.1379 -                finally:
 28.1380 -                    conn.close()
 28.1381 -                    if self.server.stats['Enabled']:
 28.1382 -                        self.requests_seen += self.conn.requests_seen
 28.1383 -                        self.bytes_read += self.conn.rfile.bytes_read
 28.1384 -                        self.bytes_written += self.conn.wfile.bytes_written
 28.1385 -                        self.work_time += time.time() - self.start_time
 28.1386 -                        self.start_time = None
 28.1387 -                    self.conn = None
 28.1388 -        except (KeyboardInterrupt, SystemExit), exc:
 28.1389 -            self.server.interrupt = exc
 28.1390 -
 28.1391 -
 28.1392 -class ThreadPool(object):
 28.1393 -    """A Request Queue for the CherryPyWSGIServer which pools threads.
 28.1394 -    
 28.1395 -    ThreadPool objects must provide min, get(), put(obj), start()
 28.1396 -    and stop(timeout) attributes.
 28.1397 -    """
 28.1398 -    
 28.1399 -    def __init__(self, server, min=10, max=-1):
 28.1400 -        self.server = server
 28.1401 -        self.min = min
 28.1402 -        self.max = max
 28.1403 -        self._threads = []
 28.1404 -        self._queue = Queue.Queue()
 28.1405 -        self.get = self._queue.get
 28.1406 -    
 28.1407 -    def start(self):
 28.1408 -        """Start the pool of threads."""
 28.1409 -        for i in range(self.min):
 28.1410 -            self._threads.append(WorkerThread(self.server))
 28.1411 -        for worker in self._threads:
 28.1412 -            worker.setName("CP Server " + worker.getName())
 28.1413 -            worker.start()
 28.1414 -        for worker in self._threads:
 28.1415 -            while not worker.ready:
 28.1416 -                time.sleep(.1)
 28.1417 -    
 28.1418 -    def _get_idle(self):
 28.1419 -        """Number of worker threads which are idle. Read-only."""
 28.1420 -        return len([t for t in self._threads if t.conn is None])
 28.1421 -    idle = property(_get_idle, doc=_get_idle.__doc__)
 28.1422 -    
 28.1423 -    def put(self, obj):
 28.1424 -        self._queue.put(obj)
 28.1425 -        if obj is _SHUTDOWNREQUEST:
 28.1426 -            return
 28.1427 -    
 28.1428 -    def grow(self, amount):
 28.1429 -        """Spawn new worker threads (not above self.max)."""
 28.1430 -        for i in range(amount):
 28.1431 -            if self.max > 0 and len(self._threads) >= self.max:
 28.1432 -                break
 28.1433 -            worker = WorkerThread(self.server)
 28.1434 -            worker.setName("CP Server " + worker.getName())
 28.1435 -            self._threads.append(worker)
 28.1436 -            worker.start()
 28.1437 -    
 28.1438 -    def shrink(self, amount):
 28.1439 -        """Kill off worker threads (not below self.min)."""
 28.1440 -        # Grow/shrink the pool if necessary.
 28.1441 -        # Remove any dead threads from our list
 28.1442 -        for t in self._threads:
 28.1443 -            if not t.isAlive():
 28.1444 -                self._threads.remove(t)
 28.1445 -                amount -= 1
 28.1446 -        
 28.1447 -        if amount > 0:
 28.1448 -            for i in range(min(amount, len(self._threads) - self.min)):
 28.1449 -                # Put a number of shutdown requests on the queue equal
 28.1450 -                # to 'amount'. Once each of those is processed by a worker,
 28.1451 -                # that worker will terminate and be culled from our list
 28.1452 -                # in self.put.
 28.1453 -                self._queue.put(_SHUTDOWNREQUEST)
 28.1454 -    
 28.1455 -    def stop(self, timeout=5):
 28.1456 -        # Must shut down threads here so the code that calls
 28.1457 -        # this method can know when all threads are stopped.
 28.1458 -        for worker in self._threads:
 28.1459 -            self._queue.put(_SHUTDOWNREQUEST)
 28.1460 -        
 28.1461 -        # Don't join currentThread (when stop is called inside a request).
 28.1462 -        current = threading.currentThread()
 28.1463 -        if timeout and timeout >= 0:
 28.1464 -            endtime = time.time() + timeout
 28.1465 -        while self._threads:
 28.1466 -            worker = self._threads.pop()
 28.1467 -            if worker is not current and worker.isAlive():
 28.1468 -                try:
 28.1469 -                    if timeout is None or timeout < 0:
 28.1470 -                        worker.join()
 28.1471 -                    else:
 28.1472 -                        remaining_time = endtime - time.time()
 28.1473 -                        if remaining_time > 0:
 28.1474 -                            worker.join(remaining_time)
 28.1475 -                        if worker.isAlive():
 28.1476 -                            # We exhausted the timeout.
 28.1477 -                            # Forcibly shut down the socket.
 28.1478 -                            c = worker.conn
 28.1479 -                            if c and not c.rfile.closed:
 28.1480 -                                try:
 28.1481 -                                    c.socket.shutdown(socket.SHUT_RD)
 28.1482 -                                except TypeError:
 28.1483 -                                    # pyOpenSSL sockets don't take an arg
 28.1484 -                                    c.socket.shutdown()
 28.1485 -                            worker.join()
 28.1486 -                except (AssertionError,
 28.1487 -                        # Ignore repeated Ctrl-C.
 28.1488 -                        # See http://www.cherrypy.org/ticket/691.
 28.1489 -                        KeyboardInterrupt), exc1:
 28.1490 -                    pass
 28.1491 -    
 28.1492 -    def _get_qsize(self):
 28.1493 -        return self._queue.qsize()
 28.1494 -    qsize = property(_get_qsize)
 28.1495 -
 28.1496 -
 28.1497 -
 28.1498 -try:
 28.1499 -    import fcntl
 28.1500 -except ImportError:
 28.1501 -    try:
 28.1502 -        from ctypes import windll, WinError
 28.1503 -    except ImportError:
 28.1504 -        def prevent_socket_inheritance(sock):
 28.1505 -            """Dummy function, since neither fcntl nor ctypes are available."""
 28.1506 -            pass
 28.1507 -    else:
 28.1508 -        def prevent_socket_inheritance(sock):
 28.1509 -            """Mark the given socket fd as non-inheritable (Windows)."""
 28.1510 -            if not windll.kernel32.SetHandleInformation(sock.fileno(), 1, 0):
 28.1511 -                raise WinError()
 28.1512 -else:
 28.1513 -    def prevent_socket_inheritance(sock):
 28.1514 -        """Mark the given socket fd as non-inheritable (POSIX)."""
 28.1515 -        fd = sock.fileno()
 28.1516 -        old_flags = fcntl.fcntl(fd, fcntl.F_GETFD)
 28.1517 -        fcntl.fcntl(fd, fcntl.F_SETFD, old_flags | fcntl.FD_CLOEXEC)
 28.1518 -
 28.1519 -
 28.1520 -class SSLAdapter(object):
 28.1521 -    """Base class for SSL driver library adapters.
 28.1522 -    
 28.1523 -    Required methods:
 28.1524 -    
 28.1525 -        * ``wrap(sock) -> (wrapped socket, ssl environ dict)``
 28.1526 -        * ``makefile(sock, mode='r', bufsize=DEFAULT_BUFFER_SIZE) -> socket file object``
 28.1527 -    """
 28.1528 -    
 28.1529 -    def __init__(self, certificate, private_key, certificate_chain=None):
 28.1530 -        self.certificate = certificate
 28.1531 -        self.private_key = private_key
 28.1532 -        self.certificate_chain = certificate_chain
 28.1533 -    
 28.1534 -    def wrap(self, sock):
 28.1535 -        raise NotImplemented
 28.1536 -    
 28.1537 -    def makefile(self, sock, mode='r', bufsize=DEFAULT_BUFFER_SIZE):
 28.1538 -        raise NotImplemented
 28.1539 -
 28.1540 -
 28.1541 -class HTTPServer(object):
 28.1542 -    """An HTTP server."""
 28.1543 -    
 28.1544 -    _bind_addr = "127.0.0.1"
 28.1545 -    _interrupt = None
 28.1546 -    
 28.1547 -    gateway = None
 28.1548 -    """A Gateway instance."""
 28.1549 -    
 28.1550 -    minthreads = None
 28.1551 -    """The minimum number of worker threads to create (default 10)."""
 28.1552 -    
 28.1553 -    maxthreads = None
 28.1554 -    """The maximum number of worker threads to create (default -1 = no limit)."""
 28.1555 -    
 28.1556 -    server_name = None
 28.1557 -    """The name of the server; defaults to socket.gethostname()."""
 28.1558 -    
 28.1559 -    protocol = "HTTP/1.1"
 28.1560 -    """The version string to write in the Status-Line of all HTTP responses.
 28.1561 -    
 28.1562 -    For example, "HTTP/1.1" is the default. This also limits the supported
 28.1563 -    features used in the response."""
 28.1564 -    
 28.1565 -    request_queue_size = 5
 28.1566 -    """The 'backlog' arg to socket.listen(); max queued connections (default 5)."""
 28.1567 -    
 28.1568 -    shutdown_timeout = 5
 28.1569 -    """The total time, in seconds, to wait for worker threads to cleanly exit."""
 28.1570 -    
 28.1571 -    timeout = 10
 28.1572 -    """The timeout in seconds for accepted connections (default 10)."""
 28.1573 -    
 28.1574 -    version = "CherryPy/3.2.0"
 28.1575 -    """A version string for the HTTPServer."""
 28.1576 -    
 28.1577 -    software = None
 28.1578 -    """The value to set for the SERVER_SOFTWARE entry in the WSGI environ.
 28.1579 -    
 28.1580 -    If None, this defaults to ``'%s Server' % self.version``."""
 28.1581 -    
 28.1582 -    ready = False
 28.1583 -    """An internal flag which marks whether the socket is accepting connections."""
 28.1584 -    
 28.1585 -    max_request_header_size = 0
 28.1586 -    """The maximum size, in bytes, for request headers, or 0 for no limit."""
 28.1587 -    
 28.1588 -    max_request_body_size = 0
 28.1589 -    """The maximum size, in bytes, for request bodies, or 0 for no limit."""
 28.1590 -    
 28.1591 -    nodelay = True
 28.1592 -    """If True (the default since 3.1), sets the TCP_NODELAY socket option."""
 28.1593 -    
 28.1594 -    ConnectionClass = HTTPConnection
 28.1595 -    """The class to use for handling HTTP connections."""
 28.1596 -    
 28.1597 -    ssl_adapter = None
 28.1598 -    """An instance of SSLAdapter (or a subclass).
 28.1599 -    
 28.1600 -    You must have the corresponding SSL driver library installed."""
 28.1601 -    
 28.1602 -    def __init__(self, bind_addr, gateway, minthreads=10, maxthreads=-1,
 28.1603 -                 server_name=None):
 28.1604 -        self.bind_addr = bind_addr
 28.1605 -        self.gateway = gateway
 28.1606 -        
 28.1607 -        self.requests = ThreadPool(self, min=minthreads or 1, max=maxthreads)
 28.1608 -        
 28.1609 -        if not server_name:
 28.1610 -            server_name = socket.gethostname()
 28.1611 -        self.server_name = server_name
 28.1612 -        self.clear_stats()
 28.1613 -    
 28.1614 -    def clear_stats(self):
 28.1615 -        self._start_time = None
 28.1616 -        self._run_time = 0
 28.1617 -        self.stats = {
 28.1618 -            'Enabled': False,
 28.1619 -            'Bind Address': lambda s: repr(self.bind_addr),
 28.1620 -            'Run time': lambda s: (not s['Enabled']) and 0 or self.runtime(),
 28.1621 -            'Accepts': 0,
 28.1622 -            'Accepts/sec': lambda s: s['Accepts'] / self.runtime(),
 28.1623 -            'Queue': lambda s: getattr(self.requests, "qsize", None),
 28.1624 -            'Threads': lambda s: len(getattr(self.requests, "_threads", [])),
 28.1625 -            'Threads Idle': lambda s: getattr(self.requests, "idle", None),
 28.1626 -            'Socket Errors': 0,
 28.1627 -            'Requests': lambda s: (not s['Enabled']) and 0 or sum([w['Requests'](w) for w
 28.1628 -                                       in s['Worker Threads'].values()], 0),
 28.1629 -            'Bytes Read': lambda s: (not s['Enabled']) and 0 or sum([w['Bytes Read'](w) for w
 28.1630 -                                         in s['Worker Threads'].values()], 0),
 28.1631 -            'Bytes Written': lambda s: (not s['Enabled']) and 0 or sum([w['Bytes Written'](w) for w
 28.1632 -                                            in s['Worker Threads'].values()], 0),
 28.1633 -            'Work Time': lambda s: (not s['Enabled']) and 0 or sum([w['Work Time'](w) for w
 28.1634 -                                         in s['Worker Threads'].values()], 0),
 28.1635 -            'Read Throughput': lambda s: (not s['Enabled']) and 0 or sum(
 28.1636 -                [w['Bytes Read'](w) / (w['Work Time'](w) or 1e-6)
 28.1637 -                 for w in s['Worker Threads'].values()], 0),
 28.1638 -            'Write Throughput': lambda s: (not s['Enabled']) and 0 or sum(
 28.1639 -                [w['Bytes Written'](w) / (w['Work Time'](w) or 1e-6)
 28.1640 -                 for w in s['Worker Threads'].values()], 0),
 28.1641 -            'Worker Threads': {},
 28.1642 -            }
 28.1643 -        logging.statistics["CherryPy HTTPServer %d" % id(self)] = self.stats
 28.1644 -    
 28.1645 -    def runtime(self):
 28.1646 -        if self._start_time is None:
 28.1647 -            return self._run_time
 28.1648 -        else:
 28.1649 -            return self._run_time + (time.time() - self._start_time)
 28.1650 -    
 28.1651 -    def __str__(self):
 28.1652 -        return "%s.%s(%r)" % (self.__module__, self.__class__.__name__,
 28.1653 -                              self.bind_addr)
 28.1654 -    
 28.1655 -    def _get_bind_addr(self):
 28.1656 -        return self._bind_addr
 28.1657 -    def _set_bind_addr(self, value):
 28.1658 -        if isinstance(value, tuple) and value[0] in ('', None):
 28.1659 -            # Despite the socket module docs, using '' does not
 28.1660 -            # allow AI_PASSIVE to work. Passing None instead
 28.1661 -            # returns '0.0.0.0' like we want. In other words:
 28.1662 -            #     host    AI_PASSIVE     result
 28.1663 -            #      ''         Y         192.168.x.y
 28.1664 -            #      ''         N         192.168.x.y
 28.1665 -            #     None        Y         0.0.0.0
 28.1666 -            #     None        N         127.0.0.1
 28.1667 -            # But since you can get the same effect with an explicit
 28.1668 -            # '0.0.0.0', we deny both the empty string and None as values.
 28.1669 -            raise ValueError("Host values of '' or None are not allowed. "
 28.1670 -                             "Use '0.0.0.0' (IPv4) or '::' (IPv6) instead "
 28.1671 -                             "to listen on all active interfaces.")
 28.1672 -        self._bind_addr = value
 28.1673 -    bind_addr = property(_get_bind_addr, _set_bind_addr,
 28.1674 -        doc="""The interface on which to listen for connections.
 28.1675 -        
 28.1676 -        For TCP sockets, a (host, port) tuple. Host values may be any IPv4
 28.1677 -        or IPv6 address, or any valid hostname. The string 'localhost' is a
 28.1678 -        synonym for '127.0.0.1' (or '::1', if your hosts file prefers IPv6).
 28.1679 -        The string '0.0.0.0' is a special IPv4 entry meaning "any active
 28.1680 -        interface" (INADDR_ANY), and '::' is the similar IN6ADDR_ANY for
 28.1681 -        IPv6. The empty string or None are not allowed.
 28.1682 -        
 28.1683 -        For UNIX sockets, supply the filename as a string.""")
 28.1684 -    
 28.1685 -    def start(self):
 28.1686 -        """Run the server forever."""
 28.1687 -        # We don't have to trap KeyboardInterrupt or SystemExit here,
 28.1688 -        # because cherrpy.server already does so, calling self.stop() for us.
 28.1689 -        # If you're using this server with another framework, you should
 28.1690 -        # trap those exceptions in whatever code block calls start().
 28.1691 -        self._interrupt = None
 28.1692 -        
 28.1693 -        if self.software is None:
 28.1694 -            self.software = "%s Server" % self.version
 28.1695 -        
 28.1696 -        # SSL backward compatibility
 28.1697 -        if (self.ssl_adapter is None and
 28.1698 -            getattr(self, 'ssl_certificate', None) and
 28.1699 -            getattr(self, 'ssl_private_key', None)):
 28.1700 -            warnings.warn(
 28.1701 -                    "SSL attributes are deprecated in CherryPy 3.2, and will "
 28.1702 -                    "be removed in CherryPy 3.3. Use an ssl_adapter attribute "
 28.1703 -                    "instead.",
 28.1704 -                    DeprecationWarning
 28.1705 -                )
 28.1706 -            try:
 28.1707 -                from cherrypy.wsgiserver.ssl_pyopenssl import pyOpenSSLAdapter
 28.1708 -            except ImportError:
 28.1709 -                pass
 28.1710 -            else:
 28.1711 -                self.ssl_adapter = pyOpenSSLAdapter(
 28.1712 -                    self.ssl_certificate, self.ssl_private_key,
 28.1713 -                    getattr(self, 'ssl_certificate_chain', None))
 28.1714 -        
 28.1715 -        # Select the appropriate socket
 28.1716 -        if isinstance(self.bind_addr, basestring):
 28.1717 -            # AF_UNIX socket
 28.1718 -            
 28.1719 -            # So we can reuse the socket...
 28.1720 -            try: os.unlink(self.bind_addr)
 28.1721 -            except: pass
 28.1722 -            
 28.1723 -            # So everyone can access the socket...
 28.1724 -            try: os.chmod(self.bind_addr, 0777)
 28.1725 -            except: pass
 28.1726 -            
 28.1727 -            info = [(socket.AF_UNIX, socket.SOCK_STREAM, 0, "", self.bind_addr)]
 28.1728 -        else:
 28.1729 -            # AF_INET or AF_INET6 socket
 28.1730 -            # Get the correct address family for our host (allows IPv6 addresses)
 28.1731 -            host, port = self.bind_addr
 28.1732 -            try:
 28.1733 -                info = socket.getaddrinfo(host, port, socket.AF_UNSPEC,
 28.1734 -                                          socket.SOCK_STREAM, 0, socket.AI_PASSIVE)
 28.1735 -            except socket.gaierror:
 28.1736 -                if ':' in self.bind_addr[0]:
 28.1737 -                    info = [(socket.AF_INET6, socket.SOCK_STREAM,
 28.1738 -                             0, "", self.bind_addr + (0, 0))]
 28.1739 -                else:
 28.1740 -                    info = [(socket.AF_INET, socket.SOCK_STREAM,
 28.1741 -                             0, "", self.bind_addr)]
 28.1742 -        
 28.1743 -        self.socket = None
 28.1744 -        msg = "No socket could be created"
 28.1745 -        for res in info:
 28.1746 -            af, socktype, proto, canonname, sa = res
 28.1747 -            try:
 28.1748 -                self.bind(af, socktype, proto)
 28.1749 -            except socket.error:
 28.1750 -                if self.socket:
 28.1751 -                    self.socket.close()
 28.1752 -                self.socket = None
 28.1753 -                continue
 28.1754 -            break
 28.1755 -        if not self.socket:
 28.1756 -            raise socket.error(msg)
 28.1757 -        
 28.1758 -        # Timeout so KeyboardInterrupt can be caught on Win32
 28.1759 -        self.socket.settimeout(1)
 28.1760 -        self.socket.listen(self.request_queue_size)
 28.1761 -        
 28.1762 -        # Create worker threads
 28.1763 -        self.requests.start()
 28.1764 -        
 28.1765 -        self.ready = True
 28.1766 -        self._start_time = time.time()
 28.1767 -        while self.ready:
 28.1768 -            self.tick()
 28.1769 -            if self.interrupt:
 28.1770 -                while self.interrupt is True:
 28.1771 -                    # Wait for self.stop() to complete. See _set_interrupt.
 28.1772 -                    time.sleep(0.1)
 28.1773 -                if self.interrupt:
 28.1774 -                    raise self.interrupt
 28.1775 -    
 28.1776 -    def bind(self, family, type, proto=0):
 28.1777 -        """Create (or recreate) the actual socket object."""
 28.1778 -        self.socket = socket.socket(family, type, proto)
 28.1779 -        prevent_socket_inheritance(self.socket)
 28.1780 -        self.socket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
 28.1781 -        if self.nodelay and not isinstance(self.bind_addr, str):
 28.1782 -            self.socket.setsockopt(socket.IPPROTO_TCP, socket.TCP_NODELAY, 1)
 28.1783 -        
 28.1784 -        if self.ssl_adapter is not None:
 28.1785 -            self.socket = self.ssl_adapter.bind(self.socket)
 28.1786 -        
 28.1787 -        # If listening on the IPV6 any address ('::' = IN6ADDR_ANY),
 28.1788 -        # activate dual-stack. See http://www.cherrypy.org/ticket/871.
 28.1789 -        if (hasattr(socket, 'AF_INET6') and family == socket.AF_INET6
 28.1790 -            and self.bind_addr[0] in ('::', '::0', '::0.0.0.0')):
 28.1791 -            try:
 28.1792 -                self.socket.setsockopt(socket.IPPROTO_IPV6, socket.IPV6_V6ONLY, 0)
 28.1793 -            except (AttributeError, socket.error):
 28.1794 -                # Apparently, the socket option is not available in
 28.1795 -                # this machine's TCP stack
 28.1796 -                pass
 28.1797 -        
 28.1798 -        self.socket.bind(self.bind_addr)
 28.1799 -    
 28.1800 -    def tick(self):
 28.1801 -        """Accept a new connection and put it on the Queue."""
 28.1802 -        try:
 28.1803 -            s, addr = self.socket.accept()
 28.1804 -            if self.stats['Enabled']:
 28.1805 -                self.stats['Accepts'] += 1
 28.1806 -            if not self.ready:
 28.1807 -                return
 28.1808 -            
 28.1809 -            prevent_socket_inheritance(s)
 28.1810 -            if hasattr(s, 'settimeout'):
 28.1811 -                s.settimeout(self.timeout)
 28.1812 -            
 28.1813 -            makefile = CP_fileobject
 28.1814 -            ssl_env = {}
 28.1815 -            # if ssl cert and key are set, we try to be a secure HTTP server
 28.1816 -            if self.ssl_adapter is not None:
 28.1817 -                try:
 28.1818 -                    s, ssl_env = self.ssl_adapter.wrap(s)
 28.1819 -                except NoSSLError:
 28.1820 -                    msg = ("The client sent a plain HTTP request, but "
 28.1821 -                           "this server only speaks HTTPS on this port.")
 28.1822 -                    buf = ["%s 400 Bad Request\r\n" % self.protocol,
 28.1823 -                           "Content-Length: %s\r\n" % len(msg),
 28.1824 -                           "Content-Type: text/plain\r\n\r\n",
 28.1825 -                           msg]
 28.1826 -                    
 28.1827 -                    wfile = CP_fileobject(s, "wb", DEFAULT_BUFFER_SIZE)
 28.1828 -                    try:
 28.1829 -                        wfile.sendall("".join(buf))
 28.1830 -                    except socket.error, x:
 28.1831 -                        if x.args[0] not in socket_errors_to_ignore:
 28.1832 -                            raise
 28.1833 -                    return
 28.1834 -                if not s:
 28.1835 -                    return
 28.1836 -                makefile = self.ssl_adapter.makefile
 28.1837 -                # Re-apply our timeout since we may have a new socket object
 28.1838 -                if hasattr(s, 'settimeout'):
 28.1839 -                    s.settimeout(self.timeout)
 28.1840 -            
 28.1841 -            conn = self.ConnectionClass(self, s, makefile)
 28.1842 -            
 28.1843 -            if not isinstance(self.bind_addr, basestring):
 28.1844 -                # optional values
 28.1845 -                # Until we do DNS lookups, omit REMOTE_HOST
 28.1846 -                if addr is None: # sometimes this can happen
 28.1847 -                    # figure out if AF_INET or AF_INET6.
 28.1848 -                    if len(s.getsockname()) == 2:
 28.1849 -                        # AF_INET
 28.1850 -                        addr = ('0.0.0.0', 0)
 28.1851 -                    else:
 28.1852 -                        # AF_INET6
 28.1853 -                        addr = ('::', 0)
 28.1854 -                conn.remote_addr = addr[0]
 28.1855 -                conn.remote_port = addr[1]
 28.1856 -            
 28.1857 -            conn.ssl_env = ssl_env
 28.1858 -            
 28.1859 -            self.requests.put(conn)
 28.1860 -        except socket.timeout:
 28.1861 -            # The only reason for the timeout in start() is so we can
 28.1862 -            # notice keyboard interrupts on Win32, which don't interrupt
 28.1863 -            # accept() by default
 28.1864 -            return
 28.1865 -        except socket.error, x:
 28.1866 -            if self.stats['Enabled']:
 28.1867 -                self.stats['Socket Errors'] += 1
 28.1868 -            if x.args[0] in socket_error_eintr:
 28.1869 -                # I *think* this is right. EINTR should occur when a signal
 28.1870 -                # is received during the accept() call; all docs say retry
 28.1871 -                # the call, and I *think* I'm reading it right that Python
 28.1872 -                # will then go ahead and poll for and handle the signal
 28.1873 -                # elsewhere. See http://www.cherrypy.org/ticket/707.
 28.1874 -                return
 28.1875 -            if x.args[0] in socket_errors_nonblocking:
 28.1876 -                # Just try again. See http://www.cherrypy.org/ticket/479.
 28.1877 -                return
 28.1878 -            if x.args[0] in socket_errors_to_ignore:
 28.1879 -                # Our socket was closed.
 28.1880 -                # See http://www.cherrypy.org/ticket/686.
 28.1881 -                return
 28.1882 -            raise
 28.1883 -    
 28.1884 -    def _get_interrupt(self):
 28.1885 -        return self._interrupt
 28.1886 -    def _set_interrupt(self, interrupt):
 28.1887 -        self._interrupt = True
 28.1888 -        self.stop()
 28.1889 -        self._interrupt = interrupt
 28.1890 -    interrupt = property(_get_interrupt, _set_interrupt,
 28.1891 -                         doc="Set this to an Exception instance to "
 28.1892 -                             "interrupt the server.")
 28.1893 -    
 28.1894 -    def stop(self):
 28.1895 -        """Gracefully shutdown a server that is serving forever."""
 28.1896 -        self.ready = False
 28.1897 -        if self._start_time is not None:
 28.1898 -            self._run_time += (time.time() - self._start_time)
 28.1899 -        self._start_time = None
 28.1900 -        
 28.1901 -        sock = getattr(self, "socket", None)
 28.1902 -        if sock:
 28.1903 -            if not isinstance(self.bind_addr, basestring):
 28.1904 -                # Touch our own socket to make accept() return immediately.
 28.1905 -                try:
 28.1906 -                    host, port = sock.getsockname()[:2]
 28.1907 -                except socket.error, x:
 28.1908 -                    if x.args[0] not in socket_errors_to_ignore:
 28.1909 -                        # Changed to use error code and not message
 28.1910 -                        # See http://www.cherrypy.org/ticket/860.
 28.1911 -                        raise
 28.1912 -                else:
 28.1913 -                    # Note that we're explicitly NOT using AI_PASSIVE,
 28.1914 -                    # here, because we want an actual IP to touch.
 28.1915 -                    # localhost won't work if we've bound to a public IP,
 28.1916 -                    # but it will if we bound to '0.0.0.0' (INADDR_ANY).
 28.1917 -                    for res in socket.getaddrinfo(host, port, socket.AF_UNSPEC,
 28.1918 -                                                  socket.SOCK_STREAM):
 28.1919 -                        af, socktype, proto, canonname, sa = res
 28.1920 -                        s = None
 28.1921 -                        try:
 28.1922 -                            s = socket.socket(af, socktype, proto)
 28.1923 -                            # See http://groups.google.com/group/cherrypy-users/
 28.1924 -                            #        browse_frm/thread/bbfe5eb39c904fe0
 28.1925 -                            s.settimeout(1.0)
 28.1926 -                            s.connect((host, port))
 28.1927 -                            s.close()
 28.1928 -                        except socket.error:
 28.1929 -                            if s:
 28.1930 -                                s.close()
 28.1931 -            if hasattr(sock, "close"):
 28.1932 -                sock.close()
 28.1933 -            self.socket = None
 28.1934 -        
 28.1935 -        self.requests.stop(self.shutdown_timeout)
 28.1936 -
 28.1937 -
 28.1938 -class Gateway(object):
 28.1939 -    
 28.1940 -    def __init__(self, req):
 28.1941 -        self.req = req
 28.1942 -    
 28.1943 -    def respond(self):
 28.1944 -        raise NotImplemented
 28.1945 -
 28.1946 -
 28.1947 -# These may either be wsgiserver.SSLAdapter subclasses or the string names
 28.1948 -# of such classes (in which case they will be lazily loaded).
 28.1949 -ssl_adapters = {
 28.1950 -    'builtin': 'cherrypy.wsgiserver.ssl_builtin.BuiltinSSLAdapter',
 28.1951 -    'pyopenssl': 'cherrypy.wsgiserver.ssl_pyopenssl.pyOpenSSLAdapter',
 28.1952 -    }
 28.1953 -
 28.1954 -def get_ssl_adapter_class(name='pyopenssl'):
 28.1955 -    adapter = ssl_adapters[name.lower()]
 28.1956 -    if isinstance(adapter, basestring):
 28.1957 -        last_dot = adapter.rfind(".")
 28.1958 -        attr_name = adapter[last_dot + 1:]
 28.1959 -        mod_path = adapter[:last_dot]
 28.1960 -        
 28.1961 -        try:
 28.1962 -            mod = sys.modules[mod_path]
 28.1963 -            if mod is None:
 28.1964 -                raise KeyError()
 28.1965 -        except KeyError:
 28.1966 -            # The last [''] is important.
 28.1967 -            mod = __import__(mod_path, globals(), locals(), [''])
 28.1968 -        
 28.1969 -        # Let an AttributeError propagate outward.
 28.1970 -        try:
 28.1971 -            adapter = getattr(mod, attr_name)
 28.1972 -        except AttributeError:
 28.1973 -            raise AttributeError("'%s' object has no attribute '%s'"
 28.1974 -                                 % (mod_path, attr_name))
 28.1975 -    
 28.1976 -    return adapter
 28.1977 -
 28.1978 -# -------------------------------- WSGI Stuff -------------------------------- #
 28.1979 -
 28.1980 -
 28.1981 -class CherryPyWSGIServer(HTTPServer):
 28.1982 -    
 28.1983 -    wsgi_version = (1, 0)
 28.1984 -    
 28.1985 -    def __init__(self, bind_addr, wsgi_app, numthreads=10, server_name=None,
 28.1986 -                 max=-1, request_queue_size=5, timeout=10, shutdown_timeout=5):
 28.1987 -        self.requests = ThreadPool(self, min=numthreads or 1, max=max)
 28.1988 -        self.wsgi_app = wsgi_app
 28.1989 -        self.gateway = wsgi_gateways[self.wsgi_version]
 28.1990 -        
 28.1991 -        self.bind_addr = bind_addr
 28.1992 -        if not server_name:
 28.1993 -            server_name = socket.gethostname()
 28.1994 -        self.server_name = server_name
 28.1995 -        self.request_queue_size = request_queue_size
 28.1996 -        
 28.1997 -        self.timeout = timeout
 28.1998 -        self.shutdown_timeout = shutdown_timeout
 28.1999 -        self.clear_stats()
 28.2000 -    
 28.2001 -    def _get_numthreads(self):
 28.2002 -        return self.requests.min
 28.2003 -    def _set_numthreads(self, value):
 28.2004 -        self.requests.min = value
 28.2005 -    numthreads = property(_get_numthreads, _set_numthreads)
 28.2006 -
 28.2007 -
 28.2008 -class WSGIGateway(Gateway):
 28.2009 -    
 28.2010 -    def __init__(self, req):
 28.2011 -        self.req = req
 28.2012 -        self.started_response = False
 28.2013 -        self.env = self.get_environ()
 28.2014 -        self.remaining_bytes_out = None
 28.2015 -    
 28.2016 -    def get_environ(self):
 28.2017 -        """Return a new environ dict targeting the given wsgi.version"""
 28.2018 -        raise NotImplemented
 28.2019 -    
 28.2020 -    def respond(self):
 28.2021 -        response = self.req.server.wsgi_app(self.env, self.start_response)
 28.2022 -        try:
 28.2023 -            for chunk in response:
 28.2024 -                # "The start_response callable must not actually transmit
 28.2025 -                # the response headers. Instead, it must store them for the
 28.2026 -                # server or gateway to transmit only after the first
 28.2027 -                # iteration of the application return value that yields
 28.2028 -                # a NON-EMPTY string, or upon the application's first
 28.2029 -                # invocation of the write() callable." (PEP 333)
 28.2030 -                if chunk:
 28.2031 -                    if isinstance(chunk, unicode):
 28.2032 -                        chunk = chunk.encode('ISO-8859-1')
 28.2033 -                    self.write(chunk)
 28.2034 -        finally:
 28.2035 -            if hasattr(response, "close"):
 28.2036 -                response.close()
 28.2037 -    
 28.2038 -    def start_response(self, status, headers, exc_info = None):
 28.2039 -        """WSGI callable to begin the HTTP response."""
 28.2040 -        # "The application may call start_response more than once,
 28.2041 -        # if and only if the exc_info argument is provided."
 28.2042 -        if self.started_response and not exc_info:
 28.2043 -            raise AssertionError("WSGI start_response called a second "
 28.2044 -                                 "time with no exc_info.")
 28.2045 -        self.started_response = True
 28.2046 -        
 28.2047 -        # "if exc_info is provided, and the HTTP headers have already been
 28.2048 -        # sent, start_response must raise an error, and should raise the
 28.2049 -        # exc_info tuple."
 28.2050 -        if self.req.sent_headers:
 28.2051 -            try:
 28.2052 -                raise exc_info[0], exc_info[1], exc_info[2]
 28.2053 -            finally:
 28.2054 -                exc_info = None
 28.2055 -        
 28.2056 -        self.req.status = status
 28.2057 -        for k, v in headers:
 28.2058 -            if not isinstance(k, str):
 28.2059 -                raise TypeError("WSGI response header key %r is not a byte string." % k)
 28.2060 -            if not isinstance(v, str):
 28.2061 -                raise TypeError("WSGI response header value %r is not a byte string." % v)
 28.2062 -            if k.lower() == 'content-length':
 28.2063 -                self.remaining_bytes_out = int(v)
 28.2064 -        self.req.outheaders.extend(headers)
 28.2065 -        
 28.2066 -        return self.write
 28.2067 -    
 28.2068 -    def write(self, chunk):
 28.2069 -        """WSGI callable to write unbuffered data to the client.
 28.2070 -        
 28.2071 -        This method is also used internally by start_response (to write
 28.2072 -        data from the iterable returned by the WSGI application).
 28.2073 -        """
 28.2074 -        if not self.started_response:
 28.2075 -            raise AssertionError("WSGI write called before start_response.")
 28.2076 -        
 28.2077 -        chunklen = len(chunk)
 28.2078 -        rbo = self.remaining_bytes_out
 28.2079 -        if rbo is not None and chunklen > rbo:
 28.2080 -            if not self.req.sent_headers:
 28.2081 -                # Whew. We can send a 500 to the client.
 28.2082 -                self.req.simple_response("500 Internal Server Error",
 28.2083 -                    "The requested resource returned more bytes than the "
 28.2084 -                    "declared Content-Length.")
 28.2085 -            else:
 28.2086 -                # Dang. We have probably already sent data. Truncate the chunk
 28.2087 -                # to fit (so the client doesn't hang) and raise an error later.
 28.2088 -                chunk = chunk[:rbo]
 28.2089 -        
 28.2090 -        if not self.req.sent_headers:
 28.2091 -            self.req.sent_headers = True
 28.2092 -            self.req.send_headers()
 28.2093 -        
 28.2094 -        self.req.write(chunk)
 28.2095 -        
 28.2096 -        if rbo is not None:
 28.2097 -            rbo -= chunklen
 28.2098 -            if rbo < 0:
 28.2099 -                raise ValueError(
 28.2100 -                    "Response body exceeds the declared Content-Length.")
 28.2101 -
 28.2102 -
 28.2103 -class WSGIGateway_10(WSGIGateway):
 28.2104 -    
 28.2105 -    def get_environ(self):
 28.2106 -        """Return a new environ dict targeting the given wsgi.version"""
 28.2107 -        req = self.req
 28.2108 -        env = {
 28.2109 -            # set a non-standard environ entry so the WSGI app can know what
 28.2110 -            # the *real* server protocol is (and what features to support).
 28.2111 -            # See http://www.faqs.org/rfcs/rfc2145.html.
 28.2112 -            'ACTUAL_SERVER_PROTOCOL': req.server.protocol,
 28.2113 -            'PATH_INFO': req.path,
 28.2114 -            'QUERY_STRING': req.qs,
 28.2115 -            'REMOTE_ADDR': req.conn.remote_addr or '',
 28.2116 -            'REMOTE_PORT': str(req.conn.remote_port or ''),
 28.2117 -            'REQUEST_METHOD': req.method,
 28.2118 -            'REQUEST_URI': req.uri,
 28.2119 -            'SCRIPT_NAME': '',
 28.2120 -            'SERVER_NAME': req.server.server_name,
 28.2121 -            # Bah. "SERVER_PROTOCOL" is actually the REQUEST protocol.
 28.2122 -            'SERVER_PROTOCOL': req.request_protocol,
 28.2123 -            'SERVER_SOFTWARE': req.server.software,
 28.2124 -            'wsgi.errors': sys.stderr,
 28.2125 -            'wsgi.input': req.rfile,
 28.2126 -            'wsgi.multiprocess': False,
 28.2127 -            'wsgi.multithread': True,
 28.2128 -            'wsgi.run_once': False,
 28.2129 -            'wsgi.url_scheme': req.scheme,
 28.2130 -            'wsgi.version': (1, 0),
 28.2131 -            }
 28.2132 -        
 28.2133 -        if isinstance(req.server.bind_addr, basestring):
 28.2134 -            # AF_UNIX. This isn't really allowed by WSGI, which doesn't
 28.2135 -            # address unix domain sockets. But it's better than nothing.
 28.2136 -            env["SERVER_PORT"] = ""
 28.2137 -        else:
 28.2138 -            env["SERVER_PORT"] = str(req.server.bind_addr[1])
 28.2139 -        
 28.2140 -        # Request headers
 28.2141 -        for k, v in req.inheaders.iteritems():
 28.2142 -            env["HTTP_" + k.upper().replace("-", "_")] = v
 28.2143 -        
 28.2144 -        # CONTENT_TYPE/CONTENT_LENGTH
 28.2145 -        ct = env.pop("HTTP_CONTENT_TYPE", None)
 28.2146 -        if ct is not None:
 28.2147 -            env["CONTENT_TYPE"] = ct
 28.2148 -        cl = env.pop("HTTP_CONTENT_LENGTH", None)
 28.2149 -        if cl is not None:
 28.2150 -            env["CONTENT_LENGTH"] = cl
 28.2151 -        
 28.2152 -        if req.conn.ssl_env:
 28.2153 -            env.update(req.conn.ssl_env)
 28.2154 -        
 28.2155 -        return env
 28.2156 -
 28.2157 -
 28.2158 -class WSGIGateway_u0(WSGIGateway_10):
 28.2159 -    
 28.2160 -    def get_environ(self):
 28.2161 -        """Return a new environ dict targeting the given wsgi.version"""
 28.2162 -        req = self.req
 28.2163 -        env_10 = WSGIGateway_10.get_environ(self)
 28.2164 -        env = dict([(k.decode('ISO-8859-1'), v) for k, v in env_10.iteritems()])
 28.2165 -        env[u'wsgi.version'] = ('u', 0)
 28.2166 -        
 28.2167 -        # Request-URI
 28.2168 -        env.setdefault(u'wsgi.url_encoding', u'utf-8')
 28.2169 -        try:
 28.2170 -            for key in [u"PATH_INFO", u"SCRIPT_NAME", u"QUERY_STRING"]:
 28.2171 -                env[key] = env_10[str(key)].decode(env[u'wsgi.url_encoding'])
 28.2172 -        except UnicodeDecodeError:
 28.2173 -            # Fall back to latin 1 so apps can transcode if needed.
 28.2174 -            env[u'wsgi.url_encoding'] = u'ISO-8859-1'
 28.2175 -            for key in [u"PATH_INFO", u"SCRIPT_NAME", u"QUERY_STRING"]:
 28.2176 -                env[key] = env_10[str(key)].decode(env[u'wsgi.url_encoding'])
 28.2177 -        
 28.2178 -        for k, v in sorted(env.items()):
 28.2179 -            if isinstance(v, str) and k not in ('REQUEST_URI', 'wsgi.input'):
 28.2180 -                env[k] = v.decode('ISO-8859-1')
 28.2181 -        
 28.2182 -        return env
 28.2183 -
 28.2184 -wsgi_gateways = {
 28.2185 -    (1, 0): WSGIGateway_10,
 28.2186 -    ('u', 0): WSGIGateway_u0,
 28.2187 -}
 28.2188 -
 28.2189 -class WSGIPathInfoDispatcher(object):
 28.2190 -    """A WSGI dispatcher for dispatch based on the PATH_INFO.
 28.2191 -    
 28.2192 -    apps: a dict or list of (path_prefix, app) pairs.
 28.2193 -    """
 28.2194 -    
 28.2195 -    def __init__(self, apps):
 28.2196 -        try:
 28.2197 -            apps = apps.items()
 28.2198 -        except AttributeError:
 28.2199 -            pass
 28.2200 -        
 28.2201 -        # Sort the apps by len(path), descending
 28.2202 -        apps.sort(cmp=lambda x,y: cmp(len(x[0]), len(y[0])))
 28.2203 -        apps.reverse()
 28.2204 -        
 28.2205 -        # The path_prefix strings must start, but not end, with a slash.
 28.2206 -        # Use "" instead of "/".
 28.2207 -        self.apps = [(p.rstrip("/"), a) for p, a in apps]
 28.2208 -    
 28.2209 -    def __call__(self, environ, start_response):
 28.2210 -        path = environ["PATH_INFO"] or "/"
 28.2211 -        for p, app in self.apps:
 28.2212 -            # The apps list should be sorted by length, descending.
 28.2213 -            if path.startswith(p + "/") or path == p:
 28.2214 -                environ = environ.copy()
 28.2215 -                environ["SCRIPT_NAME"] = environ["SCRIPT_NAME"] + p
 28.2216 -                environ["PATH_INFO"] = path[len(p):]
 28.2217 -                return app(environ, start_response)
 28.2218 -        
 28.2219 -        start_response('404 Not Found', [('Content-Type', 'text/plain'),
 28.2220 -                                         ('Content-Length', '0')])
 28.2221 -        return ['']
 28.2222 -
    29.1 --- a/OpenSecurity/install/web.py-0.37/build/lib/web/wsgiserver/ssl_builtin.py	Thu Feb 20 15:40:48 2014 +0100
    29.2 +++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
    29.3 @@ -1,72 +0,0 @@
    29.4 -"""A library for integrating Python's builtin ``ssl`` library with CherryPy.
    29.5 -
    29.6 -The ssl module must be importable for SSL functionality.
    29.7 -
    29.8 -To use this module, set ``CherryPyWSGIServer.ssl_adapter`` to an instance of
    29.9 -``BuiltinSSLAdapter``.
   29.10 -"""
   29.11 -
   29.12 -try:
   29.13 -    import ssl
   29.14 -except ImportError:
   29.15 -    ssl = None
   29.16 -
   29.17 -from cherrypy import wsgiserver
   29.18 -
   29.19 -
   29.20 -class BuiltinSSLAdapter(wsgiserver.SSLAdapter):
   29.21 -    """A wrapper for integrating Python's builtin ssl module with CherryPy."""
   29.22 -    
   29.23 -    certificate = None
   29.24 -    """The filename of the server SSL certificate."""
   29.25 -    
   29.26 -    private_key = None
   29.27 -    """The filename of the server's private key file."""
   29.28 -    
   29.29 -    def __init__(self, certificate, private_key, certificate_chain=None):
   29.30 -        if ssl is None:
   29.31 -            raise ImportError("You must install the ssl module to use HTTPS.")
   29.32 -        self.certificate = certificate
   29.33 -        self.private_key = private_key
   29.34 -        self.certificate_chain = certificate_chain
   29.35 -    
   29.36 -    def bind(self, sock):
   29.37 -        """Wrap and return the given socket."""
   29.38 -        return sock
   29.39 -    
   29.40 -    def wrap(self, sock):
   29.41 -        """Wrap and return the given socket, plus WSGI environ entries."""
   29.42 -        try:
   29.43 -            s = ssl.wrap_socket(sock, do_handshake_on_connect=True,
   29.44 -                    server_side=True, certfile=self.certificate,
   29.45 -                    keyfile=self.private_key, ssl_version=ssl.PROTOCOL_SSLv23)
   29.46 -        except ssl.SSLError, e:
   29.47 -            if e.errno == ssl.SSL_ERROR_EOF:
   29.48 -                # This is almost certainly due to the cherrypy engine
   29.49 -                # 'pinging' the socket to assert it's connectable;
   29.50 -                # the 'ping' isn't SSL.
   29.51 -                return None, {}
   29.52 -            elif e.errno == ssl.SSL_ERROR_SSL:
   29.53 -                if e.args[1].endswith('http request'):
   29.54 -                    # The client is speaking HTTP to an HTTPS server.
   29.55 -                    raise wsgiserver.NoSSLError
   29.56 -            raise
   29.57 -        return s, self.get_environ(s)
   29.58 -    
   29.59 -    # TODO: fill this out more with mod ssl env
   29.60 -    def get_environ(self, sock):
   29.61 -        """Create WSGI environ entries to be merged into each request."""
   29.62 -        cipher = sock.cipher()
   29.63 -        ssl_environ = {
   29.64 -            "wsgi.url_scheme": "https",
   29.65 -            "HTTPS": "on",
   29.66 -            'SSL_PROTOCOL': cipher[1],
   29.67 -            'SSL_CIPHER': cipher[0]
   29.68 -##            SSL_VERSION_INTERFACE 	string 	The mod_ssl program version
   29.69 -##            SSL_VERSION_LIBRARY 	string 	The OpenSSL program version
   29.70 -            }
   29.71 -        return ssl_environ
   29.72 -    
   29.73 -    def makefile(self, sock, mode='r', bufsize=-1):
   29.74 -        return wsgiserver.CP_fileobject(sock, mode, bufsize)
   29.75 -
    30.1 --- a/OpenSecurity/install/web.py-0.37/build/lib/web/wsgiserver/ssl_pyopenssl.py	Thu Feb 20 15:40:48 2014 +0100
    30.2 +++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
    30.3 @@ -1,256 +0,0 @@
    30.4 -"""A library for integrating pyOpenSSL with CherryPy.
    30.5 -
    30.6 -The OpenSSL module must be importable for SSL functionality.
    30.7 -You can obtain it from http://pyopenssl.sourceforge.net/
    30.8 -
    30.9 -To use this module, set CherryPyWSGIServer.ssl_adapter to an instance of
   30.10 -SSLAdapter. There are two ways to use SSL:
   30.11 -
   30.12 -Method One
   30.13 -----------
   30.14 -
   30.15 - * ``ssl_adapter.context``: an instance of SSL.Context.
   30.16 -
   30.17 -If this is not None, it is assumed to be an SSL.Context instance,
   30.18 -and will be passed to SSL.Connection on bind(). The developer is
   30.19 -responsible for forming a valid Context object. This approach is
   30.20 -to be preferred for more flexibility, e.g. if the cert and key are
   30.21 -streams instead of files, or need decryption, or SSL.SSLv3_METHOD
   30.22 -is desired instead of the default SSL.SSLv23_METHOD, etc. Consult
   30.23 -the pyOpenSSL documentation for complete options.
   30.24 -
   30.25 -Method Two (shortcut)
   30.26 ----------------------
   30.27 -
   30.28 - * ``ssl_adapter.certificate``: the filename of the server SSL certificate.
   30.29 - * ``ssl_adapter.private_key``: the filename of the server's private key file.
   30.30 -
   30.31 -Both are None by default. If ssl_adapter.context is None, but .private_key
   30.32 -and .certificate are both given and valid, they will be read, and the
   30.33 -context will be automatically created from them.
   30.34 -"""
   30.35 -
   30.36 -import socket
   30.37 -import threading
   30.38 -import time
   30.39 -
   30.40 -from cherrypy import wsgiserver
   30.41 -
   30.42 -try:
   30.43 -    from OpenSSL import SSL
   30.44 -    from OpenSSL import crypto
   30.45 -except ImportError:
   30.46 -    SSL = None
   30.47 -
   30.48 -
   30.49 -class SSL_fileobject(wsgiserver.CP_fileobject):
   30.50 -    """SSL file object attached to a socket object."""
   30.51 -    
   30.52 -    ssl_timeout = 3
   30.53 -    ssl_retry = .01
   30.54 -    
   30.55 -    def _safe_call(self, is_reader, call, *args, **kwargs):
   30.56 -        """Wrap the given call with SSL error-trapping.
   30.57 -        
   30.58 -        is_reader: if False EOF errors will be raised. If True, EOF errors
   30.59 -        will return "" (to emulate normal sockets).
   30.60 -        """
   30.61 -        start = time.time()
   30.62 -        while True:
   30.63 -            try:
   30.64 -                return call(*args, **kwargs)
   30.65 -            except SSL.WantReadError:
   30.66 -                # Sleep and try again. This is dangerous, because it means
   30.67 -                # the rest of the stack has no way of differentiating
   30.68 -                # between a "new handshake" error and "client dropped".
   30.69 -                # Note this isn't an endless loop: there's a timeout below.
   30.70 -                time.sleep(self.ssl_retry)
   30.71 -            except SSL.WantWriteError:
   30.72 -                time.sleep(self.ssl_retry)
   30.73 -            except SSL.SysCallError, e:
   30.74 -                if is_reader and e.args == (-1, 'Unexpected EOF'):
   30.75 -                    return ""
   30.76 -                
   30.77 -                errnum = e.args[0]
   30.78 -                if is_reader and errnum in wsgiserver.socket_errors_to_ignore:
   30.79 -                    return ""
   30.80 -                raise socket.error(errnum)
   30.81 -            except SSL.Error, e:
   30.82 -                if is_reader and e.args == (-1, 'Unexpected EOF'):
   30.83 -                    return ""
   30.84 -                
   30.85 -                thirdarg = None
   30.86 -                try:
   30.87 -                    thirdarg = e.args[0][0][2]
   30.88 -                except IndexError:
   30.89 -                    pass
   30.90 -                
   30.91 -                if thirdarg == 'http request':
   30.92 -                    # The client is talking HTTP to an HTTPS server.
   30.93 -                    raise wsgiserver.NoSSLError()
   30.94 -                
   30.95 -                raise wsgiserver.FatalSSLAlert(*e.args)
   30.96 -            except:
   30.97 -                raise
   30.98 -            
   30.99 -            if time.time() - start > self.ssl_timeout:
  30.100 -                raise socket.timeout("timed out")
  30.101 -    
  30.102 -    def recv(self, *args, **kwargs):
  30.103 -        buf = []
  30.104 -        r = super(SSL_fileobject, self).recv
  30.105 -        while True:
  30.106 -            data = self._safe_call(True, r, *args, **kwargs)
  30.107 -            buf.append(data)
  30.108 -            p = self._sock.pending()
  30.109 -            if not p:
  30.110 -                return "".join(buf)
  30.111 -    
  30.112 -    def sendall(self, *args, **kwargs):
  30.113 -        return self._safe_call(False, super(SSL_fileobject, self).sendall,
  30.114 -                               *args, **kwargs)
  30.115 -
  30.116 -    def send(self, *args, **kwargs):
  30.117 -        return self._safe_call(False, super(SSL_fileobject, self).send,
  30.118 -                               *args, **kwargs)
  30.119 -
  30.120 -
  30.121 -class SSLConnection:
  30.122 -    """A thread-safe wrapper for an SSL.Connection.
  30.123 -    
  30.124 -    ``*args``: the arguments to create the wrapped ``SSL.Connection(*args)``.
  30.125 -    """
  30.126 -    
  30.127 -    def __init__(self, *args):
  30.128 -        self._ssl_conn = SSL.Connection(*args)
  30.129 -        self._lock = threading.RLock()
  30.130 -    
  30.131 -    for f in ('get_context', 'pending', 'send', 'write', 'recv', 'read',
  30.132 -              'renegotiate', 'bind', 'listen', 'connect', 'accept',
  30.133 -              'setblocking', 'fileno', 'close', 'get_cipher_list',
  30.134 -              'getpeername', 'getsockname', 'getsockopt', 'setsockopt',
  30.135 -              'makefile', 'get_app_data', 'set_app_data', 'state_string',
  30.136 -              'sock_shutdown', 'get_peer_certificate', 'want_read',
  30.137 -              'want_write', 'set_connect_state', 'set_accept_state',
  30.138 -              'connect_ex', 'sendall', 'settimeout', 'gettimeout'):
  30.139 -        exec("""def %s(self, *args):
  30.140 -        self._lock.acquire()
  30.141 -        try:
  30.142 -            return self._ssl_conn.%s(*args)
  30.143 -        finally:
  30.144 -            self._lock.release()
  30.145 -""" % (f, f))
  30.146 -    
  30.147 -    def shutdown(self, *args):
  30.148 -        self._lock.acquire()
  30.149 -        try:
  30.150 -            # pyOpenSSL.socket.shutdown takes no args
  30.151 -            return self._ssl_conn.shutdown()
  30.152 -        finally:
  30.153 -            self._lock.release()
  30.154 -
  30.155 -
  30.156 -class pyOpenSSLAdapter(wsgiserver.SSLAdapter):
  30.157 -    """A wrapper for integrating pyOpenSSL with CherryPy."""
  30.158 -    
  30.159 -    context = None
  30.160 -    """An instance of SSL.Context."""
  30.161 -    
  30.162 -    certificate = None
  30.163 -    """The filename of the server SSL certificate."""
  30.164 -    
  30.165 -    private_key = None
  30.166 -    """The filename of the server's private key file."""
  30.167 -    
  30.168 -    certificate_chain = None
  30.169 -    """Optional. The filename of CA's intermediate certificate bundle.
  30.170 -    
  30.171 -    This is needed for cheaper "chained root" SSL certificates, and should be
  30.172 -    left as None if not required."""
  30.173 -    
  30.174 -    def __init__(self, certificate, private_key, certificate_chain=None):
  30.175 -        if SSL is None:
  30.176 -            raise ImportError("You must install pyOpenSSL to use HTTPS.")
  30.177 -        
  30.178 -        self.context = None
  30.179 -        self.certificate = certificate
  30.180 -        self.private_key = private_key
  30.181 -        self.certificate_chain = certificate_chain
  30.182 -        self._environ = None
  30.183 -    
  30.184 -    def bind(self, sock):
  30.185 -        """Wrap and return the given socket."""
  30.186 -        if self.context is None:
  30.187 -            self.context = self.get_context()
  30.188 -        conn = SSLConnection(self.context, sock)
  30.189 -        self._environ = self.get_environ()
  30.190 -        return conn
  30.191 -    
  30.192 -    def wrap(self, sock):
  30.193 -        """Wrap and return the given socket, plus WSGI environ entries."""
  30.194 -        return sock, self._environ.copy()
  30.195 -    
  30.196 -    def get_context(self):
  30.197 -        """Return an SSL.Context from self attributes."""
  30.198 -        # See http://aspn.activestate.com/ASPN/Cookbook/Python/Recipe/442473
  30.199 -        c = SSL.Context(SSL.SSLv23_METHOD)
  30.200 -        c.use_privatekey_file(self.private_key)
  30.201 -        if self.certificate_chain:
  30.202 -            c.load_verify_locations(self.certificate_chain)
  30.203 -        c.use_certificate_file(self.certificate)
  30.204 -        return c
  30.205 -    
  30.206 -    def get_environ(self):
  30.207 -        """Return WSGI environ entries to be merged into each request."""
  30.208 -        ssl_environ = {
  30.209 -            "HTTPS": "on",
  30.210 -            # pyOpenSSL doesn't provide access to any of these AFAICT
  30.211 -##            'SSL_PROTOCOL': 'SSLv2',
  30.212 -##            SSL_CIPHER 	string 	The cipher specification name
  30.213 -##            SSL_VERSION_INTERFACE 	string 	The mod_ssl program version
  30.214 -##            SSL_VERSION_LIBRARY 	string 	The OpenSSL program version
  30.215 -            }
  30.216 -        
  30.217 -        if self.certificate:
  30.218 -            # Server certificate attributes
  30.219 -            cert = open(self.certificate, 'rb').read()
  30.220 -            cert = crypto.load_certificate(crypto.FILETYPE_PEM, cert)
  30.221 -            ssl_environ.update({
  30.222 -                'SSL_SERVER_M_VERSION': cert.get_version(),
  30.223 -                'SSL_SERVER_M_SERIAL': cert.get_serial_number(),
  30.224 -##                'SSL_SERVER_V_START': Validity of server's certificate (start time),
  30.225 -##                'SSL_SERVER_V_END': Validity of server's certificate (end time),
  30.226 -                })
  30.227 -            
  30.228 -            for prefix, dn in [("I", cert.get_issuer()),
  30.229 -                               ("S", cert.get_subject())]:
  30.230 -                # X509Name objects don't seem to have a way to get the
  30.231 -                # complete DN string. Use str() and slice it instead,
  30.232 -                # because str(dn) == "<X509Name object '/C=US/ST=...'>"
  30.233 -                dnstr = str(dn)[18:-2]
  30.234 -                
  30.235 -                wsgikey = 'SSL_SERVER_%s_DN' % prefix
  30.236 -                ssl_environ[wsgikey] = dnstr
  30.237 -                
  30.238 -                # The DN should be of the form: /k1=v1/k2=v2, but we must allow
  30.239 -                # for any value to contain slashes itself (in a URL).
  30.240 -                while dnstr:
  30.241 -                    pos = dnstr.rfind("=")
  30.242 -                    dnstr, value = dnstr[:pos], dnstr[pos + 1:]
  30.243 -                    pos = dnstr.rfind("/")
  30.244 -                    dnstr, key = dnstr[:pos], dnstr[pos + 1:]
  30.245 -                    if key and value:
  30.246 -                        wsgikey = 'SSL_SERVER_%s_DN_%s' % (prefix, key)
  30.247 -                        ssl_environ[wsgikey] = value
  30.248 -        
  30.249 -        return ssl_environ
  30.250 -    
  30.251 -    def makefile(self, sock, mode='r', bufsize=-1):
  30.252 -        if SSL and isinstance(sock, SSL.ConnectionType):
  30.253 -            timeout = sock.gettimeout()
  30.254 -            f = SSL_fileobject(sock, mode, bufsize)
  30.255 -            f.ssl_timeout = timeout
  30.256 -            return f
  30.257 -        else:
  30.258 -            return wsgiserver.CP_fileobject(sock, mode, bufsize)
  30.259 -
    31.1 --- a/OpenSecurity/install/web.py-0.37/setup.py	Thu Feb 20 15:40:48 2014 +0100
    31.2 +++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
    31.3 @@ -1,20 +0,0 @@
    31.4 -#!/usr/bin/env python
    31.5 -
    31.6 -# ...
    31.7 -
    31.8 -from distutils.core import setup
    31.9 -from web import __version__
   31.10 -
   31.11 -setup(name='web.py',
   31.12 -      version=__version__,
   31.13 -      description='web.py: makes web apps',
   31.14 -      author='Aaron Swartz',
   31.15 -      author_email='me@aaronsw.com',
   31.16 -      maintainer='Anand Chitipothu',
   31.17 -      maintainer_email='anandology@gmail.com',
   31.18 -      url=' http://webpy.org/',
   31.19 -      packages=['web', 'web.wsgiserver', 'web.contrib'],
   31.20 -      long_description="Think about the ideal way to write a web app. Write the code to make it happen.",
   31.21 -      license="Public domain",
   31.22 -      platforms=["any"],
   31.23 -     )
    32.1 --- a/OpenSecurity/install/web.py-0.37/web/__init__.py	Thu Feb 20 15:40:48 2014 +0100
    32.2 +++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
    32.3 @@ -1,33 +0,0 @@
    32.4 -#!/usr/bin/env python
    32.5 -"""web.py: makes web apps (http://webpy.org)"""
    32.6 -
    32.7 -from __future__ import generators
    32.8 -
    32.9 -__version__ = "0.37"
   32.10 -__author__ = [
   32.11 -    "Aaron Swartz <me@aaronsw.com>",
   32.12 -    "Anand Chitipothu <anandology@gmail.com>"
   32.13 -]
   32.14 -__license__ = "public domain"
   32.15 -__contributors__ = "see http://webpy.org/changes"
   32.16 -
   32.17 -import utils, db, net, wsgi, http, webapi, httpserver, debugerror
   32.18 -import template, form
   32.19 -
   32.20 -import session
   32.21 -
   32.22 -from utils import *
   32.23 -from db import *
   32.24 -from net import *
   32.25 -from wsgi import *
   32.26 -from http import *
   32.27 -from webapi import *
   32.28 -from httpserver import *
   32.29 -from debugerror import *
   32.30 -from application import *
   32.31 -from browser import *
   32.32 -try:
   32.33 -    import webopenid as openid
   32.34 -except ImportError:
   32.35 -    pass # requires openid module
   32.36 -
    33.1 Binary file OpenSecurity/install/web.py-0.37/web/__init__.pyc has changed
    34.1 --- a/OpenSecurity/install/web.py-0.37/web/application.py	Thu Feb 20 15:40:48 2014 +0100
    34.2 +++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
    34.3 @@ -1,687 +0,0 @@
    34.4 -"""
    34.5 -Web application
    34.6 -(from web.py)
    34.7 -"""
    34.8 -import webapi as web
    34.9 -import webapi, wsgi, utils
   34.10 -import debugerror
   34.11 -import httpserver
   34.12 -
   34.13 -from utils import lstrips, safeunicode
   34.14 -import sys
   34.15 -
   34.16 -import urllib
   34.17 -import traceback
   34.18 -import itertools
   34.19 -import os
   34.20 -import types
   34.21 -from exceptions import SystemExit
   34.22 -
   34.23 -try:
   34.24 -    import wsgiref.handlers
   34.25 -except ImportError:
   34.26 -    pass # don't break people with old Pythons
   34.27 -
   34.28 -__all__ = [
   34.29 -    "application", "auto_application",
   34.30 -    "subdir_application", "subdomain_application", 
   34.31 -    "loadhook", "unloadhook",
   34.32 -    "autodelegate"
   34.33 -]
   34.34 -
   34.35 -class application:
   34.36 -    """
   34.37 -    Application to delegate requests based on path.
   34.38 -    
   34.39 -        >>> urls = ("/hello", "hello")
   34.40 -        >>> app = application(urls, globals())
   34.41 -        >>> class hello:
   34.42 -        ...     def GET(self): return "hello"
   34.43 -        >>>
   34.44 -        >>> app.request("/hello").data
   34.45 -        'hello'
   34.46 -    """
   34.47 -    def __init__(self, mapping=(), fvars={}, autoreload=None):
   34.48 -        if autoreload is None:
   34.49 -            autoreload = web.config.get('debug', False)
   34.50 -        self.init_mapping(mapping)
   34.51 -        self.fvars = fvars
   34.52 -        self.processors = []
   34.53 -        
   34.54 -        self.add_processor(loadhook(self._load))
   34.55 -        self.add_processor(unloadhook(self._unload))
   34.56 -        
   34.57 -        if autoreload:
   34.58 -            def main_module_name():
   34.59 -                mod = sys.modules['__main__']
   34.60 -                file = getattr(mod, '__file__', None) # make sure this works even from python interpreter
   34.61 -                return file and os.path.splitext(os.path.basename(file))[0]
   34.62 -
   34.63 -            def modname(fvars):
   34.64 -                """find name of the module name from fvars."""
   34.65 -                file, name = fvars.get('__file__'), fvars.get('__name__')
   34.66 -                if file is None or name is None:
   34.67 -                    return None
   34.68 -
   34.69 -                if name == '__main__':
   34.70 -                    # Since the __main__ module can't be reloaded, the module has 
   34.71 -                    # to be imported using its file name.                    
   34.72 -                    name = main_module_name()
   34.73 -                return name
   34.74 -                
   34.75 -            mapping_name = utils.dictfind(fvars, mapping)
   34.76 -            module_name = modname(fvars)
   34.77 -            
   34.78 -            def reload_mapping():
   34.79 -                """loadhook to reload mapping and fvars."""
   34.80 -                mod = __import__(module_name, None, None, [''])
   34.81 -                mapping = getattr(mod, mapping_name, None)
   34.82 -                if mapping:
   34.83 -                    self.fvars = mod.__dict__
   34.84 -                    self.init_mapping(mapping)
   34.85 -
   34.86 -            self.add_processor(loadhook(Reloader()))
   34.87 -            if mapping_name and module_name:
   34.88 -                self.add_processor(loadhook(reload_mapping))
   34.89 -
   34.90 -            # load __main__ module usings its filename, so that it can be reloaded.
   34.91 -            if main_module_name() and '__main__' in sys.argv:
   34.92 -                try:
   34.93 -                    __import__(main_module_name())
   34.94 -                except ImportError:
   34.95 -                    pass
   34.96 -                    
   34.97 -    def _load(self):
   34.98 -        web.ctx.app_stack.append(self)
   34.99 -        
  34.100 -    def _unload(self):
  34.101 -        web.ctx.app_stack = web.ctx.app_stack[:-1]
  34.102 -        
  34.103 -        if web.ctx.app_stack:
  34.104 -            # this is a sub-application, revert ctx to earlier state.
  34.105 -            oldctx = web.ctx.get('_oldctx')
  34.106 -            if oldctx:
  34.107 -                web.ctx.home = oldctx.home
  34.108 -                web.ctx.homepath = oldctx.homepath
  34.109 -                web.ctx.path = oldctx.path
  34.110 -                web.ctx.fullpath = oldctx.fullpath
  34.111 -                
  34.112 -    def _cleanup(self):
  34.113 -        # Threads can be recycled by WSGI servers.
  34.114 -        # Clearing up all thread-local state to avoid interefereing with subsequent requests.
  34.115 -        utils.ThreadedDict.clear_all()
  34.116 -
  34.117 -    def init_mapping(self, mapping):
  34.118 -        self.mapping = list(utils.group(mapping, 2))
  34.119 -
  34.120 -    def add_mapping(self, pattern, classname):
  34.121 -        self.mapping.append((pattern, classname))
  34.122 -
  34.123 -    def add_processor(self, processor):
  34.124 -        """
  34.125 -        Adds a processor to the application. 
  34.126 -        
  34.127 -            >>> urls = ("/(.*)", "echo")
  34.128 -            >>> app = application(urls, globals())
  34.129 -            >>> class echo:
  34.130 -            ...     def GET(self, name): return name
  34.131 -            ...
  34.132 -            >>>
  34.133 -            >>> def hello(handler): return "hello, " +  handler()
  34.134 -            ...
  34.135 -            >>> app.add_processor(hello)
  34.136 -            >>> app.request("/web.py").data
  34.137 -            'hello, web.py'
  34.138 -        """
  34.139 -        self.processors.append(processor)
  34.140 -
  34.141 -    def request(self, localpart='/', method='GET', data=None,
  34.142 -                host="0.0.0.0:8080", headers=None, https=False, **kw):
  34.143 -        """Makes request to this application for the specified path and method.
  34.144 -        Response will be a storage object with data, status and headers.
  34.145 -
  34.146 -            >>> urls = ("/hello", "hello")
  34.147 -            >>> app = application(urls, globals())
  34.148 -            >>> class hello:
  34.149 -            ...     def GET(self): 
  34.150 -            ...         web.header('Content-Type', 'text/plain')
  34.151 -            ...         return "hello"
  34.152 -            ...
  34.153 -            >>> response = app.request("/hello")
  34.154 -            >>> response.data
  34.155 -            'hello'
  34.156 -            >>> response.status
  34.157 -            '200 OK'
  34.158 -            >>> response.headers['Content-Type']
  34.159 -            'text/plain'
  34.160 -
  34.161 -        To use https, use https=True.
  34.162 -
  34.163 -            >>> urls = ("/redirect", "redirect")
  34.164 -            >>> app = application(urls, globals())
  34.165 -            >>> class redirect:
  34.166 -            ...     def GET(self): raise web.seeother("/foo")
  34.167 -            ...
  34.168 -            >>> response = app.request("/redirect")
  34.169 -            >>> response.headers['Location']
  34.170 -            'http://0.0.0.0:8080/foo'
  34.171 -            >>> response = app.request("/redirect", https=True)
  34.172 -            >>> response.headers['Location']
  34.173 -            'https://0.0.0.0:8080/foo'
  34.174 -
  34.175 -        The headers argument specifies HTTP headers as a mapping object
  34.176 -        such as a dict.
  34.177 -
  34.178 -            >>> urls = ('/ua', 'uaprinter')
  34.179 -            >>> class uaprinter:
  34.180 -            ...     def GET(self):
  34.181 -            ...         return 'your user-agent is ' + web.ctx.env['HTTP_USER_AGENT']
  34.182 -            ... 
  34.183 -            >>> app = application(urls, globals())
  34.184 -            >>> app.request('/ua', headers = {
  34.185 -            ...      'User-Agent': 'a small jumping bean/1.0 (compatible)'
  34.186 -            ... }).data
  34.187 -            'your user-agent is a small jumping bean/1.0 (compatible)'
  34.188 -
  34.189 -        """
  34.190 -        path, maybe_query = urllib.splitquery(localpart)
  34.191 -        query = maybe_query or ""
  34.192 -        
  34.193 -        if 'env' in kw:
  34.194 -            env = kw['env']
  34.195 -        else:
  34.196 -            env = {}
  34.197 -        env = dict(env, HTTP_HOST=host, REQUEST_METHOD=method, PATH_INFO=path, QUERY_STRING=query, HTTPS=str(https))
  34.198 -        headers = headers or {}
  34.199 -
  34.200 -        for k, v in headers.items():
  34.201 -            env['HTTP_' + k.upper().replace('-', '_')] = v
  34.202 -
  34.203 -        if 'HTTP_CONTENT_LENGTH' in env:
  34.204 -            env['CONTENT_LENGTH'] = env.pop('HTTP_CONTENT_LENGTH')
  34.205 -
  34.206 -        if 'HTTP_CONTENT_TYPE' in env:
  34.207 -            env['CONTENT_TYPE'] = env.pop('HTTP_CONTENT_TYPE')
  34.208 -
  34.209 -        if method not in ["HEAD", "GET"]:
  34.210 -            data = data or ''
  34.211 -            import StringIO
  34.212 -            if isinstance(data, dict):
  34.213 -                q = urllib.urlencode(data)
  34.214 -            else:
  34.215 -                q = data
  34.216 -            env['wsgi.input'] = StringIO.StringIO(q)
  34.217 -            if not env.get('CONTENT_TYPE', '').lower().startswith('multipart/') and 'CONTENT_LENGTH' not in env:
  34.218 -                env['CONTENT_LENGTH'] = len(q)
  34.219 -        response = web.storage()
  34.220 -        def start_response(status, headers):
  34.221 -            response.status = status
  34.222 -            response.headers = dict(headers)
  34.223 -            response.header_items = headers
  34.224 -        response.data = "".join(self.wsgifunc()(env, start_response))
  34.225 -        return response
  34.226 -
  34.227 -    def browser(self):
  34.228 -        import browser
  34.229 -        return browser.AppBrowser(self)
  34.230 -
  34.231 -    def handle(self):
  34.232 -        fn, args = self._match(self.mapping, web.ctx.path)
  34.233 -        return self._delegate(fn, self.fvars, args)
  34.234 -        
  34.235 -    def handle_with_processors(self):
  34.236 -        def process(processors):
  34.237 -            try:
  34.238 -                if processors:
  34.239 -                    p, processors = processors[0], processors[1:]
  34.240 -                    return p(lambda: process(processors))
  34.241 -                else:
  34.242 -                    return self.handle()
  34.243 -            except web.HTTPError:
  34.244 -                raise
  34.245 -            except (KeyboardInterrupt, SystemExit):
  34.246 -                raise
  34.247 -            except:
  34.248 -                print >> web.debug, traceback.format_exc()
  34.249 -                raise self.internalerror()
  34.250 -        
  34.251 -        # processors must be applied in the resvere order. (??)
  34.252 -        return process(self.processors)
  34.253 -                        
  34.254 -    def wsgifunc(self, *middleware):
  34.255 -        """Returns a WSGI-compatible function for this application."""
  34.256 -        def peep(iterator):
  34.257 -            """Peeps into an iterator by doing an iteration
  34.258 -            and returns an equivalent iterator.
  34.259 -            """
  34.260 -            # wsgi requires the headers first
  34.261 -            # so we need to do an iteration
  34.262 -            # and save the result for later
  34.263 -            try:
  34.264 -                firstchunk = iterator.next()
  34.265 -            except StopIteration:
  34.266 -                firstchunk = ''
  34.267 -
  34.268 -            return itertools.chain([firstchunk], iterator)    
  34.269 -                                
  34.270 -        def is_generator(x): return x and hasattr(x, 'next')
  34.271 -        
  34.272 -        def wsgi(env, start_resp):
  34.273 -            # clear threadlocal to avoid inteference of previous requests
  34.274 -            self._cleanup()
  34.275 -
  34.276 -            self.load(env)
  34.277 -            try:
  34.278 -                # allow uppercase methods only
  34.279 -                if web.ctx.method.upper() != web.ctx.method:
  34.280 -                    raise web.nomethod()
  34.281 -
  34.282 -                result = self.handle_with_processors()
  34.283 -                if is_generator(result):
  34.284 -                    result = peep(result)
  34.285 -                else:
  34.286 -                    result = [result]
  34.287 -            except web.HTTPError, e:
  34.288 -                result = [e.data]
  34.289 -
  34.290 -            result = web.safestr(iter(result))
  34.291 -
  34.292 -            status, headers = web.ctx.status, web.ctx.headers
  34.293 -            start_resp(status, headers)
  34.294 -            
  34.295 -            def cleanup():
  34.296 -                self._cleanup()
  34.297 -                yield '' # force this function to be a generator
  34.298 -                            
  34.299 -            return itertools.chain(result, cleanup())
  34.300 -
  34.301 -        for m in middleware: 
  34.302 -            wsgi = m(wsgi)
  34.303 -
  34.304 -        return wsgi
  34.305 -
  34.306 -    def run(self, *middleware):
  34.307 -        """
  34.308 -        Starts handling requests. If called in a CGI or FastCGI context, it will follow
  34.309 -        that protocol. If called from the command line, it will start an HTTP
  34.310 -        server on the port named in the first command line argument, or, if there
  34.311 -        is no argument, on port 8080.
  34.312 -        
  34.313 -        `middleware` is a list of WSGI middleware which is applied to the resulting WSGI
  34.314 -        function.
  34.315 -        """
  34.316 -        return wsgi.runwsgi(self.wsgifunc(*middleware))
  34.317 -
  34.318 -    def stop(self):
  34.319 -        """Stops the http server started by run.
  34.320 -        """
  34.321 -        if httpserver.server:
  34.322 -            httpserver.server.stop()
  34.323 -            httpserver.server = None
  34.324 -    
  34.325 -    def cgirun(self, *middleware):
  34.326 -        """
  34.327 -        Return a CGI handler. This is mostly useful with Google App Engine.
  34.328 -        There you can just do:
  34.329 -        
  34.330 -            main = app.cgirun()
  34.331 -        """
  34.332 -        wsgiapp = self.wsgifunc(*middleware)
  34.333 -
  34.334 -        try:
  34.335 -            from google.appengine.ext.webapp.util import run_wsgi_app
  34.336 -            return run_wsgi_app(wsgiapp)
  34.337 -        except ImportError:
  34.338 -            # we're not running from within Google App Engine
  34.339 -            return wsgiref.handlers.CGIHandler().run(wsgiapp)
  34.340 -    
  34.341 -    def load(self, env):
  34.342 -        """Initializes ctx using env."""
  34.343 -        ctx = web.ctx
  34.344 -        ctx.clear()
  34.345 -        ctx.status = '200 OK'
  34.346 -        ctx.headers = []
  34.347 -        ctx.output = ''
  34.348 -        ctx.environ = ctx.env = env
  34.349 -        ctx.host = env.get('HTTP_HOST')
  34.350 -
  34.351 -        if env.get('wsgi.url_scheme') in ['http', 'https']:
  34.352 -            ctx.protocol = env['wsgi.url_scheme']
  34.353 -        elif env.get('HTTPS', '').lower() in ['on', 'true', '1']:
  34.354 -            ctx.protocol = 'https'
  34.355 -        else:
  34.356 -            ctx.protocol = 'http'
  34.357 -        ctx.homedomain = ctx.protocol + '://' + env.get('HTTP_HOST', '[unknown]')
  34.358 -        ctx.homepath = os.environ.get('REAL_SCRIPT_NAME', env.get('SCRIPT_NAME', ''))
  34.359 -        ctx.home = ctx.homedomain + ctx.homepath
  34.360 -        #@@ home is changed when the request is handled to a sub-application.
  34.361 -        #@@ but the real home is required for doing absolute redirects.
  34.362 -        ctx.realhome = ctx.home
  34.363 -        ctx.ip = env.get('REMOTE_ADDR')
  34.364 -        ctx.method = env.get('REQUEST_METHOD')
  34.365 -        ctx.path = env.get('PATH_INFO')
  34.366 -        # http://trac.lighttpd.net/trac/ticket/406 requires:
  34.367 -        if env.get('SERVER_SOFTWARE', '').startswith('lighttpd/'):
  34.368 -            ctx.path = lstrips(env.get('REQUEST_URI').split('?')[0], ctx.homepath)
  34.369 -            # Apache and CherryPy webservers unquote the url but lighttpd doesn't. 
  34.370 -            # unquote explicitly for lighttpd to make ctx.path uniform across all servers.
  34.371 -            ctx.path = urllib.unquote(ctx.path)
  34.372 -
  34.373 -        if env.get('QUERY_STRING'):
  34.374 -            ctx.query = '?' + env.get('QUERY_STRING', '')
  34.375 -        else:
  34.376 -            ctx.query = ''
  34.377 -
  34.378 -        ctx.fullpath = ctx.path + ctx.query
  34.379 -        
  34.380 -        for k, v in ctx.iteritems():
  34.381 -            # convert all string values to unicode values and replace 
  34.382 -            # malformed data with a suitable replacement marker.
  34.383 -            if isinstance(v, str):
  34.384 -                ctx[k] = v.decode('utf-8', 'replace') 
  34.385 -
  34.386 -        # status must always be str
  34.387 -        ctx.status = '200 OK'
  34.388 -        
  34.389 -        ctx.app_stack = []
  34.390 -
  34.391 -    def _delegate(self, f, fvars, args=[]):
  34.392 -        def handle_class(cls):
  34.393 -            meth = web.ctx.method
  34.394 -            if meth == 'HEAD' and not hasattr(cls, meth):
  34.395 -                meth = 'GET'
  34.396 -            if not hasattr(cls, meth):
  34.397 -                raise web.nomethod(cls)
  34.398 -            tocall = getattr(cls(), meth)
  34.399 -            return tocall(*args)
  34.400 -            
  34.401 -        def is_class(o): return isinstance(o, (types.ClassType, type))
  34.402 -            
  34.403 -        if f is None:
  34.404 -            raise web.notfound()
  34.405 -        elif isinstance(f, application):
  34.406 -            return f.handle_with_processors()
  34.407 -        elif is_class(f):
  34.408 -            return handle_class(f)
  34.409 -        elif isinstance(f, basestring):
  34.410 -            if f.startswith('redirect '):
  34.411 -                url = f.split(' ', 1)[1]
  34.412 -                if web.ctx.method == "GET":
  34.413 -                    x = web.ctx.env.get('QUERY_STRING', '')
  34.414 -                    if x:
  34.415 -                        url += '?' + x
  34.416 -                raise web.redirect(url)
  34.417 -            elif '.' in f:
  34.418 -                mod, cls = f.rsplit('.', 1)
  34.419 -                mod = __import__(mod, None, None, [''])
  34.420 -                cls = getattr(mod, cls)
  34.421 -            else:
  34.422 -                cls = fvars[f]
  34.423 -            return handle_class(cls)
  34.424 -        elif hasattr(f, '__call__'):
  34.425 -            return f()
  34.426 -        else:
  34.427 -            return web.notfound()
  34.428 -
  34.429 -    def _match(self, mapping, value):
  34.430 -        for pat, what in mapping:
  34.431 -            if isinstance(what, application):
  34.432 -                if value.startswith(pat):
  34.433 -                    f = lambda: self._delegate_sub_application(pat, what)
  34.434 -                    return f, None
  34.435 -                else:
  34.436 -                    continue
  34.437 -            elif isinstance(what, basestring):
  34.438 -                what, result = utils.re_subm('^' + pat + '$', what, value)
  34.439 -            else:
  34.440 -                result = utils.re_compile('^' + pat + '$').match(value)
  34.441 -                
  34.442 -            if result: # it's a match
  34.443 -                return what, [x for x in result.groups()]
  34.444 -        return None, None
  34.445 -        
  34.446 -    def _delegate_sub_application(self, dir, app):
  34.447 -        """Deletes request to sub application `app` rooted at the directory `dir`.
  34.448 -        The home, homepath, path and fullpath values in web.ctx are updated to mimic request
  34.449 -        to the subapp and are restored after it is handled. 
  34.450 -        
  34.451 -        @@Any issues with when used with yield?
  34.452 -        """
  34.453 -        web.ctx._oldctx = web.storage(web.ctx)
  34.454 -        web.ctx.home += dir
  34.455 -        web.ctx.homepath += dir
  34.456 -        web.ctx.path = web.ctx.path[len(dir):]
  34.457 -        web.ctx.fullpath = web.ctx.fullpath[len(dir):]
  34.458 -        return app.handle_with_processors()
  34.459 -            
  34.460 -    def get_parent_app(self):
  34.461 -        if self in web.ctx.app_stack:
  34.462 -            index = web.ctx.app_stack.index(self)
  34.463 -            if index > 0:
  34.464 -                return web.ctx.app_stack[index-1]
  34.465 -        
  34.466 -    def notfound(self):
  34.467 -        """Returns HTTPError with '404 not found' message"""
  34.468 -        parent = self.get_parent_app()
  34.469 -        if parent:
  34.470 -            return parent.notfound()
  34.471 -        else:
  34.472 -            return web._NotFound()
  34.473 -            
  34.474 -    def internalerror(self):
  34.475 -        """Returns HTTPError with '500 internal error' message"""
  34.476 -        parent = self.get_parent_app()
  34.477 -        if parent:
  34.478 -            return parent.internalerror()
  34.479 -        elif web.config.get('debug'):
  34.480 -            import debugerror
  34.481 -            return debugerror.debugerror()
  34.482 -        else:
  34.483 -            return web._InternalError()
  34.484 -
  34.485 -class auto_application(application):
  34.486 -    """Application similar to `application` but urls are constructed 
  34.487 -    automatiacally using metaclass.
  34.488 -
  34.489 -        >>> app = auto_application()
  34.490 -        >>> class hello(app.page):
  34.491 -        ...     def GET(self): return "hello, world"
  34.492 -        ...
  34.493 -        >>> class foo(app.page):
  34.494 -        ...     path = '/foo/.*'
  34.495 -        ...     def GET(self): return "foo"
  34.496 -        >>> app.request("/hello").data
  34.497 -        'hello, world'
  34.498 -        >>> app.request('/foo/bar').data
  34.499 -        'foo'
  34.500 -    """
  34.501 -    def __init__(self):
  34.502 -        application.__init__(self)
  34.503 -
  34.504 -        class metapage(type):
  34.505 -            def __init__(klass, name, bases, attrs):
  34.506 -                type.__init__(klass, name, bases, attrs)
  34.507 -                path = attrs.get('path', '/' + name)
  34.508 -
  34.509 -                # path can be specified as None to ignore that class
  34.510 -                # typically required to create a abstract base class.
  34.511 -                if path is not None:
  34.512 -                    self.add_mapping(path, klass)
  34.513 -
  34.514 -        class page:
  34.515 -            path = None
  34.516 -            __metaclass__ = metapage
  34.517 -
  34.518 -        self.page = page
  34.519 -
  34.520 -# The application class already has the required functionality of subdir_application
  34.521 -subdir_application = application
  34.522 -                
  34.523 -class subdomain_application(application):
  34.524 -    """
  34.525 -    Application to delegate requests based on the host.
  34.526 -
  34.527 -        >>> urls = ("/hello", "hello")
  34.528 -        >>> app = application(urls, globals())
  34.529 -        >>> class hello:
  34.530 -        ...     def GET(self): return "hello"
  34.531 -        >>>
  34.532 -        >>> mapping = (r"hello\.example\.com", app)
  34.533 -        >>> app2 = subdomain_application(mapping)
  34.534 -        >>> app2.request("/hello", host="hello.example.com").data
  34.535 -        'hello'
  34.536 -        >>> response = app2.request("/hello", host="something.example.com")
  34.537 -        >>> response.status
  34.538 -        '404 Not Found'
  34.539 -        >>> response.data
  34.540 -        'not found'
  34.541 -    """
  34.542 -    def handle(self):
  34.543 -        host = web.ctx.host.split(':')[0] #strip port
  34.544 -        fn, args = self._match(self.mapping, host)
  34.545 -        return self._delegate(fn, self.fvars, args)
  34.546 -        
  34.547 -    def _match(self, mapping, value):
  34.548 -        for pat, what in mapping:
  34.549 -            if isinstance(what, basestring):
  34.550 -                what, result = utils.re_subm('^' + pat + '$', what, value)
  34.551 -            else:
  34.552 -                result = utils.re_compile('^' + pat + '$').match(value)
  34.553 -
  34.554 -            if result: # it's a match
  34.555 -                return what, [x for x in result.groups()]
  34.556 -        return None, None
  34.557 -        
  34.558 -def loadhook(h):
  34.559 -    """
  34.560 -    Converts a load hook into an application processor.
  34.561 -    
  34.562 -        >>> app = auto_application()
  34.563 -        >>> def f(): "something done before handling request"
  34.564 -        ...
  34.565 -        >>> app.add_processor(loadhook(f))
  34.566 -    """
  34.567 -    def processor(handler):
  34.568 -        h()
  34.569 -        return handler()
  34.570 -        
  34.571 -    return processor
  34.572 -    
  34.573 -def unloadhook(h):
  34.574 -    """
  34.575 -    Converts an unload hook into an application processor.
  34.576 -    
  34.577 -        >>> app = auto_application()
  34.578 -        >>> def f(): "something done after handling request"
  34.579 -        ...
  34.580 -        >>> app.add_processor(unloadhook(f))    
  34.581 -    """
  34.582 -    def processor(handler):
  34.583 -        try:
  34.584 -            result = handler()
  34.585 -            is_generator = result and hasattr(result, 'next')
  34.586 -        except:
  34.587 -            # run the hook even when handler raises some exception
  34.588 -            h()
  34.589 -            raise
  34.590 -
  34.591 -        if is_generator:
  34.592 -            return wrap(result)
  34.593 -        else:
  34.594 -            h()
  34.595 -            return result
  34.596 -            
  34.597 -    def wrap(result):
  34.598 -        def next():
  34.599 -            try:
  34.600 -                return result.next()
  34.601 -            except:
  34.602 -                # call the hook at the and of iterator
  34.603 -                h()
  34.604 -                raise
  34.605 -
  34.606 -        result = iter(result)
  34.607 -        while True:
  34.608 -            yield next()
  34.609 -            
  34.610 -    return processor
  34.611 -
  34.612 -def autodelegate(prefix=''):
  34.613 -    """
  34.614 -    Returns a method that takes one argument and calls the method named prefix+arg,
  34.615 -    calling `notfound()` if there isn't one. Example:
  34.616 -
  34.617 -        urls = ('/prefs/(.*)', 'prefs')
  34.618 -
  34.619 -        class prefs:
  34.620 -            GET = autodelegate('GET_')
  34.621 -            def GET_password(self): pass
  34.622 -            def GET_privacy(self): pass
  34.623 -
  34.624 -    `GET_password` would get called for `/prefs/password` while `GET_privacy` for 
  34.625 -    `GET_privacy` gets called for `/prefs/privacy`.
  34.626 -    
  34.627 -    If a user visits `/prefs/password/change` then `GET_password(self, '/change')`
  34.628 -    is called.
  34.629 -    """
  34.630 -    def internal(self, arg):
  34.631 -        if '/' in arg:
  34.632 -            first, rest = arg.split('/', 1)
  34.633 -            func = prefix + first
  34.634 -            args = ['/' + rest]
  34.635 -        else:
  34.636 -            func = prefix + arg
  34.637 -            args = []
  34.638 -        
  34.639 -        if hasattr(self, func):
  34.640 -            try:
  34.641 -                return getattr(self, func)(*args)
  34.642 -            except TypeError:
  34.643 -                raise web.notfound()
  34.644 -        else:
  34.645 -            raise web.notfound()
  34.646 -    return internal
  34.647 -
  34.648 -class Reloader:
  34.649 -    """Checks to see if any loaded modules have changed on disk and, 
  34.650 -    if so, reloads them.
  34.651 -    """
  34.652 -
  34.653 -    """File suffix of compiled modules."""
  34.654 -    if sys.platform.startswith('java'):
  34.655 -        SUFFIX = '$py.class'
  34.656 -    else:
  34.657 -        SUFFIX = '.pyc'
  34.658 -    
  34.659 -    def __init__(self):
  34.660 -        self.mtimes = {}
  34.661 -
  34.662 -    def __call__(self):
  34.663 -        for mod in sys.modules.values():
  34.664 -            self.check(mod)
  34.665 -
  34.666 -    def check(self, mod):
  34.667 -        # jython registers java packages as modules but they either
  34.668 -        # don't have a __file__ attribute or its value is None
  34.669 -        if not (mod and hasattr(mod, '__file__') and mod.__file__):
  34.670 -            return
  34.671 -
  34.672 -        try: 
  34.673 -            mtime = os.stat(mod.__file__).st_mtime
  34.674 -        except (OSError, IOError):
  34.675 -            return
  34.676 -        if mod.__file__.endswith(self.__class__.SUFFIX) and os.path.exists(mod.__file__[:-1]):
  34.677 -            mtime = max(os.stat(mod.__file__[:-1]).st_mtime, mtime)
  34.678 -            
  34.679 -        if mod not in self.mtimes:
  34.680 -            self.mtimes[mod] = mtime
  34.681 -        elif self.mtimes[mod] < mtime:
  34.682 -            try: 
  34.683 -                reload(mod)
  34.684 -                self.mtimes[mod] = mtime
  34.685 -            except ImportError: 
  34.686 -                pass
  34.687 -                
  34.688 -if __name__ == "__main__":
  34.689 -    import doctest
  34.690 -    doctest.testmod()
    35.1 Binary file OpenSecurity/install/web.py-0.37/web/application.pyc has changed
    36.1 --- a/OpenSecurity/install/web.py-0.37/web/browser.py	Thu Feb 20 15:40:48 2014 +0100
    36.2 +++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
    36.3 @@ -1,236 +0,0 @@
    36.4 -"""Browser to test web applications.
    36.5 -(from web.py)
    36.6 -"""
    36.7 -from utils import re_compile
    36.8 -from net import htmlunquote
    36.9 -
   36.10 -import httplib, urllib, urllib2
   36.11 -import copy
   36.12 -from StringIO import StringIO
   36.13 -
   36.14 -DEBUG = False
   36.15 -
   36.16 -__all__ = [
   36.17 -    "BrowserError",
   36.18 -    "Browser", "AppBrowser",
   36.19 -    "AppHandler"
   36.20 -]
   36.21 -
   36.22 -class BrowserError(Exception):
   36.23 -    pass
   36.24 -
   36.25 -class Browser:
   36.26 -    def __init__(self):
   36.27 -        import cookielib
   36.28 -        self.cookiejar = cookielib.CookieJar()
   36.29 -        self._cookie_processor = urllib2.HTTPCookieProcessor(self.cookiejar)
   36.30 -        self.form = None
   36.31 -
   36.32 -        self.url = "http://0.0.0.0:8080/"
   36.33 -        self.path = "/"
   36.34 -        
   36.35 -        self.status = None
   36.36 -        self.data = None
   36.37 -        self._response = None
   36.38 -        self._forms = None
   36.39 -
   36.40 -    def reset(self):
   36.41 -        """Clears all cookies and history."""
   36.42 -        self.cookiejar.clear()
   36.43 -
   36.44 -    def build_opener(self):
   36.45 -        """Builds the opener using urllib2.build_opener. 
   36.46 -        Subclasses can override this function to prodive custom openers.
   36.47 -        """
   36.48 -        return urllib2.build_opener()
   36.49 -
   36.50 -    def do_request(self, req):
   36.51 -        if DEBUG:
   36.52 -            print 'requesting', req.get_method(), req.get_full_url()
   36.53 -        opener = self.build_opener()
   36.54 -        opener.add_handler(self._cookie_processor)
   36.55 -        try:
   36.56 -            self._response = opener.open(req)
   36.57 -        except urllib2.HTTPError, e:
   36.58 -            self._response = e
   36.59 -
   36.60 -        self.url = self._response.geturl()
   36.61 -        self.path = urllib2.Request(self.url).get_selector()
   36.62 -        self.data = self._response.read()
   36.63 -        self.status = self._response.code
   36.64 -        self._forms = None
   36.65 -        self.form = None
   36.66 -        return self.get_response()
   36.67 -
   36.68 -    def open(self, url, data=None, headers={}):
   36.69 -        """Opens the specified url."""
   36.70 -        url = urllib.basejoin(self.url, url)
   36.71 -        req = urllib2.Request(url, data, headers)
   36.72 -        return self.do_request(req)
   36.73 -
   36.74 -    def show(self):
   36.75 -        """Opens the current page in real web browser."""
   36.76 -        f = open('page.html', 'w')
   36.77 -        f.write(self.data)
   36.78 -        f.close()
   36.79 -
   36.80 -        import webbrowser, os
   36.81 -        url = 'file://' + os.path.abspath('page.html')
   36.82 -        webbrowser.open(url)
   36.83 -
   36.84 -    def get_response(self):
   36.85 -        """Returns a copy of the current response."""
   36.86 -        return urllib.addinfourl(StringIO(self.data), self._response.info(), self._response.geturl())
   36.87 -
   36.88 -    def get_soup(self):
   36.89 -        """Returns beautiful soup of the current document."""
   36.90 -        import BeautifulSoup
   36.91 -        return BeautifulSoup.BeautifulSoup(self.data)
   36.92 -
   36.93 -    def get_text(self, e=None):
   36.94 -        """Returns content of e or the current document as plain text."""
   36.95 -        e = e or self.get_soup()
   36.96 -        return ''.join([htmlunquote(c) for c in e.recursiveChildGenerator() if isinstance(c, unicode)])
   36.97 -
   36.98 -    def _get_links(self):
   36.99 -        soup = self.get_soup()
  36.100 -        return [a for a in soup.findAll(name='a')]
  36.101 -        
  36.102 -    def get_links(self, text=None, text_regex=None, url=None, url_regex=None, predicate=None):
  36.103 -        """Returns all links in the document."""
  36.104 -        return self._filter_links(self._get_links(),
  36.105 -            text=text, text_regex=text_regex, url=url, url_regex=url_regex, predicate=predicate)
  36.106 -
  36.107 -    def follow_link(self, link=None, text=None, text_regex=None, url=None, url_regex=None, predicate=None):
  36.108 -        if link is None:
  36.109 -            links = self._filter_links(self.get_links(),
  36.110 -                text=text, text_regex=text_regex, url=url, url_regex=url_regex, predicate=predicate)
  36.111 -            link = links and links[0]
  36.112 -            
  36.113 -        if link:
  36.114 -            return self.open(link['href'])
  36.115 -        else:
  36.116 -            raise BrowserError("No link found")
  36.117 -            
  36.118 -    def find_link(self, text=None, text_regex=None, url=None, url_regex=None, predicate=None):
  36.119 -        links = self._filter_links(self.get_links(), 
  36.120 -            text=text, text_regex=text_regex, url=url, url_regex=url_regex, predicate=predicate)
  36.121 -        return links and links[0] or None
  36.122 -            
  36.123 -    def _filter_links(self, links, 
  36.124 -            text=None, text_regex=None,
  36.125 -            url=None, url_regex=None,
  36.126 -            predicate=None):
  36.127 -        predicates = []
  36.128 -        if text is not None:
  36.129 -            predicates.append(lambda link: link.string == text)
  36.130 -        if text_regex is not None:
  36.131 -            predicates.append(lambda link: re_compile(text_regex).search(link.string or ''))
  36.132 -        if url is not None:
  36.133 -            predicates.append(lambda link: link.get('href') == url)
  36.134 -        if url_regex is not None:
  36.135 -            predicates.append(lambda link: re_compile(url_regex).search(link.get('href', '')))
  36.136 -        if predicate:
  36.137 -            predicate.append(predicate)
  36.138 -
  36.139 -        def f(link):
  36.140 -            for p in predicates:
  36.141 -                if not p(link):
  36.142 -                    return False
  36.143 -            return True
  36.144 -
  36.145 -        return [link for link in links if f(link)]
  36.146 -
  36.147 -    def get_forms(self):
  36.148 -        """Returns all forms in the current document.
  36.149 -        The returned form objects implement the ClientForm.HTMLForm interface.
  36.150 -        """
  36.151 -        if self._forms is None:
  36.152 -            import ClientForm
  36.153 -            self._forms = ClientForm.ParseResponse(self.get_response(), backwards_compat=False)
  36.154 -        return self._forms
  36.155 -
  36.156 -    def select_form(self, name=None, predicate=None, index=0):
  36.157 -        """Selects the specified form."""
  36.158 -        forms = self.get_forms()
  36.159 -
  36.160 -        if name is not None:
  36.161 -            forms = [f for f in forms if f.name == name]
  36.162 -        if predicate:
  36.163 -            forms = [f for f in forms if predicate(f)]
  36.164 -            
  36.165 -        if forms:
  36.166 -            self.form = forms[index]
  36.167 -            return self.form
  36.168 -        else:
  36.169 -            raise BrowserError("No form selected.")
  36.170 -        
  36.171 -    def submit(self, **kw):
  36.172 -        """submits the currently selected form."""
  36.173 -        if self.form is None:
  36.174 -            raise BrowserError("No form selected.")
  36.175 -        req = self.form.click(**kw)
  36.176 -        return self.do_request(req)
  36.177 -
  36.178 -    def __getitem__(self, key):
  36.179 -        return self.form[key]
  36.180 -
  36.181 -    def __setitem__(self, key, value):
  36.182 -        self.form[key] = value
  36.183 -
  36.184 -class AppBrowser(Browser):
  36.185 -    """Browser interface to test web.py apps.
  36.186 -    
  36.187 -        b = AppBrowser(app)
  36.188 -        b.open('/')
  36.189 -        b.follow_link(text='Login')
  36.190 -        
  36.191 -        b.select_form(name='login')
  36.192 -        b['username'] = 'joe'
  36.193 -        b['password'] = 'secret'
  36.194 -        b.submit()
  36.195 -
  36.196 -        assert b.path == '/'
  36.197 -        assert 'Welcome joe' in b.get_text()
  36.198 -    """
  36.199 -    def __init__(self, app):
  36.200 -        Browser.__init__(self)
  36.201 -        self.app = app
  36.202 -
  36.203 -    def build_opener(self):
  36.204 -        return urllib2.build_opener(AppHandler(self.app))
  36.205 -
  36.206 -class AppHandler(urllib2.HTTPHandler):
  36.207 -    """urllib2 handler to handle requests using web.py application."""
  36.208 -    handler_order = 100
  36.209 -
  36.210 -    def __init__(self, app):
  36.211 -        self.app = app
  36.212 -
  36.213 -    def http_open(self, req):
  36.214 -        result = self.app.request(
  36.215 -            localpart=req.get_selector(),
  36.216 -            method=req.get_method(),
  36.217 -            host=req.get_host(),
  36.218 -            data=req.get_data(),
  36.219 -            headers=dict(req.header_items()),
  36.220 -            https=req.get_type() == "https"
  36.221 -        )
  36.222 -        return self._make_response(result, req.get_full_url())
  36.223 -
  36.224 -    def https_open(self, req):
  36.225 -        return self.http_open(req)
  36.226 -    
  36.227 -    try:
  36.228 -        https_request = urllib2.HTTPHandler.do_request_
  36.229 -    except AttributeError:
  36.230 -        # for python 2.3
  36.231 -        pass
  36.232 -
  36.233 -    def _make_response(self, result, url):
  36.234 -        data = "\r\n".join(["%s: %s" % (k, v) for k, v in result.header_items])
  36.235 -        headers = httplib.HTTPMessage(StringIO(data))
  36.236 -        response = urllib.addinfourl(StringIO(result.data), headers, url)
  36.237 -        code, msg = result.status.split(None, 1)
  36.238 -        response.code, response.msg = int(code), msg
  36.239 -        return response
    37.1 Binary file OpenSecurity/install/web.py-0.37/web/browser.pyc has changed
    38.1 --- a/OpenSecurity/install/web.py-0.37/web/contrib/template.py	Thu Feb 20 15:40:48 2014 +0100
    38.2 +++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
    38.3 @@ -1,131 +0,0 @@
    38.4 -"""
    38.5 -Interface to various templating engines.
    38.6 -"""
    38.7 -import os.path
    38.8 -
    38.9 -__all__ = [
   38.10 -    "render_cheetah", "render_genshi", "render_mako",
   38.11 -    "cache", 
   38.12 -]
   38.13 -
   38.14 -class render_cheetah:
   38.15 -    """Rendering interface to Cheetah Templates.
   38.16 -
   38.17 -    Example:
   38.18 -
   38.19 -        render = render_cheetah('templates')
   38.20 -        render.hello(name="cheetah")
   38.21 -    """
   38.22 -    def __init__(self, path):
   38.23 -        # give error if Chetah is not installed
   38.24 -        from Cheetah.Template import Template
   38.25 -        self.path = path
   38.26 -
   38.27 -    def __getattr__(self, name):
   38.28 -        from Cheetah.Template import Template
   38.29 -        path = os.path.join(self.path, name + ".html")
   38.30 -        
   38.31 -        def template(**kw):
   38.32 -            t = Template(file=path, searchList=[kw])
   38.33 -            return t.respond()
   38.34 -
   38.35 -        return template
   38.36 -    
   38.37 -class render_genshi:
   38.38 -    """Rendering interface genshi templates.
   38.39 -    Example:
   38.40 -
   38.41 -    for xml/html templates.
   38.42 -
   38.43 -        render = render_genshi(['templates/'])
   38.44 -        render.hello(name='genshi')
   38.45 -
   38.46 -    For text templates:
   38.47 -
   38.48 -        render = render_genshi(['templates/'], type='text')
   38.49 -        render.hello(name='genshi')
   38.50 -    """
   38.51 -
   38.52 -    def __init__(self, *a, **kwargs):
   38.53 -        from genshi.template import TemplateLoader
   38.54 -
   38.55 -        self._type = kwargs.pop('type', None)
   38.56 -        self._loader = TemplateLoader(*a, **kwargs)
   38.57 -
   38.58 -    def __getattr__(self, name):
   38.59 -        # Assuming all templates are html
   38.60 -        path = name + ".html"
   38.61 -
   38.62 -        if self._type == "text":
   38.63 -            from genshi.template import TextTemplate
   38.64 -            cls = TextTemplate
   38.65 -            type = "text"
   38.66 -        else:
   38.67 -            cls = None
   38.68 -            type = None
   38.69 -
   38.70 -        t = self._loader.load(path, cls=cls)
   38.71 -        def template(**kw):
   38.72 -            stream = t.generate(**kw)
   38.73 -            if type:
   38.74 -                return stream.render(type)
   38.75 -            else:
   38.76 -                return stream.render()
   38.77 -        return template
   38.78 -
   38.79 -class render_jinja:
   38.80 -    """Rendering interface to Jinja2 Templates
   38.81 -    
   38.82 -    Example:
   38.83 -
   38.84 -        render= render_jinja('templates')
   38.85 -        render.hello(name='jinja2')
   38.86 -    """
   38.87 -    def __init__(self, *a, **kwargs):
   38.88 -        extensions = kwargs.pop('extensions', [])
   38.89 -        globals = kwargs.pop('globals', {})
   38.90 -
   38.91 -        from jinja2 import Environment,FileSystemLoader
   38.92 -        self._lookup = Environment(loader=FileSystemLoader(*a, **kwargs), extensions=extensions)
   38.93 -        self._lookup.globals.update(globals)
   38.94 -        
   38.95 -    def __getattr__(self, name):
   38.96 -        # Assuming all templates end with .html
   38.97 -        path = name + '.html'
   38.98 -        t = self._lookup.get_template(path)
   38.99 -        return t.render
  38.100 -        
  38.101 -class render_mako:
  38.102 -    """Rendering interface to Mako Templates.
  38.103 -
  38.104 -    Example:
  38.105 -
  38.106 -        render = render_mako(directories=['templates'])
  38.107 -        render.hello(name="mako")
  38.108 -    """
  38.109 -    def __init__(self, *a, **kwargs):
  38.110 -        from mako.lookup import TemplateLookup
  38.111 -        self._lookup = TemplateLookup(*a, **kwargs)
  38.112 -
  38.113 -    def __getattr__(self, name):
  38.114 -        # Assuming all templates are html
  38.115 -        path = name + ".html"
  38.116 -        t = self._lookup.get_template(path)
  38.117 -        return t.render
  38.118 -
  38.119 -class cache:
  38.120 -    """Cache for any rendering interface.
  38.121 -    
  38.122 -    Example:
  38.123 -
  38.124 -        render = cache(render_cheetah("templates/"))
  38.125 -        render.hello(name='cache')
  38.126 -    """
  38.127 -    def __init__(self, render):
  38.128 -        self._render = render
  38.129 -        self._cache = {}
  38.130 -
  38.131 -    def __getattr__(self, name):
  38.132 -        if name not in self._cache:
  38.133 -            self._cache[name] = getattr(self._render, name)
  38.134 -        return self._cache[name]
    39.1 --- a/OpenSecurity/install/web.py-0.37/web/db.py	Thu Feb 20 15:40:48 2014 +0100
    39.2 +++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
    39.3 @@ -1,1237 +0,0 @@
    39.4 -"""
    39.5 -Database API
    39.6 -(part of web.py)
    39.7 -"""
    39.8 -
    39.9 -__all__ = [
   39.10 -  "UnknownParamstyle", "UnknownDB", "TransactionError", 
   39.11 -  "sqllist", "sqlors", "reparam", "sqlquote",
   39.12 -  "SQLQuery", "SQLParam", "sqlparam",
   39.13 -  "SQLLiteral", "sqlliteral",
   39.14 -  "database", 'DB',
   39.15 -]
   39.16 -
   39.17 -import time
   39.18 -try:
   39.19 -    import datetime
   39.20 -except ImportError:
   39.21 -    datetime = None
   39.22 -
   39.23 -try: set
   39.24 -except NameError:
   39.25 -    from sets import Set as set
   39.26 -    
   39.27 -from utils import threadeddict, storage, iters, iterbetter, safestr, safeunicode
   39.28 -
   39.29 -try:
   39.30 -    # db module can work independent of web.py
   39.31 -    from webapi import debug, config
   39.32 -except:
   39.33 -    import sys
   39.34 -    debug = sys.stderr
   39.35 -    config = storage()
   39.36 -
   39.37 -class UnknownDB(Exception):
   39.38 -    """raised for unsupported dbms"""
   39.39 -    pass
   39.40 -
   39.41 -class _ItplError(ValueError): 
   39.42 -    def __init__(self, text, pos):
   39.43 -        ValueError.__init__(self)
   39.44 -        self.text = text
   39.45 -        self.pos = pos
   39.46 -    def __str__(self):
   39.47 -        return "unfinished expression in %s at char %d" % (
   39.48 -            repr(self.text), self.pos)
   39.49 -
   39.50 -class TransactionError(Exception): pass
   39.51 -
   39.52 -class UnknownParamstyle(Exception): 
   39.53 -    """
   39.54 -    raised for unsupported db paramstyles
   39.55 -
   39.56 -    (currently supported: qmark, numeric, format, pyformat)
   39.57 -    """
   39.58 -    pass
   39.59 -    
   39.60 -class SQLParam(object):
   39.61 -    """
   39.62 -    Parameter in SQLQuery.
   39.63 -    
   39.64 -        >>> q = SQLQuery(["SELECT * FROM test WHERE name=", SQLParam("joe")])
   39.65 -        >>> q
   39.66 -        <sql: "SELECT * FROM test WHERE name='joe'">
   39.67 -        >>> q.query()
   39.68 -        'SELECT * FROM test WHERE name=%s'
   39.69 -        >>> q.values()
   39.70 -        ['joe']
   39.71 -    """
   39.72 -    __slots__ = ["value"]
   39.73 -
   39.74 -    def __init__(self, value):
   39.75 -        self.value = value
   39.76 -        
   39.77 -    def get_marker(self, paramstyle='pyformat'):
   39.78 -        if paramstyle == 'qmark':
   39.79 -            return '?'
   39.80 -        elif paramstyle == 'numeric':
   39.81 -            return ':1'
   39.82 -        elif paramstyle is None or paramstyle in ['format', 'pyformat']:
   39.83 -            return '%s'
   39.84 -        raise UnknownParamstyle, paramstyle
   39.85 -        
   39.86 -    def sqlquery(self): 
   39.87 -        return SQLQuery([self])
   39.88 -        
   39.89 -    def __add__(self, other):
   39.90 -        return self.sqlquery() + other
   39.91 -        
   39.92 -    def __radd__(self, other):
   39.93 -        return other + self.sqlquery() 
   39.94 -            
   39.95 -    def __str__(self): 
   39.96 -        return str(self.value)
   39.97 -    
   39.98 -    def __repr__(self):
   39.99 -        return '<param: %s>' % repr(self.value)
  39.100 -
  39.101 -sqlparam =  SQLParam
  39.102 -
  39.103 -class SQLQuery(object):
  39.104 -    """
  39.105 -    You can pass this sort of thing as a clause in any db function.
  39.106 -    Otherwise, you can pass a dictionary to the keyword argument `vars`
  39.107 -    and the function will call reparam for you.
  39.108 -
  39.109 -    Internally, consists of `items`, which is a list of strings and
  39.110 -    SQLParams, which get concatenated to produce the actual query.
  39.111 -    """
  39.112 -    __slots__ = ["items"]
  39.113 -
  39.114 -    # tested in sqlquote's docstring
  39.115 -    def __init__(self, items=None):
  39.116 -        r"""Creates a new SQLQuery.
  39.117 -        
  39.118 -            >>> SQLQuery("x")
  39.119 -            <sql: 'x'>
  39.120 -            >>> q = SQLQuery(['SELECT * FROM ', 'test', ' WHERE x=', SQLParam(1)])
  39.121 -            >>> q
  39.122 -            <sql: 'SELECT * FROM test WHERE x=1'>
  39.123 -            >>> q.query(), q.values()
  39.124 -            ('SELECT * FROM test WHERE x=%s', [1])
  39.125 -            >>> SQLQuery(SQLParam(1))
  39.126 -            <sql: '1'>
  39.127 -        """
  39.128 -        if items is None:
  39.129 -            self.items = []
  39.130 -        elif isinstance(items, list):
  39.131 -            self.items = items
  39.132 -        elif isinstance(items, SQLParam):
  39.133 -            self.items = [items]
  39.134 -        elif isinstance(items, SQLQuery):
  39.135 -            self.items = list(items.items)
  39.136 -        else:
  39.137 -            self.items = [items]
  39.138 -            
  39.139 -        # Take care of SQLLiterals
  39.140 -        for i, item in enumerate(self.items):
  39.141 -            if isinstance(item, SQLParam) and isinstance(item.value, SQLLiteral):
  39.142 -                self.items[i] = item.value.v
  39.143 -
  39.144 -    def append(self, value):
  39.145 -        self.items.append(value)
  39.146 -
  39.147 -    def __add__(self, other):
  39.148 -        if isinstance(other, basestring):
  39.149 -            items = [other]
  39.150 -        elif isinstance(other, SQLQuery):
  39.151 -            items = other.items
  39.152 -        else:
  39.153 -            return NotImplemented
  39.154 -        return SQLQuery(self.items + items)
  39.155 -
  39.156 -    def __radd__(self, other):
  39.157 -        if isinstance(other, basestring):
  39.158 -            items = [other]
  39.159 -        else:
  39.160 -            return NotImplemented
  39.161 -            
  39.162 -        return SQLQuery(items + self.items)
  39.163 -
  39.164 -    def __iadd__(self, other):
  39.165 -        if isinstance(other, (basestring, SQLParam)):
  39.166 -            self.items.append(other)
  39.167 -        elif isinstance(other, SQLQuery):
  39.168 -            self.items.extend(other.items)
  39.169 -        else:
  39.170 -            return NotImplemented
  39.171 -        return self
  39.172 -
  39.173 -    def __len__(self):
  39.174 -        return len(self.query())
  39.175 -        
  39.176 -    def query(self, paramstyle=None):
  39.177 -        """
  39.178 -        Returns the query part of the sql query.
  39.179 -            >>> q = SQLQuery(["SELECT * FROM test WHERE name=", SQLParam('joe')])
  39.180 -            >>> q.query()
  39.181 -            'SELECT * FROM test WHERE name=%s'
  39.182 -            >>> q.query(paramstyle='qmark')
  39.183 -            'SELECT * FROM test WHERE name=?'
  39.184 -        """
  39.185 -        s = []
  39.186 -        for x in self.items:
  39.187 -            if isinstance(x, SQLParam):
  39.188 -                x = x.get_marker(paramstyle)
  39.189 -                s.append(safestr(x))
  39.190 -            else:
  39.191 -                x = safestr(x)
  39.192 -                # automatically escape % characters in the query
  39.193 -                # For backward compatability, ignore escaping when the query looks already escaped
  39.194 -                if paramstyle in ['format', 'pyformat']:
  39.195 -                    if '%' in x and '%%' not in x:
  39.196 -                        x = x.replace('%', '%%')
  39.197 -                s.append(x)
  39.198 -        return "".join(s)
  39.199 -    
  39.200 -    def values(self):
  39.201 -        """
  39.202 -        Returns the values of the parameters used in the sql query.
  39.203 -            >>> q = SQLQuery(["SELECT * FROM test WHERE name=", SQLParam('joe')])
  39.204 -            >>> q.values()
  39.205 -            ['joe']
  39.206 -        """
  39.207 -        return [i.value for i in self.items if isinstance(i, SQLParam)]
  39.208 -        
  39.209 -    def join(items, sep=' ', prefix=None, suffix=None, target=None):
  39.210 -        """
  39.211 -        Joins multiple queries.
  39.212 -        
  39.213 -        >>> SQLQuery.join(['a', 'b'], ', ')
  39.214 -        <sql: 'a, b'>
  39.215 -
  39.216 -        Optinally, prefix and suffix arguments can be provided.
  39.217 -
  39.218 -        >>> SQLQuery.join(['a', 'b'], ', ', prefix='(', suffix=')')
  39.219 -        <sql: '(a, b)'>
  39.220 -
  39.221 -        If target argument is provided, the items are appended to target instead of creating a new SQLQuery.
  39.222 -        """
  39.223 -        if target is None:
  39.224 -            target = SQLQuery()
  39.225 -
  39.226 -        target_items = target.items
  39.227 -
  39.228 -        if prefix:
  39.229 -            target_items.append(prefix)
  39.230 -
  39.231 -        for i, item in enumerate(items):
  39.232 -            if i != 0:
  39.233 -                target_items.append(sep)
  39.234 -            if isinstance(item, SQLQuery):
  39.235 -                target_items.extend(item.items)
  39.236 -            else:
  39.237 -                target_items.append(item)
  39.238 -
  39.239 -        if suffix:
  39.240 -            target_items.append(suffix)
  39.241 -        return target
  39.242 -    
  39.243 -    join = staticmethod(join)
  39.244 -    
  39.245 -    def _str(self):
  39.246 -        try:
  39.247 -            return self.query() % tuple([sqlify(x) for x in self.values()])            
  39.248 -        except (ValueError, TypeError):
  39.249 -            return self.query()
  39.250 -        
  39.251 -    def __str__(self):
  39.252 -        return safestr(self._str())
  39.253 -        
  39.254 -    def __unicode__(self):
  39.255 -        return safeunicode(self._str())
  39.256 -
  39.257 -    def __repr__(self):
  39.258 -        return '<sql: %s>' % repr(str(self))
  39.259 -
  39.260 -class SQLLiteral: 
  39.261 -    """
  39.262 -    Protects a string from `sqlquote`.
  39.263 -
  39.264 -        >>> sqlquote('NOW()')
  39.265 -        <sql: "'NOW()'">
  39.266 -        >>> sqlquote(SQLLiteral('NOW()'))
  39.267 -        <sql: 'NOW()'>
  39.268 -    """
  39.269 -    def __init__(self, v): 
  39.270 -        self.v = v
  39.271 -
  39.272 -    def __repr__(self): 
  39.273 -        return self.v
  39.274 -
  39.275 -sqlliteral = SQLLiteral
  39.276 -
  39.277 -def _sqllist(values):
  39.278 -    """
  39.279 -        >>> _sqllist([1, 2, 3])
  39.280 -        <sql: '(1, 2, 3)'>
  39.281 -    """
  39.282 -    items = []
  39.283 -    items.append('(')
  39.284 -    for i, v in enumerate(values):
  39.285 -        if i != 0:
  39.286 -            items.append(', ')
  39.287 -        items.append(sqlparam(v))
  39.288 -    items.append(')')
  39.289 -    return SQLQuery(items)
  39.290 -
  39.291 -def reparam(string_, dictionary): 
  39.292 -    """
  39.293 -    Takes a string and a dictionary and interpolates the string
  39.294 -    using values from the dictionary. Returns an `SQLQuery` for the result.
  39.295 -
  39.296 -        >>> reparam("s = $s", dict(s=True))
  39.297 -        <sql: "s = 't'">
  39.298 -        >>> reparam("s IN $s", dict(s=[1, 2]))
  39.299 -        <sql: 's IN (1, 2)'>
  39.300 -    """
  39.301 -    dictionary = dictionary.copy() # eval mucks with it
  39.302 -    vals = []
  39.303 -    result = []
  39.304 -    for live, chunk in _interpolate(string_):
  39.305 -        if live:
  39.306 -            v = eval(chunk, dictionary)
  39.307 -            result.append(sqlquote(v))
  39.308 -        else: 
  39.309 -            result.append(chunk)
  39.310 -    return SQLQuery.join(result, '')
  39.311 -
  39.312 -def sqlify(obj): 
  39.313 -    """
  39.314 -    converts `obj` to its proper SQL version
  39.315 -
  39.316 -        >>> sqlify(None)
  39.317 -        'NULL'
  39.318 -        >>> sqlify(True)
  39.319 -        "'t'"
  39.320 -        >>> sqlify(3)
  39.321 -        '3'
  39.322 -    """
  39.323 -    # because `1 == True and hash(1) == hash(True)`
  39.324 -    # we have to do this the hard way...
  39.325 -
  39.326 -    if obj is None:
  39.327 -        return 'NULL'
  39.328 -    elif obj is True:
  39.329 -        return "'t'"
  39.330 -    elif obj is False:
  39.331 -        return "'f'"
  39.332 -    elif datetime and isinstance(obj, datetime.datetime):
  39.333 -        return repr(obj.isoformat())
  39.334 -    else:
  39.335 -        if isinstance(obj, unicode): obj = obj.encode('utf8')
  39.336 -        return repr(obj)
  39.337 -
  39.338 -def sqllist(lst): 
  39.339 -    """
  39.340 -    Converts the arguments for use in something like a WHERE clause.
  39.341 -    
  39.342 -        >>> sqllist(['a', 'b'])
  39.343 -        'a, b'
  39.344 -        >>> sqllist('a')
  39.345 -        'a'
  39.346 -        >>> sqllist(u'abc')
  39.347 -        u'abc'
  39.348 -    """
  39.349 -    if isinstance(lst, basestring): 
  39.350 -        return lst
  39.351 -    else:
  39.352 -        return ', '.join(lst)
  39.353 -
  39.354 -def sqlors(left, lst):
  39.355 -    """
  39.356 -    `left is a SQL clause like `tablename.arg = ` 
  39.357 -    and `lst` is a list of values. Returns a reparam-style
  39.358 -    pair featuring the SQL that ORs together the clause
  39.359 -    for each item in the lst.
  39.360 -
  39.361 -        >>> sqlors('foo = ', [])
  39.362 -        <sql: '1=2'>
  39.363 -        >>> sqlors('foo = ', [1])
  39.364 -        <sql: 'foo = 1'>
  39.365 -        >>> sqlors('foo = ', 1)
  39.366 -        <sql: 'foo = 1'>
  39.367 -        >>> sqlors('foo = ', [1,2,3])
  39.368 -        <sql: '(foo = 1 OR foo = 2 OR foo = 3 OR 1=2)'>
  39.369 -    """
  39.370 -    if isinstance(lst, iters):
  39.371 -        lst = list(lst)
  39.372 -        ln = len(lst)
  39.373 -        if ln == 0:
  39.374 -            return SQLQuery("1=2")
  39.375 -        if ln == 1:
  39.376 -            lst = lst[0]
  39.377 -
  39.378 -    if isinstance(lst, iters):
  39.379 -        return SQLQuery(['('] + 
  39.380 -          sum([[left, sqlparam(x), ' OR '] for x in lst], []) +
  39.381 -          ['1=2)']
  39.382 -        )
  39.383 -    else:
  39.384 -        return left + sqlparam(lst)
  39.385 -        
  39.386 -def sqlwhere(dictionary, grouping=' AND '): 
  39.387 -    """
  39.388 -    Converts a `dictionary` to an SQL WHERE clause `SQLQuery`.
  39.389 -    
  39.390 -        >>> sqlwhere({'cust_id': 2, 'order_id':3})
  39.391 -        <sql: 'order_id = 3 AND cust_id = 2'>
  39.392 -        >>> sqlwhere({'cust_id': 2, 'order_id':3}, grouping=', ')
  39.393 -        <sql: 'order_id = 3, cust_id = 2'>
  39.394 -        >>> sqlwhere({'a': 'a', 'b': 'b'}).query()
  39.395 -        'a = %s AND b = %s'
  39.396 -    """
  39.397 -    return SQLQuery.join([k + ' = ' + sqlparam(v) for k, v in dictionary.items()], grouping)
  39.398 -
  39.399 -def sqlquote(a): 
  39.400 -    """
  39.401 -    Ensures `a` is quoted properly for use in a SQL query.
  39.402 -
  39.403 -        >>> 'WHERE x = ' + sqlquote(True) + ' AND y = ' + sqlquote(3)
  39.404 -        <sql: "WHERE x = 't' AND y = 3">
  39.405 -        >>> 'WHERE x = ' + sqlquote(True) + ' AND y IN ' + sqlquote([2, 3])
  39.406 -        <sql: "WHERE x = 't' AND y IN (2, 3)">
  39.407 -    """
  39.408 -    if isinstance(a, list):
  39.409 -        return _sqllist(a)
  39.410 -    else:
  39.411 -        return sqlparam(a).sqlquery()
  39.412 -
  39.413 -class Transaction:
  39.414 -    """Database transaction."""
  39.415 -    def __init__(self, ctx):
  39.416 -        self.ctx = ctx
  39.417 -        self.transaction_count = transaction_count = len(ctx.transactions)
  39.418 -
  39.419 -        class transaction_engine:
  39.420 -            """Transaction Engine used in top level transactions."""
  39.421 -            def do_transact(self):
  39.422 -                ctx.commit(unload=False)
  39.423 -
  39.424 -            def do_commit(self):
  39.425 -                ctx.commit()
  39.426 -
  39.427 -            def do_rollback(self):
  39.428 -                ctx.rollback()
  39.429 -
  39.430 -        class subtransaction_engine:
  39.431 -            """Transaction Engine used in sub transactions."""
  39.432 -            def query(self, q):
  39.433 -                db_cursor = ctx.db.cursor()
  39.434 -                ctx.db_execute(db_cursor, SQLQuery(q % transaction_count))
  39.435 -
  39.436 -            def do_transact(self):
  39.437 -                self.query('SAVEPOINT webpy_sp_%s')
  39.438 -
  39.439 -            def do_commit(self):
  39.440 -                self.query('RELEASE SAVEPOINT webpy_sp_%s')
  39.441 -
  39.442 -            def do_rollback(self):
  39.443 -                self.query('ROLLBACK TO SAVEPOINT webpy_sp_%s')
  39.444 -
  39.445 -        class dummy_engine:
  39.446 -            """Transaction Engine used instead of subtransaction_engine 
  39.447 -            when sub transactions are not supported."""
  39.448 -            do_transact = do_commit = do_rollback = lambda self: None
  39.449 -
  39.450 -        if self.transaction_count:
  39.451 -            # nested transactions are not supported in some databases
  39.452 -            if self.ctx.get('ignore_nested_transactions'):
  39.453 -                self.engine = dummy_engine()
  39.454 -            else:
  39.455 -                self.engine = subtransaction_engine()
  39.456 -        else:
  39.457 -            self.engine = transaction_engine()
  39.458 -
  39.459 -        self.engine.do_transact()
  39.460 -        self.ctx.transactions.append(self)
  39.461 -
  39.462 -    def __enter__(self):
  39.463 -        return self
  39.464 -
  39.465 -    def __exit__(self, exctype, excvalue, traceback):
  39.466 -        if exctype is not None:
  39.467 -            self.rollback()
  39.468 -        else:
  39.469 -            self.commit()
  39.470 -
  39.471 -    def commit(self):
  39.472 -        if len(self.ctx.transactions) > self.transaction_count:
  39.473 -            self.engine.do_commit()
  39.474 -            self.ctx.transactions = self.ctx.transactions[:self.transaction_count]
  39.475 -
  39.476 -    def rollback(self):
  39.477 -        if len(self.ctx.transactions) > self.transaction_count:
  39.478 -            self.engine.do_rollback()
  39.479 -            self.ctx.transactions = self.ctx.transactions[:self.transaction_count]
  39.480 -
  39.481 -class DB: 
  39.482 -    """Database"""
  39.483 -    def __init__(self, db_module, keywords):
  39.484 -        """Creates a database.
  39.485 -        """
  39.486 -        # some DB implementaions take optional paramater `driver` to use a specific driver modue
  39.487 -        # but it should not be passed to connect
  39.488 -        keywords.pop('driver', None)
  39.489 -
  39.490 -        self.db_module = db_module
  39.491 -        self.keywords = keywords
  39.492 -
  39.493 -        self._ctx = threadeddict()
  39.494 -        # flag to enable/disable printing queries
  39.495 -        self.printing = config.get('debug_sql', config.get('debug', False))
  39.496 -        self.supports_multiple_insert = False
  39.497 -        
  39.498 -        try:
  39.499 -            import DBUtils
  39.500 -            # enable pooling if DBUtils module is available.
  39.501 -            self.has_pooling = True
  39.502 -        except ImportError:
  39.503 -            self.has_pooling = False
  39.504 -            
  39.505 -        # Pooling can be disabled by passing pooling=False in the keywords.
  39.506 -        self.has_pooling = self.keywords.pop('pooling', True) and self.has_pooling
  39.507 -            
  39.508 -    def _getctx(self): 
  39.509 -        if not self._ctx.get('db'):
  39.510 -            self._load_context(self._ctx)
  39.511 -        return self._ctx
  39.512 -    ctx = property(_getctx)
  39.513 -    
  39.514 -    def _load_context(self, ctx):
  39.515 -        ctx.dbq_count = 0
  39.516 -        ctx.transactions = [] # stack of transactions
  39.517 -        
  39.518 -        if self.has_pooling:
  39.519 -            ctx.db = self._connect_with_pooling(self.keywords)
  39.520 -        else:
  39.521 -            ctx.db = self._connect(self.keywords)
  39.522 -        ctx.db_execute = self._db_execute
  39.523 -        
  39.524 -        if not hasattr(ctx.db, 'commit'):
  39.525 -            ctx.db.commit = lambda: None
  39.526 -
  39.527 -        if not hasattr(ctx.db, 'rollback'):
  39.528 -            ctx.db.rollback = lambda: None
  39.529 -            
  39.530 -        def commit(unload=True):
  39.531 -            # do db commit and release the connection if pooling is enabled.            
  39.532 -            ctx.db.commit()
  39.533 -            if unload and self.has_pooling:
  39.534 -                self._unload_context(self._ctx)
  39.535 -                
  39.536 -        def rollback():
  39.537 -            # do db rollback and release the connection if pooling is enabled.
  39.538 -            ctx.db.rollback()
  39.539 -            if self.has_pooling:
  39.540 -                self._unload_context(self._ctx)
  39.541 -                
  39.542 -        ctx.commit = commit
  39.543 -        ctx.rollback = rollback
  39.544 -            
  39.545 -    def _unload_context(self, ctx):
  39.546 -        del ctx.db
  39.547 -            
  39.548 -    def _connect(self, keywords):
  39.549 -        return self.db_module.connect(**keywords)
  39.550 -        
  39.551 -    def _connect_with_pooling(self, keywords):
  39.552 -        def get_pooled_db():
  39.553 -            from DBUtils import PooledDB
  39.554 -
  39.555 -            # In DBUtils 0.9.3, `dbapi` argument is renamed as `creator`
  39.556 -            # see Bug#122112
  39.557 -            
  39.558 -            if PooledDB.__version__.split('.') < '0.9.3'.split('.'):
  39.559 -                return PooledDB.PooledDB(dbapi=self.db_module, **keywords)
  39.560 -            else:
  39.561 -                return PooledDB.PooledDB(creator=self.db_module, **keywords)
  39.562 -        
  39.563 -        if getattr(self, '_pooleddb', None) is None:
  39.564 -            self._pooleddb = get_pooled_db()
  39.565 -        
  39.566 -        return self._pooleddb.connection()
  39.567 -        
  39.568 -    def _db_cursor(self):
  39.569 -        return self.ctx.db.cursor()
  39.570 -
  39.571 -    def _param_marker(self):
  39.572 -        """Returns parameter marker based on paramstyle attribute if this database."""
  39.573 -        style = getattr(self, 'paramstyle', 'pyformat')
  39.574 -
  39.575 -        if style == 'qmark':
  39.576 -            return '?'
  39.577 -        elif style == 'numeric':
  39.578 -            return ':1'
  39.579 -        elif style in ['format', 'pyformat']:
  39.580 -            return '%s'
  39.581 -        raise UnknownParamstyle, style
  39.582 -
  39.583 -    def _db_execute(self, cur, sql_query): 
  39.584 -        """executes an sql query"""
  39.585 -        self.ctx.dbq_count += 1
  39.586 -        
  39.587 -        try:
  39.588 -            a = time.time()
  39.589 -            query, params = self._process_query(sql_query)
  39.590 -            out = cur.execute(query, params)
  39.591 -            b = time.time()
  39.592 -        except:
  39.593 -            if self.printing:
  39.594 -                print >> debug, 'ERR:', str(sql_query)
  39.595 -            if self.ctx.transactions:
  39.596 -                self.ctx.transactions[-1].rollback()
  39.597 -            else:
  39.598 -                self.ctx.rollback()
  39.599 -            raise
  39.600 -
  39.601 -        if self.printing:
  39.602 -            print >> debug, '%s (%s): %s' % (round(b-a, 2), self.ctx.dbq_count, str(sql_query))
  39.603 -        return out
  39.604 -
  39.605 -    def _process_query(self, sql_query):
  39.606 -        """Takes the SQLQuery object and returns query string and parameters.
  39.607 -        """
  39.608 -        paramstyle = getattr(self, 'paramstyle', 'pyformat')
  39.609 -        query = sql_query.query(paramstyle)
  39.610 -        params = sql_query.values()
  39.611 -        return query, params
  39.612 -    
  39.613 -    def _where(self, where, vars): 
  39.614 -        if isinstance(where, (int, long)):
  39.615 -            where = "id = " + sqlparam(where)
  39.616 -        #@@@ for backward-compatibility
  39.617 -        elif isinstance(where, (list, tuple)) and len(where) == 2:
  39.618 -            where = SQLQuery(where[0], where[1])
  39.619 -        elif isinstance(where, SQLQuery):
  39.620 -            pass
  39.621 -        else:
  39.622 -            where = reparam(where, vars)        
  39.623 -        return where
  39.624 -    
  39.625 -    def query(self, sql_query, vars=None, processed=False, _test=False): 
  39.626 -        """
  39.627 -        Execute SQL query `sql_query` using dictionary `vars` to interpolate it.
  39.628 -        If `processed=True`, `vars` is a `reparam`-style list to use 
  39.629 -        instead of interpolating.
  39.630 -        
  39.631 -            >>> db = DB(None, {})
  39.632 -            >>> db.query("SELECT * FROM foo", _test=True)
  39.633 -            <sql: 'SELECT * FROM foo'>
  39.634 -            >>> db.query("SELECT * FROM foo WHERE x = $x", vars=dict(x='f'), _test=True)
  39.635 -            <sql: "SELECT * FROM foo WHERE x = 'f'">
  39.636 -            >>> db.query("SELECT * FROM foo WHERE x = " + sqlquote('f'), _test=True)
  39.637 -            <sql: "SELECT * FROM foo WHERE x = 'f'">
  39.638 -        """
  39.639 -        if vars is None: vars = {}
  39.640 -        
  39.641 -        if not processed and not isinstance(sql_query, SQLQuery):
  39.642 -            sql_query = reparam(sql_query, vars)
  39.643 -        
  39.644 -        if _test: return sql_query
  39.645 -        
  39.646 -        db_cursor = self._db_cursor()
  39.647 -        self._db_execute(db_cursor, sql_query)
  39.648 -        
  39.649 -        if db_cursor.description:
  39.650 -            names = [x[0] for x in db_cursor.description]
  39.651 -            def iterwrapper():
  39.652 -                row = db_cursor.fetchone()
  39.653 -                while row:
  39.654 -                    yield storage(dict(zip(names, row)))
  39.655 -                    row = db_cursor.fetchone()
  39.656 -            out = iterbetter(iterwrapper())
  39.657 -            out.__len__ = lambda: int(db_cursor.rowcount)
  39.658 -            out.list = lambda: [storage(dict(zip(names, x))) \
  39.659 -                               for x in db_cursor.fetchall()]
  39.660 -        else:
  39.661 -            out = db_cursor.rowcount
  39.662 -        
  39.663 -        if not self.ctx.transactions: 
  39.664 -            self.ctx.commit()
  39.665 -        return out
  39.666 -    
  39.667 -    def select(self, tables, vars=None, what='*', where=None, order=None, group=None, 
  39.668 -               limit=None, offset=None, _test=False): 
  39.669 -        """
  39.670 -        Selects `what` from `tables` with clauses `where`, `order`, 
  39.671 -        `group`, `limit`, and `offset`. Uses vars to interpolate. 
  39.672 -        Otherwise, each clause can be a SQLQuery.
  39.673 -        
  39.674 -            >>> db = DB(None, {})
  39.675 -            >>> db.select('foo', _test=True)
  39.676 -            <sql: 'SELECT * FROM foo'>
  39.677 -            >>> db.select(['foo', 'bar'], where="foo.bar_id = bar.id", limit=5, _test=True)
  39.678 -            <sql: 'SELECT * FROM foo, bar WHERE foo.bar_id = bar.id LIMIT 5'>
  39.679 -        """
  39.680 -        if vars is None: vars = {}
  39.681 -        sql_clauses = self.sql_clauses(what, tables, where, group, order, limit, offset)
  39.682 -        clauses = [self.gen_clause(sql, val, vars) for sql, val in sql_clauses if val is not None]
  39.683 -        qout = SQLQuery.join(clauses)
  39.684 -        if _test: return qout
  39.685 -        return self.query(qout, processed=True)
  39.686 -    
  39.687 -    def where(self, table, what='*', order=None, group=None, limit=None, 
  39.688 -              offset=None, _test=False, **kwargs):
  39.689 -        """
  39.690 -        Selects from `table` where keys are equal to values in `kwargs`.
  39.691 -        
  39.692 -            >>> db = DB(None, {})
  39.693 -            >>> db.where('foo', bar_id=3, _test=True)
  39.694 -            <sql: 'SELECT * FROM foo WHERE bar_id = 3'>
  39.695 -            >>> db.where('foo', source=2, crust='dewey', _test=True)
  39.696 -            <sql: "SELECT * FROM foo WHERE source = 2 AND crust = 'dewey'">
  39.697 -            >>> db.where('foo', _test=True)
  39.698 -            <sql: 'SELECT * FROM foo'>
  39.699 -        """
  39.700 -        where_clauses = []
  39.701 -        for k, v in kwargs.iteritems():
  39.702 -            where_clauses.append(k + ' = ' + sqlquote(v))
  39.703 -            
  39.704 -        if where_clauses:
  39.705 -            where = SQLQuery.join(where_clauses, " AND ")
  39.706 -        else:
  39.707 -            where = None
  39.708 -            
  39.709 -        return self.select(table, what=what, order=order, 
  39.710 -               group=group, limit=limit, offset=offset, _test=_test, 
  39.711 -               where=where)
  39.712 -    
  39.713 -    def sql_clauses(self, what, tables, where, group, order, limit, offset): 
  39.714 -        return (
  39.715 -            ('SELECT', what),
  39.716 -            ('FROM', sqllist(tables)),
  39.717 -            ('WHERE', where),
  39.718 -            ('GROUP BY', group),
  39.719 -            ('ORDER BY', order),
  39.720 -            ('LIMIT', limit),
  39.721 -            ('OFFSET', offset))
  39.722 -    
  39.723 -    def gen_clause(self, sql, val, vars): 
  39.724 -        if isinstance(val, (int, long)):
  39.725 -            if sql == 'WHERE':
  39.726 -                nout = 'id = ' + sqlquote(val)
  39.727 -            else:
  39.728 -                nout = SQLQuery(val)
  39.729 -        #@@@
  39.730 -        elif isinstance(val, (list, tuple)) and len(val) == 2:
  39.731 -            nout = SQLQuery(val[0], val[1]) # backwards-compatibility
  39.732 -        elif isinstance(val, SQLQuery):
  39.733 -            nout = val
  39.734 -        else:
  39.735 -            nout = reparam(val, vars)
  39.736 -
  39.737 -        def xjoin(a, b):
  39.738 -            if a and b: return a + ' ' + b
  39.739 -            else: return a or b
  39.740 -
  39.741 -        return xjoin(sql, nout)
  39.742 -
  39.743 -    def insert(self, tablename, seqname=None, _test=False, **values): 
  39.744 -        """
  39.745 -        Inserts `values` into `tablename`. Returns current sequence ID.
  39.746 -        Set `seqname` to the ID if it's not the default, or to `False`
  39.747 -        if there isn't one.
  39.748 -        
  39.749 -            >>> db = DB(None, {})
  39.750 -            >>> q = db.insert('foo', name='bob', age=2, created=SQLLiteral('NOW()'), _test=True)
  39.751 -            >>> q
  39.752 -            <sql: "INSERT INTO foo (age, name, created) VALUES (2, 'bob', NOW())">
  39.753 -            >>> q.query()
  39.754 -            'INSERT INTO foo (age, name, created) VALUES (%s, %s, NOW())'
  39.755 -            >>> q.values()
  39.756 -            [2, 'bob']
  39.757 -        """
  39.758 -        def q(x): return "(" + x + ")"
  39.759 -        
  39.760 -        if values:
  39.761 -            _keys = SQLQuery.join(values.keys(), ', ')
  39.762 -            _values = SQLQuery.join([sqlparam(v) for v in values.values()], ', ')
  39.763 -            sql_query = "INSERT INTO %s " % tablename + q(_keys) + ' VALUES ' + q(_values)
  39.764 -        else:
  39.765 -            sql_query = SQLQuery(self._get_insert_default_values_query(tablename))
  39.766 -
  39.767 -        if _test: return sql_query
  39.768 -        
  39.769 -        db_cursor = self._db_cursor()
  39.770 -        if seqname is not False: 
  39.771 -            sql_query = self._process_insert_query(sql_query, tablename, seqname)
  39.772 -
  39.773 -        if isinstance(sql_query, tuple):
  39.774 -            # for some databases, a separate query has to be made to find 
  39.775 -            # the id of the inserted row.
  39.776 -            q1, q2 = sql_query
  39.777 -            self._db_execute(db_cursor, q1)
  39.778 -            self._db_execute(db_cursor, q2)
  39.779 -        else:
  39.780 -            self._db_execute(db_cursor, sql_query)
  39.781 -
  39.782 -        try: 
  39.783 -            out = db_cursor.fetchone()[0]
  39.784 -        except Exception: 
  39.785 -            out = None
  39.786 -        
  39.787 -        if not self.ctx.transactions: 
  39.788 -            self.ctx.commit()
  39.789 -        return out
  39.790 -        
  39.791 -    def _get_insert_default_values_query(self, table):
  39.792 -        return "INSERT INTO %s DEFAULT VALUES" % table
  39.793 -
  39.794 -    def multiple_insert(self, tablename, values, seqname=None, _test=False):
  39.795 -        """
  39.796 -        Inserts multiple rows into `tablename`. The `values` must be a list of dictioanries, 
  39.797 -        one for each row to be inserted, each with the same set of keys.
  39.798 -        Returns the list of ids of the inserted rows.        
  39.799 -        Set `seqname` to the ID if it's not the default, or to `False`
  39.800 -        if there isn't one.
  39.801 -        
  39.802 -            >>> db = DB(None, {})
  39.803 -            >>> db.supports_multiple_insert = True
  39.804 -            >>> values = [{"name": "foo", "email": "foo@example.com"}, {"name": "bar", "email": "bar@example.com"}]
  39.805 -            >>> db.multiple_insert('person', values=values, _test=True)
  39.806 -            <sql: "INSERT INTO person (name, email) VALUES ('foo', 'foo@example.com'), ('bar', 'bar@example.com')">
  39.807 -        """        
  39.808 -        if not values:
  39.809 -            return []
  39.810 -            
  39.811 -        if not self.supports_multiple_insert:
  39.812 -            out = [self.insert(tablename, seqname=seqname, _test=_test, **v) for v in values]
  39.813 -            if seqname is False:
  39.814 -                return None
  39.815 -            else:
  39.816 -                return out
  39.817 -                
  39.818 -        keys = values[0].keys()
  39.819 -        #@@ make sure all keys are valid
  39.820 -
  39.821 -        # make sure all rows have same keys.
  39.822 -        for v in values:
  39.823 -            if v.keys() != keys:
  39.824 -                raise ValueError, 'Bad data'
  39.825 -
  39.826 -        sql_query = SQLQuery('INSERT INTO %s (%s) VALUES ' % (tablename, ', '.join(keys)))
  39.827 -
  39.828 -        for i, row in enumerate(values):
  39.829 -            if i != 0:
  39.830 -                sql_query.append(", ")
  39.831 -            SQLQuery.join([SQLParam(row[k]) for k in keys], sep=", ", target=sql_query, prefix="(", suffix=")")
  39.832 -        
  39.833 -        if _test: return sql_query
  39.834 -
  39.835 -        db_cursor = self._db_cursor()
  39.836 -        if seqname is not False: 
  39.837 -            sql_query = self._process_insert_query(sql_query, tablename, seqname)
  39.838 -
  39.839 -        if isinstance(sql_query, tuple):
  39.840 -            # for some databases, a separate query has to be made to find 
  39.841 -            # the id of the inserted row.
  39.842 -            q1, q2 = sql_query
  39.843 -            self._db_execute(db_cursor, q1)
  39.844 -            self._db_execute(db_cursor, q2)
  39.845 -        else:
  39.846 -            self._db_execute(db_cursor, sql_query)
  39.847 -
  39.848 -        try: 
  39.849 -            out = db_cursor.fetchone()[0]
  39.850 -            out = range(out-len(values)+1, out+1)        
  39.851 -        except Exception: 
  39.852 -            out = None
  39.853 -
  39.854 -        if not self.ctx.transactions: 
  39.855 -            self.ctx.commit()
  39.856 -        return out
  39.857 -
  39.858 -    
  39.859 -    def update(self, tables, where, vars=None, _test=False, **values): 
  39.860 -        """
  39.861 -        Update `tables` with clause `where` (interpolated using `vars`)
  39.862 -        and setting `values`.
  39.863 -
  39.864 -            >>> db = DB(None, {})
  39.865 -            >>> name = 'Joseph'
  39.866 -            >>> q = db.update('foo', where='name = $name', name='bob', age=2,
  39.867 -            ...     created=SQLLiteral('NOW()'), vars=locals(), _test=True)
  39.868 -            >>> q
  39.869 -            <sql: "UPDATE foo SET age = 2, name = 'bob', created = NOW() WHERE name = 'Joseph'">
  39.870 -            >>> q.query()
  39.871 -            'UPDATE foo SET age = %s, name = %s, created = NOW() WHERE name = %s'
  39.872 -            >>> q.values()
  39.873 -            [2, 'bob', 'Joseph']
  39.874 -        """
  39.875 -        if vars is None: vars = {}
  39.876 -        where = self._where(where, vars)
  39.877 -
  39.878 -        query = (
  39.879 -          "UPDATE " + sqllist(tables) + 
  39.880 -          " SET " + sqlwhere(values, ', ') + 
  39.881 -          " WHERE " + where)
  39.882 -
  39.883 -        if _test: return query
  39.884 -        
  39.885 -        db_cursor = self._db_cursor()
  39.886 -        self._db_execute(db_cursor, query)
  39.887 -        if not self.ctx.transactions: 
  39.888 -            self.ctx.commit()
  39.889 -        return db_cursor.rowcount
  39.890 -    
  39.891 -    def delete(self, table, where, using=None, vars=None, _test=False): 
  39.892 -        """
  39.893 -        Deletes from `table` with clauses `where` and `using`.
  39.894 -
  39.895 -            >>> db = DB(None, {})
  39.896 -            >>> name = 'Joe'
  39.897 -            >>> db.delete('foo', where='name = $name', vars=locals(), _test=True)
  39.898 -            <sql: "DELETE FROM foo WHERE name = 'Joe'">
  39.899 -        """
  39.900 -        if vars is None: vars = {}
  39.901 -        where = self._where(where, vars)
  39.902 -
  39.903 -        q = 'DELETE FROM ' + table
  39.904 -        if using: q += ' USING ' + sqllist(using)
  39.905 -        if where: q += ' WHERE ' + where
  39.906 -
  39.907 -        if _test: return q
  39.908 -
  39.909 -        db_cursor = self._db_cursor()
  39.910 -        self._db_execute(db_cursor, q)
  39.911 -        if not self.ctx.transactions: 
  39.912 -            self.ctx.commit()
  39.913 -        return db_cursor.rowcount
  39.914 -
  39.915 -    def _process_insert_query(self, query, tablename, seqname):
  39.916 -        return query
  39.917 -
  39.918 -    def transaction(self): 
  39.919 -        """Start a transaction."""
  39.920 -        return Transaction(self.ctx)
  39.921 -    
  39.922 -class PostgresDB(DB): 
  39.923 -    """Postgres driver."""
  39.924 -    def __init__(self, **keywords):
  39.925 -        if 'pw' in keywords:
  39.926 -            keywords['password'] = keywords.pop('pw')
  39.927 -            
  39.928 -        db_module = import_driver(["psycopg2", "psycopg", "pgdb"], preferred=keywords.pop('driver', None))
  39.929 -        if db_module.__name__ == "psycopg2":
  39.930 -            import psycopg2.extensions
  39.931 -            psycopg2.extensions.register_type(psycopg2.extensions.UNICODE)
  39.932 -
  39.933 -        # if db is not provided postgres driver will take it from PGDATABASE environment variable
  39.934 -        if 'db' in keywords:
  39.935 -            keywords['database'] = keywords.pop('db')
  39.936 -        
  39.937 -        self.dbname = "postgres"
  39.938 -        self.paramstyle = db_module.paramstyle
  39.939 -        DB.__init__(self, db_module, keywords)
  39.940 -        self.supports_multiple_insert = True
  39.941 -        self._sequences = None
  39.942 -        
  39.943 -    def _process_insert_query(self, query, tablename, seqname):
  39.944 -        if seqname is None:
  39.945 -            # when seqname is not provided guess the seqname and make sure it exists
  39.946 -            seqname = tablename + "_id_seq"
  39.947 -            if seqname not in self._get_all_sequences():
  39.948 -                seqname = None
  39.949 -        
  39.950 -        if seqname:
  39.951 -            query += "; SELECT currval('%s')" % seqname
  39.952 -            
  39.953 -        return query
  39.954 -    
  39.955 -    def _get_all_sequences(self):
  39.956 -        """Query postgres to find names of all sequences used in this database."""
  39.957 -        if self._sequences is None:
  39.958 -            q = "SELECT c.relname FROM pg_class c WHERE c.relkind = 'S'"
  39.959 -            self._sequences = set([c.relname for c in self.query(q)])
  39.960 -        return self._sequences
  39.961 -
  39.962 -    def _connect(self, keywords):
  39.963 -        conn = DB._connect(self, keywords)
  39.964 -        try:
  39.965 -            conn.set_client_encoding('UTF8')
  39.966 -        except AttributeError:
  39.967 -            # fallback for pgdb driver
  39.968 -            conn.cursor().execute("set client_encoding to 'UTF-8'")
  39.969 -        return conn
  39.970 -        
  39.971 -    def _connect_with_pooling(self, keywords):
  39.972 -        conn = DB._connect_with_pooling(self, keywords)
  39.973 -        conn._con._con.set_client_encoding('UTF8')
  39.974 -        return conn
  39.975 -
  39.976 -class MySQLDB(DB): 
  39.977 -    def __init__(self, **keywords):
  39.978 -        import MySQLdb as db
  39.979 -        if 'pw' in keywords:
  39.980 -            keywords['passwd'] = keywords['pw']
  39.981 -            del keywords['pw']
  39.982 -
  39.983 -        if 'charset' not in keywords:
  39.984 -            keywords['charset'] = 'utf8'
  39.985 -        elif keywords['charset'] is None:
  39.986 -            del keywords['charset']
  39.987 -
  39.988 -        self.paramstyle = db.paramstyle = 'pyformat' # it's both, like psycopg
  39.989 -        self.dbname = "mysql"
  39.990 -        DB.__init__(self, db, keywords)
  39.991 -        self.supports_multiple_insert = True
  39.992 -        
  39.993 -    def _process_insert_query(self, query, tablename, seqname):
  39.994 -        return query, SQLQuery('SELECT last_insert_id();')
  39.995 -        
  39.996 -    def _get_insert_default_values_query(self, table):
  39.997 -        return "INSERT INTO %s () VALUES()" % table
  39.998 -
  39.999 -def import_driver(drivers, preferred=None):
 39.1000 -    """Import the first available driver or preferred driver.
 39.1001 -    """
 39.1002 -    if preferred:
 39.1003 -        drivers = [preferred]
 39.1004 -
 39.1005 -    for d in drivers:
 39.1006 -        try:
 39.1007 -            return __import__(d, None, None, ['x'])
 39.1008 -        except ImportError:
 39.1009 -            pass
 39.1010 -    raise ImportError("Unable to import " + " or ".join(drivers))
 39.1011 -
 39.1012 -class SqliteDB(DB): 
 39.1013 -    def __init__(self, **keywords):
 39.1014 -        db = import_driver(["sqlite3", "pysqlite2.dbapi2", "sqlite"], preferred=keywords.pop('driver', None))
 39.1015 -
 39.1016 -        if db.__name__ in ["sqlite3", "pysqlite2.dbapi2"]:
 39.1017 -            db.paramstyle = 'qmark'
 39.1018 -            
 39.1019 -        # sqlite driver doesn't create datatime objects for timestamp columns unless `detect_types` option is passed.
 39.1020 -        # It seems to be supported in sqlite3 and pysqlite2 drivers, not surte about sqlite.
 39.1021 -        keywords.setdefault('detect_types', db.PARSE_DECLTYPES)
 39.1022 -
 39.1023 -        self.paramstyle = db.paramstyle
 39.1024 -        keywords['database'] = keywords.pop('db')
 39.1025 -        keywords['pooling'] = False # sqlite don't allows connections to be shared by threads
 39.1026 -        self.dbname = "sqlite"        
 39.1027 -        DB.__init__(self, db, keywords)
 39.1028 -
 39.1029 -    def _process_insert_query(self, query, tablename, seqname):
 39.1030 -        return query, SQLQuery('SELECT last_insert_rowid();')
 39.1031 -    
 39.1032 -    def query(self, *a, **kw):
 39.1033 -        out = DB.query(self, *a, **kw)
 39.1034 -        if isinstance(out, iterbetter):
 39.1035 -            del out.__len__
 39.1036 -        return out
 39.1037 -
 39.1038 -class FirebirdDB(DB):
 39.1039 -    """Firebird Database.
 39.1040 -    """
 39.1041 -    def __init__(self, **keywords):
 39.1042 -        try:
 39.1043 -            import kinterbasdb as db
 39.1044 -        except Exception:
 39.1045 -            db = None
 39.1046 -            pass
 39.1047 -        if 'pw' in keywords:
 39.1048 -            keywords['passwd'] = keywords['pw']
 39.1049 -            del keywords['pw']
 39.1050 -        keywords['database'] = keywords['db']
 39.1051 -        del keywords['db']
 39.1052 -        DB.__init__(self, db, keywords)
 39.1053 -        
 39.1054 -    def delete(self, table, where=None, using=None, vars=None, _test=False):
 39.1055 -        # firebird doesn't support using clause
 39.1056 -        using=None
 39.1057 -        return DB.delete(self, table, where, using, vars, _test)
 39.1058 -
 39.1059 -    def sql_clauses(self, what, tables, where, group, order, limit, offset):
 39.1060 -        return (
 39.1061 -            ('SELECT', ''),
 39.1062 -            ('FIRST', limit),
 39.1063 -            ('SKIP', offset),
 39.1064 -            ('', what),
 39.1065 -            ('FROM', sqllist(tables)),
 39.1066 -            ('WHERE', where),
 39.1067 -            ('GROUP BY', group),
 39.1068 -            ('ORDER BY', order)
 39.1069 -        )
 39.1070 -
 39.1071 -class MSSQLDB(DB):
 39.1072 -    def __init__(self, **keywords):
 39.1073 -        import pymssql as db    
 39.1074 -        if 'pw' in keywords:
 39.1075 -            keywords['password'] = keywords.pop('pw')
 39.1076 -        keywords['database'] = keywords.pop('db')
 39.1077 -        self.dbname = "mssql"
 39.1078 -        DB.__init__(self, db, keywords)
 39.1079 -
 39.1080 -    def _process_query(self, sql_query):
 39.1081 -        """Takes the SQLQuery object and returns query string and parameters.
 39.1082 -        """
 39.1083 -        # MSSQLDB expects params to be a tuple. 
 39.1084 -        # Overwriting the default implementation to convert params to tuple.
 39.1085 -        paramstyle = getattr(self, 'paramstyle', 'pyformat')
 39.1086 -        query = sql_query.query(paramstyle)
 39.1087 -        params = sql_query.values()
 39.1088 -        return query, tuple(params)
 39.1089 -
 39.1090 -    def sql_clauses(self, what, tables, where, group, order, limit, offset): 
 39.1091 -        return (
 39.1092 -            ('SELECT', what),
 39.1093 -            ('TOP', limit),
 39.1094 -            ('FROM', sqllist(tables)),
 39.1095 -            ('WHERE', where),
 39.1096 -            ('GROUP BY', group),
 39.1097 -            ('ORDER BY', order),
 39.1098 -            ('OFFSET', offset))
 39.1099 -            
 39.1100 -    def _test(self):
 39.1101 -        """Test LIMIT.
 39.1102 -
 39.1103 -            Fake presence of pymssql module for running tests.
 39.1104 -            >>> import sys
 39.1105 -            >>> sys.modules['pymssql'] = sys.modules['sys']
 39.1106 -            
 39.1107 -            MSSQL has TOP clause instead of LIMIT clause.
 39.1108 -            >>> db = MSSQLDB(db='test', user='joe', pw='secret')
 39.1109 -            >>> db.select('foo', limit=4, _test=True)
 39.1110 -            <sql: 'SELECT * TOP 4 FROM foo'>
 39.1111 -        """
 39.1112 -        pass
 39.1113 -
 39.1114 -class OracleDB(DB): 
 39.1115 -    def __init__(self, **keywords): 
 39.1116 -        import cx_Oracle as db 
 39.1117 -        if 'pw' in keywords: 
 39.1118 -            keywords['password'] = keywords.pop('pw') 
 39.1119 -
 39.1120 -        #@@ TODO: use db.makedsn if host, port is specified 
 39.1121 -        keywords['dsn'] = keywords.pop('db') 
 39.1122 -        self.dbname = 'oracle' 
 39.1123 -        db.paramstyle = 'numeric' 
 39.1124 -        self.paramstyle = db.paramstyle
 39.1125 -
 39.1126 -        # oracle doesn't support pooling 
 39.1127 -        keywords.pop('pooling', None) 
 39.1128 -        DB.__init__(self, db, keywords) 
 39.1129 -
 39.1130 -    def _process_insert_query(self, query, tablename, seqname): 
 39.1131 -        if seqname is None: 
 39.1132 -            # It is not possible to get seq name from table name in Oracle
 39.1133 -            return query
 39.1134 -        else:
 39.1135 -            return query + "; SELECT %s.currval FROM dual" % seqname 
 39.1136 -
 39.1137 -_databases = {}
 39.1138 -def database(dburl=None, **params):
 39.1139 -    """Creates appropriate database using params.
 39.1140 -    
 39.1141 -    Pooling will be enabled if DBUtils module is available. 
 39.1142 -    Pooling can be disabled by passing pooling=False in params.
 39.1143 -    """
 39.1144 -    dbn = params.pop('dbn')
 39.1145 -    if dbn in _databases:
 39.1146 -        return _databases[dbn](**params)
 39.1147 -    else:
 39.1148 -        raise UnknownDB, dbn
 39.1149 -
 39.1150 -def register_database(name, clazz):
 39.1151 -    """
 39.1152 -    Register a database.
 39.1153 -
 39.1154 -        >>> class LegacyDB(DB): 
 39.1155 -        ...     def __init__(self, **params): 
 39.1156 -        ...        pass 
 39.1157 -        ...
 39.1158 -        >>> register_database('legacy', LegacyDB)
 39.1159 -        >>> db = database(dbn='legacy', db='test', user='joe', passwd='secret') 
 39.1160 -    """
 39.1161 -    _databases[name] = clazz
 39.1162 -
 39.1163 -register_database('mysql', MySQLDB)
 39.1164 -register_database('postgres', PostgresDB)
 39.1165 -register_database('sqlite', SqliteDB)
 39.1166 -register_database('firebird', FirebirdDB)
 39.1167 -register_database('mssql', MSSQLDB)
 39.1168 -register_database('oracle', OracleDB)
 39.1169 -
 39.1170 -def _interpolate(format): 
 39.1171 -    """
 39.1172 -    Takes a format string and returns a list of 2-tuples of the form
 39.1173 -    (boolean, string) where boolean says whether string should be evaled
 39.1174 -    or not.
 39.1175 -
 39.1176 -    from <http://lfw.org/python/Itpl.py> (public domain, Ka-Ping Yee)
 39.1177 -    """
 39.1178 -    from tokenize import tokenprog
 39.1179 -
 39.1180 -    def matchorfail(text, pos):
 39.1181 -        match = tokenprog.match(text, pos)
 39.1182 -        if match is None:
 39.1183 -            raise _ItplError(text, pos)
 39.1184 -        return match, match.end()
 39.1185 -
 39.1186 -    namechars = "abcdefghijklmnopqrstuvwxyz" \
 39.1187 -        "ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789_";
 39.1188 -    chunks = []
 39.1189 -    pos = 0
 39.1190 -
 39.1191 -    while 1:
 39.1192 -        dollar = format.find("$", pos)
 39.1193 -        if dollar < 0: 
 39.1194 -            break
 39.1195 -        nextchar = format[dollar + 1]
 39.1196 -
 39.1197 -        if nextchar == "{":
 39.1198 -            chunks.append((0, format[pos:dollar]))
 39.1199 -            pos, level = dollar + 2, 1
 39.1200 -            while level:
 39.1201 -                match, pos = matchorfail(format, pos)
 39.1202 -                tstart, tend = match.regs[3]
 39.1203 -                token = format[tstart:tend]
 39.1204 -                if token == "{": 
 39.1205 -                    level = level + 1
 39.1206 -                elif token == "}":  
 39.1207 -                    level = level - 1
 39.1208 -            chunks.append((1, format[dollar + 2:pos - 1]))
 39.1209 -
 39.1210 -        elif nextchar in namechars:
 39.1211 -            chunks.append((0, format[pos:dollar]))
 39.1212 -            match, pos = matchorfail(format, dollar + 1)
 39.1213 -            while pos < len(format):
 39.1214 -                if format[pos] == "." and \
 39.1215 -                    pos + 1 < len(format) and format[pos + 1] in namechars:
 39.1216 -                    match, pos = matchorfail(format, pos + 1)
 39.1217 -                elif format[pos] in "([":
 39.1218 -                    pos, level = pos + 1, 1
 39.1219 -                    while level:
 39.1220 -                        match, pos = matchorfail(format, pos)
 39.1221 -                        tstart, tend = match.regs[3]
 39.1222 -                        token = format[tstart:tend]
 39.1223 -                        if token[0] in "([": 
 39.1224 -                            level = level + 1
 39.1225 -                        elif token[0] in ")]":  
 39.1226 -                            level = level - 1
 39.1227 -                else: 
 39.1228 -                    break
 39.1229 -            chunks.append((1, format[dollar + 1:pos]))
 39.1230 -        else:
 39.1231 -            chunks.append((0, format[pos:dollar + 1]))
 39.1232 -            pos = dollar + 1 + (nextchar == "$")
 39.1233 -
 39.1234 -    if pos < len(format): 
 39.1235 -        chunks.append((0, format[pos:]))
 39.1236 -    return chunks
 39.1237 -
 39.1238 -if __name__ == "__main__":
 39.1239 -    import doctest
 39.1240 -    doctest.testmod()
    40.1 Binary file OpenSecurity/install/web.py-0.37/web/db.pyc has changed
    41.1 --- a/OpenSecurity/install/web.py-0.37/web/debugerror.py	Thu Feb 20 15:40:48 2014 +0100
    41.2 +++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
    41.3 @@ -1,354 +0,0 @@
    41.4 -"""
    41.5 -pretty debug errors
    41.6 -(part of web.py)
    41.7 -
    41.8 -portions adapted from Django <djangoproject.com> 
    41.9 -Copyright (c) 2005, the Lawrence Journal-World
   41.10 -Used under the modified BSD license:
   41.11 -http://www.xfree86.org/3.3.6/COPYRIGHT2.html#5
   41.12 -"""
   41.13 -
   41.14 -__all__ = ["debugerror", "djangoerror", "emailerrors"]
   41.15 -
   41.16 -import sys, urlparse, pprint, traceback
   41.17 -from template import Template
   41.18 -from net import websafe
   41.19 -from utils import sendmail, safestr
   41.20 -import webapi as web
   41.21 -
   41.22 -import os, os.path
   41.23 -whereami = os.path.join(os.getcwd(), __file__)
   41.24 -whereami = os.path.sep.join(whereami.split(os.path.sep)[:-1])
   41.25 -djangoerror_t = """\
   41.26 -$def with (exception_type, exception_value, frames)
   41.27 -<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
   41.28 -<html lang="en">
   41.29 -<head>
   41.30 -  <meta http-equiv="content-type" content="text/html; charset=utf-8" />
   41.31 -  <meta name="robots" content="NONE,NOARCHIVE" />
   41.32 -  <title>$exception_type at $ctx.path</title>
   41.33 -  <style type="text/css">
   41.34 -    html * { padding:0; margin:0; }
   41.35 -    body * { padding:10px 20px; }
   41.36 -    body * * { padding:0; }
   41.37 -    body { font:small sans-serif; }
   41.38 -    body>div { border-bottom:1px solid #ddd; }
   41.39 -    h1 { font-weight:normal; }
   41.40 -    h2 { margin-bottom:.8em; }
   41.41 -    h2 span { font-size:80%; color:#666; font-weight:normal; }
   41.42 -    h3 { margin:1em 0 .5em 0; }
   41.43 -    h4 { margin:0 0 .5em 0; font-weight: normal; }
   41.44 -    table { 
   41.45 -        border:1px solid #ccc; border-collapse: collapse; background:white; }
   41.46 -    tbody td, tbody th { vertical-align:top; padding:2px 3px; }
   41.47 -    thead th { 
   41.48 -        padding:1px 6px 1px 3px; background:#fefefe; text-align:left; 
   41.49 -        font-weight:normal; font-size:11px; border:1px solid #ddd; }
   41.50 -    tbody th { text-align:right; color:#666; padding-right:.5em; }
   41.51 -    table.vars { margin:5px 0 2px 40px; }
   41.52 -    table.vars td, table.req td { font-family:monospace; }
   41.53 -    table td.code { width:100%;}
   41.54 -    table td.code div { overflow:hidden; }
   41.55 -    table.source th { color:#666; }
   41.56 -    table.source td { 
   41.57 -        font-family:monospace; white-space:pre; border-bottom:1px solid #eee; }
   41.58 -    ul.traceback { list-style-type:none; }
   41.59 -    ul.traceback li.frame { margin-bottom:1em; }
   41.60 -    div.context { margin: 10px 0; }
   41.61 -    div.context ol { 
   41.62 -        padding-left:30px; margin:0 10px; list-style-position: inside; }
   41.63 -    div.context ol li { 
   41.64 -        font-family:monospace; white-space:pre; color:#666; cursor:pointer; }
   41.65 -    div.context ol.context-line li { color:black; background-color:#ccc; }
   41.66 -    div.context ol.context-line li span { float: right; }
   41.67 -    div.commands { margin-left: 40px; }
   41.68 -    div.commands a { color:black; text-decoration:none; }
   41.69 -    #summary { background: #ffc; }
   41.70 -    #summary h2 { font-weight: normal; color: #666; }
   41.71 -    #explanation { background:#eee; }
   41.72 -    #template, #template-not-exist { background:#f6f6f6; }
   41.73 -    #template-not-exist ul { margin: 0 0 0 20px; }
   41.74 -    #traceback { background:#eee; }
   41.75 -    #requestinfo { background:#f6f6f6; padding-left:120px; }
   41.76 -    #summary table { border:none; background:transparent; }
   41.77 -    #requestinfo h2, #requestinfo h3 { position:relative; margin-left:-100px; }
   41.78 -    #requestinfo h3 { margin-bottom:-1em; }
   41.79 -    .error { background: #ffc; }
   41.80 -    .specific { color:#cc3300; font-weight:bold; }
   41.81 -  </style>
   41.82 -  <script type="text/javascript">
   41.83 -  //<!--
   41.84 -    function getElementsByClassName(oElm, strTagName, strClassName){
   41.85 -        // Written by Jonathan Snook, http://www.snook.ca/jon; 
   41.86 -        // Add-ons by Robert Nyman, http://www.robertnyman.com
   41.87 -        var arrElements = (strTagName == "*" && document.all)? document.all :
   41.88 -        oElm.getElementsByTagName(strTagName);
   41.89 -        var arrReturnElements = new Array();
   41.90 -        strClassName = strClassName.replace(/\-/g, "\\-");
   41.91 -        var oRegExp = new RegExp("(^|\\s)" + strClassName + "(\\s|$$)");
   41.92 -        var oElement;
   41.93 -        for(var i=0; i<arrElements.length; i++){
   41.94 -            oElement = arrElements[i];
   41.95 -            if(oRegExp.test(oElement.className)){
   41.96 -                arrReturnElements.push(oElement);
   41.97 -            }
   41.98 -        }
   41.99 -        return (arrReturnElements)
  41.100 -    }
  41.101 -    function hideAll(elems) {
  41.102 -      for (var e = 0; e < elems.length; e++) {
  41.103 -        elems[e].style.display = 'none';
  41.104 -      }
  41.105 -    }
  41.106 -    window.onload = function() {
  41.107 -      hideAll(getElementsByClassName(document, 'table', 'vars'));
  41.108 -      hideAll(getElementsByClassName(document, 'ol', 'pre-context'));
  41.109 -      hideAll(getElementsByClassName(document, 'ol', 'post-context'));
  41.110 -    }
  41.111 -    function toggle() {
  41.112 -      for (var i = 0; i < arguments.length; i++) {
  41.113 -        var e = document.getElementById(arguments[i]);
  41.114 -        if (e) {
  41.115 -          e.style.display = e.style.display == 'none' ? 'block' : 'none';
  41.116 -        }
  41.117 -      }
  41.118 -      return false;
  41.119 -    }
  41.120 -    function varToggle(link, id) {
  41.121 -      toggle('v' + id);
  41.122 -      var s = link.getElementsByTagName('span')[0];
  41.123 -      var uarr = String.fromCharCode(0x25b6);
  41.124 -      var darr = String.fromCharCode(0x25bc);
  41.125 -      s.innerHTML = s.innerHTML == uarr ? darr : uarr;
  41.126 -      return false;
  41.127 -    }
  41.128 -    //-->
  41.129 -  </script>
  41.130 -</head>
  41.131 -<body>
  41.132 -
  41.133 -$def dicttable (d, kls='req', id=None):
  41.134 -    $ items = d and d.items() or []
  41.135 -    $items.sort()
  41.136 -    $:dicttable_items(items, kls, id)
  41.137 -        
  41.138 -$def dicttable_items(items, kls='req', id=None):
  41.139 -    $if items:
  41.140 -        <table class="$kls"
  41.141 -        $if id: id="$id"
  41.142 -        ><thead><tr><th>Variable</th><th>Value</th></tr></thead>
  41.143 -        <tbody>
  41.144 -        $for k, v in items:
  41.145 -            <tr><td>$k</td><td class="code"><div>$prettify(v)</div></td></tr>
  41.146 -        </tbody>
  41.147 -        </table>
  41.148 -    $else:
  41.149 -        <p>No data.</p>
  41.150 -
  41.151 -<div id="summary">
  41.152 -  <h1>$exception_type at $ctx.path</h1>
  41.153 -  <h2>$exception_value</h2>
  41.154 -  <table><tr>
  41.155 -    <th>Python</th>
  41.156 -    <td>$frames[0].filename in $frames[0].function, line $frames[0].lineno</td>
  41.157 -  </tr><tr>
  41.158 -    <th>Web</th>
  41.159 -    <td>$ctx.method $ctx.home$ctx.path</td>
  41.160 -  </tr></table>
  41.161 -</div>
  41.162 -<div id="traceback">
  41.163 -<h2>Traceback <span>(innermost first)</span></h2>
  41.164 -<ul class="traceback">
  41.165 -$for frame in frames:
  41.166 -    <li class="frame">
  41.167 -    <code>$frame.filename</code> in <code>$frame.function</code>
  41.168 -    $if frame.context_line is not None:
  41.169 -        <div class="context" id="c$frame.id">
  41.170 -        $if frame.pre_context:
  41.171 -            <ol start="$frame.pre_context_lineno" class="pre-context" id="pre$frame.id">
  41.172 -            $for line in frame.pre_context:
  41.173 -                <li onclick="toggle('pre$frame.id', 'post$frame.id')">$line</li>
  41.174 -            </ol>
  41.175 -            <ol start="$frame.lineno" class="context-line"><li onclick="toggle('pre$frame.id', 'post$frame.id')">$frame.context_line <span>...</span></li></ol>
  41.176 -        $if frame.post_context:
  41.177 -            <ol start='${frame.lineno + 1}' class="post-context" id="post$frame.id">
  41.178 -            $for line in frame.post_context:
  41.179 -                <li onclick="toggle('pre$frame.id', 'post$frame.id')">$line</li>
  41.180 -            </ol>
  41.181 -      </div>
  41.182 -    
  41.183 -    $if frame.vars:
  41.184 -        <div class="commands">
  41.185 -        <a href='#' onclick="return varToggle(this, '$frame.id')"><span>&#x25b6;</span> Local vars</a>
  41.186 -        $# $inspect.formatargvalues(*inspect.getargvalues(frame['tb'].tb_frame))
  41.187 -        </div>
  41.188 -        $:dicttable(frame.vars, kls='vars', id=('v' + str(frame.id)))
  41.189 -      </li>
  41.190 -  </ul>
  41.191 -</div>
  41.192 -
  41.193 -<div id="requestinfo">
  41.194 -$if ctx.output or ctx.headers:
  41.195 -    <h2>Response so far</h2>
  41.196 -    <h3>HEADERS</h3>
  41.197 -    $:dicttable_items(ctx.headers)
  41.198 -
  41.199 -    <h3>BODY</h3>
  41.200 -    <p class="req" style="padding-bottom: 2em"><code>
  41.201 -    $ctx.output
  41.202 -    </code></p>
  41.203 -  
  41.204 -<h2>Request information</h2>
  41.205 -
  41.206 -<h3>INPUT</h3>
  41.207 -$:dicttable(web.input(_unicode=False))
  41.208 -
  41.209 -<h3 id="cookie-info">COOKIES</h3>
  41.210 -$:dicttable(web.cookies())
  41.211 -
  41.212 -<h3 id="meta-info">META</h3>
  41.213 -$ newctx = [(k, v) for (k, v) in ctx.iteritems() if not k.startswith('_') and not isinstance(v, dict)]
  41.214 -$:dicttable(dict(newctx))
  41.215 -
  41.216 -<h3 id="meta-info">ENVIRONMENT</h3>
  41.217 -$:dicttable(ctx.env)
  41.218 -</div>
  41.219 -
  41.220 -<div id="explanation">
  41.221 -  <p>
  41.222 -    You're seeing this error because you have <code>web.config.debug</code>
  41.223 -    set to <code>True</code>. Set that to <code>False</code> if you don't want to see this.
  41.224 -  </p>
  41.225 -</div>
  41.226 -
  41.227 -</body>
  41.228 -</html>
  41.229 -"""
  41.230 -
  41.231 -djangoerror_r = None
  41.232 -
  41.233 -def djangoerror():
  41.234 -    def _get_lines_from_file(filename, lineno, context_lines):
  41.235 -        """
  41.236 -        Returns context_lines before and after lineno from file.
  41.237 -        Returns (pre_context_lineno, pre_context, context_line, post_context).
  41.238 -        """
  41.239 -        try:
  41.240 -            source = open(filename).readlines()
  41.241 -            lower_bound = max(0, lineno - context_lines)
  41.242 -            upper_bound = lineno + context_lines
  41.243 -
  41.244 -            pre_context = \
  41.245 -                [line.strip('\n') for line in source[lower_bound:lineno]]
  41.246 -            context_line = source[lineno].strip('\n')
  41.247 -            post_context = \
  41.248 -                [line.strip('\n') for line in source[lineno + 1:upper_bound]]
  41.249 -
  41.250 -            return lower_bound, pre_context, context_line, post_context
  41.251 -        except (OSError, IOError, IndexError):
  41.252 -            return None, [], None, []    
  41.253 -    
  41.254 -    exception_type, exception_value, tback = sys.exc_info()
  41.255 -    frames = []
  41.256 -    while tback is not None:
  41.257 -        filename = tback.tb_frame.f_code.co_filename
  41.258 -        function = tback.tb_frame.f_code.co_name
  41.259 -        lineno = tback.tb_lineno - 1
  41.260 -
  41.261 -        # hack to get correct line number for templates
  41.262 -        lineno += tback.tb_frame.f_locals.get("__lineoffset__", 0)
  41.263 -        
  41.264 -        pre_context_lineno, pre_context, context_line, post_context = \
  41.265 -            _get_lines_from_file(filename, lineno, 7)
  41.266 -
  41.267 -        if '__hidetraceback__' not in tback.tb_frame.f_locals:
  41.268 -            frames.append(web.storage({
  41.269 -                'tback': tback,
  41.270 -                'filename': filename,
  41.271 -                'function': function,
  41.272 -                'lineno': lineno,
  41.273 -                'vars': tback.tb_frame.f_locals,
  41.274 -                'id': id(tback),
  41.275 -                'pre_context': pre_context,
  41.276 -                'context_line': context_line,
  41.277 -                'post_context': post_context,
  41.278 -                'pre_context_lineno': pre_context_lineno,
  41.279 -            }))
  41.280 -        tback = tback.tb_next
  41.281 -    frames.reverse()
  41.282 -    urljoin = urlparse.urljoin
  41.283 -    def prettify(x):
  41.284 -        try: 
  41.285 -            out = pprint.pformat(x)
  41.286 -        except Exception, e: 
  41.287 -            out = '[could not display: <' + e.__class__.__name__ + \
  41.288 -                  ': '+str(e)+'>]'
  41.289 -        return out
  41.290 -        
  41.291 -    global djangoerror_r
  41.292 -    if djangoerror_r is None:
  41.293 -        djangoerror_r = Template(djangoerror_t, filename=__file__, filter=websafe)
  41.294 -        
  41.295 -    t = djangoerror_r
  41.296 -    globals = {'ctx': web.ctx, 'web':web, 'dict':dict, 'str':str, 'prettify': prettify}
  41.297 -    t.t.func_globals.update(globals)
  41.298 -    return t(exception_type, exception_value, frames)
  41.299 -
  41.300 -def debugerror():
  41.301 -    """
  41.302 -    A replacement for `internalerror` that presents a nice page with lots
  41.303 -    of debug information for the programmer.
  41.304 -
  41.305 -    (Based on the beautiful 500 page from [Django](http://djangoproject.com/), 
  41.306 -    designed by [Wilson Miner](http://wilsonminer.com/).)
  41.307 -    """
  41.308 -    return web._InternalError(djangoerror())
  41.309 -
  41.310 -def emailerrors(to_address, olderror, from_address=None):
  41.311 -    """
  41.312 -    Wraps the old `internalerror` handler (pass as `olderror`) to 
  41.313 -    additionally email all errors to `to_address`, to aid in
  41.314 -    debugging production websites.
  41.315 -    
  41.316 -    Emails contain a normal text traceback as well as an
  41.317 -    attachment containing the nice `debugerror` page.
  41.318 -    """
  41.319 -    from_address = from_address or to_address
  41.320 -
  41.321 -    def emailerrors_internal():
  41.322 -        error = olderror()
  41.323 -        tb = sys.exc_info()
  41.324 -        error_name = tb[0]
  41.325 -        error_value = tb[1]
  41.326 -        tb_txt = ''.join(traceback.format_exception(*tb))
  41.327 -        path = web.ctx.path
  41.328 -        request = web.ctx.method + ' ' + web.ctx.home + web.ctx.fullpath
  41.329 -        
  41.330 -        message = "\n%s\n\n%s\n\n" % (request, tb_txt)
  41.331 -        
  41.332 -        sendmail(
  41.333 -            "your buggy site <%s>" % from_address,
  41.334 -            "the bugfixer <%s>" % to_address,
  41.335 -            "bug: %(error_name)s: %(error_value)s (%(path)s)" % locals(),
  41.336 -            message,
  41.337 -            attachments=[
  41.338 -                dict(filename="bug.html", content=safestr(djangoerror()))
  41.339 -            ],
  41.340 -        )
  41.341 -        return error
  41.342 -    
  41.343 -    return emailerrors_internal
  41.344 -
  41.345 -if __name__ == "__main__":
  41.346 -    urls = (
  41.347 -        '/', 'index'
  41.348 -    )
  41.349 -    from application import application
  41.350 -    app = application(urls, globals())
  41.351 -    app.internalerror = debugerror
  41.352 -    
  41.353 -    class index:
  41.354 -        def GET(self):
  41.355 -            thisdoesnotexist
  41.356 -
  41.357 -    app.run()
    42.1 Binary file OpenSecurity/install/web.py-0.37/web/debugerror.pyc has changed
    43.1 --- a/OpenSecurity/install/web.py-0.37/web/form.py	Thu Feb 20 15:40:48 2014 +0100
    43.2 +++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
    43.3 @@ -1,410 +0,0 @@
    43.4 -"""
    43.5 -HTML forms
    43.6 -(part of web.py)
    43.7 -"""
    43.8 -
    43.9 -import copy, re
   43.10 -import webapi as web
   43.11 -import utils, net
   43.12 -
   43.13 -def attrget(obj, attr, value=None):
   43.14 -    try:
   43.15 -        if hasattr(obj, 'has_key') and obj.has_key(attr): 
   43.16 -            return obj[attr]
   43.17 -    except TypeError:
   43.18 -        # Handle the case where has_key takes different number of arguments.
   43.19 -        # This is the case with Model objects on appengine. See #134
   43.20 -        pass
   43.21 -    if hasattr(obj, attr):
   43.22 -        return getattr(obj, attr)
   43.23 -    return value
   43.24 -
   43.25 -class Form(object):
   43.26 -    r"""
   43.27 -    HTML form.
   43.28 -    
   43.29 -        >>> f = Form(Textbox("x"))
   43.30 -        >>> f.render()
   43.31 -        u'<table>\n    <tr><th><label for="x">x</label></th><td><input type="text" id="x" name="x"/></td></tr>\n</table>'
   43.32 -    """
   43.33 -    def __init__(self, *inputs, **kw):
   43.34 -        self.inputs = inputs
   43.35 -        self.valid = True
   43.36 -        self.note = None
   43.37 -        self.validators = kw.pop('validators', [])
   43.38 -
   43.39 -    def __call__(self, x=None):
   43.40 -        o = copy.deepcopy(self)
   43.41 -        if x: o.validates(x)
   43.42 -        return o
   43.43 -    
   43.44 -    def render(self):
   43.45 -        out = ''
   43.46 -        out += self.rendernote(self.note)
   43.47 -        out += '<table>\n'
   43.48 -        
   43.49 -        for i in self.inputs:
   43.50 -            html = utils.safeunicode(i.pre) + i.render() + self.rendernote(i.note) + utils.safeunicode(i.post)
   43.51 -            if i.is_hidden():
   43.52 -                out += '    <tr style="display: none;"><th></th><td>%s</td></tr>\n' % (html)
   43.53 -            else:
   43.54 -                out += '    <tr><th><label for="%s">%s</label></th><td>%s</td></tr>\n' % (i.id, net.websafe(i.description), html)
   43.55 -        out += "</table>"
   43.56 -        return out
   43.57 -        
   43.58 -    def render_css(self): 
   43.59 -        out = [] 
   43.60 -        out.append(self.rendernote(self.note)) 
   43.61 -        for i in self.inputs:
   43.62 -            if not i.is_hidden():
   43.63 -                out.append('<label for="%s">%s</label>' % (i.id, net.websafe(i.description))) 
   43.64 -            out.append(i.pre)
   43.65 -            out.append(i.render()) 
   43.66 -            out.append(self.rendernote(i.note))
   43.67 -            out.append(i.post) 
   43.68 -            out.append('\n')
   43.69 -        return ''.join(out) 
   43.70 -        
   43.71 -    def rendernote(self, note):
   43.72 -        if note: return '<strong class="wrong">%s</strong>' % net.websafe(note)
   43.73 -        else: return ""
   43.74 -    
   43.75 -    def validates(self, source=None, _validate=True, **kw):
   43.76 -        source = source or kw or web.input()
   43.77 -        out = True
   43.78 -        for i in self.inputs:
   43.79 -            v = attrget(source, i.name)
   43.80 -            if _validate:
   43.81 -                out = i.validate(v) and out
   43.82 -            else:
   43.83 -                i.set_value(v)
   43.84 -        if _validate:
   43.85 -            out = out and self._validate(source)
   43.86 -            self.valid = out
   43.87 -        return out
   43.88 -
   43.89 -    def _validate(self, value):
   43.90 -        self.value = value
   43.91 -        for v in self.validators:
   43.92 -            if not v.valid(value):
   43.93 -                self.note = v.msg
   43.94 -                return False
   43.95 -        return True
   43.96 -
   43.97 -    def fill(self, source=None, **kw):
   43.98 -        return self.validates(source, _validate=False, **kw)
   43.99 -    
  43.100 -    def __getitem__(self, i):
  43.101 -        for x in self.inputs:
  43.102 -            if x.name == i: return x
  43.103 -        raise KeyError, i
  43.104 -
  43.105 -    def __getattr__(self, name):
  43.106 -        # don't interfere with deepcopy
  43.107 -        inputs = self.__dict__.get('inputs') or []
  43.108 -        for x in inputs:
  43.109 -            if x.name == name: return x
  43.110 -        raise AttributeError, name
  43.111 -    
  43.112 -    def get(self, i, default=None):
  43.113 -        try:
  43.114 -            return self[i]
  43.115 -        except KeyError:
  43.116 -            return default
  43.117 -            
  43.118 -    def _get_d(self): #@@ should really be form.attr, no?
  43.119 -        return utils.storage([(i.name, i.get_value()) for i in self.inputs])
  43.120 -    d = property(_get_d)
  43.121 -
  43.122 -class Input(object):
  43.123 -    def __init__(self, name, *validators, **attrs):
  43.124 -        self.name = name
  43.125 -        self.validators = validators
  43.126 -        self.attrs = attrs = AttributeList(attrs)
  43.127 -        
  43.128 -        self.description = attrs.pop('description', name)
  43.129 -        self.value = attrs.pop('value', None)
  43.130 -        self.pre = attrs.pop('pre', "")
  43.131 -        self.post = attrs.pop('post', "")
  43.132 -        self.note = None
  43.133 -        
  43.134 -        self.id = attrs.setdefault('id', self.get_default_id())
  43.135 -        
  43.136 -        if 'class_' in attrs:
  43.137 -            attrs['class'] = attrs['class_']
  43.138 -            del attrs['class_']
  43.139 -        
  43.140 -    def is_hidden(self):
  43.141 -        return False
  43.142 -        
  43.143 -    def get_type(self):
  43.144 -        raise NotImplementedError
  43.145 -        
  43.146 -    def get_default_id(self):
  43.147 -        return self.name
  43.148 -
  43.149 -    def validate(self, value):
  43.150 -        self.set_value(value)
  43.151 -
  43.152 -        for v in self.validators:
  43.153 -            if not v.valid(value):
  43.154 -                self.note = v.msg
  43.155 -                return False
  43.156 -        return True
  43.157 -
  43.158 -    def set_value(self, value):
  43.159 -        self.value = value
  43.160 -
  43.161 -    def get_value(self):
  43.162 -        return self.value
  43.163 -
  43.164 -    def render(self):
  43.165 -        attrs = self.attrs.copy()
  43.166 -        attrs['type'] = self.get_type()
  43.167 -        if self.value is not None:
  43.168 -            attrs['value'] = self.value
  43.169 -        attrs['name'] = self.name
  43.170 -        return '<input %s/>' % attrs
  43.171 -
  43.172 -    def rendernote(self, note):
  43.173 -        if note: return '<strong class="wrong">%s</strong>' % net.websafe(note)
  43.174 -        else: return ""
  43.175 -        
  43.176 -    def addatts(self):
  43.177 -        # add leading space for backward-compatibility
  43.178 -        return " " + str(self.attrs)
  43.179 -
  43.180 -class AttributeList(dict):
  43.181 -    """List of atributes of input.
  43.182 -    
  43.183 -    >>> a = AttributeList(type='text', name='x', value=20)
  43.184 -    >>> a
  43.185 -    <attrs: 'type="text" name="x" value="20"'>
  43.186 -    """
  43.187 -    def copy(self):
  43.188 -        return AttributeList(self)
  43.189 -        
  43.190 -    def __str__(self):
  43.191 -        return " ".join(['%s="%s"' % (k, net.websafe(v)) for k, v in self.items()])
  43.192 -        
  43.193 -    def __repr__(self):
  43.194 -        return '<attrs: %s>' % repr(str(self))
  43.195 -
  43.196 -class Textbox(Input):
  43.197 -    """Textbox input.
  43.198 -    
  43.199 -        >>> Textbox(name='foo', value='bar').render()
  43.200 -        u'<input type="text" id="foo" value="bar" name="foo"/>'
  43.201 -        >>> Textbox(name='foo', value=0).render()
  43.202 -        u'<input type="text" id="foo" value="0" name="foo"/>'
  43.203 -    """        
  43.204 -    def get_type(self):
  43.205 -        return 'text'
  43.206 -
  43.207 -class Password(Input):
  43.208 -    """Password input.
  43.209 -
  43.210 -        >>> Password(name='password', value='secret').render()
  43.211 -        u'<input type="password" id="password" value="secret" name="password"/>'
  43.212 -    """
  43.213 -    
  43.214 -    def get_type(self):
  43.215 -        return 'password'
  43.216 -
  43.217 -class Textarea(Input):
  43.218 -    """Textarea input.
  43.219 -    
  43.220 -        >>> Textarea(name='foo', value='bar').render()
  43.221 -        u'<textarea id="foo" name="foo">bar</textarea>'
  43.222 -    """
  43.223 -    def render(self):
  43.224 -        attrs = self.attrs.copy()
  43.225 -        attrs['name'] = self.name
  43.226 -        value = net.websafe(self.value or '')
  43.227 -        return '<textarea %s>%s</textarea>' % (attrs, value)
  43.228 -
  43.229 -class Dropdown(Input):
  43.230 -    r"""Dropdown/select input.
  43.231 -    
  43.232 -        >>> Dropdown(name='foo', args=['a', 'b', 'c'], value='b').render()
  43.233 -        u'<select id="foo" name="foo">\n  <option value="a">a</option>\n  <option selected="selected" value="b">b</option>\n  <option value="c">c</option>\n</select>\n'
  43.234 -        >>> Dropdown(name='foo', args=[('a', 'aa'), ('b', 'bb'), ('c', 'cc')], value='b').render()
  43.235 -        u'<select id="foo" name="foo">\n  <option value="a">aa</option>\n  <option selected="selected" value="b">bb</option>\n  <option value="c">cc</option>\n</select>\n'
  43.236 -    """
  43.237 -    def __init__(self, name, args, *validators, **attrs):
  43.238 -        self.args = args
  43.239 -        super(Dropdown, self).__init__(name, *validators, **attrs)
  43.240 -
  43.241 -    def render(self):
  43.242 -        attrs = self.attrs.copy()
  43.243 -        attrs['name'] = self.name
  43.244 -        
  43.245 -        x = '<select %s>\n' % attrs
  43.246 -        
  43.247 -        for arg in self.args:
  43.248 -            x += self._render_option(arg)
  43.249 -
  43.250 -        x += '</select>\n'
  43.251 -        return x
  43.252 -
  43.253 -    def _render_option(self, arg, indent='  '):
  43.254 -        if isinstance(arg, (tuple, list)):
  43.255 -            value, desc= arg
  43.256 -        else:
  43.257 -            value, desc = arg, arg 
  43.258 -
  43.259 -        if self.value == value or (isinstance(self.value, list) and value in self.value):
  43.260 -            select_p = ' selected="selected"'
  43.261 -        else:
  43.262 -            select_p = ''
  43.263 -        return indent + '<option%s value="%s">%s</option>\n' % (select_p, net.websafe(value), net.websafe(desc))
  43.264 -        
  43.265 -
  43.266 -class GroupedDropdown(Dropdown):
  43.267 -    r"""Grouped Dropdown/select input.
  43.268 -    
  43.269 -        >>> GroupedDropdown(name='car_type', args=(('Swedish Cars', ('Volvo', 'Saab')), ('German Cars', ('Mercedes', 'Audi'))), value='Audi').render()
  43.270 -        u'<select id="car_type" name="car_type">\n  <optgroup label="Swedish Cars">\n    <option value="Volvo">Volvo</option>\n    <option value="Saab">Saab</option>\n  </optgroup>\n  <optgroup label="German Cars">\n    <option value="Mercedes">Mercedes</option>\n    <option selected="selected" value="Audi">Audi</option>\n  </optgroup>\n</select>\n'
  43.271 -        >>> GroupedDropdown(name='car_type', args=(('Swedish Cars', (('v', 'Volvo'), ('s', 'Saab'))), ('German Cars', (('m', 'Mercedes'), ('a', 'Audi')))), value='a').render()
  43.272 -        u'<select id="car_type" name="car_type">\n  <optgroup label="Swedish Cars">\n    <option value="v">Volvo</option>\n    <option value="s">Saab</option>\n  </optgroup>\n  <optgroup label="German Cars">\n    <option value="m">Mercedes</option>\n    <option selected="selected" value="a">Audi</option>\n  </optgroup>\n</select>\n'
  43.273 -
  43.274 -    """
  43.275 -    def __init__(self, name, args, *validators, **attrs):
  43.276 -        self.args = args
  43.277 -        super(Dropdown, self).__init__(name, *validators, **attrs)
  43.278 -
  43.279 -    def render(self):
  43.280 -        attrs = self.attrs.copy()
  43.281 -        attrs['name'] = self.name
  43.282 -        
  43.283 -        x = '<select %s>\n' % attrs
  43.284 -        
  43.285 -        for label, options in self.args:
  43.286 -            x += '  <optgroup label="%s">\n' % net.websafe(label)
  43.287 -            for arg in options:
  43.288 -                x += self._render_option(arg, indent = '    ')
  43.289 -            x +=  '  </optgroup>\n'
  43.290 -            
  43.291 -        x += '</select>\n'
  43.292 -        return x
  43.293 -
  43.294 -class Radio(Input):
  43.295 -    def __init__(self, name, args, *validators, **attrs):
  43.296 -        self.args = args
  43.297 -        super(Radio, self).__init__(name, *validators, **attrs)
  43.298 -
  43.299 -    def render(self):
  43.300 -        x = '<span>'
  43.301 -        for arg in self.args:
  43.302 -            if isinstance(arg, (tuple, list)):
  43.303 -                value, desc= arg
  43.304 -            else:
  43.305 -                value, desc = arg, arg 
  43.306 -            attrs = self.attrs.copy()
  43.307 -            attrs['name'] = self.name
  43.308 -            attrs['type'] = 'radio'
  43.309 -            attrs['value'] = value
  43.310 -            if self.value == value:
  43.311 -                attrs['checked'] = 'checked'
  43.312 -            x += '<input %s/> %s' % (attrs, net.websafe(desc))
  43.313 -        x += '</span>'
  43.314 -        return x
  43.315 -
  43.316 -class Checkbox(Input):
  43.317 -    """Checkbox input.
  43.318 -
  43.319 -    >>> Checkbox('foo', value='bar', checked=True).render()
  43.320 -    u'<input checked="checked" type="checkbox" id="foo_bar" value="bar" name="foo"/>'
  43.321 -    >>> Checkbox('foo', value='bar').render()
  43.322 -    u'<input type="checkbox" id="foo_bar" value="bar" name="foo"/>'
  43.323 -    >>> c = Checkbox('foo', value='bar')
  43.324 -    >>> c.validate('on')
  43.325 -    True
  43.326 -    >>> c.render()
  43.327 -    u'<input checked="checked" type="checkbox" id="foo_bar" value="bar" name="foo"/>'
  43.328 -    """
  43.329 -    def __init__(self, name, *validators, **attrs):
  43.330 -        self.checked = attrs.pop('checked', False)
  43.331 -        Input.__init__(self, name, *validators, **attrs)
  43.332 -        
  43.333 -    def get_default_id(self):
  43.334 -        value = utils.safestr(self.value or "")
  43.335 -        return self.name + '_' + value.replace(' ', '_')
  43.336 -
  43.337 -    def render(self):
  43.338 -        attrs = self.attrs.copy()
  43.339 -        attrs['type'] = 'checkbox'
  43.340 -        attrs['name'] = self.name
  43.341 -        attrs['value'] = self.value
  43.342 -
  43.343 -        if self.checked:
  43.344 -            attrs['checked'] = 'checked'            
  43.345 -        return '<input %s/>' % attrs
  43.346 -
  43.347 -    def set_value(self, value):
  43.348 -        self.checked = bool(value)
  43.349 -
  43.350 -    def get_value(self):
  43.351 -        return self.checked
  43.352 -
  43.353 -class Button(Input):
  43.354 -    """HTML Button.
  43.355 -    
  43.356 -    >>> Button("save").render()
  43.357 -    u'<button id="save" name="save">save</button>'
  43.358 -    >>> Button("action", value="save", html="<b>Save Changes</b>").render()
  43.359 -    u'<button id="action" value="save" name="action"><b>Save Changes</b></button>'
  43.360 -    """
  43.361 -    def __init__(self, name, *validators, **attrs):
  43.362 -        super(Button, self).__init__(name, *validators, **attrs)
  43.363 -        self.description = ""
  43.364 -
  43.365 -    def render(self):
  43.366 -        attrs = self.attrs.copy()
  43.367 -        attrs['name'] = self.name
  43.368 -        if self.value is not None:
  43.369 -            attrs['value'] = self.value
  43.370 -        html = attrs.pop('html', None) or net.websafe(self.name)
  43.371 -        return '<button %s>%s</button>' % (attrs, html)
  43.372 -
  43.373 -class Hidden(Input):
  43.374 -    """Hidden Input.
  43.375 -    
  43.376 -        >>> Hidden(name='foo', value='bar').render()
  43.377 -        u'<input type="hidden" id="foo" value="bar" name="foo"/>'
  43.378 -    """
  43.379 -    def is_hidden(self):
  43.380 -        return True
  43.381 -        
  43.382 -    def get_type(self):
  43.383 -        return 'hidden'
  43.384 -
  43.385 -class File(Input):
  43.386 -    """File input.
  43.387 -    
  43.388 -        >>> File(name='f').render()
  43.389 -        u'<input type="file" id="f" name="f"/>'
  43.390 -    """
  43.391 -    def get_type(self):
  43.392 -        return 'file'
  43.393 -    
  43.394 -class Validator:
  43.395 -    def __deepcopy__(self, memo): return copy.copy(self)
  43.396 -    def __init__(self, msg, test, jstest=None): utils.autoassign(self, locals())
  43.397 -    def valid(self, value): 
  43.398 -        try: return self.test(value)
  43.399 -        except: return False
  43.400 -
  43.401 -notnull = Validator("Required", bool)
  43.402 -
  43.403 -class regexp(Validator):
  43.404 -    def __init__(self, rexp, msg):
  43.405 -        self.rexp = re.compile(rexp)
  43.406 -        self.msg = msg
  43.407 -    
  43.408 -    def valid(self, value):
  43.409 -        return bool(self.rexp.match(value))
  43.410 -
  43.411 -if __name__ == "__main__":
  43.412 -    import doctest
  43.413 -    doctest.testmod()
    44.1 Binary file OpenSecurity/install/web.py-0.37/web/form.pyc has changed
    45.1 --- a/OpenSecurity/install/web.py-0.37/web/http.py	Thu Feb 20 15:40:48 2014 +0100
    45.2 +++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
    45.3 @@ -1,150 +0,0 @@
    45.4 -"""
    45.5 -HTTP Utilities
    45.6 -(from web.py)
    45.7 -"""
    45.8 -
    45.9 -__all__ = [
   45.10 -  "expires", "lastmodified", 
   45.11 -  "prefixurl", "modified", 
   45.12 -  "changequery", "url",
   45.13 -  "profiler",
   45.14 -]
   45.15 -
   45.16 -import sys, os, threading, urllib, urlparse
   45.17 -try: import datetime
   45.18 -except ImportError: pass
   45.19 -import net, utils, webapi as web
   45.20 -
   45.21 -def prefixurl(base=''):
   45.22 -    """
   45.23 -    Sorry, this function is really difficult to explain.
   45.24 -    Maybe some other time.
   45.25 -    """
   45.26 -    url = web.ctx.path.lstrip('/')
   45.27 -    for i in xrange(url.count('/')): 
   45.28 -        base += '../'
   45.29 -    if not base: 
   45.30 -        base = './'
   45.31 -    return base
   45.32 -
   45.33 -def expires(delta):
   45.34 -    """
   45.35 -    Outputs an `Expires` header for `delta` from now. 
   45.36 -    `delta` is a `timedelta` object or a number of seconds.
   45.37 -    """
   45.38 -    if isinstance(delta, (int, long)):
   45.39 -        delta = datetime.timedelta(seconds=delta)
   45.40 -    date_obj = datetime.datetime.utcnow() + delta
   45.41 -    web.header('Expires', net.httpdate(date_obj))
   45.42 -
   45.43 -def lastmodified(date_obj):
   45.44 -    """Outputs a `Last-Modified` header for `datetime`."""
   45.45 -    web.header('Last-Modified', net.httpdate(date_obj))
   45.46 -
   45.47 -def modified(date=None, etag=None):
   45.48 -    """
   45.49 -    Checks to see if the page has been modified since the version in the
   45.50 -    requester's cache.
   45.51 -    
   45.52 -    When you publish pages, you can include `Last-Modified` and `ETag`
   45.53 -    with the date the page was last modified and an opaque token for
   45.54 -    the particular version, respectively. When readers reload the page, 
   45.55 -    the browser sends along the modification date and etag value for
   45.56 -    the version it has in its cache. If the page hasn't changed, 
   45.57 -    the server can just return `304 Not Modified` and not have to 
   45.58 -    send the whole page again.
   45.59 -    
   45.60 -    This function takes the last-modified date `date` and the ETag `etag`
   45.61 -    and checks the headers to see if they match. If they do, it returns 
   45.62 -    `True`, or otherwise it raises NotModified error. It also sets 
   45.63 -    `Last-Modified` and `ETag` output headers.
   45.64 -    """
   45.65 -    try:
   45.66 -        from __builtin__ import set
   45.67 -    except ImportError:
   45.68 -        # for python 2.3
   45.69 -        from sets import Set as set
   45.70 -
   45.71 -    n = set([x.strip('" ') for x in web.ctx.env.get('HTTP_IF_NONE_MATCH', '').split(',')])
   45.72 -    m = net.parsehttpdate(web.ctx.env.get('HTTP_IF_MODIFIED_SINCE', '').split(';')[0])
   45.73 -    validate = False
   45.74 -    if etag:
   45.75 -        if '*' in n or etag in n:
   45.76 -            validate = True
   45.77 -    if date and m:
   45.78 -        # we subtract a second because 
   45.79 -        # HTTP dates don't have sub-second precision
   45.80 -        if date-datetime.timedelta(seconds=1) <= m:
   45.81 -            validate = True
   45.82 -    
   45.83 -    if date: lastmodified(date)
   45.84 -    if etag: web.header('ETag', '"' + etag + '"')
   45.85 -    if validate:
   45.86 -        raise web.notmodified()
   45.87 -    else:
   45.88 -        return True
   45.89 -
   45.90 -def urlencode(query, doseq=0):
   45.91 -    """
   45.92 -    Same as urllib.urlencode, but supports unicode strings.
   45.93 -    
   45.94 -        >>> urlencode({'text':'foo bar'})
   45.95 -        'text=foo+bar'
   45.96 -        >>> urlencode({'x': [1, 2]}, doseq=True)
   45.97 -        'x=1&x=2'
   45.98 -    """
   45.99 -    def convert(value, doseq=False):
  45.100 -        if doseq and isinstance(value, list):
  45.101 -            return [convert(v) for v in value]
  45.102 -        else:
  45.103 -            return utils.safestr(value)
  45.104 -        
  45.105 -    query = dict([(k, convert(v, doseq)) for k, v in query.items()])
  45.106 -    return urllib.urlencode(query, doseq=doseq)
  45.107 -
  45.108 -def changequery(query=None, **kw):
  45.109 -    """
  45.110 -    Imagine you're at `/foo?a=1&b=2`. Then `changequery(a=3)` will return
  45.111 -    `/foo?a=3&b=2` -- the same URL but with the arguments you requested
  45.112 -    changed.
  45.113 -    """
  45.114 -    if query is None:
  45.115 -        query = web.rawinput(method='get')
  45.116 -    for k, v in kw.iteritems():
  45.117 -        if v is None:
  45.118 -            query.pop(k, None)
  45.119 -        else:
  45.120 -            query[k] = v
  45.121 -    out = web.ctx.path
  45.122 -    if query:
  45.123 -        out += '?' + urlencode(query, doseq=True)
  45.124 -    return out
  45.125 -
  45.126 -def url(path=None, doseq=False, **kw):
  45.127 -    """
  45.128 -    Makes url by concatenating web.ctx.homepath and path and the 
  45.129 -    query string created using the arguments.
  45.130 -    """
  45.131 -    if path is None:
  45.132 -        path = web.ctx.path
  45.133 -    if path.startswith("/"):
  45.134 -        out = web.ctx.homepath + path
  45.135 -    else:
  45.136 -        out = path
  45.137 -
  45.138 -    if kw:
  45.139 -        out += '?' + urlencode(kw, doseq=doseq)
  45.140 -    
  45.141 -    return out
  45.142 -
  45.143 -def profiler(app):
  45.144 -    """Outputs basic profiling information at the bottom of each response."""
  45.145 -    from utils import profile
  45.146 -    def profile_internal(e, o):
  45.147 -        out, result = profile(app)(e, o)
  45.148 -        return list(out) + ['<pre>' + net.websafe(result) + '</pre>']
  45.149 -    return profile_internal
  45.150 -
  45.151 -if __name__ == "__main__":
  45.152 -    import doctest
  45.153 -    doctest.testmod()
    46.1 Binary file OpenSecurity/install/web.py-0.37/web/http.pyc has changed
    47.1 --- a/OpenSecurity/install/web.py-0.37/web/httpserver.py	Thu Feb 20 15:40:48 2014 +0100
    47.2 +++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
    47.3 @@ -1,319 +0,0 @@
    47.4 -__all__ = ["runsimple"]
    47.5 -
    47.6 -import sys, os
    47.7 -from SimpleHTTPServer import SimpleHTTPRequestHandler
    47.8 -import urllib
    47.9 -import posixpath
   47.10 -
   47.11 -import webapi as web
   47.12 -import net
   47.13 -import utils
   47.14 -
   47.15 -def runbasic(func, server_address=("0.0.0.0", 8080)):
   47.16 -    """
   47.17 -    Runs a simple HTTP server hosting WSGI app `func`. The directory `static/` 
   47.18 -    is hosted statically.
   47.19 -
   47.20 -    Based on [WsgiServer][ws] from [Colin Stewart][cs].
   47.21 -    
   47.22 -  [ws]: http://www.owlfish.com/software/wsgiutils/documentation/wsgi-server-api.html
   47.23 -  [cs]: http://www.owlfish.com/
   47.24 -    """
   47.25 -    # Copyright (c) 2004 Colin Stewart (http://www.owlfish.com/)
   47.26 -    # Modified somewhat for simplicity
   47.27 -    # Used under the modified BSD license:
   47.28 -    # http://www.xfree86.org/3.3.6/COPYRIGHT2.html#5
   47.29 -
   47.30 -    import SimpleHTTPServer, SocketServer, BaseHTTPServer, urlparse
   47.31 -    import socket, errno
   47.32 -    import traceback
   47.33 -
   47.34 -    class WSGIHandler(SimpleHTTPServer.SimpleHTTPRequestHandler):
   47.35 -        def run_wsgi_app(self):
   47.36 -            protocol, host, path, parameters, query, fragment = \
   47.37 -                urlparse.urlparse('http://dummyhost%s' % self.path)
   47.38 -
   47.39 -            # we only use path, query
   47.40 -            env = {'wsgi.version': (1, 0)
   47.41 -                   ,'wsgi.url_scheme': 'http'
   47.42 -                   ,'wsgi.input': self.rfile
   47.43 -                   ,'wsgi.errors': sys.stderr
   47.44 -                   ,'wsgi.multithread': 1
   47.45 -                   ,'wsgi.multiprocess': 0
   47.46 -                   ,'wsgi.run_once': 0
   47.47 -                   ,'REQUEST_METHOD': self.command
   47.48 -                   ,'REQUEST_URI': self.path
   47.49 -                   ,'PATH_INFO': path
   47.50 -                   ,'QUERY_STRING': query
   47.51 -                   ,'CONTENT_TYPE': self.headers.get('Content-Type', '')
   47.52 -                   ,'CONTENT_LENGTH': self.headers.get('Content-Length', '')
   47.53 -                   ,'REMOTE_ADDR': self.client_address[0]
   47.54 -                   ,'SERVER_NAME': self.server.server_address[0]
   47.55 -                   ,'SERVER_PORT': str(self.server.server_address[1])
   47.56 -                   ,'SERVER_PROTOCOL': self.request_version
   47.57 -                   }
   47.58 -
   47.59 -            for http_header, http_value in self.headers.items():
   47.60 -                env ['HTTP_%s' % http_header.replace('-', '_').upper()] = \
   47.61 -                    http_value
   47.62 -
   47.63 -            # Setup the state
   47.64 -            self.wsgi_sent_headers = 0
   47.65 -            self.wsgi_headers = []
   47.66 -
   47.67 -            try:
   47.68 -                # We have there environment, now invoke the application
   47.69 -                result = self.server.app(env, self.wsgi_start_response)
   47.70 -                try:
   47.71 -                    try:
   47.72 -                        for data in result:
   47.73 -                            if data: 
   47.74 -                                self.wsgi_write_data(data)
   47.75 -                    finally:
   47.76 -                        if hasattr(result, 'close'): 
   47.77 -                            result.close()
   47.78 -                except socket.error, socket_err:
   47.79 -                    # Catch common network errors and suppress them
   47.80 -                    if (socket_err.args[0] in \
   47.81 -                       (errno.ECONNABORTED, errno.EPIPE)): 
   47.82 -                        return
   47.83 -                except socket.timeout, socket_timeout: 
   47.84 -                    return
   47.85 -            except:
   47.86 -                print >> web.debug, traceback.format_exc(),
   47.87 -
   47.88 -            if (not self.wsgi_sent_headers):
   47.89 -                # We must write out something!
   47.90 -                self.wsgi_write_data(" ")
   47.91 -            return
   47.92 -
   47.93 -        do_POST = run_wsgi_app
   47.94 -        do_PUT = run_wsgi_app
   47.95 -        do_DELETE = run_wsgi_app
   47.96 -
   47.97 -        def do_GET(self):
   47.98 -            if self.path.startswith('/static/'):
   47.99 -                SimpleHTTPServer.SimpleHTTPRequestHandler.do_GET(self)
  47.100 -            else:
  47.101 -                self.run_wsgi_app()
  47.102 -
  47.103 -        def wsgi_start_response(self, response_status, response_headers, 
  47.104 -                              exc_info=None):
  47.105 -            if (self.wsgi_sent_headers):
  47.106 -                raise Exception \
  47.107 -                      ("Headers already sent and start_response called again!")
  47.108 -            # Should really take a copy to avoid changes in the application....
  47.109 -            self.wsgi_headers = (response_status, response_headers)
  47.110 -            return self.wsgi_write_data
  47.111 -
  47.112 -        def wsgi_write_data(self, data):
  47.113 -            if (not self.wsgi_sent_headers):
  47.114 -                status, headers = self.wsgi_headers
  47.115 -                # Need to send header prior to data
  47.116 -                status_code = status[:status.find(' ')]
  47.117 -                status_msg = status[status.find(' ') + 1:]
  47.118 -                self.send_response(int(status_code), status_msg)
  47.119 -                for header, value in headers:
  47.120 -                    self.send_header(header, value)
  47.121 -                self.end_headers()
  47.122 -                self.wsgi_sent_headers = 1
  47.123 -            # Send the data
  47.124 -            self.wfile.write(data)
  47.125 -
  47.126 -    class WSGIServer(SocketServer.ThreadingMixIn, BaseHTTPServer.HTTPServer):
  47.127 -        def __init__(self, func, server_address):
  47.128 -            BaseHTTPServer.HTTPServer.__init__(self, 
  47.129 -                                               server_address, 
  47.130 -                                               WSGIHandler)
  47.131 -            self.app = func
  47.132 -            self.serverShuttingDown = 0
  47.133 -
  47.134 -    print "http://%s:%d/" % server_address
  47.135 -    WSGIServer(func, server_address).serve_forever()
  47.136 -
  47.137 -# The WSGIServer instance. 
  47.138 -# Made global so that it can be stopped in embedded mode.
  47.139 -server = None
  47.140 -
  47.141 -def runsimple(func, server_address=("0.0.0.0", 8080)):
  47.142 -    """
  47.143 -    Runs [CherryPy][cp] WSGI server hosting WSGI app `func`. 
  47.144 -    The directory `static/` is hosted statically.
  47.145 -
  47.146 -    [cp]: http://www.cherrypy.org
  47.147 -    """
  47.148 -    global server
  47.149 -    func = StaticMiddleware(func)
  47.150 -    func = LogMiddleware(func)
  47.151 -    
  47.152 -    server = WSGIServer(server_address, func)
  47.153 -
  47.154 -    if server.ssl_adapter:
  47.155 -        print "https://%s:%d/" % server_address
  47.156 -    else:
  47.157 -        print "http://%s:%d/" % server_address
  47.158 -
  47.159 -    try:
  47.160 -        server.start()
  47.161 -    except (KeyboardInterrupt, SystemExit):
  47.162 -        server.stop()
  47.163 -        server = None
  47.164 -
  47.165 -def WSGIServer(server_address, wsgi_app):
  47.166 -    """Creates CherryPy WSGI server listening at `server_address` to serve `wsgi_app`.
  47.167 -    This function can be overwritten to customize the webserver or use a different webserver.
  47.168 -    """
  47.169 -    import wsgiserver
  47.170 -    
  47.171 -    # Default values of wsgiserver.ssl_adapters uses cherrypy.wsgiserver
  47.172 -    # prefix. Overwriting it make it work with web.wsgiserver.
  47.173 -    wsgiserver.ssl_adapters = {
  47.174 -        'builtin': 'web.wsgiserver.ssl_builtin.BuiltinSSLAdapter',
  47.175 -        'pyopenssl': 'web.wsgiserver.ssl_pyopenssl.pyOpenSSLAdapter',
  47.176 -    }
  47.177 -    
  47.178 -    server = wsgiserver.CherryPyWSGIServer(server_address, wsgi_app, server_name="localhost")
  47.179 -        
  47.180 -    def create_ssl_adapter(cert, key):
  47.181 -        # wsgiserver tries to import submodules as cherrypy.wsgiserver.foo.
  47.182 -        # That doesn't work as not it is web.wsgiserver. 
  47.183 -        # Patching sys.modules temporarily to make it work.
  47.184 -        import types
  47.185 -        cherrypy = types.ModuleType('cherrypy')
  47.186 -        cherrypy.wsgiserver = wsgiserver
  47.187 -        sys.modules['cherrypy'] = cherrypy
  47.188 -        sys.modules['cherrypy.wsgiserver'] = wsgiserver
  47.189 -        
  47.190 -        from wsgiserver.ssl_pyopenssl import pyOpenSSLAdapter
  47.191 -        adapter = pyOpenSSLAdapter(cert, key)
  47.192 -        
  47.193 -        # We are done with our work. Cleanup the patches.
  47.194 -        del sys.modules['cherrypy']
  47.195 -        del sys.modules['cherrypy.wsgiserver']
  47.196 -
  47.197 -        return adapter
  47.198 -
  47.199 -    # SSL backward compatibility
  47.200 -    if (server.ssl_adapter is None and
  47.201 -        getattr(server, 'ssl_certificate', None) and
  47.202 -        getattr(server, 'ssl_private_key', None)):
  47.203 -        server.ssl_adapter = create_ssl_adapter(server.ssl_certificate, server.ssl_private_key)
  47.204 -
  47.205 -    server.nodelay = not sys.platform.startswith('java') # TCP_NODELAY isn't supported on the JVM
  47.206 -    return server
  47.207 -
  47.208 -class StaticApp(SimpleHTTPRequestHandler):
  47.209 -    """WSGI application for serving static files."""
  47.210 -    def __init__(self, environ, start_response):
  47.211 -        self.headers = []
  47.212 -        self.environ = environ
  47.213 -        self.start_response = start_response
  47.214 -
  47.215 -    def send_response(self, status, msg=""):
  47.216 -        self.status = str(status) + " " + msg
  47.217 -
  47.218 -    def send_header(self, name, value):
  47.219 -        self.headers.append((name, value))
  47.220 -
  47.221 -    def end_headers(self):
  47.222 -        pass
  47.223 -
  47.224 -    def log_message(*a): pass
  47.225 -
  47.226 -    def __iter__(self):
  47.227 -        environ = self.environ
  47.228 -
  47.229 -        self.path = environ.get('PATH_INFO', '')
  47.230 -        self.client_address = environ.get('REMOTE_ADDR','-'), \
  47.231 -                              environ.get('REMOTE_PORT','-')
  47.232 -        self.command = environ.get('REQUEST_METHOD', '-')
  47.233 -
  47.234 -        from cStringIO import StringIO
  47.235 -        self.wfile = StringIO() # for capturing error
  47.236 -
  47.237 -        try:
  47.238 -            path = self.translate_path(self.path)
  47.239 -            etag = '"%s"' % os.path.getmtime(path)
  47.240 -            client_etag = environ.get('HTTP_IF_NONE_MATCH')
  47.241 -            self.send_header('ETag', etag)
  47.242 -            if etag == client_etag:
  47.243 -                self.send_response(304, "Not Modified")
  47.244 -                self.start_response(self.status, self.headers)
  47.245 -                raise StopIteration
  47.246 -        except OSError:
  47.247 -            pass # Probably a 404
  47.248 -
  47.249 -        f = self.send_head()
  47.250 -        self.start_response(self.status, self.headers)
  47.251 -
  47.252 -        if f:
  47.253 -            block_size = 16 * 1024
  47.254 -            while True:
  47.255 -                buf = f.read(block_size)
  47.256 -                if not buf:
  47.257 -                    break
  47.258 -                yield buf
  47.259 -            f.close()
  47.260 -        else:
  47.261 -            value = self.wfile.getvalue()
  47.262 -            yield value
  47.263 -
  47.264 -class StaticMiddleware:
  47.265 -    """WSGI middleware for serving static files."""
  47.266 -    def __init__(self, app, prefix='/static/'):
  47.267 -        self.app = app
  47.268 -        self.prefix = prefix
  47.269 -        
  47.270 -    def __call__(self, environ, start_response):
  47.271 -        path = environ.get('PATH_INFO', '')
  47.272 -        path = self.normpath(path)
  47.273 -
  47.274 -        if path.startswith(self.prefix):
  47.275 -            return StaticApp(environ, start_response)
  47.276 -        else:
  47.277 -            return self.app(environ, start_response)
  47.278 -
  47.279 -    def normpath(self, path):
  47.280 -        path2 = posixpath.normpath(urllib.unquote(path))
  47.281 -        if path.endswith("/"):
  47.282 -            path2 += "/"
  47.283 -        return path2
  47.284 -
  47.285 -    
  47.286 -class LogMiddleware:
  47.287 -    """WSGI middleware for logging the status."""
  47.288 -    def __init__(self, app):
  47.289 -        self.app = app
  47.290 -        self.format = '%s - - [%s] "%s %s %s" - %s'
  47.291 -    
  47.292 -        from BaseHTTPServer import BaseHTTPRequestHandler
  47.293 -        import StringIO
  47.294 -        f = StringIO.StringIO()
  47.295 -        
  47.296 -        class FakeSocket:
  47.297 -            def makefile(self, *a):
  47.298 -                return f
  47.299 -        
  47.300 -        # take log_date_time_string method from BaseHTTPRequestHandler
  47.301 -        self.log_date_time_string = BaseHTTPRequestHandler(FakeSocket(), None, None).log_date_time_string
  47.302 -        
  47.303 -    def __call__(self, environ, start_response):
  47.304 -        def xstart_response(status, response_headers, *args):
  47.305 -            out = start_response(status, response_headers, *args)
  47.306 -            self.log(status, environ)
  47.307 -            return out
  47.308 -
  47.309 -        return self.app(environ, xstart_response)
  47.310 -             
  47.311 -    def log(self, status, environ):
  47.312 -        outfile = environ.get('wsgi.errors', web.debug)
  47.313 -        req = environ.get('PATH_INFO', '_')
  47.314 -        protocol = environ.get('ACTUAL_SERVER_PROTOCOL', '-')
  47.315 -        method = environ.get('REQUEST_METHOD', '-')
  47.316 -        host = "%s:%s" % (environ.get('REMOTE_ADDR','-'), 
  47.317 -                          environ.get('REMOTE_PORT','-'))
  47.318 -
  47.319 -        time = self.log_date_time_string()
  47.320 -
  47.321 -        msg = self.format % (host, time, protocol, method, req, status)
  47.322 -        print >> outfile, utils.safestr(msg)
    48.1 Binary file OpenSecurity/install/web.py-0.37/web/httpserver.pyc has changed
    49.1 --- a/OpenSecurity/install/web.py-0.37/web/net.py	Thu Feb 20 15:40:48 2014 +0100
    49.2 +++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
    49.3 @@ -1,193 +0,0 @@
    49.4 -"""
    49.5 -Network Utilities
    49.6 -(from web.py)
    49.7 -"""
    49.8 -
    49.9 -__all__ = [
   49.10 -  "validipaddr", "validipport", "validip", "validaddr", 
   49.11 -  "urlquote",
   49.12 -  "httpdate", "parsehttpdate", 
   49.13 -  "htmlquote", "htmlunquote", "websafe",
   49.14 -]
   49.15 -
   49.16 -import urllib, time
   49.17 -try: import datetime
   49.18 -except ImportError: pass
   49.19 -
   49.20 -def validipaddr(address):
   49.21 -    """
   49.22 -    Returns True if `address` is a valid IPv4 address.
   49.23 -    
   49.24 -        >>> validipaddr('192.168.1.1')
   49.25 -        True
   49.26 -        >>> validipaddr('192.168.1.800')
   49.27 -        False
   49.28 -        >>> validipaddr('192.168.1')
   49.29 -        False
   49.30 -    """
   49.31 -    try:
   49.32 -        octets = address.split('.')
   49.33 -        if len(octets) != 4:
   49.34 -            return False
   49.35 -        for x in octets:
   49.36 -            if not (0 <= int(x) <= 255):
   49.37 -                return False
   49.38 -    except ValueError:
   49.39 -        return False
   49.40 -    return True
   49.41 -
   49.42 -def validipport(port):
   49.43 -    """
   49.44 -    Returns True if `port` is a valid IPv4 port.
   49.45 -    
   49.46 -        >>> validipport('9000')
   49.47 -        True
   49.48 -        >>> validipport('foo')
   49.49 -        False
   49.50 -        >>> validipport('1000000')
   49.51 -        False
   49.52 -    """
   49.53 -    try:
   49.54 -        if not (0 <= int(port) <= 65535):
   49.55 -            return False
   49.56 -    except ValueError:
   49.57 -        return False
   49.58 -    return True
   49.59 -
   49.60 -def validip(ip, defaultaddr="0.0.0.0", defaultport=8080):
   49.61 -    """Returns `(ip_address, port)` from string `ip_addr_port`"""
   49.62 -    addr = defaultaddr
   49.63 -    port = defaultport
   49.64 -    
   49.65 -    ip = ip.split(":", 1)
   49.66 -    if len(ip) == 1:
   49.67 -        if not ip[0]:
   49.68 -            pass
   49.69 -        elif validipaddr(ip[0]):
   49.70 -            addr = ip[0]
   49.71 -        elif validipport(ip[0]):
   49.72 -            port = int(ip[0])
   49.73 -        else:
   49.74 -            raise ValueError, ':'.join(ip) + ' is not a valid IP address/port'
   49.75 -    elif len(ip) == 2:
   49.76 -        addr, port = ip
   49.77 -        if not validipaddr(addr) and validipport(port):
   49.78 -            raise ValueError, ':'.join(ip) + ' is not a valid IP address/port'
   49.79 -        port = int(port)
   49.80 -    else:
   49.81 -        raise ValueError, ':'.join(ip) + ' is not a valid IP address/port'
   49.82 -    return (addr, port)
   49.83 -
   49.84 -def validaddr(string_):
   49.85 -    """
   49.86 -    Returns either (ip_address, port) or "/path/to/socket" from string_
   49.87 -    
   49.88 -        >>> validaddr('/path/to/socket')
   49.89 -        '/path/to/socket'
   49.90 -        >>> validaddr('8000')
   49.91 -        ('0.0.0.0', 8000)
   49.92 -        >>> validaddr('127.0.0.1')
   49.93 -        ('127.0.0.1', 8080)
   49.94 -        >>> validaddr('127.0.0.1:8000')
   49.95 -        ('127.0.0.1', 8000)
   49.96 -        >>> validaddr('fff')
   49.97 -        Traceback (most recent call last):
   49.98 -            ...
   49.99 -        ValueError: fff is not a valid IP address/port
  49.100 -    """
  49.101 -    if '/' in string_:
  49.102 -        return string_
  49.103 -    else:
  49.104 -        return validip(string_)
  49.105 -
  49.106 -def urlquote(val):
  49.107 -    """
  49.108 -    Quotes a string for use in a URL.
  49.109 -    
  49.110 -        >>> urlquote('://?f=1&j=1')
  49.111 -        '%3A//%3Ff%3D1%26j%3D1'
  49.112 -        >>> urlquote(None)
  49.113 -        ''
  49.114 -        >>> urlquote(u'\u203d')
  49.115 -        '%E2%80%BD'
  49.116 -    """
  49.117 -    if val is None: return ''
  49.118 -    if not isinstance(val, unicode): val = str(val)
  49.119 -    else: val = val.encode('utf-8')
  49.120 -    return urllib.quote(val)
  49.121 -
  49.122 -def httpdate(date_obj):
  49.123 -    """
  49.124 -    Formats a datetime object for use in HTTP headers.
  49.125 -    
  49.126 -        >>> import datetime
  49.127 -        >>> httpdate(datetime.datetime(1970, 1, 1, 1, 1, 1))
  49.128 -        'Thu, 01 Jan 1970 01:01:01 GMT'
  49.129 -    """
  49.130 -    return date_obj.strftime("%a, %d %b %Y %H:%M:%S GMT")
  49.131 -
  49.132 -def parsehttpdate(string_):
  49.133 -    """
  49.134 -    Parses an HTTP date into a datetime object.
  49.135 -
  49.136 -        >>> parsehttpdate('Thu, 01 Jan 1970 01:01:01 GMT')
  49.137 -        datetime.datetime(1970, 1, 1, 1, 1, 1)
  49.138 -    """
  49.139 -    try:
  49.140 -        t = time.strptime(string_, "%a, %d %b %Y %H:%M:%S %Z")
  49.141 -    except ValueError:
  49.142 -        return None
  49.143 -    return datetime.datetime(*t[:6])
  49.144 -
  49.145 -def htmlquote(text):
  49.146 -    r"""
  49.147 -    Encodes `text` for raw use in HTML.
  49.148 -    
  49.149 -        >>> htmlquote(u"<'&\">")
  49.150 -        u'&lt;&#39;&amp;&quot;&gt;'
  49.151 -    """
  49.152 -    text = text.replace(u"&", u"&amp;") # Must be done first!
  49.153 -    text = text.replace(u"<", u"&lt;")
  49.154 -    text = text.replace(u">", u"&gt;")
  49.155 -    text = text.replace(u"'", u"&#39;")
  49.156 -    text = text.replace(u'"', u"&quot;")
  49.157 -    return text
  49.158 -
  49.159 -def htmlunquote(text):
  49.160 -    r"""
  49.161 -    Decodes `text` that's HTML quoted.
  49.162 -
  49.163 -        >>> htmlunquote(u'&lt;&#39;&amp;&quot;&gt;')
  49.164 -        u'<\'&">'
  49.165 -    """
  49.166 -    text = text.replace(u"&quot;", u'"')
  49.167 -    text = text.replace(u"&#39;", u"'")
  49.168 -    text = text.replace(u"&gt;", u">")
  49.169 -    text = text.replace(u"&lt;", u"<")
  49.170 -    text = text.replace(u"&amp;", u"&") # Must be done last!
  49.171 -    return text
  49.172 -    
  49.173 -def websafe(val):
  49.174 -    r"""Converts `val` so that it is safe for use in Unicode HTML.
  49.175 -
  49.176 -        >>> websafe("<'&\">")
  49.177 -        u'&lt;&#39;&amp;&quot;&gt;'
  49.178 -        >>> websafe(None)
  49.179 -        u''
  49.180 -        >>> websafe(u'\u203d')
  49.181 -        u'\u203d'
  49.182 -        >>> websafe('\xe2\x80\xbd')
  49.183 -        u'\u203d'
  49.184 -    """
  49.185 -    if val is None:
  49.186 -        return u''
  49.187 -    elif isinstance(val, str):
  49.188 -        val = val.decode('utf-8')
  49.189 -    elif not isinstance(val, unicode):
  49.190 -        val = unicode(val)
  49.191 -        
  49.192 -    return htmlquote(val)
  49.193 -
  49.194 -if __name__ == "__main__":
  49.195 -    import doctest
  49.196 -    doctest.testmod()
    50.1 Binary file OpenSecurity/install/web.py-0.37/web/net.pyc has changed
    51.1 --- a/OpenSecurity/install/web.py-0.37/web/python23.py	Thu Feb 20 15:40:48 2014 +0100
    51.2 +++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
    51.3 @@ -1,46 +0,0 @@
    51.4 -"""Python 2.3 compatabilty"""
    51.5 -import threading
    51.6 -
    51.7 -class threadlocal(object):
    51.8 -    """Implementation of threading.local for python2.3.
    51.9 -    """
   51.10 -    def __getattribute__(self, name):
   51.11 -        if name == "__dict__":
   51.12 -            return threadlocal._getd(self)
   51.13 -        else:
   51.14 -            try:
   51.15 -                return object.__getattribute__(self, name)
   51.16 -            except AttributeError:
   51.17 -                try:
   51.18 -                    return self.__dict__[name]
   51.19 -                except KeyError:
   51.20 -                    raise AttributeError, name
   51.21 -            
   51.22 -    def __setattr__(self, name, value):
   51.23 -        self.__dict__[name] = value
   51.24 -        
   51.25 -    def __delattr__(self, name):
   51.26 -        try:
   51.27 -            del self.__dict__[name]
   51.28 -        except KeyError:
   51.29 -            raise AttributeError, name
   51.30 -    
   51.31 -    def _getd(self):
   51.32 -        t = threading.currentThread()
   51.33 -        if not hasattr(t, '_d'):
   51.34 -            # using __dict__ of thread as thread local storage
   51.35 -            t._d = {}
   51.36 -        
   51.37 -        _id = id(self)
   51.38 -        # there could be multiple instances of threadlocal.
   51.39 -        # use id(self) as key
   51.40 -        if _id not in t._d:
   51.41 -            t._d[_id] = {}
   51.42 -        return t._d[_id]
   51.43 -        
   51.44 -if __name__ == '__main__':
   51.45 -     d = threadlocal()
   51.46 -     d.x = 1
   51.47 -     print d.__dict__
   51.48 -     print d.x
   51.49 -     
   51.50 \ No newline at end of file
    52.1 --- a/OpenSecurity/install/web.py-0.37/web/session.py	Thu Feb 20 15:40:48 2014 +0100
    52.2 +++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
    52.3 @@ -1,358 +0,0 @@
    52.4 -"""
    52.5 -Session Management
    52.6 -(from web.py)
    52.7 -"""
    52.8 -
    52.9 -import os, time, datetime, random, base64
   52.10 -import os.path
   52.11 -from copy import deepcopy
   52.12 -try:
   52.13 -    import cPickle as pickle
   52.14 -except ImportError:
   52.15 -    import pickle
   52.16 -try:
   52.17 -    import hashlib
   52.18 -    sha1 = hashlib.sha1
   52.19 -except ImportError:
   52.20 -    import sha
   52.21 -    sha1 = sha.new
   52.22 -
   52.23 -import utils
   52.24 -import webapi as web
   52.25 -
   52.26 -__all__ = [
   52.27 -    'Session', 'SessionExpired',
   52.28 -    'Store', 'DiskStore', 'DBStore',
   52.29 -]
   52.30 -
   52.31 -web.config.session_parameters = utils.storage({
   52.32 -    'cookie_name': 'webpy_session_id',
   52.33 -    'cookie_domain': None,
   52.34 -    'cookie_path' : None,
   52.35 -    'timeout': 86400, #24 * 60 * 60, # 24 hours in seconds
   52.36 -    'ignore_expiry': True,
   52.37 -    'ignore_change_ip': True,
   52.38 -    'secret_key': 'fLjUfxqXtfNoIldA0A0J',
   52.39 -    'expired_message': 'Session expired',
   52.40 -    'httponly': True,
   52.41 -    'secure': False
   52.42 -})
   52.43 -
   52.44 -class SessionExpired(web.HTTPError): 
   52.45 -    def __init__(self, message):
   52.46 -        web.HTTPError.__init__(self, '200 OK', {}, data=message)
   52.47 -
   52.48 -class Session(object):
   52.49 -    """Session management for web.py
   52.50 -    """
   52.51 -    __slots__ = [
   52.52 -        "store", "_initializer", "_last_cleanup_time", "_config", "_data", 
   52.53 -        "__getitem__", "__setitem__", "__delitem__"
   52.54 -    ]
   52.55 -
   52.56 -    def __init__(self, app, store, initializer=None):
   52.57 -        self.store = store
   52.58 -        self._initializer = initializer
   52.59 -        self._last_cleanup_time = 0
   52.60 -        self._config = utils.storage(web.config.session_parameters)
   52.61 -        self._data = utils.threadeddict()
   52.62 -        
   52.63 -        self.__getitem__ = self._data.__getitem__
   52.64 -        self.__setitem__ = self._data.__setitem__
   52.65 -        self.__delitem__ = self._data.__delitem__
   52.66 -
   52.67 -        if app:
   52.68 -            app.add_processor(self._processor)
   52.69 -
   52.70 -    def __contains__(self, name):
   52.71 -        return name in self._data
   52.72 -
   52.73 -    def __getattr__(self, name):
   52.74 -        return getattr(self._data, name)
   52.75 -    
   52.76 -    def __setattr__(self, name, value):
   52.77 -        if name in self.__slots__:
   52.78 -            object.__setattr__(self, name, value)
   52.79 -        else:
   52.80 -            setattr(self._data, name, value)
   52.81 -        
   52.82 -    def __delattr__(self, name):
   52.83 -        delattr(self._data, name)
   52.84 -
   52.85 -    def _processor(self, handler):
   52.86 -        """Application processor to setup session for every request"""
   52.87 -        self._cleanup()
   52.88 -        self._load()
   52.89 -
   52.90 -        try:
   52.91 -            return handler()
   52.92 -        finally:
   52.93 -            self._save()
   52.94 -
   52.95 -    def _load(self):
   52.96 -        """Load the session from the store, by the id from cookie"""
   52.97 -        cookie_name = self._config.cookie_name
   52.98 -        cookie_domain = self._config.cookie_domain
   52.99 -        cookie_path = self._config.cookie_path
  52.100 -        httponly = self._config.httponly
  52.101 -        self.session_id = web.cookies().get(cookie_name)
  52.102 -
  52.103 -        # protection against session_id tampering
  52.104 -        if self.session_id and not self._valid_session_id(self.session_id):
  52.105 -            self.session_id = None
  52.106 -
  52.107 -        self._check_expiry()
  52.108 -        if self.session_id:
  52.109 -            d = self.store[self.session_id]
  52.110 -            self.update(d)
  52.111 -            self._validate_ip()
  52.112 -        
  52.113 -        if not self.session_id:
  52.114 -            self.session_id = self._generate_session_id()
  52.115 -
  52.116 -            if self._initializer:
  52.117 -                if isinstance(self._initializer, dict):
  52.118 -                    self.update(deepcopy(self._initializer))
  52.119 -                elif hasattr(self._initializer, '__call__'):
  52.120 -                    self._initializer()
  52.121 - 
  52.122 -        self.ip = web.ctx.ip
  52.123 -
  52.124 -    def _check_expiry(self):
  52.125 -        # check for expiry
  52.126 -        if self.session_id and self.session_id not in self.store:
  52.127 -            if self._config.ignore_expiry:
  52.128 -                self.session_id = None
  52.129 -            else:
  52.130 -                return self.expired()
  52.131 -
  52.132 -    def _validate_ip(self):
  52.133 -        # check for change of IP
  52.134 -        if self.session_id and self.get('ip', None) != web.ctx.ip:
  52.135 -            if not self._config.ignore_change_ip:
  52.136 -               return self.expired() 
  52.137 -    
  52.138 -    def _save(self):
  52.139 -        if not self.get('_killed'):
  52.140 -            self._setcookie(self.session_id)
  52.141 -            self.store[self.session_id] = dict(self._data)
  52.142 -        else:
  52.143 -            self._setcookie(self.session_id, expires=-1)
  52.144 -            
  52.145 -    def _setcookie(self, session_id, expires='', **kw):
  52.146 -        cookie_name = self._config.cookie_name
  52.147 -        cookie_domain = self._config.cookie_domain
  52.148 -        cookie_path = self._config.cookie_path
  52.149 -        httponly = self._config.httponly
  52.150 -        secure = self._config.secure
  52.151 -        web.setcookie(cookie_name, session_id, expires=expires, domain=cookie_domain, httponly=httponly, secure=secure, path=cookie_path)
  52.152 -    
  52.153 -    def _generate_session_id(self):
  52.154 -        """Generate a random id for session"""
  52.155 -
  52.156 -        while True:
  52.157 -            rand = os.urandom(16)
  52.158 -            now = time.time()
  52.159 -            secret_key = self._config.secret_key
  52.160 -            session_id = sha1("%s%s%s%s" %(rand, now, utils.safestr(web.ctx.ip), secret_key))
  52.161 -            session_id = session_id.hexdigest()
  52.162 -            if session_id not in self.store:
  52.163 -                break
  52.164 -        return session_id
  52.165 -
  52.166 -    def _valid_session_id(self, session_id):
  52.167 -        rx = utils.re_compile('^[0-9a-fA-F]+$')
  52.168 -        return rx.match(session_id)
  52.169 -        
  52.170 -    def _cleanup(self):
  52.171 -        """Cleanup the stored sessions"""
  52.172 -        current_time = time.time()
  52.173 -        timeout = self._config.timeout
  52.174 -        if current_time - self._last_cleanup_time > timeout:
  52.175 -            self.store.cleanup(timeout)
  52.176 -            self._last_cleanup_time = current_time
  52.177 -
  52.178 -    def expired(self):
  52.179 -        """Called when an expired session is atime"""
  52.180 -        self._killed = True
  52.181 -        self._save()
  52.182 -        raise SessionExpired(self._config.expired_message)
  52.183 - 
  52.184 -    def kill(self):
  52.185 -        """Kill the session, make it no longer available"""
  52.186 -        del self.store[self.session_id]
  52.187 -        self._killed = True
  52.188 -
  52.189 -class Store:
  52.190 -    """Base class for session stores"""
  52.191 -
  52.192 -    def __contains__(self, key):
  52.193 -        raise NotImplementedError
  52.194 -
  52.195 -    def __getitem__(self, key):
  52.196 -        raise NotImplementedError
  52.197 -
  52.198 -    def __setitem__(self, key, value):
  52.199 -        raise NotImplementedError
  52.200 -
  52.201 -    def cleanup(self, timeout):
  52.202 -        """removes all the expired sessions"""
  52.203 -        raise NotImplementedError
  52.204 -
  52.205 -    def encode(self, session_dict):
  52.206 -        """encodes session dict as a string"""
  52.207 -        pickled = pickle.dumps(session_dict)
  52.208 -        return base64.encodestring(pickled)
  52.209 -
  52.210 -    def decode(self, session_data):
  52.211 -        """decodes the data to get back the session dict """
  52.212 -        pickled = base64.decodestring(session_data)
  52.213 -        return pickle.loads(pickled)
  52.214 -
  52.215 -class DiskStore(Store):
  52.216 -    """
  52.217 -    Store for saving a session on disk.
  52.218 -
  52.219 -        >>> import tempfile
  52.220 -        >>> root = tempfile.mkdtemp()
  52.221 -        >>> s = DiskStore(root)
  52.222 -        >>> s['a'] = 'foo'
  52.223 -        >>> s['a']
  52.224 -        'foo'
  52.225 -        >>> time.sleep(0.01)
  52.226 -        >>> s.cleanup(0.01)
  52.227 -        >>> s['a']
  52.228 -        Traceback (most recent call last):
  52.229 -            ...
  52.230 -        KeyError: 'a'
  52.231 -    """
  52.232 -    def __init__(self, root):
  52.233 -        # if the storage root doesn't exists, create it.
  52.234 -        if not os.path.exists(root):
  52.235 -            os.makedirs(
  52.236 -                    os.path.abspath(root)
  52.237 -                    )
  52.238 -        self.root = root
  52.239 -
  52.240 -    def _get_path(self, key):
  52.241 -        if os.path.sep in key: 
  52.242 -            raise ValueError, "Bad key: %s" % repr(key)
  52.243 -        return os.path.join(self.root, key)
  52.244 -    
  52.245 -    def __contains__(self, key):
  52.246 -        path = self._get_path(key)
  52.247 -        return os.path.exists(path)
  52.248 -
  52.249 -    def __getitem__(self, key):
  52.250 -        path = self._get_path(key)
  52.251 -        if os.path.exists(path): 
  52.252 -            pickled = open(path).read()
  52.253 -            return self.decode(pickled)
  52.254 -        else:
  52.255 -            raise KeyError, key
  52.256 -
  52.257 -    def __setitem__(self, key, value):
  52.258 -        path = self._get_path(key)
  52.259 -        pickled = self.encode(value)    
  52.260 -        try:
  52.261 -            f = open(path, 'w')
  52.262 -            try:
  52.263 -                f.write(pickled)
  52.264 -            finally: 
  52.265 -                f.close()
  52.266 -        except IOError:
  52.267 -            pass
  52.268 -
  52.269 -    def __delitem__(self, key):
  52.270 -        path = self._get_path(key)
  52.271 -        if os.path.exists(path):
  52.272 -            os.remove(path)
  52.273 -    
  52.274 -    def cleanup(self, timeout):
  52.275 -        now = time.time()
  52.276 -        for f in os.listdir(self.root):
  52.277 -            path = self._get_path(f)
  52.278 -            atime = os.stat(path).st_atime
  52.279 -            if now - atime > timeout :
  52.280 -                os.remove(path)
  52.281 -
  52.282 -class DBStore(Store):
  52.283 -    """Store for saving a session in database
  52.284 -    Needs a table with the following columns:
  52.285 -
  52.286 -        session_id CHAR(128) UNIQUE NOT NULL,
  52.287 -        atime DATETIME NOT NULL default current_timestamp,
  52.288 -        data TEXT
  52.289 -    """
  52.290 -    def __init__(self, db, table_name):
  52.291 -        self.db = db
  52.292 -        self.table = table_name
  52.293 -    
  52.294 -    def __contains__(self, key):
  52.295 -        data = self.db.select(self.table, where="session_id=$key", vars=locals())
  52.296 -        return bool(list(data)) 
  52.297 -
  52.298 -    def __getitem__(self, key):
  52.299 -        now = datetime.datetime.now()
  52.300 -        try:
  52.301 -            s = self.db.select(self.table, where="session_id=$key", vars=locals())[0]
  52.302 -            self.db.update(self.table, where="session_id=$key", atime=now, vars=locals())
  52.303 -        except IndexError:
  52.304 -            raise KeyError
  52.305 -        else:
  52.306 -            return self.decode(s.data)
  52.307 -
  52.308 -    def __setitem__(self, key, value):
  52.309 -        pickled = self.encode(value)
  52.310 -        now = datetime.datetime.now()
  52.311 -        if key in self:
  52.312 -            self.db.update(self.table, where="session_id=$key", data=pickled, vars=locals())
  52.313 -        else:
  52.314 -            self.db.insert(self.table, False, session_id=key, data=pickled )
  52.315 -                
  52.316 -    def __delitem__(self, key):
  52.317 -        self.db.delete(self.table, where="session_id=$key", vars=locals())
  52.318 -
  52.319 -    def cleanup(self, timeout):
  52.320 -        timeout = datetime.timedelta(timeout/(24.0*60*60)) #timedelta takes numdays as arg
  52.321 -        last_allowed_time = datetime.datetime.now() - timeout
  52.322 -        self.db.delete(self.table, where="$last_allowed_time > atime", vars=locals())
  52.323 -
  52.324 -class ShelfStore:
  52.325 -    """Store for saving session using `shelve` module.
  52.326 -
  52.327 -        import shelve
  52.328 -        store = ShelfStore(shelve.open('session.shelf'))
  52.329 -
  52.330 -    XXX: is shelve thread-safe?
  52.331 -    """
  52.332 -    def __init__(self, shelf):
  52.333 -        self.shelf = shelf
  52.334 -
  52.335 -    def __contains__(self, key):
  52.336 -        return key in self.shelf
  52.337 -
  52.338 -    def __getitem__(self, key):
  52.339 -        atime, v = self.shelf[key]
  52.340 -        self[key] = v # update atime
  52.341 -        return v
  52.342 -
  52.343 -    def __setitem__(self, key, value):
  52.344 -        self.shelf[key] = time.time(), value
  52.345 -        
  52.346 -    def __delitem__(self, key):
  52.347 -        try:
  52.348 -            del self.shelf[key]
  52.349 -        except KeyError:
  52.350 -            pass
  52.351 -
  52.352 -    def cleanup(self, timeout):
  52.353 -        now = time.time()
  52.354 -        for k in self.shelf.keys():
  52.355 -            atime, v = self.shelf[k]
  52.356 -            if now - atime > timeout :
  52.357 -                del self[k]
  52.358 -
  52.359 -if __name__ == '__main__' :
  52.360 -    import doctest
  52.361 -    doctest.testmod()
    53.1 Binary file OpenSecurity/install/web.py-0.37/web/session.pyc has changed
    54.1 --- a/OpenSecurity/install/web.py-0.37/web/template.py	Thu Feb 20 15:40:48 2014 +0100
    54.2 +++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
    54.3 @@ -1,1515 +0,0 @@
    54.4 -"""
    54.5 -simple, elegant templating
    54.6 -(part of web.py)
    54.7 -
    54.8 -Template design:
    54.9 -
   54.10 -Template string is split into tokens and the tokens are combined into nodes. 
   54.11 -Parse tree is a nodelist. TextNode and ExpressionNode are simple nodes and 
   54.12 -for-loop, if-loop etc are block nodes, which contain multiple child nodes. 
   54.13 -
   54.14 -Each node can emit some python string. python string emitted by the 
   54.15 -root node is validated for safeeval and executed using python in the given environment.
   54.16 -
   54.17 -Enough care is taken to make sure the generated code and the template has line to line match, 
   54.18 -so that the error messages can point to exact line number in template. (It doesn't work in some cases still.)
   54.19 -
   54.20 -Grammar:
   54.21 -
   54.22 -    template -> defwith sections 
   54.23 -    defwith -> '$def with (' arguments ')' | ''
   54.24 -    sections -> section*
   54.25 -    section -> block | assignment | line
   54.26 -
   54.27 -    assignment -> '$ ' <assignment expression>
   54.28 -    line -> (text|expr)*
   54.29 -    text -> <any characters other than $>
   54.30 -    expr -> '$' pyexpr | '$(' pyexpr ')' | '${' pyexpr '}'
   54.31 -    pyexpr -> <python expression>
   54.32 -"""
   54.33 -
   54.34 -__all__ = [
   54.35 -    "Template",
   54.36 -    "Render", "render", "frender",
   54.37 -    "ParseError", "SecurityError",
   54.38 -    "test"
   54.39 -]
   54.40 -
   54.41 -import tokenize
   54.42 -import os
   54.43 -import sys
   54.44 -import glob
   54.45 -import re
   54.46 -from UserDict import DictMixin
   54.47 -import warnings
   54.48 -
   54.49 -from utils import storage, safeunicode, safestr, re_compile
   54.50 -from webapi import config
   54.51 -from net import websafe
   54.52 -
   54.53 -def splitline(text):
   54.54 -    r"""
   54.55 -    Splits the given text at newline.
   54.56 -    
   54.57 -        >>> splitline('foo\nbar')
   54.58 -        ('foo\n', 'bar')
   54.59 -        >>> splitline('foo')
   54.60 -        ('foo', '')
   54.61 -        >>> splitline('')
   54.62 -        ('', '')
   54.63 -    """
   54.64 -    index = text.find('\n') + 1
   54.65 -    if index:
   54.66 -        return text[:index], text[index:]
   54.67 -    else:
   54.68 -        return text, ''
   54.69 -
   54.70 -class Parser:
   54.71 -    """Parser Base.
   54.72 -    """
   54.73 -    def __init__(self):
   54.74 -        self.statement_nodes = STATEMENT_NODES
   54.75 -        self.keywords = KEYWORDS
   54.76 -
   54.77 -    def parse(self, text, name="<template>"):
   54.78 -        self.text = text
   54.79 -        self.name = name
   54.80 -        
   54.81 -        defwith, text = self.read_defwith(text)
   54.82 -        suite = self.read_suite(text)
   54.83 -        return DefwithNode(defwith, suite)
   54.84 -
   54.85 -    def read_defwith(self, text):
   54.86 -        if text.startswith('$def with'):
   54.87 -            defwith, text = splitline(text)
   54.88 -            defwith = defwith[1:].strip() # strip $ and spaces
   54.89 -            return defwith, text
   54.90 -        else:
   54.91 -            return '', text
   54.92 -    
   54.93 -    def read_section(self, text):
   54.94 -        r"""Reads one section from the given text.
   54.95 -        
   54.96 -        section -> block | assignment | line
   54.97 -        
   54.98 -            >>> read_section = Parser().read_section
   54.99 -            >>> read_section('foo\nbar\n')
  54.100 -            (<line: [t'foo\n']>, 'bar\n')
  54.101 -            >>> read_section('$ a = b + 1\nfoo\n')
  54.102 -            (<assignment: 'a = b + 1'>, 'foo\n')
  54.103 -            
  54.104 -        read_section('$for in range(10):\n    hello $i\nfoo)
  54.105 -        """
  54.106 -        if text.lstrip(' ').startswith('$'):
  54.107 -            index = text.index('$')
  54.108 -            begin_indent, text2 = text[:index], text[index+1:]
  54.109 -            ahead = self.python_lookahead(text2)
  54.110 -            
  54.111 -            if ahead == 'var':
  54.112 -                return self.read_var(text2)
  54.113 -            elif ahead in self.statement_nodes:
  54.114 -                return self.read_block_section(text2, begin_indent)
  54.115 -            elif ahead in self.keywords:
  54.116 -                return self.read_keyword(text2)
  54.117 -            elif ahead.strip() == '':
  54.118 -                # assignments starts with a space after $
  54.119 -                # ex: $ a = b + 2
  54.120 -                return self.read_assignment(text2)
  54.121 -        return self.readline(text)
  54.122 -        
  54.123 -    def read_var(self, text):
  54.124 -        r"""Reads a var statement.
  54.125 -        
  54.126 -            >>> read_var = Parser().read_var
  54.127 -            >>> read_var('var x=10\nfoo')
  54.128 -            (<var: x = 10>, 'foo')
  54.129 -            >>> read_var('var x: hello $name\nfoo')
  54.130 -            (<var: x = join_(u'hello ', escape_(name, True))>, 'foo')
  54.131 -        """
  54.132 -        line, text = splitline(text)
  54.133 -        tokens = self.python_tokens(line)
  54.134 -        if len(tokens) < 4:
  54.135 -            raise SyntaxError('Invalid var statement')
  54.136 -            
  54.137 -        name = tokens[1]
  54.138 -        sep = tokens[2]
  54.139 -        value = line.split(sep, 1)[1].strip()
  54.140 -        
  54.141 -        if sep == '=':
  54.142 -            pass # no need to process value
  54.143 -        elif sep == ':': 
  54.144 -            #@@ Hack for backward-compatability
  54.145 -            if tokens[3] == '\n': # multi-line var statement
  54.146 -                block, text = self.read_indented_block(text, '    ')
  54.147 -                lines = [self.readline(x)[0] for x in block.splitlines()]
  54.148 -                nodes = []
  54.149 -                for x in lines:
  54.150 -                    nodes.extend(x.nodes)
  54.151 -                    nodes.append(TextNode('\n'))         
  54.152 -            else: # single-line var statement
  54.153 -                linenode, _ = self.readline(value)
  54.154 -                nodes = linenode.nodes                
  54.155 -            parts = [node.emit('') for node in nodes]
  54.156 -            value = "join_(%s)" % ", ".join(parts)
  54.157 -        else:
  54.158 -            raise SyntaxError('Invalid var statement')
  54.159 -        return VarNode(name, value), text
  54.160 -                    
  54.161 -    def read_suite(self, text):
  54.162 -        r"""Reads section by section till end of text.
  54.163 -        
  54.164 -            >>> read_suite = Parser().read_suite
  54.165 -            >>> read_suite('hello $name\nfoo\n')
  54.166 -            [<line: [t'hello ', $name, t'\n']>, <line: [t'foo\n']>]
  54.167 -        """
  54.168 -        sections = []
  54.169 -        while text:
  54.170 -            section, text = self.read_section(text)
  54.171 -            sections.append(section)
  54.172 -        return SuiteNode(sections)
  54.173 -    
  54.174 -    def readline(self, text):
  54.175 -        r"""Reads one line from the text. Newline is supressed if the line ends with \.
  54.176 -        
  54.177 -            >>> readline = Parser().readline
  54.178 -            >>> readline('hello $name!\nbye!')
  54.179 -            (<line: [t'hello ', $name, t'!\n']>, 'bye!')
  54.180 -            >>> readline('hello $name!\\\nbye!')
  54.181 -            (<line: [t'hello ', $name, t'!']>, 'bye!')
  54.182 -            >>> readline('$f()\n\n')
  54.183 -            (<line: [$f(), t'\n']>, '\n')
  54.184 -        """
  54.185 -        line, text = splitline(text)
  54.186 -
  54.187 -        # supress new line if line ends with \
  54.188 -        if line.endswith('\\\n'):
  54.189 -            line = line[:-2]
  54.190 -                
  54.191 -        nodes = []
  54.192 -        while line:
  54.193 -            node, line = self.read_node(line)
  54.194 -            nodes.append(node)
  54.195 -            
  54.196 -        return LineNode(nodes), text
  54.197 -
  54.198 -    def read_node(self, text):
  54.199 -        r"""Reads a node from the given text and returns the node and remaining text.
  54.200 -
  54.201 -            >>> read_node = Parser().read_node
  54.202 -            >>> read_node('hello $name')
  54.203 -            (t'hello ', '$name')
  54.204 -            >>> read_node('$name')
  54.205 -            ($name, '')
  54.206 -        """
  54.207 -        if text.startswith('$$'):
  54.208 -            return TextNode('$'), text[2:]
  54.209 -        elif text.startswith('$#'): # comment
  54.210 -            line, text = splitline(text)
  54.211 -            return TextNode('\n'), text
  54.212 -        elif text.startswith('$'):
  54.213 -            text = text[1:] # strip $
  54.214 -            if text.startswith(':'):
  54.215 -                escape = False
  54.216 -                text = text[1:] # strip :
  54.217 -            else:
  54.218 -                escape = True
  54.219 -            return self.read_expr(text, escape=escape)
  54.220 -        else:
  54.221 -            return self.read_text(text)
  54.222 -    
  54.223 -    def read_text(self, text):
  54.224 -        r"""Reads a text node from the given text.
  54.225 -        
  54.226 -            >>> read_text = Parser().read_text
  54.227 -            >>> read_text('hello $name')
  54.228 -            (t'hello ', '$name')
  54.229 -        """
  54.230 -        index = text.find('$')
  54.231 -        if index < 0:
  54.232 -            return TextNode(text), ''
  54.233 -        else:
  54.234 -            return TextNode(text[:index]), text[index:]
  54.235 -            
  54.236 -    def read_keyword(self, text):
  54.237 -        line, text = splitline(text)
  54.238 -        return StatementNode(line.strip() + "\n"), text
  54.239 -
  54.240 -    def read_expr(self, text, escape=True):
  54.241 -        """Reads a python expression from the text and returns the expression and remaining text.
  54.242 -
  54.243 -        expr -> simple_expr | paren_expr
  54.244 -        simple_expr -> id extended_expr
  54.245 -        extended_expr -> attr_access | paren_expr extended_expr | ''
  54.246 -        attr_access -> dot id extended_expr
  54.247 -        paren_expr -> [ tokens ] | ( tokens ) | { tokens }
  54.248 -     
  54.249 -            >>> read_expr = Parser().read_expr
  54.250 -            >>> read_expr("name")
  54.251 -            ($name, '')
  54.252 -            >>> read_expr("a.b and c")
  54.253 -            ($a.b, ' and c')
  54.254 -            >>> read_expr("a. b")
  54.255 -            ($a, '. b')
  54.256 -            >>> read_expr("name</h1>")
  54.257 -            ($name, '</h1>')
  54.258 -            >>> read_expr("(limit)ing")
  54.259 -            ($(limit), 'ing')
  54.260 -            >>> read_expr('a[1, 2][:3].f(1+2, "weird string[).", 3 + 4) done.')
  54.261 -            ($a[1, 2][:3].f(1+2, "weird string[).", 3 + 4), ' done.')
  54.262 -        """
  54.263 -        def simple_expr():
  54.264 -            identifier()
  54.265 -            extended_expr()
  54.266 -        
  54.267 -        def identifier():
  54.268 -            tokens.next()
  54.269 -        
  54.270 -        def extended_expr():
  54.271 -            lookahead = tokens.lookahead()
  54.272 -            if lookahead is None:
  54.273 -                return
  54.274 -            elif lookahead.value == '.':
  54.275 -                attr_access()
  54.276 -            elif lookahead.value in parens:
  54.277 -                paren_expr()
  54.278 -                extended_expr()
  54.279 -            else:
  54.280 -                return
  54.281 -        
  54.282 -        def attr_access():
  54.283 -            from token import NAME # python token constants
  54.284 -            dot = tokens.lookahead()
  54.285 -            if tokens.lookahead2().type == NAME:
  54.286 -                tokens.next() # consume dot
  54.287 -                identifier()
  54.288 -                extended_expr()
  54.289 -        
  54.290 -        def paren_expr():
  54.291 -            begin = tokens.next().value
  54.292 -            end = parens[begin]
  54.293 -            while True:
  54.294 -                if tokens.lookahead().value in parens:
  54.295 -                    paren_expr()
  54.296 -                else:
  54.297 -                    t = tokens.next()
  54.298 -                    if t.value == end:
  54.299 -                        break
  54.300 -            return
  54.301 -
  54.302 -        parens = {
  54.303 -            "(": ")",
  54.304 -            "[": "]",
  54.305 -            "{": "}"
  54.306 -        }
  54.307 -        
  54.308 -        def get_tokens(text):
  54.309 -            """tokenize text using python tokenizer.
  54.310 -            Python tokenizer ignores spaces, but they might be important in some cases. 
  54.311 -            This function introduces dummy space tokens when it identifies any ignored space.
  54.312 -            Each token is a storage object containing type, value, begin and end.
  54.313 -            """
  54.314 -            readline = iter([text]).next
  54.315 -            end = None
  54.316 -            for t in tokenize.generate_tokens(readline):
  54.317 -                t = storage(type=t[0], value=t[1], begin=t[2], end=t[3])
  54.318 -                if end is not None and end != t.begin:
  54.319 -                    _, x1 = end
  54.320 -                    _, x2 = t.begin
  54.321 -                    yield storage(type=-1, value=text[x1:x2], begin=end, end=t.begin)
  54.322 -                end = t.end
  54.323 -                yield t
  54.324 -                
  54.325 -        class BetterIter:
  54.326 -            """Iterator like object with 2 support for 2 look aheads."""
  54.327 -            def __init__(self, items):
  54.328 -                self.iteritems = iter(items)
  54.329 -                self.items = []
  54.330 -                self.position = 0
  54.331 -                self.current_item = None
  54.332 -            
  54.333 -            def lookahead(self):
  54.334 -                if len(self.items) <= self.position:
  54.335 -                    self.items.append(self._next())
  54.336 -                return self.items[self.position]
  54.337 -
  54.338 -            def _next(self):
  54.339 -                try:
  54.340 -                    return self.iteritems.next()
  54.341 -                except StopIteration:
  54.342 -                    return None
  54.343 -                
  54.344 -            def lookahead2(self):
  54.345 -                if len(self.items) <= self.position+1:
  54.346 -                    self.items.append(self._next())
  54.347 -                return self.items[self.position+1]
  54.348 -                    
  54.349 -            def next(self):
  54.350 -                self.current_item = self.lookahead()
  54.351 -                self.position += 1
  54.352 -                return self.current_item
  54.353 -
  54.354 -        tokens = BetterIter(get_tokens(text))
  54.355 -                
  54.356 -        if tokens.lookahead().value in parens:
  54.357 -            paren_expr()
  54.358 -        else:
  54.359 -            simple_expr()
  54.360 -        row, col = tokens.current_item.end
  54.361 -        return ExpressionNode(text[:col], escape=escape), text[col:]    
  54.362 -
  54.363 -    def read_assignment(self, text):
  54.364 -        r"""Reads assignment statement from text.
  54.365 -    
  54.366 -            >>> read_assignment = Parser().read_assignment
  54.367 -            >>> read_assignment('a = b + 1\nfoo')
  54.368 -            (<assignment: 'a = b + 1'>, 'foo')
  54.369 -        """
  54.370 -        line, text = splitline(text)
  54.371 -        return AssignmentNode(line.strip()), text
  54.372 -    
  54.373 -    def python_lookahead(self, text):
  54.374 -        """Returns the first python token from the given text.
  54.375 -        
  54.376 -            >>> python_lookahead = Parser().python_lookahead
  54.377 -            >>> python_lookahead('for i in range(10):')
  54.378 -            'for'
  54.379 -            >>> python_lookahead('else:')
  54.380 -            'else'
  54.381 -            >>> python_lookahead(' x = 1')
  54.382 -            ' '
  54.383 -        """
  54.384 -        readline = iter([text]).next
  54.385 -        tokens = tokenize.generate_tokens(readline)
  54.386 -        return tokens.next()[1]
  54.387 -        
  54.388 -    def python_tokens(self, text):
  54.389 -        readline = iter([text]).next
  54.390 -        tokens = tokenize.generate_tokens(readline)
  54.391 -        return [t[1] for t in tokens]
  54.392 -        
  54.393 -    def read_indented_block(self, text, indent):
  54.394 -        r"""Read a block of text. A block is what typically follows a for or it statement.
  54.395 -        It can be in the same line as that of the statement or an indented block.
  54.396 -
  54.397 -            >>> read_indented_block = Parser().read_indented_block
  54.398 -            >>> read_indented_block('  a\n  b\nc', '  ')
  54.399 -            ('a\nb\n', 'c')
  54.400 -            >>> read_indented_block('  a\n    b\n  c\nd', '  ')
  54.401 -            ('a\n  b\nc\n', 'd')
  54.402 -            >>> read_indented_block('  a\n\n    b\nc', '  ')
  54.403 -            ('a\n\n  b\n', 'c')
  54.404 -        """
  54.405 -        if indent == '':
  54.406 -            return '', text
  54.407 -            
  54.408 -        block = ""
  54.409 -        while text:
  54.410 -            line, text2 = splitline(text)
  54.411 -            if line.strip() == "":
  54.412 -                block += '\n'
  54.413 -            elif line.startswith(indent):
  54.414 -                block += line[len(indent):]
  54.415 -            else:
  54.416 -                break
  54.417 -            text = text2
  54.418 -        return block, text
  54.419 -
  54.420 -    def read_statement(self, text):
  54.421 -        r"""Reads a python statement.
  54.422 -        
  54.423 -            >>> read_statement = Parser().read_statement
  54.424 -            >>> read_statement('for i in range(10): hello $name')
  54.425 -            ('for i in range(10):', ' hello $name')
  54.426 -        """
  54.427 -        tok = PythonTokenizer(text)
  54.428 -        tok.consume_till(':')
  54.429 -        return text[:tok.index], text[tok.index:]
  54.430 -        
  54.431 -    def read_block_section(self, text, begin_indent=''):
  54.432 -        r"""
  54.433 -            >>> read_block_section = Parser().read_block_section
  54.434 -            >>> read_block_section('for i in range(10): hello $i\nfoo')
  54.435 -            (<block: 'for i in range(10):', [<line: [t'hello ', $i, t'\n']>]>, 'foo')
  54.436 -            >>> read_block_section('for i in range(10):\n        hello $i\n    foo', begin_indent='    ')
  54.437 -            (<block: 'for i in range(10):', [<line: [t'hello ', $i, t'\n']>]>, '    foo')
  54.438 -            >>> read_block_section('for i in range(10):\n  hello $i\nfoo')
  54.439 -            (<block: 'for i in range(10):', [<line: [t'hello ', $i, t'\n']>]>, 'foo')
  54.440 -        """
  54.441 -        line, text = splitline(text)
  54.442 -        stmt, line = self.read_statement(line)
  54.443 -        keyword = self.python_lookahead(stmt)
  54.444 -        
  54.445 -        # if there is some thing left in the line
  54.446 -        if line.strip():
  54.447 -            block = line.lstrip()
  54.448 -        else:
  54.449 -            def find_indent(text):
  54.450 -                rx = re_compile('  +')
  54.451 -                match = rx.match(text)    
  54.452 -                first_indent = match and match.group(0)
  54.453 -                return first_indent or ""
  54.454 -
  54.455 -            # find the indentation of the block by looking at the first line
  54.456 -            first_indent = find_indent(text)[len(begin_indent):]
  54.457 -
  54.458 -            #TODO: fix this special case
  54.459 -            if keyword == "code":
  54.460 -                indent = begin_indent + first_indent
  54.461 -            else:
  54.462 -                indent = begin_indent + min(first_indent, INDENT)
  54.463 -            
  54.464 -            block, text = self.read_indented_block(text, indent)
  54.465 -            
  54.466 -        return self.create_block_node(keyword, stmt, block, begin_indent), text
  54.467 -        
  54.468 -    def create_block_node(self, keyword, stmt, block, begin_indent):
  54.469 -        if keyword in self.statement_nodes:
  54.470 -            return self.statement_nodes[keyword](stmt, block, begin_indent)
  54.471 -        else:
  54.472 -            raise ParseError, 'Unknown statement: %s' % repr(keyword)
  54.473 -        
  54.474 -class PythonTokenizer:
  54.475 -    """Utility wrapper over python tokenizer."""
  54.476 -    def __init__(self, text):
  54.477 -        self.text = text
  54.478 -        readline = iter([text]).next
  54.479 -        self.tokens = tokenize.generate_tokens(readline)
  54.480 -        self.index = 0
  54.481 -        
  54.482 -    def consume_till(self, delim):        
  54.483 -        """Consumes tokens till colon.
  54.484 -        
  54.485 -            >>> tok = PythonTokenizer('for i in range(10): hello $i')
  54.486 -            >>> tok.consume_till(':')
  54.487 -            >>> tok.text[:tok.index]
  54.488 -            'for i in range(10):'
  54.489 -            >>> tok.text[tok.index:]
  54.490 -            ' hello $i'
  54.491 -        """
  54.492 -        try:
  54.493 -            while True:
  54.494 -                t = self.next()
  54.495 -                if t.value == delim:
  54.496 -                    break
  54.497 -                elif t.value == '(':
  54.498 -                    self.consume_till(')')
  54.499 -                elif t.value == '[':
  54.500 -                    self.consume_till(']')
  54.501 -                elif t.value == '{':
  54.502 -                    self.consume_till('}')
  54.503 -
  54.504 -                # if end of line is found, it is an exception.
  54.505 -                # Since there is no easy way to report the line number,
  54.506 -                # leave the error reporting to the python parser later  
  54.507 -                #@@ This should be fixed.
  54.508 -                if t.value == '\n':
  54.509 -                    break
  54.510 -        except:
  54.511 -            #raise ParseError, "Expected %s, found end of line." % repr(delim)
  54.512 -
  54.513 -            # raising ParseError doesn't show the line number. 
  54.514 -            # if this error is ignored, then it will be caught when compiling the python code.
  54.515 -            return
  54.516 -    
  54.517 -    def next(self):
  54.518 -        type, t, begin, end, line = self.tokens.next()
  54.519 -        row, col = end
  54.520 -        self.index = col
  54.521 -        return storage(type=type, value=t, begin=begin, end=end)
  54.522 -        
  54.523 -class DefwithNode:
  54.524 -    def __init__(self, defwith, suite):
  54.525 -        if defwith:
  54.526 -            self.defwith = defwith.replace('with', '__template__') + ':'
  54.527 -            # offset 4 lines. for encoding, __lineoffset__, loop and self.
  54.528 -            self.defwith += "\n    __lineoffset__ = -4"
  54.529 -        else:
  54.530 -            self.defwith = 'def __template__():'
  54.531 -            # offset 4 lines for encoding, __template__, __lineoffset__, loop and self.
  54.532 -            self.defwith += "\n    __lineoffset__ = -5"
  54.533 -
  54.534 -        self.defwith += "\n    loop = ForLoop()"
  54.535 -        self.defwith += "\n    self = TemplateResult(); extend_ = self.extend"
  54.536 -        self.suite = suite
  54.537 -        self.end = "\n    return self"
  54.538 -
  54.539 -    def emit(self, indent):
  54.540 -        encoding = "# coding: utf-8\n"
  54.541 -        return encoding + self.defwith + self.suite.emit(indent + INDENT) + self.end
  54.542 -
  54.543 -    def __repr__(self):
  54.544 -        return "<defwith: %s, %s>" % (self.defwith, self.suite)
  54.545 -
  54.546 -class TextNode:
  54.547 -    def __init__(self, value):
  54.548 -        self.value = value
  54.549 -
  54.550 -    def emit(self, indent, begin_indent=''):
  54.551 -        return repr(safeunicode(self.value))
  54.552 -        
  54.553 -    def __repr__(self):
  54.554 -        return 't' + repr(self.value)
  54.555 -
  54.556 -class ExpressionNode:
  54.557 -    def __init__(self, value, escape=True):
  54.558 -        self.value = value.strip()
  54.559 -        
  54.560 -        # convert ${...} to $(...)
  54.561 -        if value.startswith('{') and value.endswith('}'):
  54.562 -            self.value = '(' + self.value[1:-1] + ')'
  54.563 -            
  54.564 -        self.escape = escape
  54.565 -
  54.566 -    def emit(self, indent, begin_indent=''):
  54.567 -        return 'escape_(%s, %s)' % (self.value, bool(self.escape))
  54.568 -        
  54.569 -    def __repr__(self):
  54.570 -        if self.escape:
  54.571 -            escape = ''
  54.572 -        else:
  54.573 -            escape = ':'
  54.574 -        return "$%s%s" % (escape, self.value)
  54.575 -        
  54.576 -class AssignmentNode:
  54.577 -    def __init__(self, code):
  54.578 -        self.code = code
  54.579 -        
  54.580 -    def emit(self, indent, begin_indent=''):
  54.581 -        return indent + self.code + "\n"
  54.582 -        
  54.583 -    def __repr__(self):
  54.584 -        return "<assignment: %s>" % repr(self.code)
  54.585 -        
  54.586 -class LineNode:
  54.587 -    def __init__(self, nodes):
  54.588 -        self.nodes = nodes
  54.589 -        
  54.590 -    def emit(self, indent, text_indent='', name=''):
  54.591 -        text = [node.emit('') for node in self.nodes]
  54.592 -        if text_indent:
  54.593 -            text = [repr(text_indent)] + text
  54.594 -
  54.595 -        return indent + "extend_([%s])\n" % ", ".join(text)        
  54.596 -    
  54.597 -    def __repr__(self):
  54.598 -        return "<line: %s>" % repr(self.nodes)
  54.599 -
  54.600 -INDENT = '    ' # 4 spaces
  54.601 -        
  54.602 -class BlockNode:
  54.603 -    def __init__(self, stmt, block, begin_indent=''):
  54.604 -        self.stmt = stmt
  54.605 -        self.suite = Parser().read_suite(block)
  54.606 -        self.begin_indent = begin_indent
  54.607 -
  54.608 -    def emit(self, indent, text_indent=''):
  54.609 -        text_indent = self.begin_indent + text_indent
  54.610 -        out = indent + self.stmt + self.suite.emit(indent + INDENT, text_indent)
  54.611 -        return out
  54.612 -        
  54.613 -    def __repr__(self):
  54.614 -        return "<block: %s, %s>" % (repr(self.stmt), repr(self.suite))
  54.615 -
  54.616 -class ForNode(BlockNode):
  54.617 -    def __init__(self, stmt, block, begin_indent=''):
  54.618 -        self.original_stmt = stmt
  54.619 -        tok = PythonTokenizer(stmt)
  54.620 -        tok.consume_till('in')
  54.621 -        a = stmt[:tok.index] # for i in
  54.622 -        b = stmt[tok.index:-1] # rest of for stmt excluding :
  54.623 -        stmt = a + ' loop.setup(' + b.strip() + '):'
  54.624 -        BlockNode.__init__(self, stmt, block, begin_indent)
  54.625 -        
  54.626 -    def __repr__(self):
  54.627 -        return "<block: %s, %s>" % (repr(self.original_stmt), repr(self.suite))
  54.628 -
  54.629 -class CodeNode:
  54.630 -    def __init__(self, stmt, block, begin_indent=''):
  54.631 -        # compensate one line for $code:
  54.632 -        self.code = "\n" + block
  54.633 -        
  54.634 -    def emit(self, indent, text_indent=''):
  54.635 -        import re
  54.636 -        rx = re.compile('^', re.M)
  54.637 -        return rx.sub(indent, self.code).rstrip(' ')
  54.638 -        
  54.639 -    def __repr__(self):
  54.640 -        return "<code: %s>" % repr(self.code)
  54.641 -        
  54.642 -class StatementNode:
  54.643 -    def __init__(self, stmt):
  54.644 -        self.stmt = stmt
  54.645 -        
  54.646 -    def emit(self, indent, begin_indent=''):
  54.647 -        return indent + self.stmt
  54.648 -        
  54.649 -    def __repr__(self):
  54.650 -        return "<stmt: %s>" % repr(self.stmt)
  54.651 -        
  54.652 -class IfNode(BlockNode):
  54.653 -    pass
  54.654 -
  54.655 -class ElseNode(BlockNode):
  54.656 -    pass
  54.657 -
  54.658 -class ElifNode(BlockNode):
  54.659 -    pass
  54.660 -
  54.661 -class DefNode(BlockNode):
  54.662 -    def __init__(self, *a, **kw):
  54.663 -        BlockNode.__init__(self, *a, **kw)
  54.664 -
  54.665 -        code = CodeNode("", "")
  54.666 -        code.code = "self = TemplateResult(); extend_ = self.extend\n"
  54.667 -        self.suite.sections.insert(0, code)
  54.668 -
  54.669 -        code = CodeNode("", "")
  54.670 -        code.code = "return self\n"
  54.671 -        self.suite.sections.append(code)
  54.672 -        
  54.673 -    def emit(self, indent, text_indent=''):
  54.674 -        text_indent = self.begin_indent + text_indent
  54.675 -        out = indent + self.stmt + self.suite.emit(indent + INDENT, text_indent)
  54.676 -        return indent + "__lineoffset__ -= 3\n" + out
  54.677 -
  54.678 -class VarNode:
  54.679 -    def __init__(self, name, value):
  54.680 -        self.name = name
  54.681 -        self.value = value
  54.682 -        
  54.683 -    def emit(self, indent, text_indent):
  54.684 -        return indent + "self[%s] = %s\n" % (repr(self.name), self.value)
  54.685 -        
  54.686 -    def __repr__(self):
  54.687 -        return "<var: %s = %s>" % (self.name, self.value)
  54.688 -
  54.689 -class SuiteNode:
  54.690 -    """Suite is a list of sections."""
  54.691 -    def __init__(self, sections):
  54.692 -        self.sections = sections
  54.693 -        
  54.694 -    def emit(self, indent, text_indent=''):
  54.695 -        return "\n" + "".join([s.emit(indent, text_indent) for s in self.sections])
  54.696 -        
  54.697 -    def __repr__(self):
  54.698 -        return repr(self.sections)
  54.699 -
  54.700 -STATEMENT_NODES = {
  54.701 -    'for': ForNode,
  54.702 -    'while': BlockNode,
  54.703 -    'if': IfNode,
  54.704 -    'elif': ElifNode,
  54.705 -    'else': ElseNode,
  54.706 -    'def': DefNode,
  54.707 -    'code': CodeNode
  54.708 -}
  54.709 -
  54.710 -KEYWORDS = [
  54.711 -    "pass",
  54.712 -    "break",
  54.713 -    "continue",
  54.714 -    "return"
  54.715 -]
  54.716 -
  54.717 -TEMPLATE_BUILTIN_NAMES = [
  54.718 -    "dict", "enumerate", "float", "int", "bool", "list", "long", "reversed", 
  54.719 -    "set", "slice", "tuple", "xrange",
  54.720 -    "abs", "all", "any", "callable", "chr", "cmp", "divmod", "filter", "hex", 
  54.721 -    "id", "isinstance", "iter", "len", "max", "min", "oct", "ord", "pow", "range",
  54.722 -    "True", "False",
  54.723 -    "None",
  54.724 -    "__import__", # some c-libraries like datetime requires __import__ to present in the namespace
  54.725 -]
  54.726 -
  54.727 -import __builtin__
  54.728 -TEMPLATE_BUILTINS = dict([(name, getattr(__builtin__, name)) for name in TEMPLATE_BUILTIN_NAMES if name in __builtin__.__dict__])
  54.729 -
  54.730 -class ForLoop:
  54.731 -    """
  54.732 -    Wrapper for expression in for stament to support loop.xxx helpers.
  54.733 -    
  54.734 -        >>> loop = ForLoop()
  54.735 -        >>> for x in loop.setup(['a', 'b', 'c']):
  54.736 -        ...     print loop.index, loop.revindex, loop.parity, x
  54.737 -        ...
  54.738 -        1 3 odd a
  54.739 -        2 2 even b
  54.740 -        3 1 odd c
  54.741 -        >>> loop.index
  54.742 -        Traceback (most recent call last):
  54.743 -            ...
  54.744 -        AttributeError: index
  54.745 -    """
  54.746 -    def __init__(self):
  54.747 -        self._ctx = None
  54.748 -        
  54.749 -    def __getattr__(self, name):
  54.750 -        if self._ctx is None:
  54.751 -            raise AttributeError, name
  54.752 -        else:
  54.753 -            return getattr(self._ctx, name)
  54.754 -        
  54.755 -    def setup(self, seq):        
  54.756 -        self._push()
  54.757 -        return self._ctx.setup(seq)
  54.758 -        
  54.759 -    def _push(self):
  54.760 -        self._ctx = ForLoopContext(self, self._ctx)
  54.761 -        
  54.762 -    def _pop(self):
  54.763 -        self._ctx = self._ctx.parent
  54.764 -                
  54.765 -class ForLoopContext:
  54.766 -    """Stackable context for ForLoop to support nested for loops.
  54.767 -    """
  54.768 -    def __init__(self, forloop, parent):
  54.769 -        self._forloop = forloop
  54.770 -        self.parent = parent
  54.771 -        
  54.772 -    def setup(self, seq):
  54.773 -        try:
  54.774 -            self.length = len(seq)
  54.775 -        except:
  54.776 -            self.length = 0
  54.777 -
  54.778 -        self.index = 0
  54.779 -        for a in seq:
  54.780 -            self.index += 1
  54.781 -            yield a
  54.782 -        self._forloop._pop()
  54.783 -            
  54.784 -    index0 = property(lambda self: self.index-1)
  54.785 -    first = property(lambda self: self.index == 1)
  54.786 -    last = property(lambda self: self.index == self.length)
  54.787 -    odd = property(lambda self: self.index % 2 == 1)
  54.788 -    even = property(lambda self: self.index % 2 == 0)
  54.789 -    parity = property(lambda self: ['odd', 'even'][self.even])
  54.790 -    revindex0 = property(lambda self: self.length - self.index)
  54.791 -    revindex = property(lambda self: self.length - self.index + 1)
  54.792 -        
  54.793 -class BaseTemplate:
  54.794 -    def __init__(self, code, filename, filter, globals, builtins):
  54.795 -        self.filename = filename
  54.796 -        self.filter = filter
  54.797 -        self._globals = globals
  54.798 -        self._builtins = builtins
  54.799 -        if code:
  54.800 -            self.t = self._compile(code)
  54.801 -        else:
  54.802 -            self.t = lambda: ''
  54.803 -        
  54.804 -    def _compile(self, code):
  54.805 -        env = self.make_env(self._globals or {}, self._builtins)
  54.806 -        exec(code, env)
  54.807 -        return env['__template__']
  54.808 -
  54.809 -    def __call__(self, *a, **kw):
  54.810 -        __hidetraceback__ = True
  54.811 -        return self.t(*a, **kw)
  54.812 -
  54.813 -    def make_env(self, globals, builtins):
  54.814 -        return dict(globals,
  54.815 -            __builtins__=builtins, 
  54.816 -            ForLoop=ForLoop,
  54.817 -            TemplateResult=TemplateResult,
  54.818 -            escape_=self._escape,
  54.819 -            join_=self._join
  54.820 -        )
  54.821 -    def _join(self, *items):
  54.822 -        return u"".join(items)
  54.823 -            
  54.824 -    def _escape(self, value, escape=False):
  54.825 -        if value is None: 
  54.826 -            value = ''
  54.827 -            
  54.828 -        value = safeunicode(value)
  54.829 -        if escape and self.filter:
  54.830 -            value = self.filter(value)
  54.831 -        return value
  54.832 -
  54.833 -class Template(BaseTemplate):
  54.834 -    CONTENT_TYPES = {
  54.835 -        '.html' : 'text/html; charset=utf-8',
  54.836 -        '.xhtml' : 'application/xhtml+xml; charset=utf-8',
  54.837 -        '.txt' : 'text/plain',
  54.838 -    }
  54.839 -    FILTERS = {
  54.840 -        '.html': websafe,
  54.841 -        '.xhtml': websafe,
  54.842 -        '.xml': websafe
  54.843 -    }
  54.844 -    globals = {}
  54.845 -    
  54.846 -    def __init__(self, text, filename='<template>', filter=None, globals=None, builtins=None, extensions=None):
  54.847 -        self.extensions = extensions or []
  54.848 -        text = Template.normalize_text(text)
  54.849 -        code = self.compile_template(text, filename)
  54.850 -                
  54.851 -        _, ext = os.path.splitext(filename)
  54.852 -        filter = filter or self.FILTERS.get(ext, None)
  54.853 -        self.content_type = self.CONTENT_TYPES.get(ext, None)
  54.854 -
  54.855 -        if globals is None:
  54.856 -            globals = self.globals
  54.857 -        if builtins is None:
  54.858 -            builtins = TEMPLATE_BUILTINS
  54.859 -                
  54.860 -        BaseTemplate.__init__(self, code=code, filename=filename, filter=filter, globals=globals, builtins=builtins)
  54.861 -        
  54.862 -    def normalize_text(text):
  54.863 -        """Normalizes template text by correcting \r\n, tabs and BOM chars."""
  54.864 -        text = text.replace('\r\n', '\n').replace('\r', '\n').expandtabs()
  54.865 -        if not text.endswith('\n'):
  54.866 -            text += '\n'
  54.867 -
  54.868 -        # ignore BOM chars at the begining of template
  54.869 -        BOM = '\xef\xbb\xbf'
  54.870 -        if isinstance(text, str) and text.startswith(BOM):
  54.871 -            text = text[len(BOM):]
  54.872 -        
  54.873 -        # support fort \$ for backward-compatibility 
  54.874 -        text = text.replace(r'\$', '$$')
  54.875 -        return text
  54.876 -    normalize_text = staticmethod(normalize_text)
  54.877 -                
  54.878 -    def __call__(self, *a, **kw):
  54.879 -        __hidetraceback__ = True
  54.880 -        import webapi as web
  54.881 -        if 'headers' in web.ctx and self.content_type:
  54.882 -            web.header('Content-Type', self.content_type, unique=True)
  54.883 -            
  54.884 -        return BaseTemplate.__call__(self, *a, **kw)
  54.885 -        
  54.886 -    def generate_code(text, filename, parser=None):
  54.887 -        # parse the text
  54.888 -        parser = parser or Parser()
  54.889 -        rootnode = parser.parse(text, filename)
  54.890 -                
  54.891 -        # generate python code from the parse tree
  54.892 -        code = rootnode.emit(indent="").strip()
  54.893 -        return safestr(code)
  54.894 -        
  54.895 -    generate_code = staticmethod(generate_code)
  54.896 -    
  54.897 -    def create_parser(self):
  54.898 -        p = Parser()
  54.899 -        for ext in self.extensions:
  54.900 -            p = ext(p)
  54.901 -        return p
  54.902 -                
  54.903 -    def compile_template(self, template_string, filename):
  54.904 -        code = Template.generate_code(template_string, filename, parser=self.create_parser())
  54.905 -
  54.906 -        def get_source_line(filename, lineno):
  54.907 -            try:
  54.908 -                lines = open(filename).read().splitlines()
  54.909 -                return lines[lineno]
  54.910 -            except:
  54.911 -                return None
  54.912 -        
  54.913 -        try:
  54.914 -            # compile the code first to report the errors, if any, with the filename
  54.915 -            compiled_code = compile(code, filename, 'exec')
  54.916 -        except SyntaxError, e:
  54.917 -            # display template line that caused the error along with the traceback.
  54.918 -            try:
  54.919 -                e.msg += '\n\nTemplate traceback:\n    File %s, line %s\n        %s' % \
  54.920 -                    (repr(e.filename), e.lineno, get_source_line(e.filename, e.lineno-1))
  54.921 -            except: 
  54.922 -                pass
  54.923 -            raise
  54.924 -        
  54.925 -        # make sure code is safe - but not with jython, it doesn't have a working compiler module
  54.926 -        if not sys.platform.startswith('java'):
  54.927 -            try:
  54.928 -                import compiler
  54.929 -                ast = compiler.parse(code)
  54.930 -                SafeVisitor().walk(ast, filename)
  54.931 -            except ImportError:
  54.932 -                warnings.warn("Unabled to import compiler module. Unable to check templates for safety.")
  54.933 -        else:
  54.934 -            warnings.warn("SECURITY ISSUE: You are using Jython, which does not support checking templates for safety. Your templates can execute arbitrary code.")
  54.935 -
  54.936 -        return compiled_code
  54.937 -        
  54.938 -class CompiledTemplate(Template):
  54.939 -    def __init__(self, f, filename):
  54.940 -        Template.__init__(self, '', filename)
  54.941 -        self.t = f
  54.942 -        
  54.943 -    def compile_template(self, *a):
  54.944 -        return None
  54.945 -    
  54.946 -    def _compile(self, *a):
  54.947 -        return None
  54.948 -                
  54.949 -class Render:
  54.950 -    """The most preferred way of using templates.
  54.951 -    
  54.952 -        render = web.template.render('templates')
  54.953 -        print render.foo()
  54.954 -        
  54.955 -    Optional parameter can be `base` can be used to pass output of 
  54.956 -    every template through the base template.
  54.957 -    
  54.958 -        render = web.template.render('templates', base='layout')
  54.959 -    """
  54.960 -    def __init__(self, loc='templates', cache=None, base=None, **keywords):
  54.961 -        self._loc = loc
  54.962 -        self._keywords = keywords
  54.963 -
  54.964 -        if cache is None:
  54.965 -            cache = not config.get('debug', False)
  54.966 -        
  54.967 -        if cache:
  54.968 -            self._cache = {}
  54.969 -        else:
  54.970 -            self._cache = None
  54.971 -        
  54.972 -        if base and not hasattr(base, '__call__'):
  54.973 -            # make base a function, so that it can be passed to sub-renders
  54.974 -            self._base = lambda page: self._template(base)(page)
  54.975 -        else:
  54.976 -            self._base = base
  54.977 -    
  54.978 -    def _add_global(self, obj, name=None):
  54.979 -        """Add a global to this rendering instance."""
  54.980 -        if 'globals' not in self._keywords: self._keywords['globals'] = {}
  54.981 -        if not name:
  54.982 -            name = obj.__name__
  54.983 -        self._keywords['globals'][name] = obj
  54.984 -    
  54.985 -    def _lookup(self, name):
  54.986 -        path = os.path.join(self._loc, name)
  54.987 -        if os.path.isdir(path):
  54.988 -            return 'dir', path
  54.989 -        else:
  54.990 -            path = self._findfile(path)
  54.991 -            if path:
  54.992 -                return 'file', path
  54.993 -            else:
  54.994 -                return 'none', None
  54.995 -        
  54.996 -    def _load_template(self, name):
  54.997 -        kind, path = self._lookup(name)
  54.998 -        
  54.999 -        if kind == 'dir':
 54.1000 -            return Render(path, cache=self._cache is not None, base=self._base, **self._keywords)
 54.1001 -        elif kind == 'file':
 54.1002 -            return Template(open(path).read(), filename=path, **self._keywords)
 54.1003 -        else:
 54.1004 -            raise AttributeError, "No template named " + name            
 54.1005 -
 54.1006 -    def _findfile(self, path_prefix): 
 54.1007 -        p = [f for f in glob.glob(path_prefix + '.*') if not f.endswith('~')] # skip backup files
 54.1008 -        p.sort() # sort the matches for deterministic order
 54.1009 -        return p and p[0]
 54.1010 -            
 54.1011 -    def _template(self, name):
 54.1012 -        if self._cache is not None:
 54.1013 -            if name not in self._cache:
 54.1014 -                self._cache[name] = self._load_template(name)
 54.1015 -            return self._cache[name]
 54.1016 -        else:
 54.1017 -            return self._load_template(name)
 54.1018 -        
 54.1019 -    def __getattr__(self, name):
 54.1020 -        t = self._template(name)
 54.1021 -        if self._base and isinstance(t, Template):
 54.1022 -            def template(*a, **kw):
 54.1023 -                return self._base(t(*a, **kw))
 54.1024 -            return template
 54.1025 -        else:
 54.1026 -            return self._template(name)
 54.1027 -
 54.1028 -class GAE_Render(Render):
 54.1029 -    # Render gets over-written. make a copy here.
 54.1030 -    super = Render
 54.1031 -    def __init__(self, loc, *a, **kw):
 54.1032 -        GAE_Render.super.__init__(self, loc, *a, **kw)
 54.1033 -        
 54.1034 -        import types
 54.1035 -        if isinstance(loc, types.ModuleType):
 54.1036 -            self.mod = loc
 54.1037 -        else:
 54.1038 -            name = loc.rstrip('/').replace('/', '.')
 54.1039 -            self.mod = __import__(name, None, None, ['x'])
 54.1040 -
 54.1041 -        self.mod.__dict__.update(kw.get('builtins', TEMPLATE_BUILTINS))
 54.1042 -        self.mod.__dict__.update(Template.globals)
 54.1043 -        self.mod.__dict__.update(kw.get('globals', {}))
 54.1044 -
 54.1045 -    def _load_template(self, name):
 54.1046 -        t = getattr(self.mod, name)
 54.1047 -        import types
 54.1048 -        if isinstance(t, types.ModuleType):
 54.1049 -            return GAE_Render(t, cache=self._cache is not None, base=self._base, **self._keywords)
 54.1050 -        else:
 54.1051 -            return t
 54.1052 -
 54.1053 -render = Render
 54.1054 -# setup render for Google App Engine.
 54.1055 -try:
 54.1056 -    from google import appengine
 54.1057 -    render = Render = GAE_Render
 54.1058 -except ImportError:
 54.1059 -    pass
 54.1060 -        
 54.1061 -def frender(path, **keywords):
 54.1062 -    """Creates a template from the given file path.
 54.1063 -    """
 54.1064 -    return Template(open(path).read(), filename=path, **keywords)
 54.1065 -    
 54.1066 -def compile_templates(root):
 54.1067 -    """Compiles templates to python code."""
 54.1068 -    re_start = re_compile('^', re.M)
 54.1069 -    
 54.1070 -    for dirpath, dirnames, filenames in os.walk(root):
 54.1071 -        filenames = [f for f in filenames if not f.startswith('.') and not f.endswith('~') and not f.startswith('__init__.py')]
 54.1072 -
 54.1073 -        for d in dirnames[:]:
 54.1074 -            if d.startswith('.'):
 54.1075 -                dirnames.remove(d) # don't visit this dir
 54.1076 -
 54.1077 -        out = open(os.path.join(dirpath, '__init__.py'), 'w')
 54.1078 -        out.write('from web.template import CompiledTemplate, ForLoop, TemplateResult\n\n')
 54.1079 -        if dirnames:
 54.1080 -            out.write("import " + ", ".join(dirnames))
 54.1081 -        out.write("\n")
 54.1082 -
 54.1083 -        for f in filenames:
 54.1084 -            path = os.path.join(dirpath, f)
 54.1085 -
 54.1086 -            if '.' in f:
 54.1087 -                name, _ = f.split('.', 1)
 54.1088 -            else:
 54.1089 -                name = f
 54.1090 -                
 54.1091 -            text = open(path).read()
 54.1092 -            text = Template.normalize_text(text)
 54.1093 -            code = Template.generate_code(text, path)
 54.1094 -
 54.1095 -            code = code.replace("__template__", name, 1)
 54.1096 -            
 54.1097 -            out.write(code)
 54.1098 -
 54.1099 -            out.write('\n\n')
 54.1100 -            out.write('%s = CompiledTemplate(%s, %s)\n' % (name, name, repr(path)))
 54.1101 -            out.write("join_ = %s._join; escape_ = %s._escape\n\n" % (name, name))
 54.1102 -
 54.1103 -            # create template to make sure it compiles
 54.1104 -            t = Template(open(path).read(), path)
 54.1105 -        out.close()
 54.1106 -                
 54.1107 -class ParseError(Exception):
 54.1108 -    pass
 54.1109 -    
 54.1110 -class SecurityError(Exception):
 54.1111 -    """The template seems to be trying to do something naughty."""
 54.1112 -    pass
 54.1113 -
 54.1114 -# Enumerate all the allowed AST nodes
 54.1115 -ALLOWED_AST_NODES = [
 54.1116 -    "Add", "And",
 54.1117 -#   "AssAttr",
 54.1118 -    "AssList", "AssName", "AssTuple",
 54.1119 -#   "Assert",
 54.1120 -    "Assign", "AugAssign",
 54.1121 -#   "Backquote",
 54.1122 -    "Bitand", "Bitor", "Bitxor", "Break",
 54.1123 -    "CallFunc","Class", "Compare", "Const", "Continue",
 54.1124 -    "Decorators", "Dict", "Discard", "Div",
 54.1125 -    "Ellipsis", "EmptyNode",
 54.1126 -#   "Exec",
 54.1127 -    "Expression", "FloorDiv", "For",
 54.1128 -#   "From",
 54.1129 -    "Function", 
 54.1130 -    "GenExpr", "GenExprFor", "GenExprIf", "GenExprInner",
 54.1131 -    "Getattr", 
 54.1132 -#   "Global", 
 54.1133 -    "If", "IfExp",
 54.1134 -#   "Import",
 54.1135 -    "Invert", "Keyword", "Lambda", "LeftShift",
 54.1136 -    "List", "ListComp", "ListCompFor", "ListCompIf", "Mod",
 54.1137 -    "Module",
 54.1138 -    "Mul", "Name", "Not", "Or", "Pass", "Power",
 54.1139 -#   "Print", "Printnl", "Raise",
 54.1140 -    "Return", "RightShift", "Slice", "Sliceobj",
 54.1141 -    "Stmt", "Sub", "Subscript",
 54.1142 -#   "TryExcept", "TryFinally",
 54.1143 -    "Tuple", "UnaryAdd", "UnarySub",
 54.1144 -    "While", "With", "Yield",
 54.1145 -]
 54.1146 -
 54.1147 -class SafeVisitor(object):
 54.1148 -    """
 54.1149 -    Make sure code is safe by walking through the AST.
 54.1150 -    
 54.1151 -    Code considered unsafe if:
 54.1152 -        * it has restricted AST nodes
 54.1153 -        * it is trying to access resricted attributes   
 54.1154 -        
 54.1155 -    Adopted from http://www.zafar.se/bkz/uploads/safe.txt (public domain, Babar K. Zafar)
 54.1156 -    """
 54.1157 -    def __init__(self):
 54.1158 -        "Initialize visitor by generating callbacks for all AST node types."
 54.1159 -        self.errors = []
 54.1160 -
 54.1161 -    def walk(self, ast, filename):
 54.1162 -        "Validate each node in AST and raise SecurityError if the code is not safe."
 54.1163 -        self.filename = filename
 54.1164 -        self.visit(ast)
 54.1165 -        
 54.1166 -        if self.errors:        
 54.1167 -            raise SecurityError, '\n'.join([str(err) for err in self.errors])
 54.1168 -        
 54.1169 -    def visit(self, node, *args):
 54.1170 -        "Recursively validate node and all of its children."
 54.1171 -        def classname(obj):
 54.1172 -            return obj.__class__.__name__
 54.1173 -        nodename = classname(node)
 54.1174 -        fn = getattr(self, 'visit' + nodename, None)
 54.1175 -        
 54.1176 -        if fn:
 54.1177 -            fn(node, *args)
 54.1178 -        else:
 54.1179 -            if nodename not in ALLOWED_AST_NODES:
 54.1180 -                self.fail(node, *args)
 54.1181 -            
 54.1182 -        for child in node.getChildNodes():
 54.1183 -            self.visit(child, *args)
 54.1184 -
 54.1185 -    def visitName(self, node, *args):
 54.1186 -        "Disallow any attempts to access a restricted attr."
 54.1187 -        #self.assert_attr(node.getChildren()[0], node)
 54.1188 -        pass
 54.1189 -        
 54.1190 -    def visitGetattr(self, node, *args):
 54.1191 -        "Disallow any attempts to access a restricted attribute."
 54.1192 -        self.assert_attr(node.attrname, node)
 54.1193 -            
 54.1194 -    def assert_attr(self, attrname, node):
 54.1195 -        if self.is_unallowed_attr(attrname):
 54.1196 -            lineno = self.get_node_lineno(node)
 54.1197 -            e = SecurityError("%s:%d - access to attribute '%s' is denied" % (self.filename, lineno, attrname))
 54.1198 -            self.errors.append(e)
 54.1199 -
 54.1200 -    def is_unallowed_attr(self, name):
 54.1201 -        return name.startswith('_') \
 54.1202 -            or name.startswith('func_') \
 54.1203 -            or name.startswith('im_')
 54.1204 -            
 54.1205 -    def get_node_lineno(self, node):
 54.1206 -        return (node.lineno) and node.lineno or 0
 54.1207 -        
 54.1208 -    def fail(self, node, *args):
 54.1209 -        "Default callback for unallowed AST nodes."
 54.1210 -        lineno = self.get_node_lineno(node)
 54.1211 -        nodename = node.__class__.__name__
 54.1212 -        e = SecurityError("%s:%d - execution of '%s' statements is denied" % (self.filename, lineno, nodename))
 54.1213 -        self.errors.append(e)
 54.1214 -
 54.1215 -class TemplateResult(object, DictMixin):
 54.1216 -    """Dictionary like object for storing template output.
 54.1217 -    
 54.1218 -    The result of a template execution is usally a string, but sometimes it
 54.1219 -    contains attributes set using $var. This class provides a simple
 54.1220 -    dictionary like interface for storing the output of the template and the
 54.1221 -    attributes. The output is stored with a special key __body__. Convering
 54.1222 -    the the TemplateResult to string or unicode returns the value of __body__.
 54.1223 -    
 54.1224 -    When the template is in execution, the output is generated part by part
 54.1225 -    and those parts are combined at the end. Parts are added to the
 54.1226 -    TemplateResult by calling the `extend` method and the parts are combined
 54.1227 -    seemlessly when __body__ is accessed.
 54.1228 -    
 54.1229 -        >>> d = TemplateResult(__body__='hello, world', x='foo')
 54.1230 -        >>> d
 54.1231 -        <TemplateResult: {'__body__': 'hello, world', 'x': 'foo'}>
 54.1232 -        >>> print d
 54.1233 -        hello, world
 54.1234 -        >>> d.x
 54.1235 -        'foo'
 54.1236 -        >>> d = TemplateResult()
 54.1237 -        >>> d.extend([u'hello', u'world'])
 54.1238 -        >>> d
 54.1239 -        <TemplateResult: {'__body__': u'helloworld'}>
 54.1240 -    """
 54.1241 -    def __init__(self, *a, **kw):
 54.1242 -        self.__dict__["_d"] = dict(*a, **kw)
 54.1243 -        self._d.setdefault("__body__", u'')
 54.1244 -        
 54.1245 -        self.__dict__['_parts'] = []
 54.1246 -        self.__dict__["extend"] = self._parts.extend
 54.1247 -        
 54.1248 -        self._d.setdefault("__body__", None)
 54.1249 -    
 54.1250 -    def keys(self):
 54.1251 -        return self._d.keys()
 54.1252 -        
 54.1253 -    def _prepare_body(self):
 54.1254 -        """Prepare value of __body__ by joining parts.
 54.1255 -        """
 54.1256 -        if self._parts:
 54.1257 -            value = u"".join(self._parts)
 54.1258 -            self._parts[:] = []
 54.1259 -            body = self._d.get('__body__')
 54.1260 -            if body:
 54.1261 -                self._d['__body__'] = body + value
 54.1262 -            else:
 54.1263 -                self._d['__body__'] = value
 54.1264 -                
 54.1265 -    def __getitem__(self, name):
 54.1266 -        if name == "__body__":
 54.1267 -            self._prepare_body()
 54.1268 -        return self._d[name]
 54.1269 -        
 54.1270 -    def __setitem__(self, name, value):
 54.1271 -        if name == "__body__":
 54.1272 -            self._prepare_body()
 54.1273 -        return self._d.__setitem__(name, value)
 54.1274 -        
 54.1275 -    def __delitem__(self, name):
 54.1276 -        if name == "__body__":
 54.1277 -            self._prepare_body()
 54.1278 -        return self._d.__delitem__(name)
 54.1279 -
 54.1280 -    def __getattr__(self, key): 
 54.1281 -        try:
 54.1282 -            return self[key]
 54.1283 -        except KeyError, k:
 54.1284 -            raise AttributeError, k
 54.1285 -
 54.1286 -    def __setattr__(self, key, value): 
 54.1287 -        self[key] = value
 54.1288 -
 54.1289 -    def __delattr__(self, key):
 54.1290 -        try:
 54.1291 -            del self[key]
 54.1292 -        except KeyError, k:
 54.1293 -            raise AttributeError, k
 54.1294 -        
 54.1295 -    def __unicode__(self):
 54.1296 -        self._prepare_body()
 54.1297 -        return self["__body__"]
 54.1298 -    
 54.1299 -    def __str__(self):
 54.1300 -        self._prepare_body()
 54.1301 -        return self["__body__"].encode('utf-8')
 54.1302 -        
 54.1303 -    def __repr__(self):
 54.1304 -        self._prepare_body()
 54.1305 -        return "<TemplateResult: %s>" % self._d
 54.1306 -
 54.1307 -def test():
 54.1308 -    r"""Doctest for testing template module.
 54.1309 -
 54.1310 -    Define a utility function to run template test.
 54.1311 -    
 54.1312 -        >>> class TestResult:
 54.1313 -        ...     def __init__(self, t): self.t = t
 54.1314 -        ...     def __getattr__(self, name): return getattr(self.t, name)
 54.1315 -        ...     def __repr__(self): return repr(unicode(self))
 54.1316 -        ...
 54.1317 -        >>> def t(code, **keywords):
 54.1318 -        ...     tmpl = Template(code, **keywords)
 54.1319 -        ...     return lambda *a, **kw: TestResult(tmpl(*a, **kw))
 54.1320 -        ...
 54.1321 -    
 54.1322 -    Simple tests.
 54.1323 -    
 54.1324 -        >>> t('1')()
 54.1325 -        u'1\n'
 54.1326 -        >>> t('$def with ()\n1')()
 54.1327 -        u'1\n'
 54.1328 -        >>> t('$def with (a)\n$a')(1)
 54.1329 -        u'1\n'
 54.1330 -        >>> t('$def with (a=0)\n$a')(1)
 54.1331 -        u'1\n'
 54.1332 -        >>> t('$def with (a=0)\n$a')(a=1)
 54.1333 -        u'1\n'
 54.1334 -    
 54.1335 -    Test complicated expressions.
 54.1336 -        
 54.1337 -        >>> t('$def with (x)\n$x.upper()')('hello')
 54.1338 -        u'HELLO\n'
 54.1339 -        >>> t('$(2 * 3 + 4 * 5)')()
 54.1340 -        u'26\n'
 54.1341 -        >>> t('${2 * 3 + 4 * 5}')()
 54.1342 -        u'26\n'
 54.1343 -        >>> t('$def with (limit)\nkeep $(limit)ing.')('go')
 54.1344 -        u'keep going.\n'
 54.1345 -        >>> t('$def with (a)\n$a.b[0]')(storage(b=[1]))
 54.1346 -        u'1\n'
 54.1347 -        
 54.1348 -    Test html escaping.
 54.1349 -    
 54.1350 -        >>> t('$def with (x)\n$x', filename='a.html')('<html>')
 54.1351 -        u'&lt;html&gt;\n'
 54.1352 -        >>> t('$def with (x)\n$x', filename='a.txt')('<html>')
 54.1353 -        u'<html>\n'
 54.1354 -                
 54.1355 -    Test if, for and while.
 54.1356 -    
 54.1357 -        >>> t('$if 1: 1')()
 54.1358 -        u'1\n'
 54.1359 -        >>> t('$if 1:\n    1')()
 54.1360 -        u'1\n'
 54.1361 -        >>> t('$if 1:\n    1\\')()
 54.1362 -        u'1'
 54.1363 -        >>> t('$if 0: 0\n$elif 1: 1')()
 54.1364 -        u'1\n'
 54.1365 -        >>> t('$if 0: 0\n$elif None: 0\n$else: 1')()
 54.1366 -        u'1\n'
 54.1367 -        >>> t('$if 0 < 1 and 1 < 2: 1')()
 54.1368 -        u'1\n'
 54.1369 -        >>> t('$for x in [1, 2, 3]: $x')()
 54.1370 -        u'1\n2\n3\n'
 54.1371 -        >>> t('$def with (d)\n$for k, v in d.iteritems(): $k')({1: 1})
 54.1372 -        u'1\n'
 54.1373 -        >>> t('$for x in [1, 2, 3]:\n\t$x')()
 54.1374 -        u'    1\n    2\n    3\n'
 54.1375 -        >>> t('$def with (a)\n$while a and a.pop():1')([1, 2, 3])
 54.1376 -        u'1\n1\n1\n'
 54.1377 -
 54.1378 -    The space after : must be ignored.
 54.1379 -    
 54.1380 -        >>> t('$if True: foo')()
 54.1381 -        u'foo\n'
 54.1382 -    
 54.1383 -    Test loop.xxx.
 54.1384 -
 54.1385 -        >>> t("$for i in range(5):$loop.index, $loop.parity")()
 54.1386 -        u'1, odd\n2, even\n3, odd\n4, even\n5, odd\n'
 54.1387 -        >>> t("$for i in range(2):\n    $for j in range(2):$loop.parent.parity $loop.parity")()
 54.1388 -        u'odd odd\nodd even\neven odd\neven even\n'
 54.1389 -        
 54.1390 -    Test assignment.
 54.1391 -    
 54.1392 -        >>> t('$ a = 1\n$a')()
 54.1393 -        u'1\n'
 54.1394 -        >>> t('$ a = [1]\n$a[0]')()
 54.1395 -        u'1\n'
 54.1396 -        >>> t('$ a = {1: 1}\n$a.keys()[0]')()
 54.1397 -        u'1\n'
 54.1398 -        >>> t('$ a = []\n$if not a: 1')()
 54.1399 -        u'1\n'
 54.1400 -        >>> t('$ a = {}\n$if not a: 1')()
 54.1401 -        u'1\n'
 54.1402 -        >>> t('$ a = -1\n$a')()
 54.1403 -        u'-1\n'
 54.1404 -        >>> t('$ a = "1"\n$a')()
 54.1405 -        u'1\n'
 54.1406 -
 54.1407 -    Test comments.
 54.1408 -    
 54.1409 -        >>> t('$# 0')()
 54.1410 -        u'\n'
 54.1411 -        >>> t('hello$#comment1\nhello$#comment2')()
 54.1412 -        u'hello\nhello\n'
 54.1413 -        >>> t('$#comment0\nhello$#comment1\nhello$#comment2')()
 54.1414 -        u'\nhello\nhello\n'
 54.1415 -        
 54.1416 -    Test unicode.
 54.1417 -    
 54.1418 -        >>> t('$def with (a)\n$a')(u'\u203d')
 54.1419 -        u'\u203d\n'
 54.1420 -        >>> t('$def with (a)\n$a')(u'\u203d'.encode('utf-8'))
 54.1421 -        u'\u203d\n'
 54.1422 -        >>> t(u'$def with (a)\n$a $:a')(u'\u203d')
 54.1423 -        u'\u203d \u203d\n'
 54.1424 -        >>> t(u'$def with ()\nfoo')()
 54.1425 -        u'foo\n'
 54.1426 -        >>> def f(x): return x
 54.1427 -        ...
 54.1428 -        >>> t(u'$def with (f)\n$:f("x")')(f)
 54.1429 -        u'x\n'
 54.1430 -        >>> t('$def with (f)\n$:f("x")')(f)
 54.1431 -        u'x\n'
 54.1432 -    
 54.1433 -    Test dollar escaping.
 54.1434 -    
 54.1435 -        >>> t("Stop, $$money isn't evaluated.")()
 54.1436 -        u"Stop, $money isn't evaluated.\n"
 54.1437 -        >>> t("Stop, \$money isn't evaluated.")()
 54.1438 -        u"Stop, $money isn't evaluated.\n"
 54.1439 -        
 54.1440 -    Test space sensitivity.
 54.1441 -    
 54.1442 -        >>> t('$def with (x)\n$x')(1)
 54.1443 -        u'1\n'
 54.1444 -        >>> t('$def with(x ,y)\n$x')(1, 1)
 54.1445 -        u'1\n'
 54.1446 -        >>> t('$(1 + 2*3 + 4)')()
 54.1447 -        u'11\n'
 54.1448 -        
 54.1449 -    Make sure globals are working.
 54.1450 -            
 54.1451 -        >>> t('$x')()
 54.1452 -        Traceback (most recent call last):
 54.1453 -            ...
 54.1454 -        NameError: global name 'x' is not defined
 54.1455 -        >>> t('$x', globals={'x': 1})()
 54.1456 -        u'1\n'
 54.1457 -        
 54.1458 -    Can't change globals.
 54.1459 -    
 54.1460 -        >>> t('$ x = 2\n$x', globals={'x': 1})()
 54.1461 -        u'2\n'
 54.1462 -        >>> t('$ x = x + 1\n$x', globals={'x': 1})()
 54.1463 -        Traceback (most recent call last):
 54.1464 -            ...
 54.1465 -        UnboundLocalError: local variable 'x' referenced before assignment
 54.1466 -    
 54.1467 -    Make sure builtins are customizable.
 54.1468 -    
 54.1469 -        >>> t('$min(1, 2)')()
 54.1470 -        u'1\n'
 54.1471 -        >>> t('$min(1, 2)', builtins={})()
 54.1472 -        Traceback (most recent call last):
 54.1473 -            ...
 54.1474 -        NameError: global name 'min' is not defined
 54.1475 -        
 54.1476 -    Test vars.
 54.1477 -    
 54.1478 -        >>> x = t('$var x: 1')()
 54.1479 -        >>> x.x
 54.1480 -        u'1'
 54.1481 -        >>> x = t('$var x = 1')()
 54.1482 -        >>> x.x
 54.1483 -        1
 54.1484 -        >>> x = t('$var x:  \n    foo\n    bar')()
 54.1485 -        >>> x.x
 54.1486 -        u'foo\nbar\n'
 54.1487 -
 54.1488 -    Test BOM chars.
 54.1489 -
 54.1490 -        >>> t('\xef\xbb\xbf$def with(x)\n$x')('foo')
 54.1491 -        u'foo\n'
 54.1492 -
 54.1493 -    Test for with weird cases.
 54.1494 -
 54.1495 -        >>> t('$for i in range(10)[1:5]:\n    $i')()
 54.1496 -        u'1\n2\n3\n4\n'
 54.1497 -        >>> t("$for k, v in {'a': 1, 'b': 2}.items():\n    $k $v")()
 54.1498 -        u'a 1\nb 2\n'
 54.1499 -        >>> t("$for k, v in ({'a': 1, 'b': 2}.items():\n    $k $v")()
 54.1500 -        Traceback (most recent call last):
 54.1501 -            ...
 54.1502 -        SyntaxError: invalid syntax
 54.1503 -
 54.1504 -    Test datetime.
 54.1505 -
 54.1506 -        >>> import datetime
 54.1507 -        >>> t("$def with (date)\n$date.strftime('%m %Y')")(datetime.datetime(2009, 1, 1))
 54.1508 -        u'01 2009\n'
 54.1509 -    """
 54.1510 -    pass
 54.1511 -            
 54.1512 -if __name__ == "__main__":
 54.1513 -    import sys
 54.1514 -    if '--compile' in sys.argv:
 54.1515 -        compile_templates(sys.argv[2])
 54.1516 -    else:
 54.1517 -        import doctest
 54.1518 -        doctest.testmod()
    55.1 Binary file OpenSecurity/install/web.py-0.37/web/template.pyc has changed
    56.1 --- a/OpenSecurity/install/web.py-0.37/web/test.py	Thu Feb 20 15:40:48 2014 +0100
    56.2 +++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
    56.3 @@ -1,51 +0,0 @@
    56.4 -"""test utilities
    56.5 -(part of web.py)
    56.6 -"""
    56.7 -import unittest
    56.8 -import sys, os
    56.9 -import web
   56.10 -
   56.11 -TestCase = unittest.TestCase
   56.12 -TestSuite = unittest.TestSuite
   56.13 -
   56.14 -def load_modules(names):
   56.15 -    return [__import__(name, None, None, "x") for name in names]
   56.16 -
   56.17 -def module_suite(module, classnames=None):
   56.18 -    """Makes a suite from a module."""
   56.19 -    if classnames:
   56.20 -        return unittest.TestLoader().loadTestsFromNames(classnames, module)
   56.21 -    elif hasattr(module, 'suite'):
   56.22 -        return module.suite()
   56.23 -    else:
   56.24 -        return unittest.TestLoader().loadTestsFromModule(module)
   56.25 -
   56.26 -def doctest_suite(module_names):
   56.27 -    """Makes a test suite from doctests."""
   56.28 -    import doctest
   56.29 -    suite = TestSuite()
   56.30 -    for mod in load_modules(module_names):
   56.31 -        suite.addTest(doctest.DocTestSuite(mod))
   56.32 -    return suite
   56.33 -    
   56.34 -def suite(module_names):
   56.35 -    """Creates a suite from multiple modules."""
   56.36 -    suite = TestSuite()
   56.37 -    for mod in load_modules(module_names):
   56.38 -        suite.addTest(module_suite(mod))
   56.39 -    return suite
   56.40 -
   56.41 -def runTests(suite):
   56.42 -    runner = unittest.TextTestRunner()
   56.43 -    return runner.run(suite)
   56.44 -
   56.45 -def main(suite=None):
   56.46 -    if not suite:
   56.47 -        main_module = __import__('__main__')
   56.48 -        # allow command line switches
   56.49 -        args = [a for a in sys.argv[1:] if not a.startswith('-')]
   56.50 -        suite = module_suite(main_module, args or None)
   56.51 -
   56.52 -    result = runTests(suite)
   56.53 -    sys.exit(not result.wasSuccessful())
   56.54 -
    57.1 --- a/OpenSecurity/install/web.py-0.37/web/utils.py	Thu Feb 20 15:40:48 2014 +0100
    57.2 +++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
    57.3 @@ -1,1526 +0,0 @@
    57.4 -#!/usr/bin/env python
    57.5 -"""
    57.6 -General Utilities
    57.7 -(part of web.py)
    57.8 -"""
    57.9 -
   57.10 -__all__ = [
   57.11 -  "Storage", "storage", "storify", 
   57.12 -  "Counter", "counter",
   57.13 -  "iters", 
   57.14 -  "rstrips", "lstrips", "strips", 
   57.15 -  "safeunicode", "safestr", "utf8",
   57.16 -  "TimeoutError", "timelimit",
   57.17 -  "Memoize", "memoize",
   57.18 -  "re_compile", "re_subm",
   57.19 -  "group", "uniq", "iterview",
   57.20 -  "IterBetter", "iterbetter",
   57.21 -  "safeiter", "safewrite",
   57.22 -  "dictreverse", "dictfind", "dictfindall", "dictincr", "dictadd",
   57.23 -  "requeue", "restack",
   57.24 -  "listget", "intget", "datestr",
   57.25 -  "numify", "denumify", "commify", "dateify",
   57.26 -  "nthstr", "cond",
   57.27 -  "CaptureStdout", "capturestdout", "Profile", "profile",
   57.28 -  "tryall",
   57.29 -  "ThreadedDict", "threadeddict",
   57.30 -  "autoassign",
   57.31 -  "to36",
   57.32 -  "safemarkdown",
   57.33 -  "sendmail"
   57.34 -]
   57.35 -
   57.36 -import re, sys, time, threading, itertools, traceback, os
   57.37 -
   57.38 -try:
   57.39 -    import subprocess
   57.40 -except ImportError: 
   57.41 -    subprocess = None
   57.42 -
   57.43 -try: import datetime
   57.44 -except ImportError: pass
   57.45 -
   57.46 -try: set
   57.47 -except NameError:
   57.48 -    from sets import Set as set
   57.49 -    
   57.50 -try:
   57.51 -    from threading import local as threadlocal
   57.52 -except ImportError:
   57.53 -    from python23 import threadlocal
   57.54 -
   57.55 -class Storage(dict):
   57.56 -    """
   57.57 -    A Storage object is like a dictionary except `obj.foo` can be used
   57.58 -    in addition to `obj['foo']`.
   57.59 -    
   57.60 -        >>> o = storage(a=1)
   57.61 -        >>> o.a
   57.62 -        1
   57.63 -        >>> o['a']
   57.64 -        1
   57.65 -        >>> o.a = 2
   57.66 -        >>> o['a']
   57.67 -        2
   57.68 -        >>> del o.a
   57.69 -        >>> o.a
   57.70 -        Traceback (most recent call last):
   57.71 -            ...
   57.72 -        AttributeError: 'a'
   57.73 -    
   57.74 -    """
   57.75 -    def __getattr__(self, key): 
   57.76 -        try:
   57.77 -            return self[key]
   57.78 -        except KeyError, k:
   57.79 -            raise AttributeError, k
   57.80 -    
   57.81 -    def __setattr__(self, key, value): 
   57.82 -        self[key] = value
   57.83 -    
   57.84 -    def __delattr__(self, key):
   57.85 -        try:
   57.86 -            del self[key]
   57.87 -        except KeyError, k:
   57.88 -            raise AttributeError, k
   57.89 -    
   57.90 -    def __repr__(self):     
   57.91 -        return '<Storage ' + dict.__repr__(self) + '>'
   57.92 -
   57.93 -storage = Storage
   57.94 -
   57.95 -def storify(mapping, *requireds, **defaults):
   57.96 -    """
   57.97 -    Creates a `storage` object from dictionary `mapping`, raising `KeyError` if
   57.98 -    d doesn't have all of the keys in `requireds` and using the default 
   57.99 -    values for keys found in `defaults`.
  57.100 -
  57.101 -    For example, `storify({'a':1, 'c':3}, b=2, c=0)` will return the equivalent of
  57.102 -    `storage({'a':1, 'b':2, 'c':3})`.
  57.103 -    
  57.104 -    If a `storify` value is a list (e.g. multiple values in a form submission), 
  57.105 -    `storify` returns the last element of the list, unless the key appears in 
  57.106 -    `defaults` as a list. Thus:
  57.107 -    
  57.108 -        >>> storify({'a':[1, 2]}).a
  57.109 -        2
  57.110 -        >>> storify({'a':[1, 2]}, a=[]).a
  57.111 -        [1, 2]
  57.112 -        >>> storify({'a':1}, a=[]).a
  57.113 -        [1]
  57.114 -        >>> storify({}, a=[]).a
  57.115 -        []
  57.116 -    
  57.117 -    Similarly, if the value has a `value` attribute, `storify will return _its_
  57.118 -    value, unless the key appears in `defaults` as a dictionary.
  57.119 -    
  57.120 -        >>> storify({'a':storage(value=1)}).a
  57.121 -        1
  57.122 -        >>> storify({'a':storage(value=1)}, a={}).a
  57.123 -        <Storage {'value': 1}>
  57.124 -        >>> storify({}, a={}).a
  57.125 -        {}
  57.126 -        
  57.127 -    Optionally, keyword parameter `_unicode` can be passed to convert all values to unicode.
  57.128 -    
  57.129 -        >>> storify({'x': 'a'}, _unicode=True)
  57.130 -        <Storage {'x': u'a'}>
  57.131 -        >>> storify({'x': storage(value='a')}, x={}, _unicode=True)
  57.132 -        <Storage {'x': <Storage {'value': 'a'}>}>
  57.133 -        >>> storify({'x': storage(value='a')}, _unicode=True)
  57.134 -        <Storage {'x': u'a'}>
  57.135 -    """
  57.136 -    _unicode = defaults.pop('_unicode', False)
  57.137 -
  57.138 -    # if _unicode is callable object, use it convert a string to unicode.
  57.139 -    to_unicode = safeunicode
  57.140 -    if _unicode is not False and hasattr(_unicode, "__call__"):
  57.141 -        to_unicode = _unicode
  57.142 -    
  57.143 -    def unicodify(s):
  57.144 -        if _unicode and isinstance(s, str): return to_unicode(s)
  57.145 -        else: return s
  57.146 -        
  57.147 -    def getvalue(x):
  57.148 -        if hasattr(x, 'file') and hasattr(x, 'value'):
  57.149 -            return x.value
  57.150 -        elif hasattr(x, 'value'):
  57.151 -            return unicodify(x.value)
  57.152 -        else:
  57.153 -            return unicodify(x)
  57.154 -    
  57.155 -    stor = Storage()
  57.156 -    for key in requireds + tuple(mapping.keys()):
  57.157 -        value = mapping[key]
  57.158 -        if isinstance(value, list):
  57.159 -            if isinstance(defaults.get(key), list):
  57.160 -                value = [getvalue(x) for x in value]
  57.161 -            else:
  57.162 -                value = value[-1]
  57.163 -        if not isinstance(defaults.get(key), dict):
  57.164 -            value = getvalue(value)
  57.165 -        if isinstance(defaults.get(key), list) and not isinstance(value, list):
  57.166 -            value = [value]
  57.167 -        setattr(stor, key, value)
  57.168 -
  57.169 -    for (key, value) in defaults.iteritems():
  57.170 -        result = value
  57.171 -        if hasattr(stor, key): 
  57.172 -            result = stor[key]
  57.173 -        if value == () and not isinstance(result, tuple): 
  57.174 -            result = (result,)
  57.175 -        setattr(stor, key, result)
  57.176 -    
  57.177 -    return stor
  57.178 -
  57.179 -class Counter(storage):
  57.180 -    """Keeps count of how many times something is added.
  57.181 -        
  57.182 -        >>> c = counter()
  57.183 -        >>> c.add('x')
  57.184 -        >>> c.add('x')
  57.185 -        >>> c.add('x')
  57.186 -        >>> c.add('x')
  57.187 -        >>> c.add('x')
  57.188 -        >>> c.add('y')
  57.189 -        >>> c
  57.190 -        <Counter {'y': 1, 'x': 5}>
  57.191 -        >>> c.most()
  57.192 -        ['x']
  57.193 -    """
  57.194 -    def add(self, n):
  57.195 -        self.setdefault(n, 0)
  57.196 -        self[n] += 1
  57.197 -    
  57.198 -    def most(self):
  57.199 -        """Returns the keys with maximum count."""
  57.200 -        m = max(self.itervalues())
  57.201 -        return [k for k, v in self.iteritems() if v == m]
  57.202 -        
  57.203 -    def least(self):
  57.204 -        """Returns the keys with mininum count."""
  57.205 -        m = min(self.itervalues())
  57.206 -        return [k for k, v in self.iteritems() if v == m]
  57.207 -
  57.208 -    def percent(self, key):
  57.209 -       """Returns what percentage a certain key is of all entries.
  57.210 -
  57.211 -           >>> c = counter()
  57.212 -           >>> c.add('x')
  57.213 -           >>> c.add('x')
  57.214 -           >>> c.add('x')
  57.215 -           >>> c.add('y')
  57.216 -           >>> c.percent('x')
  57.217 -           0.75
  57.218 -           >>> c.percent('y')
  57.219 -           0.25
  57.220 -       """
  57.221 -       return float(self[key])/sum(self.values())
  57.222 -             
  57.223 -    def sorted_keys(self):
  57.224 -        """Returns keys sorted by value.
  57.225 -             
  57.226 -             >>> c = counter()
  57.227 -             >>> c.add('x')
  57.228 -             >>> c.add('x')
  57.229 -             >>> c.add('y')
  57.230 -             >>> c.sorted_keys()
  57.231 -             ['x', 'y']
  57.232 -        """
  57.233 -        return sorted(self.keys(), key=lambda k: self[k], reverse=True)
  57.234 -    
  57.235 -    def sorted_values(self):
  57.236 -        """Returns values sorted by value.
  57.237 -            
  57.238 -            >>> c = counter()
  57.239 -            >>> c.add('x')
  57.240 -            >>> c.add('x')
  57.241 -            >>> c.add('y')
  57.242 -            >>> c.sorted_values()
  57.243 -            [2, 1]
  57.244 -        """
  57.245 -        return [self[k] for k in self.sorted_keys()]
  57.246 -    
  57.247 -    def sorted_items(self):
  57.248 -        """Returns items sorted by value.
  57.249 -            
  57.250 -            >>> c = counter()
  57.251 -            >>> c.add('x')
  57.252 -            >>> c.add('x')
  57.253 -            >>> c.add('y')
  57.254 -            >>> c.sorted_items()
  57.255 -            [('x', 2), ('y', 1)]
  57.256 -        """
  57.257 -        return [(k, self[k]) for k in self.sorted_keys()]
  57.258 -    
  57.259 -    def __repr__(self):
  57.260 -        return '<Counter ' + dict.__repr__(self) + '>'
  57.261 -       
  57.262 -counter = Counter
  57.263 -
  57.264 -iters = [list, tuple]
  57.265 -import __builtin__
  57.266 -if hasattr(__builtin__, 'set'):
  57.267 -    iters.append(set)
  57.268 -if hasattr(__builtin__, 'frozenset'):
  57.269 -    iters.append(set)
  57.270 -if sys.version_info < (2,6): # sets module deprecated in 2.6
  57.271 -    try:
  57.272 -        from sets import Set
  57.273 -        iters.append(Set)
  57.274 -    except ImportError: 
  57.275 -        pass
  57.276 -    
  57.277 -class _hack(tuple): pass
  57.278 -iters = _hack(iters)
  57.279 -iters.__doc__ = """
  57.280 -A list of iterable items (like lists, but not strings). Includes whichever
  57.281 -of lists, tuples, sets, and Sets are available in this version of Python.
  57.282 -"""
  57.283 -
  57.284 -def _strips(direction, text, remove):
  57.285 -    if isinstance(remove, iters):
  57.286 -        for subr in remove:
  57.287 -            text = _strips(direction, text, subr)
  57.288 -        return text
  57.289 -    
  57.290 -    if direction == 'l': 
  57.291 -        if text.startswith(remove): 
  57.292 -            return text[len(remove):]
  57.293 -    elif direction == 'r':
  57.294 -        if text.endswith(remove):   
  57.295 -            return text[:-len(remove)]
  57.296 -    else: 
  57.297 -        raise ValueError, "Direction needs to be r or l."
  57.298 -    return text
  57.299 -
  57.300 -def rstrips(text, remove):
  57.301 -    """
  57.302 -    removes the string `remove` from the right of `text`
  57.303 -
  57.304 -        >>> rstrips("foobar", "bar")
  57.305 -        'foo'
  57.306 -    
  57.307 -    """
  57.308 -    return _strips('r', text, remove)
  57.309 -
  57.310 -def lstrips(text, remove):
  57.311 -    """
  57.312 -    removes the string `remove` from the left of `text`
  57.313 -    
  57.314 -        >>> lstrips("foobar", "foo")
  57.315 -        'bar'
  57.316 -        >>> lstrips('http://foo.org/', ['http://', 'https://'])
  57.317 -        'foo.org/'
  57.318 -        >>> lstrips('FOOBARBAZ', ['FOO', 'BAR'])
  57.319 -        'BAZ'
  57.320 -        >>> lstrips('FOOBARBAZ', ['BAR', 'FOO'])
  57.321 -        'BARBAZ'
  57.322 -    
  57.323 -    """
  57.324 -    return _strips('l', text, remove)
  57.325 -
  57.326 -def strips(text, remove):
  57.327 -    """
  57.328 -    removes the string `remove` from the both sides of `text`
  57.329 -
  57.330 -        >>> strips("foobarfoo", "foo")
  57.331 -        'bar'
  57.332 -    
  57.333 -    """
  57.334 -    return rstrips(lstrips(text, remove), remove)
  57.335 -
  57.336 -def safeunicode(obj, encoding='utf-8'):
  57.337 -    r"""
  57.338 -    Converts any given object to unicode string.
  57.339 -    
  57.340 -        >>> safeunicode('hello')
  57.341 -        u'hello'
  57.342 -        >>> safeunicode(2)
  57.343 -        u'2'
  57.344 -        >>> safeunicode('\xe1\x88\xb4')
  57.345 -        u'\u1234'
  57.346 -    """
  57.347 -    t = type(obj)
  57.348 -    if t is unicode:
  57.349 -        return obj
  57.350 -    elif t is str:
  57.351 -        return obj.decode(encoding)
  57.352 -    elif t in [int, float, bool]:
  57.353 -        return unicode(obj)
  57.354 -    elif hasattr(obj, '__unicode__') or isinstance(obj, unicode):
  57.355 -        return unicode(obj)
  57.356 -    else:
  57.357 -        return str(obj).decode(encoding)
  57.358 -    
  57.359 -def safestr(obj, encoding='utf-8'):
  57.360 -    r"""
  57.361 -    Converts any given object to utf-8 encoded string. 
  57.362 -    
  57.363 -        >>> safestr('hello')
  57.364 -        'hello'
  57.365 -        >>> safestr(u'\u1234')
  57.366 -        '\xe1\x88\xb4'
  57.367 -        >>> safestr(2)
  57.368 -        '2'
  57.369 -    """
  57.370 -    if isinstance(obj, unicode):
  57.371 -        return obj.encode(encoding)
  57.372 -    elif isinstance(obj, str):
  57.373 -        return obj
  57.374 -    elif hasattr(obj, 'next'): # iterator
  57.375 -        return itertools.imap(safestr, obj)
  57.376 -    else:
  57.377 -        return str(obj)
  57.378 -
  57.379 -# for backward-compatibility
  57.380 -utf8 = safestr
  57.381 -    
  57.382 -class TimeoutError(Exception): pass
  57.383 -def timelimit(timeout):
  57.384 -    """
  57.385 -    A decorator to limit a function to `timeout` seconds, raising `TimeoutError`
  57.386 -    if it takes longer.
  57.387 -    
  57.388 -        >>> import time
  57.389 -        >>> def meaningoflife():
  57.390 -        ...     time.sleep(.2)
  57.391 -        ...     return 42
  57.392 -        >>> 
  57.393 -        >>> timelimit(.1)(meaningoflife)()
  57.394 -        Traceback (most recent call last):
  57.395 -            ...
  57.396 -        TimeoutError: took too long
  57.397 -        >>> timelimit(1)(meaningoflife)()
  57.398 -        42
  57.399 -
  57.400 -    _Caveat:_ The function isn't stopped after `timeout` seconds but continues 
  57.401 -    executing in a separate thread. (There seems to be no way to kill a thread.)
  57.402 -
  57.403 -    inspired by <http://aspn.activestate.com/ASPN/Cookbook/Python/Recipe/473878>
  57.404 -    """
  57.405 -    def _1(function):
  57.406 -        def _2(*args, **kw):
  57.407 -            class Dispatch(threading.Thread):
  57.408 -                def __init__(self):
  57.409 -                    threading.Thread.__init__(self)
  57.410 -                    self.result = None
  57.411 -                    self.error = None
  57.412 -
  57.413 -                    self.setDaemon(True)
  57.414 -                    self.start()
  57.415 -
  57.416 -                def run(self):
  57.417 -                    try:
  57.418 -                        self.result = function(*args, **kw)
  57.419 -                    except:
  57.420 -                        self.error = sys.exc_info()
  57.421 -
  57.422 -            c = Dispatch()
  57.423 -            c.join(timeout)
  57.424 -            if c.isAlive():
  57.425 -                raise TimeoutError, 'took too long'
  57.426 -            if c.error:
  57.427 -                raise c.error[0], c.error[1]
  57.428 -            return c.result
  57.429 -        return _2
  57.430 -    return _1
  57.431 -
  57.432 -class Memoize:
  57.433 -    """
  57.434 -    'Memoizes' a function, caching its return values for each input.
  57.435 -    If `expires` is specified, values are recalculated after `expires` seconds.
  57.436 -    If `background` is specified, values are recalculated in a separate thread.
  57.437 -    
  57.438 -        >>> calls = 0
  57.439 -        >>> def howmanytimeshaveibeencalled():
  57.440 -        ...     global calls
  57.441 -        ...     calls += 1
  57.442 -        ...     return calls
  57.443 -        >>> fastcalls = memoize(howmanytimeshaveibeencalled)
  57.444 -        >>> howmanytimeshaveibeencalled()
  57.445 -        1
  57.446 -        >>> howmanytimeshaveibeencalled()
  57.447 -        2
  57.448 -        >>> fastcalls()
  57.449 -        3
  57.450 -        >>> fastcalls()
  57.451 -        3
  57.452 -        >>> import time
  57.453 -        >>> fastcalls = memoize(howmanytimeshaveibeencalled, .1, background=False)
  57.454 -        >>> fastcalls()
  57.455 -        4
  57.456 -        >>> fastcalls()
  57.457 -        4
  57.458 -        >>> time.sleep(.2)
  57.459 -        >>> fastcalls()
  57.460 -        5
  57.461 -        >>> def slowfunc():
  57.462 -        ...     time.sleep(.1)
  57.463 -        ...     return howmanytimeshaveibeencalled()
  57.464 -        >>> fastcalls = memoize(slowfunc, .2, background=True)
  57.465 -        >>> fastcalls()
  57.466 -        6
  57.467 -        >>> timelimit(.05)(fastcalls)()
  57.468 -        6
  57.469 -        >>> time.sleep(.2)
  57.470 -        >>> timelimit(.05)(fastcalls)()
  57.471 -        6
  57.472 -        >>> timelimit(.05)(fastcalls)()
  57.473 -        6
  57.474 -        >>> time.sleep(.2)
  57.475 -        >>> timelimit(.05)(fastcalls)()
  57.476 -        7
  57.477 -        >>> fastcalls = memoize(slowfunc, None, background=True)
  57.478 -        >>> threading.Thread(target=fastcalls).start()
  57.479 -        >>> time.sleep(.01)
  57.480 -        >>> fastcalls()
  57.481 -        9
  57.482 -    """
  57.483 -    def __init__(self, func, expires=None, background=True): 
  57.484 -        self.func = func
  57.485 -        self.cache = {}
  57.486 -        self.expires = expires
  57.487 -        self.background = background
  57.488 -        self.running = {}
  57.489 -    
  57.490 -    def __call__(self, *args, **keywords):
  57.491 -        key = (args, tuple(keywords.items()))
  57.492 -        if not self.running.get(key):
  57.493 -            self.running[key] = threading.Lock()
  57.494 -        def update(block=False):
  57.495 -            if self.running[key].acquire(block):
  57.496 -                try:
  57.497 -                    self.cache[key] = (self.func(*args, **keywords), time.time())
  57.498 -                finally:
  57.499 -                    self.running[key].release()
  57.500 -        
  57.501 -        if key not in self.cache: 
  57.502 -            update(block=True)
  57.503 -        elif self.expires and (time.time() - self.cache[key][1]) > self.expires:
  57.504 -            if self.background:
  57.505 -                threading.Thread(target=update).start()
  57.506 -            else:
  57.507 -                update()
  57.508 -        return self.cache[key][0]
  57.509 -
  57.510 -memoize = Memoize
  57.511 -
  57.512 -re_compile = memoize(re.compile) #@@ threadsafe?
  57.513 -re_compile.__doc__ = """
  57.514 -A memoized version of re.compile.
  57.515 -"""
  57.516 -
  57.517 -class _re_subm_proxy:
  57.518 -    def __init__(self): 
  57.519 -        self.match = None
  57.520 -    def __call__(self, match): 
  57.521 -        self.match = match
  57.522 -        return ''
  57.523 -
  57.524 -def re_subm(pat, repl, string):
  57.525 -    """
  57.526 -    Like re.sub, but returns the replacement _and_ the match object.
  57.527 -    
  57.528 -        >>> t, m = re_subm('g(oo+)fball', r'f\\1lish', 'goooooofball')
  57.529 -        >>> t
  57.530 -        'foooooolish'
  57.531 -        >>> m.groups()
  57.532 -        ('oooooo',)
  57.533 -    """
  57.534 -    compiled_pat = re_compile(pat)
  57.535 -    proxy = _re_subm_proxy()
  57.536 -    compiled_pat.sub(proxy.__call__, string)
  57.537 -    return compiled_pat.sub(repl, string), proxy.match
  57.538 -
  57.539 -def group(seq, size): 
  57.540 -    """
  57.541 -    Returns an iterator over a series of lists of length size from iterable.
  57.542 -
  57.543 -        >>> list(group([1,2,3,4], 2))
  57.544 -        [[1, 2], [3, 4]]
  57.545 -        >>> list(group([1,2,3,4,5], 2))
  57.546 -        [[1, 2], [3, 4], [5]]
  57.547 -    """
  57.548 -    def take(seq, n):
  57.549 -        for i in xrange(n):
  57.550 -            yield seq.next()
  57.551 -
  57.552 -    if not hasattr(seq, 'next'):  
  57.553 -        seq = iter(seq)
  57.554 -    while True: 
  57.555 -        x = list(take(seq, size))
  57.556 -        if x:
  57.557 -            yield x
  57.558 -        else:
  57.559 -            break
  57.560 -
  57.561 -def uniq(seq, key=None):
  57.562 -    """
  57.563 -    Removes duplicate elements from a list while preserving the order of the rest.
  57.564 -
  57.565 -        >>> uniq([9,0,2,1,0])
  57.566 -        [9, 0, 2, 1]
  57.567 -
  57.568 -    The value of the optional `key` parameter should be a function that
  57.569 -    takes a single argument and returns a key to test the uniqueness.
  57.570 -
  57.571 -        >>> uniq(["Foo", "foo", "bar"], key=lambda s: s.lower())
  57.572 -        ['Foo', 'bar']
  57.573 -    """
  57.574 -    key = key or (lambda x: x)
  57.575 -    seen = set()
  57.576 -    result = []
  57.577 -    for v in seq:
  57.578 -        k = key(v)
  57.579 -        if k in seen:
  57.580 -            continue
  57.581 -        seen.add(k)
  57.582 -        result.append(v)
  57.583 -    return result
  57.584 -
  57.585 -def iterview(x):
  57.586 -   """
  57.587 -   Takes an iterable `x` and returns an iterator over it
  57.588 -   which prints its progress to stderr as it iterates through.
  57.589 -   """
  57.590 -   WIDTH = 70
  57.591 -
  57.592 -   def plainformat(n, lenx):
  57.593 -       return '%5.1f%% (%*d/%d)' % ((float(n)/lenx)*100, len(str(lenx)), n, lenx)
  57.594 -
  57.595 -   def bars(size, n, lenx):
  57.596 -       val = int((float(n)*size)/lenx + 0.5)
  57.597 -       if size - val:
  57.598 -           spacing = ">" + (" "*(size-val))[1:]
  57.599 -       else:
  57.600 -           spacing = ""
  57.601 -       return "[%s%s]" % ("="*val, spacing)
  57.602 -
  57.603 -   def eta(elapsed, n, lenx):
  57.604 -       if n == 0:
  57.605 -           return '--:--:--'
  57.606 -       if n == lenx:
  57.607 -           secs = int(elapsed)
  57.608 -       else:
  57.609 -           secs = int((elapsed/n) * (lenx-n))
  57.610 -       mins, secs = divmod(secs, 60)
  57.611 -       hrs, mins = divmod(mins, 60)
  57.612 -
  57.613 -       return '%02d:%02d:%02d' % (hrs, mins, secs)
  57.614 -
  57.615 -   def format(starttime, n, lenx):
  57.616 -       out = plainformat(n, lenx) + ' '
  57.617 -       if n == lenx:
  57.618 -           end = '     '
  57.619 -       else:
  57.620 -           end = ' ETA '
  57.621 -       end += eta(time.time() - starttime, n, lenx)
  57.622 -       out += bars(WIDTH - len(out) - len(end), n, lenx)
  57.623 -       out += end
  57.624 -       return out
  57.625 -
  57.626 -   starttime = time.time()
  57.627 -   lenx = len(x)
  57.628 -   for n, y in enumerate(x):
  57.629 -       sys.stderr.write('\r' + format(starttime, n, lenx))
  57.630 -       yield y
  57.631 -   sys.stderr.write('\r' + format(starttime, n+1, lenx) + '\n')
  57.632 -
  57.633 -class IterBetter:
  57.634 -    """
  57.635 -    Returns an object that can be used as an iterator 
  57.636 -    but can also be used via __getitem__ (although it 
  57.637 -    cannot go backwards -- that is, you cannot request 
  57.638 -    `iterbetter[0]` after requesting `iterbetter[1]`).
  57.639 -    
  57.640 -        >>> import itertools
  57.641 -        >>> c = iterbetter(itertools.count())
  57.642 -        >>> c[1]
  57.643 -        1
  57.644 -        >>> c[5]
  57.645 -        5
  57.646 -        >>> c[3]
  57.647 -        Traceback (most recent call last):
  57.648 -            ...
  57.649 -        IndexError: already passed 3
  57.650 -
  57.651 -    For boolean test, IterBetter peeps at first value in the itertor without effecting the iteration.
  57.652 -
  57.653 -        >>> c = iterbetter(iter(range(5)))
  57.654 -        >>> bool(c)
  57.655 -        True
  57.656 -        >>> list(c)
  57.657 -        [0, 1, 2, 3, 4]
  57.658 -        >>> c = iterbetter(iter([]))
  57.659 -        >>> bool(c)
  57.660 -        False
  57.661 -        >>> list(c)
  57.662 -        []
  57.663 -    """
  57.664 -    def __init__(self, iterator): 
  57.665 -        self.i, self.c = iterator, 0
  57.666 -
  57.667 -    def __iter__(self): 
  57.668 -        if hasattr(self, "_head"):
  57.669 -            yield self._head
  57.670 -
  57.671 -        while 1:    
  57.672 -            yield self.i.next()
  57.673 -            self.c += 1
  57.674 -
  57.675 -    def __getitem__(self, i):
  57.676 -        #todo: slices
  57.677 -        if i < self.c: 
  57.678 -            raise IndexError, "already passed "+str(i)
  57.679 -        try:
  57.680 -            while i > self.c: 
  57.681 -                self.i.next()
  57.682 -                self.c += 1
  57.683 -            # now self.c == i
  57.684 -            self.c += 1
  57.685 -            return self.i.next()
  57.686 -        except StopIteration: 
  57.687 -            raise IndexError, str(i)
  57.688 -            
  57.689 -    def __nonzero__(self):
  57.690 -        if hasattr(self, "__len__"):
  57.691 -            return len(self) != 0
  57.692 -        elif hasattr(self, "_head"):
  57.693 -            return True
  57.694 -        else:
  57.695 -            try:
  57.696 -                self._head = self.i.next()
  57.697 -            except StopIteration:
  57.698 -                return False
  57.699 -            else:
  57.700 -                return True
  57.701 -
  57.702 -iterbetter = IterBetter
  57.703 -
  57.704 -def safeiter(it, cleanup=None, ignore_errors=True):
  57.705 -    """Makes an iterator safe by ignoring the exceptions occured during the iteration.
  57.706 -    """
  57.707 -    def next():
  57.708 -        while True:
  57.709 -            try:
  57.710 -                return it.next()
  57.711 -            except StopIteration:
  57.712 -                raise
  57.713 -            except:
  57.714 -                traceback.print_exc()
  57.715 -
  57.716 -    it = iter(it)
  57.717 -    while True:
  57.718 -        yield next()
  57.719 -
  57.720 -def safewrite(filename, content):
  57.721 -    """Writes the content to a temp file and then moves the temp file to 
  57.722 -    given filename to avoid overwriting the existing file in case of errors.
  57.723 -    """
  57.724 -    f = file(filename + '.tmp', 'w')
  57.725 -    f.write(content)
  57.726 -    f.close()
  57.727 -    os.rename(f.name, filename)
  57.728 -
  57.729 -def dictreverse(mapping):
  57.730 -    """
  57.731 -    Returns a new dictionary with keys and values swapped.
  57.732 -    
  57.733 -        >>> dictreverse({1: 2, 3: 4})
  57.734 -        {2: 1, 4: 3}
  57.735 -    """
  57.736 -    return dict([(value, key) for (key, value) in mapping.iteritems()])
  57.737 -
  57.738 -def dictfind(dictionary, element):
  57.739 -    """
  57.740 -    Returns a key whose value in `dictionary` is `element` 
  57.741 -    or, if none exists, None.
  57.742 -    
  57.743 -        >>> d = {1:2, 3:4}
  57.744 -        >>> dictfind(d, 4)
  57.745 -        3
  57.746 -        >>> dictfind(d, 5)
  57.747 -    """
  57.748 -    for (key, value) in dictionary.iteritems():
  57.749 -        if element is value: 
  57.750 -            return key
  57.751 -
  57.752 -def dictfindall(dictionary, element):
  57.753 -    """
  57.754 -    Returns the keys whose values in `dictionary` are `element`
  57.755 -    or, if none exists, [].
  57.756 -    
  57.757 -        >>> d = {1:4, 3:4}
  57.758 -        >>> dictfindall(d, 4)
  57.759 -        [1, 3]
  57.760 -        >>> dictfindall(d, 5)
  57.761 -        []
  57.762 -    """
  57.763 -    res = []
  57.764 -    for (key, value) in dictionary.iteritems():
  57.765 -        if element is value:
  57.766 -            res.append(key)
  57.767 -    return res
  57.768 -
  57.769 -def dictincr(dictionary, element):
  57.770 -    """
  57.771 -    Increments `element` in `dictionary`, 
  57.772 -    setting it to one if it doesn't exist.
  57.773 -    
  57.774 -        >>> d = {1:2, 3:4}
  57.775 -        >>> dictincr(d, 1)
  57.776 -        3
  57.777 -        >>> d[1]
  57.778 -        3
  57.779 -        >>> dictincr(d, 5)
  57.780 -        1
  57.781 -        >>> d[5]
  57.782 -        1
  57.783 -    """
  57.784 -    dictionary.setdefault(element, 0)
  57.785 -    dictionary[element] += 1
  57.786 -    return dictionary[element]
  57.787 -
  57.788 -def dictadd(*dicts):
  57.789 -    """
  57.790 -    Returns a dictionary consisting of the keys in the argument dictionaries.
  57.791 -    If they share a key, the value from the last argument is used.
  57.792 -    
  57.793 -        >>> dictadd({1: 0, 2: 0}, {2: 1, 3: 1})
  57.794 -        {1: 0, 2: 1, 3: 1}
  57.795 -    """
  57.796 -    result = {}
  57.797 -    for dct in dicts:
  57.798 -        result.update(dct)
  57.799 -    return result
  57.800 -
  57.801 -def requeue(queue, index=-1):
  57.802 -    """Returns the element at index after moving it to the beginning of the queue.
  57.803 -
  57.804 -        >>> x = [1, 2, 3, 4]
  57.805 -        >>> requeue(x)
  57.806 -        4
  57.807 -        >>> x
  57.808 -        [4, 1, 2, 3]
  57.809 -    """
  57.810 -    x = queue.pop(index)
  57.811 -    queue.insert(0, x)
  57.812 -    return x
  57.813 -
  57.814 -def restack(stack, index=0):
  57.815 -    """Returns the element at index after moving it to the top of stack.
  57.816 -
  57.817 -           >>> x = [1, 2, 3, 4]
  57.818 -           >>> restack(x)
  57.819 -           1
  57.820 -           >>> x
  57.821 -           [2, 3, 4, 1]
  57.822 -    """
  57.823 -    x = stack.pop(index)
  57.824 -    stack.append(x)
  57.825 -    return x
  57.826 -
  57.827 -def listget(lst, ind, default=None):
  57.828 -    """
  57.829 -    Returns `lst[ind]` if it exists, `default` otherwise.
  57.830 -    
  57.831 -        >>> listget(['a'], 0)
  57.832 -        'a'
  57.833 -        >>> listget(['a'], 1)
  57.834 -        >>> listget(['a'], 1, 'b')
  57.835 -        'b'
  57.836 -    """
  57.837 -    if len(lst)-1 < ind: 
  57.838 -        return default
  57.839 -    return lst[ind]
  57.840 -
  57.841 -def intget(integer, default=None):
  57.842 -    """
  57.843 -    Returns `integer` as an int or `default` if it can't.
  57.844 -    
  57.845 -        >>> intget('3')
  57.846 -        3
  57.847 -        >>> intget('3a')
  57.848 -        >>> intget('3a', 0)
  57.849 -        0
  57.850 -    """
  57.851 -    try:
  57.852 -        return int(integer)
  57.853 -    except (TypeError, ValueError):
  57.854 -        return default
  57.855 -
  57.856 -def datestr(then, now=None):
  57.857 -    """
  57.858 -    Converts a (UTC) datetime object to a nice string representation.
  57.859 -    
  57.860 -        >>> from datetime import datetime, timedelta
  57.861 -        >>> d = datetime(1970, 5, 1)
  57.862 -        >>> datestr(d, now=d)
  57.863 -        '0 microseconds ago'
  57.864 -        >>> for t, v in {
  57.865 -        ...   timedelta(microseconds=1): '1 microsecond ago',
  57.866 -        ...   timedelta(microseconds=2): '2 microseconds ago',
  57.867 -        ...   -timedelta(microseconds=1): '1 microsecond from now',
  57.868 -        ...   -timedelta(microseconds=2): '2 microseconds from now',
  57.869 -        ...   timedelta(microseconds=2000): '2 milliseconds ago',
  57.870 -        ...   timedelta(seconds=2): '2 seconds ago',
  57.871 -        ...   timedelta(seconds=2*60): '2 minutes ago',
  57.872 -        ...   timedelta(seconds=2*60*60): '2 hours ago',
  57.873 -        ...   timedelta(days=2): '2 days ago',
  57.874 -        ... }.iteritems():
  57.875 -        ...     assert datestr(d, now=d+t) == v
  57.876 -        >>> datestr(datetime(1970, 1, 1), now=d)
  57.877 -        'January  1'
  57.878 -        >>> datestr(datetime(1969, 1, 1), now=d)
  57.879 -        'January  1, 1969'
  57.880 -        >>> datestr(datetime(1970, 6, 1), now=d)
  57.881 -        'June  1, 1970'
  57.882 -        >>> datestr(None)
  57.883 -        ''
  57.884 -    """
  57.885 -    def agohence(n, what, divisor=None):
  57.886 -        if divisor: n = n // divisor
  57.887 -
  57.888 -        out = str(abs(n)) + ' ' + what       # '2 day'
  57.889 -        if abs(n) != 1: out += 's'           # '2 days'
  57.890 -        out += ' '                           # '2 days '
  57.891 -        if n < 0:
  57.892 -            out += 'from now'
  57.893 -        else:
  57.894 -            out += 'ago'
  57.895 -        return out                           # '2 days ago'
  57.896 -
  57.897 -    oneday = 24 * 60 * 60
  57.898 -
  57.899 -    if not then: return ""
  57.900 -    if not now: now = datetime.datetime.utcnow()
  57.901 -    if type(now).__name__ == "DateTime":
  57.902 -        now = datetime.datetime.fromtimestamp(now)
  57.903 -    if type(then).__name__ == "DateTime":
  57.904 -        then = datetime.datetime.fromtimestamp(then)
  57.905 -    elif type(then).__name__ == "date":
  57.906 -        then = datetime.datetime(then.year, then.month, then.day)
  57.907 -
  57.908 -    delta = now - then
  57.909 -    deltaseconds = int(delta.days * oneday + delta.seconds + delta.microseconds * 1e-06)
  57.910 -    deltadays = abs(deltaseconds) // oneday
  57.911 -    if deltaseconds < 0: deltadays *= -1 # fix for oddity of floor
  57.912 -
  57.913 -    if deltadays:
  57.914 -        if abs(deltadays) < 4:
  57.915 -            return agohence(deltadays, 'day')
  57.916 -
  57.917 -        try:
  57.918 -            out = then.strftime('%B %e') # e.g. 'June  3'
  57.919 -        except ValueError:
  57.920 -            # %e doesn't work on Windows.
  57.921 -            out = then.strftime('%B %d') # e.g. 'June 03'
  57.922 -
  57.923 -        if then.year != now.year or deltadays < 0:
  57.924 -            out += ', %s' % then.year
  57.925 -        return out
  57.926 -
  57.927 -    if int(deltaseconds):
  57.928 -        if abs(deltaseconds) > (60 * 60):
  57.929 -            return agohence(deltaseconds, 'hour', 60 * 60)
  57.930 -        elif abs(deltaseconds) > 60:
  57.931 -            return agohence(deltaseconds, 'minute', 60)
  57.932 -        else:
  57.933 -            return agohence(deltaseconds, 'second')
  57.934 -
  57.935 -    deltamicroseconds = delta.microseconds
  57.936 -    if delta.days: deltamicroseconds = int(delta.microseconds - 1e6) # datetime oddity
  57.937 -    if abs(deltamicroseconds) > 1000:
  57.938 -        return agohence(deltamicroseconds, 'millisecond', 1000)
  57.939 -
  57.940 -    return agohence(deltamicroseconds, 'microsecond')
  57.941 -
  57.942 -def numify(string):
  57.943 -    """
  57.944 -    Removes all non-digit characters from `string`.
  57.945 -    
  57.946 -        >>> numify('800-555-1212')
  57.947 -        '8005551212'
  57.948 -        >>> numify('800.555.1212')
  57.949 -        '8005551212'
  57.950 -    
  57.951 -    """
  57.952 -    return ''.join([c for c in str(string) if c.isdigit()])
  57.953 -
  57.954 -def denumify(string, pattern):
  57.955 -    """
  57.956 -    Formats `string` according to `pattern`, where the letter X gets replaced
  57.957 -    by characters from `string`.
  57.958 -    
  57.959 -        >>> denumify("8005551212", "(XXX) XXX-XXXX")
  57.960 -        '(800) 555-1212'
  57.961 -    
  57.962 -    """
  57.963 -    out = []
  57.964 -    for c in pattern:
  57.965 -        if c == "X":
  57.966 -            out.append(string[0])
  57.967 -            string = string[1:]
  57.968 -        else:
  57.969 -            out.append(c)
  57.970 -    return ''.join(out)
  57.971 -
  57.972 -def commify(n):
  57.973 -    """
  57.974 -    Add commas to an integer `n`.
  57.975 -
  57.976 -        >>> commify(1)
  57.977 -        '1'
  57.978 -        >>> commify(123)
  57.979 -        '123'
  57.980 -        >>> commify(1234)
  57.981 -        '1,234'
  57.982 -        >>> commify(1234567890)
  57.983 -        '1,234,567,890'
  57.984 -        >>> commify(123.0)
  57.985 -        '123.0'
  57.986 -        >>> commify(1234.5)
  57.987 -        '1,234.5'
  57.988 -        >>> commify(1234.56789)
  57.989 -        '1,234.56789'
  57.990 -        >>> commify('%.2f' % 1234.5)
  57.991 -        '1,234.50'
  57.992 -        >>> commify(None)
  57.993 -        >>>
  57.994 -
  57.995 -    """
  57.996 -    if n is None: return None
  57.997 -    n = str(n)
  57.998 -    if '.' in n:
  57.999 -        dollars, cents = n.split('.')
 57.1000 -    else:
 57.1001 -        dollars, cents = n, None
 57.1002 -
 57.1003 -    r = []
 57.1004 -    for i, c in enumerate(str(dollars)[::-1]):
 57.1005 -        if i and (not (i % 3)):
 57.1006 -            r.insert(0, ',')
 57.1007 -        r.insert(0, c)
 57.1008 -    out = ''.join(r)
 57.1009 -    if cents:
 57.1010 -        out += '.' + cents
 57.1011 -    return out
 57.1012 -
 57.1013 -def dateify(datestring):
 57.1014 -    """
 57.1015 -    Formats a numified `datestring` properly.
 57.1016 -    """
 57.1017 -    return denumify(datestring, "XXXX-XX-XX XX:XX:XX")
 57.1018 -
 57.1019 -
 57.1020 -def nthstr(n):
 57.1021 -    """
 57.1022 -    Formats an ordinal.
 57.1023 -    Doesn't handle negative numbers.
 57.1024 -
 57.1025 -        >>> nthstr(1)
 57.1026 -        '1st'
 57.1027 -        >>> nthstr(0)
 57.1028 -        '0th'
 57.1029 -        >>> [nthstr(x) for x in [2, 3, 4, 5, 10, 11, 12, 13, 14, 15]]
 57.1030 -        ['2nd', '3rd', '4th', '5th', '10th', '11th', '12th', '13th', '14th', '15th']
 57.1031 -        >>> [nthstr(x) for x in [91, 92, 93, 94, 99, 100, 101, 102]]
 57.1032 -        ['91st', '92nd', '93rd', '94th', '99th', '100th', '101st', '102nd']
 57.1033 -        >>> [nthstr(x) for x in [111, 112, 113, 114, 115]]
 57.1034 -        ['111th', '112th', '113th', '114th', '115th']
 57.1035 -
 57.1036 -    """
 57.1037 -    
 57.1038 -    assert n >= 0
 57.1039 -    if n % 100 in [11, 12, 13]: return '%sth' % n
 57.1040 -    return {1: '%sst', 2: '%snd', 3: '%srd'}.get(n % 10, '%sth') % n
 57.1041 -
 57.1042 -def cond(predicate, consequence, alternative=None):
 57.1043 -    """
 57.1044 -    Function replacement for if-else to use in expressions.
 57.1045 -        
 57.1046 -        >>> x = 2
 57.1047 -        >>> cond(x % 2 == 0, "even", "odd")
 57.1048 -        'even'
 57.1049 -        >>> cond(x % 2 == 0, "even", "odd") + '_row'
 57.1050 -        'even_row'
 57.1051 -    """
 57.1052 -    if predicate:
 57.1053 -        return consequence
 57.1054 -    else:
 57.1055 -        return alternative
 57.1056 -
 57.1057 -class CaptureStdout:
 57.1058 -    """
 57.1059 -    Captures everything `func` prints to stdout and returns it instead.
 57.1060 -    
 57.1061 -        >>> def idiot():
 57.1062 -        ...     print "foo"
 57.1063 -        >>> capturestdout(idiot)()
 57.1064 -        'foo\\n'
 57.1065 -    
 57.1066 -    **WARNING:** Not threadsafe!
 57.1067 -    """
 57.1068 -    def __init__(self, func): 
 57.1069 -        self.func = func
 57.1070 -    def __call__(self, *args, **keywords):
 57.1071 -        from cStringIO import StringIO
 57.1072 -        # Not threadsafe!
 57.1073 -        out = StringIO()
 57.1074 -        oldstdout = sys.stdout
 57.1075 -        sys.stdout = out
 57.1076 -        try: 
 57.1077 -            self.func(*args, **keywords)
 57.1078 -        finally: 
 57.1079 -            sys.stdout = oldstdout
 57.1080 -        return out.getvalue()
 57.1081 -
 57.1082 -capturestdout = CaptureStdout
 57.1083 -
 57.1084 -class Profile:
 57.1085 -    """
 57.1086 -    Profiles `func` and returns a tuple containing its output
 57.1087 -    and a string with human-readable profiling information.
 57.1088 -        
 57.1089 -        >>> import time
 57.1090 -        >>> out, inf = profile(time.sleep)(.001)
 57.1091 -        >>> out
 57.1092 -        >>> inf[:10].strip()
 57.1093 -        'took 0.0'
 57.1094 -    """
 57.1095 -    def __init__(self, func): 
 57.1096 -        self.func = func
 57.1097 -    def __call__(self, *args): ##, **kw):   kw unused
 57.1098 -        import hotshot, hotshot.stats, os, tempfile ##, time already imported
 57.1099 -        f, filename = tempfile.mkstemp()
 57.1100 -        os.close(f)
 57.1101 -        
 57.1102 -        prof = hotshot.Profile(filename)
 57.1103 -
 57.1104 -        stime = time.time()
 57.1105 -        result = prof.runcall(self.func, *args)
 57.1106 -        stime = time.time() - stime
 57.1107 -        prof.close()
 57.1108 -
 57.1109 -        import cStringIO
 57.1110 -        out = cStringIO.StringIO()
 57.1111 -        stats = hotshot.stats.load(filename)
 57.1112 -        stats.stream = out
 57.1113 -        stats.strip_dirs()
 57.1114 -        stats.sort_stats('time', 'calls')
 57.1115 -        stats.print_stats(40)
 57.1116 -        stats.print_callers()
 57.1117 -
 57.1118 -        x =  '\n\ntook '+ str(stime) + ' seconds\n'
 57.1119 -        x += out.getvalue()
 57.1120 -
 57.1121 -        # remove the tempfile
 57.1122 -        try:
 57.1123 -            os.remove(filename)
 57.1124 -        except IOError:
 57.1125 -            pass
 57.1126 -            
 57.1127 -        return result, x
 57.1128 -
 57.1129 -profile = Profile
 57.1130 -
 57.1131 -
 57.1132 -import traceback
 57.1133 -# hack for compatibility with Python 2.3:
 57.1134 -if not hasattr(traceback, 'format_exc'):
 57.1135 -    from cStringIO import StringIO
 57.1136 -    def format_exc(limit=None):
 57.1137 -        strbuf = StringIO()
 57.1138 -        traceback.print_exc(limit, strbuf)
 57.1139 -        return strbuf.getvalue()
 57.1140 -    traceback.format_exc = format_exc
 57.1141 -
 57.1142 -def tryall(context, prefix=None):
 57.1143 -    """
 57.1144 -    Tries a series of functions and prints their results. 
 57.1145 -    `context` is a dictionary mapping names to values; 
 57.1146 -    the value will only be tried if it's callable.
 57.1147 -    
 57.1148 -        >>> tryall(dict(j=lambda: True))
 57.1149 -        j: True
 57.1150 -        ----------------------------------------
 57.1151 -        results:
 57.1152 -           True: 1
 57.1153 -
 57.1154 -    For example, you might have a file `test/stuff.py` 
 57.1155 -    with a series of functions testing various things in it. 
 57.1156 -    At the bottom, have a line:
 57.1157 -
 57.1158 -        if __name__ == "__main__": tryall(globals())
 57.1159 -
 57.1160 -    Then you can run `python test/stuff.py` and get the results of 
 57.1161 -    all the tests.
 57.1162 -    """
 57.1163 -    context = context.copy() # vars() would update
 57.1164 -    results = {}
 57.1165 -    for (key, value) in context.iteritems():
 57.1166 -        if not hasattr(value, '__call__'): 
 57.1167 -            continue
 57.1168 -        if prefix and not key.startswith(prefix): 
 57.1169 -            continue
 57.1170 -        print key + ':',
 57.1171 -        try:
 57.1172 -            r = value()
 57.1173 -            dictincr(results, r)
 57.1174 -            print r
 57.1175 -        except:
 57.1176 -            print 'ERROR'
 57.1177 -            dictincr(results, 'ERROR')
 57.1178 -            print '   ' + '\n   '.join(traceback.format_exc().split('\n'))
 57.1179 -        
 57.1180 -    print '-'*40
 57.1181 -    print 'results:'
 57.1182 -    for (key, value) in results.iteritems():
 57.1183 -        print ' '*2, str(key)+':', value
 57.1184 -        
 57.1185 -class ThreadedDict(threadlocal):
 57.1186 -    """
 57.1187 -    Thread local storage.
 57.1188 -    
 57.1189 -        >>> d = ThreadedDict()
 57.1190 -        >>> d.x = 1
 57.1191 -        >>> d.x
 57.1192 -        1
 57.1193 -        >>> import threading
 57.1194 -        >>> def f(): d.x = 2
 57.1195 -        ...
 57.1196 -        >>> t = threading.Thread(target=f)
 57.1197 -        >>> t.start()
 57.1198 -        >>> t.join()
 57.1199 -        >>> d.x
 57.1200 -        1
 57.1201 -    """
 57.1202 -    _instances = set()
 57.1203 -    
 57.1204 -    def __init__(self):
 57.1205 -        ThreadedDict._instances.add(self)
 57.1206 -        
 57.1207 -    def __del__(self):
 57.1208 -        ThreadedDict._instances.remove(self)
 57.1209 -        
 57.1210 -    def __hash__(self):
 57.1211 -        return id(self)
 57.1212 -    
 57.1213 -    def clear_all():
 57.1214 -        """Clears all ThreadedDict instances.
 57.1215 -        """
 57.1216 -        for t in list(ThreadedDict._instances):
 57.1217 -            t.clear()
 57.1218 -    clear_all = staticmethod(clear_all)
 57.1219 -    
 57.1220 -    # Define all these methods to more or less fully emulate dict -- attribute access
 57.1221 -    # is built into threading.local.
 57.1222 -
 57.1223 -    def __getitem__(self, key):
 57.1224 -        return self.__dict__[key]
 57.1225 -
 57.1226 -    def __setitem__(self, key, value):
 57.1227 -        self.__dict__[key] = value
 57.1228 -
 57.1229 -    def __delitem__(self, key):
 57.1230 -        del self.__dict__[key]
 57.1231 -
 57.1232 -    def __contains__(self, key):
 57.1233 -        return key in self.__dict__
 57.1234 -
 57.1235 -    has_key = __contains__
 57.1236 -        
 57.1237 -    def clear(self):
 57.1238 -        self.__dict__.clear()
 57.1239 -
 57.1240 -    def copy(self):
 57.1241 -        return self.__dict__.copy()
 57.1242 -
 57.1243 -    def get(self, key, default=None):
 57.1244 -        return self.__dict__.get(key, default)
 57.1245 -
 57.1246 -    def items(self):
 57.1247 -        return self.__dict__.items()
 57.1248 -
 57.1249 -    def iteritems(self):
 57.1250 -        return self.__dict__.iteritems()
 57.1251 -
 57.1252 -    def keys(self):
 57.1253 -        return self.__dict__.keys()
 57.1254 -
 57.1255 -    def iterkeys(self):
 57.1256 -        return self.__dict__.iterkeys()
 57.1257 -
 57.1258 -    iter = iterkeys
 57.1259 -
 57.1260 -    def values(self):
 57.1261 -        return self.__dict__.values()
 57.1262 -
 57.1263 -    def itervalues(self):
 57.1264 -        return self.__dict__.itervalues()
 57.1265 -
 57.1266 -    def pop(self, key, *args):
 57.1267 -        return self.__dict__.pop(key, *args)
 57.1268 -
 57.1269 -    def popitem(self):
 57.1270 -        return self.__dict__.popitem()
 57.1271 -
 57.1272 -    def setdefault(self, key, default=None):
 57.1273 -        return self.__dict__.setdefault(key, default)
 57.1274 -
 57.1275 -    def update(self, *args, **kwargs):
 57.1276 -        self.__dict__.update(*args, **kwargs)
 57.1277 -
 57.1278 -    def __repr__(self):
 57.1279 -        return '<ThreadedDict %r>' % self.__dict__
 57.1280 -
 57.1281 -    __str__ = __repr__
 57.1282 -    
 57.1283 -threadeddict = ThreadedDict
 57.1284 -
 57.1285 -def autoassign(self, locals):
 57.1286 -    """
 57.1287 -    Automatically assigns local variables to `self`.
 57.1288 -    
 57.1289 -        >>> self = storage()
 57.1290 -        >>> autoassign(self, dict(a=1, b=2))
 57.1291 -        >>> self
 57.1292 -        <Storage {'a': 1, 'b': 2}>
 57.1293 -    
 57.1294 -    Generally used in `__init__` methods, as in:
 57.1295 -
 57.1296 -        def __init__(self, foo, bar, baz=1): autoassign(self, locals())
 57.1297 -    """
 57.1298 -    for (key, value) in locals.iteritems():
 57.1299 -        if key == 'self': 
 57.1300 -            continue
 57.1301 -        setattr(self, key, value)
 57.1302 -
 57.1303 -def to36(q):
 57.1304 -    """
 57.1305 -    Converts an integer to base 36 (a useful scheme for human-sayable IDs).
 57.1306 -    
 57.1307 -        >>> to36(35)
 57.1308 -        'z'
 57.1309 -        >>> to36(119292)
 57.1310 -        '2k1o'
 57.1311 -        >>> int(to36(939387374), 36)
 57.1312 -        939387374
 57.1313 -        >>> to36(0)
 57.1314 -        '0'
 57.1315 -        >>> to36(-393)
 57.1316 -        Traceback (most recent call last):
 57.1317 -            ... 
 57.1318 -        ValueError: must supply a positive integer
 57.1319 -    
 57.1320 -    """
 57.1321 -    if q < 0: raise ValueError, "must supply a positive integer"
 57.1322 -    letters = "0123456789abcdefghijklmnopqrstuvwxyz"
 57.1323 -    converted = []
 57.1324 -    while q != 0:
 57.1325 -        q, r = divmod(q, 36)
 57.1326 -        converted.insert(0, letters[r])
 57.1327 -    return "".join(converted) or '0'
 57.1328 -
 57.1329 -
 57.1330 -r_url = re_compile('(?<!\()(http://(\S+))')
 57.1331 -def safemarkdown(text):
 57.1332 -    """
 57.1333 -    Converts text to HTML following the rules of Markdown, but blocking any
 57.1334 -    outside HTML input, so that only the things supported by Markdown
 57.1335 -    can be used. Also converts raw URLs to links.
 57.1336 -
 57.1337 -    (requires [markdown.py](http://webpy.org/markdown.py))
 57.1338 -    """
 57.1339 -    from markdown import markdown
 57.1340 -    if text:
 57.1341 -        text = text.replace('<', '&lt;')
 57.1342 -        # TODO: automatically get page title?
 57.1343 -        text = r_url.sub(r'<\1>', text)
 57.1344 -        text = markdown(text)
 57.1345 -        return text
 57.1346 -
 57.1347 -def sendmail(from_address, to_address, subject, message, headers=None, **kw):
 57.1348 -    """
 57.1349 -    Sends the email message `message` with mail and envelope headers
 57.1350 -    for from `from_address_` to `to_address` with `subject`. 
 57.1351 -    Additional email headers can be specified with the dictionary 
 57.1352 -    `headers.
 57.1353 -    
 57.1354 -    Optionally cc, bcc and attachments can be specified as keyword arguments.
 57.1355 -    Attachments must be an iterable and each attachment can be either a 
 57.1356 -    filename or a file object or a dictionary with filename, content and 
 57.1357 -    optionally content_type keys.
 57.1358 -
 57.1359 -    If `web.config.smtp_server` is set, it will send the message
 57.1360 -    to that SMTP server. Otherwise it will look for 
 57.1361 -    `/usr/sbin/sendmail`, the typical location for the sendmail-style
 57.1362 -    binary. To use sendmail from a different path, set `web.config.sendmail_path`.
 57.1363 -    """
 57.1364 -    attachments = kw.pop("attachments", [])
 57.1365 -    mail = _EmailMessage(from_address, to_address, subject, message, headers, **kw)
 57.1366 -
 57.1367 -    for a in attachments:
 57.1368 -        if isinstance(a, dict):
 57.1369 -            mail.attach(a['filename'], a['content'], a.get('content_type'))
 57.1370 -        elif hasattr(a, 'read'): # file
 57.1371 -            filename = os.path.basename(getattr(a, "name", ""))
 57.1372 -            content_type = getattr(a, 'content_type', None)
 57.1373 -            mail.attach(filename, a.read(), content_type)
 57.1374 -        elif isinstance(a, basestring):
 57.1375 -            f = open(a, 'rb')
 57.1376 -            content = f.read()
 57.1377 -            f.close()
 57.1378 -            filename = os.path.basename(a)
 57.1379 -            mail.attach(filename, content, None)
 57.1380 -        else:
 57.1381 -            raise ValueError, "Invalid attachment: %s" % repr(a)
 57.1382 -            
 57.1383 -    mail.send()
 57.1384 -
 57.1385 -class _EmailMessage:
 57.1386 -    def __init__(self, from_address, to_address, subject, message, headers=None, **kw):
 57.1387 -        def listify(x):
 57.1388 -            if not isinstance(x, list):
 57.1389 -                return [safestr(x)]
 57.1390 -            else:
 57.1391 -                return [safestr(a) for a in x]
 57.1392 -    
 57.1393 -        subject = safestr(subject)
 57.1394 -        message = safestr(message)
 57.1395 -
 57.1396 -        from_address = safestr(from_address)
 57.1397 -        to_address = listify(to_address)    
 57.1398 -        cc = listify(kw.get('cc', []))
 57.1399 -        bcc = listify(kw.get('bcc', []))
 57.1400 -        recipients = to_address + cc + bcc
 57.1401 -
 57.1402 -        import email.Utils
 57.1403 -        self.from_address = email.Utils.parseaddr(from_address)[1]
 57.1404 -        self.recipients = [email.Utils.parseaddr(r)[1] for r in recipients]        
 57.1405 -    
 57.1406 -        self.headers = dictadd({
 57.1407 -          'From': from_address,
 57.1408 -          'To': ", ".join(to_address),
 57.1409 -          'Subject': subject
 57.1410 -        }, headers or {})
 57.1411 -
 57.1412 -        if cc:
 57.1413 -            self.headers['Cc'] = ", ".join(cc)
 57.1414 -    
 57.1415 -        self.message = self.new_message()
 57.1416 -        self.message.add_header("Content-Transfer-Encoding", "7bit")
 57.1417 -        self.message.add_header("Content-Disposition", "inline")
 57.1418 -        self.message.add_header("MIME-Version", "1.0")
 57.1419 -        self.message.set_payload(message, 'utf-8')
 57.1420 -        self.multipart = False
 57.1421 -        
 57.1422 -    def new_message(self):
 57.1423 -        from email.Message import Message
 57.1424 -        return Message()
 57.1425 -        
 57.1426 -    def attach(self, filename, content, content_type=None):
 57.1427 -        if not self.multipart:
 57.1428 -            msg = self.new_message()
 57.1429 -            msg.add_header("Content-Type", "multipart/mixed")
 57.1430 -            msg.attach(self.message)
 57.1431 -            self.message = msg
 57.1432 -            self.multipart = True
 57.1433 -                        
 57.1434 -        import mimetypes
 57.1435 -        try:
 57.1436 -            from email import encoders
 57.1437 -        except:
 57.1438 -            from email import Encoders as encoders
 57.1439 -            
 57.1440 -        content_type = content_type or mimetypes.guess_type(filename)[0] or "applcation/octet-stream"
 57.1441 -        
 57.1442 -        msg = self.new_message()
 57.1443 -        msg.set_payload(content)
 57.1444 -        msg.add_header('Content-Type', content_type)
 57.1445 -        msg.add_header('Content-Disposition', 'attachment', filename=filename)
 57.1446 -        
 57.1447 -        if not content_type.startswith("text/"):
 57.1448 -            encoders.encode_base64(msg)
 57.1449 -            
 57.1450 -        self.message.attach(msg)
 57.1451 -
 57.1452 -    def prepare_message(self):
 57.1453 -        for k, v in self.headers.iteritems():
 57.1454 -            if k.lower() == "content-type":
 57.1455 -                self.message.set_type(v)
 57.1456 -            else:
 57.1457 -                self.message.add_header(k, v)
 57.1458 -
 57.1459 -        self.headers = {}
 57.1460 -
 57.1461 -    def send(self):
 57.1462 -        try:
 57.1463 -            import webapi
 57.1464 -        except ImportError:
 57.1465 -            webapi = Storage(config=Storage())
 57.1466 -
 57.1467 -        self.prepare_message()
 57.1468 -        message_text = self.message.as_string()
 57.1469 -    
 57.1470 -        if webapi.config.get('smtp_server'):
 57.1471 -            server = webapi.config.get('smtp_server')
 57.1472 -            port = webapi.config.get('smtp_port', 0)
 57.1473 -            username = webapi.config.get('smtp_username') 
 57.1474 -            password = webapi.config.get('smtp_password')
 57.1475 -            debug_level = webapi.config.get('smtp_debuglevel', None)
 57.1476 -            starttls = webapi.config.get('smtp_starttls', False)
 57.1477 -
 57.1478 -            import smtplib
 57.1479 -            smtpserver = smtplib.SMTP(server, port)
 57.1480 -
 57.1481 -            if debug_level:
 57.1482 -                smtpserver.set_debuglevel(debug_level)
 57.1483 -
 57.1484 -            if starttls:
 57.1485 -                smtpserver.ehlo()
 57.1486 -                smtpserver.starttls()
 57.1487 -                smtpserver.ehlo()
 57.1488 -
 57.1489 -            if username and password:
 57.1490 -                smtpserver.login(username, password)
 57.1491 -
 57.1492 -            smtpserver.sendmail(self.from_address, self.recipients, message_text)
 57.1493 -            smtpserver.quit()
 57.1494 -        elif webapi.config.get('email_engine') == 'aws':
 57.1495 -            import boto.ses
 57.1496 -            c = boto.ses.SESConnection(
 57.1497 -              aws_access_key_id=webapi.config.get('aws_access_key_id'),
 57.1498 -              aws_secret_access_key=web.api.config.get('aws_secret_access_key'))
 57.1499 -            c.send_raw_email(self.from_address, message_text, self.from_recipients)
 57.1500 -        else:
 57.1501 -            sendmail = webapi.config.get('sendmail_path', '/usr/sbin/sendmail')
 57.1502 -        
 57.1503 -            assert not self.from_address.startswith('-'), 'security'
 57.1504 -            for r in self.recipients:
 57.1505 -                assert not r.startswith('-'), 'security'
 57.1506 -                
 57.1507 -            cmd = [sendmail, '-f', self.from_address] + self.recipients
 57.1508 -
 57.1509 -            if subprocess:
 57.1510 -                p = subprocess.Popen(cmd, stdin=subprocess.PIPE)
 57.1511 -                p.stdin.write(message_text)
 57.1512 -                p.stdin.close()
 57.1513 -                p.wait()
 57.1514 -            else:
 57.1515 -                i, o = os.popen2(cmd)
 57.1516 -                i.write(message)
 57.1517 -                i.close()
 57.1518 -                o.close()
 57.1519 -                del i, o
 57.1520 -                
 57.1521 -    def __repr__(self):
 57.1522 -        return "<EmailMessage>"
 57.1523 -    
 57.1524 -    def __str__(self):
 57.1525 -        return self.message.as_string()
 57.1526 -
 57.1527 -if __name__ == "__main__":
 57.1528 -    import doctest
 57.1529 -    doctest.testmod()
    58.1 Binary file OpenSecurity/install/web.py-0.37/web/utils.pyc has changed
    59.1 --- a/OpenSecurity/install/web.py-0.37/web/webapi.py	Thu Feb 20 15:40:48 2014 +0100
    59.2 +++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
    59.3 @@ -1,525 +0,0 @@
    59.4 -"""
    59.5 -Web API (wrapper around WSGI)
    59.6 -(from web.py)
    59.7 -"""
    59.8 -
    59.9 -__all__ = [
   59.10 -    "config",
   59.11 -    "header", "debug",
   59.12 -    "input", "data",
   59.13 -    "setcookie", "cookies",
   59.14 -    "ctx", 
   59.15 -    "HTTPError", 
   59.16 -
   59.17 -    # 200, 201, 202
   59.18 -    "OK", "Created", "Accepted",    
   59.19 -    "ok", "created", "accepted",
   59.20 -    
   59.21 -    # 301, 302, 303, 304, 307
   59.22 -    "Redirect", "Found", "SeeOther", "NotModified", "TempRedirect", 
   59.23 -    "redirect", "found", "seeother", "notmodified", "tempredirect",
   59.24 -
   59.25 -    # 400, 401, 403, 404, 405, 406, 409, 410, 412, 415
   59.26 -    "BadRequest", "Unauthorized", "Forbidden", "NotFound", "NoMethod", "NotAcceptable", "Conflict", "Gone", "PreconditionFailed", "UnsupportedMediaType",
   59.27 -    "badrequest", "unauthorized", "forbidden", "notfound", "nomethod", "notacceptable", "conflict", "gone", "preconditionfailed", "unsupportedmediatype",
   59.28 -
   59.29 -    # 500
   59.30 -    "InternalError", 
   59.31 -    "internalerror",
   59.32 -]
   59.33 -
   59.34 -import sys, cgi, Cookie, pprint, urlparse, urllib
   59.35 -from utils import storage, storify, threadeddict, dictadd, intget, safestr
   59.36 -
   59.37 -config = storage()
   59.38 -config.__doc__ = """
   59.39 -A configuration object for various aspects of web.py.
   59.40 -
   59.41 -`debug`
   59.42 -   : when True, enables reloading, disabled template caching and sets internalerror to debugerror.
   59.43 -"""
   59.44 -
   59.45 -class HTTPError(Exception):
   59.46 -    def __init__(self, status, headers={}, data=""):
   59.47 -        ctx.status = status
   59.48 -        for k, v in headers.items():
   59.49 -            header(k, v)
   59.50 -        self.data = data
   59.51 -        Exception.__init__(self, status)
   59.52 -        
   59.53 -def _status_code(status, data=None, classname=None, docstring=None):
   59.54 -    if data is None:
   59.55 -        data = status.split(" ", 1)[1]
   59.56 -    classname = status.split(" ", 1)[1].replace(' ', '') # 304 Not Modified -> NotModified    
   59.57 -    docstring = docstring or '`%s` status' % status
   59.58 -
   59.59 -    def __init__(self, data=data, headers={}):
   59.60 -        HTTPError.__init__(self, status, headers, data)
   59.61 -        
   59.62 -    # trick to create class dynamically with dynamic docstring.
   59.63 -    return type(classname, (HTTPError, object), {
   59.64 -        '__doc__': docstring,
   59.65 -        '__init__': __init__
   59.66 -    })
   59.67 -
   59.68 -ok = OK = _status_code("200 OK", data="")
   59.69 -created = Created = _status_code("201 Created")
   59.70 -accepted = Accepted = _status_code("202 Accepted")
   59.71 -
   59.72 -class Redirect(HTTPError):
   59.73 -    """A `301 Moved Permanently` redirect."""
   59.74 -    def __init__(self, url, status='301 Moved Permanently', absolute=False):
   59.75 -        """
   59.76 -        Returns a `status` redirect to the new URL. 
   59.77 -        `url` is joined with the base URL so that things like 
   59.78 -        `redirect("about") will work properly.
   59.79 -        """
   59.80 -        newloc = urlparse.urljoin(ctx.path, url)
   59.81 -
   59.82 -        if newloc.startswith('/'):
   59.83 -            if absolute:
   59.84 -                home = ctx.realhome
   59.85 -            else:
   59.86 -                home = ctx.home
   59.87 -            newloc = home + newloc
   59.88 -
   59.89 -        headers = {
   59.90 -            'Content-Type': 'text/html',
   59.91 -            'Location': newloc
   59.92 -        }
   59.93 -        HTTPError.__init__(self, status, headers, "")
   59.94 -
   59.95 -redirect = Redirect
   59.96 -
   59.97 -class Found(Redirect):
   59.98 -    """A `302 Found` redirect."""
   59.99 -    def __init__(self, url, absolute=False):
  59.100 -        Redirect.__init__(self, url, '302 Found', absolute=absolute)
  59.101 -
  59.102 -found = Found
  59.103 -
  59.104 -class SeeOther(Redirect):
  59.105 -    """A `303 See Other` redirect."""
  59.106 -    def __init__(self, url, absolute=False):
  59.107 -        Redirect.__init__(self, url, '303 See Other', absolute=absolute)
  59.108 -    
  59.109 -seeother = SeeOther
  59.110 -
  59.111 -class NotModified(HTTPError):
  59.112 -    """A `304 Not Modified` status."""
  59.113 -    def __init__(self):
  59.114 -        HTTPError.__init__(self, "304 Not Modified")
  59.115 -
  59.116 -notmodified = NotModified
  59.117 -
  59.118 -class TempRedirect(Redirect):
  59.119 -    """A `307 Temporary Redirect` redirect."""
  59.120 -    def __init__(self, url, absolute=False):
  59.121 -        Redirect.__init__(self, url, '307 Temporary Redirect', absolute=absolute)
  59.122 -
  59.123 -tempredirect = TempRedirect
  59.124 -
  59.125 -class BadRequest(HTTPError):
  59.126 -    """`400 Bad Request` error."""
  59.127 -    message = "bad request"
  59.128 -    def __init__(self, message=None):
  59.129 -        status = "400 Bad Request"
  59.130 -        headers = {'Content-Type': 'text/html'}
  59.131 -        HTTPError.__init__(self, status, headers, message or self.message)
  59.132 -
  59.133 -badrequest = BadRequest
  59.134 -
  59.135 -class Unauthorized(HTTPError):
  59.136 -    """`401 Unauthorized` error."""
  59.137 -    message = "unauthorized"
  59.138 -    def __init__(self):
  59.139 -        status = "401 Unauthorized"
  59.140 -        headers = {'Content-Type': 'text/html'}
  59.141 -        HTTPError.__init__(self, status, headers, self.message)
  59.142 -
  59.143 -unauthorized = Unauthorized
  59.144 -
  59.145 -class Forbidden(HTTPError):
  59.146 -    """`403 Forbidden` error."""
  59.147 -    message = "forbidden"
  59.148 -    def __init__(self):
  59.149 -        status = "403 Forbidden"
  59.150 -        headers = {'Content-Type': 'text/html'}
  59.151 -        HTTPError.__init__(self, status, headers, self.message)
  59.152 -
  59.153 -forbidden = Forbidden
  59.154 -
  59.155 -class _NotFound(HTTPError):
  59.156 -    """`404 Not Found` error."""
  59.157 -    message = "not found"
  59.158 -    def __init__(self, message=None):
  59.159 -        status = '404 Not Found'
  59.160 -        headers = {'Content-Type': 'text/html'}
  59.161 -        HTTPError.__init__(self, status, headers, message or self.message)
  59.162 -
  59.163 -def NotFound(message=None):
  59.164 -    """Returns HTTPError with '404 Not Found' error from the active application.
  59.165 -    """
  59.166 -    if message:
  59.167 -        return _NotFound(message)
  59.168 -    elif ctx.get('app_stack'):
  59.169 -        return ctx.app_stack[-1].notfound()
  59.170 -    else:
  59.171 -        return _NotFound()
  59.172 -
  59.173 -notfound = NotFound
  59.174 -
  59.175 -class NoMethod(HTTPError):
  59.176 -    """A `405 Method Not Allowed` error."""
  59.177 -    def __init__(self, cls=None):
  59.178 -        status = '405 Method Not Allowed'
  59.179 -        headers = {}
  59.180 -        headers['Content-Type'] = 'text/html'
  59.181 -        
  59.182 -        methods = ['GET', 'HEAD', 'POST', 'PUT', 'DELETE']
  59.183 -        if cls:
  59.184 -            methods = [method for method in methods if hasattr(cls, method)]
  59.185 -
  59.186 -        headers['Allow'] = ', '.join(methods)
  59.187 -        data = None
  59.188 -        HTTPError.__init__(self, status, headers, data)
  59.189 -        
  59.190 -nomethod = NoMethod
  59.191 -
  59.192 -class NotAcceptable(HTTPError):
  59.193 -    """`406 Not Acceptable` error."""
  59.194 -    message = "not acceptable"
  59.195 -    def __init__(self):
  59.196 -        status = "406 Not Acceptable"
  59.197 -        headers = {'Content-Type': 'text/html'}
  59.198 -        HTTPError.__init__(self, status, headers, self.message)
  59.199 -
  59.200 -notacceptable = NotAcceptable
  59.201 -
  59.202 -class Conflict(HTTPError):
  59.203 -    """`409 Conflict` error."""
  59.204 -    message = "conflict"
  59.205 -    def __init__(self):
  59.206 -        status = "409 Conflict"
  59.207 -        headers = {'Content-Type': 'text/html'}
  59.208 -        HTTPError.__init__(self, status, headers, self.message)
  59.209 -
  59.210 -conflict = Conflict
  59.211 -
  59.212 -class Gone(HTTPError):
  59.213 -    """`410 Gone` error."""
  59.214 -    message = "gone"
  59.215 -    def __init__(self):
  59.216 -        status = '410 Gone'
  59.217 -        headers = {'Content-Type': 'text/html'}
  59.218 -        HTTPError.__init__(self, status, headers, self.message)
  59.219 -
  59.220 -gone = Gone
  59.221 -
  59.222 -class PreconditionFailed(HTTPError):
  59.223 -    """`412 Precondition Failed` error."""
  59.224 -    message = "precondition failed"
  59.225 -    def __init__(self):
  59.226 -        status = "412 Precondition Failed"
  59.227 -        headers = {'Content-Type': 'text/html'}
  59.228 -        HTTPError.__init__(self, status, headers, self.message)
  59.229 -
  59.230 -preconditionfailed = PreconditionFailed
  59.231 -
  59.232 -class UnsupportedMediaType(HTTPError):
  59.233 -    """`415 Unsupported Media Type` error."""
  59.234 -    message = "unsupported media type"
  59.235 -    def __init__(self):
  59.236 -        status = "415 Unsupported Media Type"
  59.237 -        headers = {'Content-Type': 'text/html'}
  59.238 -        HTTPError.__init__(self, status, headers, self.message)
  59.239 -
  59.240 -unsupportedmediatype = UnsupportedMediaType
  59.241 -
  59.242 -class _InternalError(HTTPError):
  59.243 -    """500 Internal Server Error`."""
  59.244 -    message = "internal server error"
  59.245 -    
  59.246 -    def __init__(self, message=None):
  59.247 -        status = '500 Internal Server Error'
  59.248 -        headers = {'Content-Type': 'text/html'}
  59.249 -        HTTPError.__init__(self, status, headers, message or self.message)
  59.250 -
  59.251 -def InternalError(message=None):
  59.252 -    """Returns HTTPError with '500 internal error' error from the active application.
  59.253 -    """
  59.254 -    if message:
  59.255 -        return _InternalError(message)
  59.256 -    elif ctx.get('app_stack'):
  59.257 -        return ctx.app_stack[-1].internalerror()
  59.258 -    else:
  59.259 -        return _InternalError()
  59.260 -
  59.261 -internalerror = InternalError
  59.262 -
  59.263 -def header(hdr, value, unique=False):
  59.264 -    """
  59.265 -    Adds the header `hdr: value` with the response.
  59.266 -    
  59.267 -    If `unique` is True and a header with that name already exists,
  59.268 -    it doesn't add a new one. 
  59.269 -    """
  59.270 -    hdr, value = safestr(hdr), safestr(value)
  59.271 -    # protection against HTTP response splitting attack
  59.272 -    if '\n' in hdr or '\r' in hdr or '\n' in value or '\r' in value:
  59.273 -        raise ValueError, 'invalid characters in header'
  59.274 -        
  59.275 -    if unique is True:
  59.276 -        for h, v in ctx.headers:
  59.277 -            if h.lower() == hdr.lower(): return
  59.278 -    
  59.279 -    ctx.headers.append((hdr, value))
  59.280 -    
  59.281 -def rawinput(method=None):
  59.282 -    """Returns storage object with GET or POST arguments.
  59.283 -    """
  59.284 -    method = method or "both"
  59.285 -    from cStringIO import StringIO
  59.286 -
  59.287 -    def dictify(fs): 
  59.288 -        # hack to make web.input work with enctype='text/plain.
  59.289 -        if fs.list is None:
  59.290 -            fs.list = [] 
  59.291 -
  59.292 -        return dict([(k, fs[k]) for k in fs.keys()])
  59.293 -    
  59.294 -    e = ctx.env.copy()
  59.295 -    a = b = {}
  59.296 -    
  59.297 -    if method.lower() in ['both', 'post', 'put']:
  59.298 -        if e['REQUEST_METHOD'] in ['POST', 'PUT']:
  59.299 -            if e.get('CONTENT_TYPE', '').lower().startswith('multipart/'):
  59.300 -                # since wsgi.input is directly passed to cgi.FieldStorage, 
  59.301 -                # it can not be called multiple times. Saving the FieldStorage
  59.302 -                # object in ctx to allow calling web.input multiple times.
  59.303 -                a = ctx.get('_fieldstorage')
  59.304 -                if not a:
  59.305 -                    fp = e['wsgi.input']
  59.306 -                    a = cgi.FieldStorage(fp=fp, environ=e, keep_blank_values=1)
  59.307 -                    ctx._fieldstorage = a
  59.308 -            else:
  59.309 -                fp = StringIO(data())
  59.310 -                a = cgi.FieldStorage(fp=fp, environ=e, keep_blank_values=1)
  59.311 -            a = dictify(a)
  59.312 -
  59.313 -    if method.lower() in ['both', 'get']:
  59.314 -        e['REQUEST_METHOD'] = 'GET'
  59.315 -        b = dictify(cgi.FieldStorage(environ=e, keep_blank_values=1))
  59.316 -
  59.317 -    def process_fieldstorage(fs):
  59.318 -        if isinstance(fs, list):
  59.319 -            return [process_fieldstorage(x) for x in fs]
  59.320 -        elif fs.filename is None:
  59.321 -            return fs.value
  59.322 -        else:
  59.323 -            return fs
  59.324 -
  59.325 -    return storage([(k, process_fieldstorage(v)) for k, v in dictadd(b, a).items()])
  59.326 -
  59.327 -def input(*requireds, **defaults):
  59.328 -    """
  59.329 -    Returns a `storage` object with the GET and POST arguments. 
  59.330 -    See `storify` for how `requireds` and `defaults` work.
  59.331 -    """
  59.332 -    _method = defaults.pop('_method', 'both')
  59.333 -    out = rawinput(_method)
  59.334 -    try:
  59.335 -        defaults.setdefault('_unicode', True) # force unicode conversion by default.
  59.336 -        return storify(out, *requireds, **defaults)
  59.337 -    except KeyError:
  59.338 -        raise badrequest()
  59.339 -
  59.340 -def data():
  59.341 -    """Returns the data sent with the request."""
  59.342 -    if 'data' not in ctx:
  59.343 -        cl = intget(ctx.env.get('CONTENT_LENGTH'), 0)
  59.344 -        ctx.data = ctx.env['wsgi.input'].read(cl)
  59.345 -    return ctx.data
  59.346 -
  59.347 -def setcookie(name, value, expires='', domain=None,
  59.348 -              secure=False, httponly=False, path=None):
  59.349 -    """Sets a cookie."""
  59.350 -    morsel = Cookie.Morsel()
  59.351 -    name, value = safestr(name), safestr(value)
  59.352 -    morsel.set(name, value, urllib.quote(value))
  59.353 -    if expires < 0:
  59.354 -        expires = -1000000000
  59.355 -    morsel['expires'] = expires
  59.356 -    morsel['path'] = path or ctx.homepath+'/'
  59.357 -    if domain:
  59.358 -        morsel['domain'] = domain
  59.359 -    if secure:
  59.360 -        morsel['secure'] = secure
  59.361 -    value = morsel.OutputString()
  59.362 -    if httponly:
  59.363 -        value += '; httponly'
  59.364 -    header('Set-Cookie', value)
  59.365 -        
  59.366 -def decode_cookie(value):
  59.367 -    r"""Safely decodes a cookie value to unicode. 
  59.368 -    
  59.369 -    Tries us-ascii, utf-8 and io8859 encodings, in that order.
  59.370 -
  59.371 -    >>> decode_cookie('')
  59.372 -    u''
  59.373 -    >>> decode_cookie('asdf')
  59.374 -    u'asdf'
  59.375 -    >>> decode_cookie('foo \xC3\xA9 bar')
  59.376 -    u'foo \xe9 bar'
  59.377 -    >>> decode_cookie('foo \xE9 bar')
  59.378 -    u'foo \xe9 bar'
  59.379 -    """
  59.380 -    try:
  59.381 -        # First try plain ASCII encoding
  59.382 -        return unicode(value, 'us-ascii')
  59.383 -    except UnicodeError:
  59.384 -        # Then try UTF-8, and if that fails, ISO8859
  59.385 -        try:
  59.386 -            return unicode(value, 'utf-8')
  59.387 -        except UnicodeError:
  59.388 -            return unicode(value, 'iso8859', 'ignore')
  59.389 -
  59.390 -def parse_cookies(http_cookie):
  59.391 -    r"""Parse a HTTP_COOKIE header and return dict of cookie names and decoded values.
  59.392 -        
  59.393 -    >>> sorted(parse_cookies('').items())
  59.394 -    []
  59.395 -    >>> sorted(parse_cookies('a=1').items())
  59.396 -    [('a', '1')]
  59.397 -    >>> sorted(parse_cookies('a=1%202').items())
  59.398 -    [('a', '1 2')]
  59.399 -    >>> sorted(parse_cookies('a=Z%C3%A9Z').items())
  59.400 -    [('a', 'Z\xc3\xa9Z')]
  59.401 -    >>> sorted(parse_cookies('a=1; b=2; c=3').items())
  59.402 -    [('a', '1'), ('b', '2'), ('c', '3')]
  59.403 -    >>> sorted(parse_cookies('a=1; b=w("x")|y=z; c=3').items())
  59.404 -    [('a', '1'), ('b', 'w('), ('c', '3')]
  59.405 -    >>> sorted(parse_cookies('a=1; b=w(%22x%22)|y=z; c=3').items())
  59.406 -    [('a', '1'), ('b', 'w("x")|y=z'), ('c', '3')]
  59.407 -
  59.408 -    >>> sorted(parse_cookies('keebler=E=mc2').items())
  59.409 -    [('keebler', 'E=mc2')]
  59.410 -    >>> sorted(parse_cookies(r'keebler="E=mc2; L=\"Loves\"; fudge=\012;"').items())
  59.411 -    [('keebler', 'E=mc2; L="Loves"; fudge=\n;')]
  59.412 -    """
  59.413 -    #print "parse_cookies"
  59.414 -    if '"' in http_cookie:
  59.415 -        # HTTP_COOKIE has quotes in it, use slow but correct cookie parsing
  59.416 -        cookie = Cookie.SimpleCookie()
  59.417 -        try:
  59.418 -            cookie.load(http_cookie)
  59.419 -        except Cookie.CookieError:
  59.420 -            # If HTTP_COOKIE header is malformed, try at least to load the cookies we can by
  59.421 -            # first splitting on ';' and loading each attr=value pair separately
  59.422 -            cookie = Cookie.SimpleCookie()
  59.423 -            for attr_value in http_cookie.split(';'):
  59.424 -                try:
  59.425 -                    cookie.load(attr_value)
  59.426 -                except Cookie.CookieError:
  59.427 -                    pass
  59.428 -        cookies = dict((k, urllib.unquote(v.value)) for k, v in cookie.iteritems())
  59.429 -    else:
  59.430 -        # HTTP_COOKIE doesn't have quotes, use fast cookie parsing
  59.431 -        cookies = {}
  59.432 -        for key_value in http_cookie.split(';'):
  59.433 -            key_value = key_value.split('=', 1)
  59.434 -            if len(key_value) == 2:
  59.435 -                key, value = key_value
  59.436 -                cookies[key.strip()] = urllib.unquote(value.strip())
  59.437 -    return cookies
  59.438 -
  59.439 -def cookies(*requireds, **defaults):
  59.440 -    r"""Returns a `storage` object with all the request cookies in it.
  59.441 -    
  59.442 -    See `storify` for how `requireds` and `defaults` work.
  59.443 -
  59.444 -    This is forgiving on bad HTTP_COOKIE input, it tries to parse at least
  59.445 -    the cookies it can.
  59.446 -    
  59.447 -    The values are converted to unicode if _unicode=True is passed.
  59.448 -    """
  59.449 -    # If _unicode=True is specified, use decode_cookie to convert cookie value to unicode 
  59.450 -    if defaults.get("_unicode") is True:
  59.451 -        defaults['_unicode'] = decode_cookie
  59.452 -        
  59.453 -    # parse cookie string and cache the result for next time.
  59.454 -    if '_parsed_cookies' not in ctx:
  59.455 -        http_cookie = ctx.env.get("HTTP_COOKIE", "")
  59.456 -        ctx._parsed_cookies = parse_cookies(http_cookie)
  59.457 -
  59.458 -    try:
  59.459 -        return storify(ctx._parsed_cookies, *requireds, **defaults)
  59.460 -    except KeyError:
  59.461 -        badrequest()
  59.462 -        raise StopIteration
  59.463 -
  59.464 -def debug(*args):
  59.465 -    """
  59.466 -    Prints a prettyprinted version of `args` to stderr.
  59.467 -    """
  59.468 -    try: 
  59.469 -        out = ctx.environ['wsgi.errors']
  59.470 -    except: 
  59.471 -        out = sys.stderr
  59.472 -    for arg in args:
  59.473 -        print >> out, pprint.pformat(arg)
  59.474 -    return ''
  59.475 -
  59.476 -def _debugwrite(x):
  59.477 -    try: 
  59.478 -        out = ctx.environ['wsgi.errors']
  59.479 -    except: 
  59.480 -        out = sys.stderr
  59.481 -    out.write(x)
  59.482 -debug.write = _debugwrite
  59.483 -
  59.484 -ctx = context = threadeddict()
  59.485 -
  59.486 -ctx.__doc__ = """
  59.487 -A `storage` object containing various information about the request:
  59.488 -  
  59.489 -`environ` (aka `env`)
  59.490 -   : A dictionary containing the standard WSGI environment variables.
  59.491 -
  59.492 -`host`
  59.493 -   : The domain (`Host` header) requested by the user.
  59.494 -
  59.495 -`home`
  59.496 -   : The base path for the application.
  59.497 -
  59.498 -`ip`
  59.499 -   : The IP address of the requester.
  59.500 -
  59.501 -`method`
  59.502 -   : The HTTP method used.
  59.503 -
  59.504 -`path`
  59.505 -   : The path request.
  59.506 -   
  59.507 -`query`
  59.508 -   : If there are no query arguments, the empty string. Otherwise, a `?` followed
  59.509 -     by the query string.
  59.510 -
  59.511 -`fullpath`
  59.512 -   : The full path requested, including query arguments (`== path + query`).
  59.513 -
  59.514 -### Response Data
  59.515 -
  59.516 -`status` (default: "200 OK")
  59.517 -   : The status code to be used in the response.
  59.518 -
  59.519 -`headers`
  59.520 -   : A list of 2-tuples to be used in the response.
  59.521 -
  59.522 -`output`
  59.523 -   : A string to be used as the response.
  59.524 -"""
  59.525 -
  59.526 -if __name__ == "__main__":
  59.527 -    import doctest
  59.528 -    doctest.testmod()
  59.529 \ No newline at end of file
    60.1 Binary file OpenSecurity/install/web.py-0.37/web/webapi.pyc has changed
    61.1 --- a/OpenSecurity/install/web.py-0.37/web/webopenid.py	Thu Feb 20 15:40:48 2014 +0100
    61.2 +++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
    61.3 @@ -1,115 +0,0 @@
    61.4 -"""openid.py: an openid library for web.py
    61.5 -
    61.6 -Notes:
    61.7 -
    61.8 - - This will create a file called .openid_secret_key in the 
    61.9 -   current directory with your secret key in it. If someone 
   61.10 -   has access to this file they can log in as any user. And 
   61.11 -   if the app can't find this file for any reason (e.g. you 
   61.12 -   moved the app somewhere else) then each currently logged 
   61.13 -   in user will get logged out.
   61.14 -
   61.15 - - State must be maintained through the entire auth process 
   61.16 -   -- this means that if you have multiple web.py processes 
   61.17 -   serving one set of URLs or if you restart your app often 
   61.18 -   then log ins will fail. You have to replace sessions and 
   61.19 -   store for things to work.
   61.20 -
   61.21 - - We set cookies starting with "openid_".
   61.22 -
   61.23 -"""
   61.24 -
   61.25 -import os
   61.26 -import random
   61.27 -import hmac
   61.28 -import __init__ as web
   61.29 -import openid.consumer.consumer
   61.30 -import openid.store.memstore
   61.31 -
   61.32 -sessions = {}
   61.33 -store = openid.store.memstore.MemoryStore()
   61.34 -
   61.35 -def _secret():
   61.36 -    try:
   61.37 -        secret = file('.openid_secret_key').read()
   61.38 -    except IOError:
   61.39 -        # file doesn't exist
   61.40 -        secret = os.urandom(20)
   61.41 -        file('.openid_secret_key', 'w').write(secret)
   61.42 -    return secret
   61.43 -
   61.44 -def _hmac(identity_url):
   61.45 -    return hmac.new(_secret(), identity_url).hexdigest()
   61.46 -
   61.47 -def _random_session():
   61.48 -    n = random.random()
   61.49 -    while n in sessions:
   61.50 -        n = random.random()
   61.51 -    n = str(n)
   61.52 -    return n
   61.53 -
   61.54 -def status():
   61.55 -    oid_hash = web.cookies().get('openid_identity_hash', '').split(',', 1)
   61.56 -    if len(oid_hash) > 1:
   61.57 -        oid_hash, identity_url = oid_hash
   61.58 -        if oid_hash == _hmac(identity_url):
   61.59 -            return identity_url
   61.60 -    return None
   61.61 -
   61.62 -def form(openid_loc):
   61.63 -    oid = status()
   61.64 -    if oid:
   61.65 -        return '''
   61.66 -        <form method="post" action="%s">
   61.67 -          <img src="http://openid.net/login-bg.gif" alt="OpenID" />
   61.68 -          <strong>%s</strong>
   61.69 -          <input type="hidden" name="action" value="logout" />
   61.70 -          <input type="hidden" name="return_to" value="%s" />
   61.71 -          <button type="submit">log out</button>
   61.72 -        </form>''' % (openid_loc, oid, web.ctx.fullpath)
   61.73 -    else:
   61.74 -        return '''
   61.75 -        <form method="post" action="%s">
   61.76 -          <input type="text" name="openid" value="" 
   61.77 -            style="background: url(http://openid.net/login-bg.gif) no-repeat; padding-left: 18px; background-position: 0 50%%;" />
   61.78 -          <input type="hidden" name="return_to" value="%s" />
   61.79 -          <button type="submit">log in</button>
   61.80 -        </form>''' % (openid_loc, web.ctx.fullpath)
   61.81 -
   61.82 -def logout():
   61.83 -    web.setcookie('openid_identity_hash', '', expires=-1)
   61.84 -
   61.85 -class host:
   61.86 -    def POST(self):
   61.87 -        # unlike the usual scheme of things, the POST is actually called
   61.88 -        # first here
   61.89 -        i = web.input(return_to='/')
   61.90 -        if i.get('action') == 'logout':
   61.91 -            logout()
   61.92 -            return web.redirect(i.return_to)
   61.93 -
   61.94 -        i = web.input('openid', return_to='/')
   61.95 -
   61.96 -        n = _random_session()
   61.97 -        sessions[n] = {'webpy_return_to': i.return_to}
   61.98 -        
   61.99 -        c = openid.consumer.consumer.Consumer(sessions[n], store)
  61.100 -        a = c.begin(i.openid)
  61.101 -        f = a.redirectURL(web.ctx.home, web.ctx.home + web.ctx.fullpath)
  61.102 -
  61.103 -        web.setcookie('openid_session_id', n)
  61.104 -        return web.redirect(f)
  61.105 -
  61.106 -    def GET(self):
  61.107 -        n = web.cookies('openid_session_id').openid_session_id
  61.108 -        web.setcookie('openid_session_id', '', expires=-1)
  61.109 -        return_to = sessions[n]['webpy_return_to']
  61.110 -
  61.111 -        c = openid.consumer.consumer.Consumer(sessions[n], store)
  61.112 -        a = c.complete(web.input(), web.ctx.home + web.ctx.fullpath)
  61.113 -
  61.114 -        if a.status.lower() == 'success':
  61.115 -            web.setcookie('openid_identity_hash', _hmac(a.identity_url) + ',' + a.identity_url)
  61.116 -
  61.117 -        del sessions[n]
  61.118 -        return web.redirect(return_to)
    62.1 Binary file OpenSecurity/install/web.py-0.37/web/webopenid.pyc has changed
    63.1 --- a/OpenSecurity/install/web.py-0.37/web/wsgi.py	Thu Feb 20 15:40:48 2014 +0100
    63.2 +++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
    63.3 @@ -1,70 +0,0 @@
    63.4 -"""
    63.5 -WSGI Utilities
    63.6 -(from web.py)
    63.7 -"""
    63.8 -
    63.9 -import os, sys
   63.10 -
   63.11 -import http
   63.12 -import webapi as web
   63.13 -from utils import listget
   63.14 -from net import validaddr, validip
   63.15 -import httpserver
   63.16 -    
   63.17 -def runfcgi(func, addr=('localhost', 8000)):
   63.18 -    """Runs a WSGI function as a FastCGI server."""
   63.19 -    import flup.server.fcgi as flups
   63.20 -    return flups.WSGIServer(func, multiplexed=True, bindAddress=addr, debug=False).run()
   63.21 -
   63.22 -def runscgi(func, addr=('localhost', 4000)):
   63.23 -    """Runs a WSGI function as an SCGI server."""
   63.24 -    import flup.server.scgi as flups
   63.25 -    return flups.WSGIServer(func, bindAddress=addr, debug=False).run()
   63.26 -
   63.27 -def runwsgi(func):
   63.28 -    """
   63.29 -    Runs a WSGI-compatible `func` using FCGI, SCGI, or a simple web server,
   63.30 -    as appropriate based on context and `sys.argv`.
   63.31 -    """
   63.32 -    
   63.33 -    if os.environ.has_key('SERVER_SOFTWARE'): # cgi
   63.34 -        os.environ['FCGI_FORCE_CGI'] = 'Y'
   63.35 -
   63.36 -    if (os.environ.has_key('PHP_FCGI_CHILDREN') #lighttpd fastcgi
   63.37 -      or os.environ.has_key('SERVER_SOFTWARE')):
   63.38 -        return runfcgi(func, None)
   63.39 -    
   63.40 -    if 'fcgi' in sys.argv or 'fastcgi' in sys.argv:
   63.41 -        args = sys.argv[1:]
   63.42 -        if 'fastcgi' in args: args.remove('fastcgi')
   63.43 -        elif 'fcgi' in args: args.remove('fcgi')
   63.44 -        if args:
   63.45 -            return runfcgi(func, validaddr(args[0]))
   63.46 -        else:
   63.47 -            return runfcgi(func, None)
   63.48 -    
   63.49 -    if 'scgi' in sys.argv:
   63.50 -        args = sys.argv[1:]
   63.51 -        args.remove('scgi')
   63.52 -        if args:
   63.53 -            return runscgi(func, validaddr(args[0]))
   63.54 -        else:
   63.55 -            return runscgi(func)
   63.56 -    
   63.57 -    return httpserver.runsimple(func, validip(listget(sys.argv, 1, '')))
   63.58 -    
   63.59 -def _is_dev_mode():
   63.60 -    # Some embedded python interpreters won't have sys.arv
   63.61 -    # For details, see https://github.com/webpy/webpy/issues/87
   63.62 -    argv = getattr(sys, "argv", [])
   63.63 -
   63.64 -    # quick hack to check if the program is running in dev mode.
   63.65 -    if os.environ.has_key('SERVER_SOFTWARE') \
   63.66 -        or os.environ.has_key('PHP_FCGI_CHILDREN') \
   63.67 -        or 'fcgi' in argv or 'fastcgi' in argv \
   63.68 -        or 'mod_wsgi' in argv:
   63.69 -            return False
   63.70 -    return True
   63.71 -
   63.72 -# When running the builtin-server, enable debug mode if not already set.
   63.73 -web.config.setdefault('debug', _is_dev_mode())
    64.1 Binary file OpenSecurity/install/web.py-0.37/web/wsgi.pyc has changed
    65.1 --- a/OpenSecurity/install/web.py-0.37/web/wsgiserver/__init__.py	Thu Feb 20 15:40:48 2014 +0100
    65.2 +++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
    65.3 @@ -1,2219 +0,0 @@
    65.4 -"""A high-speed, production ready, thread pooled, generic HTTP server.
    65.5 -
    65.6 -Simplest example on how to use this module directly
    65.7 -(without using CherryPy's application machinery)::
    65.8 -
    65.9 -    from cherrypy import wsgiserver
   65.10 -    
   65.11 -    def my_crazy_app(environ, start_response):
   65.12 -        status = '200 OK'
   65.13 -        response_headers = [('Content-type','text/plain')]
   65.14 -        start_response(status, response_headers)
   65.15 -        return ['Hello world!']
   65.16 -    
   65.17 -    server = wsgiserver.CherryPyWSGIServer(
   65.18 -                ('0.0.0.0', 8070), my_crazy_app,
   65.19 -                server_name='www.cherrypy.example')
   65.20 -    server.start()
   65.21 -    
   65.22 -The CherryPy WSGI server can serve as many WSGI applications 
   65.23 -as you want in one instance by using a WSGIPathInfoDispatcher::
   65.24 -    
   65.25 -    d = WSGIPathInfoDispatcher({'/': my_crazy_app, '/blog': my_blog_app})
   65.26 -    server = wsgiserver.CherryPyWSGIServer(('0.0.0.0', 80), d)
   65.27 -    
   65.28 -Want SSL support? Just set server.ssl_adapter to an SSLAdapter instance.
   65.29 -
   65.30 -This won't call the CherryPy engine (application side) at all, only the
   65.31 -HTTP server, which is independent from the rest of CherryPy. Don't
   65.32 -let the name "CherryPyWSGIServer" throw you; the name merely reflects
   65.33 -its origin, not its coupling.
   65.34 -
   65.35 -For those of you wanting to understand internals of this module, here's the
   65.36 -basic call flow. The server's listening thread runs a very tight loop,
   65.37 -sticking incoming connections onto a Queue::
   65.38 -
   65.39 -    server = CherryPyWSGIServer(...)
   65.40 -    server.start()
   65.41 -    while True:
   65.42 -        tick()
   65.43 -        # This blocks until a request comes in:
   65.44 -        child = socket.accept()
   65.45 -        conn = HTTPConnection(child, ...)
   65.46 -        server.requests.put(conn)
   65.47 -
   65.48 -Worker threads are kept in a pool and poll the Queue, popping off and then
   65.49 -handling each connection in turn. Each connection can consist of an arbitrary
   65.50 -number of requests and their responses, so we run a nested loop::
   65.51 -
   65.52 -    while True:
   65.53 -        conn = server.requests.get()
   65.54 -        conn.communicate()
   65.55 -        ->  while True:
   65.56 -                req = HTTPRequest(...)
   65.57 -                req.parse_request()
   65.58 -                ->  # Read the Request-Line, e.g. "GET /page HTTP/1.1"
   65.59 -                    req.rfile.readline()
   65.60 -                    read_headers(req.rfile, req.inheaders)
   65.61 -                req.respond()
   65.62 -                ->  response = app(...)
   65.63 -                    try:
   65.64 -                        for chunk in response:
   65.65 -                            if chunk:
   65.66 -                                req.write(chunk)
   65.67 -                    finally:
   65.68 -                        if hasattr(response, "close"):
   65.69 -                            response.close()
   65.70 -                if req.close_connection:
   65.71 -                    return
   65.72 -"""
   65.73 -
   65.74 -CRLF = '\r\n'
   65.75 -import os
   65.76 -import Queue
   65.77 -import re
   65.78 -quoted_slash = re.compile("(?i)%2F")
   65.79 -import rfc822
   65.80 -import socket
   65.81 -import sys
   65.82 -if 'win' in sys.platform and not hasattr(socket, 'IPPROTO_IPV6'):
   65.83 -    socket.IPPROTO_IPV6 = 41
   65.84 -try:
   65.85 -    import cStringIO as StringIO
   65.86 -except ImportError:
   65.87 -    import StringIO
   65.88 -DEFAULT_BUFFER_SIZE = -1
   65.89 -
   65.90 -_fileobject_uses_str_type = isinstance(socket._fileobject(None)._rbuf, basestring)
   65.91 -
   65.92 -import threading
   65.93 -import time
   65.94 -import traceback
   65.95 -def format_exc(limit=None):
   65.96 -    """Like print_exc() but return a string. Backport for Python 2.3."""
   65.97 -    try:
   65.98 -        etype, value, tb = sys.exc_info()
   65.99 -        return ''.join(traceback.format_exception(etype, value, tb, limit))
  65.100 -    finally:
  65.101 -        etype = value = tb = None
  65.102 -
  65.103 -
  65.104 -from urllib import unquote
  65.105 -from urlparse import urlparse
  65.106 -import warnings
  65.107 -
  65.108 -import errno
  65.109 -
  65.110 -def plat_specific_errors(*errnames):
  65.111 -    """Return error numbers for all errors in errnames on this platform.
  65.112 -    
  65.113 -    The 'errno' module contains different global constants depending on
  65.114 -    the specific platform (OS). This function will return the list of
  65.115 -    numeric values for a given list of potential names.
  65.116 -    """
  65.117 -    errno_names = dir(errno)
  65.118 -    nums = [getattr(errno, k) for k in errnames if k in errno_names]
  65.119 -    # de-dupe the list
  65.120 -    return dict.fromkeys(nums).keys()
  65.121 -
  65.122 -socket_error_eintr = plat_specific_errors("EINTR", "WSAEINTR")
  65.123 -
  65.124 -socket_errors_to_ignore = plat_specific_errors(
  65.125 -    "EPIPE",
  65.126 -    "EBADF", "WSAEBADF",
  65.127 -    "ENOTSOCK", "WSAENOTSOCK",
  65.128 -    "ETIMEDOUT", "WSAETIMEDOUT",
  65.129 -    "ECONNREFUSED", "WSAECONNREFUSED",
  65.130 -    "ECONNRESET", "WSAECONNRESET",
  65.131 -    "ECONNABORTED", "WSAECONNABORTED",
  65.132 -    "ENETRESET", "WSAENETRESET",
  65.133 -    "EHOSTDOWN", "EHOSTUNREACH",
  65.134 -    )
  65.135 -socket_errors_to_ignore.append("timed out")
  65.136 -socket_errors_to_ignore.append("The read operation timed out")
  65.137 -
  65.138 -socket_errors_nonblocking = plat_specific_errors(
  65.139 -    'EAGAIN', 'EWOULDBLOCK', 'WSAEWOULDBLOCK')
  65.140 -
  65.141 -comma_separated_headers = ['Accept', 'Accept-Charset', 'Accept-Encoding',
  65.142 -    'Accept-Language', 'Accept-Ranges', 'Allow', 'Cache-Control',
  65.143 -    'Connection', 'Content-Encoding', 'Content-Language', 'Expect',
  65.144 -    'If-Match', 'If-None-Match', 'Pragma', 'Proxy-Authenticate', 'TE',
  65.145 -    'Trailer', 'Transfer-Encoding', 'Upgrade', 'Vary', 'Via', 'Warning',
  65.146 -    'WWW-Authenticate']
  65.147 -
  65.148 -
  65.149 -import logging
  65.150 -if not hasattr(logging, 'statistics'): logging.statistics = {}
  65.151 -
  65.152 -
  65.153 -def read_headers(rfile, hdict=None):
  65.154 -    """Read headers from the given stream into the given header dict.
  65.155 -    
  65.156 -    If hdict is None, a new header dict is created. Returns the populated
  65.157 -    header dict.
  65.158 -    
  65.159 -    Headers which are repeated are folded together using a comma if their
  65.160 -    specification so dictates.
  65.161 -    
  65.162 -    This function raises ValueError when the read bytes violate the HTTP spec.
  65.163 -    You should probably return "400 Bad Request" if this happens.
  65.164 -    """
  65.165 -    if hdict is None:
  65.166 -        hdict = {}
  65.167 -    
  65.168 -    while True:
  65.169 -        line = rfile.readline()
  65.170 -        if not line:
  65.171 -            # No more data--illegal end of headers
  65.172 -            raise ValueError("Illegal end of headers.")
  65.173 -        
  65.174 -        if line == CRLF:
  65.175 -            # Normal end of headers
  65.176 -            break
  65.177 -        if not line.endswith(CRLF):
  65.178 -            raise ValueError("HTTP requires CRLF terminators")
  65.179 -        
  65.180 -        if line[0] in ' \t':
  65.181 -            # It's a continuation line.
  65.182 -            v = line.strip()
  65.183 -        else:
  65.184 -            try:
  65.185 -                k, v = line.split(":", 1)
  65.186 -            except ValueError:
  65.187 -                raise ValueError("Illegal header line.")
  65.188 -            # TODO: what about TE and WWW-Authenticate?
  65.189 -            k = k.strip().title()
  65.190 -            v = v.strip()
  65.191 -            hname = k
  65.192 -        
  65.193 -        if k in comma_separated_headers:
  65.194 -            existing = hdict.get(hname)
  65.195 -            if existing:
  65.196 -                v = ", ".join((existing, v))
  65.197 -        hdict[hname] = v
  65.198 -    
  65.199 -    return hdict
  65.200 -
  65.201 -
  65.202 -class MaxSizeExceeded(Exception):
  65.203 -    pass
  65.204 -
  65.205 -class SizeCheckWrapper(object):
  65.206 -    """Wraps a file-like object, raising MaxSizeExceeded if too large."""
  65.207 -    
  65.208 -    def __init__(self, rfile, maxlen):
  65.209 -        self.rfile = rfile
  65.210 -        self.maxlen = maxlen
  65.211 -        self.bytes_read = 0
  65.212 -    
  65.213 -    def _check_length(self):
  65.214 -        if self.maxlen and self.bytes_read > self.maxlen:
  65.215 -            raise MaxSizeExceeded()
  65.216 -    
  65.217 -    def read(self, size=None):
  65.218 -        data = self.rfile.read(size)
  65.219 -        self.bytes_read += len(data)
  65.220 -        self._check_length()
  65.221 -        return data
  65.222 -    
  65.223 -    def readline(self, size=None):
  65.224 -        if size is not None:
  65.225 -            data = self.rfile.readline(size)
  65.226 -            self.bytes_read += len(data)
  65.227 -            self._check_length()
  65.228 -            return data
  65.229 -        
  65.230 -        # User didn't specify a size ...
  65.231 -        # We read the line in chunks to make sure it's not a 100MB line !
  65.232 -        res = []
  65.233 -        while True:
  65.234 -            data = self.rfile.readline(256)
  65.235 -            self.bytes_read += len(data)
  65.236 -            self._check_length()
  65.237 -            res.append(data)
  65.238 -            # See http://www.cherrypy.org/ticket/421
  65.239 -            if len(data) < 256 or data[-1:] == "\n":
  65.240 -                return ''.join(res)
  65.241 -    
  65.242 -    def readlines(self, sizehint=0):
  65.243 -        # Shamelessly stolen from StringIO
  65.244 -        total = 0
  65.245 -        lines = []
  65.246 -        line = self.readline()
  65.247 -        while line:
  65.248 -            lines.append(line)
  65.249 -            total += len(line)
  65.250 -            if 0 < sizehint <= total:
  65.251 -                break
  65.252 -            line = self.readline()
  65.253 -        return lines
  65.254 -    
  65.255 -    def close(self):
  65.256 -        self.rfile.close()
  65.257 -    
  65.258 -    def __iter__(self):
  65.259 -        return self
  65.260 -    
  65.261 -    def next(self):
  65.262 -        data = self.rfile.next()
  65.263 -        self.bytes_read += len(data)
  65.264 -        self._check_length()
  65.265 -        return data
  65.266 -
  65.267 -
  65.268 -class KnownLengthRFile(object):
  65.269 -    """Wraps a file-like object, returning an empty string when exhausted."""
  65.270 -    
  65.271 -    def __init__(self, rfile, content_length):
  65.272 -        self.rfile = rfile
  65.273 -        self.remaining = content_length
  65.274 -    
  65.275 -    def read(self, size=None):
  65.276 -        if self.remaining == 0:
  65.277 -            return ''
  65.278 -        if size is None:
  65.279 -            size = self.remaining
  65.280 -        else:
  65.281 -            size = min(size, self.remaining)
  65.282 -        
  65.283 -        data = self.rfile.read(size)
  65.284 -        self.remaining -= len(data)
  65.285 -        return data
  65.286 -    
  65.287 -    def readline(self, size=None):
  65.288 -        if self.remaining == 0:
  65.289 -            return ''
  65.290 -        if size is None:
  65.291 -            size = self.remaining
  65.292 -        else:
  65.293 -            size = min(size, self.remaining)
  65.294 -        
  65.295 -        data = self.rfile.readline(size)
  65.296 -        self.remaining -= len(data)
  65.297 -        return data
  65.298 -    
  65.299 -    def readlines(self, sizehint=0):
  65.300 -        # Shamelessly stolen from StringIO
  65.301 -        total = 0
  65.302 -        lines = []
  65.303 -        line = self.readline(sizehint)
  65.304 -        while line:
  65.305 -            lines.append(line)
  65.306 -            total += len(line)
  65.307 -            if 0 < sizehint <= total:
  65.308 -                break
  65.309 -            line = self.readline(sizehint)
  65.310 -        return lines
  65.311 -    
  65.312 -    def close(self):
  65.313 -        self.rfile.close()
  65.314 -    
  65.315 -    def __iter__(self):
  65.316 -        return self
  65.317 -    
  65.318 -    def __next__(self):
  65.319 -        data = next(self.rfile)
  65.320 -        self.remaining -= len(data)
  65.321 -        return data
  65.322 -
  65.323 -
  65.324 -class ChunkedRFile(object):
  65.325 -    """Wraps a file-like object, returning an empty string when exhausted.
  65.326 -    
  65.327 -    This class is intended to provide a conforming wsgi.input value for
  65.328 -    request entities that have been encoded with the 'chunked' transfer
  65.329 -    encoding.
  65.330 -    """
  65.331 -    
  65.332 -    def __init__(self, rfile, maxlen, bufsize=8192):
  65.333 -        self.rfile = rfile
  65.334 -        self.maxlen = maxlen
  65.335 -        self.bytes_read = 0
  65.336 -        self.buffer = ''
  65.337 -        self.bufsize = bufsize
  65.338 -        self.closed = False
  65.339 -    
  65.340 -    def _fetch(self):
  65.341 -        if self.closed:
  65.342 -            return
  65.343 -        
  65.344 -        line = self.rfile.readline()
  65.345 -        self.bytes_read += len(line)
  65.346 -        
  65.347 -        if self.maxlen and self.bytes_read > self.maxlen:
  65.348 -            raise MaxSizeExceeded("Request Entity Too Large", self.maxlen)
  65.349 -        
  65.350 -        line = line.strip().split(";", 1)
  65.351 -        
  65.352 -        try:
  65.353 -            chunk_size = line.pop(0)
  65.354 -            chunk_size = int(chunk_size, 16)
  65.355 -        except ValueError:
  65.356 -            raise ValueError("Bad chunked transfer size: " + repr(chunk_size))
  65.357 -        
  65.358 -        if chunk_size <= 0:
  65.359 -            self.closed = True
  65.360 -            return
  65.361 -        
  65.362 -##            if line: chunk_extension = line[0]
  65.363 -        
  65.364 -        if self.maxlen and self.bytes_read + chunk_size > self.maxlen:
  65.365 -            raise IOError("Request Entity Too Large")
  65.366 -        
  65.367 -        chunk = self.rfile.read(chunk_size)
  65.368 -        self.bytes_read += len(chunk)
  65.369 -        self.buffer += chunk
  65.370 -        
  65.371 -        crlf = self.rfile.read(2)
  65.372 -        if crlf != CRLF:
  65.373 -            raise ValueError(
  65.374 -                 "Bad chunked transfer coding (expected '\\r\\n', "
  65.375 -                 "got " + repr(crlf) + ")")
  65.376 -    
  65.377 -    def read(self, size=None):
  65.378 -        data = ''
  65.379 -        while True:
  65.380 -            if size and len(data) >= size:
  65.381 -                return data
  65.382 -            
  65.383 -            if not self.buffer:
  65.384 -                self._fetch()
  65.385 -                if not self.buffer:
  65.386 -                    # EOF
  65.387 -                    return data
  65.388 -            
  65.389 -            if size:
  65.390 -                remaining = size - len(data)
  65.391 -                data += self.buffer[:remaining]
  65.392 -                self.buffer = self.buffer[remaining:]
  65.393 -            else:
  65.394 -                data += self.buffer
  65.395 -    
  65.396 -    def readline(self, size=None):
  65.397 -        data = ''
  65.398 -        while True:
  65.399 -            if size and len(data) >= size:
  65.400 -                return data
  65.401 -            
  65.402 -            if not self.buffer:
  65.403 -                self._fetch()
  65.404 -                if not self.buffer:
  65.405 -                    # EOF
  65.406 -                    return data
  65.407 -            
  65.408 -            newline_pos = self.buffer.find('\n')
  65.409 -            if size:
  65.410 -                if newline_pos == -1:
  65.411 -                    remaining = size - len(data)
  65.412 -                    data += self.buffer[:remaining]
  65.413 -                    self.buffer = self.buffer[remaining:]
  65.414 -                else:
  65.415 -                    remaining = min(size - len(data), newline_pos)
  65.416 -                    data += self.buffer[:remaining]
  65.417 -                    self.buffer = self.buffer[remaining:]
  65.418 -            else:
  65.419 -                if newline_pos == -1:
  65.420 -                    data += self.buffer
  65.421 -                else:
  65.422 -                    data += self.buffer[:newline_pos]
  65.423 -                    self.buffer = self.buffer[newline_pos:]
  65.424 -    
  65.425 -    def readlines(self, sizehint=0):
  65.426 -        # Shamelessly stolen from StringIO
  65.427 -        total = 0
  65.428 -        lines = []
  65.429 -        line = self.readline(sizehint)
  65.430 -        while line:
  65.431 -            lines.append(line)
  65.432 -            total += len(line)
  65.433 -            if 0 < sizehint <= total:
  65.434 -                break
  65.435 -            line = self.readline(sizehint)
  65.436 -        return lines
  65.437 -    
  65.438 -    def read_trailer_lines(self):
  65.439 -        if not self.closed:
  65.440 -            raise ValueError(
  65.441 -                "Cannot read trailers until the request body has been read.")
  65.442 -        
  65.443 -        while True:
  65.444 -            line = self.rfile.readline()
  65.445 -            if not line:
  65.446 -                # No more data--illegal end of headers
  65.447 -                raise ValueError("Illegal end of headers.")
  65.448 -            
  65.449 -            self.bytes_read += len(line)
  65.450 -            if self.maxlen and self.bytes_read > self.maxlen:
  65.451 -                raise IOError("Request Entity Too Large")
  65.452 -            
  65.453 -            if line == CRLF:
  65.454 -                # Normal end of headers
  65.455 -                break
  65.456 -            if not line.endswith(CRLF):
  65.457 -                raise ValueError("HTTP requires CRLF terminators")
  65.458 -            
  65.459 -            yield line
  65.460 -    
  65.461 -    def close(self):
  65.462 -        self.rfile.close()
  65.463 -    
  65.464 -    def __iter__(self):
  65.465 -        # Shamelessly stolen from StringIO
  65.466 -        total = 0
  65.467 -        line = self.readline(sizehint)
  65.468 -        while line:
  65.469 -            yield line
  65.470 -            total += len(line)
  65.471 -            if 0 < sizehint <= total:
  65.472 -                break
  65.473 -            line = self.readline(sizehint)
  65.474 -
  65.475 -
  65.476 -class HTTPRequest(object):
  65.477 -    """An HTTP Request (and response).
  65.478 -    
  65.479 -    A single HTTP connection may consist of multiple request/response pairs.
  65.480 -    """
  65.481 -    
  65.482 -    server = None
  65.483 -    """The HTTPServer object which is receiving this request."""
  65.484 -    
  65.485 -    conn = None
  65.486 -    """The HTTPConnection object on which this request connected."""
  65.487 -    
  65.488 -    inheaders = {}
  65.489 -    """A dict of request headers."""
  65.490 -    
  65.491 -    outheaders = []
  65.492 -    """A list of header tuples to write in the response."""
  65.493 -    
  65.494 -    ready = False
  65.495 -    """When True, the request has been parsed and is ready to begin generating
  65.496 -    the response. When False, signals the calling Connection that the response
  65.497 -    should not be generated and the connection should close."""
  65.498 -    
  65.499 -    close_connection = False
  65.500 -    """Signals the calling Connection that the request should close. This does
  65.501 -    not imply an error! The client and/or server may each request that the
  65.502 -    connection be closed."""
  65.503 -    
  65.504 -    chunked_write = False
  65.505 -    """If True, output will be encoded with the "chunked" transfer-coding.
  65.506 -    
  65.507 -    This value is set automatically inside send_headers."""
  65.508 -    
  65.509 -    def __init__(self, server, conn):
  65.510 -        self.server= server
  65.511 -        self.conn = conn
  65.512 -        
  65.513 -        self.ready = False
  65.514 -        self.started_request = False
  65.515 -        self.scheme = "http"
  65.516 -        if self.server.ssl_adapter is not None:
  65.517 -            self.scheme = "https"
  65.518 -        # Use the lowest-common protocol in case read_request_line errors.
  65.519 -        self.response_protocol = 'HTTP/1.0'
  65.520 -        self.inheaders = {}
  65.521 -        
  65.522 -        self.status = ""
  65.523 -        self.outheaders = []
  65.524 -        self.sent_headers = False
  65.525 -        self.close_connection = self.__class__.close_connection
  65.526 -        self.chunked_read = False
  65.527 -        self.chunked_write = self.__class__.chunked_write
  65.528 -    
  65.529 -    def parse_request(self):
  65.530 -        """Parse the next HTTP request start-line and message-headers."""
  65.531 -        self.rfile = SizeCheckWrapper(self.conn.rfile,
  65.532 -                                      self.server.max_request_header_size)
  65.533 -        try:
  65.534 -            self.read_request_line()
  65.535 -        except MaxSizeExceeded:
  65.536 -            self.simple_response("414 Request-URI Too Long",
  65.537 -                "The Request-URI sent with the request exceeds the maximum "
  65.538 -                "allowed bytes.")
  65.539 -            return
  65.540 -        
  65.541 -        try:
  65.542 -            success = self.read_request_headers()
  65.543 -        except MaxSizeExceeded:
  65.544 -            self.simple_response("413 Request Entity Too Large",
  65.545 -                "The headers sent with the request exceed the maximum "
  65.546 -                "allowed bytes.")
  65.547 -            return
  65.548 -        else:
  65.549 -            if not success:
  65.550 -                return
  65.551 -        
  65.552 -        self.ready = True
  65.553 -    
  65.554 -    def read_request_line(self):
  65.555 -        # HTTP/1.1 connections are persistent by default. If a client
  65.556 -        # requests a page, then idles (leaves the connection open),
  65.557 -        # then rfile.readline() will raise socket.error("timed out").
  65.558 -        # Note that it does this based on the value given to settimeout(),
  65.559 -        # and doesn't need the client to request or acknowledge the close
  65.560 -        # (although your TCP stack might suffer for it: cf Apache's history
  65.561 -        # with FIN_WAIT_2).
  65.562 -        request_line = self.rfile.readline()
  65.563 -        
  65.564 -        # Set started_request to True so communicate() knows to send 408
  65.565 -        # from here on out.
  65.566 -        self.started_request = True
  65.567 -        if not request_line:
  65.568 -            # Force self.ready = False so the connection will close.
  65.569 -            self.ready = False
  65.570 -            return
  65.571 -        
  65.572 -        if request_line == CRLF:
  65.573 -            # RFC 2616 sec 4.1: "...if the server is reading the protocol
  65.574 -            # stream at the beginning of a message and receives a CRLF
  65.575 -            # first, it should ignore the CRLF."
  65.576 -            # But only ignore one leading line! else we enable a DoS.
  65.577 -            request_line = self.rfile.readline()
  65.578 -            if not request_line:
  65.579 -                self.ready = False
  65.580 -                return
  65.581 -        
  65.582 -        if not request_line.endswith(CRLF):
  65.583 -            self.simple_response("400 Bad Request", "HTTP requires CRLF terminators")
  65.584 -            return
  65.585 -        
  65.586 -        try:
  65.587 -            method, uri, req_protocol = request_line.strip().split(" ", 2)
  65.588 -            rp = int(req_protocol[5]), int(req_protocol[7])
  65.589 -        except (ValueError, IndexError):
  65.590 -            self.simple_response("400 Bad Request", "Malformed Request-Line")
  65.591 -            return
  65.592 -        
  65.593 -        self.uri = uri
  65.594 -        self.method = method
  65.595 -        
  65.596 -        # uri may be an abs_path (including "http://host.domain.tld");
  65.597 -        scheme, authority, path = self.parse_request_uri(uri)
  65.598 -        if '#' in path:
  65.599 -            self.simple_response("400 Bad Request",
  65.600 -                                 "Illegal #fragment in Request-URI.")
  65.601 -            return
  65.602 -        
  65.603 -        if scheme:
  65.604 -            self.scheme = scheme
  65.605 -        
  65.606 -        qs = ''
  65.607 -        if '?' in path:
  65.608 -            path, qs = path.split('?', 1)
  65.609 -        
  65.610 -        # Unquote the path+params (e.g. "/this%20path" -> "/this path").
  65.611 -        # http://www.w3.org/Protocols/rfc2616/rfc2616-sec5.html#sec5.1.2
  65.612 -        #
  65.613 -        # But note that "...a URI must be separated into its components
  65.614 -        # before the escaped characters within those components can be
  65.615 -        # safely decoded." http://www.ietf.org/rfc/rfc2396.txt, sec 2.4.2
  65.616 -        # Therefore, "/this%2Fpath" becomes "/this%2Fpath", not "/this/path".
  65.617 -        try:
  65.618 -            atoms = [unquote(x) for x in quoted_slash.split(path)]
  65.619 -        except ValueError, ex:
  65.620 -            self.simple_response("400 Bad Request", ex.args[0])
  65.621 -            return
  65.622 -        path = "%2F".join(atoms)
  65.623 -        self.path = path
  65.624 -        
  65.625 -        # Note that, like wsgiref and most other HTTP servers,
  65.626 -        # we "% HEX HEX"-unquote the path but not the query string.
  65.627 -        self.qs = qs
  65.628 -        
  65.629 -        # Compare request and server HTTP protocol versions, in case our
  65.630 -        # server does not support the requested protocol. Limit our output
  65.631 -        # to min(req, server). We want the following output:
  65.632 -        #     request    server     actual written   supported response
  65.633 -        #     protocol   protocol  response protocol    feature set
  65.634 -        # a     1.0        1.0           1.0                1.0
  65.635 -        # b     1.0        1.1           1.1                1.0
  65.636 -        # c     1.1        1.0           1.0                1.0
  65.637 -        # d     1.1        1.1           1.1                1.1
  65.638 -        # Notice that, in (b), the response will be "HTTP/1.1" even though
  65.639 -        # the client only understands 1.0. RFC 2616 10.5.6 says we should
  65.640 -        # only return 505 if the _major_ version is different.
  65.641 -        sp = int(self.server.protocol[5]), int(self.server.protocol[7])
  65.642 -        
  65.643 -        if sp[0] != rp[0]:
  65.644 -            self.simple_response("505 HTTP Version Not Supported")
  65.645 -            return
  65.646 -        self.request_protocol = req_protocol
  65.647 -        self.response_protocol = "HTTP/%s.%s" % min(rp, sp)
  65.648 -    
  65.649 -    def read_request_headers(self):
  65.650 -        """Read self.rfile into self.inheaders. Return success."""
  65.651 -        
  65.652 -        # then all the http headers
  65.653 -        try:
  65.654 -            read_headers(self.rfile, self.inheaders)
  65.655 -        except ValueError, ex:
  65.656 -            self.simple_response("400 Bad Request", ex.args[0])
  65.657 -            return False
  65.658 -        
  65.659 -        mrbs = self.server.max_request_body_size
  65.660 -        if mrbs and int(self.inheaders.get("Content-Length", 0)) > mrbs:
  65.661 -            self.simple_response("413 Request Entity Too Large",
  65.662 -                "The entity sent with the request exceeds the maximum "
  65.663 -                "allowed bytes.")
  65.664 -            return False
  65.665 -        
  65.666 -        # Persistent connection support
  65.667 -        if self.response_protocol == "HTTP/1.1":
  65.668 -            # Both server and client are HTTP/1.1
  65.669 -            if self.inheaders.get("Connection", "") == "close":
  65.670 -                self.close_connection = True
  65.671 -        else:
  65.672 -            # Either the server or client (or both) are HTTP/1.0
  65.673 -            if self.inheaders.get("Connection", "") != "Keep-Alive":
  65.674 -                self.close_connection = True
  65.675 -        
  65.676 -        # Transfer-Encoding support
  65.677 -        te = None
  65.678 -        if self.response_protocol == "HTTP/1.1":
  65.679 -            te = self.inheaders.get("Transfer-Encoding")
  65.680 -            if te:
  65.681 -                te = [x.strip().lower() for x in te.split(",") if x.strip()]
  65.682 -        
  65.683 -        self.chunked_read = False
  65.684 -        
  65.685 -        if te:
  65.686 -            for enc in te:
  65.687 -                if enc == "chunked":
  65.688 -                    self.chunked_read = True
  65.689 -                else:
  65.690 -                    # Note that, even if we see "chunked", we must reject
  65.691 -                    # if there is an extension we don't recognize.
  65.692 -                    self.simple_response("501 Unimplemented")
  65.693 -                    self.close_connection = True
  65.694 -                    return False
  65.695 -        
  65.696 -        # From PEP 333:
  65.697 -        # "Servers and gateways that implement HTTP 1.1 must provide
  65.698 -        # transparent support for HTTP 1.1's "expect/continue" mechanism.
  65.699 -        # This may be done in any of several ways:
  65.700 -        #   1. Respond to requests containing an Expect: 100-continue request
  65.701 -        #      with an immediate "100 Continue" response, and proceed normally.
  65.702 -        #   2. Proceed with the request normally, but provide the application
  65.703 -        #      with a wsgi.input stream that will send the "100 Continue"
  65.704 -        #      response if/when the application first attempts to read from
  65.705 -        #      the input stream. The read request must then remain blocked
  65.706 -        #      until the client responds.
  65.707 -        #   3. Wait until the client decides that the server does not support
  65.708 -        #      expect/continue, and sends the request body on its own.
  65.709 -        #      (This is suboptimal, and is not recommended.)
  65.710 -        #
  65.711 -        # We used to do 3, but are now doing 1. Maybe we'll do 2 someday,
  65.712 -        # but it seems like it would be a big slowdown for such a rare case.
  65.713 -        if self.inheaders.get("Expect", "") == "100-continue":
  65.714 -            # Don't use simple_response here, because it emits headers
  65.715 -            # we don't want. See http://www.cherrypy.org/ticket/951
  65.716 -            msg = self.server.protocol + " 100 Continue\r\n\r\n"
  65.717 -            try:
  65.718 -                self.conn.wfile.sendall(msg)
  65.719 -            except socket.error, x:
  65.720 -                if x.args[0] not in socket_errors_to_ignore:
  65.721 -                    raise
  65.722 -        return True
  65.723 -    
  65.724 -    def parse_request_uri(self, uri):
  65.725 -        """Parse a Request-URI into (scheme, authority, path).
  65.726 -        
  65.727 -        Note that Request-URI's must be one of::
  65.728 -            
  65.729 -            Request-URI    = "*" | absoluteURI | abs_path | authority
  65.730 -        
  65.731 -        Therefore, a Request-URI which starts with a double forward-slash
  65.732 -        cannot be a "net_path"::
  65.733 -        
  65.734 -            net_path      = "//" authority [ abs_path ]
  65.735 -        
  65.736 -        Instead, it must be interpreted as an "abs_path" with an empty first
  65.737 -        path segment::
  65.738 -        
  65.739 -            abs_path      = "/"  path_segments
  65.740 -            path_segments = segment *( "/" segment )
  65.741 -            segment       = *pchar *( ";" param )
  65.742 -            param         = *pchar
  65.743 -        """
  65.744 -        if uri == "*":
  65.745 -            return None, None, uri
  65.746 -        
  65.747 -        i = uri.find('://')
  65.748 -        if i > 0 and '?' not in uri[:i]:
  65.749 -            # An absoluteURI.
  65.750 -            # If there's a scheme (and it must be http or https), then:
  65.751 -            # http_URL = "http:" "//" host [ ":" port ] [ abs_path [ "?" query ]]
  65.752 -            scheme, remainder = uri[:i].lower(), uri[i + 3:]
  65.753 -            authority, path = remainder.split("/", 1)
  65.754 -            return scheme, authority, path
  65.755 -        
  65.756 -        if uri.startswith('/'):
  65.757 -            # An abs_path.
  65.758 -            return None, None, uri
  65.759 -        else:
  65.760 -            # An authority.
  65.761 -            return None, uri, None
  65.762 -    
  65.763 -    def respond(self):
  65.764 -        """Call the gateway and write its iterable output."""
  65.765 -        mrbs = self.server.max_request_body_size
  65.766 -        if self.chunked_read:
  65.767 -            self.rfile = ChunkedRFile(self.conn.rfile, mrbs)
  65.768 -        else:
  65.769 -            cl = int(self.inheaders.get("Content-Length", 0))
  65.770 -            if mrbs and mrbs < cl:
  65.771 -                if not self.sent_headers:
  65.772 -                    self.simple_response("413 Request Entity Too Large",
  65.773 -                        "The entity sent with the request exceeds the maximum "
  65.774 -                        "allowed bytes.")
  65.775 -                return
  65.776 -            self.rfile = KnownLengthRFile(self.conn.rfile, cl)
  65.777 -        
  65.778 -        self.server.gateway(self).respond()
  65.779 -        
  65.780 -        if (self.ready and not self.sent_headers):
  65.781 -            self.sent_headers = True
  65.782 -            self.send_headers()
  65.783 -        if self.chunked_write:
  65.784 -            self.conn.wfile.sendall("0\r\n\r\n")
  65.785 -    
  65.786 -    def simple_response(self, status, msg=""):
  65.787 -        """Write a simple response back to the client."""
  65.788 -        status = str(status)
  65.789 -        buf = [self.server.protocol + " " +
  65.790 -               status + CRLF,
  65.791 -               "Content-Length: %s\r\n" % len(msg),
  65.792 -               "Content-Type: text/plain\r\n"]
  65.793 -        
  65.794 -        if status[:3] in ("413", "414"):
  65.795 -            # Request Entity Too Large / Request-URI Too Long
  65.796 -            self.close_connection = True
  65.797 -            if self.response_protocol == 'HTTP/1.1':
  65.798 -                # This will not be true for 414, since read_request_line
  65.799 -                # usually raises 414 before reading the whole line, and we
  65.800 -                # therefore cannot know the proper response_protocol.
  65.801 -                buf.append("Connection: close\r\n")
  65.802 -            else:
  65.803 -                # HTTP/1.0 had no 413/414 status nor Connection header.
  65.804 -                # Emit 400 instead and trust the message body is enough.
  65.805 -                status = "400 Bad Request"
  65.806 -        
  65.807 -        buf.append(CRLF)
  65.808 -        if msg:
  65.809 -            if isinstance(msg, unicode):
  65.810 -                msg = msg.encode("ISO-8859-1")
  65.811 -            buf.append(msg)
  65.812 -        
  65.813 -        try:
  65.814 -            self.conn.wfile.sendall("".join(buf))
  65.815 -        except socket.error, x:
  65.816 -            if x.args[0] not in socket_errors_to_ignore:
  65.817 -                raise
  65.818 -    
  65.819 -    def write(self, chunk):
  65.820 -        """Write unbuffered data to the client."""
  65.821 -        if self.chunked_write and chunk:
  65.822 -            buf = [hex(len(chunk))[2:], CRLF, chunk, CRLF]
  65.823 -            self.conn.wfile.sendall("".join(buf))
  65.824 -        else:
  65.825 -            self.conn.wfile.sendall(chunk)
  65.826 -    
  65.827 -    def send_headers(self):
  65.828 -        """Assert, process, and send the HTTP response message-headers.
  65.829 -        
  65.830 -        You must set self.status, and self.outheaders before calling this.
  65.831 -        """
  65.832 -        hkeys = [key.lower() for key, value in self.outheaders]
  65.833 -        status = int(self.status[:3])
  65.834 -        
  65.835 -        if status == 413:
  65.836 -            # Request Entity Too Large. Close conn to avoid garbage.
  65.837 -            self.close_connection = True
  65.838 -        elif "content-length" not in hkeys:
  65.839 -            # "All 1xx (informational), 204 (no content),
  65.840 -            # and 304 (not modified) responses MUST NOT
  65.841 -            # include a message-body." So no point chunking.
  65.842 -            if status < 200 or status in (204, 205, 304):
  65.843 -                pass
  65.844 -            else:
  65.845 -                if (self.response_protocol == 'HTTP/1.1'
  65.846 -                    and self.method != 'HEAD'):
  65.847 -                    # Use the chunked transfer-coding
  65.848 -                    self.chunked_write = True
  65.849 -                    self.outheaders.append(("Transfer-Encoding", "chunked"))
  65.850 -                else:
  65.851 -                    # Closing the conn is the only way to determine len.
  65.852 -                    self.close_connection = True
  65.853 -        
  65.854 -        if "connection" not in hkeys:
  65.855 -            if self.response_protocol == 'HTTP/1.1':
  65.856 -                # Both server and client are HTTP/1.1 or better
  65.857 -                if self.close_connection:
  65.858 -                    self.outheaders.append(("Connection", "close"))
  65.859 -            else:
  65.860 -                # Server and/or client are HTTP/1.0
  65.861 -                if not self.close_connection:
  65.862 -                    self.outheaders.append(("Connection", "Keep-Alive"))
  65.863 -        
  65.864 -        if (not self.close_connection) and (not self.chunked_read):
  65.865 -            # Read any remaining request body data on the socket.
  65.866 -            # "If an origin server receives a request that does not include an
  65.867 -            # Expect request-header field with the "100-continue" expectation,
  65.868 -            # the request includes a request body, and the server responds
  65.869 -            # with a final status code before reading the entire request body
  65.870 -            # from the transport connection, then the server SHOULD NOT close
  65.871 -            # the transport connection until it has read the entire request,
  65.872 -            # or until the client closes the connection. Otherwise, the client
  65.873 -            # might not reliably receive the response message. However, this
  65.874 -            # requirement is not be construed as preventing a server from
  65.875 -            # defending itself against denial-of-service attacks, or from
  65.876 -            # badly broken client implementations."
  65.877 -            remaining = getattr(self.rfile, 'remaining', 0)
  65.878 -            if remaining > 0:
  65.879 -                self.rfile.read(remaining)
  65.880 -        
  65.881 -        if "date" not in hkeys:
  65.882 -            self.outheaders.append(("Date", rfc822.formatdate()))
  65.883 -        
  65.884 -        if "server" not in hkeys:
  65.885 -            self.outheaders.append(("Server", self.server.server_name))
  65.886 -        
  65.887 -        buf = [self.server.protocol + " " + self.status + CRLF]
  65.888 -        for k, v in self.outheaders:
  65.889 -            buf.append(k + ": " + v + CRLF)
  65.890 -        buf.append(CRLF)
  65.891 -        self.conn.wfile.sendall("".join(buf))
  65.892 -
  65.893 -
  65.894 -class NoSSLError(Exception):
  65.895 -    """Exception raised when a client speaks HTTP to an HTTPS socket."""
  65.896 -    pass
  65.897 -
  65.898 -
  65.899 -class FatalSSLAlert(Exception):
  65.900 -    """Exception raised when the SSL implementation signals a fatal alert."""
  65.901 -    pass
  65.902 -
  65.903 -
  65.904 -class CP_fileobject(socket._fileobject):
  65.905 -    """Faux file object attached to a socket object."""
  65.906 -
  65.907 -    def __init__(self, *args, **kwargs):
  65.908 -        self.bytes_read = 0
  65.909 -        self.bytes_written = 0
  65.910 -        socket._fileobject.__init__(self, *args, **kwargs)
  65.911 -    
  65.912 -    def sendall(self, data):
  65.913 -        """Sendall for non-blocking sockets."""
  65.914 -        while data:
  65.915 -            try:
  65.916 -                bytes_sent = self.send(data)
  65.917 -                data = data[bytes_sent:]
  65.918 -            except socket.error, e:
  65.919 -                if e.args[0] not in socket_errors_nonblocking:
  65.920 -                    raise
  65.921 -
  65.922 -    def send(self, data):
  65.923 -        bytes_sent = self._sock.send(data)
  65.924 -        self.bytes_written += bytes_sent
  65.925 -        return bytes_sent
  65.926 -
  65.927 -    def flush(self):
  65.928 -        if self._wbuf:
  65.929 -            buffer = "".join(self._wbuf)
  65.930 -            self._wbuf = []
  65.931 -            self.sendall(buffer)
  65.932 -
  65.933 -    def recv(self, size):
  65.934 -        while True:
  65.935 -            try:
  65.936 -                data = self._sock.recv(size)
  65.937 -                self.bytes_read += len(data)
  65.938 -                return data
  65.939 -            except socket.error, e:
  65.940 -                if (e.args[0] not in socket_errors_nonblocking
  65.941 -                    and e.args[0] not in socket_error_eintr):
  65.942 -                    raise
  65.943 -
  65.944 -    if not _fileobject_uses_str_type:
  65.945 -        def read(self, size=-1):
  65.946 -            # Use max, disallow tiny reads in a loop as they are very inefficient.
  65.947 -            # We never leave read() with any leftover data from a new recv() call
  65.948 -            # in our internal buffer.
  65.949 -            rbufsize = max(self._rbufsize, self.default_bufsize)
  65.950 -            # Our use of StringIO rather than lists of string objects returned by
  65.951 -            # recv() minimizes memory usage and fragmentation that occurs when
  65.952 -            # rbufsize is large compared to the typical return value of recv().
  65.953 -            buf = self._rbuf
  65.954 -            buf.seek(0, 2)  # seek end
  65.955 -            if size < 0:
  65.956 -                # Read until EOF
  65.957 -                self._rbuf = StringIO.StringIO()  # reset _rbuf.  we consume it via buf.
  65.958 -                while True:
  65.959 -                    data = self.recv(rbufsize)
  65.960 -                    if not data:
  65.961 -                        break
  65.962 -                    buf.write(data)
  65.963 -                return buf.getvalue()
  65.964 -            else:
  65.965 -                # Read until size bytes or EOF seen, whichever comes first
  65.966 -                buf_len = buf.tell()
  65.967 -                if buf_len >= size:
  65.968 -                    # Already have size bytes in our buffer?  Extract and return.
  65.969 -                    buf.seek(0)
  65.970 -                    rv = buf.read(size)
  65.971 -                    self._rbuf = StringIO.StringIO()
  65.972 -                    self._rbuf.write(buf.read())
  65.973 -                    return rv
  65.974 -
  65.975 -                self._rbuf = StringIO.StringIO()  # reset _rbuf.  we consume it via buf.
  65.976 -                while True:
  65.977 -                    left = size - buf_len
  65.978 -                    # recv() will malloc the amount of memory given as its
  65.979 -                    # parameter even though it often returns much less data
  65.980 -                    # than that.  The returned data string is short lived
  65.981 -                    # as we copy it into a StringIO and free it.  This avoids
  65.982 -                    # fragmentation issues on many platforms.
  65.983 -                    data = self.recv(left)
  65.984 -                    if not data:
  65.985 -                        break
  65.986 -                    n = len(data)
  65.987 -                    if n == size and not buf_len:
  65.988 -                        # Shortcut.  Avoid buffer data copies when:
  65.989 -                        # - We have no data in our buffer.
  65.990 -                        # AND
  65.991 -                        # - Our call to recv returned exactly the
  65.992 -                        #   number of bytes we were asked to read.
  65.993 -                        return data
  65.994 -                    if n == left:
  65.995 -                        buf.write(data)
  65.996 -                        del data  # explicit free
  65.997 -                        break
  65.998 -                    assert n <= left, "recv(%d) returned %d bytes" % (left, n)
  65.999 -                    buf.write(data)
 65.1000 -                    buf_len += n
 65.1001 -                    del data  # explicit free
 65.1002 -                    #assert buf_len == buf.tell()
 65.1003 -                return buf.getvalue()
 65.1004 -
 65.1005 -        def readline(self, size=-1):
 65.1006 -            buf = self._rbuf
 65.1007 -            buf.seek(0, 2)  # seek end
 65.1008 -            if buf.tell() > 0:
 65.1009 -                # check if we already have it in our buffer
 65.1010 -                buf.seek(0)
 65.1011 -                bline = buf.readline(size)
 65.1012 -                if bline.endswith('\n') or len(bline) == size:
 65.1013 -                    self._rbuf = StringIO.StringIO()
 65.1014 -                    self._rbuf.write(buf.read())
 65.1015 -                    return bline
 65.1016 -                del bline
 65.1017 -            if size < 0:
 65.1018 -                # Read until \n or EOF, whichever comes first
 65.1019 -                if self._rbufsize <= 1:
 65.1020 -                    # Speed up unbuffered case
 65.1021 -                    buf.seek(0)
 65.1022 -                    buffers = [buf.read()]
 65.1023 -                    self._rbuf = StringIO.StringIO()  # reset _rbuf.  we consume it via buf.
 65.1024 -                    data = None
 65.1025 -                    recv = self.recv
 65.1026 -                    while data != "\n":
 65.1027 -                        data = recv(1)
 65.1028 -                        if not data:
 65.1029 -                            break
 65.1030 -                        buffers.append(data)
 65.1031 -                    return "".join(buffers)
 65.1032 -
 65.1033 -                buf.seek(0, 2)  # seek end
 65.1034 -                self._rbuf = StringIO.StringIO()  # reset _rbuf.  we consume it via buf.
 65.1035 -                while True:
 65.1036 -                    data = self.recv(self._rbufsize)
 65.1037 -                    if not data:
 65.1038 -                        break
 65.1039 -                    nl = data.find('\n')
 65.1040 -                    if nl >= 0:
 65.1041 -                        nl += 1
 65.1042 -                        buf.write(data[:nl])
 65.1043 -                        self._rbuf.write(data[nl:])
 65.1044 -                        del data
 65.1045 -                        break
 65.1046 -                    buf.write(data)
 65.1047 -                return buf.getvalue()
 65.1048 -            else:
 65.1049 -                # Read until size bytes or \n or EOF seen, whichever comes first
 65.1050 -                buf.seek(0, 2)  # seek end
 65.1051 -                buf_len = buf.tell()
 65.1052 -                if buf_len >= size:
 65.1053 -                    buf.seek(0)
 65.1054 -                    rv = buf.read(size)
 65.1055 -                    self._rbuf = StringIO.StringIO()
 65.1056 -                    self._rbuf.write(buf.read())
 65.1057 -                    return rv
 65.1058 -                self._rbuf = StringIO.StringIO()  # reset _rbuf.  we consume it via buf.
 65.1059 -                while True:
 65.1060 -                    data = self.recv(self._rbufsize)
 65.1061 -                    if not data:
 65.1062 -                        break
 65.1063 -                    left = size - buf_len
 65.1064 -                    # did we just receive a newline?
 65.1065 -                    nl = data.find('\n', 0, left)
 65.1066 -                    if nl >= 0:
 65.1067 -                        nl += 1
 65.1068 -                        # save the excess data to _rbuf
 65.1069 -                        self._rbuf.write(data[nl:])
 65.1070 -                        if buf_len:
 65.1071 -                            buf.write(data[:nl])
 65.1072 -                            break
 65.1073 -                        else:
 65.1074 -                            # Shortcut.  Avoid data copy through buf when returning
 65.1075 -                            # a substring of our first recv().
 65.1076 -                            return data[:nl]
 65.1077 -                    n = len(data)
 65.1078 -                    if n == size and not buf_len:
 65.1079 -                        # Shortcut.  Avoid data copy through buf when
 65.1080 -                        # returning exactly all of our first recv().
 65.1081 -                        return data
 65.1082 -                    if n >= left:
 65.1083 -                        buf.write(data[:left])
 65.1084 -                        self._rbuf.write(data[left:])
 65.1085 -                        break
 65.1086 -                    buf.write(data)
 65.1087 -                    buf_len += n
 65.1088 -                    #assert buf_len == buf.tell()
 65.1089 -                return buf.getvalue()
 65.1090 -    else:
 65.1091 -        def read(self, size=-1):
 65.1092 -            if size < 0:
 65.1093 -                # Read until EOF
 65.1094 -                buffers = [self._rbuf]
 65.1095 -                self._rbuf = ""
 65.1096 -                if self._rbufsize <= 1:
 65.1097 -                    recv_size = self.default_bufsize
 65.1098 -                else:
 65.1099 -                    recv_size = self._rbufsize
 65.1100 -
 65.1101 -                while True:
 65.1102 -                    data = self.recv(recv_size)
 65.1103 -                    if not data:
 65.1104 -                        break
 65.1105 -                    buffers.append(data)
 65.1106 -                return "".join(buffers)
 65.1107 -            else:
 65.1108 -                # Read until size bytes or EOF seen, whichever comes first
 65.1109 -                data = self._rbuf
 65.1110 -                buf_len = len(data)
 65.1111 -                if buf_len >= size:
 65.1112 -                    self._rbuf = data[size:]
 65.1113 -                    return data[:size]
 65.1114 -                buffers = []
 65.1115 -                if data:
 65.1116 -                    buffers.append(data)
 65.1117 -                self._rbuf = ""
 65.1118 -                while True:
 65.1119 -                    left = size - buf_len
 65.1120 -                    recv_size = max(self._rbufsize, left)
 65.1121 -                    data = self.recv(recv_size)
 65.1122 -                    if not data:
 65.1123 -                        break
 65.1124 -                    buffers.append(data)
 65.1125 -                    n = len(data)
 65.1126 -                    if n >= left:
 65.1127 -                        self._rbuf = data[left:]
 65.1128 -                        buffers[-1] = data[:left]
 65.1129 -                        break
 65.1130 -                    buf_len += n
 65.1131 -                return "".join(buffers)
 65.1132 -
 65.1133 -        def readline(self, size=-1):
 65.1134 -            data = self._rbuf
 65.1135 -            if size < 0:
 65.1136 -                # Read until \n or EOF, whichever comes first
 65.1137 -                if self._rbufsize <= 1:
 65.1138 -                    # Speed up unbuffered case
 65.1139 -                    assert data == ""
 65.1140 -                    buffers = []
 65.1141 -                    while data != "\n":
 65.1142 -                        data = self.recv(1)
 65.1143 -                        if not data:
 65.1144 -                            break
 65.1145 -                        buffers.append(data)
 65.1146 -                    return "".join(buffers)
 65.1147 -                nl = data.find('\n')
 65.1148 -                if nl >= 0:
 65.1149 -                    nl += 1
 65.1150 -                    self._rbuf = data[nl:]
 65.1151 -                    return data[:nl]
 65.1152 -                buffers = []
 65.1153 -                if data:
 65.1154 -                    buffers.append(data)
 65.1155 -                self._rbuf = ""
 65.1156 -                while True:
 65.1157 -                    data = self.recv(self._rbufsize)
 65.1158 -                    if not data:
 65.1159 -                        break
 65.1160 -                    buffers.append(data)
 65.1161 -                    nl = data.find('\n')
 65.1162 -                    if nl >= 0:
 65.1163 -                        nl += 1
 65.1164 -                        self._rbuf = data[nl:]
 65.1165 -                        buffers[-1] = data[:nl]
 65.1166 -                        break
 65.1167 -                return "".join(buffers)
 65.1168 -            else:
 65.1169 -                # Read until size bytes or \n or EOF seen, whichever comes first
 65.1170 -                nl = data.find('\n', 0, size)
 65.1171 -                if nl >= 0:
 65.1172 -                    nl += 1
 65.1173 -                    self._rbuf = data[nl:]
 65.1174 -                    return data[:nl]
 65.1175 -                buf_len = len(data)
 65.1176 -                if buf_len >= size:
 65.1177 -                    self._rbuf = data[size:]
 65.1178 -                    return data[:size]
 65.1179 -                buffers = []
 65.1180 -                if data:
 65.1181 -                    buffers.append(data)
 65.1182 -                self._rbuf = ""
 65.1183 -                while True:
 65.1184 -                    data = self.recv(self._rbufsize)
 65.1185 -                    if not data:
 65.1186 -                        break
 65.1187 -                    buffers.append(data)
 65.1188 -                    left = size - buf_len
 65.1189 -                    nl = data.find('\n', 0, left)
 65.1190 -                    if nl >= 0:
 65.1191 -                        nl += 1
 65.1192 -                        self._rbuf = data[nl:]
 65.1193 -                        buffers[-1] = data[:nl]
 65.1194 -                        break
 65.1195 -                    n = len(data)
 65.1196 -                    if n >= left:
 65.1197 -                        self._rbuf = data[left:]
 65.1198 -                        buffers[-1] = data[:left]
 65.1199 -                        break
 65.1200 -                    buf_len += n
 65.1201 -                return "".join(buffers)
 65.1202 -
 65.1203 -
 65.1204 -class HTTPConnection(object):
 65.1205 -    """An HTTP connection (active socket).
 65.1206 -    
 65.1207 -    server: the Server object which received this connection.
 65.1208 -    socket: the raw socket object (usually TCP) for this connection.
 65.1209 -    makefile: a fileobject class for reading from the socket.
 65.1210 -    """
 65.1211 -    
 65.1212 -    remote_addr = None
 65.1213 -    remote_port = None
 65.1214 -    ssl_env = None
 65.1215 -    rbufsize = DEFAULT_BUFFER_SIZE
 65.1216 -    wbufsize = DEFAULT_BUFFER_SIZE
 65.1217 -    RequestHandlerClass = HTTPRequest
 65.1218 -    
 65.1219 -    def __init__(self, server, sock, makefile=CP_fileobject):
 65.1220 -        self.server = server
 65.1221 -        self.socket = sock
 65.1222 -        self.rfile = makefile(sock, "rb", self.rbufsize)
 65.1223 -        self.wfile = makefile(sock, "wb", self.wbufsize)
 65.1224 -        self.requests_seen = 0
 65.1225 -    
 65.1226 -    def communicate(self):
 65.1227 -        """Read each request and respond appropriately."""
 65.1228 -        request_seen = False
 65.1229 -        try:
 65.1230 -            while True:
 65.1231 -                # (re)set req to None so that if something goes wrong in
 65.1232 -                # the RequestHandlerClass constructor, the error doesn't
 65.1233 -                # get written to the previous request.
 65.1234 -                req = None
 65.1235 -                req = self.RequestHandlerClass(self.server, self)
 65.1236 -                
 65.1237 -                # This order of operations should guarantee correct pipelining.
 65.1238 -                req.parse_request()
 65.1239 -                if self.server.stats['Enabled']:
 65.1240 -                    self.requests_seen += 1
 65.1241 -                if not req.ready:
 65.1242 -                    # Something went wrong in the parsing (and the server has
 65.1243 -                    # probably already made a simple_response). Return and
 65.1244 -                    # let the conn close.
 65.1245 -                    return
 65.1246 -                
 65.1247 -                request_seen = True
 65.1248 -                req.respond()
 65.1249 -                if req.close_connection:
 65.1250 -                    return
 65.1251 -        except socket.error, e:
 65.1252 -            errnum = e.args[0]
 65.1253 -            # sadly SSL sockets return a different (longer) time out string
 65.1254 -            if errnum == 'timed out' or errnum == 'The read operation timed out':
 65.1255 -                # Don't error if we're between requests; only error
 65.1256 -                # if 1) no request has been started at all, or 2) we're
 65.1257 -                # in the middle of a request.
 65.1258 -                # See http://www.cherrypy.org/ticket/853
 65.1259 -                if (not request_seen) or (req and req.started_request):
 65.1260 -                    # Don't bother writing the 408 if the response
 65.1261 -                    # has already started being written.
 65.1262 -                    if req and not req.sent_headers:
 65.1263 -                        try:
 65.1264 -                            req.simple_response("408 Request Timeout")
 65.1265 -                        except FatalSSLAlert:
 65.1266 -                            # Close the connection.
 65.1267 -                            return
 65.1268 -            elif errnum not in socket_errors_to_ignore:
 65.1269 -                if req and not req.sent_headers:
 65.1270 -                    try:
 65.1271 -                        req.simple_response("500 Internal Server Error",
 65.1272 -                                            format_exc())
 65.1273 -                    except FatalSSLAlert:
 65.1274 -                        # Close the connection.
 65.1275 -                        return
 65.1276 -            return
 65.1277 -        except (KeyboardInterrupt, SystemExit):
 65.1278 -            raise
 65.1279 -        except FatalSSLAlert:
 65.1280 -            # Close the connection.
 65.1281 -            return
 65.1282 -        except NoSSLError:
 65.1283 -            if req and not req.sent_headers:
 65.1284 -                # Unwrap our wfile
 65.1285 -                self.wfile = CP_fileobject(self.socket._sock, "wb", self.wbufsize)
 65.1286 -                req.simple_response("400 Bad Request",
 65.1287 -                    "The client sent a plain HTTP request, but "
 65.1288 -                    "this server only speaks HTTPS on this port.")
 65.1289 -                self.linger = True
 65.1290 -        except Exception:
 65.1291 -            if req and not req.sent_headers:
 65.1292 -                try:
 65.1293 -                    req.simple_response("500 Internal Server Error", format_exc())
 65.1294 -                except FatalSSLAlert:
 65.1295 -                    # Close the connection.
 65.1296 -                    return
 65.1297 -    
 65.1298 -    linger = False
 65.1299 -    
 65.1300 -    def close(self):
 65.1301 -        """Close the socket underlying this connection."""
 65.1302 -        self.rfile.close()
 65.1303 -        
 65.1304 -        if not self.linger:
 65.1305 -            # Python's socket module does NOT call close on the kernel socket
 65.1306 -            # when you call socket.close(). We do so manually here because we
 65.1307 -            # want this server to send a FIN TCP segment immediately. Note this
 65.1308 -            # must be called *before* calling socket.close(), because the latter
 65.1309 -            # drops its reference to the kernel socket.
 65.1310 -            if hasattr(self.socket, '_sock'):
 65.1311 -                self.socket._sock.close()
 65.1312 -            self.socket.close()
 65.1313 -        else:
 65.1314 -            # On the other hand, sometimes we want to hang around for a bit
 65.1315 -            # to make sure the client has a chance to read our entire
 65.1316 -            # response. Skipping the close() calls here delays the FIN
 65.1317 -            # packet until the socket object is garbage-collected later.
 65.1318 -            # Someday, perhaps, we'll do the full lingering_close that
 65.1319 -            # Apache does, but not today.
 65.1320 -            pass
 65.1321 -
 65.1322 -
 65.1323 -_SHUTDOWNREQUEST = None
 65.1324 -
 65.1325 -class WorkerThread(threading.Thread):
 65.1326 -    """Thread which continuously polls a Queue for Connection objects.
 65.1327 -    
 65.1328 -    Due to the timing issues of polling a Queue, a WorkerThread does not
 65.1329 -    check its own 'ready' flag after it has started. To stop the thread,
 65.1330 -    it is necessary to stick a _SHUTDOWNREQUEST object onto the Queue
 65.1331 -    (one for each running WorkerThread).
 65.1332 -    """
 65.1333 -    
 65.1334 -    conn = None
 65.1335 -    """The current connection pulled off the Queue, or None."""
 65.1336 -    
 65.1337 -    server = None
 65.1338 -    """The HTTP Server which spawned this thread, and which owns the
 65.1339 -    Queue and is placing active connections into it."""
 65.1340 -    
 65.1341 -    ready = False
 65.1342 -    """A simple flag for the calling server to know when this thread
 65.1343 -    has begun polling the Queue."""
 65.1344 -    
 65.1345 -    
 65.1346 -    def __init__(self, server):
 65.1347 -        self.ready = False
 65.1348 -        self.server = server
 65.1349 -        
 65.1350 -        self.requests_seen = 0
 65.1351 -        self.bytes_read = 0
 65.1352 -        self.bytes_written = 0
 65.1353 -        self.start_time = None
 65.1354 -        self.work_time = 0
 65.1355 -        self.stats = {
 65.1356 -            'Requests': lambda s: self.requests_seen + ((self.start_time is None) and 0 or self.conn.requests_seen),
 65.1357 -            'Bytes Read': lambda s: self.bytes_read + ((self.start_time is None) and 0 or self.conn.rfile.bytes_read),
 65.1358 -            'Bytes Written': lambda s: self.bytes_written + ((self.start_time is None) and 0 or self.conn.wfile.bytes_written),
 65.1359 -            'Work Time': lambda s: self.work_time + ((self.start_time is None) and 0 or time.time() - self.start_time),
 65.1360 -            'Read Throughput': lambda s: s['Bytes Read'](s) / (s['Work Time'](s) or 1e-6),
 65.1361 -            'Write Throughput': lambda s: s['Bytes Written'](s) / (s['Work Time'](s) or 1e-6),
 65.1362 -        }
 65.1363 -        threading.Thread.__init__(self)
 65.1364 -    
 65.1365 -    def run(self):
 65.1366 -        self.server.stats['Worker Threads'][self.getName()] = self.stats
 65.1367 -        try:
 65.1368 -            self.ready = True
 65.1369 -            while True:
 65.1370 -                conn = self.server.requests.get()
 65.1371 -                if conn is _SHUTDOWNREQUEST:
 65.1372 -                    return
 65.1373 -                
 65.1374 -                self.conn = conn
 65.1375 -                if self.server.stats['Enabled']:
 65.1376 -                    self.start_time = time.time()
 65.1377 -                try:
 65.1378 -                    conn.communicate()
 65.1379 -                finally:
 65.1380 -                    conn.close()
 65.1381 -                    if self.server.stats['Enabled']:
 65.1382 -                        self.requests_seen += self.conn.requests_seen
 65.1383 -                        self.bytes_read += self.conn.rfile.bytes_read
 65.1384 -                        self.bytes_written += self.conn.wfile.bytes_written
 65.1385 -                        self.work_time += time.time() - self.start_time
 65.1386 -                        self.start_time = None
 65.1387 -                    self.conn = None
 65.1388 -        except (KeyboardInterrupt, SystemExit), exc:
 65.1389 -            self.server.interrupt = exc
 65.1390 -
 65.1391 -
 65.1392 -class ThreadPool(object):
 65.1393 -    """A Request Queue for the CherryPyWSGIServer which pools threads.
 65.1394 -    
 65.1395 -    ThreadPool objects must provide min, get(), put(obj), start()
 65.1396 -    and stop(timeout) attributes.
 65.1397 -    """
 65.1398 -    
 65.1399 -    def __init__(self, server, min=10, max=-1):
 65.1400 -        self.server = server
 65.1401 -        self.min = min
 65.1402 -        self.max = max
 65.1403 -        self._threads = []
 65.1404 -        self._queue = Queue.Queue()
 65.1405 -        self.get = self._queue.get
 65.1406 -    
 65.1407 -    def start(self):
 65.1408 -        """Start the pool of threads."""
 65.1409 -        for i in range(self.min):
 65.1410 -            self._threads.append(WorkerThread(self.server))
 65.1411 -        for worker in self._threads:
 65.1412 -            worker.setName("CP Server " + worker.getName())
 65.1413 -            worker.start()
 65.1414 -        for worker in self._threads:
 65.1415 -            while not worker.ready:
 65.1416 -                time.sleep(.1)
 65.1417 -    
 65.1418 -    def _get_idle(self):
 65.1419 -        """Number of worker threads which are idle. Read-only."""
 65.1420 -        return len([t for t in self._threads if t.conn is None])
 65.1421 -    idle = property(_get_idle, doc=_get_idle.__doc__)
 65.1422 -    
 65.1423 -    def put(self, obj):
 65.1424 -        self._queue.put(obj)
 65.1425 -        if obj is _SHUTDOWNREQUEST:
 65.1426 -            return
 65.1427 -    
 65.1428 -    def grow(self, amount):
 65.1429 -        """Spawn new worker threads (not above self.max)."""
 65.1430 -        for i in range(amount):
 65.1431 -            if self.max > 0 and len(self._threads) >= self.max:
 65.1432 -                break
 65.1433 -            worker = WorkerThread(self.server)
 65.1434 -            worker.setName("CP Server " + worker.getName())
 65.1435 -            self._threads.append(worker)
 65.1436 -            worker.start()
 65.1437 -    
 65.1438 -    def shrink(self, amount):
 65.1439 -        """Kill off worker threads (not below self.min)."""
 65.1440 -        # Grow/shrink the pool if necessary.
 65.1441 -        # Remove any dead threads from our list
 65.1442 -        for t in self._threads:
 65.1443 -            if not t.isAlive():
 65.1444 -                self._threads.remove(t)
 65.1445 -                amount -= 1
 65.1446 -        
 65.1447 -        if amount > 0:
 65.1448 -            for i in range(min(amount, len(self._threads) - self.min)):
 65.1449 -                # Put a number of shutdown requests on the queue equal
 65.1450 -                # to 'amount'. Once each of those is processed by a worker,
 65.1451 -                # that worker will terminate and be culled from our list
 65.1452 -                # in self.put.
 65.1453 -                self._queue.put(_SHUTDOWNREQUEST)
 65.1454 -    
 65.1455 -    def stop(self, timeout=5):
 65.1456 -        # Must shut down threads here so the code that calls
 65.1457 -        # this method can know when all threads are stopped.
 65.1458 -        for worker in self._threads:
 65.1459 -            self._queue.put(_SHUTDOWNREQUEST)
 65.1460 -        
 65.1461 -        # Don't join currentThread (when stop is called inside a request).
 65.1462 -        current = threading.currentThread()
 65.1463 -        if timeout and timeout >= 0:
 65.1464 -            endtime = time.time() + timeout
 65.1465 -        while self._threads:
 65.1466 -            worker = self._threads.pop()
 65.1467 -            if worker is not current and worker.isAlive():
 65.1468 -                try:
 65.1469 -                    if timeout is None or timeout < 0:
 65.1470 -                        worker.join()
 65.1471 -                    else:
 65.1472 -                        remaining_time = endtime - time.time()
 65.1473 -                        if remaining_time > 0:
 65.1474 -                            worker.join(remaining_time)
 65.1475 -                        if worker.isAlive():
 65.1476 -                            # We exhausted the timeout.
 65.1477 -                            # Forcibly shut down the socket.
 65.1478 -                            c = worker.conn
 65.1479 -                            if c and not c.rfile.closed:
 65.1480 -                                try:
 65.1481 -                                    c.socket.shutdown(socket.SHUT_RD)
 65.1482 -                                except TypeError:
 65.1483 -                                    # pyOpenSSL sockets don't take an arg
 65.1484 -                                    c.socket.shutdown()
 65.1485 -                            worker.join()
 65.1486 -                except (AssertionError,
 65.1487 -                        # Ignore repeated Ctrl-C.
 65.1488 -                        # See http://www.cherrypy.org/ticket/691.
 65.1489 -                        KeyboardInterrupt), exc1:
 65.1490 -                    pass
 65.1491 -    
 65.1492 -    def _get_qsize(self):
 65.1493 -        return self._queue.qsize()
 65.1494 -    qsize = property(_get_qsize)
 65.1495 -
 65.1496 -
 65.1497 -
 65.1498 -try:
 65.1499 -    import fcntl
 65.1500 -except ImportError:
 65.1501 -    try:
 65.1502 -        from ctypes import windll, WinError
 65.1503 -    except ImportError:
 65.1504 -        def prevent_socket_inheritance(sock):
 65.1505 -            """Dummy function, since neither fcntl nor ctypes are available."""
 65.1506 -            pass
 65.1507 -    else:
 65.1508 -        def prevent_socket_inheritance(sock):
 65.1509 -            """Mark the given socket fd as non-inheritable (Windows)."""
 65.1510 -            if not windll.kernel32.SetHandleInformation(sock.fileno(), 1, 0):
 65.1511 -                raise WinError()
 65.1512 -else:
 65.1513 -    def prevent_socket_inheritance(sock):
 65.1514 -        """Mark the given socket fd as non-inheritable (POSIX)."""
 65.1515 -        fd = sock.fileno()
 65.1516 -        old_flags = fcntl.fcntl(fd, fcntl.F_GETFD)
 65.1517 -        fcntl.fcntl(fd, fcntl.F_SETFD, old_flags | fcntl.FD_CLOEXEC)
 65.1518 -
 65.1519 -
 65.1520 -class SSLAdapter(object):
 65.1521 -    """Base class for SSL driver library adapters.
 65.1522 -    
 65.1523 -    Required methods:
 65.1524 -    
 65.1525 -        * ``wrap(sock) -> (wrapped socket, ssl environ dict)``
 65.1526 -        * ``makefile(sock, mode='r', bufsize=DEFAULT_BUFFER_SIZE) -> socket file object``
 65.1527 -    """
 65.1528 -    
 65.1529 -    def __init__(self, certificate, private_key, certificate_chain=None):
 65.1530 -        self.certificate = certificate
 65.1531 -        self.private_key = private_key
 65.1532 -        self.certificate_chain = certificate_chain
 65.1533 -    
 65.1534 -    def wrap(self, sock):
 65.1535 -        raise NotImplemented
 65.1536 -    
 65.1537 -    def makefile(self, sock, mode='r', bufsize=DEFAULT_BUFFER_SIZE):
 65.1538 -        raise NotImplemented
 65.1539 -
 65.1540 -
 65.1541 -class HTTPServer(object):
 65.1542 -    """An HTTP server."""
 65.1543 -    
 65.1544 -    _bind_addr = "127.0.0.1"
 65.1545 -    _interrupt = None
 65.1546 -    
 65.1547 -    gateway = None
 65.1548 -    """A Gateway instance."""
 65.1549 -    
 65.1550 -    minthreads = None
 65.1551 -    """The minimum number of worker threads to create (default 10)."""
 65.1552 -    
 65.1553 -    maxthreads = None
 65.1554 -    """The maximum number of worker threads to create (default -1 = no limit)."""
 65.1555 -    
 65.1556 -    server_name = None
 65.1557 -    """The name of the server; defaults to socket.gethostname()."""
 65.1558 -    
 65.1559 -    protocol = "HTTP/1.1"
 65.1560 -    """The version string to write in the Status-Line of all HTTP responses.
 65.1561 -    
 65.1562 -    For example, "HTTP/1.1" is the default. This also limits the supported
 65.1563 -    features used in the response."""
 65.1564 -    
 65.1565 -    request_queue_size = 5
 65.1566 -    """The 'backlog' arg to socket.listen(); max queued connections (default 5)."""
 65.1567 -    
 65.1568 -    shutdown_timeout = 5
 65.1569 -    """The total time, in seconds, to wait for worker threads to cleanly exit."""
 65.1570 -    
 65.1571 -    timeout = 10
 65.1572 -    """The timeout in seconds for accepted connections (default 10)."""
 65.1573 -    
 65.1574 -    version = "CherryPy/3.2.0"
 65.1575 -    """A version string for the HTTPServer."""
 65.1576 -    
 65.1577 -    software = None
 65.1578 -    """The value to set for the SERVER_SOFTWARE entry in the WSGI environ.
 65.1579 -    
 65.1580 -    If None, this defaults to ``'%s Server' % self.version``."""
 65.1581 -    
 65.1582 -    ready = False
 65.1583 -    """An internal flag which marks whether the socket is accepting connections."""
 65.1584 -    
 65.1585 -    max_request_header_size = 0
 65.1586 -    """The maximum size, in bytes, for request headers, or 0 for no limit."""
 65.1587 -    
 65.1588 -    max_request_body_size = 0
 65.1589 -    """The maximum size, in bytes, for request bodies, or 0 for no limit."""
 65.1590 -    
 65.1591 -    nodelay = True
 65.1592 -    """If True (the default since 3.1), sets the TCP_NODELAY socket option."""
 65.1593 -    
 65.1594 -    ConnectionClass = HTTPConnection
 65.1595 -    """The class to use for handling HTTP connections."""
 65.1596 -    
 65.1597 -    ssl_adapter = None
 65.1598 -    """An instance of SSLAdapter (or a subclass).
 65.1599 -    
 65.1600 -    You must have the corresponding SSL driver library installed."""
 65.1601 -    
 65.1602 -    def __init__(self, bind_addr, gateway, minthreads=10, maxthreads=-1,
 65.1603 -                 server_name=None):
 65.1604 -        self.bind_addr = bind_addr
 65.1605 -        self.gateway = gateway
 65.1606 -        
 65.1607 -        self.requests = ThreadPool(self, min=minthreads or 1, max=maxthreads)
 65.1608 -        
 65.1609 -        if not server_name:
 65.1610 -            server_name = socket.gethostname()
 65.1611 -        self.server_name = server_name
 65.1612 -        self.clear_stats()
 65.1613 -    
 65.1614 -    def clear_stats(self):
 65.1615 -        self._start_time = None
 65.1616 -        self._run_time = 0
 65.1617 -        self.stats = {
 65.1618 -            'Enabled': False,
 65.1619 -            'Bind Address': lambda s: repr(self.bind_addr),
 65.1620 -            'Run time': lambda s: (not s['Enabled']) and 0 or self.runtime(),
 65.1621 -            'Accepts': 0,
 65.1622 -            'Accepts/sec': lambda s: s['Accepts'] / self.runtime(),
 65.1623 -            'Queue': lambda s: getattr(self.requests, "qsize", None),
 65.1624 -            'Threads': lambda s: len(getattr(self.requests, "_threads", [])),
 65.1625 -            'Threads Idle': lambda s: getattr(self.requests, "idle", None),
 65.1626 -            'Socket Errors': 0,
 65.1627 -            'Requests': lambda s: (not s['Enabled']) and 0 or sum([w['Requests'](w) for w
 65.1628 -                                       in s['Worker Threads'].values()], 0),
 65.1629 -            'Bytes Read': lambda s: (not s['Enabled']) and 0 or sum([w['Bytes Read'](w) for w
 65.1630 -                                         in s['Worker Threads'].values()], 0),
 65.1631 -            'Bytes Written': lambda s: (not s['Enabled']) and 0 or sum([w['Bytes Written'](w) for w
 65.1632 -                                            in s['Worker Threads'].values()], 0),
 65.1633 -            'Work Time': lambda s: (not s['Enabled']) and 0 or sum([w['Work Time'](w) for w
 65.1634 -                                         in s['Worker Threads'].values()], 0),
 65.1635 -            'Read Throughput': lambda s: (not s['Enabled']) and 0 or sum(
 65.1636 -                [w['Bytes Read'](w) / (w['Work Time'](w) or 1e-6)
 65.1637 -                 for w in s['Worker Threads'].values()], 0),
 65.1638 -            'Write Throughput': lambda s: (not s['Enabled']) and 0 or sum(
 65.1639 -                [w['Bytes Written'](w) / (w['Work Time'](w) or 1e-6)
 65.1640 -                 for w in s['Worker Threads'].values()], 0),
 65.1641 -            'Worker Threads': {},
 65.1642 -            }
 65.1643 -        logging.statistics["CherryPy HTTPServer %d" % id(self)] = self.stats
 65.1644 -    
 65.1645 -    def runtime(self):
 65.1646 -        if self._start_time is None:
 65.1647 -            return self._run_time
 65.1648 -        else:
 65.1649 -            return self._run_time + (time.time() - self._start_time)
 65.1650 -    
 65.1651 -    def __str__(self):
 65.1652 -        return "%s.%s(%r)" % (self.__module__, self.__class__.__name__,
 65.1653 -                              self.bind_addr)
 65.1654 -    
 65.1655 -    def _get_bind_addr(self):
 65.1656 -        return self._bind_addr
 65.1657 -    def _set_bind_addr(self, value):
 65.1658 -        if isinstance(value, tuple) and value[0] in ('', None):
 65.1659 -            # Despite the socket module docs, using '' does not
 65.1660 -            # allow AI_PASSIVE to work. Passing None instead
 65.1661 -            # returns '0.0.0.0' like we want. In other words:
 65.1662 -            #     host    AI_PASSIVE     result
 65.1663 -            #      ''         Y         192.168.x.y
 65.1664 -            #      ''         N         192.168.x.y
 65.1665 -            #     None        Y         0.0.0.0
 65.1666 -            #     None        N         127.0.0.1
 65.1667 -            # But since you can get the same effect with an explicit
 65.1668 -            # '0.0.0.0', we deny both the empty string and None as values.
 65.1669 -            raise ValueError("Host values of '' or None are not allowed. "
 65.1670 -                             "Use '0.0.0.0' (IPv4) or '::' (IPv6) instead "
 65.1671 -                             "to listen on all active interfaces.")
 65.1672 -        self._bind_addr = value
 65.1673 -    bind_addr = property(_get_bind_addr, _set_bind_addr,
 65.1674 -        doc="""The interface on which to listen for connections.
 65.1675 -        
 65.1676 -        For TCP sockets, a (host, port) tuple. Host values may be any IPv4
 65.1677 -        or IPv6 address, or any valid hostname. The string 'localhost' is a
 65.1678 -        synonym for '127.0.0.1' (or '::1', if your hosts file prefers IPv6).
 65.1679 -        The string '0.0.0.0' is a special IPv4 entry meaning "any active
 65.1680 -        interface" (INADDR_ANY), and '::' is the similar IN6ADDR_ANY for
 65.1681 -        IPv6. The empty string or None are not allowed.
 65.1682 -        
 65.1683 -        For UNIX sockets, supply the filename as a string.""")
 65.1684 -    
 65.1685 -    def start(self):
 65.1686 -        """Run the server forever."""
 65.1687 -        # We don't have to trap KeyboardInterrupt or SystemExit here,
 65.1688 -        # because cherrpy.server already does so, calling self.stop() for us.
 65.1689 -        # If you're using this server with another framework, you should
 65.1690 -        # trap those exceptions in whatever code block calls start().
 65.1691 -        self._interrupt = None
 65.1692 -        
 65.1693 -        if self.software is None:
 65.1694 -            self.software = "%s Server" % self.version
 65.1695 -        
 65.1696 -        # SSL backward compatibility
 65.1697 -        if (self.ssl_adapter is None and
 65.1698 -            getattr(self, 'ssl_certificate', None) and
 65.1699 -            getattr(self, 'ssl_private_key', None)):
 65.1700 -            warnings.warn(
 65.1701 -                    "SSL attributes are deprecated in CherryPy 3.2, and will "
 65.1702 -                    "be removed in CherryPy 3.3. Use an ssl_adapter attribute "
 65.1703 -                    "instead.",
 65.1704 -                    DeprecationWarning
 65.1705 -                )
 65.1706 -            try:
 65.1707 -                from cherrypy.wsgiserver.ssl_pyopenssl import pyOpenSSLAdapter
 65.1708 -            except ImportError:
 65.1709 -                pass
 65.1710 -            else:
 65.1711 -                self.ssl_adapter = pyOpenSSLAdapter(
 65.1712 -                    self.ssl_certificate, self.ssl_private_key,
 65.1713 -                    getattr(self, 'ssl_certificate_chain', None))
 65.1714 -        
 65.1715 -        # Select the appropriate socket
 65.1716 -        if isinstance(self.bind_addr, basestring):
 65.1717 -            # AF_UNIX socket
 65.1718 -            
 65.1719 -            # So we can reuse the socket...
 65.1720 -            try: os.unlink(self.bind_addr)
 65.1721 -            except: pass
 65.1722 -            
 65.1723 -            # So everyone can access the socket...
 65.1724 -            try: os.chmod(self.bind_addr, 0777)
 65.1725 -            except: pass
 65.1726 -            
 65.1727 -            info = [(socket.AF_UNIX, socket.SOCK_STREAM, 0, "", self.bind_addr)]
 65.1728 -        else:
 65.1729 -            # AF_INET or AF_INET6 socket
 65.1730 -            # Get the correct address family for our host (allows IPv6 addresses)
 65.1731 -            host, port = self.bind_addr
 65.1732 -            try:
 65.1733 -                info = socket.getaddrinfo(host, port, socket.AF_UNSPEC,
 65.1734 -                                          socket.SOCK_STREAM, 0, socket.AI_PASSIVE)
 65.1735 -            except socket.gaierror:
 65.1736 -                if ':' in self.bind_addr[0]:
 65.1737 -                    info = [(socket.AF_INET6, socket.SOCK_STREAM,
 65.1738 -                             0, "", self.bind_addr + (0, 0))]
 65.1739 -                else:
 65.1740 -                    info = [(socket.AF_INET, socket.SOCK_STREAM,
 65.1741 -                             0, "", self.bind_addr)]
 65.1742 -        
 65.1743 -        self.socket = None
 65.1744 -        msg = "No socket could be created"
 65.1745 -        for res in info:
 65.1746 -            af, socktype, proto, canonname, sa = res
 65.1747 -            try:
 65.1748 -                self.bind(af, socktype, proto)
 65.1749 -            except socket.error:
 65.1750 -                if self.socket:
 65.1751 -                    self.socket.close()
 65.1752 -                self.socket = None
 65.1753 -                continue
 65.1754 -            break
 65.1755 -        if not self.socket:
 65.1756 -            raise socket.error(msg)
 65.1757 -        
 65.1758 -        # Timeout so KeyboardInterrupt can be caught on Win32
 65.1759 -        self.socket.settimeout(1)
 65.1760 -        self.socket.listen(self.request_queue_size)
 65.1761 -        
 65.1762 -        # Create worker threads
 65.1763 -        self.requests.start()
 65.1764 -        
 65.1765 -        self.ready = True
 65.1766 -        self._start_time = time.time()
 65.1767 -        while self.ready:
 65.1768 -            self.tick()
 65.1769 -            if self.interrupt:
 65.1770 -                while self.interrupt is True:
 65.1771 -                    # Wait for self.stop() to complete. See _set_interrupt.
 65.1772 -                    time.sleep(0.1)
 65.1773 -                if self.interrupt:
 65.1774 -                    raise self.interrupt
 65.1775 -    
 65.1776 -    def bind(self, family, type, proto=0):
 65.1777 -        """Create (or recreate) the actual socket object."""
 65.1778 -        self.socket = socket.socket(family, type, proto)
 65.1779 -        prevent_socket_inheritance(self.socket)
 65.1780 -        self.socket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
 65.1781 -        if self.nodelay and not isinstance(self.bind_addr, str):
 65.1782 -            self.socket.setsockopt(socket.IPPROTO_TCP, socket.TCP_NODELAY, 1)
 65.1783 -        
 65.1784 -        if self.ssl_adapter is not None:
 65.1785 -            self.socket = self.ssl_adapter.bind(self.socket)
 65.1786 -        
 65.1787 -        # If listening on the IPV6 any address ('::' = IN6ADDR_ANY),
 65.1788 -        # activate dual-stack. See http://www.cherrypy.org/ticket/871.
 65.1789 -        if (hasattr(socket, 'AF_INET6') and family == socket.AF_INET6
 65.1790 -            and self.bind_addr[0] in ('::', '::0', '::0.0.0.0')):
 65.1791 -            try:
 65.1792 -                self.socket.setsockopt(socket.IPPROTO_IPV6, socket.IPV6_V6ONLY, 0)
 65.1793 -            except (AttributeError, socket.error):
 65.1794 -                # Apparently, the socket option is not available in
 65.1795 -                # this machine's TCP stack
 65.1796 -                pass
 65.1797 -        
 65.1798 -        self.socket.bind(self.bind_addr)
 65.1799 -    
 65.1800 -    def tick(self):
 65.1801 -        """Accept a new connection and put it on the Queue."""
 65.1802 -        try:
 65.1803 -            s, addr = self.socket.accept()
 65.1804 -            if self.stats['Enabled']:
 65.1805 -                self.stats['Accepts'] += 1
 65.1806 -            if not self.ready:
 65.1807 -                return
 65.1808 -            
 65.1809 -            prevent_socket_inheritance(s)
 65.1810 -            if hasattr(s, 'settimeout'):
 65.1811 -                s.settimeout(self.timeout)
 65.1812 -            
 65.1813 -            makefile = CP_fileobject
 65.1814 -            ssl_env = {}
 65.1815 -            # if ssl cert and key are set, we try to be a secure HTTP server
 65.1816 -            if self.ssl_adapter is not None:
 65.1817 -                try:
 65.1818 -                    s, ssl_env = self.ssl_adapter.wrap(s)
 65.1819 -                except NoSSLError:
 65.1820 -                    msg = ("The client sent a plain HTTP request, but "
 65.1821 -                           "this server only speaks HTTPS on this port.")
 65.1822 -                    buf = ["%s 400 Bad Request\r\n" % self.protocol,
 65.1823 -                           "Content-Length: %s\r\n" % len(msg),
 65.1824 -                           "Content-Type: text/plain\r\n\r\n",
 65.1825 -                           msg]
 65.1826 -                    
 65.1827 -                    wfile = CP_fileobject(s, "wb", DEFAULT_BUFFER_SIZE)
 65.1828 -                    try:
 65.1829 -                        wfile.sendall("".join(buf))
 65.1830 -                    except socket.error, x:
 65.1831 -                        if x.args[0] not in socket_errors_to_ignore:
 65.1832 -                            raise
 65.1833 -                    return
 65.1834 -                if not s:
 65.1835 -                    return
 65.1836 -                makefile = self.ssl_adapter.makefile
 65.1837 -                # Re-apply our timeout since we may have a new socket object
 65.1838 -                if hasattr(s, 'settimeout'):
 65.1839 -                    s.settimeout(self.timeout)
 65.1840 -            
 65.1841 -            conn = self.ConnectionClass(self, s, makefile)
 65.1842 -            
 65.1843 -            if not isinstance(self.bind_addr, basestring):
 65.1844 -                # optional values
 65.1845 -                # Until we do DNS lookups, omit REMOTE_HOST
 65.1846 -                if addr is None: # sometimes this can happen
 65.1847 -                    # figure out if AF_INET or AF_INET6.
 65.1848 -                    if len(s.getsockname()) == 2:
 65.1849 -                        # AF_INET
 65.1850 -                        addr = ('0.0.0.0', 0)
 65.1851 -                    else:
 65.1852 -                        # AF_INET6
 65.1853 -                        addr = ('::', 0)
 65.1854 -                conn.remote_addr = addr[0]
 65.1855 -                conn.remote_port = addr[1]
 65.1856 -            
 65.1857 -            conn.ssl_env = ssl_env
 65.1858 -            
 65.1859 -            self.requests.put(conn)
 65.1860 -        except socket.timeout:
 65.1861 -            # The only reason for the timeout in start() is so we can
 65.1862 -            # notice keyboard interrupts on Win32, which don't interrupt
 65.1863 -            # accept() by default
 65.1864 -            return
 65.1865 -        except socket.error, x:
 65.1866 -            if self.stats['Enabled']:
 65.1867 -                self.stats['Socket Errors'] += 1
 65.1868 -            if x.args[0] in socket_error_eintr:
 65.1869 -                # I *think* this is right. EINTR should occur when a signal
 65.1870 -                # is received during the accept() call; all docs say retry
 65.1871 -                # the call, and I *think* I'm reading it right that Python
 65.1872 -                # will then go ahead and poll for and handle the signal
 65.1873 -                # elsewhere. See http://www.cherrypy.org/ticket/707.
 65.1874 -                return
 65.1875 -            if x.args[0] in socket_errors_nonblocking:
 65.1876 -                # Just try again. See http://www.cherrypy.org/ticket/479.
 65.1877 -                return
 65.1878 -            if x.args[0] in socket_errors_to_ignore:
 65.1879 -                # Our socket was closed.
 65.1880 -                # See http://www.cherrypy.org/ticket/686.
 65.1881 -                return
 65.1882 -            raise
 65.1883 -    
 65.1884 -    def _get_interrupt(self):
 65.1885 -        return self._interrupt
 65.1886 -    def _set_interrupt(self, interrupt):
 65.1887 -        self._interrupt = True
 65.1888 -        self.stop()
 65.1889 -        self._interrupt = interrupt
 65.1890 -    interrupt = property(_get_interrupt, _set_interrupt,
 65.1891 -                         doc="Set this to an Exception instance to "
 65.1892 -                             "interrupt the server.")
 65.1893 -    
 65.1894 -    def stop(self):
 65.1895 -        """Gracefully shutdown a server that is serving forever."""
 65.1896 -        self.ready = False
 65.1897 -        if self._start_time is not None:
 65.1898 -            self._run_time += (time.time() - self._start_time)
 65.1899 -        self._start_time = None
 65.1900 -        
 65.1901 -        sock = getattr(self, "socket", None)
 65.1902 -        if sock:
 65.1903 -            if not isinstance(self.bind_addr, basestring):
 65.1904 -                # Touch our own socket to make accept() return immediately.
 65.1905 -                try:
 65.1906 -                    host, port = sock.getsockname()[:2]
 65.1907 -                except socket.error, x:
 65.1908 -                    if x.args[0] not in socket_errors_to_ignore:
 65.1909 -                        # Changed to use error code and not message
 65.1910 -                        # See http://www.cherrypy.org/ticket/860.
 65.1911 -                        raise
 65.1912 -                else:
 65.1913 -                    # Note that we're explicitly NOT using AI_PASSIVE,
 65.1914 -                    # here, because we want an actual IP to touch.
 65.1915 -                    # localhost won't work if we've bound to a public IP,
 65.1916 -                    # but it will if we bound to '0.0.0.0' (INADDR_ANY).
 65.1917 -                    for res in socket.getaddrinfo(host, port, socket.AF_UNSPEC,
 65.1918 -                                                  socket.SOCK_STREAM):
 65.1919 -                        af, socktype, proto, canonname, sa = res
 65.1920 -                        s = None
 65.1921 -                        try:
 65.1922 -                            s = socket.socket(af, socktype, proto)
 65.1923 -                            # See http://groups.google.com/group/cherrypy-users/
 65.1924 -                            #        browse_frm/thread/bbfe5eb39c904fe0
 65.1925 -                            s.settimeout(1.0)
 65.1926 -                            s.connect((host, port))
 65.1927 -                            s.close()
 65.1928 -                        except socket.error:
 65.1929 -                            if s:
 65.1930 -                                s.close()
 65.1931 -            if hasattr(sock, "close"):
 65.1932 -                sock.close()
 65.1933 -            self.socket = None
 65.1934 -        
 65.1935 -        self.requests.stop(self.shutdown_timeout)
 65.1936 -
 65.1937 -
 65.1938 -class Gateway(object):
 65.1939 -    
 65.1940 -    def __init__(self, req):
 65.1941 -        self.req = req
 65.1942 -    
 65.1943 -    def respond(self):
 65.1944 -        raise NotImplemented
 65.1945 -
 65.1946 -
 65.1947 -# These may either be wsgiserver.SSLAdapter subclasses or the string names
 65.1948 -# of such classes (in which case they will be lazily loaded).
 65.1949 -ssl_adapters = {
 65.1950 -    'builtin': 'cherrypy.wsgiserver.ssl_builtin.BuiltinSSLAdapter',
 65.1951 -    'pyopenssl': 'cherrypy.wsgiserver.ssl_pyopenssl.pyOpenSSLAdapter',
 65.1952 -    }
 65.1953 -
 65.1954 -def get_ssl_adapter_class(name='pyopenssl'):
 65.1955 -    adapter = ssl_adapters[name.lower()]
 65.1956 -    if isinstance(adapter, basestring):
 65.1957 -        last_dot = adapter.rfind(".")
 65.1958 -        attr_name = adapter[last_dot + 1:]
 65.1959 -        mod_path = adapter[:last_dot]
 65.1960 -        
 65.1961 -        try:
 65.1962 -            mod = sys.modules[mod_path]
 65.1963 -            if mod is None:
 65.1964 -                raise KeyError()
 65.1965 -        except KeyError:
 65.1966 -            # The last [''] is important.
 65.1967 -            mod = __import__(mod_path, globals(), locals(), [''])
 65.1968 -        
 65.1969 -        # Let an AttributeError propagate outward.
 65.1970 -        try:
 65.1971 -            adapter = getattr(mod, attr_name)
 65.1972 -        except AttributeError:
 65.1973 -            raise AttributeError("'%s' object has no attribute '%s'"
 65.1974 -                                 % (mod_path, attr_name))
 65.1975 -    
 65.1976 -    return adapter
 65.1977 -
 65.1978 -# -------------------------------- WSGI Stuff -------------------------------- #
 65.1979 -
 65.1980 -
 65.1981 -class CherryPyWSGIServer(HTTPServer):
 65.1982 -    
 65.1983 -    wsgi_version = (1, 0)
 65.1984 -    
 65.1985 -    def __init__(self, bind_addr, wsgi_app, numthreads=10, server_name=None,
 65.1986 -                 max=-1, request_queue_size=5, timeout=10, shutdown_timeout=5):
 65.1987 -        self.requests = ThreadPool(self, min=numthreads or 1, max=max)
 65.1988 -        self.wsgi_app = wsgi_app
 65.1989 -        self.gateway = wsgi_gateways[self.wsgi_version]
 65.1990 -        
 65.1991 -        self.bind_addr = bind_addr
 65.1992 -        if not server_name:
 65.1993 -            server_name = socket.gethostname()
 65.1994 -        self.server_name = server_name
 65.1995 -        self.request_queue_size = request_queue_size
 65.1996 -        
 65.1997 -        self.timeout = timeout
 65.1998 -        self.shutdown_timeout = shutdown_timeout
 65.1999 -        self.clear_stats()
 65.2000 -    
 65.2001 -    def _get_numthreads(self):
 65.2002 -        return self.requests.min
 65.2003 -    def _set_numthreads(self, value):
 65.2004 -        self.requests.min = value
 65.2005 -    numthreads = property(_get_numthreads, _set_numthreads)
 65.2006 -
 65.2007 -
 65.2008 -class WSGIGateway(Gateway):
 65.2009 -    
 65.2010 -    def __init__(self, req):
 65.2011 -        self.req = req
 65.2012 -        self.started_response = False
 65.2013 -        self.env = self.get_environ()
 65.2014 -        self.remaining_bytes_out = None
 65.2015 -    
 65.2016 -    def get_environ(self):
 65.2017 -        """Return a new environ dict targeting the given wsgi.version"""
 65.2018 -        raise NotImplemented
 65.2019 -    
 65.2020 -    def respond(self):
 65.2021 -        response = self.req.server.wsgi_app(self.env, self.start_response)
 65.2022 -        try:
 65.2023 -            for chunk in response:
 65.2024 -                # "The start_response callable must not actually transmit
 65.2025 -                # the response headers. Instead, it must store them for the
 65.2026 -                # server or gateway to transmit only after the first
 65.2027 -                # iteration of the application return value that yields
 65.2028 -                # a NON-EMPTY string, or upon the application's first
 65.2029 -                # invocation of the write() callable." (PEP 333)
 65.2030 -                if chunk:
 65.2031 -                    if isinstance(chunk, unicode):
 65.2032 -                        chunk = chunk.encode('ISO-8859-1')
 65.2033 -                    self.write(chunk)
 65.2034 -        finally:
 65.2035 -            if hasattr(response, "close"):
 65.2036 -                response.close()
 65.2037 -    
 65.2038 -    def start_response(self, status, headers, exc_info = None):
 65.2039 -        """WSGI callable to begin the HTTP response."""
 65.2040 -        # "The application may call start_response more than once,
 65.2041 -        # if and only if the exc_info argument is provided."
 65.2042 -        if self.started_response and not exc_info:
 65.2043 -            raise AssertionError("WSGI start_response called a second "
 65.2044 -                                 "time with no exc_info.")
 65.2045 -        self.started_response = True
 65.2046 -        
 65.2047 -        # "if exc_info is provided, and the HTTP headers have already been
 65.2048 -        # sent, start_response must raise an error, and should raise the
 65.2049 -        # exc_info tuple."
 65.2050 -        if self.req.sent_headers:
 65.2051 -            try:
 65.2052 -                raise exc_info[0], exc_info[1], exc_info[2]
 65.2053 -            finally:
 65.2054 -                exc_info = None
 65.2055 -        
 65.2056 -        self.req.status = status
 65.2057 -        for k, v in headers:
 65.2058 -            if not isinstance(k, str):
 65.2059 -                raise TypeError("WSGI response header key %r is not a byte string." % k)
 65.2060 -            if not isinstance(v, str):
 65.2061 -                raise TypeError("WSGI response header value %r is not a byte string." % v)
 65.2062 -            if k.lower() == 'content-length':
 65.2063 -                self.remaining_bytes_out = int(v)
 65.2064 -        self.req.outheaders.extend(headers)
 65.2065 -        
 65.2066 -        return self.write
 65.2067 -    
 65.2068 -    def write(self, chunk):
 65.2069 -        """WSGI callable to write unbuffered data to the client.
 65.2070 -        
 65.2071 -        This method is also used internally by start_response (to write
 65.2072 -        data from the iterable returned by the WSGI application).
 65.2073 -        """
 65.2074 -        if not self.started_response:
 65.2075 -            raise AssertionError("WSGI write called before start_response.")
 65.2076 -        
 65.2077 -        chunklen = len(chunk)
 65.2078 -        rbo = self.remaining_bytes_out
 65.2079 -        if rbo is not None and chunklen > rbo:
 65.2080 -            if not self.req.sent_headers:
 65.2081 -                # Whew. We can send a 500 to the client.
 65.2082 -                self.req.simple_response("500 Internal Server Error",
 65.2083 -                    "The requested resource returned more bytes than the "
 65.2084 -                    "declared Content-Length.")
 65.2085 -            else:
 65.2086 -                # Dang. We have probably already sent data. Truncate the chunk
 65.2087 -                # to fit (so the client doesn't hang) and raise an error later.
 65.2088 -                chunk = chunk[:rbo]
 65.2089 -        
 65.2090 -        if not self.req.sent_headers:
 65.2091 -            self.req.sent_headers = True
 65.2092 -            self.req.send_headers()
 65.2093 -        
 65.2094 -        self.req.write(chunk)
 65.2095 -        
 65.2096 -        if rbo is not None:
 65.2097 -            rbo -= chunklen
 65.2098 -            if rbo < 0:
 65.2099 -                raise ValueError(
 65.2100 -                    "Response body exceeds the declared Content-Length.")
 65.2101 -
 65.2102 -
 65.2103 -class WSGIGateway_10(WSGIGateway):
 65.2104 -    
 65.2105 -    def get_environ(self):
 65.2106 -        """Return a new environ dict targeting the given wsgi.version"""
 65.2107 -        req = self.req
 65.2108 -        env = {
 65.2109 -            # set a non-standard environ entry so the WSGI app can know what
 65.2110 -            # the *real* server protocol is (and what features to support).
 65.2111 -            # See http://www.faqs.org/rfcs/rfc2145.html.
 65.2112 -            'ACTUAL_SERVER_PROTOCOL': req.server.protocol,
 65.2113 -            'PATH_INFO': req.path,
 65.2114 -            'QUERY_STRING': req.qs,
 65.2115 -            'REMOTE_ADDR': req.conn.remote_addr or '',
 65.2116 -            'REMOTE_PORT': str(req.conn.remote_port or ''),
 65.2117 -            'REQUEST_METHOD': req.method,
 65.2118 -            'REQUEST_URI': req.uri,
 65.2119 -            'SCRIPT_NAME': '',
 65.2120 -            'SERVER_NAME': req.server.server_name,
 65.2121 -            # Bah. "SERVER_PROTOCOL" is actually the REQUEST protocol.
 65.2122 -            'SERVER_PROTOCOL': req.request_protocol,
 65.2123 -            'SERVER_SOFTWARE': req.server.software,
 65.2124 -            'wsgi.errors': sys.stderr,
 65.2125 -            'wsgi.input': req.rfile,
 65.2126 -            'wsgi.multiprocess': False,
 65.2127 -            'wsgi.multithread': True,
 65.2128 -            'wsgi.run_once': False,
 65.2129 -            'wsgi.url_scheme': req.scheme,
 65.2130 -            'wsgi.version': (1, 0),
 65.2131 -            }
 65.2132 -        
 65.2133 -        if isinstance(req.server.bind_addr, basestring):
 65.2134 -            # AF_UNIX. This isn't really allowed by WSGI, which doesn't
 65.2135 -            # address unix domain sockets. But it's better than nothing.
 65.2136 -            env["SERVER_PORT"] = ""
 65.2137 -        else:
 65.2138 -            env["SERVER_PORT"] = str(req.server.bind_addr[1])
 65.2139 -        
 65.2140 -        # Request headers
 65.2141 -        for k, v in req.inheaders.iteritems():
 65.2142 -            env["HTTP_" + k.upper().replace("-", "_")] = v
 65.2143 -        
 65.2144 -        # CONTENT_TYPE/CONTENT_LENGTH
 65.2145 -        ct = env.pop("HTTP_CONTENT_TYPE", None)
 65.2146 -        if ct is not None:
 65.2147 -            env["CONTENT_TYPE"] = ct
 65.2148 -        cl = env.pop("HTTP_CONTENT_LENGTH", None)
 65.2149 -        if cl is not None:
 65.2150 -            env["CONTENT_LENGTH"] = cl
 65.2151 -        
 65.2152 -        if req.conn.ssl_env:
 65.2153 -            env.update(req.conn.ssl_env)
 65.2154 -        
 65.2155 -        return env
 65.2156 -
 65.2157 -
 65.2158 -class WSGIGateway_u0(WSGIGateway_10):
 65.2159 -    
 65.2160 -    def get_environ(self):
 65.2161 -        """Return a new environ dict targeting the given wsgi.version"""
 65.2162 -        req = self.req
 65.2163 -        env_10 = WSGIGateway_10.get_environ(self)
 65.2164 -        env = dict([(k.decode('ISO-8859-1'), v) for k, v in env_10.iteritems()])
 65.2165 -        env[u'wsgi.version'] = ('u', 0)
 65.2166 -        
 65.2167 -        # Request-URI
 65.2168 -        env.setdefault(u'wsgi.url_encoding', u'utf-8')
 65.2169 -        try:
 65.2170 -            for key in [u"PATH_INFO", u"SCRIPT_NAME", u"QUERY_STRING"]:
 65.2171 -                env[key] = env_10[str(key)].decode(env[u'wsgi.url_encoding'])
 65.2172 -        except UnicodeDecodeError:
 65.2173 -            # Fall back to latin 1 so apps can transcode if needed.
 65.2174 -            env[u'wsgi.url_encoding'] = u'ISO-8859-1'
 65.2175 -            for key in [u"PATH_INFO", u"SCRIPT_NAME", u"QUERY_STRING"]:
 65.2176 -                env[key] = env_10[str(key)].decode(env[u'wsgi.url_encoding'])
 65.2177 -        
 65.2178 -        for k, v in sorted(env.items()):
 65.2179 -            if isinstance(v, str) and k not in ('REQUEST_URI', 'wsgi.input'):
 65.2180 -                env[k] = v.decode('ISO-8859-1')
 65.2181 -        
 65.2182 -        return env
 65.2183 -
 65.2184 -wsgi_gateways = {
 65.2185 -    (1, 0): WSGIGateway_10,
 65.2186 -    ('u', 0): WSGIGateway_u0,
 65.2187 -}
 65.2188 -
 65.2189 -class WSGIPathInfoDispatcher(object):
 65.2190 -    """A WSGI dispatcher for dispatch based on the PATH_INFO.
 65.2191 -    
 65.2192 -    apps: a dict or list of (path_prefix, app) pairs.
 65.2193 -    """
 65.2194 -    
 65.2195 -    def __init__(self, apps):
 65.2196 -        try:
 65.2197 -            apps = apps.items()
 65.2198 -        except AttributeError:
 65.2199 -            pass
 65.2200 -        
 65.2201 -        # Sort the apps by len(path), descending
 65.2202 -        apps.sort(cmp=lambda x,y: cmp(len(x[0]), len(y[0])))
 65.2203 -        apps.reverse()
 65.2204 -        
 65.2205 -        # The path_prefix strings must start, but not end, with a slash.
 65.2206 -        # Use "" instead of "/".
 65.2207 -        self.apps = [(p.rstrip("/"), a) for p, a in apps]
 65.2208 -    
 65.2209 -    def __call__(self, environ, start_response):
 65.2210 -        path = environ["PATH_INFO"] or "/"
 65.2211 -        for p, app in self.apps:
 65.2212 -            # The apps list should be sorted by length, descending.
 65.2213 -            if path.startswith(p + "/") or path == p:
 65.2214 -                environ = environ.copy()
 65.2215 -                environ["SCRIPT_NAME"] = environ["SCRIPT_NAME"] + p
 65.2216 -                environ["PATH_INFO"] = path[len(p):]
 65.2217 -                return app(environ, start_response)
 65.2218 -        
 65.2219 -        start_response('404 Not Found', [('Content-Type', 'text/plain'),
 65.2220 -                                         ('Content-Length', '0')])
 65.2221 -        return ['']
 65.2222 -
    66.1 --- a/OpenSecurity/install/web.py-0.37/web/wsgiserver/ssl_builtin.py	Thu Feb 20 15:40:48 2014 +0100
    66.2 +++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
    66.3 @@ -1,72 +0,0 @@
    66.4 -"""A library for integrating Python's builtin ``ssl`` library with CherryPy.
    66.5 -
    66.6 -The ssl module must be importable for SSL functionality.
    66.7 -
    66.8 -To use this module, set ``CherryPyWSGIServer.ssl_adapter`` to an instance of
    66.9 -``BuiltinSSLAdapter``.
   66.10 -"""
   66.11 -
   66.12 -try:
   66.13 -    import ssl
   66.14 -except ImportError:
   66.15 -    ssl = None
   66.16 -
   66.17 -from cherrypy import wsgiserver
   66.18 -
   66.19 -
   66.20 -class BuiltinSSLAdapter(wsgiserver.SSLAdapter):
   66.21 -    """A wrapper for integrating Python's builtin ssl module with CherryPy."""
   66.22 -    
   66.23 -    certificate = None
   66.24 -    """The filename of the server SSL certificate."""
   66.25 -    
   66.26 -    private_key = None
   66.27 -    """The filename of the server's private key file."""
   66.28 -    
   66.29 -    def __init__(self, certificate, private_key, certificate_chain=None):
   66.30 -        if ssl is None:
   66.31 -            raise ImportError("You must install the ssl module to use HTTPS.")
   66.32 -        self.certificate = certificate
   66.33 -        self.private_key = private_key
   66.34 -        self.certificate_chain = certificate_chain
   66.35 -    
   66.36 -    def bind(self, sock):
   66.37 -        """Wrap and return the given socket."""
   66.38 -        return sock
   66.39 -    
   66.40 -    def wrap(self, sock):
   66.41 -        """Wrap and return the given socket, plus WSGI environ entries."""
   66.42 -        try:
   66.43 -            s = ssl.wrap_socket(sock, do_handshake_on_connect=True,
   66.44 -                    server_side=True, certfile=self.certificate,
   66.45 -                    keyfile=self.private_key, ssl_version=ssl.PROTOCOL_SSLv23)
   66.46 -        except ssl.SSLError, e:
   66.47 -            if e.errno == ssl.SSL_ERROR_EOF:
   66.48 -                # This is almost certainly due to the cherrypy engine
   66.49 -                # 'pinging' the socket to assert it's connectable;
   66.50 -                # the 'ping' isn't SSL.
   66.51 -                return None, {}
   66.52 -            elif e.errno == ssl.SSL_ERROR_SSL:
   66.53 -                if e.args[1].endswith('http request'):
   66.54 -                    # The client is speaking HTTP to an HTTPS server.
   66.55 -                    raise wsgiserver.NoSSLError
   66.56 -            raise
   66.57 -        return s, self.get_environ(s)
   66.58 -    
   66.59 -    # TODO: fill this out more with mod ssl env
   66.60 -    def get_environ(self, sock):
   66.61 -        """Create WSGI environ entries to be merged into each request."""
   66.62 -        cipher = sock.cipher()
   66.63 -        ssl_environ = {
   66.64 -            "wsgi.url_scheme": "https",
   66.65 -            "HTTPS": "on",
   66.66 -            'SSL_PROTOCOL': cipher[1],
   66.67 -            'SSL_CIPHER': cipher[0]
   66.68 -##            SSL_VERSION_INTERFACE 	string 	The mod_ssl program version
   66.69 -##            SSL_VERSION_LIBRARY 	string 	The OpenSSL program version
   66.70 -            }
   66.71 -        return ssl_environ
   66.72 -    
   66.73 -    def makefile(self, sock, mode='r', bufsize=-1):
   66.74 -        return wsgiserver.CP_fileobject(sock, mode, bufsize)
   66.75 -
    67.1 --- a/OpenSecurity/install/web.py-0.37/web/wsgiserver/ssl_pyopenssl.py	Thu Feb 20 15:40:48 2014 +0100
    67.2 +++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
    67.3 @@ -1,256 +0,0 @@
    67.4 -"""A library for integrating pyOpenSSL with CherryPy.
    67.5 -
    67.6 -The OpenSSL module must be importable for SSL functionality.
    67.7 -You can obtain it from http://pyopenssl.sourceforge.net/
    67.8 -
    67.9 -To use this module, set CherryPyWSGIServer.ssl_adapter to an instance of
   67.10 -SSLAdapter. There are two ways to use SSL:
   67.11 -
   67.12 -Method One
   67.13 -----------
   67.14 -
   67.15 - * ``ssl_adapter.context``: an instance of SSL.Context.
   67.16 -
   67.17 -If this is not None, it is assumed to be an SSL.Context instance,
   67.18 -and will be passed to SSL.Connection on bind(). The developer is
   67.19 -responsible for forming a valid Context object. This approach is
   67.20 -to be preferred for more flexibility, e.g. if the cert and key are
   67.21 -streams instead of files, or need decryption, or SSL.SSLv3_METHOD
   67.22 -is desired instead of the default SSL.SSLv23_METHOD, etc. Consult
   67.23 -the pyOpenSSL documentation for complete options.
   67.24 -
   67.25 -Method Two (shortcut)
   67.26 ----------------------
   67.27 -
   67.28 - * ``ssl_adapter.certificate``: the filename of the server SSL certificate.
   67.29 - * ``ssl_adapter.private_key``: the filename of the server's private key file.
   67.30 -
   67.31 -Both are None by default. If ssl_adapter.context is None, but .private_key
   67.32 -and .certificate are both given and valid, they will be read, and the
   67.33 -context will be automatically created from them.
   67.34 -"""
   67.35 -
   67.36 -import socket
   67.37 -import threading
   67.38 -import time
   67.39 -
   67.40 -from cherrypy import wsgiserver
   67.41 -
   67.42 -try:
   67.43 -    from OpenSSL import SSL
   67.44 -    from OpenSSL import crypto
   67.45 -except ImportError:
   67.46 -    SSL = None
   67.47 -
   67.48 -
   67.49 -class SSL_fileobject(wsgiserver.CP_fileobject):
   67.50 -    """SSL file object attached to a socket object."""
   67.51 -    
   67.52 -    ssl_timeout = 3
   67.53 -    ssl_retry = .01
   67.54 -    
   67.55 -    def _safe_call(self, is_reader, call, *args, **kwargs):
   67.56 -        """Wrap the given call with SSL error-trapping.
   67.57 -        
   67.58 -        is_reader: if False EOF errors will be raised. If True, EOF errors
   67.59 -        will return "" (to emulate normal sockets).
   67.60 -        """
   67.61 -        start = time.time()
   67.62 -        while True:
   67.63 -            try:
   67.64 -                return call(*args, **kwargs)
   67.65 -            except SSL.WantReadError:
   67.66 -                # Sleep and try again. This is dangerous, because it means
   67.67 -                # the rest of the stack has no way of differentiating
   67.68 -                # between a "new handshake" error and "client dropped".
   67.69 -                # Note this isn't an endless loop: there's a timeout below.
   67.70 -                time.sleep(self.ssl_retry)
   67.71 -            except SSL.WantWriteError:
   67.72 -                time.sleep(self.ssl_retry)
   67.73 -            except SSL.SysCallError, e:
   67.74 -                if is_reader and e.args == (-1, 'Unexpected EOF'):
   67.75 -                    return ""
   67.76 -                
   67.77 -                errnum = e.args[0]
   67.78 -                if is_reader and errnum in wsgiserver.socket_errors_to_ignore:
   67.79 -                    return ""
   67.80 -                raise socket.error(errnum)
   67.81 -            except SSL.Error, e:
   67.82 -                if is_reader and e.args == (-1, 'Unexpected EOF'):
   67.83 -                    return ""
   67.84 -                
   67.85 -                thirdarg = None
   67.86 -                try:
   67.87 -                    thirdarg = e.args[0][0][2]
   67.88 -                except IndexError:
   67.89 -                    pass
   67.90 -                
   67.91 -                if thirdarg == 'http request':
   67.92 -                    # The client is talking HTTP to an HTTPS server.
   67.93 -                    raise wsgiserver.NoSSLError()
   67.94 -                
   67.95 -                raise wsgiserver.FatalSSLAlert(*e.args)
   67.96 -            except:
   67.97 -                raise
   67.98 -            
   67.99 -            if time.time() - start > self.ssl_timeout:
  67.100 -                raise socket.timeout("timed out")
  67.101 -    
  67.102 -    def recv(self, *args, **kwargs):
  67.103 -        buf = []
  67.104 -        r = super(SSL_fileobject, self).recv
  67.105 -        while True:
  67.106 -            data = self._safe_call(True, r, *args, **kwargs)
  67.107 -            buf.append(data)
  67.108 -            p = self._sock.pending()
  67.109 -            if not p:
  67.110 -                return "".join(buf)
  67.111 -    
  67.112 -    def sendall(self, *args, **kwargs):
  67.113 -        return self._safe_call(False, super(SSL_fileobject, self).sendall,
  67.114 -                               *args, **kwargs)
  67.115 -
  67.116 -    def send(self, *args, **kwargs):
  67.117 -        return self._safe_call(False, super(SSL_fileobject, self).send,
  67.118 -                               *args, **kwargs)
  67.119 -
  67.120 -
  67.121 -class SSLConnection:
  67.122 -    """A thread-safe wrapper for an SSL.Connection.
  67.123 -    
  67.124 -    ``*args``: the arguments to create the wrapped ``SSL.Connection(*args)``.
  67.125 -    """
  67.126 -    
  67.127 -    def __init__(self, *args):
  67.128 -        self._ssl_conn = SSL.Connection(*args)
  67.129 -        self._lock = threading.RLock()
  67.130 -    
  67.131 -    for f in ('get_context', 'pending', 'send', 'write', 'recv', 'read',
  67.132 -              'renegotiate', 'bind', 'listen', 'connect', 'accept',
  67.133 -              'setblocking', 'fileno', 'close', 'get_cipher_list',
  67.134 -              'getpeername', 'getsockname', 'getsockopt', 'setsockopt',
  67.135 -              'makefile', 'get_app_data', 'set_app_data', 'state_string',
  67.136 -              'sock_shutdown', 'get_peer_certificate', 'want_read',
  67.137 -              'want_write', 'set_connect_state', 'set_accept_state',
  67.138 -              'connect_ex', 'sendall', 'settimeout', 'gettimeout'):
  67.139 -        exec("""def %s(self, *args):
  67.140 -        self._lock.acquire()
  67.141 -        try:
  67.142 -            return self._ssl_conn.%s(*args)
  67.143 -        finally:
  67.144 -            self._lock.release()
  67.145 -""" % (f, f))
  67.146 -    
  67.147 -    def shutdown(self, *args):
  67.148 -        self._lock.acquire()
  67.149 -        try:
  67.150 -            # pyOpenSSL.socket.shutdown takes no args
  67.151 -            return self._ssl_conn.shutdown()
  67.152 -        finally:
  67.153 -            self._lock.release()
  67.154 -
  67.155 -
  67.156 -class pyOpenSSLAdapter(wsgiserver.SSLAdapter):
  67.157 -    """A wrapper for integrating pyOpenSSL with CherryPy."""
  67.158 -    
  67.159 -    context = None
  67.160 -    """An instance of SSL.Context."""
  67.161 -    
  67.162 -    certificate = None
  67.163 -    """The filename of the server SSL certificate."""
  67.164 -    
  67.165 -    private_key = None
  67.166 -    """The filename of the server's private key file."""
  67.167 -    
  67.168 -    certificate_chain = None
  67.169 -    """Optional. The filename of CA's intermediate certificate bundle.
  67.170 -    
  67.171 -    This is needed for cheaper "chained root" SSL certificates, and should be
  67.172 -    left as None if not required."""
  67.173 -    
  67.174 -    def __init__(self, certificate, private_key, certificate_chain=None):
  67.175 -        if SSL is None:
  67.176 -            raise ImportError("You must install pyOpenSSL to use HTTPS.")
  67.177 -        
  67.178 -        self.context = None
  67.179 -        self.certificate = certificate
  67.180 -        self.private_key = private_key
  67.181 -        self.certificate_chain = certificate_chain
  67.182 -        self._environ = None
  67.183 -    
  67.184 -    def bind(self, sock):
  67.185 -        """Wrap and return the given socket."""
  67.186 -        if self.context is None:
  67.187 -            self.context = self.get_context()
  67.188 -        conn = SSLConnection(self.context, sock)
  67.189 -        self._environ = self.get_environ()
  67.190 -        return conn
  67.191 -    
  67.192 -    def wrap(self, sock):
  67.193 -        """Wrap and return the given socket, plus WSGI environ entries."""
  67.194 -        return sock, self._environ.copy()
  67.195 -    
  67.196 -    def get_context(self):
  67.197 -        """Return an SSL.Context from self attributes."""
  67.198 -        # See http://aspn.activestate.com/ASPN/Cookbook/Python/Recipe/442473
  67.199 -        c = SSL.Context(SSL.SSLv23_METHOD)
  67.200 -        c.use_privatekey_file(self.private_key)
  67.201 -        if self.certificate_chain:
  67.202 -            c.load_verify_locations(self.certificate_chain)
  67.203 -        c.use_certificate_file(self.certificate)
  67.204 -        return c
  67.205 -    
  67.206 -    def get_environ(self):
  67.207 -        """Return WSGI environ entries to be merged into each request."""
  67.208 -        ssl_environ = {
  67.209 -            "HTTPS": "on",
  67.210 -            # pyOpenSSL doesn't provide access to any of these AFAICT
  67.211 -##            'SSL_PROTOCOL': 'SSLv2',
  67.212 -##            SSL_CIPHER 	string 	The cipher specification name
  67.213 -##            SSL_VERSION_INTERFACE 	string 	The mod_ssl program version
  67.214 -##            SSL_VERSION_LIBRARY 	string 	The OpenSSL program version
  67.215 -            }
  67.216 -        
  67.217 -        if self.certificate:
  67.218 -            # Server certificate attributes
  67.219 -            cert = open(self.certificate, 'rb').read()
  67.220 -            cert = crypto.load_certificate(crypto.FILETYPE_PEM, cert)
  67.221 -            ssl_environ.update({
  67.222 -                'SSL_SERVER_M_VERSION': cert.get_version(),
  67.223 -                'SSL_SERVER_M_SERIAL': cert.get_serial_number(),
  67.224 -##                'SSL_SERVER_V_START': Validity of server's certificate (start time),
  67.225 -##                'SSL_SERVER_V_END': Validity of server's certificate (end time),
  67.226 -                })
  67.227 -            
  67.228 -            for prefix, dn in [("I", cert.get_issuer()),
  67.229 -                               ("S", cert.get_subject())]:
  67.230 -                # X509Name objects don't seem to have a way to get the
  67.231 -                # complete DN string. Use str() and slice it instead,
  67.232 -                # because str(dn) == "<X509Name object '/C=US/ST=...'>"
  67.233 -                dnstr = str(dn)[18:-2]
  67.234 -                
  67.235 -                wsgikey = 'SSL_SERVER_%s_DN' % prefix
  67.236 -                ssl_environ[wsgikey] = dnstr
  67.237 -                
  67.238 -                # The DN should be of the form: /k1=v1/k2=v2, but we must allow
  67.239 -                # for any value to contain slashes itself (in a URL).
  67.240 -                while dnstr:
  67.241 -                    pos = dnstr.rfind("=")
  67.242 -                    dnstr, value = dnstr[:pos], dnstr[pos + 1:]
  67.243 -                    pos = dnstr.rfind("/")
  67.244 -                    dnstr, key = dnstr[:pos], dnstr[pos + 1:]
  67.245 -                    if key and value:
  67.246 -                        wsgikey = 'SSL_SERVER_%s_DN_%s' % (prefix, key)
  67.247 -                        ssl_environ[wsgikey] = value
  67.248 -        
  67.249 -        return ssl_environ
  67.250 -    
  67.251 -    def makefile(self, sock, mode='r', bufsize=-1):
  67.252 -        if SSL and isinstance(sock, SSL.ConnectionType):
  67.253 -            timeout = sock.gettimeout()
  67.254 -            f = SSL_fileobject(sock, mode, bufsize)
  67.255 -            f.ssl_timeout = timeout
  67.256 -            return f
  67.257 -        else:
  67.258 -            return wsgiserver.CP_fileobject(sock, mode, bufsize)
  67.259 -