diff --git a/paramiko/message.py b/paramiko/message.py index dc98c6f..c485662 100644 --- a/paramiko/message.py +++ b/paramiko/message.py @@ -26,46 +26,108 @@ import string, types, struct from util import inflate_long, deflate_long -class Message(object): - "represents the encoding of an SSH2 message" +class Message (object): + """ + An SSH2 I{Message} is a stream of bytes that encodes some combination of + strings, integers, bools, and infinite-precision integers (known in python + as I{long}s). This class builds or breaks down such a byte stream. + """ def __init__(self, content=''): + """ + Create a new SSH2 Message. + + @param content: the byte stream to use as the Message content (usually + passed in only when decomposing a Message). + @type content: string + """ self.packet = content self.idx = 0 - self.seqno = -1 def __str__(self): + """ + Return the byte stream content of this Message, as a string. + + @return: the contents of this Message. + @rtype: string + """ return self.packet def __repr__(self): + """ + Returns a string representation of this object, for debugging. + + @rtype: string + """ return 'Message(' + repr(self.packet) + ')' def get_remainder(self): - "remaining bytes still unparsed" + """ + Return the bytes of this Message that haven't already been parsed and + returned. + + @return: a string of the bytes not parsed yet. + @rtype: string + """ return self.packet[self.idx:] def get_so_far(self): - "bytes that have been parsed" + """ + Returns the bytes of this Message that have been parsed and returned. + The string passed into a Message's constructor can be regenerated by + concatenating C{get_so_far} and L{get_remainder}. + + @return: a string of the bytes parsed so far. + @rtype: string + """ return self.packet[:self.idx] def get_bytes(self, n): + """ + Return the next C{n} bytes of the Message, without decomposing into + an int, string, etc. Just the raw bytes are returned. + + @return: a string of the next C{n} bytes of the Message, or a string + of C{n} zero bytes, if there aren't C{n} bytes remaining. + @rtype: string + """ if self.idx + n > len(self.packet): return '\x00'*n b = self.packet[self.idx:self.idx+n] self.idx = self.idx + n return b - + def get_byte(self): + """ + Return the next byte of the Message, without decomposing it. This + is equivalent to L{get_bytes(1)}. + + @return: the next byte of the Message, or C{'\000'} if there aren't + any bytes remaining. + @rtype: string + """ return self.get_bytes(1) def get_boolean(self): + """ + Fetch a boolean from the stream. + + @return: C{True} or C{False} (from the Message). + @rtype: bool + """ b = self.get_bytes(1) if b == '\x00': - return 0 + return False else: - return 1 + return True def get_int(self): + """ + Fetch an int from the stream. + + @return: a 32-bit unsigned integer. + @rtype: int + """ x = self.packet i = self.idx if i + 4 > len(x): @@ -74,10 +136,39 @@ class Message(object): self.idx = i+4 return n + def get_int64(self): + """ + Fetch a 64-bit int from the stream. + + @return: a 64-bit unsigned integer. + @rtype: long + """ + x = self.packet + i = self.idx + if i + 8 > len(x): + return 0L + n = struct.unpack('>Q', x[i:i+8])[0] + self.idx += 8 + return n + def get_mpint(self): + """ + Fetch a long int (mpint) from the stream. + + @return: an arbitrary-length integer. + @rtype: long + """ return inflate_long(self.get_string()) def get_string(self): + """ + Fetch a string from the stream. This could be a byte string and may + contain unprintable characters. (It's not unheard of for a string to + contain another byte-stream Message.) + + @return: a string. + @rtype: string + """ l = self.get_int() if self.idx + l > len(self.packet): return '' @@ -86,6 +177,13 @@ class Message(object): return str def get_list(self): + """ + Fetch a list of strings from the stream. These are trivially encoded + as comma-separated values in a string. + + @return: a list of strings. + @type: list of strings + """ str = self.get_string() l = string.split(str, ',') return l