Commit e10d8fc8 authored by Tijmen de Mes's avatar Tijmen de Mes

Added IMDN support

parent 5a3ca07a
......@@ -174,6 +174,9 @@ class Blink(QApplication, metaclass=QSingleton):
self.main_window.conference_dialog.close()
self.main_window.filetransfer_window.close()
self.main_window.preferences_window.close()
if watched is self.chat_window:
if event.type() == QEvent.WindowActivate:
watched.send_pending_imdn_messages(watched.selected_session)
return False
def customEvent(self, event):
......
......@@ -1551,6 +1551,8 @@ class ChatWindow(base_class, ui_class, ColorHelperMixin):
if geometry:
self.restoreGeometry(geometry)
self.pending_displayed_notifications = {}
notification_center = NotificationCenter()
notification_center.add_observer(self, name='SIPApplicationDidStart')
notification_center.add_observer(self, name='BlinkSessionNewIncoming')
......@@ -1572,6 +1574,7 @@ class ChatWindow(base_class, ui_class, ColorHelperMixin):
notification_center.add_observer(self, name='MediaStreamWillEnd')
notification_center.add_observer(self, name='BlinkGotMessage')
notification_center.add_observer(self, name='BlinkGotComposingIndication')
notification_center.add_observer(self, name='BlinkGotDispositionNotification')
notification_center.add_observer(self, name='BlinkMessageDidSucceed')
notification_center.add_observer(self, name='BlinkMessageDidFail')
......@@ -2152,6 +2155,12 @@ class ChatWindow(base_class, ui_class, ColorHelperMixin):
painter.drawLine(-3.5, 3.5, 3.5, -3.5)
painter.restore()
def send_pending_imdn_messages(self, session):
if session and session.blink_session in self.pending_displayed_notifications:
item = self.pending_displayed_notifications.pop(self.selected_session.blink_session)
for (id, timestamp) in item:
MessageManager().send_imdn_message(session.blink_session, id, timestamp, 'displayed')
@run_in_gui_thread
def handle_notification(self, notification):
handler = getattr(self, '_NH_%s' % notification.name, Null)
......@@ -2313,6 +2322,12 @@ class ChatWindow(base_class, ui_class, ColorHelperMixin):
session.chat_widget.add_message(ChatMessage(content, sender, 'incoming', id=message.id))
if message.disposition is not None and 'display' in message.disposition:
if self.selected_session.blink_session is blink_session and not self.isMinimized() and self.isActiveWindow():
MessageManager().send_imdn_message(blink_session, message.id, message.timestamp, 'displayed')
else:
self.pending_displayed_notifications.setdefault(blink_session, []).append((message.id, message.timestamp))
session.remote_composing = False
settings = SIPSimpleSettings()
if settings.sounds.play_message_alerts and self.selected_session is session:
......@@ -2326,6 +2341,14 @@ class ChatWindow(base_class, ui_class, ColorHelperMixin):
return
session.update_composing_indication(notification.data)
def _NH_BlinkGotDispositionNotification(self, notification):
blink_session = notification.sender
session = blink_session.items.chat
if session is None:
return
data = notification.data
session.chat_widget.update_message_status(id=data.id, status=data.status)
def _NH_BlinkMessageDidSucceed(self, notification):
blink_session = notification.sender
session = blink_session.items.chat
......@@ -2580,6 +2603,8 @@ class ChatWindow(base_class, ui_class, ColorHelperMixin):
self.session_details.setCurrentWidget(self.selected_session.active_panel)
self.participants_list.setModel(self.selected_session.participants_model)
self.control_button.setEnabled(True)
if not self.isMinimized():
self.send_pending_imdn_messages(self.selected_session)
else:
self.tab_widget.setCurrentWidget(self.dummy_tab)
self.session_details.setCurrentWidget(self.info_panel)
......
......@@ -11,7 +11,7 @@ from sipsimple.configuration.settings import SIPSimpleSettings
from sipsimple.core import SIPURI, FromHeader, ToHeader, Message, RouteHeader
from sipsimple.lookup import DNSLookup
from sipsimple.payloads.iscomposing import IsComposingDocument, IsComposingMessage, State, LastActive, Refresh, ContentType
from sipsimple.payloads.imdn import IMDNDocument
from sipsimple.payloads.imdn import IMDNDocument, DeliveryNotification, DisplayNotification
from sipsimple.streams.msrp.chat import CPIMPayload, CPIMParserError, CPIMNamespace, CPIMHeader, ChatIdentity, Message as MSRPChatMessage, SimplePayload
from sipsimple.util import ISOTimestamp
......@@ -34,6 +34,7 @@ class BlinkMessage(MSRPChatMessage):
@implementer(IObserver)
class OutgoingMessage(object):
__ignored_content_types__ = {IsComposingDocument.content_type, IMDNDocument.content_type} #Content types to ignore in notifications
__disabled_imdn_content_types__ = {'text/pgp-public-key', 'text/pgp-private-key'}.union(__ignored_content_types__) #Content types to ignore in notifications
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
......@@ -136,11 +137,20 @@ class OutgoingMessage(object):
def _NH_SIPMessageDidSucceed(self, notification):
notification_center = NotificationCenter()
if self.content_type.lower() in self.__ignored_content_types__:
if self.content_type.lower() == IMDNDocument.content_type:
document = IMDNDocument.parse(self.content)
imdn_message_id = document.message_id.value
imdn_status = document.notification.status.__str__()
notification_center.post_notification('BlinkDidSendDispositionNotification', sender=self.session, data=NotificationData(id=imdn_message_id, status=imdn_status))
return
notification_center.post_notification('BlinkMessageDidSucceed', sender=self.session, data=NotificationData(data=notification.data, id=self.id))
def _NH_SIPMessageDidFail(self, notification):
if self.content_type.lower() in self.__ignored_content_types__:
return
notification_center = NotificationCenter()
notification_center.post_notification('BlinkMessageDidFail', sender=self.session, data=NotificationData(data=notification.data, id=self.id))
......@@ -226,12 +236,28 @@ class MessageManager(object, metaclass=Singleton):
return
timestamp = str(cpim_message.timestamp) if cpim_message is not None and cpim_message.timestamp is not None else str(ISOTimestamp.now())
message = BlinkMessage(body, content_type, sender, timestamp=timestamp, id=message_id)
if account.sms.use_cpim and account.sms.enable_imdn and content_type.lower() == IMDNDocument.content_type:
# print("-- IMDN received")
document = IMDNDocument.parse(body)
imdn_message_id = document.message_id.value
imdn_status = document.notification.status.__str__()
imdn_datetime = document.datetime.__str__()
notification_center.post_notification('BlinkGotDispositionNotification', sender=blink_session, data=NotificationData(id=imdn_message_id, status=imdn_status))
return
message = BlinkMessage(body, content_type, sender, timestamp=timestamp, id=message_id, disposition=disposition)
notification_center.post_notification('BlinkMessageIsParsed', sender=blink_session, data=message)
if disposition is not None and 'positive-delivery' in disposition:
# print("-- Should send delivered imdn")
self.send_imdn_message(blink_session, message_id, timestamp, 'delivered')
notification_center.post_notification('BlinkGotMessage', sender=blink_session, data=message)
else:
pass
# TODO handle replicated messages
pass
def _NH_BlinkSessionWasCreated(self, notification):
self.sessions.append(notification.sender)
......@@ -248,6 +274,25 @@ class MessageManager(object, metaclass=Singleton):
self.send_message(session.account, session.contact, content, IsComposingDocument.content_type)
def send_imdn_message(self, session, id, timestamp, state):
if not session.account.sms.use_cpim and not session.account.sms.enable_imdn:
return
# print(f"-- Will send imdn for {id} -> {state}")
if state == 'delivered':
notification = DeliveryNotification(state)
elif state == 'displayed':
notification = DisplayNotification(state)
elif state == 'error':
notification = DisplayNotification(state)
content = IMDNDocument.create(message_id=id,
datetime=timestamp,
recipient_uri=session.contact.uri.uri,
notification=notification)
self.send_message(session.account, session.contact, content, IMDNDocument.content_type)
def send_message(self, account, contact, content, content_type='text/plain', recipients=None, courtesy_recipients=None, subject=None, timestamp=None, required=None, additional_headers=None, id=None):
blink_session = next(session for session in self.sessions if session.reusable and session.contact.settings is contact.settings)
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment