[omemo] Polish GUI

- Port Fingerprint dialog from openpgp plugin
- Add an InfoBar for errors and informations
- Add tooltip for device clearing
- Remove fingerprints tab from plugin config dialog
This commit is contained in:
Daniel Brötzmann
2018-10-12 20:59:08 +02:00
committed by Philipp Hörist
parent 6a2114238c
commit 80a7a5db57
9 changed files with 868 additions and 883 deletions

View File

@@ -1,5 +1,5 @@
<?xml version="1.0" encoding="UTF-8"?>
<!-- Generated with glade 3.20.2 -->
<!-- Generated with glade 3.22.1 -->
<interface>
<requires lib="gtk+" version="3.20"/>
<object class="GtkListStore" id="account_store">
@@ -20,51 +20,116 @@
<column type="gchararray"/>
</columns>
</object>
<object class="GtkListStore" id="fingerprint_store">
<columns>
<!-- column-name id -->
<column type="gint"/>
<!-- column-name screenname -->
<column type="gchararray"/>
<!-- column-name trust -->
<column type="gchararray"/>
<!-- column-name fingerprint -->
<column type="gchararray"/>
<!-- column-name deviceid -->
<column type="gint"/>
</columns>
</object>
<object class="GtkNotebook" id="notebook1">
<property name="visible">True</property>
<property name="can_focus">True</property>
<child>
<object class="GtkVBox" id="vbox1">
<object class="GtkBox">
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="border_width">12</property>
<property name="spacing">10</property>
<property name="border_width">18</property>
<property name="orientation">vertical</property>
<property name="spacing">6</property>
<child>
<object class="GtkHBox" id="hbox2">
<property name="visible">True</property>
<object class="GtkInfoBar" id="qrinfo">
<property name="can_focus">False</property>
<property name="spacing">5</property>
<child>
<object class="GtkLabel" id="label4">
<property name="visible">True</property>
<property name="margin_bottom">6</property>
<property name="revealed">False</property>
<child internal-child="action_area">
<object class="GtkButtonBox">
<property name="can_focus">False</property>
<property name="label" translatable="yes" comments="label for account selector">&lt;b&gt;Account:&lt;/b&gt;</property>
<property name="use_markup">True</property>
<property name="spacing">6</property>
<property name="layout_style">end</property>
<child>
<placeholder/>
</child>
</object>
<packing>
<property name="expand">False</property>
<property name="fill">True</property>
<property name="fill">False</property>
<property name="position">0</property>
</packing>
</child>
<child internal-child="content_area">
<object class="GtkBox">
<property name="can_focus">False</property>
<property name="halign">start</property>
<property name="spacing">6</property>
<child>
<object class="GtkLabel">
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="no_show_all">True</property>
<property name="halign">start</property>
<property name="label" translatable="yes">For verification via QR-Code you have to install</property>
<property name="wrap">True</property>
</object>
<packing>
<property name="expand">False</property>
<property name="fill">True</property>
<property name="position">0</property>
</packing>
</child>
<child>
<object class="GtkLabel">
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="halign">start</property>
<property name="label" translatable="yes">python-qrcode</property>
<property name="selectable">True</property>
</object>
<packing>
<property name="expand">True</property>
<property name="fill">True</property>
<property name="position">1</property>
</packing>
</child>
</object>
<packing>
<property name="expand">False</property>
<property name="fill">False</property>
<property name="position">0</property>
</packing>
</child>
</object>
<packing>
<property name="expand">False</property>
<property name="fill">True</property>
<property name="position">0</property>
</packing>
</child>
<child>
<object class="GtkGrid">
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="halign">start</property>
<property name="row_spacing">6</property>
<property name="column_spacing">12</property>
<child>
<object class="GtkLabel">
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="halign">end</property>
<property name="label" translatable="yes" comments="label for account selector">Acc_ount</property>
<property name="use_underline">True</property>
<property name="mnemonic_widget">account_combobox</property>
<style>
<class name="bold"/>
<class name="dim-label"/>
</style>
</object>
<packing>
<property name="left_attach">0</property>
<property name="top_attach">0</property>
</packing>
</child>
<child>
<object class="GtkComboBox" id="account_combobox">
<property name="width_request">200</property>
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="has_focus">True</property>
<property name="halign">start</property>
<property name="model">account_store</property>
<signal name="changed" handler="account_combobox_changed_cb" swapped="no"/>
<child>
@@ -75,52 +140,129 @@
</child>
</object>
<packing>
<property name="expand">False</property>
<property name="fill">True</property>
<property name="position">1</property>
<property name="left_attach">1</property>
<property name="top_attach">0</property>
</packing>
</child>
</object>
<packing>
<property name="expand">False</property>
<property name="fill">True</property>
<property name="position">0</property>
</packing>
</child>
<child>
<object class="GtkHBox" id="hbox1">
<property name="visible">True</property>
<property name="can_focus">False</property>
<child>
<object class="GtkLabel" id="fingerprint_label_desc">
<property name="width_request">110</property>
<object class="GtkLabel">
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="label" translatable="yes" comments="Descriptive label">Own Fingerprint:</property>
<property name="xalign">0</property>
<attributes>
<attribute name="weight" value="bold"/>
</attributes>
<property name="halign">end</property>
<property name="margin_top">6</property>
<property name="label" translatable="yes" comments="Descriptive label">Own _Fingerprint</property>
<property name="use_underline">True</property>
<property name="mnemonic_widget">fingerprint_label</property>
<style>
<class name="bold"/>
<class name="dim-label"/>
</style>
</object>
<packing>
<property name="expand">False</property>
<property name="fill">True</property>
<property name="position">0</property>
<property name="left_attach">0</property>
<property name="top_attach">1</property>
</packing>
</child>
<child>
<object class="GtkLabel" id="fingerprint_label">
<property name="width_request">200</property>
<property name="height_request">30</property>
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="halign">start</property>
<property name="margin_top">6</property>
<property name="use_markup">True</property>
<property name="selectable">True</property>
</object>
<packing>
<property name="expand">False</property>
<property name="fill">True</property>
<property name="position">1</property>
<property name="left_attach">1</property>
<property name="top_attach">1</property>
</packing>
</child>
<child>
<object class="GtkLabel">
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="halign">end</property>
<property name="label" translatable="yes">Own _Device ID</property>
<property name="use_underline">True</property>
<property name="mnemonic_widget">ID</property>
<style>
<class name="bold"/>
<class name="dim-label"/>
</style>
</object>
<packing>
<property name="left_attach">0</property>
<property name="top_attach">2</property>
</packing>
</child>
<child>
<object class="GtkLabel" id="ID">
<property name="width_request">200</property>
<property name="height_request">30</property>
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="halign">start</property>
<property name="selectable">True</property>
<property name="xalign">0</property>
</object>
<packing>
<property name="left_attach">1</property>
<property name="top_attach">2</property>
</packing>
</child>
<child>
<object class="GtkImage" id="qrcode">
<property name="can_focus">False</property>
<property name="no_show_all">True</property>
<property name="tooltip_text" translatable="yes">Scan this QR-Code with your mobile device for easy verification</property>
<property name="halign">start</property>
<property name="margin_top">6</property>
<property name="margin_bottom">6</property>
<property name="stock">gtk-missing-image</property>
<property name="icon_size">1</property>
</object>
<packing>
<property name="left_attach">1</property>
<property name="top_attach">3</property>
</packing>
</child>
<child>
<object class="GtkImage">
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="tooltip_text">OMEMO</property>
<property name="valign">start</property>
<property name="pixbuf">omemo.png</property>
</object>
<packing>
<property name="left_attach">0</property>
<property name="top_attach">3</property>
</packing>
</child>
<child>
<object class="GtkLabel">
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="halign">start</property>
<property name="label" translatable="yes">Note: Fingerprints of your contacts are managed in the message window.</property>
<property name="wrap">True</property>
<attributes>
<attribute name="style" value="italic"/>
</attributes>
<style>
<class name="dim-label"/>
</style>
</object>
<packing>
<property name="left_attach">1</property>
<property name="top_attach">4</property>
</packing>
</child>
<child>
<placeholder/>
</child>
</object>
<packing>
<property name="expand">False</property>
@@ -128,81 +270,10 @@
<property name="position">1</property>
</packing>
</child>
<child>
<object class="GtkHBox" id="hbox5">
<property name="visible">True</property>
<property name="can_focus">False</property>
<child>
<object class="GtkLabel" id="OwnIDLabel">
<property name="width_request">110</property>
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="label" translatable="yes">Own Device ID:</property>
<property name="xalign">0</property>
<attributes>
<attribute name="weight" value="bold"/>
</attributes>
</object>
<packing>
<property name="expand">False</property>
<property name="fill">False</property>
<property name="position">0</property>
</packing>
</child>
<child>
<object class="GtkLabel" id="ID">
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="xalign">0</property>
</object>
<packing>
<property name="expand">False</property>
<property name="fill">True</property>
<property name="position">1</property>
</packing>
</child>
</object>
<packing>
<property name="expand">False</property>
<property name="fill">False</property>
<property name="position">2</property>
</packing>
</child>
<child>
<object class="GtkImage" id="qrcode">
<property name="can_focus">False</property>
<property name="no_show_all">True</property>
<property name="halign">start</property>
<property name="stock">gtk-missing-image</property>
<property name="icon_size">1</property>
</object>
<packing>
<property name="expand">True</property>
<property name="fill">True</property>
<property name="position">3</property>
</packing>
</child>
<child>
<object class="GtkLabel" id="qrinfo">
<property name="can_focus">False</property>
<property name="no_show_all">True</property>
<property name="label" translatable="yes">For Verification QRCode please install python-qrcode</property>
<property name="xalign">0</property>
<attributes>
<attribute name="weight" value="bold"/>
<attribute name="foreground" value="#ffff00000000"/>
</attributes>
</object>
<packing>
<property name="expand">True</property>
<property name="fill">True</property>
<property name="position">4</property>
</packing>
</child>
</object>
</child>
<child type="tab">
<object class="GtkLabel" id="label1">
<object class="GtkLabel">
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="label" translatable="yes" comments="tab label">Own Fingerprints</property>
@@ -212,156 +283,27 @@
</packing>
</child>
<child>
<object class="GtkVBox" id="vbox4">
<object class="GtkBox">
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="border_width">12</property>
<property name="spacing">10</property>
<property name="border_width">18</property>
<property name="orientation">vertical</property>
<property name="spacing">6</property>
<child>
<object class="GtkScrolledWindow" id="scrolledwindow1">
<property name="height_request">200</property>
<property name="visible">True</property>
<property name="can_focus">True</property>
<child>
<object class="GtkTreeView" id="fingerprint_view">
<property name="height_request">300</property>
<property name="visible">True</property>
<property name="can_focus">True</property>
<property name="model">fingerprint_store</property>
<property name="search_column">0</property>
<property name="tooltip_column">3</property>
<signal name="button-press-event" handler="fpr_button_pressed_cb" swapped="no"/>
<child internal-child="selection">
<object class="GtkTreeSelection" id="treeview-selection1"/>
</child>
<child>
<object class="GtkTreeViewColumn" id="name_column">
<property name="resizable">True</property>
<property name="title" translatable="yes">Name</property>
<property name="clickable">True</property>
<property name="sort_indicator">True</property>
<property name="sort_column_id">1</property>
<child>
<object class="GtkCellRendererText" id="cellrenderertext2"/>
<attributes>
<attribute name="text">1</attribute>
</attributes>
</child>
</object>
</child>
<child>
<object class="GtkTreeViewColumn" id="trust_column">
<property name="resizable">True</property>
<property name="title" translatable="yes">Trust</property>
<child>
<object class="GtkCellRendererText" id="cellrenderertoggle1"/>
<attributes>
<attribute name="text">2</attribute>
</attributes>
</child>
</object>
</child>
<child>
<object class="GtkTreeViewColumn" id="fingerprint_column">
<property name="resizable">True</property>
<property name="title" translatable="yes">Fingerprint</property>
<child>
<object class="GtkCellRendererText" id="cellrenderertext4"/>
<attributes>
<attribute name="markup">3</attribute>
</attributes>
</child>
</object>
</child>
</object>
</child>
</object>
<packing>
<property name="expand">True</property>
<property name="fill">True</property>
<property name="position">0</property>
</packing>
</child>
<child>
<object class="GtkHBox" id="hbox3">
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="spacing">5</property>
<child>
<object class="GtkButton" id="trust_button">
<property name="label" translatable="yes" comments="button">Trust/Revoke Fingerprint</property>
<property name="width_request">200</property>
<property name="visible">True</property>
<property name="can_focus">True</property>
<property name="receives_default">True</property>
<signal name="clicked" handler="trust_button_clicked_cb" swapped="no"/>
</object>
<packing>
<property name="expand">False</property>
<property name="fill">False</property>
<property name="position">0</property>
</packing>
</child>
<child>
<object class="GtkButton" id="delfprbutton">
<property name="label" translatable="yes">Delete Fingerprint</property>
<property name="width_request">200</property>
<property name="visible">True</property>
<property name="can_focus">True</property>
<property name="receives_default">True</property>
<signal name="clicked" handler="delfpr_button_clicked" swapped="no"/>
</object>
<packing>
<property name="expand">False</property>
<property name="fill">False</property>
<property name="position">1</property>
</packing>
</child>
</object>
<packing>
<property name="expand">False</property>
<property name="fill">False</property>
<property name="position">1</property>
</packing>
</child>
</object>
<packing>
<property name="position">1</property>
</packing>
</child>
<child type="tab">
<object class="GtkLabel" id="label2">
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="label" translatable="yes" comments="tab label">Known Fingerprints</property>
</object>
<packing>
<property name="position">1</property>
<property name="tab_fill">False</property>
</packing>
</child>
<child>
<object class="GtkVBox" id="vbox3">
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="border_width">12</property>
<property name="spacing">10</property>
<child>
<object class="GtkLabel" id="label5">
<object class="GtkLabel">
<property name="height_request">25</property>
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="halign">center</property>
<property name="label" translatable="yes">Published Devices</property>
<property name="xalign">0</property>
<attributes>
<attribute name="style" value="normal"/>
<attribute name="weight" value="bold"/>
</attributes>
<style>
<class name="bold"/>
<class name="dim-label"/>
</style>
</object>
<packing>
<property name="expand">False</property>
<property name="fill">True</property>
<property name="padding">7</property>
<property name="position">0</property>
</packing>
</child>
@@ -369,19 +311,23 @@
<object class="GtkScrolledWindow" id="scrolledwindow2">
<property name="visible">True</property>
<property name="can_focus">True</property>
<property name="halign">center</property>
<property name="hscrollbar_policy">never</property>
<child>
<object class="GtkTreeView" id="deviceid_view">
<property name="width_request">300</property>
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="model">deviceid_store</property>
<property name="search_column">0</property>
<property name="enable_grid_lines">horizontal</property>
<child internal-child="selection">
<object class="GtkTreeSelection" id="treeview-selection2"/>
</child>
<child>
<object class="GtkTreeViewColumn" id="deviceid_column">
<property name="title" translatable="yes">Device ID</property>
<property name="clickable">True</property>
<child>
<object class="GtkCellRendererText" id="cellrenderertext3"/>
<attributes>
@@ -400,18 +346,26 @@
</packing>
</child>
<child>
<object class="GtkHBox" id="hbox4">
<object class="GtkBox">
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="spacing">5</property>
<property name="halign">center</property>
<property name="margin_top">6</property>
<property name="spacing">12</property>
<child>
<object class="GtkButton" id="cleardevice_button">
<property name="label" translatable="yes">Clear Devices</property>
<property name="width_request">160</property>
<property name="label" translatable="yes">_Clear Devices</property>
<property name="visible">True</property>
<property name="can_focus">True</property>
<property name="receives_default">True</property>
<property name="tooltip_text" translatable="yes">This clears your device list from the server.
Clearing the device list helps you to remove unused devices from the encryption process.
It is advised to go online with all of your actively used devices after clearing.</property>
<property name="use_underline">True</property>
<signal name="clicked" handler="cleardevice_button_clicked_cb" swapped="no"/>
<style>
<class name="destructive-action"/>
</style>
</object>
<packing>
<property name="expand">False</property>
@@ -438,40 +392,79 @@
</object>
<packing>
<property name="expand">False</property>
<property name="fill">False</property>
<property name="fill">True</property>
<property name="position">2</property>
</packing>
</child>
</object>
<packing>
<property name="position">2</property>
<property name="position">1</property>
</packing>
</child>
<child type="tab">
<object class="GtkLabel" id="label3">
<object class="GtkLabel">
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="label" translatable="yes">Clear Devices</property>
</object>
<packing>
<property name="position">2</property>
<property name="position">1</property>
<property name="tab_fill">False</property>
</packing>
</child>
<child>
<object class="GtkVBox" id="vbox2">
<object class="GtkBox">
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="border_width">18</property>
<property name="orientation">vertical</property>
<property name="spacing">6</property>
<child>
<object class="GtkLabel" id="label6">
<property name="height_request">30</property>
<object class="GtkInfoBar">
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="label" translatable="yes">You have to restart Gajim for changes to take effect !</property>
<attributes>
<attribute name="weight" value="bold"/>
<attribute name="foreground" value="#ffff00000000"/>
</attributes>
<property name="message_type">warning</property>
<child internal-child="action_area">
<object class="GtkButtonBox">
<property name="can_focus">False</property>
<property name="spacing">6</property>
<property name="layout_style">end</property>
<child>
<placeholder/>
</child>
</object>
<packing>
<property name="expand">False</property>
<property name="fill">False</property>
<property name="position">0</property>
</packing>
</child>
<child internal-child="content_area">
<object class="GtkBox">
<property name="can_focus">False</property>
<child>
<object class="GtkLabel">
<property name="height_request">30</property>
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="label" translatable="yes">You have to restart Gajim for changes to take effect !</property>
<style>
<class name="bold"/>
</style>
</object>
<packing>
<property name="expand">False</property>
<property name="fill">True</property>
<property name="position">0</property>
</packing>
</child>
</object>
<packing>
<property name="expand">False</property>
<property name="fill">False</property>
<property name="position">0</property>
</packing>
</child>
</object>
<packing>
<property name="expand">False</property>
@@ -480,137 +473,126 @@
</packing>
</child>
<child>
<object class="GtkHBox" id="hbox6">
<object class="GtkGrid">
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="border_width">12</property>
<property name="spacing">5</property>
<property name="row_spacing">6</property>
<property name="column_spacing">12</property>
<property name="column_homogeneous">True</property>
<child>
<object class="GtkVBox" id="vbox5">
<object class="GtkScrolledWindow">
<property name="height_request">200</property>
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="can_focus">True</property>
<property name="shadow_type">out</property>
<child>
<object class="GtkScrolledWindow" id="scrolledwindow3">
<object class="GtkTreeView" id="active_accounts_view">
<property name="visible">True</property>
<property name="can_focus">True</property>
<property name="vexpand">True</property>
<property name="model">account_store</property>
<property name="enable_grid_lines">horizontal</property>
<child internal-child="selection">
<object class="GtkTreeSelection" id="treeview-selection3"/>
</child>
<child>
<object class="GtkTreeView" id="active_accounts_view">
<property name="visible">True</property>
<property name="can_focus">True</property>
<property name="model">account_store</property>
<child internal-child="selection">
<object class="GtkTreeSelection" id="treeview-selection3"/>
</child>
<object class="GtkTreeViewColumn" id="treeviewcolumn1">
<property name="title" translatable="yes">Active Accounts</property>
<property name="alignment">0.5</property>
<child>
<object class="GtkTreeViewColumn" id="treeviewcolumn1">
<property name="title" translatable="yes">Active Accounts</property>
<property name="alignment">0.5</property>
<child>
<object class="GtkCellRendererText" id="cellrenderertext5"/>
<attributes>
<attribute name="text">0</attribute>
</attributes>
</child>
</object>
<object class="GtkCellRendererText" id="cellrenderertext5"/>
<attributes>
<attribute name="text">0</attribute>
</attributes>
</child>
</object>
</child>
</object>
<packing>
<property name="expand">True</property>
<property name="fill">True</property>
<property name="position">0</property>
</packing>
</child>
<child>
<object class="GtkButton" id="disable_accounts_btn">
<property name="label" translatable="yes">Disable Account</property>
<property name="visible">True</property>
<property name="can_focus">True</property>
<property name="receives_default">True</property>
<signal name="clicked" handler="disable_accounts_btn_clicked" swapped="no"/>
</object>
<packing>
<property name="expand">False</property>
<property name="fill">False</property>
<property name="position">1</property>
</packing>
</child>
</object>
<packing>
<property name="expand">True</property>
<property name="fill">True</property>
<property name="position">0</property>
<property name="left_attach">0</property>
<property name="top_attach">0</property>
</packing>
</child>
<child>
<object class="GtkVBox" id="vbox6">
<object class="GtkScrolledWindow">
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="can_focus">True</property>
<property name="shadow_type">out</property>
<child>
<object class="GtkScrolledWindow" id="scrolledwindow4">
<object class="GtkTreeView" id="disabled_accounts_view">
<property name="visible">True</property>
<property name="can_focus">True</property>
<property name="vexpand">True</property>
<property name="model">disabled_account_store</property>
<property name="enable_grid_lines">horizontal</property>
<child internal-child="selection">
<object class="GtkTreeSelection" id="treeview-selection4"/>
</child>
<child>
<object class="GtkTreeView" id="disabled_accounts_view">
<property name="visible">True</property>
<property name="can_focus">True</property>
<property name="model">disabled_account_store</property>
<child internal-child="selection">
<object class="GtkTreeSelection" id="treeview-selection4"/>
</child>
<object class="GtkTreeViewColumn" id="treeviewcolumn2">
<property name="title" translatable="yes">Disabled Accounts</property>
<property name="alignment">0.5</property>
<child>
<object class="GtkTreeViewColumn" id="treeviewcolumn2">
<property name="title" translatable="yes">Disabled Accounts</property>
<property name="alignment">0.5</property>
<child>
<object class="GtkCellRendererText" id="cellrenderertext6"/>
<attributes>
<attribute name="text">0</attribute>
</attributes>
</child>
</object>
<object class="GtkCellRendererText" id="cellrenderertext6"/>
<attributes>
<attribute name="text">0</attribute>
</attributes>
</child>
</object>
</child>
</object>
<packing>
<property name="expand">True</property>
<property name="fill">True</property>
<property name="position">0</property>
</packing>
</child>
<child>
<object class="GtkButton" id="activate_accounts_btn">
<property name="label" translatable="yes">Activate Account</property>
<property name="visible">True</property>
<property name="can_focus">True</property>
<property name="receives_default">True</property>
<signal name="clicked" handler="activate_accounts_btn_clicked" swapped="no"/>
</object>
<packing>
<property name="expand">False</property>
<property name="fill">False</property>
<property name="position">1</property>
</packing>
</child>
</object>
<packing>
<property name="expand">True</property>
<property name="fill">True</property>
<property name="position">1</property>
<property name="left_attach">1</property>
<property name="top_attach">0</property>
</packing>
</child>
<child>
<object class="GtkButton" id="disable_accounts_btn">
<property name="label" translatable="yes">_Disable Account</property>
<property name="visible">True</property>
<property name="can_focus">True</property>
<property name="receives_default">True</property>
<property name="halign">center</property>
<property name="use_underline">True</property>
<signal name="clicked" handler="disable_accounts_btn_clicked" swapped="no"/>
<style>
<class name="destructive-action"/>
</style>
</object>
<packing>
<property name="left_attach">0</property>
<property name="top_attach">1</property>
</packing>
</child>
<child>
<object class="GtkButton" id="activate_accounts_btn">
<property name="label" translatable="yes">_Enable Account</property>
<property name="visible">True</property>
<property name="can_focus">True</property>
<property name="receives_default">True</property>
<property name="halign">center</property>
<property name="use_underline">True</property>
<signal name="clicked" handler="activate_accounts_btn_clicked" swapped="no"/>
</object>
<packing>
<property name="left_attach">1</property>
<property name="top_attach">1</property>
</packing>
</child>
</object>
<packing>
<property name="expand">True</property>
<property name="expand">False</property>
<property name="fill">True</property>
<property name="position">1</property>
</packing>
</child>
</object>
<packing>
<property name="position">3</property>
<property name="position">2</property>
</packing>
</child>
<child type="tab">
@@ -620,22 +602,23 @@
<property name="label" translatable="yes">Disable Accounts</property>
</object>
<packing>
<property name="position">3</property>
<property name="position">2</property>
<property name="tab_fill">False</property>
</packing>
</child>
</object>
<object class="GtkMenu" id="fprclipboard_menu">
<property name="visible">True</property>
<property name="can_focus">False</property>
<child>
<object class="GtkMenuItem" id="copyfprclipboard_item">
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="label" translatable="yes" comments="Context menu item">Copy to clipboard</property>
<property name="use_underline">True</property>
<signal name="activate" handler="clipboard_button_cb" swapped="no"/>
</object>
</child>
<object class="GtkListStore" id="fingerprint_store">
<columns>
<!-- column-name id -->
<column type="gint"/>
<!-- column-name screenname -->
<column type="gchararray"/>
<!-- column-name trust -->
<column type="gchararray"/>
<!-- column-name fingerprint -->
<column type="gchararray"/>
<!-- column-name deviceid -->
<column type="gint"/>
</columns>
</object>
</interface>

View File

@@ -1,21 +1,25 @@
<?xml version="1.0" encoding="UTF-8"?>
<!-- Generated with glade 3.20.0 -->
<!-- Generated with glade 3.22.1 -->
<interface>
<requires lib="gtk+" version="3.0"/>
<requires lib="gtk+" version="3.20"/>
<object class="GtkDialog" id="progress_dialog">
<property name="can_focus">True</property>
<property name="border_width">18</property>
<property name="title" translatable="yes">Download</property>
<property name="resizable">False</property>
<property name="window_position">center-on-parent</property>
<property name="destroy_with_parent">True</property>
<property name="icon_name">go-down</property>
<property name="type_hint">dialog</property>
<child>
<placeholder/>
</child>
<child internal-child="vbox">
<object class="GtkBox" id="dialog-vbox">
<property name="width_request">250</property>
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="spacing">6</property>
<property name="spacing">12</property>
<child internal-child="action_area">
<object class="GtkButtonBox" id="dialog-action_area11">
<property name="visible">True</property>
@@ -55,6 +59,20 @@
<property name="position">0</property>
</packing>
</child>
<child>
<object class="GtkImage">
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="tooltip_text">OMEMO</property>
<property name="margin_right">6</property>
<property name="pixbuf">omemo.png</property>
</object>
<packing>
<property name="expand">False</property>
<property name="fill">True</property>
<property name="position">0</property>
</packing>
</child>
<child>
<object class="GtkAlignment" id="alignment1">
<property name="visible">True</property>

View File

@@ -1,189 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<!-- Generated with glade 3.22.1 -->
<interface>
<requires lib="gtk+" version="3.20"/>
<object class="GtkListStore" id="fingerprint_store">
<columns>
<!-- column-name id -->
<column type="gint"/>
<!-- column-name screenname -->
<column type="gchararray"/>
<!-- column-name trust -->
<column type="gchararray"/>
<!-- column-name fingerprint -->
<column type="gchararray"/>
<!-- column-name deviceid -->
<column type="gint"/>
</columns>
</object>
<object class="GtkBox" id="fingerprint_box">
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="margin_left">18</property>
<property name="margin_right">18</property>
<property name="margin_top">18</property>
<property name="margin_bottom">18</property>
<property name="orientation">vertical</property>
<property name="spacing">12</property>
<child>
<object class="GtkBox">
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="spacing">6</property>
<child>
<object class="GtkLabel" id="fingerprint_label_desc1">
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="label" translatable="yes" comments="Descriptive label">Own Fingerprint:</property>
<attributes>
<attribute name="weight" value="bold"/>
</attributes>
</object>
<packing>
<property name="expand">False</property>
<property name="fill">True</property>
<property name="position">0</property>
</packing>
</child>
<child>
<object class="GtkLabel" id="fingerprint_label_own">
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="label">&lt;tt&gt;-------- -------- -------- -------- -------- &lt;/tt&gt;</property>
<property name="use_markup">True</property>
<property name="selectable">True</property>
</object>
<packing>
<property name="expand">False</property>
<property name="fill">False</property>
<property name="position">1</property>
</packing>
</child>
</object>
<packing>
<property name="expand">False</property>
<property name="fill">True</property>
<property name="position">0</property>
</packing>
</child>
<child>
<object class="GtkBox">
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="orientation">vertical</property>
<property name="spacing">12</property>
<child>
<object class="GtkScrolledWindow" id="scrolledwindow1">
<property name="height_request">200</property>
<property name="visible">True</property>
<property name="can_focus">True</property>
<child>
<object class="GtkTreeView" id="fingerprint_view">
<property name="visible">True</property>
<property name="can_focus">True</property>
<property name="has_tooltip">True</property>
<property name="model">fingerprint_store</property>
<property name="headers_clickable">False</property>
<property name="search_column">0</property>
<property name="tooltip_column">3</property>
<signal name="button-press-event" handler="fpr_button_pressed_cb" swapped="no"/>
<child internal-child="selection">
<object class="GtkTreeSelection" id="treeview-selection1"/>
</child>
<child>
<object class="GtkTreeViewColumn" id="name_column">
<property name="resizable">True</property>
<property name="title" translatable="yes">Name</property>
<property name="clickable">True</property>
<property name="sort_indicator">True</property>
<property name="sort_column_id">1</property>
<child>
<object class="GtkCellRendererText" id="NameCell"/>
<attributes>
<attribute name="text">1</attribute>
</attributes>
</child>
</object>
</child>
<child>
<object class="GtkTreeViewColumn" id="trust_column">
<property name="resizable">True</property>
<property name="title" translatable="yes">Trust</property>
<child>
<object class="GtkCellRendererText" id="TrustCell"/>
<attributes>
<attribute name="text">2</attribute>
</attributes>
</child>
</object>
</child>
<child>
<object class="GtkTreeViewColumn" id="fingerprint_column">
<property name="resizable">True</property>
<property name="title" translatable="yes">Fingerprint</property>
<child>
<object class="GtkCellRendererText" id="FingerprintCell"/>
<attributes>
<attribute name="markup">3</attribute>
</attributes>
</child>
</object>
</child>
</object>
</child>
</object>
<packing>
<property name="expand">True</property>
<property name="fill">True</property>
<property name="position">0</property>
</packing>
</child>
<child>
<object class="GtkButtonBox">
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="layout_style">start</property>
<child>
<object class="GtkButton" id="trust_button">
<property name="label" translatable="yes" comments="button">Trust/Revoke Fingerprint</property>
<property name="width_request">200</property>
<property name="visible">True</property>
<property name="can_focus">True</property>
<property name="receives_default">True</property>
<signal name="clicked" handler="trust_button_clicked_cb" swapped="no"/>
</object>
<packing>
<property name="expand">False</property>
<property name="fill">False</property>
<property name="position">0</property>
</packing>
</child>
</object>
<packing>
<property name="expand">False</property>
<property name="fill">True</property>
<property name="position">1</property>
</packing>
</child>
</object>
<packing>
<property name="expand">True</property>
<property name="fill">True</property>
<property name="position">1</property>
</packing>
</child>
</object>
<object class="GtkMenu" id="fprclipboard_menu">
<property name="visible">True</property>
<property name="can_focus">False</property>
<child>
<object class="GtkMenuItem" id="copyfprclipboard_item">
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="label" translatable="yes">Copy to clipboard</property>
<property name="use_underline">True</property>
<signal name="activate" handler="clipboard_button_cb" swapped="no"/>
</object>
</child>
</object>
</interface>

0
omemo/gtk/__init__.py Normal file
View File

349
omemo/gtk/key.py Normal file
View File

@@ -0,0 +1,349 @@
# Copyright (C) 2018 Philipp Hörist <philipp AT hoerist.com>
#
# This file is part of Gajim.
#
# Gajim is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published
# by the Free Software Foundation; version 3 only.
#
# Gajim is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with Gajim. If not, see <http://www.gnu.org/licenses/>.
import logging
import binascii
import textwrap
from gi.repository import Gtk
from gi.repository import GdkPixbuf
from gajim.common import app
from gajim.plugins.plugins_i18n import _
from omemo.gtk.util import DialogButton, ButtonAction
from omemo.gtk.util import NewConfirmationDialog
from omemo.gtk.util import Trust
log = logging.getLogger('gajim.plugin_system.omemo')
TRUST_DATA = {
Trust.NOT_TRUSTED: ('dialog-error-symbolic',
_('Not Trusted'),
'error-color'),
Trust.UNKNOWN: ('security-low-symbolic',
_('Not Decided'),
'warning-color'),
Trust.VERIFIED: ('security-high-symbolic',
_('Trusted'),
'success-color')
}
class KeyDialog(Gtk.Dialog):
def __init__(self, plugin, contact, transient, windowinstances,
groupchat=False):
flags = Gtk.DialogFlags.DESTROY_WITH_PARENT
super().__init__(_('OMEMO Fingerprints'), None, flags)
self.set_transient_for(transient)
self.set_resizable(True)
self.set_default_size(-1, 400)
self.get_style_context().add_class('omemo-key-dialog')
self._groupchat = groupchat
self._contact = contact
self._windowinstances = windowinstances
self._account = self._contact.account.name
self._plugin = plugin
self._con = plugin.connections[self._account]
self.omemostate = self._plugin.get_omemo(self._account)
self._own_jid = app.get_jid_from_account(self._account)
# Header
jid = self._contact.jid
self._header = Gtk.Label(_('Fingerprints for %s') % jid)
self._header.get_style_context().add_class('bold')
self._header.get_style_context().add_class('dim-label')
# Fingerprints list
self._listbox = Gtk.ListBox()
self._listbox.set_selection_mode(Gtk.SelectionMode.NONE)
self._scrolled = Gtk.ScrolledWindow()
self._scrolled.set_policy(Gtk.PolicyType.NEVER,
Gtk.PolicyType.AUTOMATIC)
self._scrolled.add(self._listbox)
# Own fingerprint
self._label = Gtk.Label(_('Own Fingerprint'))
self._label.get_style_context().add_class('bold')
self._label.get_style_context().add_class('dim-label')
self._omemo_logo = Gtk.Image()
omemo_img_path = self._plugin.local_file_path('omemo.png')
omemo_pixbuf = GdkPixbuf.Pixbuf.new_from_file(omemo_img_path)
self._omemo_logo.set_from_pixbuf(omemo_pixbuf)
ownfpr = binascii.hexlify(self.omemostate.store.getIdentityKeyPair()
.getPublicKey().serialize()).decode('utf-8')
ownfpr_format = KeyRow._format_fingerprint(ownfpr[2:])
self._ownfpr = Gtk.Label(ownfpr_format)
self._ownfpr.get_style_context().add_class('omemo-mono')
self._ownfpr.set_selectable(True)
self._ownfpr_box = Gtk.Box(spacing=12)
self._ownfpr_box.set_halign(Gtk.Align.CENTER)
self._ownfpr_box.pack_start(self._omemo_logo, True, True, 0)
self._ownfpr_box.pack_start(self._ownfpr, True, True, 0)
box = self.get_content_area()
box.set_orientation(Gtk.Orientation.VERTICAL)
box.set_spacing(12)
box.pack_start(self._header, False, True, 0)
box.pack_start(self._scrolled, True, True, 0)
box.pack_start(self._label, False, True, 0)
box.pack_start(self._ownfpr_box, False, True, 0)
self.update()
self.connect('destroy', self._on_destroy)
self.show_all()
def update(self):
self._listbox.foreach(lambda row: self._listbox.remove(row))
self._load_fingerprints(self._own_jid)
self._load_fingerprints(self._contact.jid, self._groupchat is True)
def _load_fingerprints(self, contact_jid, groupchat=False):
from axolotl.state.sessionrecord import SessionRecord
state = self.omemostate
if groupchat:
contact_jids = []
for nick in self._con.groupchat[contact_jid]:
real_jid = self._con.groupchat[contact_jid][nick]
if real_jid == self._own_jid:
continue
contact_jids.append(real_jid)
session_db = state.store.getSessionsFromJids(contact_jids)
else:
session_db = state.store.getSessionsFromJid(contact_jid)
for item in session_db:
_id, jid, deviceid, record, active = item
active = bool(active)
identity_key = SessionRecord(serialized=record). \
getSessionState().getRemoteIdentityKey()
fpr = binascii.hexlify(identity_key.getPublicKey().serialize()).decode('utf-8')
fpr = fpr[2:]
trust = state.store.isTrustedIdentity(jid, identity_key)
log.info('Load: %s %s', fpr, trust)
self._listbox.add(KeyRow(jid, deviceid, fpr, trust, active))
def _on_destroy(self, *args):
del self._windowinstances['dialog']
class KeyRow(Gtk.ListBoxRow):
def __init__(self, jid, deviceid, fpr, trust, active):
Gtk.ListBoxRow.__init__(self)
self.set_activatable(False)
self.active = active
self.trust = trust
self.jid = jid
self.deviceid = deviceid
box = Gtk.Box()
box.set_spacing(12)
self._trust_button = TrustButton(self)
box.add(self._trust_button)
label_box = Gtk.Box(orientation=Gtk.Orientation.VERTICAL)
jid_label = Gtk.Label(jid)
jid_label.get_style_context().add_class('dim-label')
jid_label.set_selectable(False)
jid_label.set_halign(Gtk.Align.START)
jid_label.set_valign(Gtk.Align.START)
jid_label.set_hexpand(True)
label_box.add(jid_label)
fingerprint = Gtk.Label(
label=self._format_fingerprint(fpr))
fingerprint.get_style_context().add_class('omemo-mono')
if not active:
fingerprint.get_style_context().add_class('omemo-inactive-color')
fingerprint.set_selectable(True)
fingerprint.set_halign(Gtk.Align.START)
fingerprint.set_valign(Gtk.Align.START)
fingerprint.set_hexpand(True)
label_box.add(fingerprint)
box.add(label_box)
self.add(box)
self.show_all()
def delete_fingerprint(self, *args):
def _remove():
state = self.get_toplevel().omemostate
record = state.store.loadSession(self.jid, self.deviceid)
identity_key = record.getSessionState().getRemoteIdentityKey()
state.store.deleteSession(self.jid, self.deviceid)
state.store.deleteIdentity(self.jid, identity_key)
self.get_parent().remove(self)
self.destroy()
buttons = {
Gtk.ResponseType.CANCEL: DialogButton(_('Cancel')),
Gtk.ResponseType.OK: DialogButton(_('Delete'),
_remove,
ButtonAction.DESTRUCTIVE),
}
NewConfirmationDialog(
_('Delete Fingerprint'),
_('Doing so will permanently delete this Fingerprint'),
buttons,
transient_for=self.get_toplevel())
def set_trust(self):
icon_name, tooltip, css_class = TRUST_DATA[self.trust]
image = self._trust_button.get_child()
image.set_from_icon_name(icon_name, Gtk.IconSize.MENU)
image.get_style_context().add_class(css_class)
image.set_tooltip_text(tooltip)
state = self.get_toplevel().omemostate
record = state.store.loadSession(self.jid, self.deviceid)
identity_key = record.getSessionState().getRemoteIdentityKey()
state.store.setTrust(identity_key, self.trust)
@staticmethod
def _format_fingerprint(fingerprint):
fplen = len(fingerprint)
wordsize = fplen // 8
buf = ''
for w in range(0, fplen, wordsize):
buf += '{0} '.format(fingerprint[w:w + wordsize])
buf = textwrap.fill(buf, width=36)
return buf.rstrip().upper()
class TrustButton(Gtk.MenuButton):
def __init__(self, row):
Gtk.MenuButton.__init__(self)
self._row = row
self._css_class = ''
self.set_popover(TrustPopver(row))
self.set_valign(Gtk.Align.CENTER)
self.update()
def update(self):
icon_name, tooltip, css_class = TRUST_DATA[self._row.trust]
image = self.get_child()
image.set_from_icon_name(icon_name, Gtk.IconSize.MENU)
# Remove old color from icon
image.get_style_context().remove_class(self._css_class)
if not self._row.active:
css_class = 'omemo-inactive-color'
tooltip = '%s - %s' % (_('Inactive'), tooltip)
image.get_style_context().add_class(css_class)
self._css_class = css_class
self.set_tooltip_text(tooltip)
class TrustPopver(Gtk.Popover):
def __init__(self, row):
Gtk.Popover.__init__(self)
self._row = row
self._listbox = Gtk.ListBox()
self._listbox.set_selection_mode(Gtk.SelectionMode.NONE)
if row.trust != Trust.VERIFIED:
self._listbox.add(VerifiedOption())
if row.trust != Trust.NOT_TRUSTED:
self._listbox.add(NotTrustedOption())
self._listbox.add(DeleteOption())
self.add(self._listbox)
self._listbox.show_all()
self._listbox.connect('row-activated', self._activated)
self.get_style_context().add_class('omemo-trust-popover')
def _activated(self, listbox, row):
self.popdown()
if row.type_ is None:
self._row.delete_fingerprint()
else:
self._row.trust = row.type_
self._row.set_trust()
self.get_relative_to().update()
self.update()
def update(self):
self._listbox.foreach(lambda row: self._listbox.remove(row))
if self._row.trust != Trust.VERIFIED:
self._listbox.add(VerifiedOption())
if self._row.trust != Trust.NOT_TRUSTED:
self._listbox.add(NotTrustedOption())
self._listbox.add(DeleteOption())
class MenuOption(Gtk.ListBoxRow):
def __init__(self):
Gtk.ListBoxRow.__init__(self)
box = Gtk.Box()
box.set_spacing(6)
image = Gtk.Image.new_from_icon_name(self.icon,
Gtk.IconSize.MENU)
label = Gtk.Label(label=self.label)
image.get_style_context().add_class(self.color)
box.add(image)
box.add(label)
self.add(box)
self.show_all()
class VerifiedOption(MenuOption):
type_ = Trust.VERIFIED
icon = 'security-high-symbolic'
label = _('Trusted')
color = 'success-color'
def __init__(self):
MenuOption.__init__(self)
class NotTrustedOption(MenuOption):
type_ = Trust.NOT_TRUSTED
icon = 'dialog-error-symbolic'
label = _('Not Trusted')
color = 'error-color'
def __init__(self):
MenuOption.__init__(self)
class DeleteOption(MenuOption):
type_ = None
icon = 'user-trash-symbolic'
label = _('Delete')
color = ''
def __init__(self):
MenuOption.__init__(self)

17
omemo/gtk/style.css Normal file
View File

@@ -0,0 +1,17 @@
.omemo-dark-success-color { color: darker(@success_color); }
.omemo-inactive-color { color: @unfocused_borders; }
.omemo-mono { font-size: 12px; font-family: monospace; }
.omemo-key-dialog > box { margin: 18px; }
.omemo-key-dialog scrolledwindow row {
border-bottom: 1px solid;
border-color: @unfocused_borders;
padding: 10px 20px 10px 10px;
}
.omemo-key-dialog scrolledwindow row:last-child { border-bottom: 0px}
.omemo-key-dialog scrolledwindow { border: 1px solid; border-color:@unfocused_borders; }
.omemo-trust-popover row { padding: 10px 15px 10px 10px; }

73
omemo/gtk/util.py Normal file
View File

@@ -0,0 +1,73 @@
# This file is part of Gajim-OMEMO.
#
# Gajim-OMEMO is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published
# by the Free Software Foundation; version 3 only.
#
# Gajim-OMEMO is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with Gajim-OMEMO. If not, see <http://www.gnu.org/licenses/>.
from collections import namedtuple
from enum import IntEnum
from enum import Enum
from gi.repository import Gtk
DialogButton = namedtuple('DialogButton', 'text callback action')
DialogButton.__new__.__defaults__ = (None, None) # type: ignore
class ButtonAction(Enum):
DESTRUCTIVE = 'destructive-action'
SUGGESTED = 'suggested-action'
class Trust(IntEnum):
NOT_TRUSTED = 0
VERIFIED = 1
UNKNOWN = 2
class NewConfirmationDialog(Gtk.MessageDialog):
def __init__(self, text, sec_text, buttons, transient_for=None):
Gtk.MessageDialog.__init__(self,
transient_for=transient_for,
message_type=Gtk.MessageType.QUESTION,
text=text)
self._buttons = buttons
for response, button in buttons.items():
self.add_button(button.text, response)
if button.action is not None:
widget = self.get_widget_for_response(response)
widget.get_style_context().add_class(button.action.value)
self.format_secondary_markup(sec_text)
self.connect('response', self._on_response)
self.run()
def _on_response(self, dialog, response):
if response == Gtk.ResponseType.DELETE_EVENT:
# Look if DELETE_EVENT is mapped to another response
response = self._buttons.get(response, None)
if response is None:
# If DELETE_EVENT was not mapped we assume CANCEL
response = Gtk.ResponseType.CANCEL
button = self._buttons.get(response, None)
if button is None:
self.destroy()
return
if button.callback is not None:
button.callback()
self.destroy()

View File

@@ -24,8 +24,11 @@ import logging
import binascii
import threading
from enum import IntEnum, unique
from pathlib import Path
from gi.repository import GLib
from gi.repository import Gtk
from gi.repository import Gdk
from gajim import dialogs
from gajim.common import app, ged
@@ -34,6 +37,7 @@ from gajim.plugins import GajimPlugin
from gajim.groupchat_control import GroupchatControl
from omemo.xmpp import DevicelistPEP
from omemo.gtk.key import KeyDialog
CRYPTOGRAPHY_MISSING = 'You are missing Python-Cryptography'
AXOLOTL_MISSING = 'You are missing Python-Axolotl or use an outdated version'
@@ -65,7 +69,7 @@ except Exception as error:
if not ERROR_MSG:
try:
from omemo.omemo_connection import OMEMOConnection
from omemo.ui import OMEMOConfigDialog, FingerprintWindow
from omemo.ui import OMEMOConfigDialog
except Exception as error:
log.error(error)
ERROR_MSG = 'Error: %s' % error
@@ -125,6 +129,25 @@ class OmemoPlugin(GajimPlugin):
schemes += ' aesgcm://'
app.config.set('uri_schemes', schemes)
self._load_css()
def _load_css(self):
path = Path(__file__).parent / 'gtk' / 'style.css'
try:
with open(path, "r") as f:
css = f.read()
except Exception as exc:
log.error('Error loading css: %s', exc)
return
try:
provider = Gtk.CssProvider()
provider.load_from_data(bytes(css.encode('utf-8')))
Gtk.StyleContext.add_provider_for_screen(Gdk.Screen.get_default(),
provider, 610)
except Exception:
log.exception('Error loading application css')
def signed_in(self, event):
""" Method called on SignIn
@@ -290,16 +313,15 @@ class OmemoPlugin(GajimPlugin):
if 'dialog' not in self.windowinstances:
is_groupchat = isinstance(chat_control, GroupchatControl)
self.windowinstances['dialog'] = \
FingerprintWindow(self, contact, transient,
self.windowinstances, groupchat=is_groupchat)
self.windowinstances['dialog'].show_all()
KeyDialog(self, contact, transient,
self.windowinstances, groupchat=is_groupchat)
if fingerprints:
log.debug('%s => Showing Fingerprint Prompt for %s',
account, contact.jid)
omemo.store.setShownFingerprints(fingerprints)
else:
self.windowinstances['dialog'].present()
self.windowinstances['dialog'].update_context_list()
self.windowinstances['dialog'].update()
if fingerprints:
omemo.store.setShownFingerprints(fingerprints)

View File

@@ -23,6 +23,7 @@ the Gajim-OMEMO plugin. If not, see <http://www.gnu.org/licenses/>.
import binascii
import logging
import os
import textwrap
from enum import IntEnum, unique
from gi.repository import Gtk, GdkPixbuf, Gdk
@@ -74,19 +75,16 @@ class OMEMOConfigDialog(GajimPluginConfigDialog):
log.debug('Disabled Accounts:')
log.debug(self.disabled_accounts)
self.fpr_model = self.B.get_object('fingerprint_store')
self.device_model = self.B.get_object('deviceid_store')
self.fpr_view = self.B.get_object('fingerprint_view')
self.disabled_acc_store = self.B.get_object('disabled_account_store')
self.account_store = self.B.get_object('account_store')
self.active_acc_view = self.B.get_object('active_accounts_view')
self.disabled_acc_view = self.B.get_object('disabled_accounts_view')
vbox = self.get_content_area()
vbox.pack_start(self.B.get_object('notebook1'), True, True, 0)
box = self.get_content_area()
box.pack_start(self.B.get_object('notebook1'), True, True, 0)
self.B.connect_signals(self)
@@ -178,67 +176,6 @@ class OMEMOConfigDialog(GajimPluginConfigDialog):
self.plugin.config['DISABLED_ACCOUNTS'] = self.disabled_accounts
self.update_account_combobox()
def delfpr_button_clicked(self, button, *args):
active = self.B.get_object('account_combobox').get_active()
account = self.account_store[active][0]
state = self.plugin.get_omemo(account)
mod, paths = self.fpr_view.get_selection().get_selected_rows()
def on_yes(checked):
record = state.store.loadSession(jid, deviceid)
identity_key = record.getSessionState().getRemoteIdentityKey()
state.store.deleteSession(jid, deviceid)
state.store.deleteIdentity(jid, identity_key)
self.update_context_list()
for path in paths:
it = mod.get_iter(path)
jid, fpr, deviceid = mod.get(it, 1, 3, 4)
fpr = fpr[31:-12]
YesNoDialog(
_('Delete Fingerprint?'),
_('Do you want to delete the '
'fingerprint of <b>{jid}</b> on your account <b>{account}</b>?'
'\n\n<tt>{fingerprint}</tt>').format(jid=jid, account=account, fingerprint=fpr),
on_response_yes=on_yes, transient_for=self)
def trust_button_clicked_cb(self, button, *args):
active = self.B.get_object('account_combobox').get_active()
account = self.account_store[active][0]
state = self.plugin.get_omemo(account)
mod, paths = self.fpr_view.get_selection().get_selected_rows()
def on_yes(checked, identity_key):
state.store.setTrust(identity_key, State.TRUSTED)
self.update_context_list()
def on_no(identity_key):
state.store.setTrust(identity_key, State.UNTRUSTED)
self.update_context_list()
for path in paths:
it = mod.get_iter(path)
jid, fpr, deviceid = mod.get(it, 1, 3, 4)
fpr = fpr[31:-12]
record = state.store.loadSession(jid, deviceid)
identity_key = record.getSessionState().getRemoteIdentityKey()
YesNoDialog(
_('Trust / Revoke Fingerprint?'),
_('Do you want to trust the fingerprint of <b>{jid}</b> '
'on your account <b>{account}</b>?\n\n'
'<tt>{fingerprint}</tt>').format(jid=jid, account=account, fingerprint=fpr),
on_response_yes=(on_yes, identity_key),
on_response_no=(on_no, identity_key),
transient_for=self)
def cleardevice_button_clicked_cb(self, button, *args):
active = self.B.get_object('account_combobox').get_active()
account = self.account_store[active][0]
@@ -248,48 +185,13 @@ class OMEMOConfigDialog(GajimPluginConfigDialog):
def refresh_button_clicked_cb(self, button, *args):
self.update_context_list()
def fpr_button_pressed_cb(self, tw, event):
if event.button == 3:
pthinfo = tw.get_path_at_pos(int(event.x), int(event.y))
if pthinfo is None:
# only show the popup when we right clicked on list content
# ie. don't show it when we click at empty rows
return False
# if the row under the mouse is already selected, we keep the
# selection, otherwise we only select the new item
keep_selection = tw.get_selection().path_is_selected(pthinfo[0])
pop = self.B.get_object('fprclipboard_menu')
pop.popup(None, None, None, None, event.button, event.time)
# keep_selection=True -> no further processing of click event
# keep_selection=False-> further processing -> GTK usually selects
# the item below the cursor
return keep_selection
def clipboard_button_cb(self, menuitem):
mod, paths = self.fpr_view.get_selection().get_selected_rows()
fprs = []
for path in paths:
it = mod.get_iter(path)
jid, fpr = mod.get(it, 1, 3)
fprs.append('%s: %s' % (jid, fpr[31:-12]))
clipboard = Gtk.Clipboard.get(Gdk.SELECTION_CLIPBOARD)
clipboard.set_text('\n'.join(fprs), -1)
def update_context_list(self):
self.fpr_model.clear()
self.device_model.clear()
self.qrcode = self.B.get_object('qrcode')
self.qrinfo = self.B.get_object('qrinfo')
if len(self.account_store) == 0:
self.B.get_object('ID').set_markup('')
self.B.get_object('fingerprint_label').set_markup('')
self.B.get_object('trust_button').set_sensitive(False)
self.B.get_object('delfprbutton').set_sensitive(False)
self.B.get_object('refresh').set_sensitive(False)
self.B.get_object('cleardevice_button').set_sensitive(False)
self.B.get_object('qrcode').clear()
@@ -298,8 +200,6 @@ class OMEMOConfigDialog(GajimPluginConfigDialog):
account = self.account_store[active][0]
# Set buttons active
self.B.get_object('trust_button').set_sensitive(True)
self.B.get_object('delfprbutton').set_sensitive(True)
self.B.get_object('refresh').set_sensitive(True)
if account == 'Local':
self.B.get_object('cleardevice_button').set_sensitive(False)
@@ -313,40 +213,10 @@ class OMEMOConfigDialog(GajimPluginConfigDialog):
ownfpr = binascii.hexlify(state.store.getIdentityKeyPair()
.getPublicKey().serialize()).decode('utf-8')
human_ownfpr = human_hash(ownfpr[2:])
human_ownfpr = self.human_hash(ownfpr[2:])
self.B.get_object('fingerprint_label').set_markup('<tt>%s</tt>'
% human_ownfpr)
# Set Fingerprint List
trust_str = {0: 'False', 1: 'True', 2: 'Undecided'}
session_db = state.store.getAllSessions()
for item in session_db:
color = {0: '#FF0040', # red
1: '#2EFE2E', # green
2: '#FF8000'} # orange
_id, jid, deviceid, record, active = item
active = bool(active)
identity_key = SessionRecord(serialized=record). \
getSessionState().getRemoteIdentityKey()
fpr = binascii.hexlify(
identity_key.getPublicKey().serialize()).decode('utf-8')
fpr = human_hash(fpr[2:])
trust = state.store.isTrustedIdentity(jid, identity_key)
if not active:
color[trust] = '#585858' # grey
self.fpr_model.append(
(_id, jid, trust_str[trust],
'<tt><span foreground="{}">{}</span></tt>'.
format(color[trust], fpr),
deviceid))
# Set Device ID List
for item in state.own_devices:
self.device_model.append([item])
@@ -358,175 +228,17 @@ class OMEMOConfigDialog(GajimPluginConfigDialog):
pixbuf = GdkPixbuf.Pixbuf.new_from_file(path)
self.qrcode.set_from_pixbuf(pixbuf)
self.qrcode.show()
self.qrinfo.hide()
self.qrinfo.set_revealed(False)
else:
self.qrinfo.show()
self.qrinfo.set_revealed(True)
self.qrcode.hide()
class FingerprintWindow(Gtk.Dialog):
def __init__(self, plugin, contact, parent, windowinstances,
groupchat=False):
self.groupchat = groupchat
self.contact = contact
self.windowinstances = windowinstances
self.account = self.contact.account.name
self.plugin = plugin
self.con = plugin.connections[self.account]
self.omemostate = self.plugin.get_omemo(self.account)
self.own_jid = app.get_jid_from_account(self.account)
Gtk.Dialog.__init__(self,
title=(_('Fingerprints for %s')) % contact.jid,
parent=parent,
flags=Gtk.DialogFlags.DESTROY_WITH_PARENT)
self.connect('destroy', self._on_destroy)
self.GTK_BUILDER_FILE_PATH = \
self.plugin.local_file_path('fpr_dialog.ui')
self.xml = Gtk.Builder()
self.xml.add_from_file(self.GTK_BUILDER_FILE_PATH)
self.xml.set_translation_domain('gajim_plugins')
self.fpr_model = self.xml.get_object('fingerprint_store')
self.fpr_view = self.xml.get_object('fingerprint_view')
self.fpr_view_own = self.xml.get_object('fingerprint_view_own')
self.notebook = self.xml.get_object('fingerprint_box')
vbox = self.get_content_area()
vbox.pack_start(self.notebook, True, True, 0)
self.xml.connect_signals(self)
# Set own Fingerprint Label
ownfpr = binascii.hexlify(self.omemostate.store.getIdentityKeyPair()
.getPublicKey().serialize()).decode('utf-8')
ownfpr = human_hash(ownfpr[2:])
self.xml.get_object('fingerprint_label_own').set_markup('<tt>%s</tt>'
% ownfpr)
self.update_context_list()
self.show_all()
def _on_destroy(self, *args):
del self.windowinstances['dialog']
def trust_button_clicked_cb(self, button, *args):
state = self.omemostate
mod, paths = self.fpr_view.get_selection().get_selected_rows()
def on_yes(checked, identity_key):
state.store.setTrust(identity_key, State.TRUSTED)
self.update_context_list()
def on_no(identity_key):
state.store.setTrust(identity_key, State.UNTRUSTED)
self.update_context_list()
for path in paths:
it = mod.get_iter(path)
jid, fpr, deviceid = mod.get(it, 1, 3, 4)
fpr = fpr[31:-12]
record = state.store.loadSession(jid, deviceid)
identity_key = record.getSessionState().getRemoteIdentityKey()
YesNoDialog(
_('Trust / Revoke Fingerprint?'),
_('Do you want to trust the fingerprint of <b>{jid}</b> '
'on your account <b>{account}</b>?\n\n'
'<tt>{fingerprint}</tt>').format(jid=jid,
account=self.account,
fingerprint=fpr),
on_response_yes=(on_yes, identity_key),
on_response_no=(on_no, identity_key),
transient_for=self)
def fpr_button_pressed_cb(self, tw, event):
if event.button == 3:
pthinfo = tw.get_path_at_pos(int(event.x), int(event.y))
if pthinfo is None:
# only show the popup when we right clicked on list content
# ie. don't show it when we click at empty rows
return False
# if the row under the mouse is already selected, we keep the
# selection, otherwise we only select the new item
keep_selection = tw.get_selection().path_is_selected(pthinfo[0])
pop = self.xml.get_object('fprclipboard_menu')
pop.popup(None, None, None, None, event.button, event.time)
# keep_selection=True -> no further processing of click event
# keep_selection=False-> further processing -> GTK usually selects
# the item below the cursor
return keep_selection
def clipboard_button_cb(self, menuitem):
mod, paths = self.fpr_view.get_selection().get_selected_rows()
fprs = []
for path in paths:
it = mod.get_iter(path)
jid, fpr = mod.get(it, 1, 3)
fprs.append('%s: %s' % (jid, fpr[31:-12]))
clipboard = Gtk.Clipboard.get(Gdk.SELECTION_CLIPBOARD)
clipboard.set_text('\n'.join(fprs), -1)
def update_context_list(self, *args):
self.fpr_model.clear()
self._load_fingerprints(self.own_jid)
self._load_fingerprints(self.contact.jid, self.groupchat is True)
def _load_fingerprints(self, contact_jid, groupchat=False):
state = self.omemostate
trust_str = {0: 'False', 1: 'True', 2: 'Undecided'}
if groupchat:
contact_jids = []
for nick in self.con.groupchat[contact_jid]:
real_jid = self.con.groupchat[contact_jid][nick]
if real_jid == self.own_jid:
continue
contact_jids.append(real_jid)
session_db = state.store.getSessionsFromJids(contact_jids)
else:
session_db = state.store.getSessionsFromJid(contact_jid)
for item in session_db:
color = {0: '#FF0040', # red
1: '#2EFE2E', # green
2: '#FF8000'} # orange
_id, jid, deviceid, record, active = item
active = bool(active)
identity_key = SessionRecord(serialized=record). \
getSessionState().getRemoteIdentityKey()
fpr = binascii.hexlify(identity_key.getPublicKey().serialize()).decode('utf-8')
fpr = human_hash(fpr[2:])
trust = state.store.isTrustedIdentity(jid, identity_key)
if not active:
color[trust] = '#585858' # grey
self.fpr_model.append(
(_id, jid, trust_str[trust],
'<tt><span foreground="{}">{}</span></tt>'.
format(color[trust], fpr),
deviceid))
def human_hash(fpr):
fpr = fpr.upper()
fplen = len(fpr)
wordsize = fplen // 8
buf = ''
for w in range(0, fplen, wordsize):
buf += '{0} '.format(fpr[w:w + wordsize])
return buf.rstrip()
def human_hash(self, fpr):
fpr = fpr.upper()
fplen = len(fpr)
wordsize = fplen // 8
buf = ''
for w in range(0, fplen, wordsize):
buf += '{0} '.format(fpr[w:w + wordsize])
buf = textwrap.fill(buf, width=36)
return buf.rstrip()