def __init__(self, content, content_type, charset=None, sender=None, recipients=None, courtesy_recipients=None, subject=None, timestamp=None, required=None, additional_headers=None): if not isinstance(content, bytes): raise TypeError("content should be an instance of bytes") self.content = content self.content_type = content_type self.charset = charset self.sender = sender self.recipients = recipients or [] self.courtesy_recipients = courtesy_recipients or [] self.subject = subject if isinstance( subject, (MultilingualText, type(None))) else MultilingualText(subject) self.timestamp = ISOTimestamp( timestamp) if timestamp is not None else None self.required = required or [] self.additional_headers = additional_headers or []
def __init__(self, content, content_type, sender=None, recipients=None, courtesy_recipients=None, subject=None, timestamp=None, required=None, additional_headers=None): self.content = content self.content_type = content_type self.sender = sender self.recipients = recipients or [] self.courtesy_recipients = courtesy_recipients or [] self.subject = subject self.timestamp = ISOTimestamp(timestamp) if timestamp is not None else None self.required = required or [] self.additional_headers = additional_headers or []
def renderMessage(self, message): if message.direction == 'outgoing': icon = NSApp.delegate().contactsWindowController.iconPathForSelf() else: sender_uri = sipuri_components_from_string(message.cpim_from)[0] # TODO: How to render the icons from Address Book? Especially in sandbox mode we do not have access to other folders icon = NSApp.delegate().contactsWindowController.iconPathForURI(sender_uri) try: timestamp=ISOTimestamp(message.cpim_timestamp) except Exception: pass else: is_html = False if message.content_type == 'text' else True private = True if message.private == "1" else False self.chatViewController.showMessage(message.sip_callid, message.msgid, message.direction, message.cpim_from, icon, message.body, timestamp, is_private=private, recipient=message.cpim_to, state=message.status, is_html=is_html, history_entry=True, media_type=message.media_type, encryption=message.encryption if message.media_type == 'chat' else None)
def _NH_SIPEngineGotMessage(self, sender, data): account = AccountManager().find_account(data.request_uri) if not account: BlinkLogger().log_warning( "Could not find local account for incoming SMS to %s, using default" % data.request_uri) account = AccountManager().default_account call_id = data.headers.get('Call-ID', Null).body try: self.received_call_ids.remove(call_id) except KeyError: self.received_call_ids.add(call_id) else: # drop duplicate message received return is_cpim = False cpim_message = None is_replication_message = False if data.content_type == 'message/cpim': try: cpim_message = CPIMPayload.decode(data.body) except CPIMParserError: BlinkLogger().log_warning( "Incoming SMS from %s to %s has invalid CPIM content" % format_identity_to_string(data.from_header), account.id) return else: is_cpim = True content = cpim_message.content content_type = cpim_message.content_type sender_identity = cpim_message.sender or data.from_header if cpim_message.sender and data.from_header.uri == data.to_header.uri and data.from_header.uri == cpim_message.sender.uri: is_replication_message = True window_tab_identity = cpim_message.recipients[ 0] if cpim_message.recipients else data.to_header else: window_tab_identity = data.from_header else: content = data.body.decode('utf-8') content_type = data.content_type sender_identity = data.from_header window_tab_identity = sender_identity is_html = content_type == 'text/html' if content_type in ('text/plain', 'text/html'): pass #BlinkLogger().log_info(u"Incoming SMS %s from %s to %s received" % (call_id, format_identity_to_string(sender_identity), account.id)) elif content_type == 'application/im-iscomposing+xml': # body must not be utf-8 decoded content = cpim_message.content if is_cpim else data.body msg = IsComposingMessage.parse(content) state = msg.state.value refresh = msg.refresh.value if msg.refresh is not None else None content_type = msg.content_type.value if msg.content_type is not None else None last_active = msg.last_active.value if msg.last_active is not None else None viewer = self.openMessageWindow(SIPURI.new( window_tab_identity.uri), window_tab_identity.display_name, account, create_if_needed=False, note_new_message=False) if viewer: viewer.gotIsComposing(self.windowForViewer(viewer), state, refresh, last_active) return else: BlinkLogger().log_warning( "Incoming SMS %s from %s to %s has unknown content-type %s" % (call_id, format_identity_to_string( data.from_header), account.id, data.content_type)) return # display the message note_new_message = False if is_replication_message else True viewer = self.openMessageWindow(SIPURI.new(window_tab_identity.uri), window_tab_identity.display_name, account, note_new_message=note_new_message) self.windowForViewer(viewer).noteNewMessageForSession_(viewer) replication_state = None replication_timestamp = None if is_replication_message: replicated_response_code = data.headers.get( 'X-Replication-Code', Null).body if replicated_response_code == '202': replication_state = 'deferred' elif replicated_response_code == '200': replication_state = 'delivered' else: replication_state = 'failed' replicated_timestamp = data.headers.get('X-Replication-Timestamp', Null).body try: replication_timestamp = ISOTimestamp(replicated_timestamp) except Exception: replication_timestamp = ISOTimestamp.now() window = self.windowForViewer(viewer).window() viewer.gotMessage(sender_identity, call_id, content, is_html, is_replication_message, replication_timestamp, window=window) self.windowForViewer(viewer).noteView_isComposing_(viewer, False)
def decode(cls, message): headers, separator, body = message.partition('\r\n\r\n') if not separator: raise CPIMParserError('Invalid CPIM message') sender = None recipients = [] courtesy_recipients = [] subject = None timestamp = None required = [] additional_headers = [] namespaces = {'': CPIMNamespace(cls.standard_namespace)} subjects = {} for prefix, name, value in cls.headers_re.findall(headers): namespace = namespaces.get(prefix) if namespace is None or '.' in name: continue try: #value = value.decode('cpim-header') if namespace == cls.standard_namespace: if name == 'From': sender = ChatIdentity.parse(value) elif name == 'To': recipients.append(ChatIdentity.parse(value)) elif name == 'cc': courtesy_recipients.append(ChatIdentity.parse(value)) elif name == 'Subject': match = cls.subject_re.match(value) if match is None: raise ValueError('Illegal Subject header: %r' % value) lang, subject = match.groups() # language tags must be ASCII subjects[str(lang ) if lang is not None else None] = subject elif name == 'DateTime': timestamp = ISOTimestamp(value) elif name == 'Required': required.extend(re.split(r'\s*,\s*', value)) elif name == 'NS': match = cls.namespace_re.match(value) if match is None: raise ValueError('Illegal NS header: %r' % value) prefix, uri = match.groups() namespaces[prefix] = CPIMNamespace(uri, prefix) else: additional_headers.append( CPIMHeader(name, namespace, value)) else: additional_headers.append( CPIMHeader(name, namespace, value)) except ValueError: pass if None in subjects: subject = MultilingualText(subjects.pop(None), **subjects) elif subjects: subject = MultilingualText(**subjects) mime_message = EmailParser().parsestr(body) content_type = mime_message.get_content_type() if content_type is None: raise CPIMParserError( "CPIM message missing Content-Type MIME header") content = mime_message.get_payload() charset = mime_message.get_content_charset() return cls(content, content_type, charset, sender, recipients, courtesy_recipients, subject, timestamp, required, additional_headers)
def _NH_SIPEngineGotMessage(self, sender, data): account = AccountManager().find_account(data.request_uri) if not account: BlinkLogger().log_warning( "Could not find local account for incoming SMS to %s, using default" % data.request_uri) account = AccountManager().default_account call_id = data.headers.get('Call-ID', Null).body try: self.received_call_ids.remove(call_id) except KeyError: self.received_call_ids.add(call_id) else: # drop duplicate message received return is_cpim = False cpim_message = None is_replication_message = False imdn_id = None imdn_timestamp = None if data.content_type == 'message/cpim': try: cpim_message = CPIMPayload.decode(data.body) except CPIMParserError: BlinkLogger().log_warning( "Incoming SMS from %s to %s has invalid CPIM content" % format_identity_to_string(data.from_header), account.id) return else: is_cpim = True content = cpim_message.content content_type = cpim_message.content_type sender_identity = cpim_message.sender or data.from_header cpim_imdn_events = None imdn_timestamp = cpim_message.timestamp for h in cpim_message.additional_headers: if h.name == "Message-ID": imdn_id = h.value if h.name == "Disposition-Notification": cpim_imdn_events = h.value if imdn_id: if cpim_imdn_events and 'positive-delivery' in cpim_imdn_events and imdn_timestamp: BlinkLogger().log_info( "Will send IMDN delivery notification for %s" % imdn_id) else: imdn_id = None if cpim_message.sender and data.from_header.uri == data.to_header.uri and data.from_header.uri == cpim_message.sender.uri: is_replication_message = True window_tab_identity = cpim_message.recipients[ 0] if cpim_message.recipients else data.to_header else: window_tab_identity = data.from_header else: content = data.body content_type = data.content_type sender_identity = data.from_header window_tab_identity = sender_identity if content_type in ('text/plain', 'text/html'): BlinkLogger().log_info( u"Incoming SMS %s from %s to %s received" % (call_id, format_identity_to_string(sender_identity), account.id)) elif content_type in ('text/rsa-public-key'): uri = format_identity_to_string(sender_identity) BlinkLogger().log_info( u"Public key from %s received" % (format_identity_to_string(sender_identity))) if uri == account.id: BlinkLogger().log_info( u"Public key save skipped for own account") return public_key = '' start_public = False for l in content.decode().split("\n"): if l == "-----BEGIN RSA PUBLIC KEY-----": start_public = True if l == "-----END RSA PUBLIC KEY-----": public_key = public_key + l start_public = False break if start_public: public_key = public_key + l + '\n' if public_key: blink_contact = NSApp.delegate( ).contactsWindowController.getFirstContactFromAllContactsGroupMatchingURI( uri) if blink_contact is not None: contact = blink_contact.contact if contact.public_key != public_key: contact.public_key = public_key contact.public_key_checksum = hashlib.sha1( public_key.encode()).hexdigest() contact.save() BlinkLogger().log_info( u"Public key %s from %s saved " % (contact.public_key_checksum, data.from_header.uri)) nc_title = NSLocalizedString( "Public key", "System notification title") nc_subtitle = format_identity_to_string( sender_identity, check_contact=True, format='full') nc_body = NSLocalizedString( "Public key has changed", "System notification title") NSApp.delegate().gui_notify(nc_title, nc_body, nc_subtitle) else: BlinkLogger().log_info( u"Public key from %s has not changed" % data.from_header.uri) else: BlinkLogger().log_info(u"No contact found to save the key") return elif content_type in ('text/rsa-private-key'): BlinkLogger().log_info(u"Private key from %s to %s received" % (data.from_header.uri, account.id)) if account.id == str(data.from_header.uri).split(":")[1]: self.import_key_window = ImportPrivateKeyController( account, content) self.import_key_window.show() return elif content_type in ('message/imdn+xml'): document = IMDNDocument.parse(content) imdn_message_id = document.message_id.value imdn_status = document.notification.status.__str__() BlinkLogger().log_info(u"Received IMDN message %s" % imdn_status) viewer = self.openMessageWindow( SIPURI.new(window_tab_identity.uri), window_tab_identity.display_name, account) if viewer and imdn_status == 'displayed': viewer.chatViewController.markMessage(imdn_message_id, MSG_STATE_DISPLAYED) return elif content_type == 'application/im-iscomposing+xml': content = cpim_message.content if is_cpim else data.body msg = IsComposingMessage.parse(content) state = msg.state.value refresh = msg.refresh.value if msg.refresh is not None else None content_type = msg.content_type.value if msg.content_type is not None else None last_active = msg.last_active.value if msg.last_active is not None else None viewer = self.openMessageWindow(SIPURI.new( window_tab_identity.uri), window_tab_identity.display_name, account, create_if_needed=False, note_new_message=False) if viewer: viewer.gotIsComposing(self.windowForViewer(viewer), state, refresh, last_active) return else: BlinkLogger().log_warning( "Incoming SMS %s from %s to %s has unknown content-type %s" % (call_id, format_identity_to_string( data.from_header), account.id, content_type)) return # display the message note_new_message = False if is_replication_message else True viewer = self.openMessageWindow(SIPURI.new(window_tab_identity.uri), window_tab_identity.display_name, account, note_new_message=note_new_message) self.windowForViewer(viewer).noteNewMessageForSession_(viewer) replication_state = None replication_timestamp = None if is_replication_message: replicated_response_code = data.headers.get( 'X-Replication-Code', Null).body if replicated_response_code == '202': replication_state = 'deferred' elif replicated_response_code == '200': replication_state = 'delivered' else: replication_state = 'failed' replicated_timestamp = data.headers.get('X-Replication-Timestamp', Null).body try: replication_timestamp = ISOTimestamp(replicated_timestamp) except Exception: replication_timestamp = ISOTimestamp.now() window = self.windowForViewer(viewer).window() viewer.gotMessage(sender_identity, call_id, content, content_type, is_replication_message, replication_timestamp, window=window, imdn_id=imdn_id, imdn_timestamp=imdn_timestamp) self.windowForViewer(viewer).noteView_isComposing_(viewer, False)
def render_history_messages(self, messages): if self.chatViewController.scrolling_zoom_factor: if not self.message_count_from_history: self.message_count_from_history = len(messages) self.chatViewController.lastMessagesLabel.setStringValue_( self.zoom_period_label) else: if self.message_count_from_history == len(messages): self.chatViewController.setHandleScrolling_(False) self.chatViewController.lastMessagesLabel.setStringValue_( NSLocalizedString( "%s. There are no previous messages.", "Label") % self.zoom_period_label) self.chatViewController.setHandleScrolling_(False) else: self.chatViewController.lastMessagesLabel.setStringValue_( self.zoom_period_label) else: self.message_count_from_history = len(messages) if len(messages): self.chatViewController.lastMessagesLabel.setStringValue_( NSLocalizedString("Scroll up for going back in time", "Label")) else: self.chatViewController.setHandleScrolling_(False) self.chatViewController.lastMessagesLabel.setStringValue_( NSLocalizedString("There are no previous messages", "Label")) if len(messages): message = messages[0] delta = datetime.date.today() - message.date if not self.chatViewController.scrolling_zoom_factor: if delta.days <= 2: self.chatViewController.scrolling_zoom_factor = 1 elif delta.days <= 7: self.chatViewController.scrolling_zoom_factor = 2 elif delta.days <= 31: self.chatViewController.scrolling_zoom_factor = 3 elif delta.days <= 90: self.chatViewController.scrolling_zoom_factor = 4 elif delta.days <= 180: self.chatViewController.scrolling_zoom_factor = 5 elif delta.days <= 365: self.chatViewController.scrolling_zoom_factor = 6 elif delta.days <= 3650: self.chatViewController.scrolling_zoom_factor = 7 call_id = None seen_sms = {} last_media_type = 'sms' last_chat_timestamp = None for message in messages: if message.status == 'failed': continue if message.sip_callid != '' and message.media_type == 'sms': try: seen = seen_sms[message.sip_callid] except KeyError: seen_sms[message.sip_callid] = True else: continue if message.direction == 'outgoing': icon = NSApp.delegate( ).contactsWindowController.iconPathForSelf() else: sender_uri = sipuri_components_from_string( message.cpim_from)[0] icon = NSApp.delegate( ).contactsWindowController.iconPathForURI(sender_uri) timestamp = ISOTimestamp(message.cpim_timestamp) is_html = False if message.content_type == 'text' else True #if call_id is not None and call_id != message.sip_callid and message.media_type == 'chat': # self.chatViewController.showSystemMessage(message.sip_callid, 'Chat session established', timestamp, False) #if message.media_type == 'sms' and last_media_type == 'chat': # self.chatViewController.showSystemMessage(message.sip_callid, 'Short messages', timestamp, False) self.chatViewController.showMessage(message.sip_callid, message.id, message.direction, message.cpim_from, icon, message.body, timestamp, recipient=message.cpim_to, state=message.status, is_html=is_html, history_entry=True, media_type=message.media_type, encryption=message.encryption) call_id = message.sip_callid last_media_type = 'chat' if message.media_type == 'chat' else 'sms' if message.media_type == 'chat': last_chat_timestamp = timestamp self.chatViewController.loadingProgressIndicator.stopAnimation_(None) self.chatViewController.loadingTextIndicator.setStringValue_("")