def _message_queue_handler(self): notification_center = NotificationCenter() try: while True: message = self.message_queue.wait() if self.msrp_session is None: if message.notify_progress: data = NotificationData(message_id=message.id, message=None, code=0, reason='Stream ended') notification_center.post_notification('ChatStreamDidNotDeliverMessage', sender=self, data=data) break try: if isinstance(message.content, unicode): message.content = message.content.encode('utf8') charset = 'utf8' else: charset = None if not isinstance(message, QueuedOTRInternalMessage): try: message.content = self.encryption.otr_session.handle_output(message.content, message.content_type) except OTRError, e: raise ChatStreamError(str(e)) message.sender = message.sender or self.local_identity message.recipients = message.recipients or [self.remote_identity] # check if we MUST use CPIM need_cpim = (message.sender != self.local_identity or message.recipients != [self.remote_identity] or message.courtesy_recipients or message.subject or message.timestamp or message.required or message.additional_headers) if need_cpim or not contains_mime_type(self.remote_accept_types, message.content_type): if not contains_mime_type(self.remote_accept_wrapped_types, message.content_type): raise ChatStreamError('Unsupported content_type for outgoing message: %r' % message.content_type) if not self.cpim_enabled: raise ChatStreamError('Additional message meta-data cannot be sent, because the CPIM wrapper is not used') if not self.private_messages_allowed and message.recipients != [self.remote_identity]: raise ChatStreamError('The remote end does not support private messages') if message.timestamp is None: message.timestamp = ISOTimestamp.now() payload = CPIMPayload(charset=charset, **{name: getattr(message, name) for name in Message.__slots__}) elif self.prefer_cpim and self.cpim_enabled and contains_mime_type(self.remote_accept_wrapped_types, message.content_type): if message.timestamp is None: message.timestamp = ISOTimestamp.now() payload = CPIMPayload(charset=charset, **{name: getattr(message, name) for name in Message.__slots__}) else: payload = SimplePayload(message.content, message.content_type, charset) except ChatStreamError, e: if message.notify_progress: data = NotificationData(message_id=message.id, message=None, code=0, reason=e.args[0]) notification_center.post_notification('ChatStreamDidNotDeliverMessage', sender=self, data=data) continue
def _NH_XMPPGotComposingIndication(self, notification): composing_indication = notification.data.composing_indication sender = composing_indication.sender recipient = composing_indication.recipient if not XMPPGatewayConfig.use_msrp_for_chat: state = 'active' if composing_indication.state == 'composing' else 'idle' try: interval = composing_indication.interval except AttributeError: interval = 60 body = IsComposingMessage( state=State(state), refresh=Refresh(interval), last_active=LastActive(ISOTimestamp.now()), content_type=ContentType('text')).toxml() message = NormalMessage(sender, recipient, body) sip_message_sender = SIPMessageSender( message, content_type=IsComposingDocument.content_type, use_cpim=XMPPGatewayConfig.use_cpim) if XMPPGatewayConfig.log_iscomposing: log.info('xmpp:%s to sip:%s chat is %s' % (sender, recipient, composing_indication.state)) try: sip_message_sender.send().wait() except SIPMessageError as e: if XMPPGatewayConfig.log_iscomposing: log.error('SIP Message from %s to %s failed: %s (%s)' % (sender, recipient, e.reason, e.code))
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 add_to_history(self): FileTransferHistory().add_transfer( transfer_id=self.ft_info.transfer_id, direction=self.ft_info.direction, local_uri=self.ft_info.local_uri, remote_uri=self.ft_info.remote_uri, file_path=self.ft_info.file_path, bytes_transfered=self.ft_info.bytes_transfered, file_size=self.ft_info.file_size or 0, status=self.ft_info.status) message = "<h3>%s File Transfer</h3>" % self.ft_info.direction.capitalize( ) message += "<p>%s (%s)" % (self.ft_info.file_path, format_size(self.ft_info.file_size or 0)) media_type = 'file-transfer' local_uri = self.ft_info.local_uri remote_uri = self.ft_info.remote_uri direction = self.ft_info.direction status = 'delivered' if self.ft_info.status == 'completed' else 'failed' cpim_from = self.ft_info.remote_uri cpim_to = self.ft_info.remote_uri timestamp = str(ISOTimestamp.now()) ChatHistory().add_message(self.ft_info.transfer_id, media_type, local_uri, remote_uri, direction, cpim_from, cpim_to, timestamp, message, "html", "0", status)
def offline_state(self): if self.account is BonjourAccount(): return None blink_settings = BlinkSettings() account_id = hashlib.md5(self.account.id).hexdigest() timestamp = ISOTimestamp.now() doc = pidf.PIDF(str(self.account.uri)) person = pidf.Person('PID-%s' % account_id) person.timestamp = timestamp person.activities = rpid.Activities() person.activities.add('offline') doc.add(person) service = pidf.Service('SID-%s' % account_id) service.status = 'closed' service.status.extended = 'offline' service.contact = str(self.account.uri) service.timestamp = timestamp service.capabilities = caps.ServiceCapabilities() service.display_name = self.account.display_name or None service.icon = "%s#blink-icon%s" % (self.account.xcap.icon.url, self.account.xcap.icon.etag) if self.account.xcap.icon is not None else None if blink_settings.presence.offline_note: service.notes.add(blink_settings.presence.offline_note) doc.add(service) return doc
def showSystemMessage(self, call_id, content, timestamp=None, is_error=False): msgid = str(uuid.uuid1()) rendered_message = ChatMessageObject(call_id, msgid, content, False, timestamp) self.rendered_messages.append(rendered_message) if timestamp is None: timestamp = ISOTimestamp.now() if isinstance(timestamp, datetime.datetime): if timestamp.date() != datetime.date.today(): timestamp = time.strftime( "%F %H:%M", time.localtime(calendar.timegm(timestamp.utctimetuple()))) else: timestamp = time.strftime( "%H:%M", time.localtime(calendar.timegm(timestamp.utctimetuple()))) is_error = 1 if is_error else "null" script = """renderSystemMessage('%s', "%s", "%s", %s)""" % ( msgid, processHTMLText(content), timestamp, is_error) if self.finishedLoading: self.executeJavaScript(script) else: self.messageQueue.append(script)
def offline_state(self): blink_settings = BlinkSettings() if self.account is BonjourAccount() or not blink_settings.presence.offline_note: return None account_id = hashlib.md5(self.account.id).hexdigest() timestamp = ISOTimestamp.now() doc = pidf.PIDF(str(self.account.uri)) person = pidf.Person('PID-%s' % account_id) person.timestamp = timestamp person.activities = rpid.Activities() person.activities.add('offline') doc.add(person) service = pidf.Service('SID-%s' % account_id) service.status = 'closed' service.status.extended = 'offline' service.contact = str(self.account.uri) service.timestamp = timestamp service.capabilities = caps.ServiceCapabilities() service.notes.add(blink_settings.presence.offline_note) doc.add(service) return doc
def gotIsComposing(self, window, state, refresh, last_active): self.enableIsComposing = True flag = state == "active" if flag: if refresh is None: refresh = 120 if last_active is not None and ( last_active - ISOTimestamp.now() > datetime.timedelta(seconds=refresh)): # message is old, discard it return if self.remoteTypingTimer: # if we don't get any indications in the request refresh, then we assume remote to be idle self.remoteTypingTimer.setFireDate_( NSDate.dateWithTimeIntervalSinceNow_(refresh)) else: self.remoteTypingTimer = NSTimer.scheduledTimerWithTimeInterval_target_selector_userInfo_repeats_( refresh, self, "remoteBecameIdle:", window, False) else: if self.remoteTypingTimer: self.remoteTypingTimer.invalidate() self.remoteTypingTimer = None window.noteView_isComposing_(self, flag)
def offline_state(self): if self.account is BonjourAccount(): return None blink_settings = BlinkSettings() account_id = hashlib.md5(self.account.id.encode()).hexdigest() timestamp = ISOTimestamp.now() doc = pidf.PIDF(str(self.account.uri)) person = pidf.Person('PID-%s' % account_id) person.timestamp = timestamp person.activities = rpid.Activities() person.activities.add('offline') doc.add(person) service = pidf.Service('SID-%s' % account_id) service.status = 'closed' service.status.extended = 'offline' service.contact = str(self.account.uri) service.timestamp = timestamp service.capabilities = caps.ServiceCapabilities() service.display_name = self.account.display_name or None service.icon = "%s#blink-icon%s" % ( self.account.xcap.icon.url, self.account.xcap.icon.etag ) if self.account.xcap.icon is not None else None if blink_settings.presence.offline_note: service.notes.add(blink_settings.presence.offline_note) doc.add(service) return doc
def from_session(cls, session): if session.start_time is None and session.end_time is not None: # Session may have anded before it fully started session.start_time = session.end_time call_time = session.start_time or ISOTimestamp.now() if session.start_time and session.end_time: duration = session.end_time - session.start_time else: duration = None remote_uri = '%s@%s' % (session.remote_identity.uri.user, session.remote_identity.uri.host) match = cls.phone_number_re.match(remote_uri) if match: remote_uri = match.group('number') try: contact = next(contact for contact in AddressbookManager().get_contacts() if remote_uri in (addr.uri for addr in contact.uris)) except StopIteration: display_name = session.remote_identity.display_name else: display_name = contact.name return cls(session.direction, display_name, remote_uri, unicode(session.account.id), call_time, duration)
def sendReplicationMessage(self, response_code, content, content_type="message/cpim", timestamp=None): timestamp = timestamp or ISOTimestamp.now() # Lookup routes if self.account.sip.outbound_proxy is not None: uri = SIPURI(host=self.account.sip.outbound_proxy.host, port=self.account.sip.outbound_proxy.port, parameters={ 'transport': self.account.sip.outbound_proxy.transport }) else: uri = SIPURI(host=self.account.id.domain) route = None if self.last_route is None: lookup = DNSLookup() settings = SIPSimpleSettings() try: routes = lookup.lookup_sip_proxy( uri, settings.sip.transport_list).wait() except DNSLookupError: pass else: route = routes[0] else: route = self.last_route if route: extra_headers = [ Header("X-Offline-Storage", "no"), Header("X-Replication-Code", str(response_code)), Header("X-Replication-Timestamp", str(ISOTimestamp.now())) ] message_request = Message(FromHeader(self.account.uri, self.account.display_name), ToHeader(self.account.uri), RouteHeader(route.uri), content_type, content, credentials=self.account.credentials, extra_headers=extra_headers) message_request.send( 15 if content_type != "application/im-iscomposing+xml" else 5)
def chatView_becameActive_(self, chatView, last_active): if self.enableIsComposing: content = IsComposingMessage( state=State("active"), refresh=Refresh(60), last_active=LastActive(last_active or ISOTimestamp.now()), content_type=ContentType('text')).toxml() self.sendMessage(content, IsComposingDocument.content_type)
def _janus_send_request(self, req): data = json.dumps(req.as_dict()) if JanusConfig.trace_janus: self.factory.janus_logger.msg("OUT", ISOTimestamp.now(), data) self.sendMessage(data) d = defer.Deferred() self._janus_pending_transactions[req.transaction_id] = (req, d) return d
def inject(self, msg, appdata=None): msg = unicode(msg) if appdata is not None: stream = appdata.get('stream', None) if stream is not None: try: stream.send_message(msg, timestamp=ISOTimestamp.now()) except ChatStreamError, e: BlinkLogger().log_error(u"Error sending OTR chat message: %s" % e)
def updateIdleTimer_(self, timer): must_publish = False hostname = socket.gethostname().split(".")[0] if hostname != self.hostname: must_publish = True self.hostname = hostname last_time_offset = int(pidf.TimeOffset()) if last_time_offset != self.last_time_offset: must_publish = True self.last_time_offset = last_time_offset # secret sausage after taking the red pill = indigestion last_idle_counter = CGEventSourceSecondsSinceLastEventType(0, int(4294967295)) self.previous_idle_counter = last_idle_counter if self.previous_idle_counter > last_idle_counter: self.last_input = ISOTimestamp.now() selected_item = self.owner.presenceActivityPopUp.selectedItem() if selected_item is None: return activity_object = selected_item.representedObject() if activity_object is None: return if activity_object['title'] not in ('Available', 'Away'): if must_publish: self.publish() return settings = SIPSimpleSettings() if last_idle_counter > settings.gui.idle_threshold: if not self.idle_mode: self.user_input = {'state': 'idle', 'last_input': self.last_input} if activity_object['title'] != "Away": i = self.owner.presenceActivityPopUp.indexOfItemWithTitle_('Away') self.owner.presenceActivityPopUp.selectItemAtIndex_(i) self.presenceStateBeforeIdle = activity_object self.presenceStateBeforeIdle['note'] = unicode(self.owner.presenceNoteText.stringValue()) self.idle_mode = True must_publish = True else: if self.idle_mode: self.user_input = {'state': 'active', 'last_input': None} if activity_object['title'] == "Away": if self.presenceStateBeforeIdle: i = self.owner.presenceActivityPopUp.indexOfItemWithRepresentedObject_(self.presenceStateBeforeIdle) self.owner.presenceActivityPopUp.selectItemAtIndex_(i) self.owner.presenceNoteText.setStringValue_(self.presenceStateBeforeIdle['note']) self.presenceStateBeforeIdle = None self.idle_mode = False must_publish = True if must_publish: self.publish()
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 updateIdleTimer_(self, timer): must_publish = False hostname = socket.gethostname().split(".")[0] if hostname != self.hostname: must_publish = True self.hostname = hostname last_time_offset = int(pidf.TimeOffset()) if last_time_offset != self.last_time_offset: must_publish = True self.last_time_offset = last_time_offset last_idle_counter = CGEventSourceSecondsSinceLastEventType(kCGEventSourceStateHIDSystemState, kCGEventMouseMoved) self.previous_idle_counter = last_idle_counter if self.previous_idle_counter > last_idle_counter: self.last_input = ISOTimestamp.now() selected_item = self.owner.presenceActivityPopUp.selectedItem() if selected_item is None: return activity_object = selected_item.representedObject() if activity_object is None: return if activity_object['title'] not in ('Available', 'Away'): if must_publish: self.publish() return settings = SIPSimpleSettings() if last_idle_counter > settings.gui.idle_threshold: if not self.idle_mode: self.user_input = {'state': 'idle', 'last_input': self.last_input} if activity_object['title'] != "Away": i = self.owner.presenceActivityPopUp.indexOfItemWithTitle_('Away') self.owner.presenceActivityPopUp.selectItemAtIndex_(i) self.presenceStateBeforeIdle = activity_object self.presenceStateBeforeIdle['note'] = str(settings.presence_state.note) self.idle_mode = True must_publish = True else: if self.idle_mode: self.user_input = {'state': 'active', 'last_input': None} if activity_object['title'] == "Away": if self.presenceStateBeforeIdle: i = self.owner.presenceActivityPopUp.indexOfItemWithRepresentedObject_(self.presenceStateBeforeIdle) self.owner.presenceActivityPopUp.selectItemAtIndex_(i) self.owner.presenceNoteText.setStringValue_(self.presenceStateBeforeIdle['note']) self.presenceStateBeforeIdle = None self.idle_mode = False must_publish = True if must_publish: self.publish()
def onMessage(self, payload, isBinary): if isBinary: log.warn('Unexpected binary payload received') return if JanusConfig.trace_janus: self.factory.janus_logger.msg("IN", ISOTimestamp.now(), payload) try: data = json.loads(payload) except Exception, e: log.warn('Error decoding payload: %s' % e) return
def sendMessage(self, content, content_type="text/plain"): # entry point for sending messages, they will be added to self.message_queue if content_type != "application/im-iscomposing+xml": icon = NSApp.delegate().contactsWindowController.iconPathForSelf() if not isinstance(content, OTRInternalMessage): timestamp = ISOTimestamp.now() hash = hashlib.sha1() content = content.decode() if isinstance(content, bytes) else content hash.update((content + str(timestamp)).encode("utf-8")) id = hash.hexdigest() call_id = '' encryption = '' if self.encryption.active: encryption = 'verified' if self.encryption.verified else 'unverified' self.chatViewController.showMessage(call_id, id, 'outgoing', None, icon, content, timestamp, state="sent", media_type='sms', encryption=encryption) recipient = ChatIdentity(self.target_uri, self.display_name) mInfo = MessageInfo(id, sender=self.account, recipient=recipient, timestamp=timestamp, content_type=content_type, content=content, status="queued", encryption=encryption) self.messages[id] = mInfo self.message_queue.put(mInfo) else: self.message_queue.put(content) # Async DNS lookup if host is None or host.default_ip is None: self.setRoutesFailed( NSLocalizedString("No Internet connection", "Label")) return if self.last_route is None: self.lookup_destination(self.target_uri) else: self.setRoutesResolved([self.last_route])
def onMessage(self, payload, is_binary): if is_binary: log.warn('Received invalid binary message') return if GeneralConfig.trace_websocket: self.factory.ws_logger.msg("IN", ISOTimestamp.now(), payload) try: data = json.loads(payload) except Exception, e: log.warn('Error parsing WebSocket payload: %s' % e) return
def _handle_SEND(self, chunk): if chunk.size == 0: # keep-alive self.msrp_session.send_report(chunk, 200, 'OK') return content_type = chunk.content_type.lower() if not contains_mime_type(self.accept_types, content_type): self.msrp_session.send_report(chunk, 413, 'Unwanted Message') return if chunk.contflag == '#': self.incoming_queue.pop(chunk.message_id, None) self.msrp_session.send_report(chunk, 200, 'OK') return elif chunk.contflag == '+': self.incoming_queue[chunk.message_id].append(chunk.data) self.msrp_session.send_report(chunk, 200, 'OK') return else: data = ''.join(self.incoming_queue.pop(chunk.message_id, [])) + chunk.data if content_type == 'message/cpim': try: payload = CPIMPayload.decode(data) except CPIMParserError: self.msrp_session.send_report(chunk, 400, 'CPIM Parser Error') return else: message = Message(**{name: getattr(payload, name) for name in Message.__slots__}) if not contains_mime_type(self.accept_wrapped_types, message.content_type): self.msrp_session.send_report(chunk, 413, 'Unwanted Message') return if message.timestamp is None: message.timestamp = ISOTimestamp.now() if message.sender is None: message.sender = self.remote_identity private = self.session.remote_focus and len(message.recipients) == 1 and message.recipients[0] != self.remote_identity else: payload = SimplePayload.decode(data, content_type) message = Message(payload.content, payload.content_type, sender=self.remote_identity, recipients=[self.local_identity], timestamp=ISOTimestamp.now()) private = False try: message.content = self.encryption.otr_session.handle_input(message.content, message.content_type) except IgnoreMessage: self.msrp_session.send_report(chunk, 200, 'OK') return except UnencryptedMessage: encrypted = False encryption_active = True except EncryptedMessageError, e: self.msrp_session.send_report(chunk, 400, str(e)) notification_center = NotificationCenter() notification_center.post_notification('ChatStreamOTRError', sender=self, data=NotificationData(error=str(e))) return
def _NH_SIPApplicationDidStart(self, notification): settings = SIPSimpleSettings() if settings.presence_state.timestamp is None: settings.presence_state.timestamp = ISOTimestamp.now() settings.save() self.get_location([account for account in AccountManager().iter_accounts() if account is not BonjourAccount()]) self.publish() idle_timer = NSTimer.timerWithTimeInterval_target_selector_userInfo_repeats_(1.0, self, "updateIdleTimer:", None, True) NSRunLoop.currentRunLoop().addTimer_forMode_(idle_timer, NSRunLoopCommonModes) NSRunLoop.currentRunLoop().addTimer_forMode_(idle_timer, NSEventTrackingRunLoopMode)
def _send_message(self, message): if (not self.last_route): self.log_info('No route found') return message.timestamp = ISOTimestamp.now() if not isinstance(message, OTRInternalMessage): try: content = self.encryption.otr_session.handle_output( message.content, message.content_type) except OTRError as e: if 'has ended the private conversation' in str(e): self.log_info( 'Encryption has been disabled by remote party, please resend the message again' ) self.encryption.stop() else: self.log_info('Failed to encrypt outgoing message: %s' % str(e)) return timeout = 5 if message.content_type != "application/im-iscomposing+xml": self.enableIsComposing = True timeout = 15 message_request = Message(FromHeader(self.account.uri, self.account.display_name), ToHeader(self.target_uri), RouteHeader(self.last_route.uri), message.content_type, message.content, credentials=self.account.credentials) self.notification_center.add_observer(self, sender=message_request) message_request.send(timeout) message.status = 'sent' message.call_id = message_request._request.call_id.decode() if not isinstance(message, OTRInternalMessage): if self.encryption.active: self.log_info( 'Sending encrypted %s message %s to %s' % (message.content_type, message.id, self.last_route.uri)) else: self.log_info( 'Sending %s message %s to %s' % (message.content_type, message.id, self.last_route.uri)) id = str(message_request) self.messages[id] = message
def _NH_XMPPSubscriptionGotNotify(self, notification): stanza = notification.data.presence self._stanza_cache[stanza.sender.uri] = stanza stanza.timestamp = ISOTimestamp.now() # TODO: mirror the one in the stanza, if present pidf_doc = self._build_pidf() if XMPPGatewayConfig.log_presence: log.msg('XMPP notification from %s to %s for presence flow 0x%x' % (format_uri(self.xmpp_identity.uri, 'xmpp'), format_uri(self.sip_identity.uri, 'sip'), id(self))) for subscription in self._sip_subscriptions: try: subscription.push_content(pidf.PIDFDocument.content_type, pidf_doc) except SIPCoreError, e: if XMPPGatewayConfig.log_presence: log.msg('Failed to send SIP NOTIFY from %s to %s for presence flow 0x%x: %s' % (format_uri(self.xmpp_identity.uri, 'xmpp'), format_uri(self.sip_identity.uri, 'sip'), id(self), e))
def _NH_ChatStreamOTREncryptionStateChanged(self, stream, data): if data.new_state is OTRState.Encrypted: local_fingerprint = stream.encryption.key_fingerprint remote_fingerprint = stream.encryption.peer_fingerprint self.log_info("Chat encryption activated using OTR protocol") self.log_info("OTR local fingerprint %s" % local_fingerprint) self.log_info("OTR remote fingerprint %s" % remote_fingerprint) self.chatViewController.showSystemMessage("0", "Encryption enabled", ISOTimestamp.now()) self.showSystemMessage("Encryption enabled", ISOTimestamp.now()) elif data.new_state is OTRState.Finished: self.log_info("Chat encryption deactivated") self.chatViewController.showSystemMessage("0", "Encryption deactivated", ISOTimestamp.now(), is_error=True) elif data.new_state is OTRState.Plaintext: self.log_info("Chat encryption deactivated") self.chatViewController.showSystemMessage("0", "Encryption deactivated", ISOTimestamp.now(), is_error=True)
def missed_session(originator, destination, tokens): for token in tokens: data = {'to': token, 'notification': {}, 'data': {'sylkrtc': {}}, 'content_available': True } data['notification']['body'] = 'Missed session from %s' % originator data['priority'] = 'high' # No TTL, default is 4 weeks data['data']['sylkrtc']['event'] = 'missed_session' data['data']['sylkrtc']['originator'] = originator data['data']['sylkrtc']['destination'] = destination data['data']['sylkrtc']['timestamp'] = str(ISOTimestamp.now()) _send_push_notification(json.dumps(data))
def addAnsweringMachineRecordingToHistory(self, filename, duration): message = "<h3>Answering Machine Recording</h3>" message += "<p>%s" % filename message += "<br>Duration: %s seconds" % duration message += "<p><audio src='%s' controls='controls'>" % urllib.parse.quote(filename) media_type = 'voicemail' local_uri = format_identity_to_string(self.session.account) remote_uri = format_identity_to_string(self.session.remote_identity) direction = 'incoming' status = 'delivered' cpim_from = format_identity_to_string(self.session.remote_identity) cpim_to = format_identity_to_string(self.session.remote_identity) timestamp = str(ISOTimestamp.now()) self.add_to_history(media_type, local_uri, remote_uri, direction, cpim_from, cpim_to, timestamp, message, status)
def missed_session(originator, destination, tokens): for token in tokens: data = dict(to=token, notification={}, data={'sylkrtc': {}}, content_available=True) data['notification']['body'] = 'Missed call from %s' % originator data['notification']['sound'] = 'Blow' data['priority'] = 'high' # No TTL, default is 4 weeks data['data']['sylkrtc']['event'] = 'missed_session' data['data']['sylkrtc']['originator'] = originator data['data']['sylkrtc']['destination'] = destination data['data']['sylkrtc']['timestamp'] = str(ISOTimestamp.now()) _send_push_notification(json.dumps(data))
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 incoming_session(originator, destination, tokens): for token in tokens: data = {'to': token, 'notification': {}, 'data': {'sylkrtc': {}}, 'content_available': True } data['notification']['body'] = 'Incoming session from %s' % originator data['priority'] = 'high' data['time_to_live'] = 60 # don't deliver if phone is out for over a minute data['data']['sylkrtc']['event'] = 'incoming_session' data['data']['sylkrtc']['originator'] = originator data['data']['sylkrtc']['destination'] = destination data['data']['sylkrtc']['timestamp'] = str(ISOTimestamp.now()) _send_push_notification(json.dumps(data))
def add_to_history(self): FileTransferHistory().add_transfer(transfer_id=self.ft_info.transfer_id, direction=self.ft_info.direction, local_uri=self.ft_info.local_uri, remote_uri=self.ft_info.remote_uri, file_path=self.ft_info.file_path, bytes_transfered=self.ft_info.bytes_transfered, file_size=self.ft_info.file_size or 0, status=self.ft_info.status) message = "<h3>%s File Transfer</h3>" % self.ft_info.direction.capitalize() message += "<p>%s (%s)" % (self.ft_info.file_path, format_size(self.ft_info.file_size or 0)) media_type = 'file-transfer' local_uri = self.ft_info.local_uri remote_uri = self.ft_info.remote_uri direction = self.ft_info.direction status = 'delivered' if self.ft_info.status == 'completed' else 'failed' cpim_from = self.ft_info.remote_uri cpim_to = self.ft_info.remote_uri timestamp = str(ISOTimestamp.now()) ChatHistory().add_message(self.ft_info.transfer_id, media_type, local_uri, remote_uri, direction, cpim_from, cpim_to, timestamp, message, "html", "0", status)
def addAnsweringMachineRecordingToHistory(self, filename, duration): message = "<h3>Answering Machine Recording</h3>" message += "<p>%s" % filename message += "<br>Duration: %s seconds" % duration message += "<p><audio src='%s' controls='controls'>" % urllib.quote(filename) media_type = 'voicemail' local_uri = format_identity_to_string(self.session.account) remote_uri = format_identity_to_string(self.session.remote_identity) direction = 'incoming' status = 'delivered' cpim_from = format_identity_to_string(self.session.remote_identity) cpim_to = format_identity_to_string(self.session.remote_identity) timestamp = str(ISOTimestamp.now()) self.add_to_history(media_type, local_uri, remote_uri, direction, cpim_from, cpim_to, timestamp, message, status)
def from_session(cls, session): if session.start_time is None and session.end_time is not None: # Session may have anded before it fully started session.start_time = session.end_time call_time = session.start_time or ISOTimestamp.now() if session.start_time and session.end_time: duration = session.end_time - session.start_time else: duration = None remote_uri = '%s@%s' % (session.remote_identity.uri.user, session.remote_identity.uri.host) match = cls.phone_number_re.match(remote_uri) if match: remote_uri = match.group('number') display_name = session.remote_identity.display_name return cls(session.direction, display_name, remote_uri, unicode(session.account.id), call_time, duration)
def updateDuration(self): if self.sessionController is not None and self.sessionController.session is not None: if self.sessionController.session.end_time: now = self.sessionController.session.end_time else: now = ISOTimestamp.now() if self.sessionController.session.start_time and now >= self.sessionController.session.start_time: elapsed = now - self.sessionController.session.start_time h = elapsed.days * 24 + elapsed.seconds / (60 * 60) m = (elapsed.seconds / 60) % 60 s = elapsed.seconds % 60 text = "%02i:%02i:%02i" % (h, m, s) self.duration.setStringValue_(text) else: self.duration.setStringValue_('')
def incoming_session(originator, destination, tokens): for token in tokens: data = dict(to=token, notification={}, data={'sylkrtc': {}}, content_available=True) data['notification']['body'] = 'Incoming call from %s' % originator data['notification']['sound'] = 'Blow' data['priority'] = 'high' data[ 'time_to_live'] = 60 # don't deliver if phone is out for over a minute data['data']['sylkrtc']['event'] = 'incoming_session' data['data']['sylkrtc']['originator'] = originator data['data']['sylkrtc']['destination'] = destination data['data']['sylkrtc']['timestamp'] = str(ISOTimestamp.now()) _send_push_notification(json.dumps(data))
def sendMessage(self, text, content_type="text/plain"): self.lookup_destination(self.target_uri) timestamp = ISOTimestamp.now() hash = hashlib.sha1() hash.update(text.encode("utf-8")+str(timestamp)) msgid = hash.hexdigest() call_id = '' if content_type != "application/im-iscomposing+xml": icon = NSApp.delegate().contactsWindowController.iconPathForSelf() self.chatViewController.showMessage(call_id, msgid, 'outgoing', None, icon, text, timestamp, state="sent", media_type='sms') recipient=CPIMIdentity(self.target_uri, self.display_name) self.messages[msgid] = MessageInfo(msgid, sender=self.account, recipient=recipient, timestamp=timestamp, content_type=content_type, text=text, status="queued") self.queue.append((msgid, text, content_type))
def gotMessage(self, sender, call_id, message, is_html=False, is_replication_message=False, timestamp=None): self.enableIsComposing = True icon = NSApp.delegate().contactsWindowController.iconPathForURI(format_identity_to_string(sender)) timestamp = timestamp or ISOTimestamp.now() hash = hashlib.sha1() hash.update(message.encode('utf-8')+str(timestamp)+str(sender)) msgid = hash.hexdigest() self.chatViewController.showMessage(call_id, msgid, 'incoming', format_identity_to_string(sender), icon, message, timestamp, is_html=is_html, state="delivered", media_type='sms') self.notification_center.post_notification('ChatViewControllerDidDisplayMessage', sender=self, data=NotificationData(direction='incoming', history_entry=False, remote_party=format_identity_to_string(sender), local_party=format_identity_to_string(self.account) if self.account is not BonjourAccount() else 'bonjour', check_contact=True)) # save to history if not is_replication_message: message = MessageInfo(msgid, call_id=call_id, direction='incoming', sender=sender, recipient=self.account, timestamp=timestamp, text=message, content_type="html" if is_html else "text", status="delivered") self.add_to_history(message)
def addRecordingToHistory(self, filename): message = "<h3>Video Call Recorded</h3>" message += "<p>%s" % filename message += "<p><video src='%s' width=800 controls='controls'>" % urllib.parse.quote( filename) media_type = 'video-recording' local_uri = format_identity_to_string(self.sessionController.account) remote_uri = format_identity_to_string( self.sessionController.target_uri) direction = 'incoming' status = 'delivered' cpim_from = format_identity_to_string( self.sessionController.target_uri) cpim_to = format_identity_to_string(self.sessionController.target_uri) timestamp = str(ISOTimestamp.now()) self.add_to_history(media_type, local_uri, remote_uri, direction, cpim_from, cpim_to, timestamp, message, status)
def from_session(cls, session): if session.start_time is None and session.end_time is not None: # Session may have ended before it fully started session.start_time = session.end_time call_time = session.start_time or ISOTimestamp.now() if session.start_time and session.end_time: duration = session.end_time - session.start_time else: duration = None remote_uri = '%s@%s' % (session.remote_identity.uri.user, session.remote_identity.uri.host) match = cls.phone_number_re.match(remote_uri) if match: remote_uri = match.group('number') try: contact = next(contact for contact in AddressbookManager().get_contacts() if remote_uri in (addr.uri for addr in contact.uris)) except StopIteration: display_name = session.remote_identity.display_name else: display_name = contact.name return cls(session.direction, display_name, remote_uri, unicode(session.account.id), call_time, duration)
def showSystemMessage(self, call_id, text, timestamp=None, is_error=False): msgid = str(uuid.uuid1()) rendered_message = ChatMessageObject(call_id, msgid, text, False, timestamp) self.rendered_messages.append(rendered_message) if timestamp is None: timestamp = ISOTimestamp.now() if isinstance(timestamp, datetime.datetime): if timestamp.date() != datetime.date.today(): timestamp = time.strftime("%F %T", time.localtime(calendar.timegm(timestamp.utctimetuple()))) else: timestamp = time.strftime("%T", time.localtime(calendar.timegm(timestamp.utctimetuple()))) is_error = 1 if is_error else "null" script = """renderSystemMessage('%s', "%s", "%s", %s)""" % (msgid, processHTMLText(text), timestamp, is_error) if self.finishedLoading: self.executeJavaScript(script) else: self.messageQueue.append(script)
def sendReplicationMessage(self, response_code, text, content_type="message/cpim", timestamp=None): timestamp = timestamp or ISOTimestamp.now() # Lookup routes if self.account.sip.outbound_proxy is not None: uri = SIPURI(host=self.account.sip.outbound_proxy.host, port=self.account.sip.outbound_proxy.port, parameters={'transport': self.account.sip.outbound_proxy.transport}) else: uri = SIPURI(host=self.account.id.domain) lookup = DNSLookup() settings = SIPSimpleSettings() try: routes = lookup.lookup_sip_proxy(uri, settings.sip.transport_list).wait() except DNSLookupError: pass else: utf8_encode = content_type not in ('application/im-iscomposing+xml', 'message/cpim') extra_headers = [Header("X-Offline-Storage", "no"), Header("X-Replication-Code", str(response_code)), Header("X-Replication-Timestamp", str(ISOTimestamp.now()))] message_request = Message(FromHeader(self.account.uri, self.account.display_name), ToHeader(self.account.uri), RouteHeader(routes[0].uri), content_type, text.encode('utf-8') if utf8_encode else text, credentials=self.account.credentials, extra_headers=extra_headers) message_request.send(15 if content_type != "application/im-iscomposing+xml" else 5)
def __init__(self, account, contact, content, content_type='text/plain', recipients=None, courtesy_recipients=None, subject=None, timestamp=None, required=None, additional_headers=None, id=None): self.lookup = None self.account = account self.uri = contact.uri.uri self.content_type = content_type self.content = content self.id = id if id is not None else str(uuid.uuid4()) self.timestamp = timestamp if timestamp is not None else ISOTimestamp.now( ) self.sip_uri = SIPURI.parse('sip:%s' % self.uri)
def gotIsComposing(self, window, state, refresh, last_active): self.enableIsComposing = True flag = state == "active" if flag: if refresh is None: refresh = 120 if last_active is not None and (last_active - ISOTimestamp.now() > datetime.timedelta(seconds=refresh)): # message is old, discard it return if self.remoteTypingTimer: # if we don't get any indications in the request refresh, then we assume remote to be idle self.remoteTypingTimer.setFireDate_(NSDate.dateWithTimeIntervalSinceNow_(refresh)) else: self.remoteTypingTimer = NSTimer.scheduledTimerWithTimeInterval_target_selector_userInfo_repeats_(refresh, self, "remoteBecameIdle:", window, False) else: if self.remoteTypingTimer: self.remoteTypingTimer.invalidate() self.remoteTypingTimer = None window.noteView_isComposing_(self, flag)
def setRoutesFailed(self, reason): self.message_queue.stop() self.started = False for msgObject in self.message_queue: id = msgObject.id try: message = self.messages.pop(id) except KeyError: pass else: if content_type not in ('application/im-iscomposing+xml', 'message/cpim'): self.chatViewController.markMessage( message.id, MSG_STATE_FAILED) message.status = 'failed' self.add_to_history(message) log_text = NSLocalizedString("Routing failure: %s", "Label") % msg self.chatViewController.showSystemMessage( '0', reason, ISOTimestamp.now(), True) self.log_info(log_text)
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 online_state(self): blink_settings = BlinkSettings() state = blink_settings.presence.current_state.state note = blink_settings.presence.current_state.note state = 'offline' if state=='Invisible' else state.lower() if self.account is BonjourAccount(): return BonjourPresenceState(state, note) try: hostname = socket.gethostname() except Exception: hostname = 'localhost' account_id = hashlib.md5(self.account.id).hexdigest() timestamp = ISOTimestamp.now() doc = pidf.PIDF(str(self.account.uri)) person = pidf.Person('PID-%s' % account_id) person.timestamp = timestamp person.activities = rpid.Activities() person.activities.add(state) doc.add(person) if state == 'offline': service = pidf.Service('SID-%s' % account_id) service.status = 'closed' service.status.extended = state service.contact = str(self.account.uri) service.timestamp = timestamp service.capabilities = caps.ServiceCapabilities() doc.add(service) else: settings = SIPSimpleSettings() instance_id = str(uuid.UUID(settings.instance_id)) service = pidf.Service('SID-%s' % instance_id) service.status = 'open' service.status.extended = state service.contact = str(self.account.contact.public_gruu or self.account.uri) service.timestamp = timestamp service.capabilities = caps.ServiceCapabilities() service.capabilities.audio = True service.capabilities.text = False service.capabilities.message = True service.capabilities.file_transfer = True service.capabilities.screen_sharing_server = True service.capabilities.screen_sharing_client = True service.display_name = self.account.display_name or None service.icon = "%s#blink-icon%s" % (self.account.xcap.icon.url, self.account.xcap.icon.etag) if self.account.xcap.icon is not None else None service.device_info = pidf.DeviceInfo(instance_id, description=hostname, user_agent=settings.user_agent) service.device_info.time_offset = pidf.TimeOffset() # TODO: Add real user input data -Saul service.user_input = rpid.UserInput() service.user_input.idle_threshold = 600 service.add(pidf.DeviceID(instance_id)) if note: service.notes.add(note) doc.add(service) device = pidf.Device('DID-%s' % instance_id, device_id=pidf.DeviceID(instance_id)) device.timestamp = timestamp device.notes.add(u'%s at %s' % (settings.user_agent, hostname)) doc.add(device) return doc
def _send_data(self, data): if GeneralConfig.trace_websocket: self.factory.ws_logger.msg("OUT", ISOTimestamp.now(), data) self.sendMessage(data, False)
def online_state(self): blink_settings = BlinkSettings() state = blink_settings.presence.current_state.state note = blink_settings.presence.current_state.note state = 'offline' if state == 'Invisible' else state.lower() if self.account is BonjourAccount(): return BonjourPresenceState(state, note) try: hostname = socket.gethostname() except Exception: hostname = 'localhost' account_id = hashlib.md5(self.account.id.encode()).hexdigest() timestamp = ISOTimestamp.now() doc = pidf.PIDF(str(self.account.uri)) person = pidf.Person('PID-%s' % account_id) person.timestamp = timestamp person.activities = rpid.Activities() person.activities.add(state) doc.add(person) if state == 'offline': service = pidf.Service('SID-%s' % account_id) service.status = 'closed' service.status.extended = state service.contact = str(self.account.uri) service.timestamp = timestamp service.capabilities = caps.ServiceCapabilities() service.display_name = self.account.display_name or None service.icon = "%s#blink-icon%s" % ( self.account.xcap.icon.url, self.account.xcap.icon.etag ) if self.account.xcap.icon is not None else None doc.add(service) else: settings = SIPSimpleSettings() instance_id = str(uuid.UUID(settings.instance_id)) service = pidf.Service('SID-%s' % instance_id) service.status = 'open' service.status.extended = state service.contact = str(self.account.contact.public_gruu or self.account.uri) service.timestamp = timestamp service.capabilities = caps.ServiceCapabilities() service.capabilities.audio = True service.capabilities.text = False service.capabilities.message = True service.capabilities.file_transfer = True service.capabilities.screen_sharing_server = True service.capabilities.screen_sharing_client = True service.display_name = self.account.display_name or None service.icon = "%s#blink-icon%s" % ( self.account.xcap.icon.url, self.account.xcap.icon.etag ) if self.account.xcap.icon is not None else None service.device_info = pidf.DeviceInfo( instance_id, description=hostname, user_agent=settings.user_agent) service.device_info.time_offset = pidf.TimeOffset() # TODO: Add real user input data -Saul service.user_input = rpid.UserInput() service.user_input.idle_threshold = 600 service.add(pidf.DeviceID(instance_id)) if note: service.notes.add(note) doc.add(service) device = pidf.Device('DID-%s' % instance_id, device_id=pidf.DeviceID(instance_id)) device.timestamp = timestamp device.notes.add('%s at %s' % (settings.user_agent, hostname)) doc.add(device) return doc
def logDataIn(buf): buf = buf.strip() if buf: xmpp_logger.msg("RECEIVED", ISOTimestamp.now(), buf)
def chatView_becameActive_(self, chatView, last_active): if self.enableIsComposing: content = IsComposingMessage(state=State("active"), refresh=Refresh(60), last_active=LastActive(last_active or ISOTimestamp.now()), content_type=ContentType('text')).toxml() self.sendMessage(content, IsComposingDocument.content_type)
def logDataOut(buf): buf = buf.strip() if buf: xmpp_logger.msg("SENDING", ISOTimestamp.now(), buf)
def _NH_SIPEngineGotMessage(self, sender, data): account = AccountManager().find_account(data.request_uri) if not account: BlinkLogger().log_warning(u"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 = CPIMMessage.parse(data.body) except CPIMParserError: BlinkLogger().log_warning(u"Incoming SMS from %s to %s has invalid CPIM content" % format_identity_to_string(data.from_header), account.id) return else: is_cpim = True body = cpim_message.body 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: body = 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 body = cpim_message.body if is_cpim else data.body msg = IsComposingMessage.parse(body) 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(u"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, body, is_html, is_replication_message, replication_timestamp, window=window) self.windowForViewer(viewer).noteView_isComposing_(viewer, False)