def sendMessage(self, message, whoto): # Check status of recipient in profile profile = DbI.getProfile(whoto) 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.instance().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
def processPendingContacts(torId): print("Process pending contact accept responses from:", torId) foundReq = False for resp in DbI.getPendingContactMessages(torId): name = resp.get("fromName", None) if not name: profile = DbI.getProfile(torId) name = profile["displayName"] print("Found pending contact accept request from: ", name) # Check signature using keyring _, signatureKey = CryptoClient.decryptAndCheckSignature( resp.get("encryptedMsg", None)) if signatureKey: foundReq = True # Insert new message into inbox with message contents rowToStore = { "messageType": "contactresponse", "fromId": resp.get("fromId", None), "fromName": name, "accepted": True, "messageBody": resp.get("messageBody", ""), "timestamp": resp.get("timestamp", None), "messageRead": True, "messageReplied": True, "recipients": DbI.getOwnTorid() } DbI.addToInbox(rowToStore) if foundReq: DbI.updateProfile(torId, {"status": "untrusted"}) # Delete all pending contact responses from this torId DbI.deletePendingContactMessages(torId)
def _createSubpayload(self): '''Use the stored fields to pack the payload contents together''' if self.profileHash is None or self.profileHash == "": self.profileHash = dbutils.calculateHash(DbI.getProfile()) return self.packBytesTogether([ self.encodeNumberToBytes(1 if self.online else 0, 1), self.encodeNumberToBytes(1 if self.ping else 0, 1), self.profileHash ])
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 = DbI.getProfile()['description'] bacProfile = bac.profile self.assertEqual(mydescription, bacProfile['description'])
def sendReferralMessages(friendId1, friendId2, intro): '''Send messages to both friendId1 and friendId2, to recommend they become friends with each other''' friend1Profile = DbI.getProfile(friendId1) friend2Profile = DbI.getProfile(friendId2) if friend1Profile and friend1Profile.get("status", "nostatus") == "trusted" \ and friend2Profile and friend2Profile.get("status", "nostatus") == "trusted": print("Send message to", friendId1, "referring the details of", friendId2) notify = ContactReferralMessage(friendId=friendId2, friendName=None, introMessage=intro) notify.recipients = [friendId1] DbI.addToOutbox(notify) print("Send message to", friendId2, "referring the details of", friendId1) notify = ContactReferralMessage(friendId=friendId1, friendName=None, introMessage=intro) notify.recipients = [friendId2] DbI.addToOutbox(notify)
def sendReferRequestMessage(sendToId, requestedId, intro): '''Send a message to sendToId, to ask that they recommend you to requestedId''' sendToProfile = DbI.getProfile(sendToId) if sendToProfile and sendToProfile.get("status", "nostatus") == "trusted" \ and requestedId != DbI.getOwnTorid(): print("Send message to", sendToId, "requesting referral of", requestedId) notify = ContactReferRequestMessage(friendId=requestedId, introMessage=intro) notify.recipients = [sendToId] DbI.addToOutbox(notify)
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 = DbI.getProfile(torId) if profile and profile["status"] in ["untrusted", "trusted"]: # Update the user's status to trusted DbI.updateProfile(torId, {"status": "trusted"}) # Trigger a StatusNotify to tell them we're online notify = StatusNotifyMessage(online=True, ping=True, profileHash=None) notify.recipients = [torId] DbI.addToOutbox(notify)
def _createSubpayload(self): '''Pack the specific fields into the subpayload''' # Get own name if self.senderName is None: self.senderName = DbI.getProfile().get('name', self.senderId) 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
def _createSubpayload(self): '''Use the stored fields to pack the payload contents together''' ownProfile = DbI.getProfile() if self.infoType is None: self.infoType = InfoRequestMessage.INFO_PROFILE if self.profileString is None: self.profileString = dbutils.getProfileAsString(ownProfile) self.profileHash = dbutils.calculateHash(ownProfile) return self.packBytesTogether([ self.encodeNumberToBytes(self.infoType), self.encodeNumberToBytes(len(self.profileString), 4), self.profileString, self.encodeNumberToBytes(len(self.profileHash), 4), self.profileHash ])
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 = DbI.getOwnTorid() if torid == myTorId: return (None, None, None, None) # Find the contacts of the specified person selectedProfile = DbI.getProfile(torid) 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 DbI.getMessageableProfiles(): foundid = c['torid'] ourContactIds.add(foundid) if c['status'] == 'trusted' and foundid != torid: trustedContactIds.add(foundid) nameMap[foundid] = c['displayName'] # 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) # TODO: Maybe subtract requested contacts from the "possibleForMe" set? # 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)
def _createSubpayload(self): '''Pack the specific fields into the subpayload''' # Get own name if not self.friendName: self.friendName = DbI.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
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 = DbI.getOwnTorid() if not self.senderName: self.senderName = DbI.getProfile().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 ])
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']): # 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 } DbI.addToInbox(rowToStore) elif message.messageType == Message.TYPE_CONTACT_RESPONSE: print( "It's an unencrypted contact response, so it must be a refusal" ) sender = DbI.getProfile(message.senderId) if MessageShuffler._isProfileStatusOk(message.senderId, ['requested']): senderName = sender.get("displayName") if sender else "" ContactMaker.handleReceiveDeny(message.senderId) # Store new message in inbox rowToStore = { "messageType": "contactresponse", "fromId": message.senderId, "fromName": senderName, "messageBody": "", "accepted": False, "messageRead": False, "messageReplied": False, "timestamp": message.timestamp, "recipients": DbI.getOwnTorid() } DbI.addToInbox(rowToStore) else: print("Hä? It's unencrypted but the message type is", message.messageType)
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.instance().comeOnline(DbI.getOwnTorid()) if message.senderMustBeTrusted: sender = DbI.getProfile(message.senderId) 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) elif message.encryptionType == Message.ENCTYPE_RELAY: # Get received bytes of message, and add to Outbox, send to everybody EXCEPT the sender bytesToSend = message.createOutput(None) if bytesToSend: # add to outbox, but don't send it back to message.senderId DbI.addRelayMessageToOutbox(bytesToSend, message.senderId) 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)
def generateFingerprintsPage(self, userid): '''Build the page for checking the fingerprints of the selected user''' # First, get the name of the user person = DbI.getProfile(userid) dispName = person['displayName'] fullName = person['name'] 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>" })
def handleAccept(torId): '''We want to accept a contact request, so we need to find the request(s), and use it/them to update our keyring and our database entry''' # Get this person's current status from the db, if available profile = DbI.getProfile(torId) status = profile.get("status", None) if profile else None # Look for the contact request(s) in the inbox, and extract the name and publicKey senderName, senderKeystr, directRequest = ContactMaker.getContactRequestDetails( torId) keyValid = senderKeystr and len(senderKeystr) > 20 if keyValid: if status in [None, "requested"]: # add key to keyring keyId = CryptoClient.importPublicKey(senderKeystr) # work out what name and status to stores storedSenderName = profile["name"] if profile else None nameToStore = storedSenderName if storedSenderName else senderName statusToStore = "untrusted" if directRequest else "pending" # add or update the profile DbI.updateProfile( torId, { "status": statusToStore, "keyid": keyId, "name": nameToStore, "displayName": nameToStore }) ContactMaker.processPendingContacts(torId) elif status == "pending": print("Request already pending, nothing to do") elif status in ["untrusted", "trusted"]: # set status to untrusted? Send response? print("Trying to handle an accept but status is already", status) # Move all corresponding requests to be regular messages instead DbI.changeRequestMessagesToRegular(torId) else: print("Trying to handle an accept but key isn't valid")
def _isProfileStatusOk(torId, allowedStatuses): profile = DbI.getProfile(torId) status = profile.get("status", None) if profile else None return status in allowedStatuses
def servePage(self, view, url, params): print("Special function:", url) if url == "/selectprofilepic": # Get home directory for file dialog homedir = os.path.expanduser("~/") fname = QFileDialog.getOpenFileName( view, I18nManager.getText("gui.dialogtitle.openimage"), homedir, I18nManager.getText("gui.fileselection.filetypes.jpg")) if fname: view.page().mainFrame().evaluateJavaScript( "updateProfilePic('" + fname + "');") elif url == "/friendstorm": if not DbI.hasFriends(): view.page().mainFrame().evaluateJavaScript( "window.alert('No friends :(');") return # Launch a storm self.bs = Brainstorm(I18nManager.getText("contacts.storm.title")) self.bs.show() storm = Storm() # Build up Nodes and Edges using our contact list and if possible our friends' contact lists myTorId = DbI.getOwnTorid() friends = {} friendsOfFriends = {} for c in DbI.getMessageableProfiles(): # print("Contact: id:'%s' name:'%s'" % (c['torid'], c['displayName'])) nodeid = storm.getUnusedNodeId() torid = c['torid'] friends[torid] = nodeid storm.addNode(Node(None, nodeid, c['displayName'])) friendsOfFriends[torid] = c.get('contactlist', "") # Also add ourselves c = DbI.getProfile() nodeid = storm.getUnusedNodeId() friends[c['torid']] = nodeid storm.addNode(Node(None, nodeid, c['displayName'])) # Add edges for torid in friends: if torid != myTorId: storm.addEdge(friends[torid], friends[myTorId]) for torid in friendsOfFriends: if torid != myTorId: ffList = friendsOfFriends[torid] if ffList: for ff in ffList.split(","): if ff and len(ff) > 16: ffTorid = ff[:16] ffName = ff[16:] if ffTorid != myTorId: if not friends.get(ffTorid, None): # Friend's friend is not in the list yet - add it nodeid = storm.getUnusedNodeId() friends[ffTorid] = nodeid storm.addNode( Node(None, nodeid, ffName)) # Add edge from torid to ffTorid storm.addEdge(friends[torid], friends[ffTorid]) self.bs.setStorm(storm)
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', 'status-pending.png' ]) # List of contacts, and show details for the selected one (or self if userid=None) selectedprofile = DbI.getProfile(userid) if not selectedprofile: selectedprofile = DbI.getProfile() userid = selectedprofile['torid'] ownPage = userid == DbI.getOwnTorid() # Build list of contacts userboxes = [] currTime = datetime.datetime.now() for p in DbI.getProfiles(): box = Bean() box.dispName = p['displayName'] box.torid = p['torid'] box.tilestyle = "contacttile" + ("selected" if p['torid'] == userid else "") box.status = p['status'] isonline = Contacts.instance().isOnline(box.torid) lastSeen = Contacts.instance().lastSeen(box.torid) lastSeenTime = str(lastSeen.timetz())[:5] if lastSeen and ( currTime - lastSeen).total_seconds() < 18000 else None if lastSeenTime: box.lastSeen = I18nManager.getText( "contacts.onlinesince" if isonline else "contacts.offlinesince") % lastSeenTime elif isonline: box.lastSeen = I18nManager.getText("contacts.online") else: box.lastSeen = None 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
def getPublicKey(self, torid): '''Use the keyid stored in the db, and get the corresponding public key from the Crypto module''' profile = DbI.getProfile(torid) keyid = profile.get('keyid', None) if profile else None if keyid: return CryptoClient.getPublicKey(keyid)
def _makeFingerprintChecker(self, userid): '''Use the given userid to make a FingerprintChecker between me and them''' person = DbI.getProfile(userid) ownFingerprint = CryptoClient.getFingerprint(DbI.getOwnKeyid()) usrFingerprint = CryptoClient.getFingerprint(person['keyid']) return FingerprintChecker(ownFingerprint, usrFingerprint)
def dealWithAsymmetricMessage(message): '''Decide what to do with the given asymmetric message''' if message.senderId == DbI.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) # 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": DbI.getOwnTorid() } DbI.addToInbox(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 } DbI.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 = DbI.getProfile(message.senderId) if prof: storedHash = prof.get("profileHash", "empty") if message.profileHash != storedHash: reply = InfoRequestMessage( infoType=InfoRequestMessage.INFO_PROFILE) reply.recipients = [message.senderId] DbI.addToOutbox(reply) if message.ping: print("Now sending back a pong, too") reply = StatusNotifyMessage(online=True, ping=False, profileHash=None) reply.recipients = [message.senderId] DbI.addToOutbox(reply) else: print("It's already a pong so I won't reply") Contacts.instance().comeOnline(message.senderId) else: print("One of our contacts is going offline -", message.senderId) Contacts.instance().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] DbI.addToOutbox(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 DbI.updateProfile(message.senderId, message.profile, Config.getWebCacheDir()) elif message.messageType == Message.TYPE_FRIEND_REFERRAL: print("I've received a friend referral message from:", message.senderId, "for:", message.friendName) if MessageShuffler._isProfileStatusOk(message.senderId, ['trusted']): # Store new referral message in inbox rowToStore = { "messageType": "contactrefer", "fromId": message.senderId, "friendId": message.friendId, "friendName": message.friendName, "messageBody": message.message, "publicKey": message.publicKey, "timestamp": message.timestamp, "messageRead": False, "messageReplied": False } DbI.addToInbox(rowToStore) elif message.messageType == Message.TYPE_FRIENDREFER_REQUEST: print("I've received a friend referral request from:", message.senderId, "who wants me to refer:", message.friendId) if MessageShuffler._isProfileStatusOk(message.senderId, ['trusted']): # Store message in the inbox rowToStore = { "messageType": "referrequest", "fromId": message.senderId, "friendId": message.friendId, "friendName": message.friendName, "messageBody": message.message, "publicKey": message.publicKey, "timestamp": message.timestamp, "messageRead": False, "messageReplied": False } DbI.addToInbox(rowToStore) 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']): rowToStore = { "messageType": "normal", "fromId": message.senderId, "messageBody": message.messageBody, "timestamp": message.timestamp, "messageRead": False, "messageReplied": False, "recipients": message.sendTo, "parentHash": message.replyToHash } DbI.addToInbox(rowToStore) Contacts.instance().comeOnline(message.senderId) else: # It's another asymmetric message type print("Hä? What kind of asymmetric message type is that? ", message.messageType)
except ImportError: canStartMurmeli = False # if it all looks ok so far, we need to check the database db = None dbFilePath = Config.getSsDatabaseFile() if not os.path.exists(dbFilePath): canStartMurmeli = False if canStartMurmeli: print("I think I can start Murmeli, checking database status") db = MurmeliDb(dbFilePath) DbI.setDb(db) try: ownprofile = DbI.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 as e: print( "Exception thrown trying to get profile, so I can't start Murmeli:", e) canStartMurmeli = False # maybe authentication failed?
def servePage(self, view, url, params): self.requirePageResources( ['button-compose.png', 'default.css', 'jquery-3.1.1.js']) DbI.exportAllAvatars(Config.getWebCacheDir()) messageList = None if url == "/send": print("send message of type '%(messageType)s' to id '%(sendTo)s'" % params) if params['messageType'] == "contactresponse": torId = params['sendTo'] if params.get("accept", "0") == "1": ContactMaker.handleAccept(torId) # Make sure this new contact has an empty avatar DbI.exportAllAvatars(Config.getWebCacheDir()) outmsg = message.ContactResponseMessage( message=params['messageBody']) else: ContactMaker.handleDeny(torId) outmsg = message.ContactDenyMessage() # Construct a ContactResponse message object for sending outmsg.recipients = [params['sendTo']] DbI.addToOutbox(outmsg) elif url.startswith("/delete/"): DbI.deleteFromInbox(params.get("msgId", "")) elif url in ["/search", "/search/"]: messageList = DbI.searchInboxMessages(params.get("searchTerm")) # Make dictionary to convert ids to names contactNames = { c['torid']: c['displayName'] for c in DbI.getProfiles() } unknownSender = I18nManager.getText("messages.sender.unknown") unknownRecpt = I18nManager.getText("messages.recpt.unknown") # Get contact requests, responses and mails from inbox conreqs = [] conresps = [] mailTree = MessageTree() if messageList is None: messageList = DbI.getInboxMessages() # TODO: Paging options? for m in messageList: if not m: continue 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'] = DbI.getProfile(m['fromId'])["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 = DbI.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: replyAll = recpts.split(",") m['recipients'] = ", ".join( [contactNames.get(i, unknownRecpt) for i in replyAll]) replyAll.append(senderId) m['replyAll'] = ",".join(replyAll) else: m['recipients'] = unknownRecpt m['replyAll'] = "" mailTree.addMsg(m) mails = mailTree.build() 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)