diff --git a/data/gui/chat_control.ui b/data/gui/chat_control.ui index de3208afbc9fa94f54b7f9be20355858383ee499..8ab302dad1d07cb9c341a46538d7d8e06d88c9a3 100644 --- a/data/gui/chat_control.ui +++ b/data/gui/chat_control.ui @@ -5,7 +5,6 @@ <object class="GtkVBox" id="chat_control_vbox"> <property name="can_focus">True</property> <property name="border_width">3</property> - <property name="orientation">vertical</property> <property name="spacing">1</property> <child> <object class="GtkHBox" id="hbox3"> @@ -13,7 +12,6 @@ <child> <object class="GtkVBox" id="vbox2"> <property name="visible">True</property> - <property name="orientation">vertical</property> <child> <object class="GtkAlignment" id="alignment"> <property name="visible">True</property> @@ -43,7 +41,6 @@ <property name="visible">True</property> <property name="events">GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK</property> <property name="border_width">5</property> - <property name="orientation">vertical</property> <child> <object class="GtkLabel" id="banner_name_label"> <property name="visible">True</property> @@ -195,7 +192,6 @@ <child> <object class="GtkVBox" id="vbox106"> <property name="visible">True</property> - <property name="orientation">vertical</property> <child> <object class="GtkScrolledWindow" id="conversation_scrolledwindow"> <property name="height_request">60</property> @@ -314,7 +310,6 @@ <object class="GtkVSeparator" id="vseparator1"> <property name="visible">True</property> <property name="events">GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK</property> - <property name="orientation">vertical</property> </object> <packing> <property name="expand">False</property> @@ -470,7 +465,6 @@ <object class="GtkVSeparator" id="vseparator3"> <property name="visible">True</property> <property name="events">GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK</property> - <property name="orientation">vertical</property> </object> <packing> <property name="expand">False</property> @@ -499,6 +493,14 @@ <property name="position">11</property> </packing> </child> + <child> + <object class="GtkComboBox" id="label_selector"> + <property name="visible">True</property> + </object> + <packing> + <property name="position">12</property> + </packing> + </child> <child> <object class="GtkAlignment" id="alignment1"> <property name="visible">True</property> @@ -508,7 +510,7 @@ </child> </object> <packing> - <property name="position">12</property> + <property name="position">13</property> </packing> </child> <child> @@ -555,7 +557,7 @@ </object> <packing> <property name="expand">False</property> - <property name="position">13</property> + <property name="position">14</property> </packing> </child> </object> @@ -572,7 +574,6 @@ <child> <object class="GtkVBox" id="audio_vbox"> <property name="no_show_all">True</property> - <property name="orientation">vertical</property> <property name="spacing">6</property> <child> <object class="GtkLabel" id="label3"> diff --git a/data/gui/groupchat_control.ui b/data/gui/groupchat_control.ui index f9ebc2798be25bb124260bd4d7d8683d778a7fdb..9c164c76ba994663ac482500ca73507513db8f34 100644 --- a/data/gui/groupchat_control.ui +++ b/data/gui/groupchat_control.ui @@ -5,7 +5,6 @@ <object class="GtkVBox" id="groupchat_control_vbox"> <property name="can_focus">True</property> <property name="border_width">3</property> - <property name="orientation">vertical</property> <child> <object class="GtkAlignment" id="alignment"> <property name="visible">True</property> @@ -35,7 +34,6 @@ <property name="visible">True</property> <property name="events">GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK</property> <property name="border_width">5</property> - <property name="orientation">vertical</property> <child> <object class="GtkLabel" id="banner_name_label"> <property name="visible">True</property> @@ -133,7 +131,6 @@ <object class="GtkVSeparator" id="vseparator2"> <property name="visible">True</property> <property name="events">GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK</property> - <property name="orientation">vertical</property> </object> <packing> <property name="expand">False</property> @@ -233,7 +230,6 @@ <object class="GtkVSeparator" id="vseparator4"> <property name="visible">True</property> <property name="events">GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK</property> - <property name="orientation">vertical</property> </object> <packing> <property name="expand">False</property> @@ -269,6 +265,14 @@ <property name="position">8</property> </packing> </child> + <child> + <object class="GtkComboBox" id="label_selector"> + <property name="visible">True</property> + </object> + <packing> + <property name="position">9</property> + </packing> + </child> <child> <object class="GtkAlignment" id="alignment2"> <property name="visible">True</property> @@ -278,7 +282,7 @@ </child> </object> <packing> - <property name="position">9</property> + <property name="position">10</property> </packing> </child> <child> @@ -326,7 +330,7 @@ <packing> <property name="expand">False</property> <property name="fill">False</property> - <property name="position">10</property> + <property name="position">11</property> </packing> </child> </object> @@ -346,12 +350,10 @@ <object class="GtkVBox" id="vbox108"> <property name="width_request">0</property> <property name="visible">True</property> - <property name="orientation">vertical</property> <property name="spacing">6</property> <child> <object class="GtkVBox" id="vbox109"> <property name="visible">True</property> - <property name="orientation">vertical</property> <property name="spacing">6</property> <child> <object class="GtkScrolledWindow" id="conversation_scrolledwindow"> diff --git a/src/chat_control.py b/src/chat_control.py index e71a6d2a58af3d60f985693494522f5cb11b4c2a..d0028745e83202f4b6f1be2611b7b5846db46746 100644 --- a/src/chat_control.py +++ b/src/chat_control.py @@ -232,6 +232,29 @@ class ChatControlBase(MessageControl, ChatCommandProcessor, CommandTools): def status_url_clicked(self, widget, url): helpers.launch_browser_mailer('url', url) + def setup_seclabel(self, combo): + self.seclabel_combo = combo + self.seclabel_combo.hide() + self.seclabel_combo.set_no_show_all(True) + lb = gtk.ListStore(str) + self.seclabel_combo.set_model(lb) + cell = gtk.CellRendererText() + cell.set_property('xpad', 5) # padding for status text + self.seclabel_combo.pack_start(cell, True) + # text to show is in in first column of liststore + self.seclabel_combo.add_attribute(cell, 'text', 0) + if gajim.connections[self.account].seclabel_supported: + gajim.connections[self.account].seclabel_catalogue(self.contact.jid, self.on_seclabels_ready) + + def on_seclabels_ready(self): + lb = self.seclabel_combo.get_model() + lb.clear() + for label in gajim.connections[self.account].seclabel_catalogues[self.contact.jid][2]: + lb.append([label]) + self.seclabel_combo.set_active(0) + self.seclabel_combo.set_no_show_all(False) + self.seclabel_combo.show_all() + def __init__(self, type_id, parent_win, widget_name, contact, acct, resource=None): # Undo needs this variable to know if space has been pressed. @@ -721,6 +744,16 @@ class ChatControlBase(MessageControl, ChatCommandProcessor, CommandTools): self.drag_entered_conv = True self.conv_textview.tv.set_editable(True) + def get_seclabel(self): + label = None + if self.seclabel_combo is not None: + idx = self.seclabel_combo.get_active() + if idx != -1: + cat = gajim.connections[self.account].seclabel_catalogues[self.contact.jid] + lname = cat[2][idx] + label = cat[1][lname] + return label + def send_message(self, message, keyID='', type_='chat', chatstate=None, msg_id=None, composing_xep=None, resource=None, xhtml=None, callback=None, callback_args=[], process_commands=True): @@ -733,9 +766,11 @@ class ChatControlBase(MessageControl, ChatCommandProcessor, CommandTools): if process_commands and self.process_as_command(message): return + label = self.get_seclabel() MessageControl.send_message(self, message, keyID, type_=type_, chatstate=chatstate, msg_id=msg_id, composing_xep=composing_xep, resource=resource, user_nick=self.user_nick, xhtml=xhtml, + label=label, callback=callback, callback_args=callback_args) # Record message history @@ -769,7 +804,7 @@ class ChatControlBase(MessageControl, ChatCommandProcessor, CommandTools): other_tags_for_name=[], other_tags_for_time=[], other_tags_for_text=[], count_as_new=True, subject=None, old_kind=None, xhtml=None, simple=False, xep0184_id=None, - graphics=True): + graphics=True, displaymarking=None): """ Print 'chat' type messages """ @@ -781,7 +816,8 @@ class ChatControlBase(MessageControl, ChatCommandProcessor, CommandTools): end = True textview.print_conversation_line(text, jid, kind, name, tim, other_tags_for_name, other_tags_for_time, other_tags_for_text, - subject, old_kind, xhtml, simple=simple, graphics=graphics) + subject, old_kind, xhtml, simple=simple, graphics=graphics, + displaymarking=displaymarking) if xep0184_id is not None: textview.show_xep0184_warning(xep0184_id) @@ -1429,6 +1465,7 @@ class ChatControl(ChatControlBase): session = gajim.connections[self.account].find_controlless_session( self.contact.jid, resource) + self.setup_seclabel(self.xml.get_object('label_selector')) if session: session.control = self self.session = session @@ -2048,20 +2085,23 @@ class ChatControl(ChatControlBase): gobject.source_remove(self.possible_inactive_timeout_id) self._schedule_activity_timers() - def _on_sent(id_, contact, message, encrypted, xhtml): + def _on_sent(id_, contact, message, encrypted, xhtml, label): if contact.supports(NS_RECEIPTS) and gajim.config.get_per('accounts', self.account, 'request_receipt'): xep0184_id = id_ else: xep0184_id = None - + if label: + displaymarking = label.getTag('displaymarking') + else: + displaymarking = None self.print_conversation(message, self.contact.jid, encrypted=encrypted, - xep0184_id=xep0184_id, xhtml=xhtml) + xep0184_id=xep0184_id, xhtml=xhtml, displaymarking=displaymarking) ChatControlBase.send_message(self, message, keyID, type_='chat', chatstate=chatstate_to_send, composing_xep=composing_xep, xhtml=xhtml, callback=_on_sent, - callback_args=[contact, message, encrypted, xhtml], + callback_args=[contact, message, encrypted, xhtml, self.get_seclabel()], process_commands=process_commands) def check_for_possible_paused_chatstate(self, arg): @@ -2150,7 +2190,8 @@ class ChatControl(ChatControlBase): self.session.is_loggable(), self.session and self.session.verified_identity) def print_conversation(self, text, frm='', tim=None, encrypted=False, - subject=None, xhtml=None, simple=False, xep0184_id=None): + subject=None, xhtml=None, simple=False, xep0184_id=None, + displaymarking=None): """ Print a line in the conversation @@ -2213,7 +2254,7 @@ class ChatControl(ChatControlBase): xhtml = '<body xmlns="%s">%s</body>' % (NS_XHTML, xhtml) ChatControlBase.print_conversation_line(self, text, kind, name, tim, subject=subject, old_kind=self.old_msg_kind, xhtml=xhtml, - simple=simple, xep0184_id=xep0184_id) + simple=simple, xep0184_id=xep0184_id, displaymarking=displaymarking) if text.startswith('/me ') or text.startswith('/me\n'): self.old_msg_kind = None else: @@ -2660,8 +2701,12 @@ class ChatControl(ChatControlBase): kind = 'info' else: kind = 'print_queue' + dm = None + if len(data) > 10: + dm = data[10] self.print_conversation(data[0], kind, tim = data[3], - encrypted = data[4], subject = data[1], xhtml = data[7]) + encrypted = data[4], subject = data[1], xhtml = data[7], + displaymarking=dm) if len(data) > 6 and isinstance(data[6], int): message_ids.append(data[6]) diff --git a/src/common/connection.py b/src/common/connection.py index d9dbb961d2755997d6a079744cd7ca105b2805e9..336516fdfc587dd847965f6a3476341f264b2472 100644 --- a/src/common/connection.py +++ b/src/common/connection.py @@ -135,6 +135,9 @@ class CommonConnection: self.blocked_groups = [] self.blocked_all = False + self.seclabel_supported = False + self.seclabel_catalogues = {} + self.pep_supported = False self.pep = {} # Do we continue connection when we get roster (send presence,get vcard..) @@ -239,7 +242,7 @@ class CommonConnection: def _prepare_message(self, jid, msg, keyID, type_='chat', subject='', chatstate=None, msg_id=None, composing_xep=None, resource=None, user_nick=None, xhtml=None, session=None, forward_from=None, form_node=None, - original_message=None, delayed=None, callback=None): + label=None, original_message=None, delayed=None, callback=None): if not self.connection or self.connected < 2: return 1 try: @@ -287,14 +290,14 @@ class CommonConnection: else: self._message_encrypted_cb(output, type_, msg, msgtxt, original_message, fjid, resource, jid, xhtml, - subject, chatstate, composing_xep, forward_from, + subject, chatstate, composing_xep, label, forward_from, delayed, session, form_node, user_nick, keyID, callback) self.dispatch('GPG_ALWAYS_TRUST', _on_always_trust) else: self._message_encrypted_cb(output, type_, msg, msgtxt, original_message, fjid, resource, jid, xhtml, subject, - chatstate, composing_xep, forward_from, delayed, session, + chatstate, composing_xep, label, forward_from, delayed, session, form_node, user_nick, keyID, callback) gajim.thread_interface(encrypt_thread, [msg, keyID, False], _on_encrypted, []) @@ -302,15 +305,15 @@ class CommonConnection: self._message_encrypted_cb(('', error), type_, msg, msgtxt, original_message, fjid, resource, jid, xhtml, subject, chatstate, - composing_xep, forward_from, delayed, session, form_node, user_nick, + composing_xep, label, forward_from, delayed, session, form_node, user_nick, keyID, callback) self._on_continue_message(type_, msg, msgtxt, original_message, fjid, resource, jid, xhtml, subject, msgenc, keyID, chatstate, composing_xep, - forward_from, delayed, session, form_node, user_nick, callback) + label, forward_from, delayed, session, form_node, user_nick, callback) def _message_encrypted_cb(self, output, type_, msg, msgtxt, original_message, - fjid, resource, jid, xhtml, subject, chatstate, composing_xep, forward_from, + fjid, resource, jid, xhtml, subject, chatstate, composing_xep, label, forward_from, delayed, session, form_node, user_nick, keyID, callback): msgenc, error = output @@ -323,7 +326,7 @@ class CommonConnection: ' (' + msgtxt + ')' self._on_continue_message(type_, msg, msgtxt, original_message, fjid, resource, jid, xhtml, subject, msgenc, keyID, chatstate, - composing_xep, forward_from, delayed, session, form_node, user_nick, + composing_xep, label, forward_from, delayed, session, form_node, user_nick, callback) return # Encryption failed, do not send message @@ -332,6 +335,7 @@ class CommonConnection: def _on_continue_message(self, type_, msg, msgtxt, original_message, fjid, resource, jid, xhtml, subject, msgenc, keyID, chatstate, composing_xep, + label, forward_from, delayed, session, form_node, user_nick, callback): if type_ == 'chat': msg_iq = common.xmpp.Message(to=fjid, body=msgtxt, typ=type_, @@ -348,6 +352,8 @@ class CommonConnection: if form_node: msg_iq.addChild(node=form_node) + if label: + msg_iq.addChild(node=label) # XEP-0172: user_nickname if user_nick: @@ -1605,7 +1611,7 @@ class Connection(CommonConnection, ConnectionHandlers): def send_message(self, jid, msg, keyID, type_='chat', subject='', chatstate=None, msg_id=None, composing_xep=None, resource=None, - user_nick=None, xhtml=None, session=None, forward_from=None, form_node=None, + user_nick=None, xhtml=None, label=None, session=None, forward_from=None, form_node=None, original_message=None, delayed=None, callback=None, callback_args=[], now=False): @@ -1622,7 +1628,8 @@ class Connection(CommonConnection, ConnectionHandlers): self._prepare_message(jid, msg, keyID, type_=type_, subject=subject, chatstate=chatstate, msg_id=msg_id, composing_xep=composing_xep, - resource=resource, user_nick=user_nick, xhtml=xhtml, session=session, + resource=resource, user_nick=user_nick, xhtml=xhtml, label=label, + session=session, forward_from=forward_from, form_node=form_node, original_message=original_message, delayed=delayed, callback=cb) @@ -1830,6 +1837,15 @@ class Connection(CommonConnection, ConnectionHandlers): iq2.addChild(name='gajim', namespace='gajim:prefs') self.connection.send(iq) + def seclabel_catalogue(self, to, callback): + if not gajim.account_is_connected(self.name): + return + self.seclabel_catalogue_request(to, callback) + iq = common.xmpp.Iq(typ='get') + iq2 = iq.addChild(name='catalog', namespace=common.xmpp.NS_SECLABEL_CATALOG) + iq2.setAttr('to', to) + self.connection.send(iq) + def _request_bookmarks_xml(self): if not gajim.account_is_connected(self.name): return @@ -2041,13 +2057,15 @@ class Connection(CommonConnection, ConnectionHandlers): t.setTagData('password', password) self.connection.send(p) - def send_gc_message(self, jid, msg, xhtml = None): + def send_gc_message(self, jid, msg, xhtml = None, label = None): if not gajim.account_is_connected(self.name): return if not xhtml and gajim.config.get('rst_formatting_outgoing_messages'): from common.rst_xhtml_generator import create_xhtml xhtml = create_xhtml(msg) msg_iq = common.xmpp.Message(jid, msg, typ = 'groupchat', xhtml = xhtml) + if label is not None: + msg_iq.addChild(node = label) self.connection.send(msg_iq) self.dispatch('MSGSENT', (jid, msg)) diff --git a/src/common/connection_handlers.py b/src/common/connection_handlers.py index d60e14b5ef0e1bad4ba6d2b5694469c699d8a029..a42f17cbc6517289eaf37f4351e60a3356520d82 100644 --- a/src/common/connection_handlers.py +++ b/src/common/connection_handlers.py @@ -341,6 +341,8 @@ class ConnectionDisco: if features.__contains__(common.xmpp.NS_GMAILNOTIFY): gajim.gmail_domains.append(jid) self.request_gmail_notifications() + if features.__contains__(common.xmpp.NS_SECLABEL): + self.seclabel_supported = True for identity in identities: if identity['category'] == 'pubsub' and identity.get('type') == \ 'pep': @@ -1071,6 +1073,33 @@ ConnectionCaps, ConnectionHandlersBase, ConnectionJingle): annotation = note.getData() self.annotations[jid] = annotation + def _SecLabelCB(self, con, iq_obj): + """ + Security Label callback, used for catalogues. + """ + log.debug('SecLabelCB') + query = iq_obj.getTag('catalog') + to = query.getAttr('to') + items = query.getTags('securitylabel') + labels = {} + ll = [] + for item in items: + label = item.getTag('displaymarking').getData() + labels[label] = item + ll.append(label) + if to not in self.seclabel_catalogues: + self.seclabel_catalogues[to] = [[], None, None] + self.seclabel_catalogues[to][1] = labels + self.seclabel_catalogues[to][2] = ll + for callback in self.seclabel_catalogues[to][0]: + callback() + self.seclabel_catalogues[to][0] = [] + + def seclabel_catalogue_request(self, to, callback): + if to not in self.seclabel_catalogues: + self.seclabel_catalogues[to] = [[], None, None] + self.seclabel_catalogues[to][0].append(callback) + def _parse_bookmarks(self, storage, storage_type): """ storage_type can be 'pubsub' or 'xml' to tell from where we got bookmarks @@ -1618,12 +1647,15 @@ ConnectionCaps, ConnectionHandlersBase, ConnectionJingle): self.dispatch('GC_CONFIG_CHANGE', (jid, statusCode)) return - # Ignore message from room in which we are not + displaymarking = None + seclabel = msg.getTag('securitylabel') + if seclabel and seclabel.getNamespace() == common.xmpp.NS_SECLABEL: + displaymarking = seclabel.getTag('displaymarking') # Ignore message from room in which we are not if jid not in self.last_history_time: return self.dispatch('GC_MSG', (frm, msgtxt, tim, has_timestamp, msg.getXHTML(), - statusCode)) + statusCode, displaymarking)) tim_int = int(float(mktime(tim))) if gajim.config.should_log(self.name, jid) and not \ @@ -2302,6 +2334,8 @@ ConnectionCaps, ConnectionHandlersBase, ConnectionJingle): common.xmpp.NS_MUC_ADMIN) con.RegisterHandler('iq', self._PrivateCB, 'result', common.xmpp.NS_PRIVATE) + con.RegisterHandler('iq', self._SecLabelCB, 'result', + common.xmpp.NS_SECLABEL_CATALOG) con.RegisterHandler('iq', self._HttpAuthCB, 'get', common.xmpp.NS_HTTP_AUTH) con.RegisterHandler('iq', self._CommandExecuteCB, 'set', diff --git a/src/common/gajim.py b/src/common/gajim.py index 214f5e369d651324d40ae12a785fe42ac0496c31..a44029de5016bc66fe60c73eb00f27e7eabf2232 100644 --- a/src/common/gajim.py +++ b/src/common/gajim.py @@ -193,7 +193,7 @@ gajim_common_features = [xmpp.NS_BYTESTREAM, xmpp.NS_SI, xmpp.NS_FILE, 'jabber:iq:gateway', xmpp.NS_LAST, xmpp.NS_PRIVACY, xmpp.NS_PRIVATE, xmpp.NS_REGISTER, xmpp.NS_VERSION, xmpp.NS_DATA, xmpp.NS_ENCRYPTED, 'msglog', 'sslc2s', 'stringprep', xmpp.NS_PING, xmpp.NS_TIME_REVISED, xmpp.NS_SSN, - xmpp.NS_MOOD, xmpp.NS_ACTIVITY, xmpp.NS_NICK, xmpp.NS_ROSTERX] + xmpp.NS_MOOD, xmpp.NS_ACTIVITY, xmpp.NS_NICK, xmpp.NS_ROSTERX, xmpp.NS_SECLABEL] # Optional features gajim supports per account gajim_optional_features = {} diff --git a/src/common/xmpp/protocol.py b/src/common/xmpp/protocol.py index a14eb8dca7d1972419040d0d1c75ffbcf6cd39bb..01dd49d86b36ddbaf7449700c37d24286fe48cac 100644 --- a/src/common/xmpp/protocol.py +++ b/src/common/xmpp/protocol.py @@ -100,6 +100,8 @@ NS_ROSTERX ='http://jabber.org/protocol/rosterx' NS_ROSTER_VER ='urn:xmpp:features:rosterver' # XEP-0273 NS_RPC ='jabber:iq:rpc' # XEP-0009 NS_SASL ='urn:ietf:params:xml:ns:xmpp-sasl' +NS_SECLABEL ='urn:xmpp:sec-label:0' +NS_SECLABEL_CATALOG ='urn:xmpp:sec-label:catalog:0' NS_SEARCH ='jabber:iq:search' NS_SERVER ='jabber:server' NS_SESSION ='urn:ietf:params:xml:ns:xmpp-session' diff --git a/src/conversation_textview.py b/src/conversation_textview.py index a96c41b677d6826ddc1c37dd29eda8da76e14c4c..46e60dfeef9c183cebcd3626c09b142bce98fac2 100644 --- a/src/conversation_textview.py +++ b/src/conversation_textview.py @@ -326,6 +326,7 @@ class ConversationTextview(gobject.GObject): tag.set_property('underline', pango.UNDERLINE_SINGLE) buffer_.create_tag('focus-out-line', justification = gtk.JUSTIFY_CENTER) + self.displaymarking_tags = {} tag = buffer_.create_tag('xep0184-warning') @@ -1173,7 +1174,7 @@ class ConversationTextview(gobject.GObject): def print_conversation_line(self, text, jid, kind, name, tim, other_tags_for_name=[], other_tags_for_time=[], other_tags_for_text=[], subject=None, old_kind=None, xhtml=None, - simple=False, graphics=True): + simple=False, graphics=True, displaymarking=None): """ Print 'chat' type messages """ @@ -1238,6 +1239,9 @@ class ConversationTextview(gobject.GObject): tim_format = self.get_time_to_show(tim) buffer_.insert_with_tags_by_name(end_iter, tim_format + '\n', 'time_sometimes') + # If there's a displaymarking, print it here. + if displaymarking: + self.print_displaymarking(displaymarking) # kind = info, we print things as if it was a status: same color, ... if kind in ('error', 'info'): kind = 'status' @@ -1309,6 +1313,19 @@ class ConversationTextview(gobject.GObject): elif text.startswith('/me ') or text.startswith('/me\n'): return kind + def print_displaymarking(self, displaymarking): + bgcolor = displaymarking.getAttr('bgcolor') or '#FFF' + fgcolor = displaymarking.getAttr('fgcolor') or '#000' + text = displaymarking.getData() + if text: + buffer_ = self.tv.get_buffer() + end_iter = buffer_.get_end_iter() + tag = self.displaymarking_tags.setdefault(bgcolor + '/' + fgcolor, + buffer_.create_tag(None, background=bgcolor, foreground=fgcolor)) + buffer_.insert_with_tags(end_iter, '[' + text + ']', tag) + end_iter = buffer_.get_end_iter() + buffer_.insert_with_tags(end_iter, ' ') + def print_name(self, name, kind, other_tags_for_name): if name: buffer_ = self.tv.get_buffer() diff --git a/src/groupchat_control.py b/src/groupchat_control.py index bf78159db2be80c090a038c24b1b2fe60413dd9c..b604c8fba3eb06798f64fa42bb9c28baeab7959c 100644 --- a/src/groupchat_control.py +++ b/src/groupchat_control.py @@ -385,6 +385,8 @@ class GroupchatControl(ChatControlBase): column.set_visible(False) self.list_treeview.set_expander_column(column) + self.setup_seclabel(self.xml.get_object('label_selector')) + gajim.gc_connected[self.account][self.room_jid] = False # disable win, we are not connected yet ChatControlBase.got_disconnected(self) @@ -785,30 +787,30 @@ class GroupchatControl(ChatControlBase): menu.destroy() def on_message(self, nick, msg, tim, has_timestamp=False, xhtml=None, - status_code=[]): + status_code=[], displaymarking=None): if '100' in status_code: # Room is not anonymous self.is_anonymous = False if not nick: # message from server - self.print_conversation(msg, tim=tim, xhtml=xhtml) + self.print_conversation(msg, tim=tim, xhtml=xhtml, displaymarking=displaymarking) else: # message from someone if has_timestamp: # don't print xhtml if it's an old message. # Like that xhtml messages are grayed too. - self.print_old_conversation(msg, nick, tim, None) + self.print_old_conversation(msg, nick, tim, None, displaymarking=displaymarking) else: - self.print_conversation(msg, nick, tim, xhtml) + self.print_conversation(msg, nick, tim, xhtml, displaymarking=displaymarking) def on_private_message(self, nick, msg, tim, xhtml, session, msg_id=None, - encrypted=False): + encrypted=False, displaymarking=None): # Do we have a queue? fjid = self.room_jid + '/' + nick no_queue = len(gajim.events.get_events(self.account, fjid)) == 0 event = gajim.events.create_event('pm', (msg, '', 'incoming', tim, - encrypted, '', msg_id, xhtml, session)) + encrypted, '', msg_id, xhtml, session, displaymarking)) gajim.events.add_event(self.account, fjid, event) autopopup = gajim.config.get('autopopup') @@ -851,7 +853,8 @@ class GroupchatControl(ChatControlBase): role_iter = model.iter_next(role_iter) return None - def print_old_conversation(self, text, contact='', tim=None, xhtml = None): + def print_old_conversation(self, text, contact='', tim=None, xhtml = None, + displaymarking=None): if isinstance(text, str): text = unicode(text, 'utf-8') if contact: @@ -867,10 +870,11 @@ class GroupchatControl(ChatControlBase): small_attr = [] ChatControlBase.print_conversation_line(self, text, kind, contact, tim, small_attr, small_attr + ['restored_message'], - small_attr + ['restored_message'], count_as_new=False, xhtml=xhtml) + small_attr + ['restored_message'], count_as_new=False, xhtml=xhtml, + displaymarking=displaymarking) def print_conversation(self, text, contact='', tim=None, xhtml=None, - graphics=True): + graphics=True, displaymarking=None): """ Print a line in the conversation @@ -937,7 +941,7 @@ class GroupchatControl(ChatControlBase): ChatControlBase.print_conversation_line(self, text, kind, contact, tim, other_tags_for_name, [], other_tags_for_text, xhtml=xhtml, - graphics=graphics) + graphics=graphics, displaymarking=displaymarking) def get_nb_unread(self): type_events = ['printed_marked_gc_msg'] @@ -1588,12 +1592,13 @@ class GroupchatControl(ChatControlBase): if not message: return + label = self.get_seclabel() if message != '' or message != '\n': self.save_sent_message(message) # Send the message gajim.connections[self.account].send_gc_message(self.room_jid, - message, xhtml=xhtml) + message, xhtml=xhtml, label=label) self.msg_textview.get_buffer().set_text('') self.msg_textview.grab_focus() diff --git a/src/gui_interface.py b/src/gui_interface.py index 769b14deddc9e5ab42d8e65206b7f4ab3156935b..369c27637e9717426ba2da9b142829940b9a96d0 100644 --- a/src/gui_interface.py +++ b/src/gui_interface.py @@ -956,7 +956,7 @@ class Interface: def handle_event_gc_msg(self, account, array): # ('GC_MSG', account, (jid, msg, time, has_timestamp, htmlmsg, - # [status_codes])) + # [status_codes], displaymarking)) jids = array[0].split('/', 1) room_jid = jids[0] @@ -980,7 +980,7 @@ class Interface: # message from someone nick = jids[1] - gc_control.on_message(nick, msg, array[2], array[3], xhtml, array[5]) + gc_control.on_message(nick, msg, array[2], array[3], xhtml, array[5], displaymarking=array[6]) if self.remote_ctrl: highlight = gc_control.needs_visual_notification(msg) diff --git a/src/message_control.py b/src/message_control.py index 5dfce76eb8a038a4713ec3cba2955469f00de01c..d09cb446be1efa1328d01f1505244ef69153bb92 100644 --- a/src/message_control.py +++ b/src/message_control.py @@ -208,7 +208,7 @@ class MessageControl: def send_message(self, message, keyID='', type_='chat', chatstate=None, msg_id=None, composing_xep=None, resource=None, user_nick=None, - xhtml=None, callback=None, callback_args=[]): + xhtml=None, label=None, callback=None, callback_args=[]): # Send the given message to the active tab. # Doesn't return None if error jid = self.contact.jid @@ -241,5 +241,5 @@ class MessageControl: conn.send_message(jid, message, keyID, type_=type_, chatstate=chatstate, msg_id=msg_id, composing_xep=composing_xep, resource=self.resource, user_nick=user_nick, session=self.session, - original_message=original_message, xhtml=xhtml, callback=callback, + original_message=original_message, xhtml=xhtml, label=label, callback=callback, callback_args=callback_args) diff --git a/src/session.py b/src/session.py index 25889e1f0b8fa12757ac38b9c6432c1e75b9b028..4af87c20b78cf6f0453e7c8e8e88bd790e631f04 100644 --- a/src/session.py +++ b/src/session.py @@ -95,6 +95,8 @@ class ChatControlSession(stanza_session.EncryptedStanzaSession): self.resource = resource if self.control and self.control.resource: self.control.change_resource(self.resource) + seclabel = None + displaymarking = None if not msg_type or msg_type not in ('chat', 'groupchat', 'error'): msg_type = 'normal' @@ -113,7 +115,9 @@ class ChatControlSession(stanza_session.EncryptedStanzaSession): break composing_xep, chatstate = self.get_chatstate(msg, msgtxt) - + seclabel = msg.getTag('securitylabel') + if seclabel and seclabel.getNamespace() == common.xmpp.NS_SECLABEL: + displaymarking = seclabel.getTag('displaymarking') xhtml = msg.getXHTML() if msg_type == 'chat': @@ -236,15 +240,15 @@ class ChatControlSession(stanza_session.EncryptedStanzaSession): if self.control: # print if a control is open self.control.print_conversation(msgtxt, tim=tim, xhtml=xhtml, - encrypted=encrypted) + encrypted=encrypted, displaymarking=displaymarking) else: # otherwise pass it off to the control to be queued groupchat_control.on_private_message(nickname, msgtxt, tim, - xhtml, self, msg_id=msg_id, encrypted=encrypted) + xhtml, self, msg_id=msg_id, encrypted=encrypted, displaymarking=displaymarking) else: self.roster_message(jid, msgtxt, tim, encrypted, msg_type, subject, resource, msg_id, user_nick, advanced_notif_num, - xhtml=xhtml, form_node=form_node) + xhtml=xhtml, form_node=form_node, displaymarking=displaymarking) nickname = gajim.get_name_from_jid(self.conn.name, jid) @@ -270,7 +274,7 @@ class ChatControlSession(stanza_session.EncryptedStanzaSession): def roster_message(self, jid, msg, tim, encrypted=False, msg_type='', subject=None, resource='', msg_id=None, user_nick='', - advanced_notif_num=None, xhtml=None, form_node=None): + advanced_notif_num=None, xhtml=None, form_node=None, displaymarking=None): """ Display the message or show notification in the roster """ @@ -334,7 +338,7 @@ class ChatControlSession(stanza_session.EncryptedStanzaSession): if msg_type == 'normal' and popup: # it's single message to be autopopuped dialogs.SingleMessageWindow(self.conn.name, contact.jid, action='receive', from_whom=jid, subject=subject, message=msg, - resource=resource, session=self, form_node=form_node) + resource=resource, session=self, form_node=form_node, displaymarking=displaymarking) return # We print if window is opened and it's not a single message @@ -345,7 +349,7 @@ class ChatControlSession(stanza_session.EncryptedStanzaSession): typ = 'error' self.control.print_conversation(msg, typ, tim=tim, encrypted=encrypted, - subject=subject, xhtml=xhtml) + subject=subject, xhtml=xhtml, displaymarking=displaymarking) if msg_id: gajim.logger.set_read_messages([msg_id]) @@ -366,7 +370,7 @@ class ChatControlSession(stanza_session.EncryptedStanzaSession): contact) event = gajim.events.create_event(type_, (msg, subject, msg_type, tim, - encrypted, resource, msg_id, xhtml, self, form_node), + encrypted, resource, msg_id, xhtml, self, form_node, displaymarking), show_in_roster=show_in_roster, show_in_systray=show_in_systray) gajim.events.add_event(self.conn.name, fjid, event)