Ejemplo n.º 1
0
Archivo: mesgen.py Proyecto: zooko/egtp
    def parse(self, wired_string):
        """
        @returns (counterparty_pub_key_sexp, cleartext,)

        @raises SessionInvalidated if the incoming message was an "invalidate session" message \000\000\000\002.
        @raises UnknownSession error if the incoming message did not identify a known session key.

        @precondition `wired_string' must be a string.: type(wired_string) == types.StringType: "wired_string: %s :: %s" % (hr(wired_string), hr(type(wired_string)))
        @postcondition `counterparty_pub_key_sexp' is a public key.: MojoKey.publicRSAKeyForCommunicationSecurityIsWellFormed(counterparty_pub_key_sexp): "counterparty_pub_key_sexp: %s" % hr(counterparty_pub_key_sexp)
        """
        assert type(wired_string) == types.StringType, "precondition: `wired_string' must be a string." + " -- " + "wired_string: %s :: %s" % (hr(wired_string), hr(type(wired_string)))
        session = None

        try:
            u = Unpacker(wired_string)
            mtype = u.unpack_fstring(4)
            if mtype == '\000\000\000\000':   # a message with a full PK header
                header = u.unpack_string()
                iv = u.unpack_fstring(8)
                prefix = wired_string[:u.get_position()]
                encrypted = u.unpack_string()
                u.done()
                
                counterparty_pub_key_sexp, symmetric_key = self._session_keeper.parse_header(header)
                decrypted = tripledescbc.new(symmetric_key).decrypt(iv, encrypted)
                u = Unpacker(decrypted)
                message = u.unpack_string()
                mac = u.unpack_fstring(SIZE_OF_UNIQS)
                u.done()
                
                # debugprint("------ ------ ------ ------ hmachish(key=%s, message=%s)\n" % (`symmetric_key`, `message`))
                maccomp = cryptutil.hmacish(key=symmetric_key, message=message)

                if mac != maccomp:
                    raise Error, 'incorrect MAC'
                return (counterparty_pub_key_sexp, message)
            elif mtype == '\000\000\000\001':   # a message using an already established session id
                session = u.unpack_fstring(SIZE_OF_UNIQS)
                iv = u.unpack_fstring(8)
                prefix = wired_string[:u.get_position()]
                encrypted = u.unpack_string()
                u.done()
                
                counterparty_pub_key_sexp, symmetric_key, want_ack = self._session_keeper.get_session_info(session)
                decrypted = tripledescbc.new(symmetric_key).decrypt(iv, encrypted)

                u = Unpacker(decrypted)
                message = u.unpack_string()
                mac = u.unpack_fstring(SIZE_OF_UNIQS)
                u.done()

                counterparty_id = idlib.make_id(counterparty_pub_key_sexp, 'broker')

                # debugprint("------ ------ ------ ------ hmachish(key=%s, message=%s)\n" % (`symmetric_key`, `message`))
                maccomp = cryptutil.hmacish(key=symmetric_key, message=message)
                if mac != maccomp:
                    raise Error, 'incorrect MAC'
                if want_ack:
                    self._session_keeper.got_ack(counterparty_id)
                return (counterparty_pub_key_sexp, message)
            elif mtype == '\000\000\002\002':   # a short "message" invalidating an outgoing session id
                bad_session_id_out = u.unpack_fstring(SIZE_OF_UNIQS)
                unverified_counterparty_id = u.unpack_fstring(SIZE_OF_UNIQS)
                self._session_keeper.invalidate_session(bad_session_id_out, unverified_counterparty_id)
                raise SessionInvalidated, 'session_id %s with %s invalidated' % (`bad_session_id_out`, idlib.to_ascii(unverified_counterparty_id))
            else:
                raise Error, 'unsupported message type'
        except (modval.Error, tripledescbc.Error, xdrlib.Error, EOFError), le:
            debugprint("got error in mesgen.parse(): %s", args=(le,), v=4, vs="debug")
            if session is not None:
                raise UnknownSession(session, self.get_id())
            else:
                raise Error, le
Ejemplo n.º 2
0
class TprTopology:
	funcNames = [
		"bonds", "g96bonds", "morse", "cubicbonds", "connbonds",
		"harmonic", "fenebonds", "tabbonds", "tabbondsnc",
		"angles", "g96angles", "cross_bond_bond",
		"cross_bond_angle", "urey_bradley", "qangles",
		"tabangles", "pdihs", "rbdihs", "fourdihs", "idihs",
		"pidihs", "tabdihs", "lj14", "coul14", "ljc14_q",
		"ljc_nb", "lj_sr", "bham", "lj_lr", "bham_lr",
		"dispcorr", "coul_sr", "coul_lr", "rf_excl",
		"coul_recip", "dpd", "polarization", "waterpol",
		"thole", "posres", "disres", "drviol", "orires",
		"ordev", "angres", "angresz", "dihres", "dihviol",
		"constr", "constrnc", "settle", "vsite2", "vsite3",
		"vsite3fd", "vsite3fad", "vsite3out", "vsite4fd",
		"vsite4fdn", "vsiten", "com_pull", "eqm", "epot",
		"ekin", "etot", "econs", "temp", "pres", "dv/dl",
		"dk/dl", "dg/dl_con"
	]
	def __init__(self, topology):
		from OpenSave import osOpen
		topFile = osOpen(topology, 'rb')
		import os
		self.topFileSize = os.stat(topology).st_size
		from xdrlib import Unpacker
		self.fileString = FileString(topFile, 0, self.topFileSize)
		self.xdr = Unpacker(self.fileString)
		version = self._readHeader()
		self._readTopology(version)

	def _do_block(self, version):
		if version < 44:
			for i in range(256):
				self.xdr.unpack_uint()
		blockNR = self.xdr.unpack_uint()
		if version < 51:
			blockNRA = self.xdr.unpack_uint()
		for i in range(blockNR + 1):
			self.xdr.unpack_uint()
		if version < 51:
			for i in range(blockNRA):
				self.xdr.unpack_uint()

	def _do_blocka(self, version):
		if version < 44:
			for i in range(256):
				self.xdr.unpack_uint()
		blockNR = self.xdr.unpack_uint()
		blockNRA = self.xdr.unpack_uint()
		for i in range(blockNR + blockNRA + 1):
			self.xdr.unpack_uint()

	def _do_ffparams(self, version):
		self.funcNumber = {}
		for i, fn in enumerate(self.funcNames):
			self.funcNumber[fn] = i
		self.funcNumberUpdate = [
			(20, "cubicbonds"), (20, "connbonds"), (20, "harmonic"),
			(34, "fenebonds"), (43, "tabbonds"),
			(43, "tabbondsnc"), (30, "cross_bond_bond"),
			(30, "cross_bond_angle"), (30, "urey_bradley"),
			(34, "qangles"), (43, "tabangles"),
			(26, "fourdihs"), (26, "pdihs"), (43, "tabdihs"),
			(41, "ljc14_q"), (41, "ljc_nb"),
			(32, "bham_lr"), (32, "rf_excl"), (32, "coul_recip"),
			(46, "dpd"), (30, "polarization"), (36, "thole"),
			(22, "drviol"), (22, "orires"), (22, "ordev"),
			(26, "dihres"), (26, "dihviol"),
			(49, "vsite4fdn"), (50, "vsiten"), (46, "com_pull"),
			(20, "eqm"), (46, "econs"), (54, "dg/dl_con")
		]
		self.xdr.unpack_uint()
		if version < 57:
			self.xdr.unpack_uint()
		ntypes = self.xdr.unpack_uint()

		functions = []
		for i in range(ntypes):
			fnum = self.xdr.unpack_uint()
			for vn, funcName in self.funcNumberUpdate:
				if version < vn \
				and fnum >= self.funcNumber[funcName]:
					fnum += 1
			funcName = self.funcNames[fnum]
			functions.append(funcName)
		if version >= 57:
			self.unpackFloatFunc()
		for i, funcName in enumerate(functions):
			if funcName in ["angles", "g96angles", "bonds",
					"g96bonds", "harmonic", "idihs",
					"cross_bond_angle", "urey_bradley",
					"thole", "lj14", "ljc_nb"]:
				floatInts = "rrrr"
			elif funcName in ["fenebonds", "lj_sr", "constr",
					"constrnc", "settle", "vsite3",
					"vsite3fd", "vsite3fad"]:
				floatInts = "rr"
			elif funcName in ["cross_bond_bond", "bham", "morse",
					"cubic_bonds", "vsite3out", "vsite4fd",
					"vsite4fdn"]:
				floatInts = "rrr"
			elif funcName in ["qangles", "waterpol"]:
				floatInts = "rrrrrr"
			elif funcName in ["connbonds"]:
				floatInts = ""
			elif funcName in ["polarization", "vsite2"]:
				floatInts = "r"
			elif funcName in ["pdihs", "pidihs"]:
				floatInts = "rrrri"
			elif funcName in ["disres"]:
				floatInts = "iirrrr"
			elif funcName in ["orires"]:
				floatInts = "iiirrr"
			elif funcName in ["dihres"]:
				floatInts = "iirrr"
			elif funcName in ["posres"]:
				if version < 27:
					floatInts = "rrrrrr"
				else:
					floatInts = "rrrrrrrrrrrr"
			elif funcName in ["rbdihs"]:
				if version >= 25:
					floatInts = "rrrrrrrrrrrr"
				else:
					floatInts = "rrrrrr"
			elif funcName in ["fourdihs"]:
				floatInts = "rrrrrrrrrrrr"
			elif funcName in ["tabbonds", "tabbondsnc", "tabangles",
					"tabdihs"]:
				floatInts = "rir"
			elif funcName in ["ljc14_q"]:
				floatInts = "rrrrr"
			elif funcName in ["angres", "angresz"]:
				if version < 42:
					floatInts = "rrrr"
				else:
					floatInts = "rrrri"
			elif funcName in ["vsiten"]:
				floatInts = "ir"
			else:
				raise ValueError("Don't know correct"
						" parameters for '%s' function"
						% funcName)
			for fi in floatInts:
				if fi == 'r':
					t = self.unpackFloatFunc()
				else:
					t = self.xdr.unpack_uint()

	def _do_ilists(self, version):
		# bonds is first func, at least
		seen = set()
		self.bonds = []
		checklist = set()
		def addBond(a1, a2):
			if (a1, a2) in checklist:
				return
			self.bonds.append((a1, a2))
			checklist.add((a1, a2))
			checklist.add((a2, a1))
			seen.add(a1)
			seen.add(a2)
		for fname in self.funcNames:
			for uv, uname in self.funcNumberUpdate:
				if version < uv and fname == uname:
					skip = True
					break
			else:
				skip = False
			if skip:
				continue
			if version < 44:
				for i in range(256):
					self.xdr.unpack_uint()
			nr = self.xdr.unpack_uint()
			if fname == "bonds":
				for i in range(nr/3):
					self.xdr.unpack_uint()
					a1index = self.xdr.unpack_uint()
					a2index = self.xdr.unpack_uint()
					addBond(a1index, a2index)
			elif fname in ["angles", "g96angles"]:
				for i in range(nr/4):
					self.xdr.unpack_uint()
					a1index = self.xdr.unpack_uint()
					a2index = self.xdr.unpack_uint()
					a3index = self.xdr.unpack_uint()
					addBond(a1index, a2index)
					addBond(a2index, a3index)
			else:
				for i in range(nr):
					self.xdr.unpack_uint()
		# okay, the above doesn't hook up water (and maybe methane?)...
		hyds = {}
		heavys = {}
		for i, element in enumerate(self.elements):
			if i in seen:
				continue
			key = self.resNums[i]
			if element.number == 1:
				hyds.setdefault(key, []).append(i)
			else:
				heavys.setdefault(key, []).append(i)
		for i, heavysList in heavys.items():
			if len(heavysList) != 1 or i not in hyds:
				# beats me what to do
				continue
			heavy = heavysList[0]
			for hyd in hyds[i]:
				self.bonds.append((heavy, hyd))
		self.molInfo.append((self.atomNames, self.resNames,
				self.resIndices, self.bonds, self.elements))
		
	def _readHeader(self):
		# version string
		replyobj.info("%s\n" % self._readString())
		realSize = self.xdr.unpack_uint()
		if realSize == 4:
			self.unpackFloatFunc = self.xdr.unpack_float
			replyobj.info("using floats\n")
		elif realSize == 8:
			self.unpackFloatFunc = self.xdr.unpack_double
			replyobj.info("using doubles\n")
		else:
			raise ValueError("Floating-point values in .tpr file"
				" are not the same as either single-precision"
				" or double-precision floating point on this"
				" machine")
		version = self.xdr.unpack_uint()
		if version >= 26:
			generation = self.xdr.unpack_uint()
		else:
			generation = 0
		replyobj.info("version %d, generation %d\n"
						% (version, generation))
		natoms = self.xdr.unpack_uint()
		replyobj.info("%d atoms\n" % natoms)
		if version >= 28:
			tempCouplingGroups = self.xdr.unpack_uint()
		curStep = self.xdr.unpack_uint()
		curTime = self.unpackFloatFunc()
		curLambda = self.unpackFloatFunc()
		hasInputRec = self.xdr.unpack_uint()
		hasTopology = self.xdr.unpack_uint()
		if not hasTopology:
			raise ValueError(
				".tpr file does not have topology section")

		hasCoord = self.xdr.unpack_uint()
		hasVelocities = self.xdr.unpack_uint()
		hasForces = self.xdr.unpack_uint()
		hasBbox = self.xdr.unpack_uint()

		if hasBbox:
			for i in range(9):
				self.unpackFloatFunc()
			if version >= 51:
				for i in range(9):
					self.unpackFloatFunc()
			if version >= 28:
				for i in range(9):
					self.unpackFloatFunc()
				if version < 56:
					for i in range(9):
						self.unpackFloatFunc()

		if version >= 28 and tempCouplingGroups > 0:
			for i in range(tempCouplingGroups):
				self.unpackFloatFunc()
				self.unpackFloatFunc()

		if version < 26 and hasInputRec:
			raise ValueError("Cannot read version 26 or earlier"
				" .tpr files")

		return version

	def _readString(self):
		strlen = self.xdr.unpack_uint()
		self.xdr.unpack_uint()
		return self.xdr.unpack_fstring(strlen-1)

	def _readTopology(self, version):
		self.molInfo = []
		numStrings = self.xdr.unpack_uint()
		symbols = []
		for i in range(numStrings):
			symbols.append(self._readString())
		name = symbols[self.xdr.unpack_uint()]
		replyobj.info("%s\n" % name)

		if version >= 57:
			self._do_ffparams(version)
			numMolTypes = self.xdr.unpack_uint()
		else:
			numMolTypes = 1

		from Trajectory import determineElementFromMass
		for i in range(numMolTypes):
			if version >= 57:
				molName = symbols[self.xdr.unpack_uint()]
				replyobj.info("mol name: %s\n" % molName)
				

			numAtoms = self.xdr.unpack_uint()
			replyobj.info("%d atoms\n" % numAtoms)
			numResidues = self.xdr.unpack_uint()
			replyobj.info("%d residues\n" % numResidues)
			if version < 57:
				numGroupNames = self.xdr.unpack_uint()
				if version < 23:
					numGroups = 8
				elif version < 39:
					numGroups = 9
				else:
					numGroups = 10
			self.elements = []
			self.resNums = []
			for i in range(numAtoms):
				mass = self.unpackFloatFunc()
				self.elements.append(
						determineElementFromMass(mass))
				charge = self.unpackFloatFunc()
				self.unpackFloatFunc()
				self.unpackFloatFunc()
				for j in range(3):
					self.xdr.unpack_uint()
				resNum = self.xdr.unpack_uint()
				self.resNums.append(resNum)
				if version >= 52:
					self.xdr.unpack_uint()
				if version < 57:
					for j in range(numGroups):
						self.xdr.unpack_uint()
			self.atomNames = [symbols[self.xdr.unpack_uint()]
						for i in range(numAtoms)]
			for i in range(numAtoms*2):
				self.xdr.unpack_uint()
			self.resNames = [symbols[self.xdr.unpack_uint()]
						for i in range(numResidues)]
			self.resIndices = []
			curResNum = None
			for i, rn in enumerate(self.resNums):
				if curResNum != rn:
					self.resIndices.append(i+1)
					curResNum = rn
			
			if version < 57:
				# group names
				for i in range(numGroupNames):
					gn = symbols[self.xdr.unpack_uint()]

				# group contents
				for i in range(numGroups):
					grpN = self.xdr.unpack_uint()
					for j in range(grpN):
						self.xdr.unpack_uint()

			if version >= 57:
				self._do_ilists(version)
				self._do_block(version)

			self._do_blocka(version)

		if version >= 57:
			self.atomNames = []
			self.resNames = []
			self.resIndices = []
			self.bonds = []
			self.elements = []
		
			numMolBlocks = self.xdr.unpack_uint()
			for i in range(numMolBlocks):
				self.xdr.unpack_uint()
				repeat = self.xdr.unpack_uint()
				self.xdr.unpack_uint()
				# position restraints
				for j in range(2):
					npr = self.xdr.unpack_uint()
					for k in range(npr):
						self.unpackFloatFunc()
						self.unpackFloatFunc()
						self.unpackFloatFunc()
				atomNames, resNames, resIndices, bonds, \
						elements = self.molInfo[i]
				for j in range(repeat):
					aBase = len(self.atomNames)
					self.atomNames.extend(atomNames)
					self.resNames.extend(resNames)
					for ri in resIndices:
						self.resIndices.append(
								aBase + ri)
					for a1, a2 in bonds:
						self.bonds.append((aBase + a1,
								aBase + a2))
					self.elements.extend(elements)
		
			self.xdr.unpack_uint()

		# atom types
		if version > 25:
			nr = self.xdr.unpack_uint()
			# radii
			for i in range(nr):
				self.unpackFloatFunc()
			# volume
			for i in range(nr):
				self.unpackFloatFunc()
			# surface tension
			for i in range(nr):
				self.unpackFloatFunc()
			if version >= 40:
				# atom number
				for i in range(nr):
					self.xdr.unpack_uint()

		if version < 57:
			self._do_ffparams(version)
			if version >= 54:
				self.unpackFloatFunc()
			self._do_ilists(version)
Ejemplo n.º 3
0
Archivo: mesgen.py Proyecto: zooko/egtp
    def __parse_header(self, header):
        """
        Parses a header and stores information contained in it as necessary
        
        Returns (counterparty pub key sexp, symmetric key) throws Error
        """
        assert type(header) == type('')
        try:
            hash = sha(header).digest()
            cached = self.__cached_headers.get(hash)
            if cached is not None:
                return cached
            u = Unpacker(header)
            # messages start with the hash of the recipient's public id
            recipient_id = u.unpack_fstring(SIZE_OF_UNIQS)
            if recipient_id != self.__my_public_key_id:
                raise Error, 'message not intended for me'
            # unpack PK encrypted public key
            encrypted_key = u.unpack_string()
            self.__key.set_value_string(encrypted_key)
            self.__key.decrypt()   # PKop
            decrypted = self.__key.get_value()

            try:
                symmetric_key = cryptutil.oaep_decode(decrypted[1:]) # Leave off the initial 0 byte. ### XX check whether it really is 0 and raise bad-encoding error if not.  --Zooko 2000-07-29
            except cryptutil.OAEPError, le:
                raise Error, 'bad encryption -- pad badding: padded: %s, Error: %s' % (`decrypted`, `le.args`)

            iv = u.unpack_fstring(8)
            # first half of the MAC # XXX A.K.A. the key?  --Zooko 2000-07-29
            prefix = header[:u.get_position()]
            # all data except the symmetric key and recipient, encrypted
            encrypted = u.unpack_string()
            u.done()

            decrypted = tripledescbc.new(symmetric_key).decrypt(iv, encrypted)                
            u = Unpacker(decrypted)
            # the full public key of the sender
            sender_key = u.unpack_string()
            full_key = MojoKey.makePublicRSAKeyForCommunicating(modval.new(sender_key, HARDCODED_RSA_PUBLIC_EXPONENT))
            full_key_id = idlib.make_id(full_key, 'broker')
            # the session id for messages sent 'here'
            id_in = _mix_counterparties(full_key_id, self.__my_public_key_id, u.unpack_fstring(SIZE_OF_UNIQS))
            # the session id for messages sent 'there'
            id_out = _mix_counterparties(full_key_id, self.__my_public_key_id, u.unpack_fstring(SIZE_OF_UNIQS))
            # check that the pk encrypted symmetric key used to send this message is the same was generated properly
            strl = u.unpack_fstring(SIZE_OF_UNIQS)
            sr = HashRandom.SHARandom(_mix_counterparties(full_key_id, self.__my_public_key_id, strl))
            spaml = sr.get(SIZE_OF_SYMMETRIC_KEYS)
            if symmetric_key != spaml:
                raise Error, 'improperly generated key'
            # the second half of what's in the MAC # XXX A.K.A. the message?  --Zooko 2000-07-29
            end = decrypted[:u.get_position()]
            # the signature of everything
            signature = u.unpack_fstring(len(sender_key))
            u.done()
            
            # debugprint("------ ------ ------ ------ hmachish(key=%s, message=%s)\n" % (`symmetric_key`, `end`))
            summary = cryptutil.hmacish(key=symmetric_key, message=end)
            
            x = modval.new(sender_key, HARDCODED_RSA_PUBLIC_EXPONENT, signature)
            x.undo_signature()   # PKop
            signed_value = x.get_value()

            try:
                thingie = cryptutil.oaep_decode(signed_value[1:]) # Leave off the initial 0 byte. ### XX check whether it really is 0 and raise bad-encoding error if not.  --Zooko 2000-07-29
            except cryptutil.OAEPError, le:
                raise Error, 'bad encryption -- pad badding: padded: %s, Error: %s' % (`signed_value`, `le.args`)
class TprTopology:
    funcNames = [
        "bonds", "g96bonds", "morse", "cubicbonds", "connbonds", "harmonic",
        "fenebonds", "tabbonds", "tabbondsnc", "angles", "g96angles",
        "cross_bond_bond", "cross_bond_angle", "urey_bradley", "qangles",
        "tabangles", "pdihs", "rbdihs", "fourdihs", "idihs", "pidihs",
        "tabdihs", "lj14", "coul14", "ljc14_q", "ljc_nb", "lj_sr", "bham",
        "lj_lr", "bham_lr", "dispcorr", "coul_sr", "coul_lr", "rf_excl",
        "coul_recip", "dpd", "polarization", "waterpol", "thole", "posres",
        "disres", "drviol", "orires", "ordev", "angres", "angresz", "dihres",
        "dihviol", "constr", "constrnc", "settle", "vsite2", "vsite3",
        "vsite3fd", "vsite3fad", "vsite3out", "vsite4fd", "vsite4fdn",
        "vsiten", "com_pull", "eqm", "epot", "ekin", "etot", "econs", "temp",
        "pres", "dv/dl", "dk/dl", "dg/dl_con"
    ]

    def __init__(self, topology):
        from OpenSave import osOpen
        topFile = osOpen(topology, 'rb')
        import os
        self.topFileSize = os.stat(topology).st_size
        from xdrlib import Unpacker
        self.fileString = FileString(topFile, 0, self.topFileSize)
        self.xdr = Unpacker(self.fileString)
        version = self._readHeader()
        self._readTopology(version)

    def _do_block(self, version):
        if version < 44:
            for i in range(256):
                self.xdr.unpack_uint()
        blockNR = self.xdr.unpack_uint()
        if version < 51:
            blockNRA = self.xdr.unpack_uint()
        for i in range(blockNR + 1):
            self.xdr.unpack_uint()
        if version < 51:
            for i in range(blockNRA):
                self.xdr.unpack_uint()

    def _do_blocka(self, version):
        if version < 44:
            for i in range(256):
                self.xdr.unpack_uint()
        blockNR = self.xdr.unpack_uint()
        blockNRA = self.xdr.unpack_uint()
        for i in range(blockNR + blockNRA + 1):
            self.xdr.unpack_uint()

    def _do_ffparams(self, version):
        self.funcNumber = {}
        for i, fn in enumerate(self.funcNames):
            self.funcNumber[fn] = i
        self.funcNumberUpdate = [(20, "cubicbonds"), (20, "connbonds"),
                                 (20, "harmonic"), (34, "fenebonds"),
                                 (43, "tabbonds"), (43, "tabbondsnc"),
                                 (30, "cross_bond_bond"),
                                 (30, "cross_bond_angle"),
                                 (30, "urey_bradley"), (34, "qangles"),
                                 (43, "tabangles"), (26, "fourdihs"),
                                 (26, "pdihs"), (43, "tabdihs"),
                                 (41, "ljc14_q"), (41, "ljc_nb"),
                                 (32, "bham_lr"), (32, "rf_excl"),
                                 (32, "coul_recip"), (46, "dpd"),
                                 (30, "polarization"), (36, "thole"),
                                 (22, "drviol"), (22, "orires"), (22, "ordev"),
                                 (26, "dihres"), (26, "dihviol"),
                                 (49, "vsite4fdn"), (50, "vsiten"),
                                 (46, "com_pull"), (20, "eqm"), (46, "econs"),
                                 (54, "dg/dl_con")]
        self.xdr.unpack_uint()
        if version < 57:
            self.xdr.unpack_uint()
        ntypes = self.xdr.unpack_uint()

        functions = []
        for i in range(ntypes):
            fnum = self.xdr.unpack_uint()
            for vn, funcName in self.funcNumberUpdate:
                if version < vn \
                and fnum >= self.funcNumber[funcName]:
                    fnum += 1
            funcName = self.funcNames[fnum]
            functions.append(funcName)
        if version >= 57:
            self.unpackFloatFunc()
        for i, funcName in enumerate(functions):
            if funcName in [
                    "angles", "g96angles", "bonds", "g96bonds", "harmonic",
                    "idihs", "cross_bond_angle", "urey_bradley", "thole",
                    "lj14", "ljc_nb"
            ]:
                floatInts = "rrrr"
            elif funcName in [
                    "fenebonds", "lj_sr", "constr", "constrnc", "settle",
                    "vsite3", "vsite3fd", "vsite3fad"
            ]:
                floatInts = "rr"
            elif funcName in [
                    "cross_bond_bond", "bham", "morse", "cubic_bonds",
                    "vsite3out", "vsite4fd", "vsite4fdn"
            ]:
                floatInts = "rrr"
            elif funcName in ["qangles", "waterpol"]:
                floatInts = "rrrrrr"
            elif funcName in ["connbonds"]:
                floatInts = ""
            elif funcName in ["polarization", "vsite2"]:
                floatInts = "r"
            elif funcName in ["pdihs", "pidihs"]:
                floatInts = "rrrri"
            elif funcName in ["disres"]:
                floatInts = "iirrrr"
            elif funcName in ["orires"]:
                floatInts = "iiirrr"
            elif funcName in ["dihres"]:
                floatInts = "iirrr"
            elif funcName in ["posres"]:
                if version < 27:
                    floatInts = "rrrrrr"
                else:
                    floatInts = "rrrrrrrrrrrr"
            elif funcName in ["rbdihs"]:
                if version >= 25:
                    floatInts = "rrrrrrrrrrrr"
                else:
                    floatInts = "rrrrrr"
            elif funcName in ["fourdihs"]:
                floatInts = "rrrrrrrrrrrr"
            elif funcName in [
                    "tabbonds", "tabbondsnc", "tabangles", "tabdihs"
            ]:
                floatInts = "rir"
            elif funcName in ["ljc14_q"]:
                floatInts = "rrrrr"
            elif funcName in ["angres", "angresz"]:
                if version < 42:
                    floatInts = "rrrr"
                else:
                    floatInts = "rrrri"
            elif funcName in ["vsiten"]:
                floatInts = "ir"
            else:
                raise ValueError("Don't know correct"
                                 " parameters for '%s' function" % funcName)
            for fi in floatInts:
                if fi == 'r':
                    t = self.unpackFloatFunc()
                else:
                    t = self.xdr.unpack_uint()

    def _do_ilists(self, version):
        # bonds is first func, at least
        seen = set()
        self.bonds = []
        checklist = set()

        def addBond(a1, a2):
            if (a1, a2) in checklist:
                return
            self.bonds.append((a1, a2))
            checklist.add((a1, a2))
            checklist.add((a2, a1))
            seen.add(a1)
            seen.add(a2)

        for fname in self.funcNames:
            for uv, uname in self.funcNumberUpdate:
                if version < uv and fname == uname:
                    skip = True
                    break
            else:
                skip = False
            if skip:
                continue
            if version < 44:
                for i in range(256):
                    self.xdr.unpack_uint()
            nr = self.xdr.unpack_uint()
            if fname == "bonds":
                for i in range(nr / 3):
                    self.xdr.unpack_uint()
                    a1index = self.xdr.unpack_uint()
                    a2index = self.xdr.unpack_uint()
                    addBond(a1index, a2index)
            elif fname in ["angles", "g96angles"]:
                for i in range(nr / 4):
                    self.xdr.unpack_uint()
                    a1index = self.xdr.unpack_uint()
                    a2index = self.xdr.unpack_uint()
                    a3index = self.xdr.unpack_uint()
                    addBond(a1index, a2index)
                    addBond(a2index, a3index)
            else:
                for i in range(nr):
                    self.xdr.unpack_uint()
        # okay, the above doesn't hook up water (and maybe methane?)...
        hyds = {}
        heavys = {}
        for i, element in enumerate(self.elements):
            if i in seen:
                continue
            key = self.resNums[i]
            if element.number == 1:
                hyds.setdefault(key, []).append(i)
            else:
                heavys.setdefault(key, []).append(i)
        for i, heavysList in heavys.items():
            if len(heavysList) != 1 or i not in hyds:
                # beats me what to do
                continue
            heavy = heavysList[0]
            for hyd in hyds[i]:
                self.bonds.append((heavy, hyd))
        self.molInfo.append((self.atomNames, self.resNames, self.resIndices,
                             self.bonds, self.elements))

    def _readHeader(self):
        # version string
        replyobj.info("%s\n" % self._readString())
        realSize = self.xdr.unpack_uint()
        if realSize == 4:
            self.unpackFloatFunc = self.xdr.unpack_float
            replyobj.info("using floats\n")
        elif realSize == 8:
            self.unpackFloatFunc = self.xdr.unpack_double
            replyobj.info("using doubles\n")
        else:
            raise ValueError("Floating-point values in .tpr file"
                             " are not the same as either single-precision"
                             " or double-precision floating point on this"
                             " machine")
        version = self.xdr.unpack_uint()
        if version >= 26:
            generation = self.xdr.unpack_uint()
        else:
            generation = 0
        replyobj.info("version %d, generation %d\n" % (version, generation))
        natoms = self.xdr.unpack_uint()
        replyobj.info("%d atoms\n" % natoms)
        if version >= 28:
            tempCouplingGroups = self.xdr.unpack_uint()
        curStep = self.xdr.unpack_uint()
        curTime = self.unpackFloatFunc()
        curLambda = self.unpackFloatFunc()
        hasInputRec = self.xdr.unpack_uint()
        hasTopology = self.xdr.unpack_uint()
        if not hasTopology:
            raise ValueError(".tpr file does not have topology section")

        hasCoord = self.xdr.unpack_uint()
        hasVelocities = self.xdr.unpack_uint()
        hasForces = self.xdr.unpack_uint()
        hasBbox = self.xdr.unpack_uint()

        if hasBbox:
            for i in range(9):
                self.unpackFloatFunc()
            if version >= 51:
                for i in range(9):
                    self.unpackFloatFunc()
            if version >= 28:
                for i in range(9):
                    self.unpackFloatFunc()
                if version < 56:
                    for i in range(9):
                        self.unpackFloatFunc()

        if version >= 28 and tempCouplingGroups > 0:
            for i in range(tempCouplingGroups):
                self.unpackFloatFunc()
                self.unpackFloatFunc()

        if version < 26 and hasInputRec:
            raise ValueError("Cannot read version 26 or earlier" " .tpr files")

        return version

    def _readString(self):
        strlen = self.xdr.unpack_uint()
        self.xdr.unpack_uint()
        return self.xdr.unpack_fstring(strlen - 1)

    def _readTopology(self, version):
        self.molInfo = []
        numStrings = self.xdr.unpack_uint()
        symbols = []
        for i in range(numStrings):
            symbols.append(self._readString())
        name = symbols[self.xdr.unpack_uint()]
        replyobj.info("%s\n" % name)

        if version >= 57:
            self._do_ffparams(version)
            numMolTypes = self.xdr.unpack_uint()
        else:
            numMolTypes = 1

        from Trajectory import determineElementFromMass
        for i in range(numMolTypes):
            if version >= 57:
                molName = symbols[self.xdr.unpack_uint()]
                replyobj.info("mol name: %s\n" % molName)

            numAtoms = self.xdr.unpack_uint()
            replyobj.info("%d atoms\n" % numAtoms)
            numResidues = self.xdr.unpack_uint()
            replyobj.info("%d residues\n" % numResidues)
            if version < 57:
                numGroupNames = self.xdr.unpack_uint()
                if version < 23:
                    numGroups = 8
                elif version < 39:
                    numGroups = 9
                else:
                    numGroups = 10
            self.elements = []
            self.resNums = []
            for i in range(numAtoms):
                mass = self.unpackFloatFunc()
                self.elements.append(determineElementFromMass(mass))
                charge = self.unpackFloatFunc()
                self.unpackFloatFunc()
                self.unpackFloatFunc()
                for j in range(3):
                    self.xdr.unpack_uint()
                resNum = self.xdr.unpack_uint()
                self.resNums.append(resNum)
                if version >= 52:
                    self.xdr.unpack_uint()
                if version < 57:
                    for j in range(numGroups):
                        self.xdr.unpack_uint()
            self.atomNames = [
                symbols[self.xdr.unpack_uint()] for i in range(numAtoms)
            ]
            for i in range(numAtoms * 2):
                self.xdr.unpack_uint()
            self.resNames = [
                symbols[self.xdr.unpack_uint()] for i in range(numResidues)
            ]
            self.resIndices = []
            curResNum = None
            for i, rn in enumerate(self.resNums):
                if curResNum != rn:
                    self.resIndices.append(i + 1)
                    curResNum = rn

            if version < 57:
                # group names
                for i in range(numGroupNames):
                    gn = symbols[self.xdr.unpack_uint()]

                # group contents
                for i in range(numGroups):
                    grpN = self.xdr.unpack_uint()
                    for j in range(grpN):
                        self.xdr.unpack_uint()

            if version >= 57:
                self._do_ilists(version)
                self._do_block(version)

            self._do_blocka(version)

        if version >= 57:
            self.atomNames = []
            self.resNames = []
            self.resIndices = []
            self.bonds = []
            self.elements = []

            numMolBlocks = self.xdr.unpack_uint()
            for i in range(numMolBlocks):
                self.xdr.unpack_uint()
                repeat = self.xdr.unpack_uint()
                self.xdr.unpack_uint()
                # position restraints
                for j in range(2):
                    npr = self.xdr.unpack_uint()
                    for k in range(npr):
                        self.unpackFloatFunc()
                        self.unpackFloatFunc()
                        self.unpackFloatFunc()
                atomNames, resNames, resIndices, bonds, \
                  elements = self.molInfo[i]
                for j in range(repeat):
                    aBase = len(self.atomNames)
                    self.atomNames.extend(atomNames)
                    self.resNames.extend(resNames)
                    for ri in resIndices:
                        self.resIndices.append(aBase + ri)
                    for a1, a2 in bonds:
                        self.bonds.append((aBase + a1, aBase + a2))
                    self.elements.extend(elements)

            self.xdr.unpack_uint()

        # atom types
        if version > 25:
            nr = self.xdr.unpack_uint()
            # radii
            for i in range(nr):
                self.unpackFloatFunc()
            # volume
            for i in range(nr):
                self.unpackFloatFunc()
            # surface tension
            for i in range(nr):
                self.unpackFloatFunc()
            if version >= 40:
                # atom number
                for i in range(nr):
                    self.xdr.unpack_uint()

        if version < 57:
            self._do_ffparams(version)
            if version >= 54:
                self.unpackFloatFunc()
            self._do_ilists(version)