Skip to content
Projects
Groups
Snippets
Help
Loading...
Help
Support
Keyboard shortcuts
?
Submit feedback
Contribute to GitLab
Sign in / Register
Toggle navigation
Z
Zope
Project overview
Project overview
Details
Activity
Releases
Repository
Repository
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Issues
0
Issues
0
List
Boards
Labels
Milestones
Merge Requests
0
Merge Requests
0
Analytics
Analytics
Repository
Value Stream
Wiki
Wiki
Snippets
Snippets
Members
Members
Collapse sidebar
Close sidebar
Activity
Graph
Create a new issue
Commits
Issue Boards
Open sidebar
Kirill Smelkov
Zope
Commits
accff8b5
Commit
accff8b5
authored
Feb 18, 1999
by
Jim Fulton
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
Major decomposition!
parent
7fa9e2be
Changes
9
Show whitespace changes
Inline
Side-by-side
Showing
9 changed files
with
2106 additions
and
1535 deletions
+2106
-1535
lib/python/ZPublisher/BaseRequest.py
lib/python/ZPublisher/BaseRequest.py
+464
-0
lib/python/ZPublisher/BaseResponse.py
lib/python/ZPublisher/BaseResponse.py
+189
-0
lib/python/ZPublisher/HTTPRequest.py
lib/python/ZPublisher/HTTPRequest.py
+570
-0
lib/python/ZPublisher/HTTPResponse.py
lib/python/ZPublisher/HTTPResponse.py
+686
-0
lib/python/ZPublisher/Publish.py
lib/python/ZPublisher/Publish.py
+87
-694
lib/python/ZPublisher/Request.py
lib/python/ZPublisher/Request.py
+3
-250
lib/python/ZPublisher/Response.py
lib/python/ZPublisher/Response.py
+3
-569
lib/python/ZPublisher/Test.py
lib/python/ZPublisher/Test.py
+12
-22
lib/python/ZPublisher/maybe_lock.py
lib/python/ZPublisher/maybe_lock.py
+92
-0
No files found.
lib/python/ZPublisher/BaseRequest.py
0 → 100644
View file @
accff8b5
##############################################################################
#
# Zope Public License (ZPL) Version 0.9.4
# ---------------------------------------
#
# Copyright (c) Digital Creations. All rights reserved.
#
# Redistribution and use in source and binary forms, with or
# without modification, are permitted provided that the following
# conditions are met:
#
# 1. Redistributions in source code must retain the above
# copyright notice, this list of conditions, and the following
# disclaimer.
#
# 2. Redistributions in binary form must reproduce the above
# copyright notice, this list of conditions, and the following
# disclaimer in the documentation and/or other materials
# provided with the distribution.
#
# 3. Any use, including use of the Zope software to operate a
# website, must either comply with the terms described below
# under "Attribution" or alternatively secure a separate
# license from Digital Creations.
#
# 4. All advertising materials, documentation, or technical papers
# mentioning features derived from or use of this software must
# display the following acknowledgement:
#
# "This product includes software developed by Digital
# Creations for use in the Z Object Publishing Environment
# (http://www.zope.org/)."
#
# 5. Names associated with Zope or Digital Creations must not be
# used to endorse or promote products derived from this
# software without prior written permission from Digital
# Creations.
#
# 6. Redistributions of any form whatsoever must retain the
# following acknowledgment:
#
# "This product includes software developed by Digital
# Creations for use in the Z Object Publishing Environment
# (http://www.zope.org/)."
#
# 7. Modifications are encouraged but must be packaged separately
# as patches to official Zope releases. Distributions that do
# not clearly separate the patches from the original work must
# be clearly labeled as unofficial distributions.
#
# Disclaimer
#
# THIS SOFTWARE IS PROVIDED BY DIGITAL CREATIONS ``AS IS'' AND
# ANY EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
# FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT
# SHALL DIGITAL CREATIONS OR ITS CONTRIBUTORS BE LIABLE FOR ANY
# DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
# CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
# PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
# ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
# IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
# THE POSSIBILITY OF SUCH DAMAGE.
#
# Attribution
#
# Individuals or organizations using this software as a web site
# must provide attribution by placing the accompanying "button"
# and a link to the accompanying "credits page" on the website's
# main entry point. In cases where this placement of
# attribution is not feasible, a separate arrangment must be
# concluded with Digital Creations. Those using the software
# for purposes other than web sites must provide a corresponding
# attribution in locations that include a copyright using a
# manner best suited to the application environment.
#
# This software consists of contributions made by Digital
# Creations and many individuals on behalf of Digital Creations.
# Specific attributions are listed in the accompanying credits
# file.
#
##############################################################################
__version__
=
'$Revision: 1.1 $'
[
11
:
-
2
]
from
string
import
join
,
split
,
find
,
rfind
,
lower
,
upper
from
urllib
import
quote
UNSPECIFIED_ROLES
=
''
try
:
from
ExtensionClass
import
Base
class
RequestContainer
(
Base
):
def
__init__
(
self
,
**
kw
):
for
k
,
v
in
kw
.
items
():
self
.
__dict__
[
k
]
=
v
def
manage_property_types
(
self
):
return
type_converters
.
keys
()
except
:
class
RequestContainer
:
def
__init__
(
self
,
**
kw
):
for
k
,
v
in
kw
.
items
():
self
.
__dict__
[
k
]
=
v
_marker
=
[]
class
BaseRequest
:
"""Provide basic ZPublisher request management
This object provides access to request data. Request data may
vary depending on the protocol used.
Request objects are created by the object publisher and will be
passed to published objects through the argument name, REQUEST.
The request object is a mapping object that represents a
collection of variable to value mappings.
"""
common
=
{}
# Common request data
_auth
=
None
def
__init__
(
self
,
other
=
None
,
**
kw
):
if
other
is
None
:
other
=
kw
else
:
for
k
,
v
in
kw
.
items
():
other
[
k
]
=
v
self
.
other
=
other
def
__setitem__
(
self
,
key
,
value
):
"""Set application variables
This method is used to set a variable in the requests "other"
category.
"""
self
.
other
[
key
]
=
value
set
=
__setitem__
def
__getitem__
(
self
,
key
,
default
=
_marker
,
# Any special internal marker will do
):
"""Get a variable value
Return a value for the required variable name.
The value will be looked up from one of the request data
categories. The search order is environment variables,
other variables, form data, and then cookies.
"""
if
key
==
'REQUEST'
:
return
self
v
=
self
.
other
.
get
(
key
,
_marker
)
if
v
is
not
_marker
:
return
v
v
=
self
.
common
.
get
(
key
,
default
)
if
v
is
not
_marker
:
return
v
raise
KeyError
,
key
__getattr__
=
get
=
__getitem__
def
has_key
(
self
,
key
):
return
self
.
get
(
key
,
_marker
)
is
not
_marker
def
keys
(
self
):
keys
=
{}
keys
.
update
(
self
.
common
)
keys
.
update
(
self
.
other
)
return
keys
.
keys
()
def
items
(
self
):
result
=
[]
get
=
self
.
get
for
k
in
self
.
keys
():
result
.
append
((
k
,
get
(
k
)))
return
result
def
values
(
self
):
result
=
[]
get
=
self
.
get
for
k
in
self
.
keys
():
result
.
append
(
get
(
k
))
return
result
def
__str__
(
self
):
return
join
(
map
(
lambda
item
:
"%s:
\
t
%s"
%
item
,
self
.
items
()),
"
\
n
"
)
__repr__
=
__str__
def
traverse
(
self
,
path
,
response
=
None
):
"""Traverse the object space
The REQUEST must already have a PARENTS item with at least one
object in it. This is typically the root object.
"""
request
=
self
request_get
=
request
.
get
if
response
is
None
:
response
=
self
.
response
debug_mode
=
response
.
debug_mode
if
path
[:
1
]
!=
'/'
:
path
=
'/'
+
path
if
path
[
-
1
:]
!=
'/'
:
path
=
path
+
'/'
if
find
(
path
,
'/.'
)
>=
0
:
path
=
join
(
split
(
path
,
'/./'
),
'/'
)
l
=
find
(
path
,
'/../'
,
1
)
while
l
>
0
:
p1
=
path
[:
l
]
path
=
path
[:
rfind
(
p1
,
'/'
)
+
1
]
+
path
[
l
+
4
:]
l
=
find
(
path
,
'/../'
,
1
)
path
=
path
[
1
:
-
1
]
path
=
split
(
path
,
'/'
)
while
path
and
not
path
[
0
]:
path
=
path
[
1
:]
method
=
upper
(
request_get
(
'REQUEST_METHOD'
,
''
))
baseflag
=
0
if
method
==
'GET'
or
method
==
'POST'
:
method
=
'index_html'
else
:
baseflag
=
1
URL
=
request
[
'URL'
]
parents
=
request
[
'PARENTS'
]
object
=
parents
[
-
1
]
del
parents
[
-
1
]
if
hasattr
(
object
,
'__roles__'
):
roles
=
object
.
__roles__
else
:
roles
=
UNSPECIFIED_ROLES
# if the top object has a __bobo_traverse__ method, then use it
# to possibly traverse to an alternate top-level object.
if
hasattr
(
object
,
'__bobo_traverse__'
):
try
:
object
=
object
.
__bobo_traverse__
(
request
)
except
:
pass
# Get default object if no path was specified:
if
not
path
:
if
not
method
:
return
response
.
forbiddenError
(
entry_name
)
entry_name
=
method
try
:
if
hasattr
(
object
,
entry_name
):
response
.
setBase
(
URL
)
path
=
[
entry_name
]
else
:
try
:
if
object
.
has_key
(
entry_name
):
path
=
[
entry_name
]
except
:
pass
except
:
pass
# Traverse the URL to find the object:
if
hasattr
(
object
,
'__of__'
):
# Try to bind the top-level object to the request
object
=
object
.
__of__
(
RequestContainer
(
REQUEST
=
request
))
steps
=
[]
path
.
reverse
()
while
path
:
entry_name
=
path
[
-
1
]
del
path
[
-
1
]
URL
=
"%s/%s"
%
(
URL
,
quote
(
entry_name
))
got
=
0
if
entry_name
:
if
entry_name
[:
1
]
==
'_'
:
if
debug_mode
:
return
response
.
debugError
(
"Object name begins with an underscore at: %s"
%
URL
)
else
:
return
response
.
forbiddenError
(
entry_name
)
if
hasattr
(
object
,
'__bobo_traverse__'
):
request
[
'URL'
]
=
URL
subobject
=
object
.
__bobo_traverse__
(
request
,
entry_name
)
if
type
(
subobject
)
is
type
(())
and
len
(
subobject
)
>
1
:
while
len
(
subobject
)
>
2
:
parents
.
append
(
subobject
[
0
])
subobject
=
subobject
[
1
:]
object
,
subobject
=
subobject
else
:
try
:
if
baseflag
and
hasattr
(
object
,
'aq_base'
):
subobject
=
getattr
(
object
.
aq_base
,
entry_name
)
else
:
subobject
=
getattr
(
object
,
entry_name
)
except
AttributeError
:
got
=
1
try
:
subobject
=
object
[
entry_name
]
except
(
KeyError
,
IndexError
,
TypeError
,
AttributeError
):
if
entry_name
==
'.'
:
subobject
=
object
elif
entry_name
==
'..'
and
parents
:
subobject
=
parents
[
-
1
]
elif
debug_mode
:
return
response
.
debugError
(
"Cannot locate object at: %s"
%
URL
)
else
:
return
response
.
notFoundError
(
URL
)
if
subobject
is
object
and
entry_name
==
'.'
:
URL
=
URL
[:
rfind
(
URL
,
'/'
)]
else
:
try
:
try
:
doc
=
subobject
.
__doc__
except
:
doc
=
getattr
(
object
,
entry_name
+
'__doc__'
)
if
not
doc
:
raise
AttributeError
,
entry_name
except
:
if
debug_mode
:
return
response
.
debugError
(
"Missing doc string at: %s"
%
URL
)
else
:
return
response
.
notFoundError
(
"%s"
%
(
URL
))
if
hasattr
(
subobject
,
'__roles__'
):
roles
=
subobject
.
__roles__
else
:
if
not
got
:
roleshack
=
entry_name
+
'__roles__'
if
hasattr
(
object
,
roleshack
):
roles
=
getattr
(
object
,
roleshack
)
# Promote subobject to object
parents
.
append
(
object
)
object
=
subobject
steps
.
append
(
entry_name
)
# Check for method:
if
not
path
:
if
method
and
hasattr
(
object
,
method
)
and
entry_name
!=
method
:
request
.
_hacked_path
=
1
path
=
[
method
]
else
:
if
(
hasattr
(
object
,
'__call__'
)
and
hasattr
(
object
.
__call__
,
'__roles__'
)):
roles
=
object
.
__call__
.
__roles__
if
request
.
_hacked_path
:
i
=
rfind
(
URL
,
'/'
)
if
i
>
0
:
response
.
setBase
(
URL
[:
i
])
# THIS LOOKS WRONG!
if
entry_name
!=
method
and
method
!=
'index_html'
:
if
debug_mode
:
response
.
debugError
(
"Method %s not found at: %s"
%
(
method
,
URL
))
else
:
response
.
notFoundError
(
method
)
request
.
steps
=
steps
parents
.
reverse
()
# Do authorization checks
user
=
groups
=
None
i
=
0
if
roles
is
not
None
:
last_parent_index
=
len
(
parents
)
if
hasattr
(
object
,
'__allow_groups__'
):
groups
=
object
.
__allow_groups__
inext
=
0
else
:
inext
=
None
for
i
in
range
(
last_parent_index
):
if
hasattr
(
parents
[
i
],
'__allow_groups__'
):
groups
=
parents
[
i
].
__allow_groups__
inext
=
i
+
1
break
if
inext
is
not
None
:
i
=
inext
if
hasattr
(
groups
,
'validate'
):
v
=
groups
.
validate
else
:
v
=
old_validation
auth
=
request
.
_auth
if
v
is
old_validation
and
roles
is
UNSPECIFIED_ROLES
:
# No roles, so if we have a named group, get roles from
# group keys
if
hasattr
(
groups
,
'keys'
):
roles
=
groups
.
keys
()
else
:
try
:
groups
=
groups
()
except
:
pass
try
:
roles
=
groups
.
keys
()
except
:
pass
if
groups
is
None
:
# Public group, hack structures to get it to validate
roles
=
None
auth
=
''
if
v
is
old_validation
:
user
=
old_validation
(
groups
,
request
,
auth
,
roles
)
elif
roles
is
UNSPECIFIED_ROLES
:
user
=
v
(
request
,
auth
)
else
:
user
=
v
(
request
,
auth
,
roles
)
while
user
is
None
and
i
<
last_parent_index
:
parent
=
parents
[
i
]
i
=
i
+
1
if
hasattr
(
parent
,
'__allow_groups__'
):
groups
=
parent
.
__allow_groups__
else
:
continue
if
hasattr
(
groups
,
'validate'
):
v
=
groups
.
validate
else
:
v
=
old_validation
if
v
is
old_validation
:
user
=
old_validation
(
groups
,
request
,
auth
,
roles
)
elif
roles
is
UNSPECIFIED_ROLES
:
user
=
v
(
request
,
auth
)
else
:
user
=
v
(
request
,
auth
,
roles
)
if
user
is
None
and
roles
!=
UNSPECIFIED_ROLES
:
response
.
unauthorized
()
steps
=
join
(
steps
[:
-
i
],
'/'
)
if
user
is
not
None
:
request
[
'AUTHENTICATED_USER'
]
=
user
request
[
'AUTHENTICATION_PATH'
]
=
steps
request
[
'URL'
]
=
URL
return
object
def
old_validation
(
groups
,
request
,
auth
,
roles
=
UNSPECIFIED_ROLES
):
if
auth
:
auth
=
request
.
_authUserPW
()
if
auth
:
name
,
password
=
auth
elif
roles
is
None
:
return
''
else
:
return
None
elif
request
.
environ
.
has_key
(
'REMOTE_USER'
):
name
=
request
.
environ
[
'REMOTE_USER'
]
password
=
None
else
:
if
roles
is
None
:
return
''
return
None
if
roles
is
None
:
return
name
keys
=
None
try
:
keys
=
groups
.
keys
except
:
try
:
groups
=
groups
()
# Maybe it was a method defining a group
keys
=
groups
.
keys
except
:
pass
if
keys
is
not
None
:
# OK, we have a named group, so apply the roles to the named
# group.
if
roles
is
UNSPECIFIED_ROLES
:
roles
=
keys
()
g
=
[]
for
role
in
roles
:
if
groups
.
has_key
(
role
):
g
.
append
(
groups
[
role
])
groups
=
g
for
d
in
groups
:
if
d
.
has_key
(
name
)
and
(
d
[
name
]
==
password
or
password
is
None
):
return
name
if
keys
is
None
:
# Not a named group, so don't go further
raise
'Forbidden'
,
(
"""<strong>You are not authorized to access this resource"""
)
return
None
lib/python/ZPublisher/BaseResponse.py
0 → 100644
View file @
accff8b5
##############################################################################
#
# Zope Public License (ZPL) Version 0.9.4
# ---------------------------------------
#
# Copyright (c) Digital Creations. All rights reserved.
#
# Redistribution and use in source and binary forms, with or
# without modification, are permitted provided that the following
# conditions are met:
#
# 1. Redistributions in source code must retain the above
# copyright notice, this list of conditions, and the following
# disclaimer.
#
# 2. Redistributions in binary form must reproduce the above
# copyright notice, this list of conditions, and the following
# disclaimer in the documentation and/or other materials
# provided with the distribution.
#
# 3. Any use, including use of the Zope software to operate a
# website, must either comply with the terms described below
# under "Attribution" or alternatively secure a separate
# license from Digital Creations.
#
# 4. All advertising materials, documentation, or technical papers
# mentioning features derived from or use of this software must
# display the following acknowledgement:
#
# "This product includes software developed by Digital
# Creations for use in the Z Object Publishing Environment
# (http://www.zope.org/)."
#
# 5. Names associated with Zope or Digital Creations must not be
# used to endorse or promote products derived from this
# software without prior written permission from Digital
# Creations.
#
# 6. Redistributions of any form whatsoever must retain the
# following acknowledgment:
#
# "This product includes software developed by Digital
# Creations for use in the Z Object Publishing Environment
# (http://www.zope.org/)."
#
# 7. Modifications are encouraged but must be packaged separately
# as patches to official Zope releases. Distributions that do
# not clearly separate the patches from the original work must
# be clearly labeled as unofficial distributions.
#
# Disclaimer
#
# THIS SOFTWARE IS PROVIDED BY DIGITAL CREATIONS ``AS IS'' AND
# ANY EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
# FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT
# SHALL DIGITAL CREATIONS OR ITS CONTRIBUTORS BE LIABLE FOR ANY
# DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
# CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
# PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
# ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
# IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
# THE POSSIBILITY OF SUCH DAMAGE.
#
# Attribution
#
# Individuals or organizations using this software as a web site
# must provide attribution by placing the accompanying "button"
# and a link to the accompanying "credits page" on the website's
# main entry point. In cases where this placement of
# attribution is not feasible, a separate arrangment must be
# concluded with Digital Creations. Those using the software
# for purposes other than web sites must provide a corresponding
# attribution in locations that include a copyright using a
# manner best suited to the application environment.
#
# This software consists of contributions made by Digital
# Creations and many individuals on behalf of Digital Creations.
# Specific attributions are listed in the accompanying credits
# file.
#
##############################################################################
'''CGI Response Output formatter
$Id: BaseResponse.py,v 1.1 1999/02/18 17:17:55 jim Exp $'''
__version__
=
'$Revision: 1.1 $'
[
11
:
-
2
]
import
string
,
types
,
sys
,
regex
from
string
import
find
,
rfind
,
lower
,
upper
,
strip
,
split
,
join
,
translate
from
types
import
StringType
,
InstanceType
class
BaseResponse
:
"""Base Response Class
What should be here?
"""
debug_mode
=
None
_auth
=
None
def
__init__
(
self
,
stdout
,
stderr
,
body
=
''
,
headers
=
None
,
status
=
None
,
cookies
=
None
):
self
.
stdout
=
stdout
self
.
stderr
=
stderr
self
.
body
=
body
if
headers
is
None
:
headers
=
{}
self
.
headers
=
headers
self
.
status
=
status
if
cookies
is
None
:
cookies
=
{}
self
.
cookies
=
cookies
def
setStatus
(
self
,
status
,
reason
=
None
):
self
.
status
=
status
def
setHeader
(
self
,
name
,
value
):
self
.
headers
[
n
]
=
value
__setitem__
=
setHeader
def
setBody
(
self
,
body
):
self
.
body
=
body
def
getStatus
(
self
):
'Returns the current HTTP status code as an integer. '
return
self
.
status
def
setCookie
(
self
,
name
,
value
,
**
kw
):
'''
\
Set an HTTP cookie on the browser
The response will include an HTTP header that sets a cookie on
cookie-enabled browsers with a key "name" and value
"value". This overwrites any previously set value for the
cookie in the Response object.
'''
cookies
=
self
.
cookies
if
cookies
.
has_key
(
name
):
cookie
=
cookies
[
name
]
else
:
cookie
=
cookies
[
name
]
=
{}
for
k
,
v
in
kw
.
items
():
cookie
[
k
]
=
v
cookie
[
'value'
]
=
value
def
appendBody
(
self
,
body
):
self
.
setBody
(
self
.
getBody
()
+
body
)
def
getHeader
(
self
,
name
):
'''
\
Get a header value
Returns the value associated with a HTTP return header, or
"None" if no such header has been set in the response
yet. '''
return
self
.
headers
.
get
(
name
,
None
)
def
__getitem__
(
self
,
name
):
'Get the value of an output header'
return
self
.
headers
[
name
]
def
getBody
(
self
):
'Returns a string representing the currently set body. '
return
self
.
body
def
__str__
(
self
):
return
str
(
self
.
body
)
def
__repr__
(
self
):
return
'%s(%s)'
%
(
self
.
__class__
.
__name__
,
`self.body`
)
def
flush
(
self
):
pass
def
write
(
self
,
data
):
"""
\
Return data as a stream
HTML data may be returned using a stream-oriented interface.
This allows the browser to display partial results while
computation of a response to proceed.
The published object should first set any output headers or
cookies on the response object.
Note that published objects must not generate any errors
after beginning stream-oriented output.
"""
self
.
body
=
self
.
body
+
data
lib/python/ZPublisher/HTTPRequest.py
0 → 100644
View file @
accff8b5
##############################################################################
#
# Zope Public License (ZPL) Version 0.9.4
# ---------------------------------------
#
# Copyright (c) Digital Creations. All rights reserved.
#
# Redistribution and use in source and binary forms, with or
# without modification, are permitted provided that the following
# conditions are met:
#
# 1. Redistributions in source code must retain the above
# copyright notice, this list of conditions, and the following
# disclaimer.
#
# 2. Redistributions in binary form must reproduce the above
# copyright notice, this list of conditions, and the following
# disclaimer in the documentation and/or other materials
# provided with the distribution.
#
# 3. Any use, including use of the Zope software to operate a
# website, must either comply with the terms described below
# under "Attribution" or alternatively secure a separate
# license from Digital Creations.
#
# 4. All advertising materials, documentation, or technical papers
# mentioning features derived from or use of this software must
# display the following acknowledgement:
#
# "This product includes software developed by Digital
# Creations for use in the Z Object Publishing Environment
# (http://www.zope.org/)."
#
# 5. Names associated with Zope or Digital Creations must not be
# used to endorse or promote products derived from this
# software without prior written permission from Digital
# Creations.
#
# 6. Redistributions of any form whatsoever must retain the
# following acknowledgment:
#
# "This product includes software developed by Digital
# Creations for use in the Z Object Publishing Environment
# (http://www.zope.org/)."
#
# 7. Modifications are encouraged but must be packaged separately
# as patches to official Zope releases. Distributions that do
# not clearly separate the patches from the original work must
# be clearly labeled as unofficial distributions.
#
# Disclaimer
#
# THIS SOFTWARE IS PROVIDED BY DIGITAL CREATIONS ``AS IS'' AND
# ANY EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
# FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT
# SHALL DIGITAL CREATIONS OR ITS CONTRIBUTORS BE LIABLE FOR ANY
# DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
# CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
# PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
# ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
# IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
# THE POSSIBILITY OF SUCH DAMAGE.
#
# Attribution
#
# Individuals or organizations using this software as a web site
# must provide attribution by placing the accompanying "button"
# and a link to the accompanying "credits page" on the website's
# main entry point. In cases where this placement of
# attribution is not feasible, a separate arrangment must be
# concluded with Digital Creations. Those using the software
# for purposes other than web sites must provide a corresponding
# attribution in locations that include a copyright using a
# manner best suited to the application environment.
#
# This software consists of contributions made by Digital
# Creations and many individuals on behalf of Digital Creations.
# Specific attributions are listed in the accompanying credits
# file.
#
##############################################################################
__version__
=
'$Revision: 1.1 $'
[
11
:
-
2
]
import
regex
,
sys
,
os
from
string
import
lower
,
atoi
,
rfind
,
split
,
strip
,
join
,
upper
,
find
from
BaseRequest
import
BaseRequest
from
HTTPResponse
import
HTTPResponse
from
cgi
import
FieldStorage
from
urllib
import
quote
,
unquote
from
Converters
import
type_converters
from
maybe_lock
import
allocate_lock
isCGI_NAME
=
{
'SERVER_SOFTWARE'
:
1
,
'SERVER_NAME'
:
1
,
'GATEWAY_INTERFACE'
:
1
,
'SERVER_PROTOCOL'
:
1
,
'SERVER_PORT'
:
1
,
'REQUEST_METHOD'
:
1
,
'PATH_INFO'
:
1
,
'PATH_TRANSLATED'
:
1
,
'SCRIPT_NAME'
:
1
,
'QUERY_STRING'
:
1
,
'REMOTE_HOST'
:
1
,
'REMOTE_ADDR'
:
1
,
'AUTH_TYPE'
:
1
,
'REMOTE_USER'
:
1
,
'REMOTE_IDENT'
:
1
,
'CONTENT_TYPE'
:
1
,
'CONTENT_LENGTH'
:
1
,
'SERVER_URL'
:
1
,
}.
has_key
hide_key
=
{
'HTTP_AUTHORIZATION'
:
1
,
'HTTP_CGI_AUTHORIZATION'
:
1
,
}.
has_key
_marker
=
[]
class
HTTPRequest
(
BaseRequest
):
"""
\
Model HTTP request data.
This object provides access to request data. This includes, the
input headers, form data, server data, and cookies.
Request objects are created by the object publisher and will be
passed to published objects through the argument name, REQUEST.
The request object is a mapping object that represents a
collection of variable to value mappings. In addition, variables
are divided into four categories:
- Environment variables
These variables include input headers, server data, and other
request-related data. The variable names are as <a
href="http://hoohoo.ncsa.uiuc.edu/cgi/env.html">specified</a>
in the <a
href="http://hoohoo.ncsa.uiuc.edu/cgi/interface.html">CGI
specification</a>
- Form data
These are data extracted from either a URL-encoded query
string or body, if present.
- Cookies
These are the cookie data, if present.
- Other
Data that may be set by an application object.
The form attribute of a request is actually a Field Storage
object. When file uploads are used, this provides a richer and
more complex interface than is provided by accessing form data as
items of the request. See the FieldStorage class documentation
for more details.
The request object may be used as a mapping object, in which case
values will be looked up in the order: environment variables,
other variables, form data, and then cookies.
"""
_hacked_path
=
None
def
__init__
(
self
,
stdin
,
environ
,
response
,
clean
=
0
):
# Avoid the overhead of scrubbing the environment in the
# case of request cloning for traversal purposes. If the
# clean flag is set, we know we can use the passed in
# environ dict directly.
if
not
clean
:
environ
=
sane_environment
(
environ
)
if
environ
.
get
(
'REQUEST_METHOD'
,
'GET'
)
!=
'GET'
:
fp
=
stdin
else
:
fp
=
None
if
environ
.
has_key
(
'HTTP_AUTHORIZATION'
):
self
.
_auth
=
environ
[
'HTTP_AUTHORIZATION'
]
response
.
_auth
=
1
del
environ
[
'HTTP_AUTHORIZATION'
]
form
=
{}
form_has
=
form
.
has_key
meth
=
None
fs
=
FieldStorage
(
fp
=
fp
,
environ
=
environ
,
keep_blank_values
=
1
)
if
not
hasattr
(
fs
,
'list'
)
or
fs
.
list
is
None
:
form
[
'BODY'
]
=
fs
.
value
else
:
fslist
=
fs
.
list
tuple_items
=
{}
type_re
=
regex
.
compile
(
':[a-zA-Z][a-zA-Z0-9_]+'
)
type_search
=
type_re
.
search
lt
=
type
([])
CGI_name
=
isCGI_NAME
for
item
in
fslist
:
key
=
unquote
(
item
.
name
)
if
(
hasattr
(
item
,
'file'
)
and
hasattr
(
item
,
'filename'
)
and
hasattr
(
item
,
'headers'
)):
if
(
item
.
file
and
(
item
.
filename
is
not
None
or
'content-type'
in
map
(
lower
,
item
.
headers
.
keys
()))):
item
=
FileUpload
(
item
)
else
:
item
=
item
.
value
seqf
=
None
l
=
type_search
(
key
)
while
l
>=
0
:
type_name
=
type_re
.
group
(
0
)[
1
:]
key
=
key
[:
l
]
+
key
[
l
+
len
(
type_name
)
+
1
:]
if
type_name
==
'list'
:
seqf
=
list
elif
type_name
==
'tuple'
:
seqf
=
tuple
tuple_items
[
key
]
=
1
elif
type_name
==
'method'
:
if
l
:
meth
=
key
else
:
meth
=
item
elif
type_name
==
'default_method'
:
if
not
meth
:
if
l
:
meth
=
key
else
:
meth
=
item
else
:
item
=
type_converters
[
type_name
](
item
)
l
=
type_search
(
key
)
# Filter out special names from form:
if
CGI_name
(
key
)
or
key
[:
5
]
==
'HTTP_'
:
continue
if
form_has
(
key
):
found
=
form
[
key
]
if
type
(
found
)
is
lt
:
found
.
append
(
item
)
else
:
found
=
[
found
,
item
]
form
[
key
]
=
found
else
:
if
seqf
:
item
=
[
item
]
form
[
key
]
=
item
for
key
in
tuple_items
.
keys
():
item
=
tuple
(
form
[
key
])
form
[
key
]
=
item
other
=
self
.
other
=
{}
other
.
update
(
form
)
if
meth
:
if
environ
.
has_key
(
'PATH_INFO'
):
path
=
environ
[
'PATH_INFO'
]
while
path
[
-
1
:]
==
'/'
:
path
=
path
[:
-
1
]
else
:
path
=
''
other
[
'PATH_INFO'
]
=
path
=
"%s/%s"
%
(
path
,
meth
)
self
.
_hacked_path
=
1
# Cookie values should *not* be appended to existing form
# vars with the same name - they are more like default values
# for names not otherwise specified in the form.
cookies
=
{}
if
environ
.
has_key
(
'HTTP_COOKIE'
):
parse_cookie
(
environ
[
'HTTP_COOKIE'
],
cookies
)
for
k
,
item
in
cookies
.
items
():
if
not
other
.
has_key
(
k
):
other
[
k
]
=
item
self
.
form
=
form
self
.
cookies
=
cookies
other
[
'RESPONSE'
]
=
self
.
response
=
response
self
.
environ
=
environ
self
.
stdin
=
stdin
have_env
=
environ
.
has_key
b
=
script
=
strip
(
environ
[
'SCRIPT_NAME'
])
while
b
and
b
[
-
1
]
==
'/'
:
b
=
b
[:
-
1
]
p
=
rfind
(
b
,
'/'
)
if
p
>=
0
:
b
=
b
[:
p
+
1
]
else
:
b
=
''
while
b
and
b
[
0
]
==
'/'
:
b
=
b
[
1
:]
if
have_env
(
'SERVER_URL'
):
server_url
=
strip
(
environ
[
'SERVER_URL'
])
else
:
if
have_env
(
'HTTPS'
)
and
(
environ
[
'HTTPS'
]
==
"on"
or
environ
[
'HTTPS'
]
==
"ON"
):
server_url
=
'https://'
elif
(
have_env
(
'SERVER_PORT_SECURE'
)
and
environ
[
'SERVER_PORT_SECURE'
]
==
"1"
):
server_url
=
'https://'
else
:
server_url
=
'http://'
if
have_env
(
'HTTP_HOST'
):
server_url
=
server_url
+
strip
(
environ
[
'HTTP_HOST'
])
else
:
server_url
=
server_url
+
strip
(
environ
[
'SERVER_NAME'
])
server_port
=
environ
[
'SERVER_PORT'
]
if
server_port
!=
'80'
:
server_url
=
server_url
+
':'
+
server_port
other
[
'SERVER_URL'
]
=
server_url
if
server_url
[
-
1
:]
==
'/'
:
server_url
=
server_url
[:
-
1
]
self
.
base
=
"%s/%s"
%
(
server_url
,
b
)
while
script
[:
1
]
==
'/'
:
script
=
script
[
1
:]
if
script
:
script
=
"%s/%s"
%
(
server_url
,
script
)
else
:
script
=
server_url
other
[
'URL'
]
=
self
.
script
=
script
def
resolve_url
(
self
,
url
):
# Attempt to resolve a url into an object in the Zope
# namespace. The url must be a fully-qualified url. The
# method will return the requested object if it is found
# or raise the same HTTP error that would be raised in
# the case of a real web request. If the passed in url
# does not appear to describe an object in the system
# namespace (e.g. the host, port or script name dont
# match that of the current request), a ValueError will
# be raised.
if
find
(
url
,
self
.
script
)
!=
0
:
raise
ValueError
,
'Different namespace.'
path
=
url
[
len
(
self
.
script
):]
while
path
and
path
[
0
]
==
'/'
:
path
=
path
[
1
:]
while
path
and
path
[
-
1
]
==
'/'
:
path
=
path
[:
-
1
]
req
=
self
.
clone
()
rsp
=
req
.
response
req
[
'PATH_INFO'
]
=
path
object
=
None
try
:
object
=
req
.
traverse
(
path
)
except
:
rsp
.
exception
(
0
)
if
object
is
not
None
:
return
object
raise
rsp
.
errmsg
,
sys
.
exc_value
def
clone
(
self
):
# Return a clone of the current request object
# that may be used to perform object traversal.
environ
=
self
.
environ
.
copy
()
environ
[
'REQUEST_METHOD'
]
=
'GET'
if
self
.
_auth
:
environ
[
'HTTP_AUTHORIZATION'
]
=
self
.
_auth
clone
=
HTTPRequest
(
None
,
environ
,
HTTPResponse
(),
clean
=
1
)
clone
[
'PARENTS'
]
=
[
self
[
'PARENTS'
][
-
1
]]
clone
.
_auth
=
self
.
_auth
return
clone
def
get_header
(
self
,
name
,
default
=
None
):
"""Return the named HTTP header, or an optional default
argument or None if the header is not found. Note that
both original and CGI-ified header names are recognized,
e.g. 'Content-Type', 'CONTENT_TYPE' and 'HTTP_CONTENT_TYPE'
should all return the Content-Type header, if available.
"""
environ
=
self
.
environ
name
=
upper
(
join
(
split
(
name
,
"-"
),
"_"
))
val
=
environ
.
get
(
name
,
None
)
if
val
is
not
None
:
return
val
if
name
[:
5
]
!=
'HTTP_'
:
name
=
'HTTP_%s'
%
name
return
environ
.
get
(
name
,
default
)
def
__getitem__
(
self
,
key
,
default
=
_marker
,
# Any special internal marker will do
URLmatch
=
regex
.
compile
(
'URL[0-9]$'
).
match
,
BASEmatch
=
regex
.
compile
(
'BASE[0-9]$'
).
match
,
):
"""Get a variable value
Return a value for the required variable name.
The value will be looked up from one of the request data
categories. The search order is environment variables,
other variables, form data, and then cookies.
"""
#"
other
=
self
.
other
if
other
.
has_key
(
key
):
if
key
==
'REQUEST'
:
return
self
return
other
[
key
]
if
key
[:
1
]
==
'U'
and
URLmatch
(
key
)
>=
0
:
n
=
ord
(
key
[
3
])
-
ord
(
'0'
)
URL
=
other
[
'URL'
]
for
i
in
range
(
0
,
n
):
l
=
rfind
(
URL
,
'/'
)
if
l
>=
0
:
URL
=
URL
[:
l
]
else
:
raise
KeyError
,
key
other
[
key
]
=
URL
return
URL
if
isCGI_NAME
(
key
)
or
key
[:
5
]
==
'HTTP_'
:
environ
=
self
.
environ
if
environ
.
has_key
(
key
)
and
(
not
hide_key
(
key
)):
return
environ
[
key
]
return
''
if
key
==
'REQUEST'
:
return
self
if
key
[:
1
]
==
'B'
and
BASEmatch
(
key
)
>=
0
:
n
=
ord
(
key
[
4
])
-
ord
(
'0'
)
if
n
:
v
=
self
.
script
while
v
[
-
1
:]
==
'/'
:
v
=
v
[:
-
1
]
v
=
join
([
v
]
+
self
.
steps
[:
n
-
1
],
'/'
)
else
:
v
=
self
.
base
while
v
[
-
1
:]
==
'/'
:
v
=
v
[:
-
1
]
other
[
key
]
=
v
return
v
v
=
self
.
common
.
get
(
key
,
default
)
if
v
is
not
_marker
:
return
v
raise
KeyError
,
key
__getattr__
=
get
=
__getitem__
def
keys
(
self
):
keys
=
{}
keys
.
update
(
self
.
common
)
for
key
in
self
.
environ
.
keys
():
if
(
isCGI_NAME
(
key
)
or
key
[:
5
]
==
'HTTP_'
)
and
\
(
not
hide_key
(
key
)):
keys
[
key
]
=
1
keys
.
update
(
self
.
other
)
lasturl
=
""
for
n
in
"0123456789"
:
key
=
"URL%s"
%
n
try
:
if
lasturl
!=
self
[
key
]:
keys
[
key
]
=
1
lasturl
=
self
[
key
]
else
:
break
except
KeyError
:
pass
for
n
in
"0123456789"
:
key
=
"BASE%s"
%
n
try
:
if
lasturl
!=
self
[
key
]:
keys
[
key
]
=
1
lasturl
=
self
[
key
]
else
:
break
except
KeyError
:
pass
return
keys
.
keys
()
def
_authUserPW
(
self
):
global
base64
auth
=
self
.
_auth
if
auth
:
if
lower
(
auth
[:
6
])
==
'basic '
:
if
base64
is
None
:
import
base64
[
name
,
password
]
=
split
(
base64
.
decodestring
(
split
(
auth
)[
-
1
]),
':'
)
return
name
,
password
base64
=
None
def
sane_environment
(
env
):
# return an environment mapping which has been cleaned of
# funny business such as REDIRECT_ prefixes added by Apache
# or HTTP_CGI_AUTHORIZATION hacks.
dict
=
{}
for
key
,
val
in
env
.
items
():
while
key
[:
9
]
==
'REDIRECT_'
:
key
=
key
[
9
:]
dict
[
key
]
=
val
if
dict
.
has_key
(
'HTTP_CGI_AUTHORIZATION'
):
dict
[
'HTTP_AUTHORIZATION'
]
=
dict
[
'HTTP_CGI_AUTHORIZATION'
]
try
:
del
dict
[
'HTTP_CGI_AUTHORIZATION'
]
except
:
pass
return
dict
def
str_field
(
v
):
if
type
(
v
)
is
ListType
:
return
map
(
str_field
,
v
)
if
hasattr
(
v
,
'__class__'
)
and
v
.
__class__
is
FieldStorage
:
v
=
v
.
value
elif
type
(
v
)
is
not
StringType
:
if
hasattr
(
v
,
'file'
)
and
v
.
file
:
v
=
v
.
file
elif
hasattr
(
v
,
'value'
):
v
=
v
.
value
return
v
class
FileUpload
:
'''
\
File upload objects
File upload objects are used to represent file-uploaded data.
File upload objects can be used just like files.
In addition, they have a 'headers' attribute that is a dictionary
containing the file-upload headers, and a 'filename' attribute
containing the name of the uploaded file.
'''
def
__init__
(
self
,
aFieldStorage
):
file
=
aFieldStorage
.
file
if
hasattr
(
file
,
'__methods__'
):
methods
=
file
.
__methods__
else
:
methods
=
[
'close'
,
'fileno'
,
'flush'
,
'isatty'
,
'read'
,
'readline'
,
'readlines'
,
'seek'
,
'tell'
,
'truncate'
,
'write'
,
'writelines'
]
d
=
self
.
__dict__
for
m
in
methods
:
if
hasattr
(
file
,
m
):
d
[
m
]
=
getattr
(
file
,
m
)
self
.
headers
=
aFieldStorage
.
headers
self
.
filename
=
aFieldStorage
.
filename
parse_cookie_lock
=
allocate_lock
()
def
parse_cookie
(
text
,
result
=
None
,
qparmre
=
regex
.
compile
(
'
\
([
\
0- ]*'
'
\
([^
\
0- ;,=
\
"
]+
\
)=
"
\
([^
"
]*
\
)
\
"'
'
\
([
\
0- ]*[;,]
\
)?[
\
0- ]*
\
)
'
),
parmre=regex.compile(
'
\
([
\
0
-
]
*
'
'
\
([
^
\
0
-
;,
=
\
"]+
\
)=
\
([^
\
0
- ;,
\
"
]*
\
)
'
'
\
([
\
0- ]*[;,]
\
)?[
\
0- ]*
\
)
'
),
acquire=parse_cookie_lock.acquire,
release=parse_cookie_lock.release,
):
if result is None: result={}
already_have=result.has_key
acquire()
try:
if qparmre.match(text) >= 0:
# Match quoted correct cookies
name=qparmre.group(2)
value=qparmre.group(3)
l=len(qparmre.group(1))
elif parmre.match(text) >= 0:
# Match evil MSIE cookies ;)
name=parmre.group(2)
value=parmre.group(3)
l=len(parmre.group(1))
else:
# this may be an invalid cookie.
# We'll simply bail without raising an error
# if the cookie is invalid.
return result
finally: release()
if not already_have(name): result[name]=value
return apply(parse_cookie,(text[l:],result))
lib/python/ZPublisher/HTTPResponse.py
0 → 100644
View file @
accff8b5
##############################################################################
#
# Zope Public License (ZPL) Version 0.9.4
# ---------------------------------------
#
# Copyright (c) Digital Creations. All rights reserved.
#
# Redistribution and use in source and binary forms, with or
# without modification, are permitted provided that the following
# conditions are met:
#
# 1. Redistributions in source code must retain the above
# copyright notice, this list of conditions, and the following
# disclaimer.
#
# 2. Redistributions in binary form must reproduce the above
# copyright notice, this list of conditions, and the following
# disclaimer in the documentation and/or other materials
# provided with the distribution.
#
# 3. Any use, including use of the Zope software to operate a
# website, must either comply with the terms described below
# under "Attribution" or alternatively secure a separate
# license from Digital Creations.
#
# 4. All advertising materials, documentation, or technical papers
# mentioning features derived from or use of this software must
# display the following acknowledgement:
#
# "This product includes software developed by Digital
# Creations for use in the Z Object Publishing Environment
# (http://www.zope.org/)."
#
# 5. Names associated with Zope or Digital Creations must not be
# used to endorse or promote products derived from this
# software without prior written permission from Digital
# Creations.
#
# 6. Redistributions of any form whatsoever must retain the
# following acknowledgment:
#
# "This product includes software developed by Digital
# Creations for use in the Z Object Publishing Environment
# (http://www.zope.org/)."
#
# 7. Modifications are encouraged but must be packaged separately
# as patches to official Zope releases. Distributions that do
# not clearly separate the patches from the original work must
# be clearly labeled as unofficial distributions.
#
# Disclaimer
#
# THIS SOFTWARE IS PROVIDED BY DIGITAL CREATIONS ``AS IS'' AND
# ANY EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
# FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT
# SHALL DIGITAL CREATIONS OR ITS CONTRIBUTORS BE LIABLE FOR ANY
# DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
# CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
# PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
# ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
# IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
# THE POSSIBILITY OF SUCH DAMAGE.
#
# Attribution
#
# Individuals or organizations using this software as a web site
# must provide attribution by placing the accompanying "button"
# and a link to the accompanying "credits page" on the website's
# main entry point. In cases where this placement of
# attribution is not feasible, a separate arrangment must be
# concluded with Digital Creations. Those using the software
# for purposes other than web sites must provide a corresponding
# attribution in locations that include a copyright using a
# manner best suited to the application environment.
#
# This software consists of contributions made by Digital
# Creations and many individuals on behalf of Digital Creations.
# Specific attributions are listed in the accompanying credits
# file.
#
##############################################################################
'''CGI Response Output formatter
$Id: HTTPResponse.py,v 1.1 1999/02/18 17:17:56 jim Exp $'''
__version__
=
'$Revision: 1.1 $'
[
11
:
-
2
]
import
string
,
types
,
sys
,
regex
from
string
import
find
,
rfind
,
lower
,
upper
,
strip
,
split
,
join
,
translate
from
types
import
StringType
,
InstanceType
from
BaseResponse
import
BaseResponse
nl2sp
=
string
.
maketrans
(
'
\
n
'
,
' '
)
status_reasons
=
{
100
:
'Continue'
,
101
:
'Switching Protocols'
,
102
:
'Processing'
,
200
:
'OK'
,
201
:
'Created'
,
202
:
'Accepted'
,
203
:
'Non-Authoritative Information'
,
204
:
'No Content'
,
205
:
'Reset Content'
,
206
:
'Partial Content'
,
207
:
'Multi-Status'
,
300
:
'Multiple Choices'
,
301
:
'Moved Permanently'
,
302
:
'Moved Temporarily'
,
303
:
'See Other'
,
304
:
'Not Modified'
,
305
:
'Use Proxy'
,
307
:
'Temporary Redirect'
,
400
:
'Bad Request'
,
401
:
'Unauthorized'
,
402
:
'Payment Required'
,
403
:
'Forbidden'
,
404
:
'Not Found'
,
405
:
'Method Not Allowed'
,
406
:
'Not Acceptable'
,
407
:
'Proxy Authentication Required'
,
408
:
'Request Time-out'
,
409
:
'Conflict'
,
410
:
'Gone'
,
411
:
'Length Required'
,
412
:
'Precondition Failed'
,
413
:
'Request Entity Too Large'
,
414
:
'Request-URI Too Large'
,
415
:
'Unsupported Media Type'
,
416
:
'Requested range not satisfiable'
,
417
:
'Expectation Failed'
,
422
:
'Unprocessable Entity'
,
423
:
'Locked'
,
424
:
'Failed Dependency'
,
500
:
'Internal Server Error'
,
501
:
'Not Implemented'
,
502
:
'Bad Gateway'
,
503
:
'Service Unavailable'
,
504
:
'Gateway Time-out'
,
505
:
'HTTP Version not supported'
,
507
:
'Insufficient Storage'
,
}
status_codes
=
{}
# Add mappings for builtin exceptions and
# provide text -> error code lookups.
for
key
,
val
in
status_reasons
.
items
():
status_codes
[
lower
(
join
(
split
(
val
,
' '
),
''
))]
=
key
status_codes
[
lower
(
val
)]
=
key
status_codes
[
key
]
=
key
en
=
filter
(
lambda
n
:
n
[
-
5
:]
==
'Error'
,
dir
(
__builtins__
))
for
name
in
map
(
lower
,
en
):
status_codes
[
name
]
=
500
status_codes
[
'nameerror'
]
=
503
status_codes
[
'keyerror'
]
=
503
status_codes
[
'redirect'
]
=
300
end_of_header_search
=
regex
.
compile
(
'</head>'
,
regex
.
casefold
).
search
accumulate_header
=
{
'set-cookie'
:
1
}.
has_key
class
HTTPResponse
(
BaseResponse
):
"""
\
An object representation of an HTTP response.
The Response type encapsulates all possible responses to HTTP
requests. Responses are normally created by the object publisher.
A published object may recieve the response abject as an argument
named 'RESPONSE'. A published object may also create it's own
response object. Normally, published objects use response objects
to:
- Provide specific control over output headers,
- Set cookies, or
- Provide stream-oriented output.
If stream oriented output is used, then the response object
passed into the object must be used.
"""
#'
accumulated_headers
=
''
body
=
''
realm
=
'Zope'
def
__init__
(
self
,
body
=
''
,
status
=
200
,
headers
=
None
,
stdout
=
sys
.
stdout
,
stderr
=
sys
.
stderr
,):
'''
\
Creates a new response. In effect, the constructor calls
"self.setBody(body); self.setStatus(status); for name in
headers.keys(): self.setHeader(name, headers[name])"
'''
if
headers
is
None
:
headers
=
{}
self
.
headers
=
headers
if
status
==
200
:
self
.
status
=
200
self
.
errmsg
=
'OK'
headers
[
'status'
]
=
"200 OK"
else
:
self
.
setStatus
(
status
)
self
.
base
=
''
if
body
:
self
.
setBody
(
body
)
self
.
cookies
=
{}
self
.
stdout
=
stdout
self
.
stderr
=
stderr
def
setStatus
(
self
,
status
,
reason
=
None
):
'''
\
Sets the HTTP status code of the response; the argument may
either be an integer or a string from { OK, Created, Accepted,
NoContent, MovedPermanently, MovedTemporarily,
NotModified, BadRequest, Unauthorized, Forbidden,
NotFound, InternalError, NotImplemented, BadGateway,
ServiceUnavailable } that will be converted to the correct
integer value. '''
if
type
(
status
)
is
types
.
StringType
:
status
=
lower
(
status
)
if
status_codes
.
has_key
(
status
):
status
=
status_codes
[
status
]
else
:
status
=
500
self
.
status
=
status
if
reason
is
None
:
if
status_reasons
.
has_key
(
status
):
reason
=
status_reasons
[
status
]
else
:
reason
=
'Unknown'
self
.
setHeader
(
'Status'
,
"%d %s"
%
(
status
,
str
(
reason
)))
self
.
errmsg
=
reason
def
setHeader
(
self
,
name
,
value
):
'''
\
Sets an HTTP return header "name" with value "value", clearing
the previous value set for the header, if one exists. '''
n
=
lower
(
name
)
if
accumulate_header
(
n
):
self
.
accumulated_headers
=
(
"%s%s: %s
\
n
"
%
(
self
.
accumulated_headers
,
name
,
value
))
else
:
self
.
headers
[
n
]
=
value
__setitem__
=
setHeader
def
setBody
(
self
,
body
,
title
=
''
,
bogus_str_search
=
regex
.
compile
(
" [a-fA-F0-9]+>$"
).
search
,
):
'''
\
Set the body of the response
Sets the return body equal to the (string) argument "body". Also
updates the "content-length" return header.
You can also specify a title, in which case the title and body
will be wrapped up in html, head, title, and body tags.
If the body is a 2-element tuple, then it will be treated
as (title,body)
'''
if
type
(
body
)
is
types
.
TupleType
:
title
,
body
=
body
if
type
(
body
)
is
not
types
.
StringType
:
if
hasattr
(
body
,
'asHTML'
):
body
=
body
.
asHTML
()
body
=
str
(
body
)
l
=
len
(
body
)
if
(
find
(
body
,
'>'
)
==
l
-
1
and
body
[:
1
]
==
'<'
and
l
<
200
and
bogus_str_search
(
body
)
>
0
):
if
self
.
debug_mode
:
_tbopen
,
_tbclose
=
'<PRE>'
,
'</PRE>'
else
:
_tbopen
,
_tbclose
=
'<!--'
,
'-->'
raise
'NotFound'
,
(
"Sorry, the requested document does not exist.<p>"
"
\
n
%s
\
n
%s
\
n
%s"
%
(
_tbopen
,
body
[
1
:
-
1
],
_tbclose
))
if
(
title
):
self
.
body
=
(
'<html>
\
n
<head>
\
n
<title>%s</title>
\
n
</head>
\
n
'
'<body>
\
n
%s
\
n
</body>
\
n
</html>'
%
(
str
(
title
),
str
(
body
)))
else
:
self
.
body
=
str
(
body
)
self
.
insertBase
()
return
self
def
setBase
(
self
,
base
):
'Set the base URL for the returned document.'
if
base
[
-
1
:]
!=
'/'
:
base
=
base
+
'/'
self
.
base
=
base
self
.
insertBase
()
def
insertBase
(
self
,
base_re_search
=
regex
.
compile
(
'
\
(<
b
ase[
\
0
- ]+[^>]+>
\
)
'
,
regex.casefold).search
):
if (self.headers.has_key('
content
-
type
') and
self.headers['
content
-
type
']!='
text
/
html
'): return
if self.base:
body=self.body
if body:
e=end_of_header_search(body)
if e >= 0:
b=base_re_search(body)
if b < 0:
self.body=('
%
s
\
t
<
base
href
=
"%s"
>
\
n
%
s
' %
(body[:e],self.base,body[e:]))
def appendCookie(self, name, value):
'''
\
Returns an HTTP header that sets a cookie on cookie-enabled
browsers with a key "name" and value "value". If a value for the
cookie has previously been set in the response object, the new
value is appended to the old one separated by a colon. '''
cookies=self.cookies
if cookies.has_key(name): cookie=cookies[name]
else: cookie=cookies[name]={}
if cookie.has_key('
value
'):
cookie['
value
']='
%
s
:
%
s
' % (cookie['
value
'], value)
else: cookie['
value
']=value
def expireCookie(self, name, **kw):
'''
\
Cause an HTTP cookie to be removed from the browser
The response will include an HTTP header that will remove the cookie
corresponding to "name" on the client, if one exists. This is
accomplished by sending a new cookie with an expiration date
that has already passed. Note that some clients require a path
to be specified - this path must exactly match the path given
when creating the cookie. The path can be specified as a keyword
argument.
'''
dict={'
max_age
':0, '
expires
':'
Wed
,
31
-
Dec
-
97
23
:
59
:
59
GMT
'}
for k, v in kw.items():
dict[k]=v
apply(Response.setCookie, (self, name, '
deleted
'), dict)
def setCookie(self,name,value,**kw):
'''
\
Set an HTTP cookie on the browser
The response will include an HTTP header that sets a cookie on
cookie-enabled browsers with a key "name" and value
"value". This overwrites any previously set value for the
cookie in the Response object.
'''
cookies=self.cookies
if cookies.has_key(name):
cookie=cookies[name]
else: cookie=cookies[name]={}
for k, v in kw.items():
cookie[k]=v
cookie['
value
']=value
def appendHeader(self, name, value, delimiter=","):
'''
\
Append a value to a cookie
Sets an HTTP return header "name" with value "value",
appending it following a comma if there was a previous value
set for the header. '''
headers=self.headers
if headers.has_key(name):
h=self.header[name]
h="%s%s
\
n
\
t
%s" % (h,delimiter,value)
else: h=value
self.setHeader(name,h)
def isHTML(self,str):
return lower(strip(str)[:6]) == '
<
html
>
' or find(str,'
</
') > 0
def quoteHTML(self,text,
subs={'
&
':'
&
amp
;
', "<":'
&
lt
;
', ">":'
&
gt
;
', '
\
"':'"'}
):
for ent in '&<>
\
"
':
if find(text, ent) >= 0:
text=join(split(text,ent),subs[ent])
return text
def format_exception(self,etype,value,tb,limit=None):
import traceback
result=['Traceback (innermost last):']
if limit is None:
if hasattr(sys, 'tracebacklimit'):
limit = sys.tracebacklimit
n = 0
while tb is not None and (limit is None or n < limit):
f = tb.tb_frame
lineno = tb.tb_lineno
co = f.f_code
filename = co.co_filename
name = co.co_name
locals=f.f_locals
result.append(' File %s, line %d, in %s'
% (filename,lineno,name))
try: result.append(' (Object: %s)' %
locals[co.co_varnames[0]].__name__)
except: pass
try: result.append(' (Info: %s)' %
str(locals['__traceback_info__']))
except: pass
tb = tb.tb_next
n = n+1
result.append(join(traceback.format_exception_only(etype, value),
' '))
return result
def _traceback(self,t,v,tb):
tb=self.format_exception(t,v,tb,200)
tb=join(tb,'
\
n
')
tb=self.quoteHTML(tb)
if self.debug_mode: _tbopen, _tbclose = '<PRE>', '</PRE>'
else: _tbopen, _tbclose = '<!--', '-->'
return "
\
n
%
s
\
n
%
s
\
n
%
s
" % (_tbopen, tb, _tbclose)
def redirect(self, location):
"""Cause a redirection without raising an error"""
self.status=302
headers=self.headers
headers['status']='302 Moved Temporarily'
headers['location']=location
return location
def _html(self,title,body):
return ("
<
html
>
\
n
"
"
<
head
>
\
n
<
title
>%
s
</
title
>
\
n
</
head
>
\
n
"
"
<
body
>
\
n
%
s
\
n
</
body
>
\
n
"
"
</
html
>
\
n
" % (title,body))
def notFoundError(self,entry='who knows!'):
raise 'NotFound',self._html(
"
Resource
not
found
",
"
Sorry
,
the
requested
document
does
not
exist
.
<
p
>
"
"
\
n
<
!
--
\
n
%
s
\
n
-->
" % entry)
forbiddenError=notFoundError # If a resource is forbidden,
# why reveal that it exists?
def debugError(self,entry):
raise 'NotFound',self._html(
"
Debugging
Notice
",
"
Bobo
has
encountered
a
problem
publishing
your
object
.
<
p
>
"
"
\
n
%
s
" % entry)
def badRequestError(self,name):
if regex.match('^[A-Z_0-9]+$',name) >= 0:
raise 'InternalError', self._html(
"
Internal
Error
",
"
Sorry
,
an
internal
error
occurred
in
this
resource
.
")
raise 'BadRequest',self.html(
"
Invalid
request
",
"
The
parameter
,
<
em
>%
s
</
em
>
,
was
omitted
from
the
request
.
"
"
<
!
--%
s
-->
"
% (name,self.request))
def _unauthorized(self):
realm=self.realm
if realm: self['WWW-authenticate']='basic realm="
%
s
"' % realm
def unauthorized(self):
self._unauthorized()
m="
<
strong
>
You
are
not
authorized
to
access
this
resource
.
</
strong
>
"
if self.debug_mode:
if self._auth:
m=m+'
\
n
Username and password are not correct.'
else:
m=m+'
\
n
No Authorization header found.'
raise 'Unauthorized', m
def forbiddenError(self,object=None):
raise 'NotFound',self._html(
"
Resource
not
found
",
"
Sorry
,
the
requested
document
does
not
exist
.
<
p
>
"
"
<
!
--%
s
-->
" % object)
def exception(self, fatal=0, info=None,
absuri_match=regex.compile(
"
^
"
"
\
(
/
\
|
\
([
a
-
zA
-
Z0
-
9
+
.
-
]
+
:
\
)
\
)
"
"
[
^
\
000
-
\
"
\
\
#<>]*"
"
\
\
(#[^
\
000
-
\
"
\
\
#<>]*
\
\
)?"
"$"
).
match
,
tag_search
=
regex
.
compile
(
'[a-zA-Z]>'
).
search
,
):
if
type
(
info
)
is
type
(())
and
len
(
info
)
==
3
:
t
,
v
,
tb
=
info
else
:
t
,
v
,
tb
=
sys
.
exc_info
()
if
str
(
t
)
==
'Unauthorized'
:
self
.
_unauthorized
()
stb
=
tb
# Abort running transaction, if any:
try
:
get_transaction
().
abort
()
except
:
pass
try
:
# Try to capture exception info for bci calls
et
=
translate
(
str
(
t
),
nl2sp
)
ev
=
translate
(
str
(
v
),
nl2sp
)
# Get the tb tail, which is the interesting part:
while
tb
.
tb_next
is
not
None
:
tb
=
tb
.
tb_next
el
=
str
(
tb
.
tb_lineno
)
ef
=
str
(
tb
.
tb_frame
.
f_code
.
co_filename
)
if
find
(
ev
,
'<html>'
)
>=
0
:
ev
=
'bobo exception'
self
.
setHeader
(
'bobo-exception-type'
,
et
)
self
.
setHeader
(
'bobo-exception-value'
,
ev
[:
255
])
self
.
setHeader
(
'bobo-exception-file'
,
ef
)
self
.
setHeader
(
'bobo-exception-line'
,
el
)
except
:
# Dont try so hard that we cause other problems ;)
pass
tb
=
stb
stb
=
None
self
.
setStatus
(
t
)
if
self
.
status
>=
300
and
self
.
status
<
400
:
if
type
(
v
)
==
types
.
StringType
and
absuri_match
(
v
)
>=
0
:
if
self
.
status
==
300
:
self
.
setStatus
(
302
)
self
.
setHeader
(
'location'
,
v
)
tb
=
None
return
self
else
:
try
:
l
,
b
=
v
if
type
(
l
)
==
types
.
StringType
and
absuri_match
(
l
)
>=
0
:
if
self
.
status
==
300
:
self
.
setStatus
(
302
)
self
.
setHeader
(
'location'
,
l
)
self
.
setBody
(
b
)
tb
=
None
return
self
except
:
pass
b
=
v
if
isinstance
(
b
,
Exception
):
b
=
str
(
b
)
if
fatal
:
if
t
is
SystemExit
and
v
.
code
==
0
:
tb
=
self
.
setBody
(
(
str
(
t
),
'This application has exited normally.<p>'
+
self
.
_traceback
(
t
,
v
,
tb
)))
else
:
tb
=
self
.
setBody
(
(
str
(
t
),
'Sorry, a SERIOUS APPLICATION ERROR occurred.<p>'
+
self
.
_traceback
(
t
,
v
,
tb
)))
elif
type
(
b
)
is
not
types
.
StringType
or
tag_search
(
b
)
<
0
:
tb
=
self
.
setBody
(
(
str
(
t
),
'Sorry, an error occurred.<p>'
+
self
.
_traceback
(
t
,
v
,
tb
)))
elif
self
.
isHTML
(
b
):
tb
=
self
.
setBody
(
b
+
self
.
_traceback
(
t
,
'(see above)'
,
tb
))
else
:
tb
=
self
.
setBody
((
str
(
t
),
b
+
self
.
_traceback
(
t
,
'(see above)'
,
tb
)))
return
tb
_wrote
=
None
def
_cookie_list
(
self
):
cookie_list
=
[]
for
name
,
attrs
in
self
.
cookies
.
items
():
# Note that as of May 98, IE4 ignores cookies with
# quoted cookie attr values, so only the value part
# of name=value pairs may be quoted.
cookie
=
'Set-Cookie: %s="%s"'
%
(
name
,
attrs
[
'value'
])
for
name
,
v
in
attrs
.
items
():
name
=
lower
(
name
)
if
name
==
'expires'
:
cookie
=
'%s; Expires=%s'
%
(
cookie
,
v
)
elif
name
==
'domain'
:
cookie
=
'%s; Domain=%s'
%
(
cookie
,
v
)
elif
name
==
'path'
:
cookie
=
'%s; Path=%s'
%
(
cookie
,
v
)
elif
name
==
'max_age'
:
cookie
=
'%s; Max-Age=%s'
%
(
cookie
,
v
)
elif
name
==
'comment'
:
cookie
=
'%s; Comment=%s'
%
(
cookie
,
v
)
elif
name
==
'secure'
:
cookie
=
'%s; Secure'
%
cookie
cookie_list
.
append
(
cookie
)
# Should really check size of cookies here!
return
cookie_list
def
__str__
(
self
,
html_search
=
regex
.
compile
(
'<html>'
,
regex
.
casefold
).
search
,
):
if
self
.
_wrote
:
return
''
# Streaming output was used.
headers
=
self
.
headers
body
=
self
.
body
if
body
:
isHTML
=
self
.
isHTML
(
body
)
if
not
headers
.
has_key
(
'content-type'
):
if
isHTML
:
c
=
'text/html'
else
:
c
=
'text/plain'
self
.
setHeader
(
'content-type'
,
c
)
else
:
isHTML
=
headers
[
'content-type'
]
==
'text/html'
if
isHTML
and
end_of_header_search
(
self
.
body
)
<
0
:
lhtml
=
html_search
(
body
)
if
lhtml
>=
0
:
lhtml
=
lhtml
+
6
body
=
'%s<head></head>
\
n
%s'
%
(
body
[:
lhtml
],
body
[
lhtml
:])
else
:
body
=
'<html><head></head>
\
n
'
+
body
self
.
setBody
(
body
)
body
=
self
.
body
if
not
headers
.
has_key
(
'content-type'
)
and
self
.
status
==
200
:
self
.
setStatus
(
'nocontent'
)
if
not
headers
.
has_key
(
'content-length'
):
self
.
setHeader
(
'content-length'
,
len
(
body
))
headersl
=
[]
append
=
headersl
.
append
# Make sure status comes out first!
try
:
v
=
headers
[
'status'
]
del
headers
[
'status'
]
except
:
v
=
"200 OK"
append
(
"Status: "
+
v
)
for
k
,
v
in
headers
.
items
():
k
=
upper
(
k
[:
1
])
+
k
[
1
:]
start
=
0
l
=
find
(
k
,
'-'
,
start
)
while
l
>=
start
:
k
=
"%s-%s%s"
%
(
k
[:
l
],
upper
(
k
[
l
+
1
:
l
+
2
]),
k
[
l
+
2
:])
start
=
l
+
1
l
=
find
(
k
,
'-'
,
start
)
append
(
"%s: %s"
%
(
k
,
v
))
if
self
.
cookies
:
headersl
=
headersl
+
self
.
_cookie_list
()
headersl
[
len
(
headersl
):]
=
[
self
.
accumulated_headers
,
body
]
return
join
(
headersl
,
'
\
n
'
)
def
write
(
self
,
data
):
"""
\
Return data as a stream
HTML data may be returned using a stream-oriented interface.
This allows the browser to display partial results while
computation of a response to proceed.
The published object should first set any output headers or
cookies on the response object.
Note that published objects must not generate any errors
after beginning stream-oriented output.
"""
self
.
body
=
self
.
body
+
data
headers
=
self
.
headers
if
headers
.
has_key
(
'content-length'
):
del
headers
[
'content-length'
]
if
not
self
.
headers
.
has_key
(
'content-type'
):
self
.
setHeader
(
'content-type'
,
'text/html'
)
self
.
insertBase
()
body
=
self
.
body
self
.
body
=
''
self
.
write
=
write
=
self
.
stdout
.
write
try
:
self
.
flush
=
self
.
stdout
.
flush
except
:
pass
write
(
str
(
self
))
self
.
_wrote
=
1
write
(
body
)
lib/python/ZPublisher/Publish.py
View file @
accff8b5
...
...
@@ -84,237 +84,19 @@
##############################################################################
__doc__
=
"""Python Object Publisher -- Publish Python objects on web servers
$Id: Publish.py,v 1.12
2 1999/01/26 14:42:31 brian
Exp $"""
__version__
=
'$Revision: 1.12
2
$'
[
11
:
-
2
]
$Id: Publish.py,v 1.12
3 1999/02/18 17:17:56 jim
Exp $"""
__version__
=
'$Revision: 1.12
3
$'
[
11
:
-
2
]
import
sys
,
os
,
string
,
cgi
,
regex
from
string
import
lower
,
atoi
,
rfind
,
s
plit
,
strip
,
join
,
upper
,
find
import
sys
,
os
from
string
import
lower
,
atoi
,
rfind
,
s
trip
from
Response
import
Response
from
urllib
import
quote
,
unquote
from
cgi
import
FieldStorage
from
Request
import
Request
,
isCGI_NAME
from
Converters
import
type_converters
# Waaaa, I wish I didn't have to work this hard.
try
:
from
thread
import
allocate_lock
except
:
class
allocate_lock
:
def
acquire
(
*
args
):
pass
def
release
(
*
args
):
pass
ListType
=
type
([])
StringType
=
type
(
''
)
UNSPECIFIED_ROLES
=
''
try
:
from
ExtensionClass
import
Base
class
RequestContainer
(
Base
):
def
__init__
(
self
,
**
kw
):
for
k
,
v
in
kw
.
items
():
self
.
__dict__
[
k
]
=
v
def
manage_property_types
(
self
):
return
type_converters
.
keys
()
except
:
class
RequestContainer
:
def
__init__
(
self
,
**
kw
):
for
k
,
v
in
kw
.
items
():
self
.
__dict__
[
k
]
=
v
def
sane_environment
(
env
):
# return an environment mapping which has been cleaned of
# funny business such as REDIRECT_ prefixes added by Apache
# or HTTP_CGI_AUTHORIZATION hacks.
dict
=
{}
for
key
,
val
in
env
.
items
():
while
key
[:
9
]
==
'REDIRECT_'
:
key
=
key
[
9
:]
dict
[
key
]
=
val
if
dict
.
has_key
(
'HTTP_CGI_AUTHORIZATION'
):
dict
[
'HTTP_AUTHORIZATION'
]
=
dict
[
'HTTP_CGI_AUTHORIZATION'
]
try
:
del
dict
[
'HTTP_CGI_AUTHORIZATION'
]
except
:
pass
return
dict
class
ModulePublisher
:
HTTP_AUTHORIZATION
=
None
_hacked_path
=
None
def
__init__
(
self
,
stdin
=
sys
.
stdin
,
stdout
=
sys
.
stdout
,
stderr
=
sys
.
stderr
,
environ
=
os
.
environ
):
environ
=
sane_environment
(
environ
)
fp
=
None
try
:
if
environ
[
'REQUEST_METHOD'
]
!=
'GET'
:
fp
=
stdin
except
:
pass
if
environ
.
has_key
(
'HTTP_AUTHORIZATION'
):
self
.
HTTP_AUTHORIZATION
=
environ
[
'HTTP_AUTHORIZATION'
]
try
:
del
environ
[
'HTTP_AUTHORIZATION'
]
except
:
pass
form
=
{}
form_has
=
form
.
has_key
other
=
{}
meth
=
None
fs
=
FieldStorage
(
fp
=
fp
,
environ
=
environ
,
keep_blank_values
=
1
)
if
not
hasattr
(
fs
,
'list'
)
or
fs
.
list
is
None
:
form
[
'BODY'
]
=
other
[
'BODY'
]
=
fs
.
value
else
:
fslist
=
fs
.
list
tuple_items
=
{}
type_re
=
regex
.
compile
(
':[a-zA-Z][a-zA-Z0-9_]+'
)
type_search
=
type_re
.
search
lt
=
type
([])
CGI_name
=
isCGI_NAME
for
item
in
fslist
:
key
=
unquote
(
item
.
name
)
if
(
hasattr
(
item
,
'file'
)
and
hasattr
(
item
,
'filename'
)
and
hasattr
(
item
,
'headers'
)):
if
(
item
.
file
and
(
item
.
filename
is
not
None
or
'content-type'
in
map
(
lower
,
item
.
headers
.
keys
()))):
item
=
FileUpload
(
item
)
else
:
item
=
item
.
value
seqf
=
None
l
=
type_search
(
key
)
while
l
>=
0
:
type_name
=
type_re
.
group
(
0
)[
1
:]
key
=
key
[:
l
]
+
key
[
l
+
len
(
type_name
)
+
1
:]
if
type_name
==
'list'
:
seqf
=
list
elif
type_name
==
'tuple'
:
seqf
=
tuple
tuple_items
[
key
]
=
1
elif
type_name
==
'method'
:
if
l
:
meth
=
key
else
:
meth
=
item
elif
type_name
==
'default_method'
:
if
not
meth
:
if
l
:
meth
=
key
else
:
meth
=
item
else
:
item
=
type_converters
[
type_name
](
item
)
l
=
type_search
(
key
)
# Filter out special names from form:
if
CGI_name
(
key
)
or
key
[:
5
]
==
'HTTP_'
:
continue
if
form_has
(
key
):
found
=
form
[
key
]
if
type
(
found
)
is
lt
:
found
.
append
(
item
)
else
:
found
=
[
found
,
item
]
form
[
key
]
=
found
other
[
key
]
=
found
else
:
if
seqf
:
item
=
[
item
]
form
[
key
]
=
item
other
[
key
]
=
item
for
key
in
tuple_items
.
keys
():
item
=
tuple
(
form
[
key
])
form
[
key
]
=
item
other
[
key
]
=
item
if
meth
:
if
environ
.
has_key
(
'PATH_INFO'
):
path
=
environ
[
'PATH_INFO'
]
while
path
[
-
1
:]
==
'/'
:
path
=
path
[:
-
1
]
else
:
path
=
''
other
[
'PATH_INFO'
]
=
path
=
"%s/%s"
%
(
path
,
meth
)
self
.
_hacked_path
=
1
# Cookie values should *not* be appended to existing form
# vars with the same name - they are more like default values
# for names not otherwise specified in the form.
cookies
=
{}
if
environ
.
has_key
(
'HTTP_COOKIE'
):
parse_cookie
(
environ
[
'HTTP_COOKIE'
],
cookies
)
for
k
,
item
in
cookies
.
items
():
if
not
other
.
has_key
(
k
):
other
[
k
]
=
item
request
=
self
.
request
=
Request
(
environ
,
other
,
stdin
)
request
.
form
=
form
if
cookies
is
not
None
:
request
.
cookies
=
cookies
self
.
response
=
response
=
Response
(
stdout
=
stdout
,
stderr
=
stderr
)
request
[
'RESPONSE'
]
=
response
def
html
(
self
,
title
,
body
):
return
(
"<html>
\
n
"
"<head>
\
n
<title>%s</title>
\
n
</head>
\
n
"
"<body>
\
n
%s
\
n
</body>
\
n
"
"</html>
\
n
"
%
(
title
,
body
))
def
notFoundError
(
self
,
entry
=
'who knows!'
):
raise
'NotFound'
,
self
.
html
(
"Resource not found"
,
"Sorry, the requested document does not exist.<p>"
"
\
n
<!--
\
n
%s
\
n
-->"
%
entry
)
forbiddenError
=
notFoundError
# If a resource is forbidden,
# why reveal that it exists?
def
debugError
(
self
,
entry
):
raise
'NotFound'
,
self
.
html
(
"Debugging Notice"
,
"Bobo has encountered a problem publishing your object.<p>"
"
\
n
%s"
%
entry
)
def
badRequestError
(
self
,
name
):
if
regex
.
match
(
'^[A-Z_0-9]+$'
,
name
)
>=
0
:
raise
'InternalError'
,
self
.
html
(
"Internal Error"
,
"Sorry, an internal error occurred in this resource."
)
raise
'BadRequest'
,
self
.
html
(
"Invalid request"
,
"The parameter, <em>%s</em>, was omitted from the request."
"<!--%s-->"
%
(
name
,
self
.
request
))
def
unauthorized
(
self
,
realm
,
debug_mode
=
None
):
if
not
(
self
.
request
.
has_key
(
'REMOTE_USER'
)
and
self
.
request
[
'REMOTE_USER'
]):
self
.
response
[
'WWW-authenticate'
]
=
'basic realm="%s"'
%
realm
m
=
"<strong>You are not authorized to access this resource.</strong>"
if
debug_mode
:
if
self
.
HTTP_AUTHORIZATION
:
m
=
m
+
'
\
n
Username and password are not correct.'
else
:
m
=
m
+
'
\
n
No Authorization header found.'
raise
'Unauthorized'
,
m
def
forbiddenError
(
self
,
object
=
None
):
raise
'NotFound'
,
self
.
html
(
"Resource not found"
,
"Sorry, the requested document does not exist.<p>"
"<!--%s-->"
%
object
)
def
get_request_data
(
self
,
request_params
):
try
:
request_params
=
request_params
()
except
:
pass
for
key
in
request_params
.
keys
():
self
.
request
[
key
]
=
request_params
[
key
]
from
Request
import
Request
from
maybe_lock
import
allocate_lock
def
publish
(
self
,
module_name
,
after_list
,
published
=
'web_objects'
,
imported_modules
=
{},
module_dicts
=
{},
debug
=
0
):
def
publish
(
request
,
module_name
,
after_list
,
debug
=
0
):
request
=
self
.
request
request_get
=
request
.
get
response
=
self
.
response
response
=
request
.
response
# First check for "cancel" redirect:
cancel
=
''
...
...
@@ -322,232 +104,39 @@ class ModulePublisher:
cancel
=
request_get
(
'CANCEL_ACTION'
,
''
)
if
cancel
:
raise
'Redirect'
,
cancel
(
bobo_before
,
bobo_after
,
request_params
,
inherited_groups
,
groups
,
roles
,
object
,
doc
,
published
,
realm
,
module_name
,
debug_mode
)
=
get_module_info
(
module_name
)
bobo_before
,
bobo_after
,
object
,
realm
,
debug_mode
=
get_module_info
(
module_name
)
after_list
[
0
]
=
bobo_after
if
debug_mode
:
response
.
debug_mode
=
debug_mode
if
realm
and
not
request
.
get
(
'REMOTE_USER'
,
None
):
response
.
realm
=
realm
if
bobo_before
is
not
None
:
bobo_before
();
if
request_params
:
self
.
get_request_data
(
request_params
)
# Get a nice clean path list:
path
=
strip
(
request_get
(
'PATH_INFO'
))
__traceback_info__
=
path
if
path
[:
1
]
!=
'/'
:
path
=
'/'
+
path
if
path
[
-
1
:]
!=
'/'
:
path
=
path
+
'/'
if
find
(
path
,
'/.'
)
>=
0
:
path
=
join
(
split
(
path
,
'/./'
),
'/'
)
l
=
find
(
path
,
'/../'
,
1
)
while
l
>
0
:
p1
=
path
[:
l
]
path
=
path
[:
rfind
(
p1
,
'/'
)
+
1
]
+
path
[
l
+
4
:]
l
=
find
(
path
,
'/../'
,
1
)
path
=
path
[
1
:
-
1
]
path
=
split
(
path
,
'/'
)
while
path
and
not
path
[
0
]:
path
=
path
[
1
:]
method
=
upper
(
request_get
(
'REQUEST_METHOD'
))
if
method
==
'GET'
or
method
==
'POST'
:
method
=
'index_html'
URL
=
request
.
script
# if the top object has a __bobo_traverse__ method, then use it
# to possibly traverse to an alternate top-level object.
if
hasattr
(
object
,
'__bobo_traverse__'
):
request
[
'URL'
]
=
URL
try
:
object
=
object
.
__bobo_traverse__
(
request
)
except
:
pass
# Get default object if no path was specified:
if
not
path
:
entry_name
=
method
try
:
if
hasattr
(
object
,
entry_name
):
response
.
setBase
(
URL
)
path
=
[
entry_name
]
else
:
try
:
if
object
.
has_key
(
entry_name
):
path
=
[
entry_name
]
except
:
pass
except
:
pass
if
not
path
:
path
=
[
'help'
]
# Traverse the URL to find the object:
request
[
'PARENTS'
]
=
parents
=
[]
if
hasattr
(
object
,
'__of__'
):
# Try to bind the top-level object to the request
object
=
object
.
__of__
(
RequestContainer
(
REQUEST
=
request
))
steps
=
[]
while
path
:
entry_name
,
path
=
path
[
0
],
path
[
1
:]
URL
=
"%s/%s"
%
(
URL
,
quote
(
entry_name
))
got
=
0
if
entry_name
:
if
entry_name
[:
1
]
==
'_'
:
if
debug_mode
:
self
.
debugError
(
"Object name begins with an underscore at: %s"
%
URL
)
else
:
self
.
forbiddenError
(
entry_name
)
if
hasattr
(
object
,
'__bobo_traverse__'
):
request
[
'URL'
]
=
URL
subobject
=
object
.
__bobo_traverse__
(
request
,
entry_name
)
if
type
(
subobject
)
is
type
(())
and
len
(
subobject
)
>
1
:
while
len
(
subobject
)
>
2
:
parents
.
append
(
subobject
[
0
])
subobject
=
subobject
[
1
:]
object
,
subobject
=
subobject
else
:
try
:
subobject
=
getattr
(
object
,
entry_name
)
except
AttributeError
:
got
=
1
try
:
subobject
=
object
[
entry_name
]
except
(
KeyError
,
IndexError
,
TypeError
,
AttributeError
):
if
entry_name
==
'.'
:
subobject
=
object
elif
entry_name
==
'..'
and
parents
:
subobject
=
parents
[
-
1
]
elif
debug_mode
:
self
.
debugError
(
"Cannot locate object at: %s"
%
URL
)
else
:
self
.
notFoundError
(
URL
)
if
subobject
is
object
and
entry_name
==
'.'
:
URL
=
URL
[:
rfind
(
URL
,
'/'
)]
else
:
try
:
try
:
doc
=
subobject
.
__doc__
except
:
doc
=
getattr
(
object
,
entry_name
+
'__doc__'
)
if
not
doc
:
raise
AttributeError
,
entry_name
except
:
if
debug_mode
:
self
.
debugError
(
"Missing doc string at: %s"
%
URL
)
else
:
self
.
notFoundError
(
"%s"
%
(
URL
))
if
hasattr
(
subobject
,
'__roles__'
):
roles
=
subobject
.
__roles__
else
:
if
not
got
:
roleshack
=
entry_name
+
'__roles__'
if
hasattr
(
object
,
roleshack
):
roles
=
getattr
(
object
,
roleshack
)
# Promote subobject to object
parents
.
append
(
object
)
object
=
subobject
steps
.
append
(
entry_name
)
# Check for method:
if
not
path
:
if
hasattr
(
object
,
method
)
and
entry_name
!=
method
:
response
.
setBase
(
URL
)
path
=
[
method
]
else
:
if
(
hasattr
(
object
,
'__call__'
)
and
hasattr
(
object
.
__call__
,
'__roles__'
)):
roles
=
object
.
__call__
.
__roles__
if
self
.
_hacked_path
:
i
=
rfind
(
URL
,
'/'
)
if
i
>
0
:
response
.
setBase
(
URL
[:
i
])
if
entry_name
!=
method
and
method
!=
'index_html'
:
if
debug_mode
:
self
.
debugError
(
"Method %s not found at: %s"
%
(
method
,
URL
))
else
:
self
.
notFoundError
(
method
)
request
.
steps
=
steps
parents
.
reverse
()
# Do authorization checks
user
=
None
i
=
0
if
roles
is
not
None
:
last_parent_index
=
len
(
parents
)
if
hasattr
(
object
,
'__allow_groups__'
):
groups
=
object
.
__allow_groups__
inext
=
0
else
:
inext
=
None
for
i
in
range
(
last_parent_index
):
if
hasattr
(
parents
[
i
],
'__allow_groups__'
):
groups
=
parents
[
i
].
__allow_groups__
inext
=
i
+
1
break
if
inext
is
not
None
:
i
=
inext
if
hasattr
(
groups
,
'validate'
):
v
=
groups
.
validate
else
:
v
=
old_validation
auth
=
self
.
HTTP_AUTHORIZATION
if
v
is
old_validation
and
roles
is
UNSPECIFIED_ROLES
:
# No roles, so if we have a named group, get roles from
# group keys
if
hasattr
(
groups
,
'keys'
):
roles
=
groups
.
keys
()
else
:
try
:
groups
=
groups
()
except
:
pass
try
:
roles
=
groups
.
keys
()
except
:
pass
if
groups
is
None
:
# Public group, hack structures to get it to validate
roles
=
None
auth
=
''
if
v
is
old_validation
:
user
=
old_validation
(
groups
,
request
,
auth
,
roles
)
elif
roles
is
UNSPECIFIED_ROLES
:
user
=
v
(
request
,
auth
)
else
:
user
=
v
(
request
,
auth
,
roles
)
while
user
is
None
and
i
<
last_parent_index
:
parent
=
parents
[
i
]
i
=
i
+
1
if
hasattr
(
parent
,
'__allow_groups__'
):
groups
=
parent
.
__allow_groups__
else
:
continue
if
hasattr
(
groups
,
'validate'
):
v
=
groups
.
validate
else
:
v
=
old_validation
if
v
is
old_validation
:
user
=
old_validation
(
groups
,
request
,
auth
,
roles
)
elif
roles
is
UNSPECIFIED_ROLES
:
user
=
v
(
request
,
auth
)
else
:
user
=
v
(
request
,
auth
,
roles
)
if
user
is
None
and
roles
!=
UNSPECIFIED_ROLES
:
self
.
unauthorized
(
realm
,
debug_mode
)
steps
=
join
(
steps
[:
-
i
],
'/'
)
if
user
is
not
None
:
request
[
'AUTHENTICATED_USER'
]
=
user
request
[
'AUTHENTICATION_PATH'
]
=
steps
request
[
'PARENTS'
]
=
parents
=
[
object
]
# Attempt to start a transaction:
try
:
transaction
=
get_transaction
()
except
:
transaction
=
None
if
transaction
is
not
None
:
transaction
.
begin
()
object
=
request
.
traverse
(
path
)
# Record transaction meta-data
if
transaction
is
not
None
:
info
=
"
\
t
"
+
request_get
(
'PATH_INFO'
)
auth_user
=
request_get
(
'AUTHENTICATED_USER'
,
None
)
if
auth_user
is
not
None
:
info
=
(
"%s %s"
%
(
steps
,
auth_user
))
+
info
transaction
.
begin
(
info
)
# Now get object meta-data to decide if and how it should be
# called:
info
=
(
"%s %s"
%
(
request_get
(
'AUTHENTICATION_PATH'
),
auth_user
))
+
info
transaction
.
note
(
info
)
# Now get object meta-data to decide if and how it should be called:
object_as_function
=
object
# First, assume we have a method:
...
...
@@ -570,9 +159,6 @@ class ModulePublisher:
else
:
return
response
.
setBody
(
object
)
request
[
'URL'
]
=
URL
request
[
'PARENT_URL'
]
=
URL
[:
rfind
(
URL
,
'/'
)]
args
=
[]
nrequired
=
len
(
argument_names
)
-
(
len
(
defaults
or
[]))
for
name_index
in
range
(
len
(
argument_names
)):
...
...
@@ -586,7 +172,7 @@ class ModulePublisher:
else
:
args
.
append
(
v
)
args
=
tuple
(
args
)
if
debug
:
result
=
self
.
call_object
(
object
,
args
)
if
debug
:
result
=
call_object
(
object
,
args
)
else
:
result
=
apply
(
object
,
args
)
if
result
and
result
is
not
response
:
response
.
setBody
(
result
)
...
...
@@ -595,7 +181,7 @@ class ModulePublisher:
return
response
def
call_object
(
self
,
object
,
args
):
def
call_object
(
object
,
args
):
result
=
apply
(
object
,
args
)
# Type s<cr> to step into published object.
return
result
...
...
@@ -642,27 +228,6 @@ def get_module_info(module_name, modules={},
except
:
debug_mode
=
None
else
:
debug_mode
=
None
# Check whether tracebacks should be hidden:
if
hasattr
(
module
,
'__bobo_hide_tracebacks__'
):
hide_tracebacks
=
not
not
module
.
__bobo_hide_tracebacks__
elif
os
.
environ
.
has_key
(
'BOBO_HIDE_TRACEBACKS'
):
hide_tracebacks
=
lower
(
os
.
environ
[
'BOBO_HIDE_TRACEBACKS'
])
if
hide_tracebacks
==
'y'
or
hide_tracebacks
==
'yes'
:
hide_tracebacks
=
1
else
:
try
:
hide_tracebacks
=
atoi
(
hide_tracebacks
)
except
:
hide_tracebacks
=
None
else
:
hide_tracebacks
=
1
# Reset response handling of tracebacks, if necessary:
if
debug_mode
or
not
hide_tracebacks
:
def
hack_response
():
import
Response
Response
.
_tbopen
=
'<PRE>'
Response
.
_tbclose
=
'</PRE>'
hack_response
()
if
hasattr
(
module
,
'__bobo_before__'
):
bobo_before
=
module
.
__bobo_before__
else
:
bobo_before
=
None
...
...
@@ -670,192 +235,25 @@ def get_module_info(module_name, modules={},
if
hasattr
(
module
,
'__bobo_after__'
):
bobo_after
=
module
.
__bobo_after__
else
:
bobo_after
=
None
# Get request data from outermost environment:
if
hasattr
(
module
,
'__request_data__'
):
request_params
=
module
.
__request_data__
else
:
request_params
=
None
# Get initial group data:
inherited_groups
=
[]
if
hasattr
(
module
,
'__allow_groups__'
):
groups
=
module
.
__allow_groups__
inherited_groups
.
append
(
groups
)
else
:
groups
=
None
web_objects
=
None
roles
=
UNSPECIFIED_ROLES
if
hasattr
(
module
,
'bobo_application'
):
object
=
module
.
bobo_application
if
hasattr
(
object
,
'__allow_groups__'
):
groups
=
object
.
__allow_groups__
inherited_groups
.
append
(
groups
)
else
:
groups
=
None
if
hasattr
(
object
,
'__roles__'
):
roles
=
object
.
__roles__
else
:
if
hasattr
(
module
,
'web_objects'
):
web_objects
=
module
.
web_objects
object
=
web_objects
elif
hasattr
(
module
,
'web_objects'
):
object
=
module
.
web_objects
else
:
object
=
module
published
=
web_objects
try
:
doc
=
module
.
__doc__
except
:
if
web_objects
is
not
None
:
doc
=
' '
else
:
doc
=
None
info
=
(
bobo_before
,
bobo_after
,
request_params
,
inherited_groups
,
groups
,
roles
,
object
,
doc
,
published
,
realm
,
module_name
,
debug_mode
)
info
=
(
bobo_before
,
bobo_after
,
object
,
realm
,
debug_mode
)
modules
[
module_name
]
=
modules
[
module_name
+
'.cgi'
]
=
info
return
info
except
:
if
hasattr
(
sys
,
'exc_info'
):
t
,
v
,
tb
=
sys
.
exc_info
()
else
:
t
,
v
,
tb
=
sys
.
exc_type
,
sys
.
exc_value
,
sys
.
exc_traceback
t
,
v
,
tb
=
sys
.
exc_info
()
v
=
str
(
v
)
raise
ImportError
,
(
t
,
v
),
tb
finally
:
tb
=
None
release
()
def
str_field
(
v
):
if
type
(
v
)
is
ListType
:
return
map
(
str_field
,
v
)
if
hasattr
(
v
,
'__class__'
)
and
v
.
__class__
is
FieldStorage
:
v
=
v
.
value
elif
type
(
v
)
is
not
StringType
:
if
hasattr
(
v
,
'file'
)
and
v
.
file
:
v
=
v
.
file
elif
hasattr
(
v
,
'value'
):
v
=
v
.
value
return
v
class
FileUpload
:
'''
\
File upload objects
File upload objects are used to represent file-uploaded data.
File upload objects can be used just like files.
In addition, they have a 'headers' attribute that is a dictionary
containing the file-upload headers, and a 'filename' attribute
containing the name of the uploaded file.
'''
def
__init__
(
self
,
aFieldStorage
):
file
=
aFieldStorage
.
file
if
hasattr
(
file
,
'__methods__'
):
methods
=
file
.
__methods__
else
:
methods
=
[
'close'
,
'fileno'
,
'flush'
,
'isatty'
,
'read'
,
'readline'
,
'readlines'
,
'seek'
,
'tell'
,
'truncate'
,
'write'
,
'writelines'
]
d
=
self
.
__dict__
for
m
in
methods
:
if
hasattr
(
file
,
m
):
d
[
m
]
=
getattr
(
file
,
m
)
self
.
headers
=
aFieldStorage
.
headers
self
.
filename
=
aFieldStorage
.
filename
parse_cookie_lock
=
allocate_lock
()
def
parse_cookie
(
text
,
result
=
None
,
qparmre
=
regex
.
compile
(
'
\
([
\
0- ]*'
'
\
([^
\
0- ;,=
\
"
]+
\
)=
"
\
([^
"
]*
\
)
\
"'
'
\
([
\
0- ]*[;,]
\
)?[
\
0- ]*
\
)
'
),
parmre=regex.compile(
'
\
([
\
0
-
]
*
'
'
\
([
^
\
0
-
;,
=
\
"]+
\
)=
\
([^
\
0
- ;,
\
"
]*
\
)
'
'
\
([
\
0- ]*[;,]
\
)?[
\
0- ]*
\
)
'
),
acquire=parse_cookie_lock.acquire,
release=parse_cookie_lock.release,
):
if result is None: result={}
already_have=result.has_key
acquire()
try:
if qparmre.match(text) >= 0:
# Match quoted correct cookies
name=qparmre.group(2)
value=qparmre.group(3)
l=len(qparmre.group(1))
elif parmre.match(text) >= 0:
# Match evil MSIE cookies ;)
name=parmre.group(2)
value=parmre.group(3)
l=len(parmre.group(1))
else:
# this may be an invalid cookie.
# We'll simply bail without raising an error
# if the cookie is invalid.
return result
finally: release()
if not already_have(name): result[name]=value
return apply(parse_cookie,(text[l:],result))
base64=None
def old_validation(groups, request, HTTP_AUTHORIZATION,
roles=UNSPECIFIED_ROLES):
global base64
if base64 is None: import base64
if HTTP_AUTHORIZATION:
if lower(HTTP_AUTHORIZATION[:6]) != 'basic ':
if roles is None: return ''
return None
[name,password] = split(
base64.decodestring(
split(HTTP_AUTHORIZATION)[-1]), ':')
elif request.environ.has_key('REMOTE_USER'):
name=request.environ['REMOTE_USER']
password=None
else:
if roles is None: return ''
return None
if roles is None: return name
keys=None
try:
keys=groups.keys
except:
try:
groups=groups() # Maybe it was a method defining a group
keys=groups.keys
except: pass
if keys is not None:
# OK, we have a named group, so apply the roles to the named
# group.
if roles is UNSPECIFIED_ROLES: roles=keys()
g=[]
for role in roles:
if groups.has_key(role): g.append(groups[role])
groups=g
for d in groups:
if d.has_key(name) and (d[name]==password or password is None):
return name
if keys is None:
# Not a named group, so don't go further
raise 'Forbidden', (
"""<strong>You are not authorized to access this resource""")
return None
def
publish_module
(
module_name
,
stdin
=
sys
.
stdin
,
stdout
=
sys
.
stdout
,
stderr
=
sys
.
stderr
,
environ
=
os
.
environ
,
debug
=
0
):
...
...
@@ -867,15 +265,10 @@ def publish_module(module_name,
try
:
try
:
response
=
Response
(
stdout
=
stdout
,
stderr
=
stderr
)
publisher = ModulePublisher(
stdin=stdin, stdout=stdout, stderr=stderr,
environ=environ)
response = publisher.response
request=publisher.request
request
=
Request
(
stdin
,
environ
,
response
)
finally
:
pass
response = publisher.publish(module_name,after_list,
debug=debug)
response
=
publish
(
request
,
module_name
,
after_list
,
debug
=
debug
)
except
SystemExit
,
v
:
if
hasattr
(
sys
,
'exc_info'
):
must_die
=
sys
.
exc_info
()
else
:
must_die
=
SystemExit
,
v
,
sys
.
exc_traceback
...
...
lib/python/ZPublisher/Request.py
View file @
accff8b5
...
...
@@ -82,253 +82,6 @@
# file.
#
##############################################################################
__version__
=
'$Revision: 1.6 $'
[
11
:
-
2
]
import
regex
from
string
import
atoi
,
atol
,
join
,
upper
,
split
,
strip
,
rfind
isCGI_NAME
=
{
'SERVER_SOFTWARE'
:
1
,
'SERVER_NAME'
:
1
,
'GATEWAY_INTERFACE'
:
1
,
'SERVER_PROTOCOL'
:
1
,
'SERVER_PORT'
:
1
,
'REQUEST_METHOD'
:
1
,
'PATH_INFO'
:
1
,
'PATH_TRANSLATED'
:
1
,
'SCRIPT_NAME'
:
1
,
'QUERY_STRING'
:
1
,
'REMOTE_HOST'
:
1
,
'REMOTE_ADDR'
:
1
,
'AUTH_TYPE'
:
1
,
'REMOTE_USER'
:
1
,
'REMOTE_IDENT'
:
1
,
'CONTENT_TYPE'
:
1
,
'CONTENT_LENGTH'
:
1
,
}.
has_key
hide_key
=
{
'HTTP_AUTHORIZATION'
:
1
,
'HTTP_CGI_AUTHORIZATION'
:
1
,
}.
has_key
class
Request
:
"""
\
Model HTTP request data.
This object provides access to request data. This includes, the
input headers, form data, server data, and cookies.
Request objects are created by the object publisher and will be
passed to published objects through the argument name, REQUEST.
The request object is a mapping object that represents a
collection of variable to value mappings. In addition, variables
are divided into four categories:
- Environment variables
These variables include input headers, server data, and other
request-related data. The variable names are as <a
href="http://hoohoo.ncsa.uiuc.edu/cgi/env.html">specified</a>
in the <a
href="http://hoohoo.ncsa.uiuc.edu/cgi/interface.html">CGI
specification</a>
- Form data
These are data extracted from either a URL-encoded query
string or body, if present.
- Cookies
These are the cookie data, if present.
- Other
Data that may be set by an application object.
The form attribute of a request is actually a Field Storage
object. When file uploads are used, this provides a richer and
more complex interface than is provided by accessing form data as
items of the request. See the FieldStorage class documentation
for more details.
The request object may be used as a mapping object, in which case
values will be looked up in the order: environment variables,
other variables, form data, and then cookies.
"""
def
__init__
(
self
,
environ
,
form
,
stdin
):
self
.
environ
=
environ
self
.
other
=
form
self
.
stdin
=
stdin
have_env
=
environ
.
has_key
b
=
script
=
strip
(
environ
[
'SCRIPT_NAME'
])
while
b
and
b
[
-
1
]
==
'/'
:
b
=
b
[:
-
1
]
p
=
rfind
(
b
,
'/'
)
if
p
>=
0
:
b
=
b
[:
p
+
1
]
else
:
b
=
''
while
b
and
b
[
0
]
==
'/'
:
b
=
b
[
1
:]
if
have_env
(
'SERVER_URL'
):
server_url
=
strip
(
environ
[
'SERVER_URL'
])
else
:
if
have_env
(
'HTTPS'
)
and
(
environ
[
'HTTPS'
]
==
"on"
or
environ
[
'HTTPS'
]
==
"ON"
):
server_url
=
'https://'
elif
(
have_env
(
'SERVER_PORT_SECURE'
)
and
environ
[
'SERVER_PORT_SECURE'
]
==
"1"
):
server_url
=
'https://'
else
:
server_url
=
'http://'
if
have_env
(
'HTTP_HOST'
):
server_url
=
server_url
+
strip
(
environ
[
'HTTP_HOST'
])
else
:
server_url
=
server_url
+
strip
(
environ
[
'SERVER_NAME'
])
server_port
=
environ
[
'SERVER_PORT'
]
if
server_port
!=
'80'
:
server_url
=
server_url
+
':'
+
server_port
if
server_url
[
-
1
:]
==
'/'
:
server_url
=
server_url
[:
-
1
]
self
.
base
=
"%s/%s"
%
(
server_url
,
b
)
while
script
[:
1
]
==
'/'
:
script
=
script
[
1
:]
if
script
:
self
.
script
=
"%s/%s"
%
(
server_url
,
script
)
else
:
self
.
script
=
server_url
def
get_header
(
self
,
name
,
default
=
None
):
"""Return the named HTTP header, or an optional default
argument or None if the header is not found. Note that
both original and CGI-ified header names are recognized,
e.g. 'Content-Type', 'CONTENT_TYPE' and 'HTTP_CONTENT_TYPE'
should all return the Content-Type header, if available.
"""
environ
=
self
.
environ
name
=
upper
(
join
(
split
(
name
,
"-"
),
"_"
))
val
=
environ
.
get
(
name
,
None
)
if
val
is
not
None
:
return
val
if
name
[:
5
]
!=
'HTTP_'
:
name
=
'HTTP_%s'
%
name
return
environ
.
get
(
name
,
default
)
def
__setitem__
(
self
,
key
,
value
):
"""Set application variables
This method is used to set a variable in the requests "other"
category.
"""
self
.
other
[
key
]
=
value
set
=
__setitem__
def
__getitem__
(
self
,
key
,
default
=
isCGI_NAME
,
# Any special internal marker will do
URLmatch
=
regex
.
compile
(
'URL[0-9]$'
).
match
,
BASEmatch
=
regex
.
compile
(
'BASE[0-9]$'
).
match
,
):
"""Get a variable value
Return a value for the required variable name.
The value will be looked up from one of the request data
categories. The search order is environment variables,
other variables, form data, and then cookies.
"""
#"
other
=
self
.
other
if
other
.
has_key
(
key
):
if
key
==
'REQUEST'
:
return
self
return
other
[
key
]
if
key
[:
1
]
==
'U'
and
URLmatch
(
key
)
>=
0
and
other
.
has_key
(
'URL'
):
n
=
ord
(
key
[
3
])
-
ord
(
'0'
)
URL
=
other
[
'URL'
]
for
i
in
range
(
0
,
n
):
l
=
rfind
(
URL
,
'/'
)
if
l
>=
0
:
URL
=
URL
[:
l
]
else
:
raise
KeyError
,
key
other
[
key
]
=
URL
return
URL
if
isCGI_NAME
(
key
)
or
key
[:
5
]
==
'HTTP_'
:
environ
=
self
.
environ
if
environ
.
has_key
(
key
)
and
(
not
hide_key
(
key
)):
return
environ
[
key
]
return
''
if
key
==
'REQUEST'
:
return
self
if
key
[:
1
]
==
'B'
and
BASEmatch
(
key
)
>=
0
and
other
.
has_key
(
'URL'
):
n
=
ord
(
key
[
4
])
-
ord
(
'0'
)
if
n
:
v
=
self
.
script
while
v
[
-
1
:]
==
'/'
:
v
=
v
[:
-
1
]
v
=
join
([
v
]
+
self
.
steps
[:
n
-
1
],
'/'
)
else
:
v
=
self
.
base
while
v
[
-
1
:]
==
'/'
:
v
=
v
[:
-
1
]
other
[
key
]
=
v
return
v
if
default
is
not
isCGI_NAME
:
# Check marker
return
default
raise
KeyError
,
key
__getattr__
=
get
=
__getitem__
def
has_key
(
self
,
key
):
return
self
.
get
(
key
,
Request
)
is
not
Request
def
keys
(
self
):
keys
=
{}
for
key
in
self
.
environ
.
keys
():
if
(
isCGI_NAME
(
key
)
or
key
[:
5
]
==
'HTTP_'
)
and
\
(
not
hide_key
(
key
)):
keys
[
key
]
=
1
keys
.
update
(
self
.
other
)
lasturl
=
""
for
n
in
"0123456789"
:
key
=
"URL%s"
%
n
try
:
if
lasturl
!=
self
[
key
]:
keys
[
key
]
=
1
lasturl
=
self
[
key
]
else
:
break
except
KeyError
:
pass
for
n
in
"0123456789"
:
key
=
"BASE%s"
%
n
try
:
if
lasturl
!=
self
[
key
]:
keys
[
key
]
=
1
lasturl
=
self
[
key
]
else
:
break
except
KeyError
:
pass
return
keys
.
keys
()
def
items
(
self
):
result
=
[]
for
k
in
self
.
keys
():
result
.
append
((
k
,
self
.
get
(
k
)))
return
result
def
__str__
(
self
):
def
str
(
self
,
name
):
dict
=
getattr
(
self
,
name
)
data
=
[]
for
key
,
val
in
dict
.
items
():
if
not
hide_key
(
key
):
data
.
append
(
'%s: %s'
%
(
key
,
`val`
))
return
"%s:
\
n
\
t
%s
\
n
\
n
"
%
(
name
,
join
(
data
,
'
\
n
\
t
'
))
return
"%s
\
n
%s
\
n
"
%
(
str
(
self
,
'form'
),
str
(
self
,
'environ'
))
__repr__
=
__str__
import
HTTPRequest
Request
=
HTTPRequest
.
HTTPRequest
del
HTTPRequest
lib/python/ZPublisher/Response.py
View file @
accff8b5
...
...
@@ -82,572 +82,6 @@
# file.
#
##############################################################################
'''CGI Response Output formatter
$Id: Response.py,v 1.47 1999/02/05 21:12:47 brian Exp $'''
__version__
=
'$Revision: 1.47 $'
[
11
:
-
2
]
import
string
,
types
,
sys
,
regex
from
string
import
find
,
rfind
,
lower
,
upper
,
strip
,
split
,
join
,
translate
from
types
import
StringType
,
InstanceType
nl2sp
=
string
.
maketrans
(
'
\
n
'
,
' '
)
status_reasons
=
{
100
:
'Continue'
,
101
:
'Switching Protocols'
,
102
:
'Processing'
,
200
:
'OK'
,
201
:
'Created'
,
202
:
'Accepted'
,
203
:
'Non-Authoritative Information'
,
204
:
'No Content'
,
205
:
'Reset Content'
,
206
:
'Partial Content'
,
207
:
'Multi-Status'
,
300
:
'Multiple Choices'
,
301
:
'Moved Permanently'
,
302
:
'Moved Temporarily'
,
303
:
'See Other'
,
304
:
'Not Modified'
,
305
:
'Use Proxy'
,
307
:
'Temporary Redirect'
,
400
:
'Bad Request'
,
401
:
'Unauthorized'
,
402
:
'Payment Required'
,
403
:
'Forbidden'
,
404
:
'Not Found'
,
405
:
'Method Not Allowed'
,
406
:
'Not Acceptable'
,
407
:
'Proxy Authentication Required'
,
408
:
'Request Time-out'
,
409
:
'Conflict'
,
410
:
'Gone'
,
411
:
'Length Required'
,
412
:
'Precondition Failed'
,
413
:
'Request Entity Too Large'
,
414
:
'Request-URI Too Large'
,
415
:
'Unsupported Media Type'
,
416
:
'Requested range not satisfiable'
,
417
:
'Expectation Failed'
,
422
:
'Unprocessable Entity'
,
423
:
'Locked'
,
424
:
'Failed Dependency'
,
500
:
'Internal Server Error'
,
501
:
'Not Implemented'
,
502
:
'Bad Gateway'
,
503
:
'Service Unavailable'
,
504
:
'Gateway Time-out'
,
505
:
'HTTP Version not supported'
,
507
:
'Insufficient Storage'
,
}
status_codes
=
{}
# Add mappings for builtin exceptions and
# provide text -> error code lookups.
for
key
,
val
in
status_reasons
.
items
():
status_codes
[
lower
(
join
(
split
(
val
,
' '
),
''
))]
=
key
status_codes
[
lower
(
val
)]
=
key
status_codes
[
key
]
=
key
en
=
filter
(
lambda
n
:
n
[
-
5
:]
==
'Error'
,
dir
(
__builtins__
))
for
name
in
map
(
lower
,
en
):
status_codes
[
name
]
=
500
status_codes
[
'nameerror'
]
=
503
status_codes
[
'keyerror'
]
=
503
status_codes
[
'redirect'
]
=
300
end_of_header_search
=
regex
.
compile
(
'</head>'
,
regex
.
casefold
).
search
accumulate_header
=
{
'set-cookie'
:
1
}.
has_key
_tbopen
,
_tbclose
=
'<!--'
,
'-->'
class
Response
:
"""
\
An object representation of an HTTP response.
The Response type encapsulates all possible responses to HTTP
requests. Responses are normally created by the object publisher.
A published object may recieve the response abject as an argument
named 'RESPONSE'. A published object may also create it's own
response object. Normally, published objects use response objects
to:
- Provide specific control over output headers,
- Set cookies, or
- Provide stream-oriented output.
If stream oriented output is used, then the response object
passed into the object must be used.
"""
#'
accumulated_headers
=
''
body
=
''
def
__init__
(
self
,
body
=
''
,
status
=
200
,
headers
=
None
,
stdout
=
sys
.
stdout
,
stderr
=
sys
.
stderr
,):
'''
\
Creates a new response. In effect, the constructor calls
"self.setBody(body); self.setStatus(status); for name in
headers.keys(): self.setHeader(name, headers[name])"
'''
if
headers
is
None
:
headers
=
{}
self
.
headers
=
headers
if
status
==
200
:
self
.
status
=
200
headers
[
'status'
]
=
"200 OK"
else
:
self
.
setStatus
(
status
)
self
.
base
=
''
if
body
:
self
.
setBody
(
body
)
self
.
cookies
=
{}
self
.
stdout
=
stdout
self
.
stderr
=
stderr
def
setStatus
(
self
,
status
,
reason
=
None
):
'''
\
Sets the HTTP status code of the response; the argument may
either be an integer or a string from { OK, Created, Accepted,
NoContent, MovedPermanently, MovedTemporarily,
NotModified, BadRequest, Unauthorized, Forbidden,
NotFound, InternalError, NotImplemented, BadGateway,
ServiceUnavailable } that will be converted to the correct
integer value. '''
if
type
(
status
)
is
types
.
StringType
:
status
=
lower
(
status
)
if
status_codes
.
has_key
(
status
):
status
=
status_codes
[
status
]
else
:
status
=
500
self
.
status
=
status
if
reason
is
None
:
if
status_reasons
.
has_key
(
status
):
reason
=
status_reasons
[
status
]
else
:
reason
=
'Unknown'
self
.
setHeader
(
'Status'
,
"%d %s"
%
(
status
,
str
(
reason
)))
def
setHeader
(
self
,
name
,
value
):
'''
\
Sets an HTTP return header "name" with value "value", clearing
the previous value set for the header, if one exists. '''
n
=
lower
(
name
)
if
accumulate_header
(
n
):
self
.
accumulated_headers
=
(
"%s%s: %s
\
n
"
%
(
self
.
accumulated_headers
,
name
,
value
))
else
:
self
.
headers
[
n
]
=
value
__setitem__
=
setHeader
def
setBody
(
self
,
body
,
title
=
''
,
bogus_str_search
=
regex
.
compile
(
" [a-fA-F0-9]+>$"
).
search
,
):
'''
\
Set the body of the response
Sets the return body equal to the (string) argument "body". Also
updates the "content-length" return header.
You can also specify a title, in which case the title and body
will be wrapped up in html, head, title, and body tags.
If the body is a 2-element tuple, then it will be treated
as (title,body)
'''
if
type
(
body
)
is
types
.
TupleType
:
title
,
body
=
body
if
type
(
body
)
is
not
types
.
StringType
:
if
hasattr
(
body
,
'asHTML'
):
body
=
body
.
asHTML
()
body
=
str
(
body
)
l
=
len
(
body
)
if
(
find
(
body
,
'>'
)
==
l
-
1
and
body
[:
1
]
==
'<'
and
l
<
200
and
bogus_str_search
(
body
)
>
0
):
raise
'NotFound'
,
(
"Sorry, the requested document does not exist.<p>"
"
\
n
%s
\
n
%s
\
n
%s"
%
(
_tbopen
,
body
[
1
:
-
1
],
_tbclose
))
if
(
title
):
self
.
body
=
(
'<html>
\
n
<head>
\
n
<title>%s</title>
\
n
</head>
\
n
'
'<body>
\
n
%s
\
n
</body>
\
n
</html>'
%
(
str
(
title
),
str
(
body
)))
else
:
self
.
body
=
str
(
body
)
self
.
insertBase
()
return
self
def
getStatus
(
self
):
'Returns the current HTTP status code as an integer. '
return
self
.
status
def
setBase
(
self
,
base
):
'Set the base URL for the returned document.'
if
base
[
-
1
:]
!=
'/'
:
base
=
base
+
'/'
self
.
base
=
base
self
.
insertBase
()
def
insertBase
(
self
,
base_re_search
=
regex
.
compile
(
'
\
(<
b
ase[
\
0
- ]+[^>]+>
\
)
'
,
regex.casefold).search
):
if (self.headers.has_key('
content
-
type
') and
self.headers['
content
-
type
']!='
text
/
html
'): return
if self.base:
body=self.body
if body:
e=end_of_header_search(body)
if e >= 0:
b=base_re_search(body)
if b < 0:
self.body=('
%
s
\
t
<
base
href
=
"%s"
>
\
n
%
s
' %
(body[:e],self.base,body[e:]))
def appendCookie(self, name, value):
'''
\
Returns an HTTP header that sets a cookie on cookie-enabled
browsers with a key "name" and value "value". If a value for the
cookie has previously been set in the response object, the new
value is appended to the old one separated by a colon. '''
cookies=self.cookies
if cookies.has_key(name): cookie=cookies[name]
else: cookie=cookies[name]={}
if cookie.has_key('
value
'):
cookie['
value
']='
%
s
:
%
s
' % (cookie['
value
'], value)
else: cookie['
value
']=value
def expireCookie(self, name, **kw):
'''
\
Cause an HTTP cookie to be removed from the browser
The response will include an HTTP header that will remove the cookie
corresponding to "name" on the client, if one exists. This is
accomplished by sending a new cookie with an expiration date
that has already passed. Note that some clients require a path
to be specified - this path must exactly match the path given
when creating the cookie. The path can be specified as a keyword
argument.
'''
dict={'
max_age
':0, '
expires
':'
Wed
,
31
-
Dec
-
97
23
:
59
:
59
GMT
'}
for k, v in kw.items():
dict[k]=v
apply(Response.setCookie, (self, name, '
deleted
'), dict)
def setCookie(self,name,value,**kw):
'''
\
Set an HTTP cookie on the browser
The response will include an HTTP header that sets a cookie on
cookie-enabled browsers with a key "name" and value
"value". This overwrites any previously set value for the
cookie in the Response object.
'''
cookies=self.cookies
if cookies.has_key(name):
cookie=cookies[name]
else: cookie=cookies[name]={}
for k, v in kw.items():
cookie[k]=v
cookie['
value
']=value
def appendBody(self, body):
self.setBody(self.getBody() + body)
def getHeader(self, name):
'''
\
Get a header value
Returns the value associated with a HTTP return header, or
"None" if no such header has been set in the response
yet. '''
headers=self.headers
if headers.has_key(name): return headers[name]
def __getitem__(self, name):
'
Get
the
value
of
an
output
header
'
return self.headers[name]
def getBody(self):
'
Returns
a
string
representing
the
currently
set
body
.
'
return self.body
def appendHeader(self, name, value, delimiter=","):
'''
\
Append a value to a cookie
Sets an HTTP return header "name" with value "value",
appending it following a comma if there was a previous value
set for the header. '''
headers=self.headers
if headers.has_key(name):
h=self.header[name]
h="%s%s
\
n
\
t
%s" % (h,delimiter,value)
else: h=value
self.setHeader(name,h)
def isHTML(self,str):
return lower(strip(str)[:6]) == '
<
html
>
' or find(str,'
</
') > 0
def quoteHTML(self,text,
subs={'
&
':'
&
amp
;
', "<":'
&
lt
;
', ">":'
&
gt
;
', '
\
"':'"'}
):
for ent in '&<>
\
"
':
if find(text, ent) >= 0:
text=join(split(text,ent),subs[ent])
return text
def format_exception(self,etype,value,tb,limit=None):
import traceback
result=['Traceback (innermost last):']
if limit is None:
if hasattr(sys, 'tracebacklimit'):
limit = sys.tracebacklimit
n = 0
while tb is not None and (limit is None or n < limit):
f = tb.tb_frame
lineno = tb.tb_lineno
co = f.f_code
filename = co.co_filename
name = co.co_name
locals=f.f_locals
result.append(' File %s, line %d, in %s'
% (filename,lineno,name))
try: result.append(' (Object: %s)' %
locals[co.co_varnames[0]].__name__)
except: pass
try: result.append(' (Info: %s)' %
str(locals['__traceback_info__']))
except: pass
tb = tb.tb_next
n = n+1
result.append(join(traceback.format_exception_only(etype, value),
' '))
return result
def _traceback(self,t,v,tb):
tb=self.format_exception(t,v,tb,200)
tb=join(tb,'
\
n
')
tb=self.quoteHTML(tb)
return "
\
n
%
s
\
n
%
s
\
n
%
s
" % (_tbopen, tb, _tbclose)
def redirect(self, location):
"""Cause a redirection without raising an error"""
self.status=302
headers=self.headers
headers['status']='302 Moved Temporarily'
headers['location']=location
return location
def exception(self, fatal=0, info=None,
absuri_match=regex.compile(
"
^
"
"
\
(
/
\
|
\
([
a
-
zA
-
Z0
-
9
+
.
-
]
+
:
\
)
\
)
"
"
[
^
\
000
-
\
"
\
\
#<>]*"
"
\
\
(#[^
\
000
-
\
"
\
\
#<>]*
\
\
)?"
"$"
).
match
,
tag_search
=
regex
.
compile
(
'[a-zA-Z]>'
).
search
,
):
if
type
(
info
)
is
type
(())
and
len
(
info
)
==
3
:
t
,
v
,
tb
=
info
elif
hasattr
(
sys
,
'exc_info'
):
t
,
v
,
tb
=
sys
.
exc_info
()
else
:
t
,
v
,
tb
=
sys
.
exc_type
,
sys
.
exc_value
,
sys
.
exc_traceback
stb
=
tb
# Abort running transaction, if any:
try
:
get_transaction
().
abort
()
except
:
pass
try
:
# Try to capture exception info for bci calls
et
=
translate
(
str
(
t
),
nl2sp
)
ev
=
translate
(
str
(
v
),
nl2sp
)
# Get the tb tail, which is the interesting part:
while
tb
.
tb_next
is
not
None
:
tb
=
tb
.
tb_next
el
=
str
(
tb
.
tb_lineno
)
ef
=
str
(
tb
.
tb_frame
.
f_code
.
co_filename
)
if
find
(
ev
,
'<html>'
)
>=
0
:
ev
=
'bobo exception'
self
.
setHeader
(
'bobo-exception-type'
,
et
)
self
.
setHeader
(
'bobo-exception-value'
,
ev
[:
255
])
self
.
setHeader
(
'bobo-exception-file'
,
ef
)
self
.
setHeader
(
'bobo-exception-line'
,
el
)
except
:
# Dont try so hard that we cause other problems ;)
pass
tb
=
stb
stb
=
None
self
.
setStatus
(
t
)
if
self
.
status
>=
300
and
self
.
status
<
400
:
if
type
(
v
)
==
types
.
StringType
and
absuri_match
(
v
)
>=
0
:
if
self
.
status
==
300
:
self
.
setStatus
(
302
)
self
.
setHeader
(
'location'
,
v
)
tb
=
None
return
self
else
:
try
:
l
,
b
=
v
if
type
(
l
)
==
types
.
StringType
and
absuri_match
(
l
)
>=
0
:
if
self
.
status
==
300
:
self
.
setStatus
(
302
)
self
.
setHeader
(
'location'
,
l
)
self
.
setBody
(
b
)
tb
=
None
return
self
except
:
pass
b
=
v
if
isinstance
(
b
,
Exception
):
b
=
str
(
b
)
if
fatal
:
if
t
is
SystemExit
and
v
.
code
==
0
:
tb
=
self
.
setBody
(
(
str
(
t
),
'This application has exited normally.<p>'
+
self
.
_traceback
(
t
,
v
,
tb
)))
else
:
tb
=
self
.
setBody
(
(
str
(
t
),
'Sorry, a SERIOUS APPLICATION ERROR occurred.<p>'
+
self
.
_traceback
(
t
,
v
,
tb
)))
elif
type
(
b
)
is
not
types
.
StringType
or
tag_search
(
b
)
<
0
:
tb
=
self
.
setBody
(
(
str
(
t
),
'Sorry, an error occurred.<p>'
+
self
.
_traceback
(
t
,
v
,
tb
)))
elif
self
.
isHTML
(
b
):
tb
=
self
.
setBody
(
b
+
self
.
_traceback
(
t
,
'(see above)'
,
tb
))
else
:
tb
=
self
.
setBody
((
str
(
t
),
b
+
self
.
_traceback
(
t
,
'(see above)'
,
tb
)))
return
tb
_wrote
=
None
def
_cookie_list
(
self
):
cookie_list
=
[]
for
name
,
attrs
in
self
.
cookies
.
items
():
# Note that as of May 98, IE4 ignores cookies with
# quoted cookie attr values, so only the value part
# of name=value pairs may be quoted.
cookie
=
'Set-Cookie: %s="%s"'
%
(
name
,
attrs
[
'value'
])
for
name
,
v
in
attrs
.
items
():
name
=
lower
(
name
)
if
name
==
'expires'
:
cookie
=
'%s; Expires=%s'
%
(
cookie
,
v
)
elif
name
==
'domain'
:
cookie
=
'%s; Domain=%s'
%
(
cookie
,
v
)
elif
name
==
'path'
:
cookie
=
'%s; Path=%s'
%
(
cookie
,
v
)
elif
name
==
'max_age'
:
cookie
=
'%s; Max-Age=%s'
%
(
cookie
,
v
)
elif
name
==
'comment'
:
cookie
=
'%s; Comment=%s'
%
(
cookie
,
v
)
elif
name
==
'secure'
:
cookie
=
'%s; Secure'
%
cookie
cookie_list
.
append
(
cookie
)
# Should really check size of cookies here!
return
cookie_list
def
__str__
(
self
,
html_search
=
regex
.
compile
(
'<html>'
,
regex
.
casefold
).
search
,
):
if
self
.
_wrote
:
return
''
# Streaming output was used.
headers
=
self
.
headers
body
=
self
.
body
if
body
:
isHTML
=
self
.
isHTML
(
body
)
if
not
headers
.
has_key
(
'content-type'
):
if
isHTML
:
c
=
'text/html'
else
:
c
=
'text/plain'
self
.
setHeader
(
'content-type'
,
c
)
else
:
isHTML
=
headers
[
'content-type'
]
==
'text/html'
if
isHTML
and
end_of_header_search
(
self
.
body
)
<
0
:
lhtml
=
html_search
(
body
)
if
lhtml
>=
0
:
lhtml
=
lhtml
+
6
body
=
'%s<head></head>
\
n
%s'
%
(
body
[:
lhtml
],
body
[
lhtml
:])
else
:
body
=
'<html><head></head>
\
n
'
+
body
self
.
setBody
(
body
)
body
=
self
.
body
if
not
headers
.
has_key
(
'content-type'
)
and
self
.
status
==
200
:
self
.
setStatus
(
'nocontent'
)
if
not
headers
.
has_key
(
'content-length'
):
self
.
setHeader
(
'content-length'
,
len
(
body
))
headersl
=
[]
append
=
headersl
.
append
# Make sure status comes out first!
try
:
v
=
headers
[
'status'
]
del
headers
[
'status'
]
except
:
v
=
"200 OK"
append
(
"Status: "
+
v
)
for
k
,
v
in
headers
.
items
():
k
=
upper
(
k
[:
1
])
+
k
[
1
:]
start
=
0
l
=
find
(
k
,
'-'
,
start
)
while
l
>=
start
:
k
=
"%s-%s%s"
%
(
k
[:
l
],
upper
(
k
[
l
+
1
:
l
+
2
]),
k
[
l
+
2
:])
start
=
l
+
1
l
=
find
(
k
,
'-'
,
start
)
append
(
"%s: %s"
%
(
k
,
v
))
if
self
.
cookies
:
headersl
=
headersl
+
self
.
_cookie_list
()
headersl
[
len
(
headersl
):]
=
[
self
.
accumulated_headers
,
body
]
return
join
(
headersl
,
'
\
n
'
)
def
__repr__
(
self
):
return
'Response(%s)'
%
`self.body`
def
flush
(
self
):
pass
def
write
(
self
,
data
):
"""
\
Return data as a stream
HTML data may be returned using a stream-oriented interface.
This allows the browser to display partial results while
computation of a response to proceed.
The published object should first set any output headers or
cookies on the response object.
Note that published objects must not generate any errors
after beginning stream-oriented output.
"""
self
.
body
=
self
.
body
+
data
headers
=
self
.
headers
if
headers
.
has_key
(
'content-length'
):
del
headers
[
'content-length'
]
if
not
self
.
headers
.
has_key
(
'content-type'
):
self
.
setHeader
(
'content-type'
,
'text/html'
)
self
.
insertBase
()
body
=
self
.
body
self
.
body
=
''
self
.
write
=
write
=
self
.
stdout
.
write
try
:
self
.
flush
=
self
.
stdout
.
flush
except
:
pass
write
(
str
(
self
))
self
.
_wrote
=
1
write
(
body
)
import
HTTPResponse
Response
=
HTTPResponse
.
HTTPResponse
del
HTTPResponse
lib/python/ZPublisher/Test.py
View file @
accff8b5
...
...
@@ -162,9 +162,9 @@ Examples
s
$Id: Test.py,v 1.2
8 1998/12/04 20:15:34
jim Exp $
$Id: Test.py,v 1.2
9 1999/02/18 17:17:56
jim Exp $
'''
__version__
=
'$Revision: 1.2
8
$'
[
11
:
-
2
]
__version__
=
'$Revision: 1.2
9
$'
[
11
:
-
2
]
import
sys
,
traceback
,
profile
,
os
,
getopt
,
string
from
time
import
clock
...
...
@@ -243,23 +243,15 @@ def publish_module_pm(module_name,
environ
=
os
.
environ
,
debug
=
0
):
from
Response
import
Response
from
Publish
import
ModulePublisher
from
Request
import
Request
from
Publish
import
publish
after_list
=
[
None
]
request
=
None
try
:
response
=
Response
(
stdout
=
stdout
,
stderr
=
stderr
)
publisher
=
ModulePublisher
(
stdin
=
stdin
,
stdout
=
stdout
,
stderr
=
stderr
,
environ
=
environ
)
response
=
publisher
.
response
request
=
publisher
.
request
response
=
publisher
.
publish
(
module_name
,
after_list
,
debug
=
debug
)
request
.
other
=
{}
response
=
str
(
response
)
finally
:
try
:
request
.
other
=
{}
except
:
pass
if
after_list
[
0
]
is
not
None
:
after_list
[
0
]()
request
=
Request
(
stdin
,
environ
,
response
)
response
=
publish
(
request
,
module_name
,
after_list
,
debug
=
debug
)
try
:
from
codehack
import
getlineno
except
:
...
...
@@ -325,7 +317,7 @@ def publish(script=None,path_info='/',
else
:
run
(
c
)
elif
debug
:
import
Publish
from
Publish
import
ModulePublisher
from
Publish
import
publish
,
call_object
import
pdb
class
Pdb
(
pdb
.
Pdb
):
...
...
@@ -355,10 +347,8 @@ def publish(script=None,path_info='/',
filename
=
code
.
co_filename
db
.
set_break
(
filename
,
lineno
)
fbreak
(
db
,
ModulePublisher
.
publish
)
fbreak
(
db
,
ModulePublisher
.
call_object
)
#fbreak(db,Publish.new_find_object)
#fbreak(db,Publish.old_find_object)
fbreak
(
db
,
publish
)
fbreak
(
db
,
call_object
)
dbdata
=
{
'breakpoints'
:(),
'env'
:
env
}
b
=
''
...
...
lib/python/ZPublisher/maybe_lock.py
0 → 100644
View file @
accff8b5
##############################################################################
#
# Zope Public License (ZPL) Version 0.9.4
# ---------------------------------------
#
# Copyright (c) Digital Creations. All rights reserved.
#
# Redistribution and use in source and binary forms, with or
# without modification, are permitted provided that the following
# conditions are met:
#
# 1. Redistributions in source code must retain the above
# copyright notice, this list of conditions, and the following
# disclaimer.
#
# 2. Redistributions in binary form must reproduce the above
# copyright notice, this list of conditions, and the following
# disclaimer in the documentation and/or other materials
# provided with the distribution.
#
# 3. Any use, including use of the Zope software to operate a
# website, must either comply with the terms described below
# under "Attribution" or alternatively secure a separate
# license from Digital Creations.
#
# 4. All advertising materials, documentation, or technical papers
# mentioning features derived from or use of this software must
# display the following acknowledgement:
#
# "This product includes software developed by Digital
# Creations for use in the Z Object Publishing Environment
# (http://www.zope.org/)."
#
# 5. Names associated with Zope or Digital Creations must not be
# used to endorse or promote products derived from this
# software without prior written permission from Digital
# Creations.
#
# 6. Redistributions of any form whatsoever must retain the
# following acknowledgment:
#
# "This product includes software developed by Digital
# Creations for use in the Z Object Publishing Environment
# (http://www.zope.org/)."
#
# 7. Modifications are encouraged but must be packaged separately
# as patches to official Zope releases. Distributions that do
# not clearly separate the patches from the original work must
# be clearly labeled as unofficial distributions.
#
# Disclaimer
#
# THIS SOFTWARE IS PROVIDED BY DIGITAL CREATIONS ``AS IS'' AND
# ANY EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
# FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT
# SHALL DIGITAL CREATIONS OR ITS CONTRIBUTORS BE LIABLE FOR ANY
# DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
# CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
# PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
# ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
# IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
# THE POSSIBILITY OF SUCH DAMAGE.
#
# Attribution
#
# Individuals or organizations using this software as a web site
# must provide attribution by placing the accompanying "button"
# and a link to the accompanying "credits page" on the website's
# main entry point. In cases where this placement of
# attribution is not feasible, a separate arrangment must be
# concluded with Digital Creations. Those using the software
# for purposes other than web sites must provide a corresponding
# attribution in locations that include a copyright using a
# manner best suited to the application environment.
#
# This software consists of contributions made by Digital
# Creations and many individuals on behalf of Digital Creations.
# Specific attributions are listed in the accompanying credits
# file.
#
##############################################################################
__version__
=
'$Revision: 1.1 $'
[
11
:
-
2
]
# Waaaa, I wish I didn't have to work this hard.
try
:
from
thread
import
allocate_lock
except
:
class
allocate_lock
:
def
acquire
(
*
args
):
pass
def
release
(
*
args
):
pass
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment