Package mms :: Module wsp_pdu
[hide private]
[frames] | no frames]

Source Code for Module mms.wsp_pdu

   1  #!/usr/bin/env python 
   2  # 
   3  # This library is free software, distributed under the terms of 
   4  # the GNU Lesser General Public License Version 2. 
   5  # See the COPYING file included in this archive 
   6  # 
   7  # The docstrings in this module contain epytext markup; API documentation 
   8  # may be created by processing this file with epydoc: http://epydoc.sf.net 
   9   
  10  """ WSP Data Unit structure encoding and decoding classes 
  11   
  12  Throughout the classes defined in this module, the following "primitive data 
  13  type" terminology applies, as specified in [5], section 8.1.1:: 
  14   
  15      Data Type     Definition 
  16      bit           1 bit of data 
  17      octet         8 bits of opaque data 
  18      uint8         8-bit unsigned integer 
  19      uint16        16-bit unsigned integer 
  20      uint32        32-bit unsigned integer 
  21      uintvar       variable length unsigned integer 
  22   
  23  This Encoder and Decoder classes provided in this module firstly provides 
  24  public methods for decoding and encoding each of these data primitives (where 
  25  needed). 
  26   
  27  Next, they provide methods encapsulating the basic WSP Header encoding rules 
  28  as defined in section 8.4.2.1 of [5]. 
  29   
  30  Finally, the classes defined here provide methods for decoding/parsing 
  31  specific WSP header fields. 
  32   
  33  @author: Francois Aucamp C{<faucamp@csir.co.za>} 
  34  @license:  GNU Lesser General Public License, version 2 
  35  @note: This is part of the PyMMS library 
  36   
  37  @note: References used in the code and this document: 
  38      5. Wap Forum/Open Mobile Alliance, "WAP-230 Wireless Session Protocol Specification" 
  39      U{http://www.openmobilealliance.org/tech/affiliates/LicenseAgreement.asp?DocName=/wap/wap-230-wsp-20010705-a.pdf} 
  40  """ 
  41   
  42  import array 
  43  from iterator import PreviewIterator 
  44  #import itertools 
  45   
  46   
  47        
  48   
49 -class WSPEncodingAssignments:
50 """ Static class containing the constant values defined in [5] for 51 well-known content types, parameter names, etc. 52 53 It also defines some function for combining assigned number-tables for 54 specific WSP encoding versions, where appropriate. 55 56 This is used by both the Encoder and Decoder classes during well-known 57 assigned number lookups (usually these functions have the string 58 C{WellKnown} in their names). 59 60 - Assigned parameters are stored in a dictionary, C{wkParameters}, 61 containing all assigned values for WSP encoding versions 1.1 - 1.4, 62 in the format: 63 C{{<int>assigned number: (<str>name, <str>expected value type)}} 64 A "encoding versioned"-version of this dictionary can be retrieved 65 by calling the C{wellKnowParameters()} function with an appropriate 66 WSP encoding version as parameter. 67 - Assigned content types are stored in a list, C{wkContentTypes}, in 68 order; thus, their index in the list is equal to their assigned 69 value. 70 71 """ 72 wspPDUTypes = {0x01: 'Connect', 73 0x02: 'ConnectReply', 74 0x03: 'Redirect', 75 0x04: 'Reply', 76 0x05: 'Disconnect', 77 0x06: 'Push', 78 0x07: 'ConfirmedPush', 79 0x08: 'Suspend', 80 0x09: 'Resume', 81 0x40: 'Get', 82 0x60: 'Post'} 83 84 # Well-known parameter assignments ([5], table 38) 85 wkParameters = {0x00: ('Q', 'QValue'), 86 0x01: ('Charset', 'WellKnownCharset'), 87 0x02: ('Level', 'VersionValue'), 88 0x03: ('Type', 'IntegerValue'), 89 0x05: ('Name', 'TextString'), 90 0x06: ('Filename', 'TextString'), 91 0x07: ('Differences', 'Field-name'), 92 0x08: ('Padding', 'ShortInteger'), 93 0x09: ('Type', 'ConstrainedEncoding'), # encoding version 1.2 94 0x0a: ('Start', 'TextString'), 95 0x0b: ('Start-info', 'TextString'), 96 0x0c: ('Comment', 'TextString'), # encoding version 1.3 97 0x0d: ('Domain', 'TextString'), 98 0x0e: ('Max-Age', 'DeltaSecondsValue'), 99 0x0f: ('Path', 'TextString'), 100 0x10: ('Secure', 'NoValue'), 101 0x11: ('SEC', 'ShortInteger'), # encoding version 1.4 102 0x12: ('MAC', 'TextValue'), 103 0x13: ('Creation-date', 'DateValue'), 104 0x14: ('Modification-date', 'DateValue'), 105 0x15: ('Read-date', 'DateValue'), 106 0x16: ('Size', 'IntegerValue'), 107 0x17: ('Name', 'TextValue'), 108 0x18: ('Filename', 'TextValue'), 109 0x19: ('Start', 'TextValue'), 110 0x1a: ('Start-info', 'TextValue'), 111 0x1b: ('Comment', 'TextValue'), 112 0x1c: ('Domain', 'TextValue'), 113 0x1d: ('Path', 'TextValue')} 114 115 # Content type assignments ([5], table 40) 116 wkContentTypes = ['*/*', 'text/*', 'text/html', 'text/plain', 117 'text/x-hdml', 'text/x-ttml', 'text/x-vCalendar', 118 'text/x-vCard', 'text/vnd.wap.wml', 119 'text/vnd.wap.wmlscript', 'text/vnd.wap.wta-event', 120 'multipart/*', 'multipart/mixed', 'multipart/form-data', 121 'multipart/byterantes', 'multipart/alternative', 122 'application/*', 'application/java-vm', 123 'application/x-www-form-urlencoded', 124 'application/x-hdmlc', 'application/vnd.wap.wmlc', 125 'application/vnd.wap.wmlscriptc', 126 'application/vnd.wap.wta-eventc', 127 'application/vnd.wap.uaprof', 128 'application/vnd.wap.wtls-ca-certificate', 129 'application/vnd.wap.wtls-user-certificate', 130 'application/x-x509-ca-cert', 131 'application/x-x509-user-cert', 132 'image/*', 'image/gif', 'image/jpeg', 'image/tiff', 133 'image/png', 'image/vnd.wap.wbmp', 134 'application/vnd.wap.multipart.*', 135 'application/vnd.wap.multipart.mixed', 136 'application/vnd.wap.multipart.form-data', 137 'application/vnd.wap.multipart.byteranges', 138 'application/vnd.wap.multipart.alternative', 139 'application/xml', 'text/xml', 140 'application/vnd.wap.wbxml', 141 'application/x-x968-cross-cert', 142 'application/x-x968-ca-cert', 143 'application/x-x968-user-cert', 144 'text/vnd.wap.si', 145 'application/vnd.wap.sic', 146 'text/vnd.wap.sl', 147 'application/vnd.wap.slc', 148 'text/vnd.wap.co', 149 'application/vnd.wap.coc', 150 'application/vnd.wap.multipart.related', 151 'application/vnd.wap.sia', 152 'text/vnd.wap.connectivity-xml', 153 'application/vnd.wap.connectivity-wbxml', 154 'application/pkcs7-mime', 155 'application/vnd.wap.hashed-certificate', 156 'application/vnd.wap.signed-certificate', 157 'application/vnd.wap.cert-response', 158 'application/xhtml+xml', 159 'application/wml+xml', 160 'text/css', 161 'application/vnd.wap.mms-message', 162 'application/vnd.wap.rollover-certificate', 163 'application/vnd.wap.locc+wbxml', 164 'application/vnd.wap.loc+xml', 165 'application/vnd.syncml.dm+wbxml', 166 'application/vnd.syncml.dm+xml', 167 'application/vnd.syncml.notification', 168 'application/vnd.wap.xhtml+xml', 169 'application/vnd.wv.csp.cir', 170 'application/vnd.oma.dd+xml', 171 'application/vnd.oma.drm.message', 172 'application/vnd.oma.drm.content', 173 'application/vnd.oma.drm.rights+xml', 174 'application/vnd.oma.drm.rights+wbxml'] 175 176 177 # Well-known character sets (table 42 of [5]) 178 # Format {<assinged_number> : <charset>} 179 # Note that the assigned number is the same as the IANA MIBEnum value 180 # "gsm-default-alphabet" is not included, as it is not assigned any value in [5] 181 # Also note, this is by no means a complete list 182 wkCharSets = {0x07EA: 'big5', 183 0x03E8: 'iso-10646-ucs-2', 184 0x04: 'iso-8859-1', 185 0x05: 'iso-8859-2', 186 0x06: 'iso-8859-3', 187 0x07: 'iso-8859-4', 188 0x08: 'iso-8859-5', 189 0x09: 'iso-8859-6', 190 0x0A: 'iso-8859-7', 191 0x0B: 'iso-8859-8', 192 0x0C: 'iso-8859-9', 193 0x11: 'shift_JIS', 194 0x03: 'us-ascii', 195 0x6A: 'utf-8'} 196 197 # Header Field Name assignments ([5], table 39) 198 hdrFieldNames = ['Accept', 'Accept-Charset', 'Accept-Encoding', 199 'Accept-Language', 'Accept-Ranges', 'Age', 200 'Allow', 'Authorization', 'Cache-Control', 201 'Connection', 'Content-Base', 'Content-Encoding', 202 'Content-Language', 'Content-Length', 203 'Content-Location', 'Content-MD5', 'Content-Range', 204 'Content-Type', 'Date', 'Etag', 'Expires', 'From', 205 'Host', 'If-Modified-Since', 'If-Match', 206 'If-None-Match', 'If-Range', 'If-Unmodified-Since', 207 'Location', 'Last-Modified', 'Max-Forwards', 'Pragma', 208 'Proxy-Authenticate', 'Proxy-Authorization', 'Public', 209 'Range', 'Referer', 'Retry-After', 'Server', 210 'Transfer-Encoding', 'Upgrade', 'User-Agent', 211 'Vary', 'Via', 'Warning', 'WWW-Authenticate', 212 'Content-Disposition', 213 # encoding version 1.2 214 'X-Wap-Application-Id', 'X-Wap-Content-URI', 215 'X-Wap-Initiator-URI', 'Accept-Application', 216 'Bearer-Indication', 'Push-Flag', 'Profile', 217 'Profile-Diff', 'Profile-Warning', 218 # encoding version 1.3 219 'Expect', 'TE', 'Trailer', 'Accept-Charset', 220 'Accept-Encoding', 'Cache-Control', 221 'Content-Range', 'X-Wap-Tod', 'Content-ID', 222 'Set-Cookie', 'Cookie', 'Encoding-Version', 223 # encoding version 1.4 224 'Profile-Warning', 'Content-Disposition', 225 'X-WAP-Security', 'Cache-Control'] 226 227 #TODO: combine this dict with the hdrFieldNames table (same as well known parameter assignments) 228 # Temporary fix to allow different types of header field values to be dynamically decoded 229 hdrFieldEncodings = {'Accept': 'AcceptValue', 230 'Pragma': 'PragmaValue'} 231 232 @staticmethod
233 - def wellKnownParameters(encodingVersion = '1.2'):
234 """ Formats list of assigned values for well-known parameter names, 235 for the specified WSP encoding version. 236 237 @param encodingVersion: The WSP encoding version to use. This defaults 238 to "1.2", but may be "1.1", "1.2", "1.3" or 239 "1.4" (see table 38 in [5] for details). 240 @type encodingVersion: str 241 242 @raise ValueError: The specified encoding version is invalid. 243 244 @return: A dictionary containing the well-known parameters with 245 assigned numbers for the specified encoding version (and 246 lower). Entries in this dict follow the format: 247 C{{<int:assigned_number> : (<str:param_name>, <str:expected_type>)}} 248 @rtype: dict 249 """ 250 if encodingVersion not in ('1.1', '1.2', '1.3', '1.4'): 251 raise ValueError, 'encodingVersion must be "1.1", "1.2", "1.3" or "1.4"' 252 else: 253 version = int(encodingVersion.split('.')[1]) 254 wkVersionedParameters = dict(WSPEncodingAssignments.wkParameters) 255 if version <= 3: 256 for assignedNumber in range(0x11, 0x1e): 257 del wkVersionedParameters[assignedNumber] 258 if version <= 2: 259 for assignedNumber in range(0x0c, 0x11): 260 del wkVersionedParameters[assignedNumber] 261 if version == 1: 262 for assignedNumber in range(0x09, 0x0c): 263 del wkVersionedParameters[assignedNumber] 264 return wkVersionedParameters
265 266 @staticmethod
267 - def headerFieldNames(encodingVersion = '1.2'):
268 """ Formats list of assigned values for header field names, for the 269 specified WSP encoding version. 270 271 @param encodingVersion: The WSP encoding version to use. This defaults 272 to "1.2", but may be "1.1", "1.2", "1.3" or 273 "1.4" (see table 39 in [5] for details). 274 @type encodingVersion: str 275 276 @raise ValueError: The specified encoding version is invalid. 277 278 @return: A list containing the WSP header field names with assigned 279 numbers for the specified encoding version (and lower). 280 @rtype: list 281 """ 282 if encodingVersion not in ('1.1', '1.2', '1.3', '1.4'): 283 raise ValueError, 'encodingVersion must be "1.1", "1.2", "1.3" or "1.4"' 284 else: 285 version = int(encodingVersion.split('.')[1]) 286 versionedHdrFieldNames = list(WSPEncodingAssignments.hdrFieldNames) 287 if version == 3: 288 versionedHdrFieldNames = versionedHdrFieldNames[:0x44] 289 elif version == 2: 290 versionedHdrFieldNames = versionedHdrFieldNames[:0x38] 291 elif version == 1: 292 versionedHdrFieldNames = versionedHdrFieldNames[:0x2f] 293 return versionedHdrFieldNames
294 295
296 -class DecodeError(Exception):
297 """ The decoding operation failed; most probably due to an invalid byte in 298 the sequence provided for decoding """
299
300 -class EncodeError(Exception):
301 """ The encoding operation failed; most probably due to an invalid value 302 provided for encoding """
303
304 -class Decoder:
305 """ A WSP Data unit decoder """ 306 @staticmethod
307 - def decodeUint8(byteIter):
308 """ Decodes an 8-bit unsigned integer from the byte pointed to by 309 C{byteIter.next()} 310 311 @note: this function will move the iterator passed as C{byteIter} one 312 byte forward. 313 314 @param byteIter: an iterator over a sequence of bytes 315 @type byteIter: iter 316 317 @return: the decoded 8-bit unsigned integer 318 @rtype: int 319 """ 320 # Make the byte unsigned 321 return byteIter.next() & 0xff
322 323 @staticmethod
324 - def decodeUintvar(byteIter):
325 """ Decodes the variable-length unsigned integer starting at the 326 byte pointed to by C{byteIter.next()} 327 328 See C{wsp.Encoder.encodeUintvar()} for a detailed description of the 329 encoding scheme used for C{Uintvar} sequences. 330 331 @note: this function will move the iterator passed as C{byteIter} to 332 the last octet in the uintvar sequence; thus, after calling 333 this, that iterator's C{next()} function will return the first 334 byte B{after}the uintvar sequence. 335 336 @param byteIter: an iterator over a sequence of bytes 337 @type byteIter: iter 338 339 @return: the decoded unsigned integer 340 @rtype: int 341 """ 342 uint = 0 343 byte = byteIter.next() 344 while (byte >> 7) == 0x01: 345 uint = uint << 7 346 uint |= byte & 0x7f 347 byte = byteIter.next() 348 uint = uint << 7 349 uint |= byte & 0x7f 350 return uint
351 352 353 @staticmethod
354 - def decodeShortInteger(byteIter):
355 """ Decodes the short-integer value starting at the byte pointed to 356 by C{byteIter.next()}. 357 358 The encoding for a long integer is specified in [5], section 8.4.2.1: 359 C{Short-integer = OCTET 360 Integers in range 0-127 shall be encoded as a one octet value with 361 the most significant bit set to one (1xxx xxxx) and with the value 362 in the remaining least significant bits.} 363 364 @raise DecodeError: Not a valid short-integer; the most significant 365 isn't set to 1. 366 C{byteIter} will not be modified if this is raised 367 368 @return: The decoded short integer 369 @rtype: int 370 """ 371 byte = byteIter.preview() 372 if not byte & 0x80: 373 byteIter.resetPreview() 374 raise DecodeError, 'Not a valid short-integer: most significant bit not set' 375 byte = byteIter.next() 376 return byte & 0x7f
377 378 @staticmethod
379 - def decodeShortIntegerFromByte(byte):
380 """ Decodes the short-integer value contained in the specified byte 381 value 382 383 @param byte: the byte value to decode 384 @type byte: int 385 386 @raise DecodeError: Not a valid short-integer; the most significant 387 isn't set to 1. 388 @return: The decoded short integer 389 @rtype: int 390 """ 391 if not byte & 0x80: 392 raise DecodeError, 'Not a valid short-integer: most significant bit not set' 393 return byte & 0x7f
394 395 @staticmethod
396 - def decodeLongInteger(byteIter):
397 """ Decodes the long integer value starting at the byte pointed to 398 by C{byteIter.next()}. 399 400 The encoding for a long integer is specified in [5], section 8.4.2.1, 401 and follows the form:: 402 403 Long-integer = [Short-length] [Multi-octet-integer] 404 ^^^^^^ ^^^^^^^^^^^^^^^^^^^^^ 405 1 byte <Short-length> bytes 406 407 The Short-length indicates the length of the Multi-octet-integer. 408 409 @raise DecodeError: The byte pointed to by C{byteIter.next()} does 410 not indicate the start of a valid long-integer 411 sequence (short-length is invalid). If this is 412 raised, the iterator passed as C{byteIter} will 413 not be modified. 414 415 @note: If this function returns successfully, it will move the 416 iterator passed as C{byteIter} to the last octet in the encoded 417 long integer sequence; thus, after calling this, that 418 iterator's C{next()} function will return the first byte 419 B{after}the encoded long integer sequence. 420 421 @param byteIter: an iterator over a sequence of bytes 422 @type byteIter: iter 423 424 @return: The decoded long integer 425 @rtype: int 426 """ 427 try: 428 shortLength = Decoder.decodeShortLength(byteIter) 429 except DecodeError: 430 raise DecodeError, 'Not a valid long-integer: short-length byte is invalid' 431 longInt = 0 432 # Decode the Multi-octect-integer 433 for i in range(shortLength): 434 longInt = longInt << 8 435 longInt |= byteIter.next() 436 return longInt
437 438 @staticmethod
439 - def decodeTextString(byteIter):
440 """ Decodes the null-terminated, binary-encoded string value starting 441 at the byte pointed to by C{dataIter.next()}. 442 443 This follows the basic encoding rules specified in [5], section 444 8.4.2.1 445 446 @note: this function will move the iterator passed as C{byteIter} to 447 the last octet in the encoded string sequence; thus, after 448 calling this, that iterator's C{next()} function will return 449 the first byte B{after}the encoded string sequence. 450 451 @param byteIter: an iterator over a sequence of bytes 452 @type byteIter: iter 453 454 @return: The decoded text string 455 @rtype: str 456 """ 457 decodedString = '' 458 byte = byteIter.next() 459 # Remove Quote character (octet 127), if present 460 if byte == 127: 461 byte = byteIter.next() 462 while byte != 0x00: 463 decodedString += chr(byte) 464 byte = byteIter.next() 465 return decodedString
466 467 @staticmethod
468 - def decodeQuotedString(byteIter):
469 """ From [5], section 8.4.2.1: 470 Quoted-string = <Octet 34> *TEXT End-of-string 471 The TEXT encodes an RFC2616 Quoted-string with the enclosing 472 quotation-marks <"> removed 473 474 @return: The decoded text string 475 @rtype: str 476 """ 477 # byteIter, localIter = itertools.tee(byteIter) 478 # look for the quote character 479 byte = byteIter.preview() 480 if byte != 34: 481 byteIter.resetPreview() 482 raise DecodeError, 'Invalid quoted string; must start with <octect 34>' 483 else: 484 byteIter.next() 485 #CHECK: should the quotation chars be pre- and appended before returning/ 486 # *technically* we should not check for quote characters. oh well. 487 return Decoder.decodeTextString(byteIter)
488 489 490 @staticmethod
491 - def decodeTokenText(byteIter):
492 """ From [5], section 8.4.2.1: 493 Token-text = Token End-of-string 494 495 @raise DecodeError: invalid token; in this case, byteIter is not modified 496 497 @return: The token string if successful, or the byte that was read if not 498 @rtype: str or int 499 """ 500 separators = (11, 32, 40, 41, 44, 47, 58, 59, 60, 61, 62, 63, 64, 91, 501 92, 93, 123, 125) 502 token = '' 503 # byteIter, localIter = itertools.tee(byteIter) 504 # byte = localIter.next() 505 byte = byteIter.preview() 506 if byte <= 31 or byte in separators: 507 byteIter.resetPreview() 508 raise DecodeError, 'Invalid token' 509 byte = byteIter.next() 510 while byte > 31 and byte not in separators: 511 token += chr(byte) 512 byte = byteIter.next() 513 return token
514 515 @staticmethod
516 - def decodeExtensionMedia(byteIter):
517 """ From [5], section 8.4.2.1: 518 Extension-media = *TEXT End-of-string 519 This encoding is used for media values, which have no well-known 520 binary encoding 521 522 @raise DecodeError: The TEXT started with an invalid character. 523 C{byteIter} is not modified if this happens. 524 525 @return: The decoded media type value 526 @rtype: str 527 """ 528 mediaValue = '' 529 # byteIter, localIter = itertools.tee(byteIter) 530 # byte = localIter.next() 531 byte = byteIter.preview() 532 if byte < 32 or byte == 127: 533 byteIter.resetPreview() 534 raise DecodeError, 'Invalid Extension-media: TEXT starts with invalid character: %d' % byte 535 byte = byteIter.next() 536 while byte != 0x00: 537 mediaValue += chr(byte) 538 byte = byteIter.next() 539 return mediaValue
540 541 542 @staticmethod
543 - def decodeConstrainedEncoding(byteIter):
544 """ Constrained-encoding = Extension-Media --or-- Short-integer 545 This encoding is used for token values, which have no well-known 546 binary encoding, or when the assigned number of the well-known 547 encoding is small enough to fit into Short-integer. 548 549 @return: The decoding constrained-encoding token value 550 @rtype: str or int 551 """ 552 result = None 553 #backupIter, localIter = itertools.tee(byteIter) 554 try: 555 #byteIter, localIter = itertools.tee(byteIter) 556 # First try and see if this is just a short-integer 557 result = Decoder.decodeShortInteger(byteIter) 558 #byteIter = localIter 559 except DecodeError, msg: 560 # Ok, it should be Extension-Media then 561 try: 562 #backupIter, localIter = itertools.tee(byteIter) 563 result = Decoder.decodeExtensionMedia(byteIter) 564 except DecodeError, msg: 565 # Give up 566 #fakeByte =localIter.next() 567 #fakeByte= localIter.next() 568 #fakeByte = localIter.next() 569 #byte = byteIter.next() 570 #byte = byteIter.next() 571 raise DecodeError, 'Not a valid Constrained-encoding sequence' 572 #byteIter = localIter 573 return result
574 575 @staticmethod
576 - def decodeShortLength(byteIter):
577 """ From [5], section 8.4.2.2: 578 Short-length = <Any octet 0-30> 579 580 @raise DecodeError: The byte is not a valid short-length value; 581 it is not in octet range 0-30. In this case, the 582 iterator passed as C{byteIter} is not modified. 583 584 @note: If this function returns successfully, the iterator passed as 585 C{byteIter} is moved one byte forward. 586 587 @return: The decoded short-length 588 @rtype: int 589 """ 590 # byteIter, localIter = itertools.tee(byteIter) 591 # Make sure it's a valid short-length 592 # byte = localIter.next() 593 byte = byteIter.preview() 594 if byte > 30: 595 byteIter.resetPreview() 596 raise DecodeError, 'Not a valid short-length; should be in octet range 0-30' 597 else: 598 return byteIter.next()
599 600 @staticmethod
601 - def decodeValueLength(byteIter):
602 """ Decodes the value length indicator starting at the byte pointed to 603 by C{byteIter.next()}. 604 605 "Value length" is used to indicate the length of a value to follow, as 606 used in the C{Content-Type} header in the MMS body, for example. 607 608 The encoding for a value length indicator is specified in [5], 609 section 8.4.2.2, and follows the form:: 610 611 Value-length = [Short-length] --or-- [Length-quote] [Length] 612 ^^^^^^ ^^^^^^ ^^^^^^ 613 1 byte 1 byte x bytes 614 <Any octet 0-30> <Octet 31> Uintvar-integer 615 616 @raise DecodeError: The ValueLength could not be decoded. If this 617 happens, C{byteIter} is not modified. 618 619 @return: The decoded value length indicator 620 @rtype: int 621 """ 622 lengthValue = 0 623 # Check for short-length 624 try: 625 lengthValue = Decoder.decodeShortLength(byteIter) 626 except DecodeError: 627 byte = byteIter.preview() 628 #CHECK: this strictness MAY cause issues, but it is correct 629 if byte == 31: 630 byteIter.next() # skip past the length-quote 631 lengthValue = Decoder.decodeUintvar(byteIter) 632 else: 633 byteIter.resetPreview() 634 raise DecodeError, 'Invalid Value-length: not short-length, and no length-quote present' 635 return lengthValue
636 637 @staticmethod
638 - def decodeIntegerValue(byteIter):
639 """ From [5], section 8.4.2.3: 640 Integer-Value = Short-integer | Long-integer 641 642 @raise DecodeError: The sequence of bytes starting at 643 C{byteIter.next()} does not contain a valid 644 integervalue. If this is raised, the iterator 645 passed as C{byteIter} is not modified. 646 647 @note: If successful, this function will move the iterator passed as 648 C{byteIter} to the last octet in the integer value sequence; 649 thus, after calling this, that iterator's C{next()} function 650 will return the first byte B{after}the integer value sequence. 651 652 @return: The decoded integer value 653 @rtype: int 654 """ 655 integer = 0 656 # First try and see if it's a short-integer 657 try: 658 integer = Decoder.decodeShortInteger(byteIter) 659 except DecodeError: 660 try: 661 integer = Decoder.decodeLongInteger(byteIter) 662 except DecodeError: 663 raise DecodeError, 'Not a valid integer value' 664 return integer
665 666 @staticmethod
667 - def decodeContentTypeValue(byteIter):
668 """ Decodes an encoded content type value. 669 670 From [5], section 8.4.2.24: 671 C{Content-type-value = Constrained-media | Content-general-form} 672 673 The short form of the Content-type-value MUST only be used when the 674 well-known media is in the range of 0-127 or a text string. In all 675 other cases the general form MUST be used. 676 677 @return: The media type (content type), and a dictionary of 678 parameters to this content type (which is empty if there 679 are no parameters). This parameter dictionary is in the 680 format: 681 C{{<str:parameter_name>: <str/int/float:parameter_value>}}. 682 The final returned tuple is in the format: 683 (<str:media_type>, <dict:parameter_dict>) 684 @rtype: tuple 685 """ 686 # First try do decode it as Constrained-media 687 contentType = '' 688 parameters = {} 689 try: 690 contentType = Decoder.decodeConstrainedMedia(byteIter) 691 except DecodeError: 692 # Try the general form 693 contentType, parameters = Decoder.decodeContentGeneralForm(byteIter) 694 return (contentType, parameters)
695 696 697 @staticmethod
698 - def decodeWellKnownMedia(byteIter):
699 """ From [5], section 8.4.2.7: 700 Well-known-media = Integer-value 701 It is encoded using values from the "Content Type Assignments" table 702 (see [5], table 40). 703 704 @param byteIter: an iterator over a sequence of bytes 705 @type byteIter: iter 706 707 @raise DecodeError: This is raised if the integer value representing 708 the well-known media type cannot be decoded 709 correctly, or the well-known media type value 710 could not be found in the table of assigned 711 content types. 712 If this exception is raised, the iterator passed 713 as C{byteIter} is not modified. 714 715 @note: If successful, this function will move the iterator passed as 716 C{byteIter} to the last octet in the content type value 717 sequence; thus, after calling this, that iterator's C{next()} 718 function will return the first byte B{after}the content type 719 value sequence. 720 721 @return: the decoded MIME content type name 722 @rtype: str 723 """ 724 # byteIter, localIter = itertools.tee(byteIter) 725 try: 726 # wkContentTypeValue = Decoder.decodeIntegerValue(localIter) 727 wkContentTypeValue = Decoder.decodeIntegerValue(byteIter) 728 except DecodeError: 729 raise DecodeError, 'Invalid well-known media: could not read integer value representing it' 730 731 if wkContentTypeValue in range(len(WSPEncodingAssignments.wkContentTypes)): 732 decodedContentType = WSPEncodingAssignments.wkContentTypes[wkContentTypeValue] 733 # # Only iterate the main iterator now that everything is ok 734 # byteIter.next() 735 else: 736 raise DecodeError, 'Invalid well-known media: could not find content type in table of assigned values' 737 return decodedContentType
738 739 740 @staticmethod
741 - def decodeMediaType(byteIter):
742 """ From [5], section 8.2.4.24: 743 Media-type = (Well-known-media | Extension-Media) *(Parameter) 744 745 @param byteIter: an iterator over a sequence of bytes 746 @type byteIter: iter 747 748 @note: Used by C{decodeContentGeneralForm()} 749 750 @return: The decoded media type 751 @rtype: str 752 """ 753 try: 754 mediaType = Decoder.decodeWellKnownMedia(byteIter) 755 except DecodeError: 756 mediaType = Decoder.decodeExtensionMedia(byteIter) 757 return mediaType
758 759 @staticmethod
760 - def decodeConstrainedMedia(byteIter):
761 """ From [5], section 8.4.2.7: 762 Constrained-media = Constrained-encoding 763 It is encoded using values from the "Content Type Assignments" table. 764 765 @raise DecodeError: Invalid constrained media sequence 766 767 @return: The decoded media type 768 @rtype: str 769 """ 770 constrainedMedia = '' 771 try: 772 constrainedMediaValue = Decoder.decodeConstrainedEncoding(byteIter) 773 except DecodeError, msg: 774 #byte = byteIter.next() 775 raise DecodeError, 'Invalid Constrained-media: %s' % msg 776 if type(constrainedMediaValue) == int: 777 if constrainedMediaValue in range(len(WSPEncodingAssignments.wkContentTypes)): 778 constrainedMedia = WSPEncodingAssignments.wkContentTypes[constrainedMediaValue] 779 else: 780 raise DecodeError, 'Invalid constrained media: could not find well-known content type' 781 else: 782 constrainedMedia = constrainedMediaValue 783 return constrainedMedia
784 785 @staticmethod
786 - def decodeContentGeneralForm(byteIter):
787 """ From [5], section 8.4.2.24: 788 Content-general-form = Value-length Media-type 789 790 @note: Used in decoding Content-type fields and their parameters; 791 see C{decodeContentTypeValue} 792 793 @note: Used by C{decodeContentTypeValue()} 794 795 @return: The media type (content type), and a dictionary of 796 parameters to this content type (which is empty if there 797 are no parameters). This parameter dictionary is in the 798 format: 799 C{{<str:parameter_name>: <str/int/float:parameter_value>}}. 800 The final returned tuple is in the format: 801 (<str:media_type>, <dict:parameter_dict>) 802 @rtype: tuple 803 """ 804 # This is the length of the (encoded) media-type and all parameters 805 #try: 806 valueLength = Decoder.decodeValueLength(byteIter) 807 #except DecodeError: 808 #CHECK: this is being very leniet, based on real-world tests (specs don't mention this): 809 # valueLength = Decoder.decodeIntegerValue(byteIter) 810 811 # Read parameters, etc, until <valueLength> is reached 812 ctFieldBytes = array.array('B') 813 for i in range(valueLength): 814 ctFieldBytes.append(byteIter.next()) 815 # contentTypeIter = iter(ctFieldBytes) 816 ctIter = PreviewIterator(ctFieldBytes) 817 # Now, decode all the bytes read 818 mediaType = Decoder.decodeMediaType(ctIter) 819 # Decode the included paramaters (if any) 820 parameters = {} 821 while True: 822 try: 823 parameter, value = Decoder.decodeParameter(ctIter) 824 parameters[parameter] = value 825 except StopIteration: 826 break 827 return (mediaType, parameters)
828 829 @staticmethod
830 - def decodeParameter(byteIter):
831 """ From [5], section 8.4.2.4: 832 Parameter = Typed-parameter | Untyped-parameter 833 834 @return: The name of the parameter, and its value, in the format: 835 (<parameter name>, <parameter value>) 836 @rtype: tuple 837 """ 838 try: 839 parameter, value = Decoder.decodeTypedParameter(byteIter) 840 except DecodeError: 841 parameter, value = Decoder.decodeUntypedParameter(byteIter) 842 return (parameter, value)
843 844 @staticmethod
845 - def decodeTypedParameter(byteIter):
846 """ From [5], section 8.4.2.4: 847 C{Typed-parameter = Well-known-parameter-token Typed-value} 848 The actual expected type of the value is implied by the well-known 849 parameter. 850 851 @note: This is used in decoding parameters; see C{decodeParameter} 852 853 @return: The name of the parameter, and its value, in the format: 854 (<parameter name>, <parameter value>) 855 @rtype: tuple 856 """ 857 parameterToken, expectedValueType = Decoder.decodeWellKnownParameter(byteIter) 858 typedValue = '' 859 try: 860 # Split the iterator; sometimes the exec call seems to mess up with itertools if this not done here 861 # (to replicate: trace the program from here to decodeShortInteger(); the itertools.tee command there 862 # doesn't copy the iterator as it should - it creates pointers to the same memory) 863 #byteIter, execIter = itertools.tee(byteIter) 864 exec 'typedValue = Decoder.decode%s(byteIter)' % expectedValueType 865 except DecodeError, msg: 866 raise DecodeError, 'Could not decode Typed-parameter: %s' % msg 867 except: 868 print 'A fatal error occurred, probably due to an unimplemented decoding operation' 869 raise 870 return (parameterToken, typedValue)
871 872 @staticmethod
873 - def decodeUntypedParameter(byteIter):
874 """ From [5], section 8.4.2.4: 875 C{Untyped-parameter = Token-text Untyped-value} 876 The type of the value is unknown, but it shall be encoded as an 877 integer, if that is possible. 878 879 @note: This is used in decoding parameters; see C{decodeParameter} 880 881 @return: The name of the parameter, and its value, in the format: 882 (<parameter name>, <parameter value>) 883 @rtype: tuple 884 """ 885 parameterToken = Decoder.decodeTokenText(byteIter) 886 parameterValue = Decoder.decodeUntypedValue(byteIter) 887 return (parameterToken, parameterValue)
888 889 @staticmethod
890 - def decodeUntypedValue(byteIter):
891 """ From [5], section 8.4.2.4: 892 Untyped-value = Integer-value | Text-value 893 894 @note: This is used in decoding parameter values; see 895 C{decodeUntypedParameter} 896 @return: The decoded untyped-value 897 @rtype: int or str 898 """ 899 try: 900 value = Decoder.decodeIntegerValue(byteIter) 901 except DecodeError: 902 value = Decoder.decodeTextValue(byteIter) 903 return value
904 905 @staticmethod
906 - def decodeWellKnownParameter(byteIter, encodingVersion='1.2'):
907 """ Decodes the name and expected value type of a parameter of (for 908 example) a "Content-Type" header entry, taking into account the WSP 909 short form (assigned numbers) of well-known parameter names, as 910 specified in section 8.4.2.4 and table 38 of [5]. 911 912 From [5], section 8.4.2.4: 913 Well-known-parameter-token = Integer-value 914 The code values used for parameters are specified in [5], table 38 915 916 @raise ValueError: The specified encoding version is invalid. 917 918 @raise DecodeError: This is raised if the integer value representing 919 the well-known parameter name cannot be decoded 920 correctly, or the well-known paramter token value 921 could not be found in the table of assigned 922 content types. 923 If this exception is raised, the iterator passed 924 as C{byteIter} is not modified. 925 926 @param encodingVersion: The WSP encoding version to use. This defaults 927 to "1.2", but may be "1.1", "1.2", "1.3" or 928 "1.4" (see table 39 in [5] for details). 929 @type encodingVersion: str 930 931 @return: the decoded parameter name, and its expected value type, in 932 the format (<parameter name>, <expected type>) 933 @rtype: tuple 934 """ 935 decodedParameterName = '' 936 expectedValue = '' 937 # byteIter, localIter = itertools.tee(byteIter) 938 try: 939 # wkParameterValue = Decoder.decodeIntegerValue(localIter) 940 wkParameterValue = Decoder.decodeIntegerValue(byteIter) 941 except DecodeError: 942 raise DecodeError, 'Invalid well-known parameter token: could not read integer value representing it' 943 944 wkParameters = WSPEncodingAssignments.wellKnownParameters(encodingVersion) 945 if wkParameterValue in wkParameters: 946 decodedParameterName, expectedValue = wkParameters[wkParameterValue] 947 # Only iterate the main iterator now that everything is ok 948 # byteIter.next() 949 else: 950 #If this is reached, the parameter isn't a WSP well-known one 951 raise DecodeError, 'Invalid well-known parameter token: could not find in table of assigned numbers (encoding version %s)' % encodingVersion 952 return (decodedParameterName, expectedValue)
953 954 #TODO: somehow this should be more dynamic; we need to know what type is EXPECTED (hence the TYPED value) 955 @staticmethod
956 - def decodeTypedValue(byteIter):
957 """ From [5], section 8.4.2.4: 958 Typed-value = Compact-value | Text-value 959 In addition to the expected type, there may be no value. 960 If the value cannot be encoded using the expected type, it shall be 961 encoded as text. 962 963 @note: This is used in decoding parameters, see C{decodeParameter()} 964 965 @return: The decoded Parameter Typed-value 966 @rtype: str 967 """ 968 typedValue = '' 969 try: 970 typedValue = Decoder.decodeCompactValue(byteIter) 971 except DecodeError: 972 try: 973 typedValue = Decoder.decodeTextValue(byteIter) 974 except DecodeError: 975 raise DecodeError, 'Could not decode the Parameter Typed-value' 976 return typedValue
977 978 #TODO: somehow this should be more dynamic; we need to know what type is EXPECTED 979 @staticmethod
980 - def decodeCompactValue(byteIter):
981 """ From [5], section 8.4.2.4: 982 Compact-value = Integer-value | Date-value | Delta-seconds-value 983 | Q-value | Version-value | Uri-value 984 985 @raise DecodeError: Failed to decode the Parameter Compact-value; 986 if this happens, C{byteIter} is unmodified 987 988 @note: This is used in decoding parameters, see C{decodeTypeValue()} 989 990 @return: The decoded Compact-value (this is specific to the 991 parameter type 992 @rtype: str or int 993 """ 994 compactValue = None 995 try: 996 # First, see if it's an integer value 997 # This solves the checks for: Integer-value, Date-value, Delta-seconds-value, Q-value, Version-value 998 compactValue = Decoder.decodeIntegerValue(byteIter) 999 except DecodeError: 1000 try: 1001 # Try parsing it as a Uri-value 1002 compactValue = Decoder.decodeUriValue(byteIter) 1003 except DecodeError: 1004 raise DecodeError, 'Could not decode Parameter Compact-value' 1005 return compactValue
1006 1007 #TODO: the string output from this should be in the MMS format..? 1008 @staticmethod
1009 - def decodeDateValue(byteIter):
1010 """ From [5], section 8.4.2.3: 1011 Date-value = Long-integer 1012 The encoding of dates shall be done in number of seconds from 1013 1970-01-01, 00:00:00 GMT. 1014 1015 @raise DecodeError: This method uses C{decodeLongInteger}, and thus 1016 raises this under the same conditions. 1017 1018 @return: The date, in a format such as: C{Tue Nov 27 16:12:21 2007} 1019 @rtype: str 1020 """ 1021 import time 1022 return time.ctime(Decoder.decodeLongInteger(byteIter))
1023 1024 @staticmethod
1025 - def decodeDeltaSecondsValue(byteIter):
1026 """ From [5], section 8.4.2.3: 1027 Delta-seconds-value = Integer-value 1028 @raise DecodeError: This method uses C{decodeIntegerValue}, and thus 1029 raises this under the same conditions. 1030 @return: the decoded delta-seconds-value 1031 @rtype: int 1032 """ 1033 return Decoder.decodeIntegerValue(byteIter)
1034 1035 @staticmethod
1036 - def decodeQValue(byteIter):
1037 """ From [5], section 8.4.2.1: 1038 The encoding is the same as in Uintvar-integer, but with restricted 1039 size. When quality factor 0 and quality factors with one or two 1040 decimal digits are encoded, they shall be multiplied by 100 and 1041 incremented by one, so that they encode as a one-octet value in 1042 range 1-100, ie, 0.1 is encoded as 11 (0x0B) and 0.99 encoded as 1043 100 (0x64). Three decimal quality factors shall be multiplied with 1044 1000 and incremented by 100, and the result shall be encoded as a 1045 one-octet or two-octet uintvar, eg, 0.333 shall be encoded as 0x83 0x31. 1046 Quality factor 1 is the default value and shall never be sent. 1047 1048 @return: The decode quality factor (Q-value) 1049 @rtype: float 1050 """ 1051 qValue = 0.0 1052 qValueInt = Decoder.decodeUintvar(byteIter) 1053 #TODO: limit the amount of decimal points 1054 if qValueInt > 100: 1055 qValue = float(qValueInt - 100) / 1000.0 1056 else: 1057 qValue = float(qValueInt - 1) / 100.0 1058 return qValue
1059 1060 1061 @staticmethod
1062 - def decodeVersionValue(byteIter):
1063 """ Decodes the version-value. From [5], section 8.4.2.3: 1064 Version-value = Short-integer | Text-string 1065 1066 @return: the decoded version value in the format, usually in the 1067 format: "<major_version>.<minor_version>" 1068 @rtype: str 1069 """ 1070 version = '' 1071 try: 1072 byteValue = Decoder.decodeShortInteger(byteIter) 1073 major = (byteValue & 0x70) >> 4 1074 minor = byteValue & 0x0f 1075 version = '%d.%d' % (major, minor) 1076 except DecodeError: 1077 version = Decoder.decodeTextString(byteIter) 1078 return version
1079 1080 @staticmethod
1081 - def decodeUriValue(byteIter):
1082 """ Stub for Uri-value decoding; this is a wrapper to C{decodeTextString} """ 1083 return Decoder.decodeTextString(byteIter)
1084 1085 @staticmethod
1086 - def decodeTextValue(byteIter):
1087 """ Stub for Parameter Text-value decoding. 1088 From [5], section 8.4.2.3: 1089 Text-value = No-value | Token-text | Quoted-string 1090 1091 This is used when decoding parameter values; see C{decodeTypedValue()} 1092 1093 @return: The decoded Parameter Text-value 1094 @rtype: str 1095 """ 1096 textValue = '' 1097 try: 1098 textValue = Decoder.decodeTokenText(byteIter) 1099 except DecodeError: 1100 try: 1101 textValue = Decoder.decodeQuotedString(byteIter) 1102 except DecodeError: 1103 # Ok, so it's a "No-value" 1104 pass 1105 return textValue
1106 1107 @staticmethod
1108 - def decodeNoValue(byteIter):
1109 """ Basically verifies that the byte pointed to by C{byteIter.next()} 1110 is 0x00. 1111 1112 @note: If successful, this function will move C{byteIter} one byte 1113 forward. 1114 1115 @raise DecodeError: If 0x00 is not found; C{byteIter} is not modified 1116 if this is raised. 1117 1118 @return: No-value, which is 0x00 1119 @rtype: int 1120 """ 1121 byteIter, localIter = byteIter.next() 1122 if localIter.next() != 0x00: 1123 raise DecodeError, 'Expected No-value' 1124 else: 1125 byteIter.next() 1126 return 0x00
1127 1128 @staticmethod
1129 - def decodeAcceptValue(byteIter):
1130 """ From [5], section 8.4.2.7: 1131 Accept-value = Constrained-media | Accept-general-form 1132 Accept-general-form = Value-length Media-range [Accept-parameters] 1133 Media-range = (Well-known-media | Extension-Media) *(Parameter) 1134 Accept-parameters = Q-token Q-value *(Accept-extension) 1135 Accept-extension = Parameter 1136 Q-token = <Octet 128> 1137 1138 @note: most of these things are currently decoded, but discarded (e.g 1139 accept-parameters); we only return the media type 1140 1141 @raise DecodeError: The decoding failed. C{byteIter} will not be 1142 modified in this case. 1143 @return: the decoded Accept-value (media/content type) 1144 @rtype: str 1145 """ 1146 acceptValue = '' 1147 # Try to use Constrained-media encoding 1148 try: 1149 acceptValue = Decoder.decodeConstrainedMedia(byteIter) 1150 except DecodeError: 1151 # ...now try Accept-general-form 1152 valueLength = Decoder.decodeValueLength(byteIter) 1153 try: 1154 media = Decoder.decodeWellKnownMedia(byteIter) 1155 except DecodeError: 1156 media = Decoder.decodeExtensionMedia(byteIter) 1157 # Check for the Q-Token (to see if there are Accept-parameters) 1158 if byteIter.preview() == 128: 1159 byteIter.next() 1160 qValue = Decoder.decodeQValue(byteIter) 1161 try: 1162 acceptExtension = Decoder.decodeParameter(byteIter) 1163 except DecodeError: 1164 # Just set an empty iterable 1165 acceptExtension = [] 1166 byteIter.resetPreview() 1167 acceptValue = media 1168 return acceptValue
1169 1170 @staticmethod
1171 - def decodePragmaValue(byteIter):
1172 """ Defined in [5], section 8.4.2.38: 1173 Pragma-value = No-cache | (Value-length Parameter) 1174 1175 From [5], section 8.4.2.15: 1176 No-cache = <Octet 128> 1177 1178 @raise DecodeError: The decoding failed. C{byteIter} will not be 1179 modified in this case. 1180 @return: the decoded Pragma-value, in the format: 1181 (<parameter name>, <parameter value>) 1182 @rtype: tuple 1183 """ 1184 byte = byteIter.preview() 1185 if byte == 0x80: # No-cache 1186 byteIter.next() 1187 #TODO: Not sure if this parameter name (or even usage) is correct 1188 parameterName = 'Cache-control' 1189 parameterValue = 'No-cache' 1190 else: 1191 byteIter.resetPreview() 1192 valueLength = Decoder.decodeValueLength(byteIter) 1193 parameterName, parameterValue = Decoder.decodeParameter(byteIter) 1194 return parameterName, parameterValue
1195 1196 @staticmethod
1197 - def decodeWellKnownCharset(byteIter):
1198 """ From [5], section 8.4.2.8: 1199 C{Well-known-charset = Any-charset | Integer-value} 1200 It is encoded using values from "Character Set Assignments" table. 1201 C{Any-charset = <Octet 128>} 1202 Equivalent to the special RFC2616 charset value "*" 1203 """ 1204 decodedCharSet = '' 1205 # Look for the Any-charset value 1206 byte = byteIter.preview() 1207 byteIter.resetPreview() 1208 if byte == 127: 1209 byteIter.next() 1210 decodcedCharSet = '*' 1211 else: 1212 charSetValue = Decoder.decodeIntegerValue(byteIter) 1213 if charSetValue in WSPEncodingAssignments.wkCharSets: 1214 decodedCharSet = WSPEncodingAssignments.wkCharSets[charSetValue] 1215 else: 1216 # This charset is not in our table... so just use the value (at least for now) 1217 decodedCharSet = str(charSetValue) 1218 return decodedCharSet
1219 1220 @staticmethod
1221 - def decodeWellKnownHeader(byteIter):
1222 """ From [5], section 8.4.2.6: 1223 C{Well-known-header = Well-known-field-name Wap-value} 1224 C{Well-known-field-name = Short-integer} 1225 C{Wap-value = <many different headers value, most not implemented>} 1226 1227 @todo: Currently, "Wap-value" is decoded as a Text-string in most cases 1228 1229 @return: The header name, and its value, in the format: 1230 (<str:header_name>, <str:header_value>) 1231 @rtype: tuple 1232 """ 1233 decodedHeaderFieldName = '' 1234 hdrFieldValue = Decoder.decodeShortInteger(byteIter) 1235 hdrFields = WSPEncodingAssignments.headerFieldNames() 1236 #TODO: *technically* this can fail, but then we have already read a byte... should fix? 1237 if hdrFieldValue in range(len(hdrFields)): 1238 decodedHeaderFieldName = hdrFields[hdrFieldValue] 1239 else: 1240 raise DecodeError, 'Invalid Header Field value: %d' % hdrFieldValue 1241 #TODO: make this flow better, and implement it in decodeApplicationHeader also 1242 # Currently we decode most headers as TextStrings, except where we have a specific decoding algorithm implemented 1243 if decodedHeaderFieldName in WSPEncodingAssignments.hdrFieldEncodings: 1244 wapValueType = WSPEncodingAssignments.hdrFieldEncodings[decodedHeaderFieldName] 1245 try: 1246 exec 'decodedValue = Decoder.decode%s(byteIter)' % wapValueType 1247 except DecodeError, msg: 1248 raise DecodeError, 'Could not decode Wap-value: %s' % msg 1249 except: 1250 print 'An error occurred, probably due to an unimplemented decoding operation. Tried to decode header: %s' % decodedHeaderFieldName 1251 raise 1252 else: 1253 decodedValue = Decoder.decodeTextString(byteIter) 1254 return (decodedHeaderFieldName, decodedValue)
1255 1256 @staticmethod
1257 - def decodeApplicationHeader(byteIter):
1258 """ From [5], section 8.4.2.6: 1259 C{Application-header = Token-text Application-specific-value} 1260 1261 From [4], section 7.1: 1262 C{Application-header = Token-text Application-specific-value} 1263 C{Application-specific-value = Text-string} 1264 1265 @note: This is used when decoding generic WSP headers; 1266 see C{decodeHeader()}. 1267 @note: We follow [4], and decode the "Application-specific-value" 1268 as a Text-string 1269 1270 @return: The application-header, and its value, in the format: 1271 (<str:application_header>, <str:application_specific_value>) 1272 @rtype: tuple 1273 """ 1274 try: 1275 appHeader = Decoder.decodeTokenText(byteIter) 1276 #FNA: added for brute-forcing 1277 except DecodeError: 1278 appHeader = Decoder.decodeTextString(byteIter) 1279 appSpecificValue = Decoder.decodeTextString(byteIter) 1280 return (appHeader, appSpecificValue)
1281 1282 @staticmethod
1283 - def decodeHeader(byteIter):
1284 """ Decodes a WSP header entry 1285 1286 From [5], section 8.4.2.6: 1287 C{Header = Message-header | Shift-sequence} 1288 C{Message-header = Well-known-header | Application-header} 1289 C{Well-known-header = Well-known-field-name Wap-value} 1290 C{Application-header = Token-text Application-specific-value} 1291 1292 @note: "Shift-sequence" encoding has not been implemented 1293 @note: Currently, almost all header values are treated as text-strings 1294 1295 @return: The decoded headername, and its value, in the format: 1296 (<str:header_name>, <str:header_value>) 1297 @rtype: tuple 1298 """ 1299 header = '' 1300 value = '' 1301 # First try decoding the header as a well-known-header 1302 try: 1303 header, value = Decoder.decodeWellKnownHeader(byteIter) 1304 except DecodeError: 1305 # ...now try Application-header encoding 1306 header, value = Decoder.decodeApplicationHeader(byteIter) 1307 return (header, value)
1308 1309
1310 -class Encoder:
1311 """ A WSP Data unit decoder """ 1312 1313 #@staticmethod 1314 #def encodeUint8(uint): 1315 # """ Encodes an 8-bit unsigned integer 1316 # 1317 # @param uint: The integer to encode 1318 # @type byteIter: int 1319 # 1320 # @return: the encoded Uint8, as a sequence of bytes 1321 # @rtype: list 1322 # """ 1323 # # Make the byte unsigned 1324 # return [uint & 0xff] 1325 1326 1327 @staticmethod
1328 - def encodeUintvar(uint):
1329 """ Variable Length Unsigned Integer encoding algorithm 1330 1331 This binary-encodes the given unsigned integer number as specified 1332 in section 8.1.2 of [5]. Basically, each encoded byte has the 1333 following structure:: 1334 1335 [0][ Payload ] 1336 | ^^^^^^^ 1337 | 7 bits (actual data) 1338 | 1339 Continue bit 1340 1341 The uint is split into 7-bit segments, and the "continue bit" of each 1342 used octet is set to '1' to indicate more is to follow; the last used 1343 octet's "continue bit" is set to 0. 1344 1345 @return: the binary-encoded Uintvar, as a list of byte values 1346 @rtype: list 1347 """ 1348 uintVar = [] 1349 # Since this is the lowest entry, we do not set the continue bit to 1 1350 uintVar.append(uint & 0x7f) 1351 uint = uint >> 7 1352 # ...but for the remaining octets, we have to 1353 while uint > 0: 1354 uintVar.insert(0, 0x80 | (uint & 0x7f)) 1355 uint = uint >> 7 1356 return uintVar
1357 1358 @staticmethod
1359 - def encodeTextString(string):
1360 """ Encodes a "Text-string" value. 1361 1362 This follows the basic encoding rules specified in [5], section 1363 8.4.2.1 1364 1365 @param string: The text string to encode 1366 @type string: str 1367 1368 @return: the null-terminated, binary-encoded version of the 1369 specified Text-string, as a list of byte values 1370 @rtype: list 1371 """ 1372 encodedString = [] 1373 for char in string: 1374 encodedString.append(ord(char)) 1375 encodedString.append(0x00) 1376 return encodedString
1377 1378 @staticmethod
1379 - def encodeShortInteger(integer):
1380 """ Encodes the specified short-integer value 1381 1382 The encoding for a long integer is specified in [5], section 8.4.2.1: 1383 C{Short-integer = OCTET} 1384 Integers in range 0-127 shall be encoded as a one octet value with 1385 the most significant bit set to one (1xxx xxxx) and with the value 1386 in the remaining least significant bits. 1387 1388 @param integer: The short-integer value to encode 1389 @type integer: int 1390 1391 @raise EncodeError: Not a valid short-integer; the integer must be in 1392 the range of 0-127 1393 1394 @return: The encoded short integer, as a list of byte values 1395 @rtype: list 1396 """ 1397 if integer < 0 or integer > 127: 1398 raise EncodeError, 'Short-integer value must be in range 0-127: %d' % integer 1399 encodedInteger = [] 1400 # Make sure the most significant bit is set 1401 byte = 0x80 | integer 1402 encodedInteger.append(byte) 1403 return encodedInteger
1404 1405 @staticmethod
1406 - def encodeLongInteger(integer):
1407 """ Encodes a Long-integer value 1408 1409 The encoding for a long integer is specified in [5], section 8.4.2.1; 1410 for a description of this encoding scheme, see 1411 C{wsp.Decoder.decodeLongIntger()}. 1412 1413 Basically: 1414 From [5], section 8.4.2.2: 1415 Long-integer = Short-length Multi-octet-integer 1416 Short-length = <Any octet 0-30> 1417 1418 @raise EncodeError: <integer> is not of type "int" 1419 1420 @param integer: The integer value to encode 1421 @type integer: int 1422 1423 @return: The encoded Long-integer, as a sequence of byte values 1424 @rtype: list 1425 """ 1426 if type(integer) != int: 1427 raise EncodeError, '<integer> must be of type "int"' 1428 encodedLongInt = [] 1429 longInt = integer 1430 # Encode the Multi-octect-integer 1431 while longInt > 0: 1432 byte = 0xff & longInt 1433 encodedLongInt.append(byte) 1434 longInt = longInt >> 8 1435 # Now add the SHort-length value, and make sure it's ok 1436 shortLength = len(encodedLongInt) 1437 if shortLength > 30: 1438 raise EncodeError, 'Cannot encode Long-integer value: Short-length is too long; should be in octet range 0-30' 1439 encodedLongInt.insert(0, shortLength) 1440 return encodedLongInt
1441 1442 @staticmethod
1443 - def encodeVersionValue(version):
1444 """ Encodes the version-value. From [5], section 8.4.2.3: 1445 Version-value = Short-integer | Text-string 1446 1447 Example: An MMS version of "1.0" consists of a major version of 1 and a 1448 minor version of 0, and would be encoded as 0x90. However, a version 1449 of "1.2.4" would be encoded as the Text-string "1.2.4". 1450 1451 @param version: The version number to encode, e.g. "1.0" 1452 @type version: str 1453 1454 @raise TypeError: The specified version value was not of type C{str} 1455 1456 @return: the encoded version value, as a list of byte values 1457 @rtype: list 1458 """ 1459 if type(version) != str: 1460 raise TypeError, 'Parameter must be of type "str"' 1461 encodedVersionValue = [] 1462 # First try short-integer encoding 1463 try: 1464 if len(version.split('.')) <= 2: 1465 majorVersion = int(version.split('.')[0]) 1466 if majorVersion < 1 or majorVersion > 7: 1467 raise ValueError, 'Major version must be in range 1-7' 1468 major = majorVersion << 4 1469 if len(version.split('.')) == 2: 1470 minorVersion = int(version.split('.')[1]) 1471 if minorVersion < 0 or minorVersion > 14: 1472 raise ValueError, 'Minor version must be in range 0-14' 1473 else: 1474 minorVersion = 15 1475 minor = minorVersion 1476 encodedVersionValue = Encoder.encodeShortInteger(major|minor) 1477 except: 1478 # The value couldn't be encoded as a short-integer; use a text-string instead 1479 encodedVersionValue = Encoder.encodeTextString(version) 1480 return encodedVersionValue
1481 1482 @staticmethod
1483 - def encodeMediaType(contentType):
1484 """ Encodes the specified MIME content type ("Media-type" value) 1485 1486 From [5], section 8.2.4.24: 1487 Media-type = (Well-known-media | Extension-Media) *(Parameter) 1488 1489 "Well-known-media" takes into account the WSP short form of well-known 1490 content types, as specified in section 8.4.2.24 and table 40 of [5]. 1491 1492 @param contentType: The MIME content type to encode 1493 @type contentType: str 1494 1495 @return: The binary-encoded content type, as a list of (integer) byte 1496 values 1497 @rtype: list 1498 """ 1499 encodedContentType = [] 1500 if contentType in WSPEncodingAssignments.wkContentTypes: 1501 # Short-integer encoding 1502 encodedContentType.extend(Encoder.encodeShortInteger(WSPEncodingAssignments.wkContentTypes.index(contentType))) 1503 else: 1504 encodedContentType.extend(Encoder.encodeTextString(contentType)) 1505 return encodedContentType
1506 1507 @staticmethod
1508 - def encodeParameter(parameterName, parameterValue, encodingVersion='1.2'):
1509 """ Binary-encodes the name of a parameter of (for example) a 1510 "Content-Type" header entry, taking into account the WSP short form of 1511 well-known parameter names, as specified in section 8.4.2.4 and table 1512 38 of [5]. 1513 1514 From [5], section 8.4.2.4: 1515 C{Parameter = Typed-parameter | Untyped-parameter} 1516 C{Typed-parameter = Well-known-parameter-token Typed-value} 1517 C{Untyped-parameter = Token-text Untyped-value} 1518 C{Untyped-value = Integer-value | Text-value} 1519 1520 @param parameterName: The name of the parameter to encode 1521 @type parameterName: str 1522 @param parameterValue: The value of the parameter 1523 @type parameterValue: str or int 1524 1525 @param encodingVersion: The WSP encoding version to use. This defaults 1526 to "1.2", but may be "1.1", "1.2", "1.3" or 1527 "1.4" (see table 38 in [5] for details). 1528 @type encodingVersion: str 1529 1530 @raise ValueError: The specified encoding version is invalid. 1531 1532 @return: The binary-encoded parameter name, as a list of (integer) 1533 byte values 1534 @rtype: list 1535 """ 1536 wkParameters = WSPEncodingAssignments.wellKnownParameters(encodingVersion) 1537 encodedParameter = [] 1538 # Try to encode the parameter using a "Typed-parameter" value 1539 wkParamNumbers = wkParameters.keys().sort(reverse=True) 1540 for assignedNumber in wkParamNumbers: 1541 if wkParameters[assignedNumber][0] == parameterName: 1542 # Ok, it's a Typed-parameter; encode the parameter name 1543 encodedParameter.extend(Encoder.encodeShortInteger(assignedNumber)) 1544 # ...and now the value 1545 expectedType = wkParameters[assignedNumber][1] 1546 try: 1547 exec 'encodedParameter.extend(Encoder.encode%s(parameterValue))' % expectedType 1548 except EncodeError, msg: 1549 raise EncodeError, 'Error encoding parameter value: %s' % msg 1550 except: 1551 print 'A fatal error occurred, probably due to an unimplemented encoding operation' 1552 raise 1553 break 1554 # See if the "Typed-parameter" encoding worked 1555 if len(encodedParameter) == 0: 1556 # ...it didn't. Use "Untyped-parameter" encoding 1557 encodedParameter.extend(Encoder.encodeTokenText(parameterName)) 1558 value = [] 1559 # First try to encode the untyped-value as an integer 1560 try: 1561 value = Encoder.encodeIntegerValue(parameterValue) 1562 except EncodeError: 1563 value = Encoder.encodeTextString(parameterValue) 1564 encodedParameter.extend(value) 1565 return encodedParameter
1566 1567 #TODO: check up on the encoding/decoding of Token-text, in particular, how does this differ from text-string? does it have 0x00 at the end? 1568 @staticmethod
1569 - def encodeTokenText(text):
1570 """ From [5], section 8.4.2.1: 1571 Token-text = Token End-of-string 1572 1573 @raise EncodeError: Specified text cannot be encoding as a token 1574 1575 @return: The encoded token string, as a list of byte values 1576 @rtype: list 1577 """ 1578 separators = (11, 32, 40, 41, 44, 47, 58, 59, 60, 61, 62, 63, 64, 91, 1579 92, 93, 123, 125) 1580 # Sanity check 1581 for char in separators: 1582 if chr(char) in text: 1583 raise EncodeError, 'Char "%s" in text string; cannot encode as Token-text' % chr(char) 1584 encodedToken = Encoder.encodeTextString(text) 1585 return encodedToken
1586 1587 @staticmethod
1588 - def encodeIntegerValue(integer):
1589 """ Encodes an integer value 1590 1591 From [5], section 8.4.2.3: 1592 Integer-Value = Short-integer | Long-integer 1593 1594 This function will first try to encode the specified integer value 1595 into a short-integer, and failing that, will encode into a 1596 long-integer value. 1597 1598 @param integer: The integer to encode 1599 @type integer: int 1600 1601 @raise EncodeError: The <integer> parameter is not of type C{int} 1602 1603 @return: The encoded integer value, as a list of byte values 1604 @rtype: list 1605 """ 1606 if type(integer) != int: 1607 raise EncodeError, '<integer> must be of type "int"' 1608 encodedInteger = [] 1609 # First try and see if it's a short-integer 1610 try: 1611 encodedInteger = Encoder.encodeShortInteger(integer) 1612 except EncodeError: 1613 encodedInteger = Encoder.encodeLongInteger(integer) 1614 return encodedInteger
1615 1616 @staticmethod
1617 - def encodeTextValue(text):
1618 """ Stub for encoding Text-values; this is equivalent to 1619 C{encodeTextString} """ 1620 return Encoder.encodeTextString(text)
1621 1622 @staticmethod
1623 - def encodeNoValue(value=None):
1624 """ Encodes a No-value, which is 0x00 1625 1626 @note: This function mainly exists for use by automatically-selected 1627 encoding routines (see C{encodeParameter()} for an example. 1628 1629 @param value: This value is ignored; it is present so that this 1630 method complies with the format of the other C{encode} 1631 methods. 1632 1633 @return: A list containing a single "No-value", which is 0x00 1634 @rtype: list 1635 """ 1636 return [0x00]
1637 1638 @staticmethod
1639 - def encodeHeader(headerFieldName, headerValue):
1640 """ Encodes a WSP header entry, and its value 1641 1642 From [5], section 8.4.2.6: 1643 C{Header = Message-header | Shift-sequence} 1644 C{Message-header = Well-known-header | Application-header} 1645 C{Well-known-header = Well-known-field-name Wap-value} 1646 C{Application-header = Token-text Application-specific-value} 1647 1648 @note: "Shift-sequence" encoding has not been implemented 1649 @note: Currently, almost all header values are encoded as text-strings 1650 1651 @return: The encoded header, and its value, as a sequence of byte 1652 values 1653 @rtype: list 1654 """ 1655 encodedHeader = [] 1656 # First try encoding the header name as a "well-known-header"... 1657 wkHdrFields = WSPEncodingAssignments.headerFieldNames() 1658 if headerFieldName in wkHdrFields: 1659 headerFieldValue = Encoder.encodeShortInteger(wkHdrFields.index(headerFieldName)) 1660 encodedHeader.extend(headerFieldValue) 1661 else: 1662 # ...otherwise, encode it as an "application header" 1663 encodedHeaderName = Encoder.encodeTokenText(headerFieldName) 1664 encodedHeader.extend(encodedHeaderName) 1665 # Now add the value 1666 #TODO: make this flow better (see also Decoder.decodeHeader) 1667 # most header values are encoded as TextStrings, except where we have a specific Wap-value encoding implementation 1668 if headerFieldName in WSPEncodingAssignments.hdrFieldEncodings: 1669 wapValueType = WSPEncodingAssignments.hdrFieldEncodings[headerFieldName] 1670 try: 1671 exec 'encodedHeader.extend(Encoder.encode%s(headerValue))' % wapValueType 1672 except EncodeError, msg: 1673 raise EncodeError, 'Error encoding Wap-value: %s' % msg 1674 except: 1675 print 'A fatal error occurred, probably due to an unimplemented encoding operation' 1676 raise 1677 else: 1678 encodedHeader.extend(Encoder.encodeTextString(headerValue)) 1679 return encodedHeader
1680 1681 @staticmethod
1682 - def encodeContentTypeValue(mediaType, parameters):
1683 """ Encodes a content type, and its parameters 1684 1685 From [5], section 8.4.2.24: 1686 C{Content-type-value = Constrained-media | Content-general-form} 1687 1688 The short form of the Content-type-value MUST only be used when the 1689 well-known media is in the range of 0-127 or a text string. In all 1690 other cases the general form MUST be used. 1691 1692 @return: The encoded Content-type-value (including parameters, if 1693 any), as a sequence of bytes 1694 @rtype: list 1695 """ 1696 encodedContentTypeValue = [] 1697 # First try do encode it using Constrained-media encoding 1698 try: 1699 if len(parameters) > 0: 1700 raise EncodeError, 'Need to use Content-general-form for parameters' 1701 else: 1702 encodedContentTypeValue = Encoder.encodeConstrainedMedia(mediaType) 1703 except EncodeError: 1704 # Try the general form 1705 encodedContentTypeValue = Encoder.encodeContentGeneralForm(mediaType, parameters) 1706 return encodedContentTypeValue
1707 1708 @staticmethod
1709 - def encodeConstrainedMedia(mediaType):
1710 """ From [5], section 8.4.2.7: 1711 Constrained-media = Constrained-encoding 1712 It is encoded using values from the "Content Type Assignments" table. 1713 1714 @param mediaType: The media type to encode 1715 @type mediaType: str 1716 1717 @raise EncodeError: Media value is unsuitable for Constrained-encoding 1718 1719 @return: The encoded media type, as a sequence of bytes 1720 @rtype: list 1721 """ 1722 encodedMediaType = [] 1723 mediaValue = '' 1724 # See if this value is in the table of well-known content types 1725 if mediaType in WSPEncodingAssignments.wkContentTypes: 1726 mediaValue = WSPEncodingAssignments.wkContentTypes.index(mediaType) 1727 else: 1728 mediaValue = mediaType 1729 encodedMediaType = Encoder.encodeConstrainedEncoding(mediaValue) 1730 return encodedMediaType
1731 1732 @staticmethod
1733 - def encodeConstrainedEncoding(value):
1734 """ Constrained-encoding = Extension-Media --or-- Short-integer 1735 This encoding is used for token values, which have no well-known 1736 binary encoding, or when the assigned number of the well-known 1737 encoding is small enough to fit into Short-integer. 1738 1739 @param value: The value to encode 1740 @type value: int or str 1741 1742 @raise EncodeError: <value> cannot be encoded as a 1743 Constrained-encoding sequence 1744 1745 @return: The encoded constrained-encoding token value, as a sequence 1746 of bytes 1747 @rtype: list 1748 """ 1749 encodedValue = None 1750 if type(value) == int: 1751 # First try and encode the value as a short-integer 1752 encodedValue = Encoder.encodeShortInteger(value) 1753 else: 1754 # Ok, it should be Extension-Media then 1755 try: 1756 encodedValue = Encoder.encodeExtensionMedia(value) 1757 except EncodeError: 1758 # Give up 1759 raise EncodeError, 'Cannot encode %s as a Constrained-encoding sequence' % str(value) 1760 return encodedValue
1761 1762 @staticmethod
1763 - def encodeExtensionMedia(mediaValue):
1764 """ From [5], section 8.4.2.1: 1765 Extension-media = *TEXT End-of-string 1766 This encoding is used for media values, which have no well-known 1767 binary encoding 1768 1769 @param mediaValue: The media value (string) to encode 1770 @type mediaValue: str 1771 1772 @raise EncodeError: The value cannot be encoded as TEXT; probably it 1773 starts with/contains an invalid character 1774 1775 @return: The encoded media type value, as a sequence of bytes 1776 @rtype: str 1777 """ 1778 encodedMediaValue = '' 1779 if type(mediaValue) != str: 1780 try: 1781 mediaValue = str(mediaValue) 1782 except: 1783 raise EncodeError, 'Invalid Extension-media: Cannot convert value to text string' 1784 char = mediaValue[0] 1785 if ord(char) < 32 or ord(char) == 127: 1786 raise EncodeError, 'Invalid Extension-media: TEXT starts with invalid character: %s' % ord(char) 1787 encodedMediaValue = Encoder.encodeTextString(mediaValue) 1788 return encodedMediaValue
1789 1790 @staticmethod
1791 - def encodeContentGeneralForm(mediaType, parameters):
1792 """ From [5], section 8.4.2.24: 1793 Content-general-form = Value-length Media-type 1794 1795 @note: Used in decoding Content-type fields and their parameters; 1796 see C{decodeContentTypeValue} 1797 1798 @note: Used by C{decodeContentTypeValue()} 1799 1800 @return: The encoded Content-general-form, as a sequence of bytes 1801 @rtype: list 1802 """ 1803 encodedContentGeneralForm = [] 1804 encodedMediaType = [] 1805 encodedParameters = [] 1806 # Encode the actual content type 1807 encodedMediaType = Encoder.encodeMediaType(mediaType) 1808 # Encode all parameters 1809 for paramName in parameters: 1810 encodedParameters.extend(Encoder.encodeParameter(paramName, parameters[paramName])) 1811 valueLength = len(encodedMediaType) + len(encodedParameters) 1812 encodedValueLength = Encoder.encodeValueLength(valueLength) 1813 encodedContentGeneralForm.extend(encodedValueLength) 1814 encodedContentGeneralForm.extend(encodedMediaType) 1815 encodedContentGeneralForm.extend(encodedParameters) 1816 return encodedContentGeneralForm
1817 1818 @staticmethod
1819 - def encodeValueLength(length):
1820 """ Encodes the specified length value as a value length indicator 1821 1822 "Value length" is used to indicate the length of a value to follow, as 1823 used in the C{Content-Type} header in the MMS body, for example. 1824 1825 The encoding for a value length indicator is specified in [5], 1826 section 8.4.2.2, and follows the form:: 1827 1828 Value-length = [Short-length] --or-- [Length-quote] [Length] 1829 ^^^^^^ ^^^^^^ ^^^^^^ 1830 1 byte 1 byte x bytes 1831 <Any octet 0-30> <Octet 31> Uintvar-integer 1832 1833 @raise EncodeError: The ValueLength could not be encoded. 1834 1835 @return: The encoded value length indicator, as a sequence of bytes 1836 @rtype: list 1837 """ 1838 encodedValueLength = [] 1839 # Try and encode it as a short-length 1840 try: 1841 encodedValueLength = Encoder.encodeShortLength(length) 1842 except EncodeError: 1843 # Encode it with a Length-quote and Uintvar 1844 encodedValueLength.append(31) # Length-quote 1845 encodedValueLength.extend(Encoder.encodeUintvar(length)) 1846 return encodedValueLength
1847 1848 @staticmethod
1849 - def encodeShortLength(length):
1850 """ From [5], section 8.4.2.2: 1851 Short-length = <Any octet 0-30> 1852 1853 @raise EmcodeError: The specified <length> cannot be encoded as a 1854 short-length value; it is not in octet range 0-30. 1855 1856 @return: The encoded short-length, as a sequence of bytes 1857 @rtype: list 1858 """ 1859 if length < 0 or length > 30: 1860 raise EncodeError, 'Cannot encode short-length; length should be in range 0-30' 1861 else: 1862 return [length]
1863 1864 @staticmethod
1865 - def encodeAcceptValue(acceptValue):
1866 """ From [5], section 8.4.2.7: 1867 Accept-value = Constrained-media | Accept-general-form 1868 Accept-general-form = Value-length Media-range [Accept-parameters] 1869 Media-range = (Well-known-media | Extension-Media) *(Parameter) 1870 Accept-parameters = Q-token Q-value *(Accept-extension) 1871 Accept-extension = Parameter 1872 Q-token = <Octet 128> 1873 1874 @note: This implementation does not currently support encoding of 1875 "Accept-parameters". 1876 1877 @param acceptValue: The Accept-value to encode (media/content type) 1878 @type acceptValue: str 1879 1880 @raise EncodeError: The encoding failed. 1881 1882 @return: The encoded Accept-value, as a sequence of bytes 1883 @rtype: list 1884 """ 1885 encodedAcceptValue = [] 1886 # Try to use Constrained-media encoding 1887 try: 1888 encodedAcceptValue = Encoder.encodeConstrainedMedia(acceptValue) 1889 except EncodeError: 1890 # ...now try Accept-general-form 1891 try: 1892 encodedMediaRange = Encoder.encodeMediaType(acceptValue) 1893 except EncodeError, msg: 1894 raise EncodeError, 'Cannot encode Accept-value: %s' % msg 1895 valueLength = Encoder.encodeValueLength(len(encodedMediaRange)) 1896 encodedAcceptValue = valueLength 1897 encodedAcceptValue.extend(encodedMediaRange) 1898 return encodedAcceptValue
1899