Module prototypes
[hide private]
[frames] | no frames]

Source Code for Module prototypes

   1  ### BITPIM 
   2  ### 
   3  ### Copyright (C) 2003-2005 Roger Binns <rogerb@rogerbinns.com> 
   4  ### 
   5  ### This program is free software; you can redistribute it and/or modify 
   6  ### it under the terms of the BitPim license as detailed in the LICENSE file. 
   7  ### 
   8  ### $Id: prototypes.py 4784 2010-01-15 01:44:50Z djpham $ 
   9   
  10  """The various types used in protocol descriptions 
  11   
  12  To implement a type used for protocol descriptions, 
  13  examine the code for UINTlsb in this file.  Of note: 
  14   
  15    - Inherit from BaseProtogenClass 
  16    - Call superclass constructors using super() 
  17    - Do not process any of the args or kwargs in the constructor unless 
  18      you need them to alter how you are constructed 
  19    - At the end of the constructor, call _update if you are the most 
  20      derived class 
  21    - In _update, call super()._update, and then delete keyword arguments 
  22      as you process them (consider using L{BaseProtogenClass._consumekw} function) 
  23    - If you are the most derived class, complain about 
  24      unused keyword arguments (consider using 
  25      L{BaseProtogenClass._complainaboutunusedargs} function) 
  26    - set _bufferstartoffset and _bufferendoffset whenever 
  27      you are read or written from a buffer 
  28    - (optionally) define a getvalue() method that returns 
  29      a better type.  For example if your class is integer 
  30      like then this would return a real int.  If string like, 
  31      then this will return a real string. 
  32    - If you are a container, override iscontainer.  You will 
  33      also need to provide a containerelements() method which 
  34      lets you iterate over the entries. 
  35   
  36  containerelements method: 
  37   
  38    - You should return tuples of (fieldname, fieldvalue, descriptionstring or None) 
  39    - fieldvalue should be the actual object, not a pretty version (eg a USTRING not str) 
  40     
  41       
  42  """ 
  43  import sys 
  44  import calendar 
  45  import cStringIO 
  46  import re 
  47  import time 
  48   
  49  import common 
  50   
51 -class ProtogenException(Exception):
52 """Base class for exceptions encountered with data marshalling"""
53 - def __init__(self, *args, **kwargs):
54 Exception.__init__(self, *args, **kwargs)
55
56 -class SizeNotKnownException(ProtogenException):
57 "Unable to marshal since size isn't known"
58 - def __init__(self):
59 ProtogenException.__init__(self, "The size of this item is not known and hence cannot be en/decoded")
60
61 -class ValueNotSetException(ProtogenException):
62 "Value not been set"
63 - def __init__(self):
64 ProtogenException.__init__(self, "The value for this object has not been set.")
65
66 -class ValueException(ProtogenException):
67 "Some sort of problem with the value"
68 - def __init__(self, str):
70
71 -class NotTerminatedException(ProtogenException):
72 "The value should have been terminated and wasn't"
73 - def __init__(self):
74 ProtogenException.__init__(self,"The value should have been terminated and wasn't")
75
76 -class ValueLengthException(ProtogenException):
77 "The value is the wrong size (too big or too small)"
78 - def __init__(self, sz, space):
79 ProtogenException.__init__(self, "The value (length %d) is the wrong size for space %d" % (sz,space))
80
81 -class MissingQuotesException(ProtogenException):
82 "The value does not have quotes around it"
83 - def __init__(self):
84 ProtogenException.__init__(self, "The value does not have the required quote characters around it")
85 86
87 -class BaseProtogenClass(object):
88 """All types are derived from this"""
89 - def __init__(self, *args, **kwargs):
90 pass
91
92 - def packetsize(self):
93 "Returns size in bytes that we occupy" 94 # This is implemented by writing to a buffer and seeing how big the result is. 95 # The code generator used to make one per class but it was so rarely used (twice) 96 # that this implementation is available instead 97 b=buffer() 98 self.writetobuffer(b) 99 return len(b.getvalue())
100
101 - def writetobuffer(self, buf):
102 "Scribble ourselves to the buf" 103 raise NotImplementedError("writetobuffer()")
104 - def readfrombuffer(self, buf):
105 "Get our value from the buffer" 106 raise NotImplementedError("readfrombuffer()")
107 - def getvalue(self):
108 "Returns our underlying value if sensible (eg an integer, string or list) else returns self" 109 return self
110 - def packetspan(self):
111 """Returns tuple of begining,end offsets from last packet we were read or written from. 112 113 Note that in normal Python style, end is one beyond the last byte we 114 actually own""" 115 return self._bufferstartoffset, self._bufferendoffset
116 - def _consumekw(self, dict, consumelist):
117 """A helper function for easily setting internal values from the dict 118 119 For each name in consumelist, we look for it in the dict and 120 set self._name to the value from dict. The key is then deleted 121 from the dict.""" 122 for name in consumelist: 123 if dict.has_key(name): 124 setattr(self, "_"+name, dict[name]) 125 del dict[name]
126 - def _complainaboutunusedargs(self, klass, dict):
127 """A helper function that will raise an exception if there are unused keyword arguments. 128 129 @Note: that we only complain if in the most derived class, so it is safe 130 to always call this helper as the last line of your constructor. 131 132 @param klass: This should be the class you are calling this function from 133 @param dict: The keyword arguments still in play 134 """ 135 if len(dict) and self.__class__.__mro__[0]==klass: 136 raise TypeError('Unexpected keyword args supplied: '+`dict`)
137
138 - def _ismostderived(self, klass):
139 140 return self.__class__.__mro__[0]==klass
141
142 - def _update(self, args, kwargs):
143 return
144
145 - def iscontainer(self):
146 """Do we contain fields?""" 147 return False
148
149 - def update(self, *args, **kwargs):
150 self._update(args, kwargs)
151 152 # help with logging
153 - def autologwrite(self, buf, logtitle="<written data>"):
154 f=sys._getframe() # my frame 155 f=f.f_back # my caller 156 if f: 157 f=f.f_back # their caller 158 if f: 159 caller=f.f_locals.get("self", None) 160 if caller: 161 try: 162 caller.logdata(logtitle, buf.getvalue(), self) 163 except: 164 pass
165
166 - def autologread(self, buf, logtitle="<read data>"):
167 f=sys._getframe() # my frame 168 f=f.f_back # my caller 169 if f: 170 f=f.f_back # their caller 171 if f: 172 caller=f.f_locals.get("self", None) 173 if caller: 174 try: 175 caller.logdata(logtitle, buf.getdata(), self) 176 except: 177 pass
178
179 -class UINTlsb(BaseProtogenClass):
180 "An integer in Least Significant Byte first order"
181 - def __init__(self, *args, **kwargs):
182 """ 183 An integer value can be specified in the constructor, or as the value keyword arg. 184 185 @keyword constant: (Optional) A constant value. All reads must have this value 186 @keyword constantexception: (Optional) Type of exception raised when data doesn't match constant. 187 @keyword sizeinbytes: (Mandatory for writing, else Optional) How big we are in bytes 188 @keyword default: (Optional) Our default value 189 @keyword value: (Optional) The value 190 """ 191 super(UINTlsb, self).__init__(*args, **kwargs) 192 self._constant=None 193 self._constantexception=ValueError 194 self._sizeinbytes=None 195 self._value=None 196 self._default=None 197 198 if self._ismostderived(UINTlsb): 199 self._update(args,kwargs)
200 201
202 - def _update(self, args, kwargs):
203 super(UINTlsb,self)._update(args, kwargs) 204 205 self._consumekw(kwargs, ("constant", "constantexception", "sizeinbytes", "default", "value")) 206 self._complainaboutunusedargs(UINTlsb,kwargs) 207 208 # Set our value if one was specified 209 if len(args)==0: 210 pass 211 elif len(args)==1: 212 self._value=int(args[0]) 213 else: 214 raise TypeError("Unexpected arguments "+`args`) 215 216 if self._value is None and self._default is not None: 217 self._value=self._default 218 219 if self._value is None and self._constant is not None: 220 self._value=self._constant 221 222 if self._constant is not None and self._constant!=self._value: 223 raise self._constantexception("This field is a constant of %d. You tried setting it to %d" % (self._constant, self._value))
224 225
226 - def readfrombuffer(self, buf):
227 if self._sizeinbytes is None: 228 raise SizeNotKnownException() 229 self._bufferstartoffset=buf.getcurrentoffset() 230 # lsb read 231 res=0 232 shift=0 233 for dummy in range(self._sizeinbytes): 234 res|=buf.getnextbyte()<<shift 235 shift+=8 236 self._value=res 237 self._bufferendoffset=buf.getcurrentoffset() 238 if self._constant is not None and self._value!=self._constant: 239 raise self._constantexception("The value read should be a constant of %d, but was %d instead" % (self._constant, self._value))
240
241 - def writetobuffer(self, buf):
242 if self._sizeinbytes is None: 243 raise SizeNotKnownException() 244 if self._value is None: 245 raise ValueNotSetException() 246 247 self._bufferstartoffset=buf.getcurrentoffset() 248 # lsb write 249 res=self._value 250 for dummy in range(self._sizeinbytes): 251 buf.appendbyte(res&0xff) 252 res>>=8 253 self._bufferendoffset=buf.getcurrentoffset()
254
255 - def packetsize(self):
256 if self._sizeinbytes is None: 257 raise SizeNotKnownException() 258 return self._sizeinbytes
259
260 - def getvalue(self):
261 """Returns the integer we are""" 262 if self._value is None: 263 raise ValueNotSetException() 264 return self._value
265
266 -class BOOLlsb(UINTlsb):
267 "An Boolean in Least Significant Byte first order"
268 - def __init__(self, *args, **kwargs):
269 """ 270 A boolean value can be specified in the constructor. 271 272 Keyword arguments are the same a UINTlsb 273 """ 274 super(BOOLlsb, self).__init__(*args, **kwargs) 275 276 if self._ismostderived(BOOLlsb): 277 self._update(args,kwargs)
278
279 - def _update(self, args, kwargs):
280 super(BOOLlsb,self)._update(args,kwargs) 281 self._complainaboutunusedargs(BOOLlsb,kwargs) 282 self._boolme()
283
284 - def _boolme(self):
285 if self._value is not None: 286 self._value=bool(self._value)
287
288 - def readfrombuffer(self, buf):
289 UINTlsb.readfrombuffer(self,buf) 290 self._boolme()
291
292 -class STRING(BaseProtogenClass):
293 "A text string DEPRECATED USE USTRING "
294 - def __init__(self, *args, **kwargs):
295 """ 296 A string value can be specified to this constructor, or in the value keyword arg. 297 298 @keyword constant: (Optional) A constant value. All reads must have this value 299 @keyword terminator: (Default=0) The string terminator (or None). If set there will 300 always be a terminator when writing. The terminator is not returned when getting 301 the value. 302 @keyword pad: (Default=0) The padding byte if fixed length when writing, or stripped off 303 when reading 304 @keyword sizeinbytes: (Optional) Set if fixed length. 305 If not set, then the terminator will be used to find the end of strings on reading. 306 If not set and the terminator is None, then reads will be entire rest of buffer. 307 @keyword maxsizeinbytes: (Optional) Max string length. Used together 308 with terminator to limit the max length of the value. 309 TODO: Need to add this to USTRING also. 310 @keyword default: (Optional) Our default value 311 @keyword raiseonunterminatedread: (Default True) raise L{NotTerminatedException} if there is 312 no terminator on the value being read in. terminator must also be set. 313 @keyword raiseontruncate: (Default True) raise L{ValueLengthException} if the supplied 314 value is too large to fit within sizeinbytes. 315 @keyword value: (Optional) Value 316 @keyword pascal: (Default False) The string is preceded with one byte giving the length 317 of the string (including terminator if there is one) 318 """ 319 super(STRING, self).__init__(*args, **kwargs) 320 321 self._constant=None 322 self._terminator=0 323 self._pad=0 324 self._sizeinbytes=None 325 self._default=None 326 self._raiseonunterminatedread=True 327 self._raiseontruncate=True 328 self._value=None 329 self._pascal=False 330 self._maxsizeinbytes=None 331 332 if self._ismostderived(STRING): 333 self._update(args,kwargs)
334
335 - def _update(self, args, kwargs):
336 super(STRING,self)._update(args, kwargs) 337 338 self._consumekw(kwargs, ("constant", "terminator", "pad", "pascal", 339 "sizeinbytes", "default", "raiseonunterminatedread", "value", 340 "raiseontruncate", "maxsizeinbytes")) 341 self._complainaboutunusedargs(STRING,kwargs) 342 343 # Set our value if one was specified 344 if len(args)==0: 345 pass 346 elif len(args)==1: 347 self._value=common.forceascii(args[0]) 348 if self._constant is not None and self._constant!=self._value: 349 raise ValueException("This field is a constant of '%s'. You tried setting it to '%s'" % (self._constant, self._value)) 350 else: 351 raise TypeError("Unexpected arguments "+`args`) 352 if self._value is None and self._default is not None: 353 self._value=self._default 354 355 if self._value is not None: 356 self._value=str(self._value) # no unicode here! 357 l=len(self._value) 358 if self._sizeinbytes is not None: 359 if self._terminator is not None: 360 l+=1 361 if l>self._sizeinbytes: 362 if self._raiseontruncate: 363 raise ValueLengthException(l, self._sizeinbytes) 364 365 self._value=self._value[:self._sizeinbytes] 366 if len(self._value) and self._terminator is not None: 367 self._value=self._value[:-1] 368 elif self._maxsizeinbytes is not None: 369 if l>self._maxsizeinbytes: 370 if self._raiseontruncate: 371 raise ValueLengthException(l, self._maxsizeinbytes) 372 self._value=self._value[:self._maxsizeinbytes]
373
374 - def readfrombuffer(self, buf):
375 self._bufferstartoffset=buf.getcurrentoffset() 376 377 flush=0 378 if self._pascal: 379 if self._sizeinbytes is None: 380 self._sizeinbytes=buf.getnextbyte() 381 # allow for fixed size pascal strings 382 else: 383 temp=self._sizeinbytes-1 384 self._sizeinbytes=buf.getnextbyte() 385 flush=temp-self._sizeinbytes 386 if(temp < 0): 387 raise ValueLengthException() 388 389 if self._sizeinbytes is not None: 390 # fixed size 391 self._value=buf.getnextbytes(self._sizeinbytes) 392 if self._terminator is not None: 393 # using a terminator 394 pos=self._value.find(chr(self._terminator)) 395 if pos>=0: 396 self._value=self._value[:pos] 397 elif self._raiseonunterminatedread: 398 raise NotTerminatedException() 399 elif self._pad is not None: 400 # else remove any padding 401 while len(self._value) and self._value[-1]==chr(self._pad): 402 self._value=self._value[:-1] 403 else: 404 if self._terminator is None: 405 # read in entire rest of packet 406 self._value=buf.getremainingbytes() 407 else: 408 # read up to terminator 409 self._value="" 410 while buf.hasmore(): 411 self._value+=chr(buf.getnextbyte()) 412 if self._value[-1]==chr(self._terminator): 413 break 414 if self._value[-1]!=chr(self._terminator): 415 if self._raiseonunterminatedread: 416 raise NotTerminatedException() 417 else: 418 self._value=self._value[:-1] 419 420 if self._maxsizeinbytes is not None: 421 self._value=self._value[:self._maxsizeinbytes] 422 423 if self._constant is not None and self._value!=self._constant: 424 raise ValueException("The value read was not the constant") 425 426 # for fixed size pascal strings flush the remainder of the buffer 427 if(flush): 428 buf.getnextbytes(flush) 429 430 self._bufferendoffset=buf.getcurrentoffset()
431
432 - def writetobuffer(self, buf):
433 if self._value is None: 434 raise ValueNotSetException() 435 436 self._bufferstartoffset=buf.getcurrentoffset() 437 # calculate length 438 l=len(self._value) 439 if self._terminator is not None: 440 l+=1 441 if self._pascal: 442 buf.appendbyte(l) 443 l+=1 444 buf.appendbytes(self._value) 445 if self._terminator is not None: 446 buf.appendbyte(self._terminator) 447 if self._sizeinbytes is not None: 448 if l<self._sizeinbytes: 449 buf.appendbytes(chr(self._pad)*(self._sizeinbytes-l)) 450 451 self._bufferendoffset=buf.getcurrentoffset()
452
453 - def packetsize(self):
454 if self._sizeinbytes is not None: 455 return self._sizeinbytes 456 457 if self._value is None: 458 raise ValueNotSetException() 459 460 l=len(self._value) 461 if self._terminator is not None: 462 l+=1 463 if self._pascal: 464 l+=1 465 return l
466
467 - def getvalue(self):
468 """Returns the string we are""" 469 if self._value is None: 470 raise ValueNotSetException() 471 return self._value
472 473
474 -class USTRING(BaseProtogenClass):
475 "A text string that supports configurable encodings"
476 - def __init__(self, *args, **kwargs):
477 """ 478 A string value can be specified to this constructor, or in the value keyword arg. 479 480 @keyword constant: (Optional) A constant value. All reads must have this value 481 @keyword terminator: (Default=0) The string terminator (or None). If set there will 482 always be a terminator when writing. The terminator is not returned when getting 483 the value. 484 @keyword terminator_length: (Default=1) (min:1, max:4)The length of the string terminator. 485 This keyword is not used if the terminator is None. Multi-byte terminators are treated 486 as LSB when read from the phone. 487 @keyword pad: (Default=0) The padding byte if fixed length when writing, or stripped off 488 when reading 489 @keyword sizeinbytes: (Optional) Set if fixed length. 490 If not set, then the terminator will be used to find the end of strings on reading. 491 If not set and the terminator is None, then reads will be entire rest of buffer. 492 @keyword maxsizeinbytes: (Optional) Max string length. Used together 493 with terminator to limit the max length of the value. 494 @keyword default: (Optional) Our default value 495 @keyword raiseonunterminatedread: (Default True) raise L{NotTerminatedException} if there is 496 no terminator on the value being read in. Terminator must also be set. 497 @keyword raiseontruncate: (Default True) raise L{ValueLengthException} if the supplied 498 value is too large to fit within sizeinbytes. 499 @keyword value: (Optional) Value 500 @keyword pascal: (Default False) The string is preceded with one byte giving the length 501 of the string (including terminator if there is one) 502 @keyword encoding: (Default 'ascii') The charset to use when reading/writing to a buffer 503 @keyword read_encoding: (Default keyword:encoding) The charset to use when reading from a buffer 504 @keyword write_encoding: (Default keyword:encoding) The charset to use when writing to a buffer 505 """ 506 super(USTRING, self).__init__(*args, **kwargs) 507 508 self._constant=None 509 self._terminator=0 510 self._pad=0 511 self._sizeinbytes=None 512 self._default=None 513 self._raiseonunterminatedread=True 514 self._raiseontruncate=True 515 self._value=None 516 self._pascal=False 517 self._maxsizeinbytes=None 518 self._encoding='ascii' 519 self._read_encoding=None 520 self._write_encoding=None 521 self._terminator_length=1 522 523 if self._ismostderived(USTRING): 524 self._update(args,kwargs)
525
526 - def _update(self, args, kwargs):
527 super(USTRING,self)._update(args, kwargs) 528 529 self._consumekw(kwargs, ("constant", "terminator", "pad", "pascal", 530 "sizeinbytes", "default", "raiseonunterminatedread", "value", "raiseontruncate", 531 "encoding", "read_encoding", "write_encoding", "maxsizeinbytes")) 532 self._complainaboutunusedargs(USTRING,kwargs) 533 if self._read_encoding==None: 534 self._read_encoding=self._encoding 535 if self._write_encoding==None: 536 self._write_encoding=self._encoding 537 if self._terminator_length < 1 or self._terminator_length > 4: 538 raise ValueException("Terminator length outside allowed range of 1-4. You tried setting it to %d" % self._terminator_length) 539 540 # detect correct terminator length 541 if self._terminator == 0: 542 if self._encoding.startswith("utf_16"): 543 self._terminator_length = 2 544 elif self._encoding.startswith("utf_32"): 545 self._terminator_length = 4 546 547 # Set our value if one was specified 548 if len(args)==0: 549 pass 550 elif len(args)==1: 551 self._value=args[0] 552 else: 553 raise TypeError("Unexpected arguments "+`args`) 554 if self._value is None and self._default is not None: 555 self._value=self._default 556 557 if self._value is not None: 558 # convert _value to unicode, non-string objects convert to string representation 559 # allows strings to be initialised with integers etc. 560 if not isinstance(self._value, (str, unicode)): 561 # convert numbers into strings 562 temp=str(self._value) 563 self._value=unicode(temp, 'ascii', 'replace') 564 # we should only receive unicode strings from bitpim, but... 565 elif not isinstance(self._value, unicode): 566 # there is a bug, the docs say this should work on unicode strings but it does not 567 self._value=unicode(self._value, 'ascii', 'replace') 568 569 if self._constant is not None and self._constant!=self._value: 570 raise ValueException("This field is a constant of '%s'. You tried setting it to '%s'" % (self._constant, self._value)) 571 # test that we can convert the string, also needed for "sizeinbytes" 572 try: 573 test=self.convert_for_write() 574 except UnicodeEncodeError: 575 raise common.PhoneStringEncodeException(self._value, uni_string_codec) 576 577 if self._value is not None: 578 max_size=None 579 if self._sizeinbytes is not None: 580 max_size=self._sizeinbytes 581 elif self._maxsizeinbytes is not None: 582 max_size=self._maxsizeinbytes 583 if max_size is not None: 584 l=len(test) 585 if self._terminator is not None: 586 l+=self._terminator_length 587 if l>max_size: 588 if self._raiseontruncate: 589 raise ValueLengthException(l, self._sizeinbytes) 590 # truncate, but the number of bytes might be larger than the number of 591 # unicode characters depending on conversion 592 self._value=self._value[:max_size] 593 term_len=0 594 if self._terminator!=None: 595 term_len=self._terminator_length 596 # adjust for terminator and multibyte characters 597 while (len(self.convert_for_write())+term_len)>max_size: 598 self._value=self._value[:-1]
599
600 - def readfrombuffer(self, buf):
601 self._bufferstartoffset=buf.getcurrentoffset() 602 603 flush=0 604 _value='' 605 if self._pascal: 606 if self._sizeinbytes is None: 607 self._sizeinbytes=buf.getnextbyte() 608 # allow for fixed size pascal strings 609 else: 610 temp=self._sizeinbytes-1 611 self._sizeinbytes=buf.getnextbyte() 612 flush=temp-self._sizeinbytes 613 if(temp < 0): 614 raise ValueLengthException() 615 616 if self._sizeinbytes is not None: 617 # fixed size 618 _value=buf.getnextbytes(self._sizeinbytes) 619 if self._terminator is not None: 620 # using a terminator 621 pos=-1 622 for i in range(0, self._sizeinbytes, self._terminator_length): 623 term=0 624 for j in range(self._terminator_length): 625 term+=ord(_value[i+j])<<(j*8) 626 if term==self._terminator: 627 pos=i 628 break 629 if pos>=0: 630 _value=_value[:pos] 631 elif self._raiseonunterminatedread: 632 raise NotTerminatedException() 633 elif self._pad is not None: 634 # else remove any padding 635 while len(_value) and _value[-1]==chr(self._pad): 636 _value=_value[:-1] 637 else: 638 if self._terminator is None: 639 # read in entire rest of packet 640 _value=buf.getremainingbytes() 641 else: 642 # read up to terminator 643 _value="" 644 count=0 645 term=0 646 while buf.hasmore(): 647 _value+=chr(buf.getnextbyte()) 648 count+=1 649 if (count % self._terminator_length)==0: 650 term=0 651 # see if we have a terminator 652 for j in range(self._terminator_length): 653 term=(term<<8)+ord(_value[count-1-j]) 654 if term==self._terminator: 655 break 656 if term!=self._terminator and self._raiseonunterminatedread: 657 raise NotTerminatedException() 658 else: 659 _value=_value[:-1] 660 661 if self._maxsizeinbytes is not None: 662 _value=_value[:self._maxsizeinbytes] 663 664 if self._constant is not None and _value!=self._constant: 665 raise ValueException("The value read was not the constant") 666 667 # for fixed size pascal strings flush the remainder of the buffer 668 if(flush): 669 buf.getnextbytes(flush) 670 671 self._bufferendoffset=buf.getcurrentoffset() 672 673 # we will convert to unicode when the value is retrieved. 674 # this prevents garbage that will never be used from causing an 675 # exception. e.g. A deleted calendar record on some LG phones may contain 676 # all f's, this causes an exception if we try to convert to unicode. 677 self._value=_value
678
679 - def writetobuffer(self, buf):
680 if self._value is None: 681 raise ValueNotSetException() 682 self._bufferstartoffset=buf.getcurrentoffset() 683 # calculate length 684 temp_str=self.convert_for_write() 685 # this length is the character length of the string NOT the byte length (BIG DIFFERENCE) 686 # the byte length is calculated below 687 l=len(temp_str) 688 if self._terminator is not None: 689 l+=1 690 if self._pascal: 691 buf.appendbyte(l) 692 l+=1 693 buf.appendbytes(temp_str) 694 term=self._terminator 695 if self._terminator is not None: 696 for j in range(self._terminator_length): 697 buf.appendbyte((term & 0xFF)) 698 term=term>>8 699 # calculate the byte length 700 self._bufferendoffset=buf.getcurrentoffset() 701 l = self._bufferendoffset - self._bufferstartoffset 702 # pad the buffer 703 if self._sizeinbytes is not None: 704 if l<self._sizeinbytes: 705 buf.appendbytes(chr(self._pad)*(self._sizeinbytes-l)) 706 self._bufferendoffset=buf.getcurrentoffset()
707
708 - def convert_for_write(self):
709 # if we are not in unicode this means that we contain data read from the phone 710 # in this case just return this back, there is no need to convert twice. 711 if not isinstance(self._value, unicode): 712 return self._value 713 try: 714 temp_str=common.encode_with_degrade(self._value, self._write_encoding) 715 except UnicodeError: 716 raise common.PhoneStringEncodeException(self._value, self._write_encoding) 717 return temp_str
718
719 - def packetsize(self):
720 if self._sizeinbytes is not None: 721 return self._sizeinbytes 722 723 if self._value is None: 724 raise ValueNotSetException() 725 726 l=len(self.convert_for_write()) 727 if self._terminator is not None: 728 l+=1 729 730 return l
731
732 - def getvalue(self):
733 """Returns the string we are""" 734 if self._value is None: 735 raise ValueNotSetException() 736 737 # convert to unicode if we are not already 738 if not isinstance(self._value, unicode): 739 try: 740 self._value=self._value.decode(self._read_encoding) 741 except UnicodeDecodeError: 742 # this means the codec is set wrong for this phone !! 743 raise common.PhoneStringDecodeException(self._value, self._read_encoding) 744 return self._value
745
746 -class SEVENBITSTRING(BaseProtogenClass):
747 """A text string where ASCII characters are stored as packed 7 bit characters. This is 748 typically used in SMS messages."""
749 - def __init__(self, *args, **kwargs):
750 """ 751 @keyword terminator: (Default=\x00) The termination character 752 @keyword sizeinbytes: Amount of space the string sits in 753 """ 754 super(SEVENBITSTRING, self).__init__(*args, **kwargs) 755 self._value=None 756 self._terminator='\x00' 757 self._sizeinbytes=None 758 if self._ismostderived(SEVENBITSTRING): 759 self._update(args,kwargs)
760
761 - def _update(self, args, kwargs):
762 super(SEVENBITSTRING,self)._update(args, kwargs) 763 764 self._consumekw(kwargs, ("terminator", "value", "sizeinbytes")) 765 self._complainaboutunusedargs(SEVENBITSTRING, kwargs) 766 if len(args): 767 raise TypeError("Unexpected arguments "+`args`) 768 if self._sizeinbytes is None: 769 raise ValueException("You must specify a size in bytes")
770
771 - def readfrombuffer(self, buf):
772 self._bufferstartoffset=buf.getcurrentoffset() 773 bytes=buf.getnextbytes(self._sizeinbytes) 774 self._value=common.decodecharacterbits(bytes, bitsperchar=7, charconv=chr, terminator=self._terminator) 775 self._bufferendoffset=buf.getcurrentoffset()
776
777 - def getvalue(self):
778 """Returns the string we are""" 779 if self._value is None: 780 raise ValueNotSetException() 781 return self._value
782
783 -class SMSDATE(BaseProtogenClass):
784 """A date as used in SMS messages. It is six bytes long with the 785 bytes being year month day hour minute second. From stuff on the 786 web, it appears GSM phones swap each nybble."""
787 - def __init__(self, *args, **kwargs):
788 """@keyword sizeinbytes: (optional) Must be six""" 789 super(SMSDATE, self).__init__(*args, **kwargs) 790 self._values=None 791 self._sizeinbytes=6 792 if self._ismostderived(SMSDATE): 793 self._update(args, kwargs)
794
795 - def _update(self, args, kwargs):
796 super(SMSDATE, self)._update(args, kwargs) 797 self._consumekw(kwargs, ("sizeinbytes",)) 798 self._complainaboutunusedargs(SMSDATE, kwargs) 799 if len(args): 800 raise TypeError("Unexpected arguments "+`args`) 801 if self._sizeinbytes != 6: 802 raise ValueNotSetException("You can only specify 6 as the size in bytes")
803
804 - def readfrombuffer(self, buf):
805 self._bufferstartoffset=buf.getcurrentoffset() 806 year=buf.getnextbyte() 807 if year<0: 808 year+=1900 809 else: 810 year+=2000 811 month=buf.getnextbyte() 812 day=buf.getnextbyte() 813 hour=buf.getnextbyte() 814 minute=buf.getnextbyte() 815 second=buf.getnextbyte() 816 self._value=year,month,day, hour,minute,second 817 self._bufferendoffset=buf.getcurrentoffset()
818
819 - def getvalue(self):
820 """Returns the ISO date time string we are""" 821 if self._value is None: 822 raise ValueNotSetException() 823 return "%d%02d%02dT%02d%02d%02d" % self._value
824 825
826 -class CSVSTRING(BaseProtogenClass):
827 """A text string enclosed in quotes, with a way to escape quotes that a supposed 828 to be part of the string. Typical of Samsung phones."""
829 - def __init__(self, *args, **kwargs):
830 """ 831 A string value can be specified to this constructor, or in the value keyword arg. 832 833 @keyword constant: (Optional) A constant value. All reads must have this value 834 @keyword terminator: (Default=,) The string terminator (or None). If set there will 835 always be a terminator when writing. The terminator is not returned when getting 836 the value. 837 @keyword quotechar: (Default=Double Quote) Quote character that surrounds string 838 @keyword readescape: (Default=True) Interpret PPP escape char (0x7d) 839 @keywors writeescape: (Default=False) Escape quotechar. If false, drop quotechar in string. 840 @keyword maxsizeinbytes: (Optional) On writing, truncate strings longer than this (length is before 841 any escaping and quoting 842 @keyword default: (Optional) Our default value 843 @keyword raiseonunterminatedread: (Default True) raise L{NotTerminatedException} if there is 844 no terminator on the value being read in. terminator must also be set. 845 @keyword raiseontruncate: (Default True) raise L{ValueLengthException} if the supplied 846 value is too large to fit within sizeinbytes. 847 @keyword raiseonmissingquotes: (Default True) raise L{MissingQuotesException} if the string does 848 not have quote characters around it 849 @keyword value: (Optional) Value 850 @keyword invalidchars: (Default=quotechar) A string containing invalid 851 characters which would be removed before writing to buffer. 852 @keyword encoding: (Default=None) If specified Unicode charset. 853 @keyword raiseonunicodeerror: (Default=True) raise exception if fail 854 to encode/decode Unicode. 855 """ 856 super(CSVSTRING, self).__init__(*args, **kwargs) 857 858 self._constant=None 859 self._terminator=ord(',') 860 self._quotechar=ord('"') 861 self._readescape=True 862 self._writeescape=False 863 self._maxsizeinbytes=None 864 self._default=None 865 self._raiseonunterminatedread=True 866 self._raiseontruncate=True 867 self._raiseonmissingquotes=True 868 self._invalidchars=chr(self._quotechar) 869 self._value=None 870 self._encoding=None 871 self._raiseonunicodeerror=True 872 873 if self._ismostderived(CSVSTRING): 874 self._update(args,kwargs)
875
876 - def _update(self, args, kwargs):
877 super(CSVSTRING,self)._update(args, kwargs) 878 879 self._consumekw(kwargs, ("constant", "terminator", "quotechar", "readescape", 880 "writeescape", "maxsizeinbytes", "default", 881 "raiseonunterminatedread", "value", 882 "raiseontruncate", "raiseonmissingquotes", 883 "invalidchars")) 884 self._complainaboutunusedargs(CSVSTRING,kwargs) 885 886 # Set our value if one was specified 887 if len(args)==0: 888 pass 889 elif len(args)==1: 890 self._value=common.forceascii(args[0]) 891 if self._constant is not None and self._constant!=self._value: 892 raise ValueException("This field is a constant of '%s'. You tried setting it to '%s'" % (self._constant, self._value)) 893 else: 894 raise TypeError("Unexpected arguments "+`args`) 895 if self._value is None and self._default is not None: 896 self._value=self._default 897 898 if self._value is not None: 899 self._value=str(self._value) # no unicode here! 900 if self._invalidchars: 901 self._value=re.sub(r'[%s]'%self._invalidchars, r'', self._value) 902 if self._maxsizeinbytes is not None: 903 l=len(self._value) 904 if l>self._maxsizeinbytes: 905 if self._raiseontruncate: 906 raise ValueLengthException(l, self._maxsizeinbytes) 907 908 self._value=self._value[:self._maxsizeinbytes]
909
910 - def readfrombuffer(self, buf):
911 self._bufferstartoffset=buf.getcurrentoffset() 912 913 # First character better be a terminator 914 # Raise on, first char not quote, trailing quote never found, character after last quote 915 # not , or EOL. Will need an option to not raise if initial quote not found 916 917 if self._terminator is None: 918 # read in entire rest of packet 919 # Ignore maxsizeinbytes 920 # Check for leading and trailing quotes later 921 self._value=buf.getremainingbytes() 922 else: 923 # Possibly quoted string. If first character is not a quote, read until 924 # terminator or end of line. Exception will be thrown after reading if 925 # the string was not quoted and it was supposed to be. 926 self._value=chr(buf.getnextbyte()) 927 if self._value == ',': 928 self._value = '' 929 else: 930 inquotes=False 931 if self._quotechar is not None: 932 if self._value[0]==chr(self._quotechar): 933 inquotes=True 934 while buf.hasmore(): 935 self._value+=chr(buf.getnextbyte()) 936 if inquotes: 937 if self._value[-1]==chr(self._quotechar): 938 inquotes=False 939 else: 940 if self._value[-1]==chr(self._terminator): 941 break 942 if self._value[-1]==self._terminator: 943 if self._raiseonunterminatedread: 944 raise NotTerminatedException() 945 else: 946 self._value=self._value[:-1] 947 948 if self._quotechar is not None and self._value: 949 if self._value[0]==chr(self._quotechar) and self._value[-1]==chr(self._quotechar): 950 self._value=self._value[1:-1] 951 else: 952 raise MissingQuotesException() 953 954 if self._readescape: 955 self._value=common.pppunescape(self._value) 956 957 if self._constant is not None and self._value!=self._constant: 958 raise ValueException("The value read was not the constant") 959 960 self._bufferendoffset=buf.getcurrentoffset()
961
962 - def writetobuffer(self, buf):
963 # Need to raise exception if string exceeds maxsizeinbytes 964 if self._value is None: 965 raise ValueNotSetException() 966 967 if self._encoding and isinstance(self._value, unicode): 968 try: 969 _value=common.encode_with_degrade(self._value, 970 self._encoding) 971 except UnicodeError: 972 if self._raiseonunicodeerror: 973 raise common.PhoneStringEncodeException(self._value, 974 self._encoding) 975 else: 976 # failed to encode, clear it out 977 _value='' 978 else: 979 _value=self._value 980 981 self._bufferstartoffset=buf.getcurrentoffset() 982 983 if self._quotechar is not None: 984 buf.appendbyte(self._quotechar) 985 buf.appendbytes(_value) 986 if self._quotechar is not None: 987 buf.appendbyte(self._quotechar) 988 if self._terminator is not None: 989 buf.appendbyte(self._terminator) 990 991 self._bufferendoffset=buf.getcurrentoffset()
992
993 - def packetsize(self):
994 if self._sizeinbytes is not None: 995 return self._sizeinbytes 996 997 if self._value is None: 998 raise ValueNotSetException() 999 1000 l=len(self._value) 1001 if self._terminator is not None: 1002 l+=1 1003 1004 return l
1005
1006 - def getvalue(self):
1007 """Returns the string we are""" 1008 if self._value is None: 1009 raise ValueNotSetException() 1010 # convert to unicode if we are not already 1011 if self._encoding and not isinstance(self._value, unicode): 1012 try: 1013 if self._raiseonunicodeerror: 1014 self._value=self._value.decode(self._encoding) 1015 else: 1016 self._value=self._value.decode(self._encoding, 'ignore') 1017 except UnicodeDecodeError: 1018 # this means the codec is set wrong for this phone !! 1019 raise common.PhoneStringDecodeException(self._value, self._encoding) 1020 return self._value
1021
1022 -class CSVINT(CSVSTRING):
1023 """Integers in CSV lines"""
1024 - def __init__(self, *args, **kwargs):
1025 super(CSVINT,self).__init__(*args, **kwargs) 1026 1027 self._quotechar=None 1028 1029 if self._ismostderived(CSVINT): 1030 self._update(args,kwargs)
1031
1032 - def _update(self, args, kwargs):
1033 for k in 'constant', 'default', 'value': 1034 if kwargs.has_key(k): 1035 kwargs[k]=str(kwargs[k]) 1036 if len(args)==0: 1037 pass 1038 elif len(args)==1: 1039 args=(str(args[0]),) 1040 else: 1041 raise TypeError("expected integer as arg") 1042 1043 super(CSVINT,self)._update(args,kwargs) 1044 self._complainaboutunusedargs(CSVINT,kwargs)
1045
1046 - def getvalue(self):
1047 """Convert the string into an integer 1048 1049 @rtype: integer 1050 """ 1051 1052 # Will probably want an flag to allow null strings, converting 1053 # them to a default value 1054 1055 val=super(CSVINT,self).getvalue() 1056 try: 1057 ival=int(val) 1058 except: 1059 try: 1060 ival=int(self._default) 1061 except: 1062 raise ValueException("The field '%s' is not an integer" % (val)) 1063 return ival
1064
1065 -class CSVDATE(CSVSTRING):
1066 """Dates in CSV lines"""
1067 - def __init__(self, *args, **kwargs):
1068 super(CSVDATE,self).__init__(*args, **kwargs) 1069 1070 self._valuedate=(0,0,0) # Year month day 1071 1072 self._quotechar=None 1073 1074 if self._ismostderived(CSVDATE): 1075 self._update(args,kwargs)
1076
1077 - def _update(self, args, kwargs):
1078 for k in 'constant', 'default', 'value': 1079 if kwargs.has_key(k): 1080 kwargs[k]=self._converttostring(kwargs[k]) 1081 if len(args)==0: 1082 pass 1083 elif len(args)==1: 1084 args=(self._converttostring(args[0]),) 1085 else: 1086 raise TypeError("expected (year,month,day) as arg") 1087 1088 super(CSVDATE,self)._update(args, kwargs) # we want the args 1089 self._complainaboutunusedargs(CSVDATE,kwargs)
1090
1091 - def getvalue(self):
1092 """Unpack the string into the date 1093 1094 @rtype: tuple 1095 @return: (year, month, day) 1096 """ 1097 1098 s=super(CSVDATE,self).getvalue() 1099 val=s.split("/") # List of of Month, day, year 1100 if len(val)<2: 1101 year = 0 1102 month = 0 1103 day = 0 1104 else: 1105 year=int(val[2]) 1106 month=int(val[0]) 1107 day=int(val[1]) 1108 return (year, month, day)
1109
1110 - def _converttostring(self, date):
1111 if len(date)>=3: 1112 year,month,day=date[:3] 1113 if month>0 or day>0 or year>0: 1114 s='%2.2d/%2.2d/%4.4d'%(month, day, year) 1115 else: 1116 s="" 1117 else: 1118 s="" 1119 return s
1120 1121
1122 -class CSVTIME(CSVSTRING):
1123 """Timestamp in CSV lines"""
1124 - def __init__(self, *args, **kwargs):
1125 super(CSVTIME,self).__init__(*args, **kwargs) 1126 1127 self._valuetime=(0,0,0,0,0,0) # Year month day, hour, minute, second 1128 1129 self._quotechar=None 1130 1131 if self._ismostderived(CSVTIME): 1132 self._update(args,kwargs)
1133
1134 - def _update(self, args, kwargs):
1135 for k in 'constant', 'default', 'value': 1136 if kwargs.has_key(k): 1137 kwargs[k]=self._converttostring(kwargs[k]) 1138 if len(args)==0: 1139 pass 1140 elif len(args)==1: 1141 args=(self._converttostring(args[0]),) 1142 else: 1143 raise TypeError("expected (year,month,day) as arg") 1144 1145 super(CSVTIME,self)._update(args, kwargs) # we want the args 1146 self._complainaboutunusedargs(CSVTIME,kwargs)
1147
1148 - def getvalue(self):
1149 """Unpack the string into the date 1150 1151 @rtype: tuple 1152 @return: (year, month, day) 1153 """ 1154 1155 s=super(CSVTIME,self).getvalue() 1156 year=int(s[0:4]) 1157 month=int(s[4:6]) 1158 day=int(s[6:8]) 1159 hour=int(s[9:11]) 1160 minute=int(s[11:13]) 1161 second=int(s[13:15]) 1162 return (year, month, day, hour, minute, second)
1163
1164 - def _converttostring(self, time):
1165 if len(time)>=6: 1166 year,month,day,hour,minute,second=time[:6] 1167 s='%4.4d%2.2d%2.2dT%2.2d%2.2d%2.2d'%(year, month, day, hour, minute, second) 1168 else: 1169 s="" 1170 return s
1171 1172
1173 -class COUNTEDBUFFEREDSTRING(BaseProtogenClass):
1174 """A string as used on Audiovox. There is a one byte header saying how long the string 1175 is, followed by the string in a fixed sized buffer"""
1176 - def __init__(self, *args, **kwargs):
1177 """ 1178 A string value can be specified to this constructor, or in the value keyword arg. 1179 1180 @keyword constant: (Optional) A constant value. All reads must have this value 1181 @keyword pad: (Default=32 - space) When writing, what to pad the rest of the buffer with 1182 @keyword default: (Optional) Our default value 1183 @keyword raiseontruncate: (Default True) raise L{ValueLengthException} if the supplied 1184 value is too large to fit within the buffer. 1185 @keyword value: (Optional) Value 1186 @keyword sizeinbytes: (Mandatory) Size of the buffer, including the count byte 1187 """ 1188 super(COUNTEDBUFFEREDSTRING,self).__init__(*args, **kwargs) 1189 1190 self._constant=None 1191 self._pad=32 1192 self._sizeinbytes=None 1193 self._default=None 1194 self._raiseontruncate=True 1195 self._value=None 1196 1197 if self._ismostderived(COUNTEDBUFFEREDSTRING): 1198 self._update(args, kwargs)
1199
1200 - def _update(self, args, kwargs):
1201 super(COUNTEDBUFFEREDSTRING,self)._update(args, kwargs) 1202 1203 self._consumekw(kwargs, ("constant", "pad", "sizeinbytes", "default", "raiseontruncate", "value")) 1204 self._complainaboutunusedargs(COUNTEDBUFFEREDSTRING,kwargs) 1205 # Set our value if one was specified 1206 if len(args)==0: 1207 pass 1208 elif len(args)==1: 1209 self._value=str(args[0]) 1210 if self._constant is not None and self._constant!=self._value: 1211 raise ValueException("This field is a constant of '%s'. You tried setting it to '%s'" % (self._constant, self._value)) 1212 else: 1213 raise TypeError("Unexpected arguments "+`args`) 1214 if self._value is None and self._default is not None: 1215 self._value=self._default 1216 1217 if self._sizeinbytes is None: 1218 raise ValueException("sizeinbytes must be specified for COUNTEDBUFFEREDSTRING") 1219 1220 if self._value is not None: 1221 l=len(self._value) 1222 if l>self._sizeinbytes-1: 1223 if self._raiseontruncate: 1224 raise ValueLengthException(l, self._sizeinbytes-1) 1225 1226 self._value=self._value[:self._sizeinbytes-1]
1227
1228 - def readfrombuffer(self, buf):
1229 assert self._sizeinbytes is not None 1230 self._bufferstartoffset=buf.getcurrentoffset() 1231 1232 strlen=buf.getnextbyte() 1233 if strlen>self._sizeinbytes-1: 1234 raise ValueException("counter specifies size of %d which is greater than remaining stringbuffer size of %d!" % (strlen, self._sizeinbytes-1)) 1235 self._value=buf.getnextbytes(self._sizeinbytes-1) # -1 due to counter byte 1236 self._value=self._value[:strlen] 1237 if self._constant is not None and self._value!=self._constant: 1238 raise ValueException("The value read was not the constant") 1239 1240 self._bufferendoffset=buf.getcurrentoffset()
1241
1242 - def writetobuffer(self, buf):
1243 assert self._sizeinbytes is not None 1244 if self._value is None: 1245 raise ValueNotSetException() 1246 1247 self._bufferstartoffset=buf.getcurrentoffset() 1248 buf.appendbyte(len(self._value)) 1249 buf.appendbytes(self._value) 1250 if len(self._value)+1<self._sizeinbytes: 1251 buf.appendbytes(chr(self._pad)*(self._sizeinbytes-1-len(self._value))) 1252 1253 self._bufferendoffset=buf.getcurrentoffset()
1254
1255 - def packetsize(self):
1256 assert self._sizeinbytes is not None 1257 return self._sizeinbytes
1258
1259 - def getvalue(self):
1260 """Returns the string we are""" 1261 if self._value is None: 1262 raise ValueNotSetException() 1263 return self._value
1264
1265 -class DATA(BaseProtogenClass):
1266 "A block of bytes"
1267 - def __init__(self, *args, **kwargs):
1268 """ 1269 A data value can be specified to this constructor or in the value keyword arg 1270 1271 @keyword constant: (Optional) A constant value. All reads must have this value 1272 @keyword pad: (Default=0) The padding byte if fixed length when writing and the 1273 value isn't long enough 1274 @keyword sizeinbytes: (Optional) Set if fixed length. 1275 If not set, then the rest of the packet will be consumed on reads. 1276 @keyword default: (Optional) Our default value 1277 @keyword raiseonwrongsize: (Default True) raise L{ValueLengthException} if the supplied 1278 value is too large to fit within sizeinbytes. 1279 """ 1280 super(DATA, self).__init__(*args, **kwargs) 1281 1282 self._constant=None 1283 self._pad=0 1284 self._sizeinbytes=None 1285 self._default=None 1286 self._raiseonwrongsize=True 1287 self._value=None 1288 1289 if self._ismostderived(DATA): 1290 self._update(args,kwargs)
1291
1292 - def _update(self, args, kwargs):
1293 super(DATA,self)._update(args, kwargs) 1294 1295 self._consumekw(kwargs, ("constant", "pad", "sizeinbytes", "default", "raiseonwrongsize", "value")) 1296 self._complainaboutunusedargs(DATA,kwargs) 1297 1298 # Set our value if one was specified 1299 if len(args)==0: 1300 pass 1301 elif len(args)==1: 1302 self._value=args[0] 1303 if self._constant is not None and self._constant!=self._value: 1304 raise ValueException("This field is a constant and you set it to a different value") 1305 else: 1306 raise TypeError("Unexpected arguments "+`args`) 1307 if self._value is None and self._default is not None: 1308 self._value=self._default 1309 1310 if self._value is not None: 1311 if self._sizeinbytes is not None: 1312 l=len(self._value) 1313 if l<self._sizeinbytes: 1314 if self._pad is not None: 1315 self._value+=chr(self._pad)*(self._sizeinbytes-l) 1316 1317 l=len(self._value) 1318 1319 if l!=self._sizeinbytes: 1320 if self._raiseonwrongsize: 1321 raise ValueLengthException(l, self._sizeinbytes) 1322 else: 1323 self._value=self._value[:self._sizeinbytes]
1324 1325
1326 - def readfrombuffer(self, buf):
1327 self._bufferstartoffset=buf.getcurrentoffset() 1328 1329 if self._sizeinbytes is not None: 1330 # fixed size 1331 self._value=buf.getnextbytes(self._sizeinbytes) 1332 else: 1333 # read in entire rest of packet 1334 self._value=buf.getremainingbytes() 1335 1336 if self._constant is not None and self._value!=self._constant: 1337 raise ValueException("The value read was not the constant") 1338 self._bufferendoffset=buf.getcurrentoffset()
1339
1340 - def writetobuffer(self, buf):
1341 if self._value is None: 1342 raise ValueNotSetException() 1343 1344 self._bufferstartoffset=buf.getcurrentoffset() 1345 buf.appendbytes(self._value) 1346 self._bufferendoffset=buf.getcurrentoffset()
1347
1348 - def packetsize(self):
1349 if self._sizeinbytes is not None: 1350 return self._sizeinbytes 1351 1352 if self._value is None: 1353 raise ValueNotSetException() 1354 1355 l=len(self._value) 1356 1357 return l
1358
1359 - def getvalue(self):
1360 """Returns the bytes we are""" 1361 if self._value is None: 1362 raise ValueNotSetException() 1363 return self._value
1364
1365 -class DONTCARE(BaseProtogenClass):
1366 "Block of bytes that we don't know and don't care for"
1367 - def __init__(self, *args, **kwargs):
1368 """ 1369 A data value can be specified to this constructor or in the value keyword arg 1370 1371 @keyword sizeinbytes: Length of block 1372 If not set, then the rest of the packet will be consumed on reads. 1373 @keyword default: (Optional) Our default value, could be either a char or string. 1374 @keyword storage: (Optional) True or False: whether to store the data or ignore. 1375 """ 1376 super(DONTCARE, self).__init__(*args, **kwargs) 1377 1378 self._sizeinbytes=None 1379 self._default='\x00' 1380 self._storage=False 1381 self._value=None 1382 1383 if self._ismostderived(DONTCARE): 1384 self._update(args,kwargs)
1385
1386 - def _update(self, args, kwargs):
1387 super(DONTCARE, self)._update(args, kwargs) 1388 1389 self._consumekw(kwargs, ("sizeinbytes", "default", "storage", "value")) 1390 self._complainaboutunusedargs(DONTCARE, kwargs) 1391 1392 # Set our value if one was specified 1393 if len(args)==0: 1394 pass 1395 elif len(args)==1: 1396 if self._storage: 1397 self._value=args[0] 1398 else: 1399 raise TypeError("Unexpected arguments "+`args`) 1400 # must specify the length of the block 1401 if self._sizeinbytes is None: 1402 raise ValueException("sizeinbytes must be specified for DONTCARE") 1403 # check for valid length 1404 if self._storage and self._value is not None and \ 1405 len(self._value)!=self._sizeinbytes: 1406 raise ValueLengthException(len(self._value), self._sizeinbytes) 1407 if len(self._default)>1 and len(self._default)!=self._sizeinbytes: 1408 raise ValueLengthException(len(self._default), self._sizeinbytes)
1409
1410 - def readfrombuffer(self, buf):
1411 self._bufferstartoffset=buf.getcurrentoffset() 1412 if self._storage: 1413 # read & store 1414 self._value=buf.getnextbytes(self._sizeinbytes) 1415 else: 1416 # just ignore it 1417 buf.discardnextbytes(self._sizeinbytes) 1418 self._bufferendoffset=buf.getcurrentoffset()
1419
1420 - def writetobuffer(self, buf):
1421 self._bufferstartoffset=buf.getcurrentoffset() 1422 buf.appendbytes(self.getvalue()) 1423 self._bufferendoffset=buf.getcurrentoffset()
1424
1425 - def packetsize(self):
1426 return self._sizeinbytes
1427
1428 - def getvalue(self):
1429 """Returns the bytes we are""" 1430 if self._value is not None: 1431 return self._value 1432 elif len(self._default)>1: 1433 return self._default 1434 else: 1435 return self._default*self._sizeinbytes
1436
1437 -class UNKNOWN(DATA):
1438 "A block of bytes whose purpose we don't know" 1439
1440 - def __init__(self, *args, **kwargs):
1441 """ 1442 Same arguments as L{DATA.__init__}. We default to a block 1443 of pad chars (usually \x00) 1444 """ 1445 dict={'pad':0 , 'default': ""} 1446 dict.update(kwargs) 1447 super(UNKNOWN,self).__init__(*args, **dict) 1448 1449 if self._ismostderived(UNKNOWN): 1450 self._update(args,dict)
1451
1452 - def _update(self, args, kwargs):
1453 super(UNKNOWN,self)._update(args, kwargs) 1454 self._complainaboutunusedargs(UNKNOWN,kwargs) 1455 1456 # Was a value specified? 1457 if len(args)==1: 1458 self._value=args[0] 1459 elif len(args)>1: 1460 raise TypeError("Unexpected arguments "+`args`)
1461
1462 -class LIST(BaseProtogenClass):
1463 """A list of items 1464 1465 You can generally treat this class as though it is a list. Note that some 1466 list like methods haven't been implemented (there are so darn many!) If you 1467 are missing one you want to use, please add it to this class. 1468 """ 1469
1470 - def __init__(self, *args, **kwargs):
1471 """ 1472 You can pass objects to start the list with, or to the value keyword arg 1473 1474 @keyword createdefault: (Default False) Creates default members of the list if enough 1475 were not supplied before writing. 1476 @keyword length: (Optional) How many items there are in the list 1477 @keyword raiseonbadlength: (Default True) raises L{ValueLengthException} if there are 1478 the wrong number of items in the list. Note that this checking is only done 1479 when writing or reading from a buffer. length must be set for this to have any 1480 effect. If you have createdefault set then having less than length elements will 1481 not cause the exception. 1482 @keyword elementclass: (Mandatory) The class of each element 1483 @keyword elementinitkwargs: (Optional) KWargs for the constructor of each element 1484 @keyword value: (Optional) Value 1485 """ 1486 self._thelist=[] 1487 super(LIST, self).__init__(*args, **kwargs) 1488 self._createdefault=False 1489 self._length=None 1490 self._raiseonbadlength=True 1491 self._raiseonincompleteread=True 1492 self._elementclass=None 1493 self._elementinitkwargs={} 1494 1495 if self._ismostderived(LIST): 1496 self._update(args,kwargs)
1497
1498 - def _update(self, args, kwargs):
1499 super(LIST,self)._update(args, kwargs) 1500 self._consumekw(kwargs, ("createdefault","length","raiseonbadlength","elementclass","elementinitkwargs","raiseonincompleteread")) 1501 if kwargs.has_key("value"): 1502 self._thelist=list(kwargs['value']) 1503 del kwargs['value'] 1504 1505 self._complainaboutunusedargs(LIST,kwargs) 1506 1507 if self._elementclass is None: 1508 raise TypeError("elementclass argument was not supplied") 1509 1510 if len(args): 1511 self.extend(args)
1512
1513 - def readfrombuffer(self,buf):
1514 self._bufferstartoffset=buf.getcurrentoffset() 1515 # delete all existing items 1516 self._thelist=[] 1517 1518 if self._length is None: 1519 # read rest of buffer 1520 while buf.hasmore(): 1521 x=self._makeitem() 1522 x.readfrombuffer(buf) 1523 self._thelist.append(x) 1524 else: 1525 for dummy in range(self._length): 1526 # read specified number of items 1527 x=self._makeitem() 1528 try: 1529 x.readfrombuffer(buf) 1530 except IndexError: 1531 if self._raiseonincompleteread: 1532 raise IndexError("tried to read too many list items") 1533 self._thelist.append(x) 1534 1535 self._bufferendoffset=buf.getcurrentoffset()
1536
1537 - def writetobuffer(self, buf):
1538 self._bufferstartoffset=buf.getcurrentoffset() 1539 1540 self._ensurelength() 1541 for i in self: 1542 i.writetobuffer(buf) 1543 1544 self._bufferendoffset=buf.getcurrentoffset()
1545
1546 - def packetsize(self):
1547 self._ensurelength() 1548 sz=0 1549 for item in self: 1550 sz+=item.packetsize() 1551 return sz
1552
1553 - def iscontainer(self):
1554 return True
1555
1556 - def containerelements(self):
1557 self._ensurelength() 1558 for i,v in enumerate(self._thelist): 1559 yield "["+`i`+"]",v,None
1560 1561 1562 # Provide various list methods. I initially tried just subclassing list, 1563 # but it was impossible to tell which methods needed to be reimplemented. 1564 # I was concerned about double conversions. 1565 # For example if my append turned the item into elementclass 1566 # and then the builtin list append called setitem to do the append, which I 1567 # also override so it then converts the elementclass into another 1568 # elementclass.
1569 - def append(self, item):
1570 self._thelist.append(self._makeitem(item))
1571
1572 - def extend(self, items):
1573 self._thelist.extend(map(self._makeitem, items))
1574
1575 - def insert(self, index, item):
1576 self._thelist.insert(index, self._makeitem(item))
1577
1578 - def __getitem__(self, index):
1579 return self._thelist[index]
1580
1581 - def __iter__(self):
1582 try: 1583 return self._thelist.__iter__() 1584 except: 1585 return self.__fallbackiter()
1586
1587 - def __fallbackiter(self):
1588 # used for Python 2.2 which doesn't have list.__iter__ 1589 for item in self._thelist: 1590 yield item
1591
1592 - def __len__(self):
1593 return self._thelist.__len__()
1594
1595 - def __setitem__(self, index, value):
1596 self._thelist.__setitem__(index, self._makeitem(value))
1597
1598 - def __delitem__(self, index):
1599 self._thelist.__delitem__(index)
1600 1601
1602 - def _makeitem(self, *args, **kwargs):
1603 "Creates a child element" 1604 # if already of the type, then return it 1605 if len(args)==1 and isinstance(args[0], self._elementclass): 1606 return args[0] 1607 d={} 1608 d.update(self._elementinitkwargs) 1609 d.update(kwargs) 1610 return self._elementclass(*args, **d)
1611
1612 - def _ensurelength(self):
1613 "Ensures we are the correct length" 1614 if self._createdefault and self._length is not None and len(self._thelist)<self._length: 1615 while len(self._thelist)<self._length: 1616 x=self._makeitem() 1617 self._thelist.append(x) 1618 return 1619 if self._length is not None and self._raiseonbadlength and len(self._thelist)!=self._length: 1620 raise ValueLengthException(len(self), self._length)
1621
1622 -class buffer(object):
1623 "This is used for reading and writing byte data"
1624 - def __init__(self, data=None):
1625 "Call with data to read from it, or with None to write to it" 1626 self.reset(data)
1627
1628 - def reset(self, data=None):
1629 "Call with data to read from it, or with None to write to it" 1630 if data is None: 1631 self._buffer=cStringIO.StringIO() 1632 else: 1633 self._data=data 1634 1635 self._offset=0
1636
1637 - def getcurrentoffset(self):
1638 "Returns distance into data we are" 1639 return self._offset
1640 - def setcurrentoffset(self, ofs):
1641 "Set the current offset" 1642 if ofs>len(self._data): 1643 raise IndexError('Trying to set offset beyond end of %d byte buffer'%len(self._data)) 1644 self._offset=ofs
1645 offset=property(fget=getcurrentoffset, fset=setcurrentoffset) 1646
1647 - def peeknextbyte(self, howmuch=0):
1648 "Returns value of next byte, but doesn't advance position" 1649 if self._offset+howmuch>=len(self._data): 1650 return None 1651 return ord(self._data[self._offset+howmuch])
1652
1653 - def getnextbyte(self):
1654 "Returns next byte" 1655 if self._offset>=len(self._data): 1656 raise IndexError("trying to read one byte beyond end of "+`len(self._data)`+" byte buffer") 1657 res=ord(self._data[self._offset]) 1658 self._offset+=1 1659 return res
1660
1661 - def getnextbytes(self, howmany):
1662 "Returns howmany bytes" 1663 assert howmany>=0 1664 if self._offset+howmany>len(self._data): 1665 raise IndexError("Trying to read "+`howmany`+" bytes starting at "+`self._offset`+" which will go beyond end of "+`len(self._data)`+" byte buffer") 1666 res=self._data[self._offset:self._offset+howmany] 1667 self._offset+=howmany 1668 return res
1669
1670 - def peeknextbytes(self, howmany):
1671 if self._offset+howmany>len(self._data): 1672 return None 1673 return self._data[self._offset:self._offset+howmany]
1674
1675 - def getremainingbytes(self):
1676 "Returns rest of buffer" 1677 sz=len(self._data)-self._offset 1678 return self.getnextbytes(sz)
1679
1680 - def discardnextbytes(self, howmany):
1681 "Discards howmany bytes" 1682 if self._offset+howmany>len(self._data): 1683 raise IndexError("Trying to discard "+`howmany`+" bytes starting at "+`self._offset`+" which will go beyond end of "+`len(self._data)`+" byte buffer") 1684 self._offset+=howmany
1685
1686 - def discardremainingbytes(self):
1687 "Discards the rest of the buffer" 1688 self._offset=len(self._data)
1689
1690 - def hasmore(self):
1691 "Is there any data left?" 1692 return self._offset<len(self._data)
1693
1694 - def howmuchmore(self):
1695 "Returns how many bytes left" 1696 return len(self._data)-self._offset
1697
1698 - def appendbyte(self, val):
1699 """Appends byte to data. 1700 @param val: a number 0 <= val <=255 1701 """ 1702 assert val>=0 and val<=255 1703 self._buffer.write(chr(val)) 1704 self._offset+=1 1705 assert self._offset==len(self._buffer.getvalue())
1706
1707 - def appendbytes(self, bytes):
1708 "Adds bytes to end" 1709 self._buffer.write(bytes) 1710 self._offset+=len(bytes) 1711 assert self._offset==len(self._buffer.getvalue())
1712
1713 - def getvalue(self):
1714 "Returns the buffer being built" 1715 return self._buffer.getvalue()
1716
1717 - def getdata(self):
1718 "Returns the data passed in" 1719 return self._data
1720