1 #!/usr/bin/env python 2 3 """ 4 mod_python classes. 5 """ 6 7 import Generic 8 from Helpers.Response import ConvertingStream 9 from mod_python.util import parse_qs, FieldStorage 10 from mod_python import apache 11 try: 12 from mod_python import Cookie 13 except ImportError: 14 # NOTE: Should provide an alternative implementation. 15 Cookie = None 16 17 class Transaction(Generic.Transaction): 18 19 """ 20 mod_python transaction interface. 21 """ 22 23 def __init__(self, trans): 24 25 "Initialise the transaction using the mod_python transaction 'trans'." 26 27 self.trans = trans 28 self.response_code = apache.OK 29 self.user = None 30 self.content_type = None 31 32 # Cached information. 33 34 self.storage_body = None 35 36 # Request-related methods. 37 38 def get_request_stream(self): 39 40 """ 41 A framework-specific method which returns the request stream for 42 the transaction. 43 """ 44 45 return self.trans 46 47 def get_request_method(self): 48 49 """ 50 A framework-specific method which gets the request method. 51 """ 52 53 return self.trans.method 54 55 def get_headers(self): 56 57 """ 58 A framework-specific method which returns all request headers as a 59 dictionary-like object mapping header names to values. 60 NOTE: If duplicate header names are permitted, then this interface will 61 NOTE: need to change. 62 """ 63 64 return self.trans.headers_in 65 66 def get_header_values(self, key): 67 68 """ 69 A framework-specific method which returns a list of all request header 70 values associated with the given 'key'. Note that according to RFC 2616, 71 'key' is treated as a case-insensitive string. 72 """ 73 74 return self.convert_to_list(self.trans.headers_in.get(key)) 75 76 def get_content_type(self): 77 78 """ 79 A framework-specific method which gets the content type specified on the 80 request, along with the charset employed. 81 """ 82 83 return self.parse_content_type(self.trans.content_type) 84 85 def get_content_charsets(self): 86 87 """ 88 Returns the character set preferences. 89 """ 90 91 return self.parse_content_preferences(self.trans.headers_in.get("Accept-Charset")) 92 93 def get_content_languages(self): 94 95 """ 96 A framework-specific method which extracts language information from 97 the transaction. 98 """ 99 100 return self.parse_content_preferences(self.trans.headers_in.get("Accept-Language")) 101 102 def get_path(self): 103 104 """ 105 A framework-specific method which gets the entire path from the request. 106 """ 107 108 query_string = self.get_query_string() 109 if query_string: 110 return self.trans.uri + "?" + query_string 111 else: 112 return self.trans.uri 113 114 def get_path_without_query(self): 115 116 """ 117 A framework-specific method which gets the entire path from the request 118 minus the query string. 119 """ 120 121 return self.trans.uri 122 123 def get_path_info(self): 124 125 """ 126 A framework-specific method which gets the "path info" (the part of the 127 URL after the resource name handling the current request) from the 128 request. 129 """ 130 131 return self.trans.path_info 132 133 def get_query_string(self): 134 135 """ 136 A framework-specific method which gets the query string from the path in 137 the request. 138 """ 139 140 return self.trans.args or "" 141 142 # Higher level request-related methods. 143 144 def get_fields_from_path(self): 145 146 """ 147 A framework-specific method which extracts the form fields from the 148 path specified in the transaction. The underlying framework may refuse 149 to supply fields from the path if handling a POST transaction. 150 151 Returns a dictionary mapping field names to lists of values (even if a 152 single value is associated with any given field name). 153 """ 154 155 return parse_qs(self.get_query_string(), 1) # keep_blank_values=1 156 157 def get_fields_from_body(self, encoding=None): 158 159 """ 160 A framework-specific method which extracts the form fields from the 161 message body in the transaction. The optional 'encoding' parameter 162 specifies the character encoding of the message body for cases where no 163 such information is available, but where the default encoding is to be 164 overridden. 165 166 Returns a dictionary mapping field names to lists of values (even if a 167 single value is associated with any given field name). 168 169 The mod_python.util.FieldStorage class may augment the fields from the 170 body with fields found in the path. 171 """ 172 173 encoding = self.get_content_type().charset or encoding or "iso-8859-1" 174 175 if self.storage_body is None: 176 self.storage_body = FieldStorage(self.trans, keep_blank_values=1) 177 178 # Traverse the storage, finding each field value. 179 180 fields = {} 181 for field in self.storage_body.list: 182 if not fields.has_key(field.name): 183 fields[field.name] = [] 184 fields[field.name].append(unicode(field.value, encoding)) 185 return fields 186 187 def get_user(self): 188 189 """ 190 A framework-specific method which extracts user information from the 191 transaction. 192 193 Returns a username as a string or None if no user is defined. 194 """ 195 196 if self.user is not None: 197 return self.user 198 else: 199 return self.trans.user 200 201 def get_cookies(self): 202 203 """ 204 A framework-specific method which obtains cookie information from the 205 request. 206 207 Returns a dictionary mapping cookie names to cookie objects. 208 209 NOTE: No additional information is passed to the underlying API despite 210 NOTE: support for enhanced cookies in mod_python. 211 """ 212 213 if Cookie: 214 return Cookie.get_cookies(self.trans) 215 else: 216 return None 217 218 def get_cookie(self, cookie_name): 219 220 """ 221 A framework-specific method which obtains cookie information from the 222 request. 223 224 Returns a cookie object for the given 'cookie_name' or None if no such 225 cookie exists. 226 """ 227 228 return self.get_cookies().get(cookie_name) 229 230 # Response-related methods. 231 232 def get_response_stream(self): 233 234 """ 235 A framework-specific method which returns the response stream for 236 the transaction. 237 """ 238 239 # Unicode can upset this operation. Using either the specified charset, 240 # the same charset as that used in the request, or a default encoding. 241 242 encoding = self.get_content_type().charset or "utf-8" 243 if self.content_type: 244 encoding = self.content_type.charset or encoding 245 return ConvertingStream(self.trans, encoding) 246 247 def get_response_code(self): 248 249 """ 250 Get the response code associated with the transaction. If no response 251 code is defined, None is returned. 252 """ 253 254 return self.response_code 255 256 def set_response_code(self, response_code): 257 258 """ 259 Set the 'response_code' using a numeric constant defined in the HTTP 260 specification. 261 """ 262 263 self.response_code = response_code 264 265 def set_header_value(self, header, value): 266 267 """ 268 Set the HTTP 'header' with the given 'value'. 269 """ 270 271 self.trans.headers_out[self.format_header_value(header)] = self.format_header_value(value) 272 273 def set_content_type(self, content_type): 274 275 """ 276 A framework-specific method which sets the 'content_type' for the 277 response. 278 """ 279 280 # Remember the content type for encoding purposes later. 281 282 self.content_type = content_type 283 self.trans.content_type = self.format_content_type(content_type) 284 285 def set_cookie(self, cookie): 286 287 """ 288 A framework-specific method which stores the given 'cookie' object in 289 the response. 290 """ 291 292 if Cookie: 293 Cookie.add_cookie(self.trans, cookie) 294 else: 295 # NOTE: Should raise an exception or provide an implementation. 296 pass 297 298 def set_cookie_value(self, name, value, path=None, expires=None): 299 300 """ 301 A framework-specific method which stores a cookie with the given 'name' 302 and 'value' in the response. 303 304 The optional 'path' is a string which specifies the scope of the cookie, 305 and the optional 'expires' parameter is a value compatible with the 306 time.time function, and indicates the expiry date/time of the cookie. 307 """ 308 309 # NOTE: We just hope that Cookie converts Unicode arguments to US-ASCII. 310 311 if Cookie: 312 cookie = Cookie.Cookie(name, value) 313 if expires is not None: 314 cookie.expires = expires 315 if path is not None: 316 cookie.path = path 317 Cookie.add_cookie(self.trans, cookie) 318 else: 319 # NOTE: Should raise an exception or provide an implementation. 320 pass 321 322 def delete_cookie(self, cookie_name): 323 324 """ 325 A framework-specific method which adds to the response a request that 326 the cookie with the given 'cookie_name' be deleted/discarded by the 327 client. 328 """ 329 330 # Create a special cookie, given that we do not know whether the browser 331 # has been sent the cookie or not. 332 # NOTE: Magic discovered in Webware. 333 334 if Cookie: 335 cookie = Cookie.Cookie(cookie_name, "") 336 cookie.path = "/" 337 cookie.expires = 0 338 cookie.max_age = 0 339 Cookie.add_cookie(self.trans, cookie) 340 else: 341 # NOTE: Should raise an exception or provide an implementation. 342 pass 343 344 # Application-specific methods. 345 346 def set_user(self, username): 347 348 """ 349 An application-specific method which sets the user information with 350 'username' in the transaction. This affects subsequent calls to 351 'get_user'. 352 """ 353 354 self.user = username 355 356 # vim: tabstop=4 expandtab shiftwidth=4