Пример #1
0
	def testProfileHash(self):
		# Clear out and reset own profile
		myTorId = "ABC123DEF456GH78"
		myprofile = {"name" : "Constantin Taylor", "keyid" : "someKeyId", "displayName" : "Me",
					"status" : "self", "ownprofile" : True, "description":"Some fairly descriptive text",
					"birthday":None, "interests":"chocolate pudding with fudge"}
		DbClient.updateContact(myTorId, myprofile)
		firstHash = DbClient.calculateHash(DbClient.getProfile())
		self.assertEqual(firstHash, "12ae5c8dc8e1c2186b4ed4918040bb16", "First hash not what I was expecting")
		# Now change interests and check that hash changes
		DbClient.updateContact(myTorId, {"interests":"roasted vegetables and hummus"})
		secondHash = DbClient.calculateHash(DbClient.getProfile())
		self.assertNotEqual(firstHash, secondHash, "Profile hash should have changed")
Пример #2
0
	def generateListPage(self, doEdit=False, userid=None, extraParams=None):
		self.requirePageResources(['avatar-none.jpg', 'status-self.png', 'status-requested.png', 'status-untrusted.png', 'status-trusted.png'])
		# List of contacts, and show details for the selected one (or self if userid=None)
		selectedprofile = DbClient.getProfile(userid)
		if selectedprofile is None:
			selectedprofile = DbClient.getProfile()
		userid = selectedprofile['torid']
		ownPage = userid == DbClient.getOwnTorId()

		# Build list of contacts
		userboxes = []
		for p in DbClient.getContactList():
			box = Bean()
			box.dispName = p['displayName']
			box.torid = p['torid']
			box.tilestyle = "contacttile" + ("selected" if p['torid'] == userid else "")
			box.status = p['status']
			box.isonline = Contacts.isOnline(box.torid)
			userboxes.append(box)
		# expand templates using current details
		lefttext = self.listtemplate.getHtml({'webcachedir' : Config.getWebCacheDir(), 'contacts' : userboxes})
		pageProps = {"webcachedir" : Config.getWebCacheDir(), 'person':selectedprofile}
		# Add extra parameters if necessary
		if extraParams:
			pageProps.update(extraParams)

		# See which contacts we have in common with this person
		(sharedContactIds, possIdsForThem, possIdsForMe, nameMap) = ContactMaker.getSharedAndPossibleContacts(userid)
		sharedContacts = self._makeIdAndNameBeanList(sharedContactIds, nameMap)
		pageProps.update({"sharedcontacts" : sharedContacts})
		possibleContacts = self._makeIdAndNameBeanList(possIdsForThem, nameMap)
		pageProps.update({"possiblecontactsforthem" : possibleContacts})
		possibleContacts = self._makeIdAndNameBeanList(possIdsForMe, nameMap)
		pageProps.update({"possiblecontactsforme" : possibleContacts})

		# Which template to use depends on whether we're just showing or also editing
		if doEdit:
			# Use two different details templates, one for self and one for others
			detailstemplate = self.editowndetailstemplate if ownPage else self.editdetailstemplate
			righttext = detailstemplate.getHtml(pageProps)
		else:
			detailstemplate = self.detailstemplate  # just show
			righttext = detailstemplate.getHtml(pageProps)

		contents = self.buildTwoColumnPage({'pageTitle' : I18nManager.getText("contacts.title"),
			'leftColumn' : lefttext,
			'rightColumn' : righttext,
			'pageFooter' : "<p>Footer</p>"})
		return contents
Пример #3
0
	def generateFingerprintsPage(self, userid):
		'''Build the page for checking the fingerprints of the selected user'''
		# First, get the name of the user
		person = DbClient.getProfile(userid, False)
		dispName = person.get('displayName', '')
		fullName = person.get('name', '')
		if not dispName: dispName = fullName
		if dispName != fullName:
			fullName = dispName + " (" + fullName + ")"
		fc = self._makeFingerprintChecker(userid)
		# check it's ok to generate
		status = person.get('status', '')
		if not fc.valid \
		  or status not in ['untrusted', 'trusted']:
			print("Not generating fingerprints page because status is", status)
			return None

		# Get one set of words for us and three sets for them
		printsAlreadyChecked = (person.get('status', '') == "trusted")
		bodytext = self.fingerprintstemplate.getHtml(
			{"mywords":fc.getCodeWords(True, 0, "en"), "theirwords0":fc.getCodeWords(False, 0, "en"),
			 "theirwords1":fc.getCodeWords(False, 1, "en"), "theirwords2":fc.getCodeWords(False, 2, "en"),
			 "fullname":fullName, "shortname":dispName, "userid":userid, "alreadychecked":printsAlreadyChecked})
		return self.buildPage({'pageTitle' : I18nManager.getText("contacts.title"),
			'pageBody' : bodytext,
			'pageFooter' : "<p>Footer</p>"})
Пример #4
0
	def getPublicKey(self, torid):
		'''Use the keyid stored in mongo, and get the corresponding public key from the Crypto module'''
		profile = DbClient.getProfile(torid)
		if profile is not None:
			keyid = profile.get('keyid', None)
			if keyid is not None:
				return CryptoClient.getPublicKey(keyid)
Пример #5
0
	def dealWithUnencryptedMessage(message):
		'''Decide what to do with the given unencrypted message'''
		if message.messageType == Message.TYPE_CONTACT_REQUEST:
			print("Received a contact request from", message.senderId)
			# Check config to see whether we accept these or not
			if Config.getProperty(Config.KEY_ALLOW_FRIEND_REQUESTS) \
			  and MessageShuffler._isProfileStatusOk(message.senderId, [None, 'requested', 'untrusted', 'trusted']):
				# Call DbClient to store new message in inbox
				rowToStore = {"messageType":"contactrequest", "fromId":message.senderId,
					"fromName":message.senderName, "messageBody":message.message,
					"publicKey":message.publicKey, "timestamp":message.timestamp,
					"messageRead":False, "messageReplied":False}
				DbClient.addMessageToInbox(rowToStore)
		elif message.messageType == Message.TYPE_CONTACT_RESPONSE:
			print("It's an unencrypted contact response, so it must be a refusal")
			sender = DbClient.getProfile(message.senderId, False)
			if MessageShuffler._isProfileStatusOk(message.senderId, ['requested']):
				senderName = sender.get("displayName") if sender else ""
				ContactMaker.handleReceiveDeny(message.senderId)
				# Call DbClient to store new message in inbox
				rowToStore = {"messageType":"contactresponse", "fromId":message.senderId,
					"fromName":senderName, "messageBody":"", "accepted":False,
					"messageRead":False, "messageReplied":False, "timestamp":message.timestamp,
					"recipients":MessageShuffler.getOwnTorId()}
				DbClient.addMessageToInbox(rowToStore)
		else:
			print("Hä?  It's unencrypted but the message type is", message.messageType)
Пример #6
0
	def dealWithMessage(message):
		'''Examine the received message and decide what to do with it'''
		print("Hmm, the MessageShuffler has been given some kind of message")
		# We must be online if we've received a message
		Contacts.comeOnline(MessageShuffler.getOwnTorId())

		if message.senderMustBeTrusted:
			sender = DbClient.getProfile(message.senderId, False)
			if not sender or sender['status'] != "trusted":
				return # throw message away

		if not message.isComplete():
			print("A message of type", message.encryptionType, "was received but it's not complete - throwing away")
			return # throw message away

		# if it's not encrypted, it's for us -> save in inbox
		if message.encryptionType == Message.ENCTYPE_NONE:
			MessageShuffler.dealWithUnencryptedMessage(message)

		elif message.encryptionType == Message.ENCTYPE_SYMM:
			# if it's symmetric, forget it for now
			pass

		elif message.encryptionType == Message.ENCTYPE_ASYM:
			MessageShuffler.dealWithAsymmetricMessage(message)

		else:
			print("Hä?  What kind of encryption type is that? ", message.encryptionType)

		# Log receipt of message (do we want to know about relays at all?)
		if message.encryptionType in [Message.ENCTYPE_NONE, Message.ENCTYPE_ASYM]:
			logMessage = "Message of type: %s received from %s" % (message.getMessageTypeKey(), message.senderId)
			MessageShuffler.getTannoy().shout(logMessage)
Пример #7
0
	def _createSubpayload(self):
		'''Use the stored fields to pack the payload contents together'''
		if self.profileHash is None or self.profileHash == "":
			self.profileHash = DbClient.calculateHash(DbClient.getProfile())
		return self.packBytesTogether([
			self.encodeNumberToBytes(1 if self.online else 0, 1),
			self.encodeNumberToBytes(1 if self.ping else 0, 1),
			self.profileHash])
Пример #8
0
 def testProfileResponse(self):
     m = message.InfoResponseMessage(message.InfoRequestMessage.INFO_PROFILE)
     output = m.createUnencryptedOutput()
     bac = message.Message.MessageFromReceivedData(output, False)
     self.assertIsNotNone(bac, "couldn't decode the data")
     self.assertEqual(message.InfoRequestMessage.INFO_PROFILE, bac.infoType)
     mydescription = DbClient.getProfile()["description"]
     bacProfile = bac.profile
     self.assertEqual(mydescription, bacProfile["description"])
Пример #9
0
	def testGetProfile(self):
		# Delete whole profiles table
		DbClient._getProfileTable().remove({})
		self.assertEqual(DbClient._getProfileTable().count(), 0, "Profiles table should be empty")
		# Add own profile
		myTorId = "ABC123DEF456GH78"
		myprofile = {"name" : "Constantin Taylor", "keyid" : "someKeyId", "displayName" : "Me",
					"status" : "self", "ownprofile" : True}
		DbClient.updateContact(myTorId, myprofile)
		self.assertEqual(DbClient._getProfileTable().count(), 1, "Profiles table should have my profile in it")

		profileFromDb = DbClient.getProfile(None)
		self.assertIsNotNone(profileFromDb, "Couldn't retrieve own profile")
		profileFromDb = DbClient.getProfile(myTorId)
		self.assertIsNotNone(profileFromDb, "Couldn't retrieve profile using own id")

		# Initiate contact with a new person
		otherTorId = "PQR123STU456VWX78"
		otherName = "Olivia Barnacles"
		DbClient.updateContact(otherTorId, {"status" : "untrusted", "keyid" : "donotknow", "name" : otherName})
		self.assertEqual(DbClient._getProfileTable().count(), 2, "Profiles table should have 2 profiles")
		self.assertEqual(DbClient.getMessageableContacts().count(), 1, "Profiles table should have 1 messageable")
		self.assertEqual(DbClient.getTrustedContacts().count(), 0, "Profiles table should have 0 trusted")
		profileFromDb = DbClient.getProfile(otherTorId)
		self.assertIsNotNone(profileFromDb, "Couldn't retrieve profile using other id")
		self.assertEqual(profileFromDb.get("name", None), otherName, "Profile name doesn't match what was stored")
		self.assertEqual(profileFromDb.get("status", None), "untrusted", "Profile status doesn't match what was stored")

		# Update existing record, change status
		DbClient.updateContact(otherTorId, {"status" : "trusted"})
		self.assertEqual(DbClient._getProfileTable().count(), 2, "Profiles table should still have 2 profiles")
		profileFromDb = DbClient.getProfile(otherTorId)
		self.assertIsNotNone(profileFromDb, "Couldn't retrieve profile using other id")
		self.assertEqual(profileFromDb.get("status", None), "trusted", "Profile status should have been updated")
		self.assertEqual(DbClient.getMessageableContacts().count(), 1, "Profiles table should have 1 messageable")
		self.assertEqual(DbClient.getTrustedContacts().count(), 1, "Profiles table should have 1 trusted")

		# Delete other contact
		DbClient.updateContact(otherTorId, {"status" : "deleted"})
		self.assertEqual(DbClient.getMessageableContacts().count(), 0, "Profiles table should have 0 messageable")
		self.assertEqual(DbClient.getTrustedContacts().count(), 0, "Profiles table should have 0 trusted")
		self.assertFalse(DbClient.hasFriends(), "Shouldn't have any friends any more")
Пример #10
0
	def keyFingerprintChecked(torId):
		'''The fingerprint of this contact's public key has been checked (over a separate channel)'''
		# Check that userid exists and that status is currently "untrusted" (trusted also doesn't hurt)
		profile = DbClient.getProfile(torId, False)
		if profile and profile.get("status", "nostatus") in ["untrusted", "trusted"]:
			# Update the user's status to trusted
			DbClient.updateContact(torId, {"status" : "trusted"})
			# Trigger a StatusNotify to tell them we're online
			notify = StatusNotifyMessage(online=True, ping=True, profileHash=None)
			notify.recipients = [torId]
			DbClient.addMessageToOutbox(notify)
Пример #11
0
	def _createSubpayload(self):
		'''Use the stored fields to pack the payload contents together'''
		if self.infoType is None:
			self.infoType = InfoRequestMessage.INFO_PROFILE
		if self.profileString is None:
			self.profileString = dbutils.getOwnProfileAsString()
		self.profileHash = DbClient.calculateHash(DbClient.getProfile())
		return self.packBytesTogether([
			self.encodeNumberToBytes(self.infoType),
			self.encodeNumberToBytes(len(self.profileString), 4),
			self.profileString,
			self.encodeNumberToBytes(len(self.profileHash), 4),
			self.profileHash])
Пример #12
0
	def servePage(self, view, url, params):
		DbClient.exportAvatars(Config.getWebCacheDir())
		if url == "/send":
			print("send message of type '%(messageType)s' to id '%(sendTo)s'" % params)
		elif url.startswith("/delete/"):
			DbClient.deleteMessageFromInbox(params.get("msgId", ""))
		# Make dictionary to convert ids to names
		contactNames = {c['torid']:c['displayName'] for c in DbClient.getContactList()}
		unknownSender = I18nManager.getText("messages.sender.unknown")
		unknownRecpt = I18nManager.getText("messages.recpt.unknown")
		# Get contact requests, responses and mails from inbox
		conreqs = []
		conresps = []
		mails = []
		for m in DbClient.getInboxMessages():
			m['msgId'] = str(m.get("_id", ""))
			if m['messageType'] == "contactrequest":
				conreqs.append(m)
			elif m['messageType'] == "contactrefer":
				senderId = m.get('fromId', None)
				m['senderName'] = contactNames.get(senderId, unknownSender)
				conreqs.append(m)
			elif m['messageType'] == "contactresponse":
				if not m.get('accepted', False):
					m['messageBody'] = I18nManager.getText("messages.contactrequest.refused")
					m['fromName'] = DbClient.getProfile(m['fromId'], True).get("displayName")
				elif not m.get('messageBody', False):
					m['messageBody'] = I18nManager.getText("messages.contactrequest.accepted")
				conresps.append(m)
			else:
				senderId = m.get('fromId', None)
				if not senderId and m.get('signatureKeyId', None):
					senderId = DbClient.findUserIdFromKeyId(m['signatureKeyId'])
				m['senderName'] = contactNames.get(senderId, unknownSender)
				m['sentTimeStr'] = self.makeLocalTimeString(m['timestamp'])
				# Split m['recipients'] by commas, and look up each id with contactNames
				recpts = m.get('recipients', '')
				if recpts:
					m['recipients'] = ", ".join([contactNames.get(i, unknownRecpt) for i in recpts.split(",")])
				else:
					m['recipients'] = unknownRecpt
				mails.append(m)
		bodytext = self.messagestemplate.getHtml({"contactrequests":conreqs, "contactresponses":conresps,
			"mails":mails, "nummessages":len(conreqs)+len(conresps)+len(mails),
			"webcachedir" : Config.getWebCacheDir()})
		contents = self.buildPage({'pageTitle' : I18nManager.getText("messages.title"),
			'pageBody' : bodytext,
			'pageFooter' : "<p>Footer</p>"})
		view.setHtml(contents)
Пример #13
0
	def _createSubpayload(self):
		'''Pack the specific fields into the subpayload'''
		# Get own name
		if self.senderName is None:
			self.senderName = DbClient.getProfile(None).get('name', self.senderId)
		# Get own public key (first get identifier from DbClient, then use that id to ask crypto module)
		myPublicKey = self.getOwnPublicKey()
		messageAsBytes = self.message.encode('utf-8')
		nameAsBytes = self.senderName.encode('utf-8')
		subpayload = Message.packBytesTogether([
			self.encodeNumberToBytes(len(nameAsBytes), 4),
			nameAsBytes,
			self.encodeNumberToBytes(len(messageAsBytes), 4),
			messageAsBytes, myPublicKey])
		return subpayload
Пример #14
0
	def _createSubpayload(self):
		'''Pack the specific fields into the subpayload'''
		# Get own name
		if self.friendName is None:
			self.friendName = DbClient.getProfile(self.friendId).get('name', self.friendId)
		publicKey = self.getPublicKey(torid=self.friendId)
		# TODO: Complain if publicKey is empty
		messageAsBytes = self.message.encode('utf-8')
		nameAsBytes = self.friendName.encode('utf-8')
		subpayload = Message.packBytesTogether([
			self.friendId,
			self.encodeNumberToBytes(len(nameAsBytes), 4),
			nameAsBytes,
			self.encodeNumberToBytes(len(messageAsBytes), 4),
			messageAsBytes, publicKey])
		return subpayload
Пример #15
0
	def _createSubpayload(self):
		'''Use the stored fields to pack the payload contents together'''
		if self.senderKey is None: self.senderKey = self.getOwnPublicKey()
		# Get own torid and name
		if not self.senderId: self.senderId = DbClient.getOwnTorId()
		if not self.senderName:
			self.senderName = DbClient.getProfile(None).get('name', self.senderId)
		if not self.introMessage: self.introMessage = ""
		nameAsBytes = self.senderName.encode('utf-8')
		messageAsBytes = self.introMessage.encode('utf-8')
		print("Packing contact request with senderId", self.senderId)
		return self.packBytesTogether([
			self.senderId,
			self.encodeNumberToBytes(len(nameAsBytes), 4),
			nameAsBytes,
			self.encodeNumberToBytes(len(messageAsBytes), 4),
			messageAsBytes,
			self.senderKey])
Пример #16
0
	def getSharedAndPossibleContacts(torid):
		'''Check which contacts we share with the given torid and which ones we could recommend to each other'''
		nameMap = {}
		ourContactIds = set()
		trustedContactIds = set()
		theirContactIds = set()
		# Get our id so we can exclude it from the sets
		myTorId = DbClient.getOwnTorId()
		if torid == myTorId:
			return (None, None, None, None)
		# Find the contacts of the specified person
		selectedProfile = DbClient.getProfile(torid, False)
		selectedContacts = selectedProfile.get('contactlist', None) if selectedProfile else None
		if selectedContacts:
			for s in selectedContacts.split(","):
				if s and len(s) >= 16:
					foundid = s[0:16]
					if foundid != myTorId:
						foundName = s[16:]
						theirContactIds.add(foundid)
						nameMap[foundid] = foundName
		foundTheirContacts = len(theirContactIds) > 0
		# Now get information about our contacts
		for c in DbClient.getMessageableContacts():
			foundid = c['torid']
			ourContactIds.add(foundid)
			if c['status'] == 'trusted' and foundid != torid:
				trustedContactIds.add(foundid)
			nameMap[foundid] = c.get('displayName', c.get('name', None))
			# Should we check the contact information too?
			if not foundTheirContacts:
				foundContacts = c.get('contactlist', None)
				if foundContacts:
					for s in foundContacts.split(","):
						if s[0:16] == torid:
							theirContactIds.add(foundid)
		# Now we have three sets of torids: our contacts, our trusted contacts, and their contacts.
		sharedContactIds = ourContactIds.intersection(theirContactIds) # might be empty
		suggestionsForThem = trustedContactIds.difference(theirContactIds)
		possibleForMe = theirContactIds.difference(ourContactIds)

		# Some or all of these sets may be empty, but we still return the map so we can look up names
		return (sharedContactIds, suggestionsForThem, possibleForMe, nameMap)
Пример #17
0
	def sendMessage(self, message, whoto):
		# Check status of recipient in profile
		profile = DbClient.getProfile(whoto, False)
		status = profile['status'] if profile else "deleted"
		if status in ['deleted', 'blocked']:
			return self.RC_MESSAGE_IGNORED
		print("Trying to send message to '%s'" % whoto)
		if whoto is not None and len(whoto) == 16:
			try:
				s = socks.socksocket()
				s.setproxy(socks.PROXY_TYPE_SOCKS4, "localhost", 11109)
				s.connect((whoto + ".onion", 11009))
				numsent = s.send(message)
				s.close()
				if numsent != len(message):
					print("Oops - num bytes sent:", numsent, "but message has length:", len(message))
					# For really long messages, maybe need to chunk into 4k blocks or something?
				else:
					Contacts.comeOnline(whoto)
					return self.RC_MESSAGE_SENT
			except Exception as e:
				print("Woah, that threw something:", e)
		print("Bailed from the send attempt, returning failure")
		return self.RC_MESSAGE_FAILED   # it didn't work
Пример #18
0
		print("Please stop this mongod service before running Murmeli.")
		exit()
	print("No password is saved, so we can't use Auth on the db: go to startupwizard")
	canStartMurmeli = False

if canStartMurmeli:
	print("I think I can start Murmeli, checking database status")
	if dbStatus == DbClient.NOT_RUNNING:
		canStartMurmeli = DbClient.startDatabase(useAuth=True)

	# Either the database was already running with auth, or we've just started it with auth
	if canStartMurmeli:
		# if we can't connect, or if we haven't got our own keypair stored, then we need the startupwizard
		print("Database is now running, now checking for profile")
		try:
			ownprofile = DbClient.getProfile()
			if ownprofile is None or ownprofile.get("keyid", None) is None:
				print("I didn't get a profile or didn't get a key, so I can't start Murmeli")
				canStartMurmeli = False
			else:
				print("I think I got a profile and a keyid: '", ownprofile.get("keyid", ""), "' so I'm going to start Murmeli")
		except Exception:
			canStartMurmeli = False # maybe authentication failed?

if not canStartMurmeli:
	# Ask DbClient to stop mongo again
	DbClient.stopDatabase()

# Get ready to launch a Qt GUI
I18nManager.setLanguage()
Config.registerSubscriber(I18nManager.instance())
Пример #19
0
	def _isProfileStatusOk(torId, allowedStatuses):
		profile = DbClient.getProfile(torId, False)
		status = profile.get("status", None) if profile else None
		return status in allowedStatuses
Пример #20
0
	def _makeFingerprintChecker(self, userid):
		'''Use the given userid to make a FingerprintChecker between me and them'''
		person = DbClient.getProfile(userid, False)
		ownFingerprint = CryptoClient.getFingerprint(DbClient.getOwnKeyId())
		usrFingerprint = CryptoClient.getFingerprint(person['keyid'])
		return FingerprintChecker(ownFingerprint, usrFingerprint)
Пример #21
0
	def dealWithAsymmetricMessage(message):
		'''Decide what to do with the given asymmetric message'''
		if message.senderId == MessageShuffler.getOwnTorId():
			print("*** Shouldn't receive a message from myself!")
			return
		# Sort message according to type
		if message.messageType == Message.TYPE_CONTACT_RESPONSE:
			print("Received a contact accept from", message.senderId, "name", message.senderName)
			if MessageShuffler._isProfileStatusOk(message.senderId, ['pending', 'requested', 'untrusted']):
				print(message.senderName, "'s public key is", message.senderKey)
				ContactMaker.handleReceiveAccept(message.senderId, message.senderName, message.senderKey)
				# Call DbClient to store new message in inbox
				rowToStore = {"messageType":"contactresponse", "fromId":message.senderId,
					"fromName":message.senderName, "messageBody":message.introMessage, "accepted":True,
					"messageRead":False, "messageReplied":False, "timestamp":message.timestamp,
					"recipients":MessageShuffler.getOwnTorId()}
				DbClient.addMessageToInbox(rowToStore)
			elif MessageShuffler._isProfileStatusOk(message.senderId, [None, 'blocked']):
				print("Received a contact response but I didn't send them a request!")
				print("Encrypted contents are:", message.encryptedContents)
				rowToStore = {"messageType":"contactresponse", "fromId":message.senderId,
					"fromName":message.senderName, "messageBody":message.introMessage, "accepted":True,
					"timestamp":message.timestamp, "encryptedMsg":message.encryptedContents}
				DbClient.addMessageToPendingContacts(rowToStore)
		elif message.messageType == Message.TYPE_STATUS_NOTIFY:
			if message.online:
				print("One of our contacts has just come online- ", message.senderId,
					"and hash is", message.profileHash)
				prof = DbClient.getProfile(userid=message.senderId, extend=False)
				if prof:
					storedHash = prof.get("profileHash", "empty")
					if message.profileHash != storedHash:
						reply = InfoRequestMessage(infoType=InfoRequestMessage.INFO_PROFILE)
						reply.recipients = [message.senderId]
						DbClient.addMessageToOutbox(reply)
					if message.ping:
						print("Now sending back a pong, too")
						reply = StatusNotifyMessage(online=True, ping=False, profileHash=None)
						reply.recipients = [message.senderId]
						DbClient.addMessageToOutbox(reply)
					else:
						print("It's already a pong so I won't reply")
				Contacts.comeOnline(message.senderId)
			else:
				print("One of our contacts is going offline -", message.senderId)
				Contacts.goneOffline(message.senderId)
		elif message.messageType == Message.TYPE_INFO_REQUEST:
			print("I've received an info request message for type", message.infoType)
			if MessageShuffler._isProfileStatusOk(message.senderId, ['trusted']):
				reply = InfoResponseMessage(message.messageType)
				reply.recipients = [message.senderId]
				DbClient.addMessageToOutbox(reply)
		elif message.messageType == Message.TYPE_INFO_RESPONSE:
			if message.profile and MessageShuffler._isProfileStatusOk(message.senderId, ['trusted', 'untrusted']):
				if message.profileHash:
					message.profile['profileHash'] = message.profileHash
				DbClient.updateContact(message.senderId, message.profile)
		elif message.messageType == Message.TYPE_ASYM_MESSAGE:
			print("It's a general kind of message, this should go in the Inbox, right?")
			if MessageShuffler._isProfileStatusOk(message.senderId, ['trusted', 'untrusted']):
				Contacts.comeOnline(message.senderId)
		else:
			# It's another asymmetric message type
			print("Hä?  What kind of asymmetric message type is that? ", message.messageType)