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