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>▶</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'<'&">'
19.151 - """
19.152 - text = text.replace(u"&", u"&") # Must be done first!
19.153 - text = text.replace(u"<", u"<")
19.154 - text = text.replace(u">", u">")
19.155 - text = text.replace(u"'", u"'")
19.156 - text = text.replace(u'"', u""")
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'<'&">')
19.164 - u'<\'&">'
19.165 - """
19.166 - text = text.replace(u""", u'"')
19.167 - text = text.replace(u"'", u"'")
19.168 - text = text.replace(u">", u">")
19.169 - text = text.replace(u"<", u"<")
19.170 - text = text.replace(u"&", 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'<'&">'
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'<html>\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('<', '<')
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>▶</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'<'&">'
49.151 - """
49.152 - text = text.replace(u"&", u"&") # Must be done first!
49.153 - text = text.replace(u"<", u"<")
49.154 - text = text.replace(u">", u">")
49.155 - text = text.replace(u"'", u"'")
49.156 - text = text.replace(u'"', u""")
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'<'&">')
49.164 - u'<\'&">'
49.165 - """
49.166 - text = text.replace(u""", u'"')
49.167 - text = text.replace(u"'", u"'")
49.168 - text = text.replace(u">", u">")
49.169 - text = text.replace(u"<", u"<")
49.170 - text = text.replace(u"&", 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'<'&">'
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'<html>\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('<', '<')
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 -