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