0001 ### BITPIM 0002 ### 0003 ### Copyright (C) 2003-2005 Roger Binns <rogerb@rogerbinns.com> 0004 ### 0005 ### This program is free software; you can redistribute it and/or modify 0006 ### it under the terms of the BitPim license as detailed in the LICENSE file. 0007 ### 0008 ### $Id: prototypes.py 4660 2008-08-03 17:23:40Z hjelmn $ 0009 0010 """The various types used in protocol descriptions 0011 0012 To implement a type used for protocol descriptions, 0013 examine the code for UINTlsb in this file. Of note: 0014 0015 - Inherit from BaseProtogenClass 0016 - Call superclass constructors using super() 0017 - Do not process any of the args or kwargs in the constructor unless 0018 you need them to alter how you are constructed 0019 - At the end of the constructor, call _update if you are the most 0020 derived class 0021 - In _update, call super()._update, and then delete keyword arguments 0022 as you process them (consider using L{BaseProtogenClass._consumekw} function) 0023 - If you are the most derived class, complain about 0024 unused keyword arguments (consider using 0025 L{BaseProtogenClass._complainaboutunusedargs} function) 0026 - set _bufferstartoffset and _bufferendoffset whenever 0027 you are read or written from a buffer 0028 - (optionally) define a getvalue() method that returns 0029 a better type. For example if your class is integer 0030 like then this would return a real int. If string like, 0031 then this will return a real string. 0032 - If you are a container, override iscontainer. You will 0033 also need to provide a containerelements() method which 0034 lets you iterate over the entries. 0035 0036 containerelements method: 0037 0038 - You should return tuples of (fieldname, fieldvalue, descriptionstring or None) 0039 - fieldvalue should be the actual object, not a pretty version (eg a USTRING not str) 0040 0041 0042 """ 0043 import sys 0044 import calendar 0045 import cStringIO 0046 import re 0047 import time 0048 0049 import common 0050 0051 class ProtogenException(Exception): 0052 """Base class for exceptions encountered with data marshalling""" 0053 def __init__(self, *args, **kwargs): 0054 Exception.__init__(self, *args, **kwargs) 0055 0056 class SizeNotKnownException(ProtogenException): 0057 "Unable to marshal since size isn't known" 0058 def __init__(self): 0059 ProtogenException.__init__(self, "The size of this item is not known and hence cannot be en/decoded") 0060 0061 class ValueNotSetException(ProtogenException): 0062 "Value not been set" 0063 def __init__(self): 0064 ProtogenException.__init__(self, "The value for this object has not been set.") 0065 0066 class ValueException(ProtogenException): 0067 "Some sort of problem with the value" 0068 def __init__(self, str): 0069 ProtogenException.__init__(self,str) 0070 0071 class NotTerminatedException(ProtogenException): 0072 "The value should have been terminated and wasn't" 0073 def __init__(self): 0074 ProtogenException.__init__(self,"The value should have been terminated and wasn't") 0075 0076 class ValueLengthException(ProtogenException): 0077 "The value is the wrong size (too big or too small)" 0078 def __init__(self, sz, space): 0079 ProtogenException.__init__(self, "The value (length %d) is the wrong size for space %d" % (sz,space)) 0080 0081 class MissingQuotesException(ProtogenException): 0082 "The value does not have quotes around it" 0083 def __init__(self): 0084 ProtogenException.__init__(self, "The value does not have the required quote characters around it") 0085 0086 0087 class BaseProtogenClass(object): 0088 """All types are derived from this""" 0089 def packetsize(self): 0090 "Returns size in bytes that we occupy" 0091 # This is implemented by writing to a buffer and seeing how big the result is. 0092 # The code generator used to make one per class but it was so rarely used (twice) 0093 # that this implementation is available instead 0094 b=buffer() 0095 self.writetobuffer(b) 0096 return len(b.getvalue()) 0097 0098 def writetobuffer(self, buf): 0099 "Scribble ourselves to the buf" 0100 raise NotImplementedError("writetobuffer()") 0101 def readfrombuffer(self, buf): 0102 "Get our value from the buffer" 0103 raise NotImplementedError("readfrombuffer()") 0104 def getvalue(self): 0105 "Returns our underlying value if sensible (eg an integer, string or list) else returns self" 0106 return self 0107 def packetspan(self): 0108 """Returns tuple of begining,end offsets from last packet we were read or written from. 0109 0110 Note that in normal Python style, end is one beyond the last byte we 0111 actually own""" 0112 return self._bufferstartoffset, self._bufferendoffset 0113 def _consumekw(self, dict, consumelist): 0114 """A helper function for easily setting internal values from the dict 0115 0116 For each name in consumelist, we look for it in the dict and 0117 set self._name to the value from dict. The key is then deleted 0118 from the dict.""" 0119 for name in consumelist: 0120 if dict.has_key(name): 0121 setattr(self, "_"+name, dict[name]) 0122 del dict[name] 0123 def _complainaboutunusedargs(self, klass, dict): 0124 """A helper function that will raise an exception if there are unused keyword arguments. 0125 0126 @Note: that we only complain if in the most derived class, so it is safe 0127 to always call this helper as the last line of your constructor. 0128 0129 @param klass: This should be the class you are calling this function from 0130 @param dict: The keyword arguments still in play 0131 """ 0132 if len(dict) and self.__class__.__mro__[0]==klass: 0133 raise TypeError('Unexpected keyword args supplied: '+`dict`) 0134 0135 def _ismostderived(self, klass): 0136 0137 return self.__class__.__mro__[0]==klass 0138 0139 def _update(self, args, kwargs): 0140 return 0141 0142 def iscontainer(self): 0143 """Do we contain fields?""" 0144 return False 0145 0146 def update(self, *args, **kwargs): 0147 self._update(args, kwargs) 0148 0149 # help with logging 0150 def autologwrite(self, buf, logtitle="<written data>"): 0151 f=sys._getframe() # my frame 0152 f=f.f_back # my caller 0153 if f: 0154 f=f.f_back # their caller 0155 if f: 0156 caller=f.f_locals.get("self", None) 0157 if caller: 0158 try: 0159 caller.logdata(logtitle, buf.getvalue(), self) 0160 except: 0161 pass 0162 0163 def autologread(self, buf, logtitle="<read data>"): 0164 f=sys._getframe() # my frame 0165 f=f.f_back # my caller 0166 if f: 0167 f=f.f_back # their caller 0168 if f: 0169 caller=f.f_locals.get("self", None) 0170 if caller: 0171 try: 0172 caller.logdata(logtitle, buf.getdata(), self) 0173 except: 0174 pass 0175 0176 class UINTlsb(BaseProtogenClass): 0177 "An integer in Least Significant Byte first order" 0178 def __init__(self, *args, **kwargs): 0179 """ 0180 An integer value can be specified in the constructor, or as the value keyword arg. 0181 0182 @keyword constant: (Optional) A constant value. All reads must have this value 0183 @keyword constantexception: (Optional) Type of exception raised when data doesn't match constant. 0184 @keyword sizeinbytes: (Mandatory for writing, else Optional) How big we are in bytes 0185 @keyword default: (Optional) Our default value 0186 @keyword value: (Optional) The value 0187 """ 0188 super(UINTlsb, self).__init__(*args, **kwargs) 0189 self._constant=None 0190 self._constantexception=ValueError 0191 self._sizeinbytes=None 0192 self._value=None 0193 self._default=None 0194 0195 if self._ismostderived(UINTlsb): 0196 self._update(args,kwargs) 0197 0198 0199 def _update(self, args, kwargs): 0200 super(UINTlsb,self)._update(args, kwargs) 0201 0202 self._consumekw(kwargs, ("constant", "constantexception", "sizeinbytes", "default", "value")) 0203 self._complainaboutunusedargs(UINTlsb,kwargs) 0204 0205 # Set our value if one was specified 0206 if len(args)==0: 0207 pass 0208 elif len(args)==1: 0209 self._value=int(args[0]) 0210 else: 0211 raise TypeError("Unexpected arguments "+`args`) 0212 0213 if self._value is None and self._default is not None: 0214 self._value=self._default 0215 0216 if self._value is None and self._constant is not None: 0217 self._value=self._constant 0218 0219 if self._constant is not None and self._constant!=self._value: 0220 raise self._constantexception("This field is a constant of %d. You tried setting it to %d" % (self._constant, self._value)) 0221 0222 0223 def readfrombuffer(self, buf): 0224 if self._sizeinbytes is None: 0225 raise SizeNotKnownException() 0226 self._bufferstartoffset=buf.getcurrentoffset() 0227 # lsb read 0228 res=0 0229 shift=0 0230 for dummy in range(self._sizeinbytes): 0231 res|=buf.getnextbyte()<<shift 0232 shift+=8 0233 self._value=res 0234 self._bufferendoffset=buf.getcurrentoffset() 0235 if self._constant is not None and self._value!=self._constant: 0236 raise self._constantexception("The value read should be a constant of %d, but was %d instead" % (self._constant, self._value)) 0237 0238 def writetobuffer(self, buf): 0239 if self._sizeinbytes is None: 0240 raise SizeNotKnownException() 0241 if self._value is None: 0242 raise ValueNotSetException() 0243 0244 self._bufferstartoffset=buf.getcurrentoffset() 0245 # lsb write 0246 res=self._value 0247 for dummy in range(self._sizeinbytes): 0248 buf.appendbyte(res&0xff) 0249 res>>=8 0250 self._bufferendoffset=buf.getcurrentoffset() 0251 0252 def packetsize(self): 0253 if self._sizeinbytes is None: 0254 raise SizeNotKnownException() 0255 return self._sizeinbytes 0256 0257 def getvalue(self): 0258 """Returns the integer we are""" 0259 if self._value is None: 0260 raise ValueNotSetException() 0261 return self._value 0262 0263 class BOOLlsb(UINTlsb): 0264 "An Boolean in Least Significant Byte first order" 0265 def __init__(self, *args, **kwargs): 0266 """ 0267 A boolean value can be specified in the constructor. 0268 0269 Keyword arguments are the same a UINTlsb 0270 """ 0271 super(BOOLlsb, self).__init__(*args, **kwargs) 0272 0273 if self._ismostderived(BOOLlsb): 0274 self._update(args,kwargs) 0275 0276 def _update(self, args, kwargs): 0277 super(BOOLlsb,self)._update(args,kwargs) 0278 self._complainaboutunusedargs(BOOLlsb,kwargs) 0279 self._boolme() 0280 0281 def _boolme(self): 0282 if self._value is not None: 0283 self._value=bool(self._value) 0284 0285 def readfrombuffer(self, buf): 0286 UINTlsb.readfrombuffer(self,buf) 0287 self._boolme() 0288 0289 class STRING(BaseProtogenClass): 0290 "A text string DEPRECATED USE USTRING " 0291 def __init__(self, *args, **kwargs): 0292 """ 0293 A string value can be specified to this constructor, or in the value keyword arg. 0294 0295 @keyword constant: (Optional) A constant value. All reads must have this value 0296 @keyword terminator: (Default=0) The string terminator (or None). If set there will 0297 always be a terminator when writing. The terminator is not returned when getting 0298 the value. 0299 @keyword pad: (Default=0) The padding byte if fixed length when writing, or stripped off 0300 when reading 0301 @keyword sizeinbytes: (Optional) Set if fixed length. 0302 If not set, then the terminator will be used to find the end of strings on reading. 0303 If not set and the terminator is None, then reads will be entire rest of buffer. 0304 @keyword maxsizeinbytes: (Optional) Max string length. Used together 0305 with terminator to limit the max length of the value. 0306 TODO: Need to add this to USTRING also. 0307 @keyword default: (Optional) Our default value 0308 @keyword raiseonunterminatedread: (Default True) raise L{NotTerminatedException} if there is 0309 no terminator on the value being read in. terminator must also be set. 0310 @keyword raiseontruncate: (Default True) raise L{ValueLengthException} if the supplied 0311 value is too large to fit within sizeinbytes. 0312 @keyword value: (Optional) Value 0313 @keyword pascal: (Default False) The string is preceded with one byte giving the length 0314 of the string (including terminator if there is one) 0315 """ 0316 super(STRING, self).__init__(*args, **kwargs) 0317 0318 self._constant=None 0319 self._terminator=0 0320 self._pad=0 0321 self._sizeinbytes=None 0322 self._default=None 0323 self._raiseonunterminatedread=True 0324 self._raiseontruncate=True 0325 self._value=None 0326 self._pascal=False 0327 self._maxsizeinbytes=None 0328 0329 if self._ismostderived(STRING): 0330 self._update(args,kwargs) 0331 0332 def _update(self, args, kwargs): 0333 super(STRING,self)._update(args, kwargs) 0334 0335 self._consumekw(kwargs, ("constant", "terminator", "pad", "pascal", 0336 "sizeinbytes", "default", "raiseonunterminatedread", "value", 0337 "raiseontruncate", "maxsizeinbytes")) 0338 self._complainaboutunusedargs(STRING,kwargs) 0339 0340 # Set our value if one was specified 0341 if len(args)==0: 0342 pass 0343 elif len(args)==1: 0344 self._value=common.forceascii(args[0]) 0345 if self._constant is not None and self._constant!=self._value: 0346 raise ValueException("This field is a constant of '%s'. You tried setting it to '%s'" % (self._constant, self._value)) 0347 else: 0348 raise TypeError("Unexpected arguments "+`args`) 0349 if self._value is None and self._default is not None: 0350 self._value=self._default 0351 0352 if self._value is not None: 0353 self._value=str(self._value) # no unicode here! 0354 l=len(self._value) 0355 if self._sizeinbytes is not None: 0356 if self._terminator is not None: 0357 l+=1 0358 if l>self._sizeinbytes: 0359 if self._raiseontruncate: 0360 raise ValueLengthException(l, self._sizeinbytes) 0361 0362 self._value=self._value[:self._sizeinbytes] 0363 if len(self._value) and self._terminator is not None: 0364 self._value=self._value[:-1] 0365 elif self._maxsizeinbytes is not None: 0366 if l>self._maxsizeinbytes: 0367 if self._raiseontruncate: 0368 raise ValueLengthException(l, self._maxsizeinbytes) 0369 self._value=self._value[:self._maxsizeinbytes] 0370 0371 def readfrombuffer(self, buf): 0372 self._bufferstartoffset=buf.getcurrentoffset() 0373 0374 flush=0 0375 if self._pascal: 0376 if self._sizeinbytes is None: 0377 self._sizeinbytes=buf.getnextbyte() 0378 # allow for fixed size pascal strings 0379 else: 0380 temp=self._sizeinbytes-1 0381 self._sizeinbytes=buf.getnextbyte() 0382 flush=temp-self._sizeinbytes 0383 if(temp < 0): 0384 raise ValueLengthException() 0385 0386 if self._sizeinbytes is not None: 0387 # fixed size 0388 self._value=buf.getnextbytes(self._sizeinbytes) 0389 if self._terminator is not None: 0390 # using a terminator 0391 pos=self._value.find(chr(self._terminator)) 0392 if pos>=0: 0393 self._value=self._value[:pos] 0394 elif self._raiseonunterminatedread: 0395 raise NotTerminatedException() 0396 elif self._pad is not None: 0397 # else remove any padding 0398 while len(self._value) and self._value[-1]==chr(self._pad): 0399 self._value=self._value[:-1] 0400 else: 0401 if self._terminator is None: 0402 # read in entire rest of packet 0403 self._value=buf.getremainingbytes() 0404 else: 0405 # read up to terminator 0406 self._value="" 0407 while buf.hasmore(): 0408 self._value+=chr(buf.getnextbyte()) 0409 if self._value[-1]==chr(self._terminator): 0410 break 0411 if self._value[-1]!=chr(self._terminator): 0412 if self._raiseonunterminatedread: 0413 raise NotTerminatedException() 0414 else: 0415 self._value=self._value[:-1] 0416 0417 if self._maxsizeinbytes is not None: 0418 self._value=self._value[:self._maxsizeinbytes] 0419 0420 if self._constant is not None and self._value!=self._constant: 0421 raise ValueException("The value read was not the constant") 0422 0423 # for fixed size pascal strings flush the remainder of the buffer 0424 if(flush): 0425 buf.getnextbytes(flush) 0426 0427 self._bufferendoffset=buf.getcurrentoffset() 0428 0429 def writetobuffer(self, buf): 0430 if self._value is None: 0431 raise ValueNotSetException() 0432 0433 self._bufferstartoffset=buf.getcurrentoffset() 0434 # calculate length 0435 l=len(self._value) 0436 if self._terminator is not None: 0437 l+=1 0438 if self._pascal: 0439 buf.appendbyte(l) 0440 l+=1 0441 buf.appendbytes(self._value) 0442 if self._terminator is not None: 0443 buf.appendbyte(self._terminator) 0444 if self._sizeinbytes is not None: 0445 if l<self._sizeinbytes: 0446 buf.appendbytes(chr(self._pad)*(self._sizeinbytes-l)) 0447 0448 self._bufferendoffset=buf.getcurrentoffset() 0449 0450 def packetsize(self): 0451 if self._sizeinbytes is not None: 0452 return self._sizeinbytes 0453 0454 if self._value is None: 0455 raise ValueNotSetException() 0456 0457 l=len(self._value) 0458 if self._terminator is not None: 0459 l+=1 0460 if self._pascal: 0461 l+=1 0462 return l 0463 0464 def getvalue(self): 0465 """Returns the string we are""" 0466 if self._value is None: 0467 raise ValueNotSetException() 0468 return self._value 0469 0470 0471 class USTRING(BaseProtogenClass): 0472 "A text string that supports configurable encodings" 0473 def __init__(self, *args, **kwargs): 0474 """ 0475 A string value can be specified to this constructor, or in the value keyword arg. 0476 0477 @keyword constant: (Optional) A constant value. All reads must have this value 0478 @keyword terminator: (Default=0) The string terminator (or None). If set there will 0479 always be a terminator when writing. The terminator is not returned when getting 0480 the value. 0481 @keyword terminator_length: (Default=1) (min:1, max:4)The length of the string terminator. 0482 This keyword is not used if the terminator is None. Multi-byte terminators are treated 0483 as LSB when read from the phone. 0484 @keyword pad: (Default=0) The padding byte if fixed length when writing, or stripped off 0485 when reading 0486 @keyword sizeinbytes: (Optional) Set if fixed length. 0487 If not set, then the terminator will be used to find the end of strings on reading. 0488 If not set and the terminator is None, then reads will be entire rest of buffer. 0489 @keyword maxsizeinbytes: (Optional) Max string length. Used together 0490 with terminator to limit the max length of the value. 0491 @keyword default: (Optional) Our default value 0492 @keyword raiseonunterminatedread: (Default True) raise L{NotTerminatedException} if there is 0493 no terminator on the value being read in. Terminator must also be set. 0494 @keyword raiseontruncate: (Default True) raise L{ValueLengthException} if the supplied 0495 value is too large to fit within sizeinbytes. 0496 @keyword value: (Optional) Value 0497 @keyword pascal: (Default False) The string is preceded with one byte giving the length 0498 of the string (including terminator if there is one) 0499 @keyword encoding: (Default 'ascii') The charset to use when reading/writing to a buffer 0500 @keyword read_encoding: (Default keyword:encoding) The charset to use when reading from a buffer 0501 @keyword write_encoding: (Default keyword:encoding) The charset to use when writing to a buffer 0502 """ 0503 super(USTRING, self).__init__(*args, **kwargs) 0504 0505 self._constant=None 0506 self._terminator=0 0507 self._pad=0 0508 self._sizeinbytes=None 0509 self._default=None 0510 self._raiseonunterminatedread=True 0511 self._raiseontruncate=True 0512 self._value=None 0513 self._pascal=False 0514 self._maxsizeinbytes=None 0515 self._encoding='ascii' 0516 self._read_encoding=None 0517 self._write_encoding=None 0518 self._terminator_length=1 0519 0520 if self._ismostderived(USTRING): 0521 self._update(args,kwargs) 0522 0523 def _update(self, args, kwargs): 0524 super(USTRING,self)._update(args, kwargs) 0525 0526 self._consumekw(kwargs, ("constant", "terminator", "pad", "pascal", 0527 "sizeinbytes", "default", "raiseonunterminatedread", "value", "raiseontruncate", 0528 "encoding", "read_encoding", "write_encoding", "maxsizeinbytes")) 0529 self._complainaboutunusedargs(USTRING,kwargs) 0530 if self._read_encoding==None: 0531 self._read_encoding=self._encoding 0532 if self._write_encoding==None: 0533 self._write_encoding=self._encoding 0534 if self._terminator_length < 1 or self._terminator_length > 4: 0535 raise ValueException("Terminator length outside allowed range of 1-4. You tried setting it to %d" % self._terminator_length) 0536 0537 # Set our value if one was specified 0538 if len(args)==0: 0539 pass 0540 elif len(args)==1: 0541 self._value=args[0] 0542 else: 0543 raise TypeError("Unexpected arguments "+`args`) 0544 if self._value is None and self._default is not None: 0545 self._value=self._default 0546 0547 if self._value is not None: 0548 # convert _value to unicode, non-string objects convert to string representation 0549 # allows strings to be initialised with integers etc. 0550 if not isinstance(self._value, (str, unicode)): 0551 # convert numbers into strings 0552 temp=str(self._value) 0553 self._value=unicode(temp, 'ascii', 'replace') 0554 # we should only receive unicode strings from bitpim, but... 0555 elif not isinstance(self._value, unicode): 0556 # there is a bug, the docs say this should work on unicode strings but it does not 0557 self._value=unicode(self._value, 'ascii', 'replace') 0558 0559 if self._constant is not None and self._constant!=self._value: 0560 raise ValueException("This field is a constant of '%s'. You tried setting it to '%s'" % (self._constant, self._value)) 0561 # test that we can convert the string, also needed for "sizeinbytes" 0562 try: 0563 test=self.convert_for_write() 0564 except UnicodeEncodeError: 0565 raise common.PhoneStringEncodeException(self._value, uni_string_codec) 0566 0567 if self._value is not None: 0568 max_size=None 0569 if self._sizeinbytes is not None: 0570 max_size=self._sizeinbytes 0571 elif self._maxsizeinbytes is not None: 0572 max_size=self._maxsizeinbytes 0573 if max_size is not None: 0574 l=len(test) 0575 if self._terminator is not None: 0576 l+=self._terminator_length 0577 if l>max_size: 0578 if self._raiseontruncate: 0579 raise ValueLengthException(l, self._sizeinbytes) 0580 # truncate, but the number of bytes might be larger than the number of 0581 # unicode characters depending on conversion 0582 self._value=self._value[:max_size] 0583 term_len=0 0584 if self._terminator!=None: 0585 term_len=self._terminator_length 0586 # adjust for terminator and multibyte characters 0587 while (len(self.convert_for_write())+term_len)>max_size: 0588 self._value=self._value[:-1] 0589 0590 def readfrombuffer(self, buf): 0591 self._bufferstartoffset=buf.getcurrentoffset() 0592 0593 flush=0 0594 _value='' 0595 if self._pascal: 0596 if self._sizeinbytes is None: 0597 self._sizeinbytes=buf.getnextbyte() 0598 # allow for fixed size pascal strings 0599 else: 0600 temp=self._sizeinbytes-1 0601 self._sizeinbytes=buf.getnextbyte() 0602 flush=temp-self._sizeinbytes 0603 if(temp < 0): 0604 raise ValueLengthException() 0605 0606 if self._sizeinbytes is not None: 0607 # fixed size 0608 _value=buf.getnextbytes(self._sizeinbytes) 0609 if self._terminator is not None: 0610 # using a terminator 0611 pos=-1 0612 for i in range(0, self._sizeinbytes, self._terminator_length): 0613 term=0 0614 for j in range(self._terminator_length): 0615 term+=ord(_value[i+j])<<(j*8) 0616 if term==self._terminator: 0617 pos=i 0618 break 0619 if pos>=0: 0620 _value=_value[:pos] 0621 elif self._raiseonunterminatedread: 0622 raise NotTerminatedException() 0623 elif self._pad is not None: 0624 # else remove any padding 0625 while len(_value) and _value[-1]==chr(self._pad): 0626 _value=_value[:-1] 0627 else: 0628 if self._terminator is None: 0629 # read in entire rest of packet 0630 _value=buf.getremainingbytes() 0631 else: 0632 # read up to terminator 0633 _value="" 0634 count=0 0635 term=0 0636 while buf.hasmore(): 0637 _value+=chr(buf.getnextbyte()) 0638 count+=1 0639 if (count % self._terminator_length)==0: 0640 term=0 0641 # see if we have a terminator 0642 for j in range(self._terminator_length): 0643 term=(term<<8)+ord(_value[count-1-j]) 0644 if term==self._terminator: 0645 break 0646 if term!=self._terminator and self._raiseonunterminatedread: 0647 raise NotTerminatedException() 0648 else: 0649 _value=_value[:-1] 0650 0651 if self._maxsizeinbytes is not None: 0652 self._value=self._value[:self._maxsizeinbytes] 0653 0654 if self._constant is not None and _value!=self._constant: 0655 raise ValueException("The value read was not the constant") 0656 0657 # for fixed size pascal strings flush the remainder of the buffer 0658 if(flush): 0659 buf.getnextbytes(flush) 0660 0661 self._bufferendoffset=buf.getcurrentoffset() 0662 0663 # we will convert to unicode when the value is retrieved. 0664 # this prevents garbage that will never be used from causing an 0665 # exception. e.g. A deleted calendar record on some LG phones may contain 0666 # all f's, this causes an exception if we try to convert to unicode. 0667 self._value=_value 0668 0669 def writetobuffer(self, buf): 0670 if self._value is None: 0671 raise ValueNotSetException() 0672 self._bufferstartoffset=buf.getcurrentoffset() 0673 # calculate length 0674 temp_str=self.convert_for_write() 0675 l=len(temp_str) 0676 if self._terminator is not None: 0677 l+=1 0678 if self._pascal: 0679 buf.appendbyte(l) 0680 l+=1 0681 buf.appendbytes(temp_str) 0682 term=self._terminator 0683 if self._terminator is not None: 0684 for j in range(self._terminator_length): 0685 buf.appendbyte((term & 0xFF)) 0686 term=term>>8 0687 if self._sizeinbytes is not None: 0688 if l<self._sizeinbytes: 0689 buf.appendbytes(chr(self._pad)*(self._sizeinbytes-l)) 0690 self._bufferendoffset=buf.getcurrentoffset() 0691 0692 def convert_for_write(self): 0693 # if we are not in unicode this means that we contain data read from the phone 0694 # in this case just return this back, there is no need to convert twice. 0695 if not isinstance(self._value, unicode): 0696 return self._value 0697 try: 0698 temp_str=common.encode_with_degrade(self._value, self._write_encoding) 0699 except UnicodeError: 0700 raise common.PhoneStringEncodeException(self._value, self._write_encoding) 0701 return temp_str 0702 0703 def packetsize(self): 0704 if self._sizeinbytes is not None: 0705 return self._sizeinbytes 0706 0707 if self._value is None: 0708 raise ValueNotSetException() 0709 0710 l=len(self.convert_for_write()) 0711 if self._terminator is not None: 0712 l+=1 0713 0714 return l 0715 0716 def getvalue(self): 0717 """Returns the string we are""" 0718 if self._value is None: 0719 raise ValueNotSetException() 0720 # convert to unicode if we are not already 0721 if not isinstance(self._value, unicode): 0722 try: 0723 self._value=self._value.decode(self._read_encoding) 0724 except UnicodeDecodeError: 0725 # this means the codec is set wrong for this phone !! 0726 raise common.PhoneStringDecodeException(self._value, self._read_encoding) 0727 return self._value 0728 0729 class SEVENBITSTRING(BaseProtogenClass): 0730 """A text string where ASCII characters are stored as packed 7 bit characters. This is 0731 typically used in SMS messages.""" 0732 def __init__(self, *args, **kwargs): 0733 """ 0734 @keyword terminator: (Default=\x00) The termination character 0735 @keyword sizeinbytes: Amount of space the string sits in 0736 """ 0737 super(SEVENBITSTRING, self).__init__(*args, **kwargs) 0738 self._value=None 0739 self._terminator='\x00' 0740 self._sizeinbytes=None 0741 if self._ismostderived(SEVENBITSTRING): 0742 self._update(args,kwargs) 0743 0744 def _update(self, args, kwargs): 0745 super(SEVENBITSTRING,self)._update(args, kwargs) 0746 0747 self._consumekw(kwargs, ("terminator", "value", "sizeinbytes")) 0748 self._complainaboutunusedargs(SEVENBITSTRING, kwargs) 0749 if len(args): 0750 raise TypeError("Unexpected arguments "+`args`) 0751 if self._sizeinbytes is None: 0752 raise ValueException("You must specify a size in bytes") 0753 0754 def readfrombuffer(self, buf): 0755 self._bufferstartoffset=buf.getcurrentoffset() 0756 bytes=buf.getnextbytes(self._sizeinbytes) 0757 self._value=common.decodecharacterbits(bytes, bitsperchar=7, charconv=chr, terminator=self._terminator) 0758 self._bufferendoffset=buf.getcurrentoffset() 0759 0760 def getvalue(self): 0761 """Returns the string we are""" 0762 if self._value is None: 0763 raise ValueNotSetException() 0764 return self._value 0765 0766 class SMSDATE(BaseProtogenClass): 0767 """A date as used in SMS messages. It is six bytes long with the 0768 bytes being year month day hour minute second. From stuff on the 0769 web, it appears GSM phones swap each nybble.""" 0770 def __init__(self, *args, **kwargs): 0771 """@keyword sizeinbytes: (optional) Must be six""" 0772 super(SMSDATE, self).__init__(*args, **kwargs) 0773 self._values=None 0774 self._sizeinbytes=6 0775 if self._ismostderived(SMSDATE): 0776 self._update(args, kwargs) 0777 0778 def _update(self, args, kwargs): 0779 super(SMSDATE, self)._update(args, kwargs) 0780 self._consumekw(kwargs, ("sizeinbytes",)) 0781 self._complainaboutunusedargs(SMSDATE, kwargs) 0782 if len(args): 0783 raise TypeError("Unexpected arguments "+`args`) 0784 if self._sizeinbytes != 6: 0785 raise ValueNotSetException("You can only specify 6 as the size in bytes") 0786 0787 def readfrombuffer(self, buf): 0788 self._bufferstartoffset=buf.getcurrentoffset() 0789 year=buf.getnextbyte() 0790 if year<0: 0791 year+=1900 0792 else: 0793 year+=2000 0794 month=buf.getnextbyte() 0795 day=buf.getnextbyte() 0796 hour=buf.getnextbyte() 0797 minute=buf.getnextbyte() 0798 second=buf.getnextbyte() 0799 self._value=year,month,day, hour,minute,second 0800 self._bufferendoffset=buf.getcurrentoffset() 0801 0802 def getvalue(self): 0803 """Returns the ISO date time string we are""" 0804 if self._value is None: 0805 raise ValueNotSetException() 0806 return "%d%02d%02dT%02d%02d%02d" % self._value 0807 0808 0809 class CSVSTRING(BaseProtogenClass): 0810 """A text string enclosed in quotes, with a way to escape quotes that a supposed 0811 to be part of the string. Typical of Samsung phones.""" 0812 def __init__(self, *args, **kwargs): 0813 """ 0814 A string value can be specified to this constructor, or in the value keyword arg. 0815 0816 @keyword constant: (Optional) A constant value. All reads must have this value 0817 @keyword terminator: (Default=,) The string terminator (or None). If set there will 0818 always be a terminator when writing. The terminator is not returned when getting 0819 the value. 0820 @keyword quotechar: (Default=Double Quote) Quote character that surrounds string 0821 @keyword readescape: (Default=True) Interpret PPP escape char (0x7d) 0822 @keywors writeescape: (Default=False) Escape quotechar. If false, drop quotechar in string. 0823 @keyword maxsizeinbytes: (Optional) On writing, truncate strings longer than this (length is before 0824 any escaping and quoting 0825 @keyword default: (Optional) Our default value 0826 @keyword raiseonunterminatedread: (Default True) raise L{NotTerminatedException} if there is 0827 no terminator on the value being read in. terminator must also be set. 0828 @keyword raiseontruncate: (Default True) raise L{ValueLengthException} if the supplied 0829 value is too large to fit within sizeinbytes. 0830 @keyword raiseonmissingquotes: (Default True) raise L{MissingQuotesException} if the string does 0831 not have quote characters around it 0832 @keyword value: (Optional) Value 0833 @keyword invalidchars: (Default=quotechar) A string containing invalid 0834 characters which would be removed before writing to buffer. 0835 @keyword encoding: (Default=None) If specified Unicode charset. 0836 @keyword raiseonunicodeerror: (Default=True) raise exception if fail 0837 to encode/decode Unicode. 0838 """ 0839 super(CSVSTRING, self).__init__(*args, **kwargs) 0840 0841 self._constant=None 0842 self._terminator=ord(',') 0843 self._quotechar=ord('"') 0844 self._readescape=True 0845 self._writeescape=False 0846 self._maxsizeinbytes=None 0847 self._default=None 0848 self._raiseonunterminatedread=True 0849 self._raiseontruncate=True 0850 self._raiseonmissingquotes=True 0851 self._invalidchars=chr(self._quotechar) 0852 self._value=None 0853 self._encoding=None 0854 self._raiseonunicodeerror=True 0855 0856 if self._ismostderived(CSVSTRING): 0857 self._update(args,kwargs) 0858 0859 def _update(self, args, kwargs): 0860 super(CSVSTRING,self)._update(args, kwargs) 0861 0862 self._consumekw(kwargs, ("constant", "terminator", "quotechar", "readescape", 0863 "writeescape", "maxsizeinbytes", "default", 0864 "raiseonunterminatedread", "value", 0865 "raiseontruncate", "raiseonmissingquotes", 0866 "invalidchars")) 0867 self._complainaboutunusedargs(CSVSTRING,kwargs) 0868 0869 # Set our value if one was specified 0870 if len(args)==0: 0871 pass 0872 elif len(args)==1: 0873 self._value=common.forceascii(args[0]) 0874 if self._constant is not None and self._constant!=self._value: 0875 raise ValueException("This field is a constant of '%s'. You tried setting it to '%s'" % (self._constant, self._value)) 0876 else: 0877 raise TypeError("Unexpected arguments "+`args`) 0878 if self._value is None and self._default is not None: 0879 self._value=self._default 0880 0881 if self._value is not None: 0882 self._value=str(self._value) # no unicode here! 0883 if self._invalidchars: 0884 self._value=re.sub(r'[%s]'%self._invalidchars, r'', self._value) 0885 if self._maxsizeinbytes is not None: 0886 l=len(self._value) 0887 if l>self._maxsizeinbytes: 0888 if self._raiseontruncate: 0889 raise ValueLengthException(l, self._maxsizeinbytes) 0890 0891 self._value=self._value[:self._maxsizeinbytes] 0892 0893 def readfrombuffer(self, buf): 0894 self._bufferstartoffset=buf.getcurrentoffset() 0895 0896 # First character better be a terminator 0897 # Raise on, first char not quote, trailing quote never found, character after last quote 0898 # not , or EOL. Will need an option to not raise if initial quote not found 0899 0900 if self._terminator is None: 0901 # read in entire rest of packet 0902 # Ignore maxsizeinbytes 0903 # Check for leading and trailing quotes later 0904 self._value=buf.getremainingbytes() 0905 else: 0906 # Possibly quoted string. If first character is not a quote, read until 0907 # terminator or end of line. Exception will be thrown after reading if 0908 # the string was not quoted and it was supposed to be. 0909 self._value=chr(buf.getnextbyte()) 0910 if self._value == ',': 0911 self._value = '' 0912 else: 0913 inquotes=False 0914 if self._quotechar is not None: 0915 if self._value[0]==chr(self._quotechar): 0916 inquotes=True 0917 while buf.hasmore(): 0918 self._value+=chr(buf.getnextbyte()) 0919 if inquotes: 0920 if self._value[-1]==chr(self._quotechar): 0921 inquotes=False 0922 else: 0923 if self._value[-1]==chr(self._terminator): 0924 break 0925 if self._value[-1]==self._terminator: 0926 if self._raiseonunterminatedread: 0927 raise NotTerminatedException() 0928 else: 0929 self._value=self._value[:-1] 0930 0931 if self._quotechar is not None and self._value: 0932 if self._value[0]==chr(self._quotechar) and self._value[-1]==chr(self._quotechar): 0933 self._value=self._value[1:-1] 0934 else: 0935 raise MissingQuotesException() 0936 0937 if self._readescape: 0938 self._value=common.pppunescape(self._value) 0939 0940 if self._constant is not None and self._value!=self._constant: 0941 raise ValueException("The value read was not the constant") 0942 0943 self._bufferendoffset=buf.getcurrentoffset() 0944 0945 def writetobuffer(self, buf): 0946 # Need to raise exception if string exceeds maxsizeinbytes 0947 if self._value is None: 0948 raise ValueNotSetException() 0949 0950 if self._encoding and isinstance(self._value, unicode): 0951 try: 0952 _value=common.encode_with_degrade(self._value, 0953 self._encoding) 0954 except UnicodeError: 0955 if self._raiseonunicodeerror: 0956 raise common.PhoneStringEncodeException(self._value, 0957 self._encoding) 0958 else: 0959 # failed to encode, clear it out 0960 _value='' 0961 else: 0962 _value=self._value 0963 0964 self._bufferstartoffset=buf.getcurrentoffset() 0965 0966 if self._quotechar is not None: 0967 buf.appendbyte(self._quotechar) 0968 buf.appendbytes(_value) 0969 if self._quotechar is not None: 0970 buf.appendbyte(self._quotechar) 0971 if self._terminator is not None: 0972 buf.appendbyte(self._terminator) 0973 0974 self._bufferendoffset=buf.getcurrentoffset() 0975 0976 def packetsize(self): 0977 if self._sizeinbytes is not None: 0978 return self._sizeinbytes 0979 0980 if self._value is None: 0981 raise ValueNotSetException() 0982 0983 l=len(self._value) 0984 if self._terminator is not None: 0985 l+=1 0986 0987 return l 0988 0989 def getvalue(self): 0990 """Returns the string we are""" 0991 if self._value is None: 0992 raise ValueNotSetException() 0993 # convert to unicode if we are not already 0994 if self._encoding and not isinstance(self._value, unicode): 0995 try: 0996 if self._raiseonunicodeerror: 0997 self._value=self._value.decode(self._encoding) 0998 else: 0999 self._value=self._value.decode(self._encoding, 'ignore') 1000 except UnicodeDecodeError: 1001 # this means the codec is set wrong for this phone !! 1002 raise common.PhoneStringDecodeException(self._value, self._encoding) 1003 return self._value 1004 1005 class CSVINT(CSVSTRING): 1006 """Integers in CSV lines""" 1007 def __init__(self, *args, **kwargs): 1008 super(CSVINT,self).__init__(*args, **kwargs) 1009 1010 self._quotechar=None 1011 1012 if self._ismostderived(CSVINT): 1013 self._update(args,kwargs) 1014 1015 def _update(self, args, kwargs): 1016 for k in 'constant', 'default', 'value': 1017 if kwargs.has_key(k): 1018 kwargs[k]=str(kwargs[k]) 1019 if len(args)==0: 1020 pass 1021 elif len(args)==1: 1022 args=(str(args[0]),) 1023 else: 1024 raise TypeError("expected integer as arg") 1025 1026 super(CSVINT,self)._update(args,kwargs) 1027 self._complainaboutunusedargs(CSVINT,kwargs) 1028 1029 def getvalue(self): 1030 """Convert the string into an integer 1031 1032 @rtype: integer 1033 """ 1034 1035 # Will probably want an flag to allow null strings, converting 1036 # them to a default value 1037 1038 val=super(CSVINT,self).getvalue() 1039 try: 1040 ival=int(val) 1041 except: 1042 try: 1043 ival=int(self._default) 1044 except: 1045 raise ValueException("The field '%s' is not an integer" % (val)) 1046 return ival 1047 1048 class CSVDATE(CSVSTRING): 1049 """Dates in CSV lines""" 1050 def __init__(self, *args, **kwargs): 1051 super(CSVDATE,self).__init__(*args, **kwargs) 1052 1053 self._valuedate=(0,0,0) # Year month day 1054 1055 self._quotechar=None 1056 1057 if self._ismostderived(CSVDATE): 1058 self._update(args,kwargs) 1059 1060 def _update(self, args, kwargs): 1061 for k in 'constant', 'default', 'value': 1062 if kwargs.has_key(k): 1063 kwargs[k]=self._converttostring(kwargs[k]) 1064 if len(args)==0: 1065 pass 1066 elif len(args)==1: 1067 args=(self._converttostring(args[0]),) 1068 else: 1069 raise TypeError("expected (year,month,day) as arg") 1070 1071 super(CSVDATE,self)._update(args, kwargs) # we want the args 1072 self._complainaboutunusedargs(CSVDATE,kwargs) 1073 1074 def getvalue(self): 1075 """Unpack the string into the date 1076 1077 @rtype: tuple 1078 @return: (year, month, day) 1079 """ 1080 1081 s=super(CSVDATE,self).getvalue() 1082 val=s.split("/") # List of of Month, day, year 1083 if len(val)<2: 1084 year = 0 1085 month = 0 1086 day = 0 1087 else: 1088 year=int(val[2]) 1089 month=int(val[0]) 1090 day=int(val[1]) 1091 return (year, month, day) 1092 1093 def _converttostring(self, date): 1094 if len(date)>=3: 1095 year,month,day=date[:3] 1096 if month>0 or day>0 or year>0: 1097 s='%2.2d/%2.2d/%4.4d'%(month, day, year) 1098 else: 1099 s="" 1100 else: 1101 s="" 1102 return s 1103 1104 1105 class CSVTIME(CSVSTRING): 1106 """Timestamp in CSV lines""" 1107 def __init__(self, *args, **kwargs): 1108 super(CSVTIME,self).__init__(*args, **kwargs) 1109 1110 self._valuetime=(0,0,0,0,0,0) # Year month day, hour, minute, second 1111 1112 self._quotechar=None 1113 1114 if self._ismostderived(CSVTIME): 1115 self._update(args,kwargs) 1116 1117 def _update(self, args, kwargs): 1118 for k in 'constant', 'default', 'value': 1119 if kwargs.has_key(k): 1120 kwargs[k]=self._converttostring(kwargs[k]) 1121 if len(args)==0: 1122 pass 1123 elif len(args)==1: 1124 args=(self._converttostring(args[0]),) 1125 else: 1126 raise TypeError("expected (year,month,day) as arg") 1127 1128 super(CSVTIME,self)._update(args, kwargs) # we want the args 1129 self._complainaboutunusedargs(CSVTIME,kwargs) 1130 1131 def getvalue(self): 1132 """Unpack the string into the date 1133 1134 @rtype: tuple 1135 @return: (year, month, day) 1136 """ 1137 1138 s=super(CSVTIME,self).getvalue() 1139 year=int(s[0:4]) 1140 month=int(s[4:6]) 1141 day=int(s[6:8]) 1142 hour=int(s[9:11]) 1143 minute=int(s[11:13]) 1144 second=int(s[13:15]) 1145 return (year, month, day, hour, minute, second) 1146 1147 def _converttostring(self, time): 1148 if len(time)>=6: 1149 year,month,day,hour,minute,second=time[:6] 1150 s='%4.4d%2.2d%2.2dT%2.2d%2.2d%2.2d'%(year, month, day, hour, minute, second) 1151 else: 1152 s="" 1153 return s 1154 1155 1156 class COUNTEDBUFFEREDSTRING(BaseProtogenClass): 1157 """A string as used on Audiovox. There is a one byte header saying how long the string 1158 is, followed by the string in a fixed sized buffer""" 1159 def __init__(self, *args, **kwargs): 1160 """ 1161 A string value can be specified to this constructor, or in the value keyword arg. 1162 1163 @keyword constant: (Optional) A constant value. All reads must have this value 1164 @keyword pad: (Default=32 - space) When writing, what to pad the rest of the buffer with 1165 @keyword default: (Optional) Our default value 1166 @keyword raiseontruncate: (Default True) raise L{ValueLengthException} if the supplied 1167 value is too large to fit within the buffer. 1168 @keyword value: (Optional) Value 1169 @keyword sizeinbytes: (Mandatory) Size of the buffer, including the count byte 1170 """ 1171 super(COUNTEDBUFFEREDSTRING,self).__init__(*args, **kwargs) 1172 1173 self._constant=None 1174 self._pad=32 1175 self._sizeinbytes=None 1176 self._default=None 1177 self._raiseontruncate=True 1178 self._value=None 1179 1180 if self._ismostderived(COUNTEDBUFFEREDSTRING): 1181 self._update(args, kwargs) 1182 1183 def _update(self, args, kwargs): 1184 super(COUNTEDBUFFEREDSTRING,self)._update(args, kwargs) 1185 1186 self._consumekw(kwargs, ("constant", "pad", "sizeinbytes", "default", "raiseontruncate", "value")) 1187 self._complainaboutunusedargs(COUNTEDBUFFEREDSTRING,kwargs) 1188 # Set our value if one was specified 1189 if len(args)==0: 1190 pass 1191 elif len(args)==1: 1192 self._value=str(args[0]) 1193 if self._constant is not None and self._constant!=self._value: 1194 raise ValueException("This field is a constant of '%s'. You tried setting it to '%s'" % (self._constant, self._value)) 1195 else: 1196 raise TypeError("Unexpected arguments "+`args`) 1197 if self._value is None and self._default is not None: 1198 self._value=self._default 1199 1200 if self._sizeinbytes is None: 1201 raise ValueException("sizeinbytes must be specified for COUNTEDBUFFEREDSTRING") 1202 1203 if self._value is not None: 1204 l=len(self._value) 1205 if l>self._sizeinbytes-1: 1206 if self._raiseontruncate: 1207 raise ValueLengthException(l, self._sizeinbytes-1) 1208 1209 self._value=self._value[:self._sizeinbytes-1] 1210 1211 def readfrombuffer(self, buf): 1212 assert self._sizeinbytes is not None 1213 self._bufferstartoffset=buf.getcurrentoffset() 1214 1215 strlen=buf.getnextbyte() 1216 if strlen>self._sizeinbytes-1: 1217 raise ValueException("counter specifies size of %d which is greater than remaining stringbuffer size of %d!" % (strlen, self._sizeinbytes-1)) 1218 self._value=buf.getnextbytes(self._sizeinbytes-1) # -1 due to counter byte 1219 self._value=self._value[:strlen] 1220 if self._constant is not None and self._value!=self._constant: 1221 raise ValueException("The value read was not the constant") 1222 1223 self._bufferendoffset=buf.getcurrentoffset() 1224 1225 def writetobuffer(self, buf): 1226 assert self._sizeinbytes is not None 1227 if self._value is None: 1228 raise ValueNotSetException() 1229 1230 self._bufferstartoffset=buf.getcurrentoffset() 1231 buf.appendbyte(len(self._value)) 1232 buf.appendbytes(self._value) 1233 if len(self._value)+1<self._sizeinbytes: 1234 buf.appendbytes(chr(self._pad)*(self._sizeinbytes-1-len(self._value))) 1235 1236 self._bufferendoffset=buf.getcurrentoffset() 1237 1238 def packetsize(self): 1239 assert self._sizeinbytes is not None 1240 return self._sizeinbytes 1241 1242 def getvalue(self): 1243 """Returns the string we are""" 1244 if self._value is None: 1245 raise ValueNotSetException() 1246 return self._value 1247 1248 class DATA(BaseProtogenClass): 1249 "A block of bytes" 1250 def __init__(self, *args, **kwargs): 1251 """ 1252 A data value can be specified to this constructor or in the value keyword arg 1253 1254 @keyword constant: (Optional) A constant value. All reads must have this value 1255 @keyword pad: (Default=0) The padding byte if fixed length when writing and the 1256 value isn't long enough 1257 @keyword sizeinbytes: (Optional) Set if fixed length. 1258 If not set, then the rest of the packet will be consumed on reads. 1259 @keyword default: (Optional) Our default value 1260 @keyword raiseonwrongsize: (Default True) raise L{ValueLengthException} if the supplied 1261 value is too large to fit within sizeinbytes. 1262 """ 1263 super(DATA, self).__init__(*args, **kwargs) 1264 1265 self._constant=None 1266 self._pad=0 1267 self._sizeinbytes=None 1268 self._default=None 1269 self._raiseonwrongsize=True 1270 self._value=None 1271 1272 if self._ismostderived(DATA): 1273 self._update(args,kwargs) 1274 1275 def _update(self, args, kwargs): 1276 super(DATA,self)._update(args, kwargs) 1277 1278 self._consumekw(kwargs, ("constant", "pad", "sizeinbytes", "default", "raiseonwrongsize", "value")) 1279 self._complainaboutunusedargs(DATA,kwargs) 1280 1281 # Set our value if one was specified 1282 if len(args)==0: 1283 pass 1284 elif len(args)==1: 1285 self._value=args[0] 1286 if self._constant is not None and self._constant!=self._value: 1287 raise ValueException("This field is a constant and you set it to a different value") 1288 else: 1289 raise TypeError("Unexpected arguments "+`args`) 1290 if self._value is None and self._default is not None: 1291 self._value=self._default 1292 1293 if self._value is not None: 1294 if self._sizeinbytes is not None: 1295 l=len(self._value) 1296 if l<self._sizeinbytes: 1297 if self._pad is not None: 1298 self._value+=chr(self._pad)*(self._sizeinbytes-l) 1299 1300 l=len(self._value) 1301 1302 if l!=self._sizeinbytes: 1303 if self._raiseonwrongsize: 1304 raise ValueLengthException(l, self._sizeinbytes) 1305 else: 1306 self._value=self._value[:self._sizeinbytes] 1307 1308 1309 def readfrombuffer(self, buf): 1310 self._bufferstartoffset=buf.getcurrentoffset() 1311 1312 if self._sizeinbytes is not None: 1313 # fixed size 1314 self._value=buf.getnextbytes(self._sizeinbytes) 1315 else: 1316 # read in entire rest of packet 1317 self._value=buf.getremainingbytes() 1318 1319 if self._constant is not None and self._value!=self._constant: 1320 raise ValueException("The value read was not the constant") 1321 self._bufferendoffset=buf.getcurrentoffset() 1322 1323 def writetobuffer(self, buf): 1324 if self._value is None: 1325 raise ValueNotSetException() 1326 1327 self._bufferstartoffset=buf.getcurrentoffset() 1328 buf.appendbytes(self._value) 1329 self._bufferendoffset=buf.getcurrentoffset() 1330 1331 def packetsize(self): 1332 if self._sizeinbytes is not None: 1333 return self._sizeinbytes 1334 1335 if self._value is None: 1336 raise ValueNotSetException() 1337 1338 l=len(self._value) 1339 1340 return l 1341 1342 def getvalue(self): 1343 """Returns the bytes we are""" 1344 if self._value is None: 1345 raise ValueNotSetException() 1346 return self._value 1347 1348 1349 class UNKNOWN(DATA): 1350 "A block of bytes whose purpose we don't know" 1351 1352 def __init__(self, *args, **kwargs): 1353 """ 1354 Same arguments as L{DATA.__init__}. We default to a block 1355 of pad chars (usually \x00) 1356 """ 1357 dict={'pad':0 , 'default': ""} 1358 dict.update(kwargs) 1359 super(UNKNOWN,self).__init__(*args, **dict) 1360 1361 if self._ismostderived(UNKNOWN): 1362 self._update(args,dict) 1363 1364 def _update(self, args, kwargs): 1365 super(UNKNOWN,self)._update(args, kwargs) 1366 self._complainaboutunusedargs(UNKNOWN,kwargs) 1367 1368 # Was a value specified? 1369 if len(args)==1: 1370 self._value=args[0] 1371 elif len(args)>1: 1372 raise TypeError("Unexpected arguments "+`args`) 1373 1374 class LIST(BaseProtogenClass): 1375 """A list of items 1376 1377 You can generally treat this class as though it is a list. Note that some 1378 list like methods haven't been implemented (there are so darn many!) If you 1379 are missing one you want to use, please add it to this class. 1380 """ 1381 1382 def __init__(self, *args, **kwargs): 1383 """ 1384 You can pass objects to start the list with, or to the value keyword arg 1385 1386 @keyword createdefault: (Default False) Creates default members of the list if enough 1387 were not supplied before writing. 1388 @keyword length: (Optional) How many items there are in the list 1389 @keyword raiseonbadlength: (Default True) raises L{ValueLengthException} if there are 1390 the wrong number of items in the list. Note that this checking is only done 1391 when writing or reading from a buffer. length must be set for this to have any 1392 effect. If you have createdefault set then having less than length elements will 1393 not cause the exception. 1394 @keyword elementclass: (Mandatory) The class of each element 1395 @keyword elementinitkwargs: (Optional) KWargs for the constructor of each element 1396 @keyword value: (Optional) Value 1397 """ 1398 self._thelist=[] 1399 super(LIST, self).__init__(*args, **kwargs) 1400 self._createdefault=False 1401 self._length=None 1402 self._raiseonbadlength=True 1403 self._raiseonincompleteread=True 1404 self._elementclass=None 1405 self._elementinitkwargs={} 1406 1407 if self._ismostderived(LIST): 1408 self._update(args,kwargs) 1409 1410 def _update(self, args, kwargs): 1411 super(LIST,self)._update(args, kwargs) 1412 self._consumekw(kwargs, ("createdefault","length","raiseonbadlength","elementclass","elementinitkwargs","raiseonincompleteread")) 1413 if kwargs.has_key("value"): 1414 self._thelist=list(kwargs['value']) 1415 del kwargs['value'] 1416 1417 self._complainaboutunusedargs(LIST,kwargs) 1418 1419 if self._elementclass is None: 1420 raise TypeError("elementclass argument was not supplied") 1421 1422 if len(args): 1423 self.extend(args) 1424 1425 def readfrombuffer(self,buf): 1426 self._bufferstartoffset=buf.getcurrentoffset() 1427 # delete all existing items 1428 self._thelist=[] 1429 1430 if self._length is None: 1431 # read rest of buffer 1432 while buf.hasmore(): 1433 x=self._makeitem() 1434 x.readfrombuffer(buf) 1435 self._thelist.append(x) 1436 else: 1437 for dummy in range(self._length): 1438 # read specified number of items 1439 x=self._makeitem() 1440 try: 1441 x.readfrombuffer(buf) 1442 except IndexError: 1443 if self._raiseonincompleteread: 1444 raise IndexError("tried to read too many list items") 1445 self._thelist.append(x) 1446 1447 self._bufferendoffset=buf.getcurrentoffset() 1448 1449 def writetobuffer(self, buf): 1450 self._bufferstartoffset=buf.getcurrentoffset() 1451 1452 self._ensurelength() 1453 for i in self: 1454 i.writetobuffer(buf) 1455 1456 self._bufferendoffset=buf.getcurrentoffset() 1457 1458 def packetsize(self): 1459 self._ensurelength() 1460 sz=0 1461 for item in self: 1462 sz+=item.packetsize() 1463 return sz 1464 1465 def iscontainer(self): 1466 return True 1467 1468 def containerelements(self): 1469 self._ensurelength() 1470 for i,v in enumerate(self._thelist): 1471 yield "["+`i`+"]",v,None 1472 1473 1474 # Provide various list methods. I initially tried just subclassing list, 1475 # but it was impossible to tell which methods needed to be reimplemented. 1476 # I was concerned about double conversions. 1477 # For example if my append turned the item into elementclass 1478 # and then the builtin list append called setitem to do the append, which I 1479 # also override so it then converts the elementclass into another 1480 # elementclass. 1481 def append(self, item): 1482 self._thelist.append(self._makeitem(item)) 1483 1484 def extend(self, items): 1485 self._thelist.extend(map(self._makeitem, items)) 1486 1487 def insert(self, index, item): 1488 self._thelist.insert(index, self._makeitem(item)) 1489 1490 def __getitem__(self, index): 1491 return self._thelist[index] 1492 1493 def __iter__(self): 1494 try: 1495 return self._thelist.__iter__() 1496 except: 1497 return self.__fallbackiter() 1498 1499 def __fallbackiter(self): 1500 # used for Python 2.2 which doesn't have list.__iter__ 1501 for item in self._thelist: 1502 yield item 1503 1504 def __len__(self): 1505 return self._thelist.__len__() 1506 1507 def __setitem__(self, index, value): 1508 self._thelist.__setitem__(index, self._makeitem(value)) 1509 1510 def __delitem__(self, index): 1511 self._thelist.__delitem__(index) 1512 1513 1514 def _makeitem(self, *args, **kwargs): 1515 "Creates a child element" 1516 # if already of the type, then return it 1517 if len(args)==1 and isinstance(args[0], self._elementclass): 1518 return args[0] 1519 d={} 1520 d.update(self._elementinitkwargs) 1521 d.update(kwargs) 1522 return self._elementclass(*args, **d) 1523 1524 def _ensurelength(self): 1525 "Ensures we are the correct length" 1526 if self._createdefault and self._length is not None and len(self._thelist)<self._length: 1527 while len(self._thelist)<self._length: 1528 x=self._makeitem() 1529 self._thelist.append(x) 1530 return 1531 if self._length is not None and self._raiseonbadlength and len(self._thelist)!=self._length: 1532 raise ValueLengthException(len(self), self._length) 1533 1534 class buffer(object): 1535 "This is used for reading and writing byte data" 1536 def __init__(self, data=None): 1537 "Call with data to read from it, or with None to write to it" 1538 if data is not None: 1539 self._data=data 1540 else: 1541 self._buffer=cStringIO.StringIO() 1542 1543 self._offset=0 1544 1545 def getcurrentoffset(self): 1546 "Returns distance into data we are" 1547 return self._offset 1548 def setcurrentoffset(self, ofs): 1549 "Set the current offset" 1550 if ofs>len(self._data): 1551 raise IndexError('Trying to set offset beyond end of %d byte buffer'%len(self._data)) 1552 self._offset=ofs 1553 offset=property(fget=getcurrentoffset, fset=setcurrentoffset) 1554 1555 def peeknextbyte(self, howmuch=0): 1556 "Returns value of next byte, but doesn't advance position" 1557 if self._offset+howmuch>=len(self._data): 1558 return None 1559 return ord(self._data[self._offset+howmuch]) 1560 1561 def getnextbyte(self): 1562 "Returns next byte" 1563 if self._offset>=len(self._data): 1564 raise IndexError("trying to read one byte beyond end of "+`len(self._data)`+" byte buffer") 1565 res=ord(self._data[self._offset]) 1566 self._offset+=1 1567 return res 1568 1569 def getnextbytes(self, howmany): 1570 "Returns howmany bytes" 1571 assert howmany>=0 1572 if self._offset+howmany>len(self._data): 1573 raise IndexError("Trying to read "+`howmany`+" bytes starting at "+`self._offset`+" which will go beyond end of "+`len(self._data)`+" byte buffer") 1574 res=self._data[self._offset:self._offset+howmany] 1575 self._offset+=howmany 1576 return res 1577 1578 def peeknextbytes(self, howmany): 1579 if self._offset+howmany>len(self._data): 1580 return None 1581 return self._data[self._offset:self._offset+howmany] 1582 1583 def getremainingbytes(self): 1584 "Returns rest of buffer" 1585 sz=len(self._data)-self._offset 1586 return self.getnextbytes(sz) 1587 1588 def hasmore(self): 1589 "Is there any data left?" 1590 return self._offset<len(self._data) 1591 1592 def howmuchmore(self): 1593 "Returns how many bytes left" 1594 return len(self._data)-self._offset 1595 1596 def appendbyte(self, val): 1597 """Appends byte to data. 1598 @param val: a number 0 <= val <=255 1599 """ 1600 assert val>=0 and val<=255 1601 self._buffer.write(chr(val)) 1602 self._offset+=1 1603 assert self._offset==len(self._buffer.getvalue()) 1604 1605 def appendbytes(self, bytes): 1606 "Adds bytes to end" 1607 self._buffer.write(bytes) 1608 self._offset+=len(bytes) 1609 assert self._offset==len(self._buffer.getvalue()) 1610 1611 def getvalue(self): 1612 "Returns the buffer being built" 1613 return self._buffer.getvalue() 1614 1615 def getdata(self): 1616 "Returns the data passed in" 1617 return self._data 1618 1619 1620
Generated by PyXR 0.9.4