Merge lp:~mate-ob/gm-notify/multi-account into lp:gm-notify
- multi-account
- Merge into trunk
Proposed by
Mateusz Balbus
Status: | Merged |
---|---|
Approved by: | Mateusz Balbus |
Approved revision: | 117 |
Merged at revision: | 81 |
Proposed branch: | lp:~mate-ob/gm-notify/multi-account |
Merge into: | lp:gm-notify |
Prerequisite: | lp:~jconti/gm-notify/messaging-menu |
Diff against target: |
2127 lines (+1146/-382) 17 files modified
README (+23/-10) account_config.py (+197/-0) account_settings_provider.py (+128/-0) data/net.launchpad.gm-notify.gschema.xml (+34/-22) debian/changelog (+44/-0) debian/compat (+1/-1) debian/control (+23/-10) debian/copyright (+34/-19) debian/rules (+4/-12) debian/source/format (+1/-0) gm-config.ui (+3/-54) gm-list.ui (+224/-0) gm-notify (+188/-91) gm-notify-config (+76/-127) gm_notify_keyring.py (+48/-5) gtalk.py (+113/-27) setup.py (+5/-4) |
To merge this branch: | bzr merge lp:~mate-ob/gm-notify/multi-account |
Related bugs: |
Reviewer | Review Type | Date Requested | Status |
---|---|---|---|
gm-notify Maintainers | Pending | ||
Review via email: mp+279670@code.launchpad.net |
Commit message
Description of the change
Changes:
- Added multi-account support
- Added support for multiple ports (default port 443)
- Added error messages
- Refactored the code
- Made app a single instance
- Added support for MP3 sound files
To post a comment you must log in.
Preview Diff
[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1 | === modified file 'README' | |||
2 | --- README 2010-09-18 13:06:25 +0000 | |||
3 | +++ README 2015-12-05 00:25:32 +0000 | |||
4 | @@ -1,4 +1,5 @@ | |||
6 | 1 | gm-notify 0.10.3 README | 1 | gm-notify 1.0 README |
7 | 2 | |||
8 | 2 | ------------------------------------ | 3 | ------------------------------------ |
9 | 3 | 4 | ||
10 | 4 | QUICK INSTALLATION: | 5 | QUICK INSTALLATION: |
11 | @@ -9,56 +10,66 @@ | |||
12 | 9 | The Configuration-Dialog is placed unter System-Settings-GMail Notifier Configuration. | 10 | The Configuration-Dialog is placed unter System-Settings-GMail Notifier Configuration. |
13 | 10 | 11 | ||
14 | 11 | DEPENDENCIES: | 12 | DEPENDENCIES: |
15 | 13 | |||
16 | 12 | - python-indicate | 14 | - python-indicate |
17 | 13 | - python-notify | 15 | - python-notify |
19 | 14 | - python-gst0.10 | 16 | - gir1.2-gstreamer-1.0 |
20 | 17 | - python-glade2 | ||
21 | 15 | - python-gtk2 | 18 | - python-gtk2 |
22 | 16 | - python-gconf | 19 | - python-gconf |
23 | 17 | - python-twisted-words | 20 | - python-twisted-words |
24 | 18 | - python-gnomekeyring | 21 | - python-gnomekeyring |
25 | 19 | 22 | ||
26 | 20 | install via | 23 | install via |
28 | 21 | sudo apt-get install python-indicate python-notify python-gst0.10 python-gtk2 python-gconf python-twisted-words python-gnomekeyring | 24 | sudo apt-get install python-indicate python-notify gir1.2-gstreamer-1.0 python-glade2 python-gtk2 python-gconf python-twisted-words python-gnomekeyring |
29 | 22 | 25 | ||
30 | 23 | ------------------------------------ | 26 | ------------------------------------ |
31 | 24 | 27 | ||
32 | 25 | RELEASE NOTES: | 28 | RELEASE NOTES: |
33 | 26 | 29 | ||
39 | 27 | v0.10.3: | 30 | v1.0: |
40 | 28 | - Fixed CPU Usage Bug occuring on Maverick | 31 | |
41 | 29 | - Included Bulgarian Translation | 32 | - Added multi-account support |
42 | 30 | - Correct Priority Inbox usage | 33 | - Added support for multiple ports (default port 443) |
43 | 31 | - Mutt Label support | 34 | - Added error messages |
44 | 35 | - Refactored the code | ||
45 | 36 | - Made app a single instance | ||
46 | 32 | 37 | ||
47 | 33 | v0.10.2: | 38 | v0.10.2: |
48 | 39 | |||
49 | 34 | - Added Checkbox to ignore the inbox | 40 | - Added Checkbox to ignore the inbox |
50 | 35 | - fixed autorun | 41 | - fixed autorun |
51 | 36 | - fixed some other small bugs | 42 | - fixed some other small bugs |
52 | 37 | 43 | ||
53 | 38 | v0.10.1: | 44 | v0.10.1: |
54 | 45 | |||
55 | 39 | - Configurable autorun | 46 | - Configurable autorun |
56 | 40 | - Usage of Ubuntu's default mail icon | 47 | - Usage of Ubuntu's default mail icon |
57 | 41 | - Fix of a displaying bug | 48 | - Fix of a displaying bug |
58 | 42 | - Ignoring the inbox is possible (see http://bleedingpaper.com/gm-notify) | 49 | - Ignoring the inbox is possible (see http://bleedingpaper.com/gm-notify) |
59 | 43 | 50 | ||
60 | 44 | v0.10: | 51 | v0.10: |
61 | 52 | |||
62 | 45 | - Written for Lucid 10.04 (no warranty that older versions will be supported) | 53 | - Written for Lucid 10.04 (no warranty that older versions will be supported) |
63 | 46 | - Finally switched to Google Talk backend for instant mail notification (hopefully this time it'll last longer ;-) ) | 54 | - Finally switched to Google Talk backend for instant mail notification (hopefully this time it'll last longer ;-) ) |
64 | 47 | - Labels with a count of 0 will be hidden (except Inbox) | 55 | - Labels with a count of 0 will be hidden (except Inbox) |
65 | 48 | - Sound properties work again | 56 | - Sound properties work again |
66 | 49 | 57 | ||
67 | 50 | v0.9: | 58 | v0.9: |
68 | 59 | |||
69 | 51 | - Switched to IMAP backend (twisted), now allowing to check different labels | 60 | - Switched to IMAP backend (twisted), now allowing to check different labels |
70 | 52 | - allows to choose if it should start webinterface or native client | 61 | - allows to choose if it should start webinterface or native client |
71 | 53 | - Fixed many bugs/crashes | 62 | - Fixed many bugs/crashes |
72 | 54 | 63 | ||
73 | 55 | v0.8: | 64 | v0.8: |
74 | 65 | |||
75 | 56 | - included Danish translation | 66 | - included Danish translation |
76 | 57 | - integrated with GNOME Sound Framework. | 67 | - integrated with GNOME Sound Framework. |
77 | 58 | - Fixed Bug #367242 | 68 | - Fixed Bug #367242 |
78 | 59 | - warn when using small check intervals | 69 | - warn when using small check intervals |
79 | 60 | 70 | ||
80 | 61 | v0.7: | 71 | v0.7: |
81 | 72 | |||
82 | 62 | - included (old) Catalan translation | 73 | - included (old) Catalan translation |
83 | 63 | - German and English is completely translated (I hope my English weren't to bad ;-) ) | 74 | - German and English is completely translated (I hope my English weren't to bad ;-) ) |
84 | 64 | - added a graphical configuration interface | 75 | - added a graphical configuration interface |
85 | @@ -76,9 +87,11 @@ | |||
86 | 76 | Concept and code taken from cgmail | 87 | Concept and code taken from cgmail |
87 | 77 | implemented by Sassur <sassur@gmail.com> | 88 | implemented by Sassur <sassur@gmail.com> |
88 | 78 | 89 | ||
90 | 79 | all the rest is written 2009-2010 by Alexander Hungenberg | 90 | all the rest developed and maintained by: |
91 | 91 | 2009-2010 by Alexander Hungenberg | ||
92 | 92 | 2015 Mateusz Balbus | ||
93 | 80 | 93 | ||
95 | 81 | ------------------------------------------------------------------------ | 94 | ------------------------------------ |
96 | 82 | 95 | ||
97 | 83 | LICENSE: | 96 | LICENSE: |
98 | 84 | 97 | ||
99 | 85 | 98 | ||
100 | === added file 'account_config.py' | |||
101 | --- account_config.py 1970-01-01 00:00:00 +0000 | |||
102 | +++ account_config.py 2015-12-05 00:25:32 +0000 | |||
103 | @@ -0,0 +1,197 @@ | |||
104 | 1 | #!/usr/bin/env python | ||
105 | 2 | # -*- coding: utf-8 -*- | ||
106 | 3 | # account_config.py v0.10.3 | ||
107 | 4 | # Provides settins to GMail notify | ||
108 | 5 | # | ||
109 | 6 | # Copyright (c) 2015, Mateusz Balbus <balbusm@gmail.com> | ||
110 | 7 | # | ||
111 | 8 | # This program is free software: you can redistribute it and/or modify | ||
112 | 9 | # it under the terms of the GNU General Public License as published by | ||
113 | 10 | # the Free Software Foundation, either version 3 of the License, or | ||
114 | 11 | # (at your option) any later version. | ||
115 | 12 | # | ||
116 | 13 | # This program is distributed in the hope that it will be useful, | ||
117 | 14 | # but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
118 | 15 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
119 | 16 | # GNU General Public License for more details. | ||
120 | 17 | # | ||
121 | 18 | # You should have received a copy of the GNU General Public License | ||
122 | 19 | # along with this program. If not, see <http://www.gnu.org/licenses/>. | ||
123 | 20 | from __future__ import print_function | ||
124 | 21 | |||
125 | 22 | import os | ||
126 | 23 | import gettext | ||
127 | 24 | |||
128 | 25 | from gi.repository import Gio, Gtk | ||
129 | 26 | from twisted.words.protocols.jabber import jid | ||
130 | 27 | |||
131 | 28 | from gtalk import MailChecker | ||
132 | 29 | import account_settings_provider | ||
133 | 30 | |||
134 | 31 | _ = gettext.translation('gm-notify', fallback=True).ugettext | ||
135 | 32 | |||
136 | 33 | class AccountConfig: | ||
137 | 34 | def __init__(self, keys, creds): | ||
138 | 35 | self.keys = keys | ||
139 | 36 | self.creds = creds | ||
140 | 37 | |||
141 | 38 | def init_window(self, parent): | ||
142 | 39 | if os.path.exists("gm-config.ui"): | ||
143 | 40 | builder_file = "gm-config.ui" | ||
144 | 41 | elif os.path.exists("/usr/local/share/gm-notify/gm-config.ui"): | ||
145 | 42 | builder_file = "/usr/local/share/gm-notify/gm-config.ui" | ||
146 | 43 | elif os.path.exists("/usr/share/gm-notify/gm-config.ui"): | ||
147 | 44 | builder_file = "/usr/share/gm-notify/gm-config.ui" | ||
148 | 45 | |||
149 | 46 | self.wTree = Gtk.Builder.new() | ||
150 | 47 | self.wTree.add_from_file(builder_file) | ||
151 | 48 | self.wTree.set_translation_domain("gm-notify") | ||
152 | 49 | self.window = self.wTree.get_object("gmnotify_add_account") | ||
153 | 50 | |||
154 | 51 | self.window.set_transient_for(parent) | ||
155 | 52 | self.window.set_modal(True) | ||
156 | 53 | self.window.set_destroy_with_parent(True) | ||
157 | 54 | |||
158 | 55 | self.window.show_all() | ||
159 | 56 | |||
160 | 57 | self.wTree.get_object("notebook_main").set_current_page(0) | ||
161 | 58 | |||
162 | 59 | self.input_user = self.wTree.get_object("input_user") | ||
163 | 60 | self.input_password = self.wTree.get_object("input_password") | ||
164 | 61 | self.image_credentials = self.wTree.get_object("image_credentials") | ||
165 | 62 | self.label_credentials = self.wTree.get_object("label_credentials") | ||
166 | 63 | self.button_apply = self.wTree.get_object("button_apply") | ||
167 | 64 | |||
168 | 65 | self.window.connect("delete_event", self.close) | ||
169 | 66 | self.wTree.get_object("button_close").connect("clicked", self.close) | ||
170 | 67 | self.button_apply.connect("clicked", self.apply) | ||
171 | 68 | self.input_password.connect("focus-out-event", self.check_credentials) | ||
172 | 69 | self.input_user.connect("focus-out-event", self.check_user) | ||
173 | 70 | self.wTree.get_object("checkbutton_sound").connect("toggled", self.on_checkbutton_sound_toggled) | ||
174 | 71 | |||
175 | 72 | ##### | ||
176 | 73 | # Init with stored values | ||
177 | 74 | ##### | ||
178 | 75 | |||
179 | 76 | # Credentials | ||
180 | 77 | |||
181 | 78 | settings_provider = account_settings_provider.create_settings_provider(self.creds.username) | ||
182 | 79 | |||
183 | 80 | if self.creds.username: | ||
184 | 81 | self.input_user.set_text(self.creds.username) | ||
185 | 82 | self.input_user.set_sensitive(False) | ||
186 | 83 | |||
187 | 84 | self.input_password.set_text(self.creds.password) | ||
188 | 85 | |||
189 | 86 | self.api = MailChecker("", "", settings_provider) | ||
190 | 87 | self.api.setOnAuthSucceeded(self.credentials_valid) | ||
191 | 88 | self.api.setOnAuthFailed(self.credentials_invalid) | ||
192 | 89 | self.api.setOnConnectionErrorCB(self.connection_error) | ||
193 | 90 | |||
194 | 91 | self.check_credentials(None, None) | ||
195 | 92 | |||
196 | 93 | # Sound | ||
197 | 94 | self.wTree.get_object("checkbutton_sound").set_active(settings_provider.retrieve_sound_enabled()) | ||
198 | 95 | sound_file = settings_provider.retrieve_sound_file() | ||
199 | 96 | if sound_file: | ||
200 | 97 | self.wTree.get_object("fcbutton_sound").set_filename(sound_file) | ||
201 | 98 | self.on_checkbutton_sound_toggled(self.wTree.get_object("checkbutton_sound")) | ||
202 | 99 | |||
203 | 100 | # ClickAction | ||
204 | 101 | if settings_provider.retrieve_use_mail_client(): | ||
205 | 102 | self.wTree.get_object("radiobutton_openclient").set_active(True) | ||
206 | 103 | else: | ||
207 | 104 | self.wTree.get_object("radiobutton_openweb").set_active(True) | ||
208 | 105 | |||
209 | 106 | # Mailboxes | ||
210 | 107 | self.wTree.get_object("checkbutton_inbox").set_active(settings_provider.retrieve_ignore_inbox()) | ||
211 | 108 | labels = settings_provider.retrieve_labels() | ||
212 | 109 | self.wTree.get_object("entry_labels").set_text(", ".join(labels)) | ||
213 | 110 | |||
214 | 111 | return self.window | ||
215 | 112 | |||
216 | 113 | def close(self, widget = None, event = None): | ||
217 | 114 | if self.api.is_running(): | ||
218 | 115 | self.api.die() | ||
219 | 116 | self.window.close() | ||
220 | 117 | |||
221 | 118 | def apply(self, widget): | ||
222 | 119 | self.save() | ||
223 | 120 | self.close() | ||
224 | 121 | |||
225 | 122 | def save(self): | ||
226 | 123 | '''saves the entered data and closes the app''' | ||
227 | 124 | # Credentials | ||
228 | 125 | self.keys.delete_credentials(self.creds.username) | ||
229 | 126 | |||
230 | 127 | user = self.input_user.get_text() | ||
231 | 128 | self.keys.set_credentials(user, | ||
232 | 129 | self.input_password.get_text()) | ||
233 | 130 | |||
234 | 131 | settings_provider = account_settings_provider.create_settings_provider(user) | ||
235 | 132 | # Mailboxes | ||
236 | 133 | labels = [] | ||
237 | 134 | for label in self.wTree.get_object("entry_labels").get_text().split(","): | ||
238 | 135 | labels.append(label.strip()) | ||
239 | 136 | settings_provider.save_labels(labels) | ||
240 | 137 | settings_provider.save_ignore_inbox(self.wTree.get_object("checkbutton_inbox").get_active()) | ||
241 | 138 | |||
242 | 139 | # ClickAction | ||
243 | 140 | settings_provider.save_use_mail_client(self.wTree.get_object("radiobutton_openclient").get_active()) | ||
244 | 141 | |||
245 | 142 | # Port | ||
246 | 143 | settings_provider.save_preferred_port(settings_provider.retrieve_preferred_port()) | ||
247 | 144 | |||
248 | 145 | # Soundfile | ||
249 | 146 | if self.wTree.get_object("checkbutton_sound").get_active() and self.wTree.get_object("fcbutton_sound").get_filename(): | ||
250 | 147 | settings_provider.save_sound_enabled(True) | ||
251 | 148 | settings_provider.save_sound_file(str(self.wTree.get_object("fcbutton_sound").get_filename())) | ||
252 | 149 | else: | ||
253 | 150 | settings_provider.save_sound_enabled(False) | ||
254 | 151 | |||
255 | 152 | def on_checkbutton_sound_toggled(self, widget): | ||
256 | 153 | self.wTree.get_object("fcbutton_sound").set_sensitive(self.wTree.get_object("checkbutton_sound").get_active()) | ||
257 | 154 | |||
258 | 155 | def check_user(self, widget, event): | ||
259 | 156 | user = self.input_user.get_text() | ||
260 | 157 | if not self.has_mail_postfix(user): | ||
261 | 158 | self.input_user.set_text(user + "@gmail.com") | ||
262 | 159 | |||
263 | 160 | def has_mail_postfix(self, user): | ||
264 | 161 | return len(user) == 0 or "@" in user | ||
265 | 162 | |||
266 | 163 | def check_credentials(self, widget, event, data=None): | ||
267 | 164 | '''check if the given credentials are valid''' | ||
268 | 165 | |||
269 | 166 | self.button_apply.set_sensitive(False) | ||
270 | 167 | |||
271 | 168 | # Change status text and disable input fields | ||
272 | 169 | if self.input_user.get_text() and self.input_password.get_text(): | ||
273 | 170 | self.image_credentials.set_from_file("/usr/share/gm-notify/checking.gif") | ||
274 | 171 | self.label_credentials.set_text(_("checking...")) | ||
275 | 172 | self.input_user.set_sensitive(False) | ||
276 | 173 | self.input_password.set_sensitive(False) | ||
277 | 174 | |||
278 | 175 | self.api.jid = jid.JID(self.input_user.get_text()) | ||
279 | 176 | self.api.password = self.input_password.get_text() | ||
280 | 177 | self.api.connect() | ||
281 | 178 | return False | ||
282 | 179 | |||
283 | 180 | def credentials_valid(self, username): | ||
284 | 181 | self.on_credentials_checked("gtk-yes", "Valid credentials", True) | ||
285 | 182 | |||
286 | 183 | def credentials_invalid(self, username, reason): | ||
287 | 184 | self.on_credentials_checked("gtk-stop", "Invalid credentials") | ||
288 | 185 | |||
289 | 186 | def connection_error(self, username, reason): | ||
290 | 187 | self.on_credentials_checked("gtk-stop", "Connection error") | ||
291 | 188 | |||
292 | 189 | def on_credentials_checked(self, icon_name, text, valid = False): | ||
293 | 190 | self.image_credentials.set_from_icon_name(icon_name, Gtk.IconSize.MENU) | ||
294 | 191 | self.label_credentials.set_text(_(text)) | ||
295 | 192 | self.input_user.set_sensitive(not bool(self.creds.username)) | ||
296 | 193 | self.input_password.set_sensitive(True) | ||
297 | 194 | self.button_apply.set_sensitive(valid) | ||
298 | 195 | |||
299 | 196 | self.api.die() | ||
300 | 197 | |||
301 | 0 | \ No newline at end of file | 198 | \ No newline at end of file |
302 | 1 | 199 | ||
303 | === added file 'account_settings_provider.py' | |||
304 | --- account_settings_provider.py 1970-01-01 00:00:00 +0000 | |||
305 | +++ account_settings_provider.py 2015-12-05 00:25:32 +0000 | |||
306 | @@ -0,0 +1,128 @@ | |||
307 | 1 | #!/usr/bin/env python | ||
308 | 2 | # -*- coding: utf-8 -*- | ||
309 | 3 | # settings_provider.py v0.10.3 | ||
310 | 4 | # Provides settins to GMail notify | ||
311 | 5 | # | ||
312 | 6 | # Copyright (c) 2015, Mateusz Balbus <balbusm@gmail.com> | ||
313 | 7 | # | ||
314 | 8 | # This program is free software: you can redistribute it and/or modify | ||
315 | 9 | # it under the terms of the GNU General Public License as published by | ||
316 | 10 | # the Free Software Foundation, either version 3 of the License, or | ||
317 | 11 | # (at your option) any later version. | ||
318 | 12 | # | ||
319 | 13 | # This program is distributed in the hope that it will be useful, | ||
320 | 14 | # but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
321 | 15 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
322 | 16 | # GNU General Public License for more details. | ||
323 | 17 | # | ||
324 | 18 | # You should have received a copy of the GNU General Public License | ||
325 | 19 | # along with this program. If not, see <http://www.gnu.org/licenses/>. | ||
326 | 20 | # | ||
327 | 21 | from __future__ import print_function | ||
328 | 22 | |||
329 | 23 | from gi.repository import Gio | ||
330 | 24 | |||
331 | 25 | def create_settings_provider(username): | ||
332 | 26 | if username: | ||
333 | 27 | return AccountSettingsProvider(username) | ||
334 | 28 | else: | ||
335 | 29 | return DefaultSettingsProvider() | ||
336 | 30 | |||
337 | 31 | class AccountSettingsProvider: | ||
338 | 32 | def __init__(self, username): | ||
339 | 33 | self.client = Gio.Settings("net.launchpad.gm-notify.account", "/net/launchpad/gm-notify/" + username + "/") | ||
340 | 34 | |||
341 | 35 | def retrieve_sound_file(self, default_file = None): | ||
342 | 36 | soundfile = self.client.get_string("soundfile") | ||
343 | 37 | if soundfile == '': | ||
344 | 38 | soundfile = default_file | ||
345 | 39 | return soundfile | ||
346 | 40 | |||
347 | 41 | def retrieve_sound_enabled(self): | ||
348 | 42 | return self.client.get_boolean("play-sound") | ||
349 | 43 | |||
350 | 44 | def retrieve_preferred_port(self): | ||
351 | 45 | return self.client.get_int("preferred-port") | ||
352 | 46 | |||
353 | 47 | def retrieve_ignore_inbox(self): | ||
354 | 48 | return self.client.get_boolean("ignore-inbox") | ||
355 | 49 | |||
356 | 50 | |||
357 | 51 | def retrieve_labels(self): | ||
358 | 52 | return self.client.get_strv("labels") | ||
359 | 53 | |||
360 | 54 | |||
361 | 55 | def retrieve_use_mail_client(self): | ||
362 | 56 | return self.client.get_boolean("openclient") | ||
363 | 57 | |||
364 | 58 | def save_labels(self, labels): | ||
365 | 59 | self.client.set_strv("labels", labels) | ||
366 | 60 | |||
367 | 61 | |||
368 | 62 | def save_ignore_inbox(self, ignore_inbox): | ||
369 | 63 | self.client.set_boolean("ignore-inbox", ignore_inbox) | ||
370 | 64 | |||
371 | 65 | |||
372 | 66 | def save_use_mail_client(self, use_mail_client): | ||
373 | 67 | self.client.set_boolean("openclient", use_mail_client) | ||
374 | 68 | |||
375 | 69 | |||
376 | 70 | def save_sound_enabled(self, enabled): | ||
377 | 71 | self.client.set_boolean("play-sound", enabled) | ||
378 | 72 | |||
379 | 73 | |||
380 | 74 | def save_sound_file(self, sound_file): | ||
381 | 75 | self.client.set_string("soundfile", sound_file) | ||
382 | 76 | |||
383 | 77 | |||
384 | 78 | def save_preferred_port(self, preferred_port): | ||
385 | 79 | self.client.set_int("preferred-port", preferred_port) | ||
386 | 80 | |||
387 | 81 | def remove_all_settings(self): | ||
388 | 82 | self.client.reset("") | ||
389 | 83 | |||
390 | 84 | class DefaultSettingsProvider: | ||
391 | 85 | def retrieve_sound_file(self, default_file = None): | ||
392 | 86 | return default_file | ||
393 | 87 | |||
394 | 88 | def retrieve_sound_enabled(self): | ||
395 | 89 | return False | ||
396 | 90 | |||
397 | 91 | def retrieve_preferred_port(self): | ||
398 | 92 | return 443 | ||
399 | 93 | |||
400 | 94 | def retrieve_ignore_inbox(self): | ||
401 | 95 | return False | ||
402 | 96 | |||
403 | 97 | def retrieve_labels(self): | ||
404 | 98 | return '' | ||
405 | 99 | |||
406 | 100 | def retrieve_use_mail_client(self): | ||
407 | 101 | return False | ||
408 | 102 | |||
409 | 103 | def save_labels(self, mailboxes): | ||
410 | 104 | raise NotImplementedError("Saving not supported") | ||
411 | 105 | |||
412 | 106 | |||
413 | 107 | def save_ignore_inbox(self, ignore_inbox): | ||
414 | 108 | raise NotImplementedError("Saving not supported") | ||
415 | 109 | |||
416 | 110 | |||
417 | 111 | def save_use_mail_client(self, use_mail_client): | ||
418 | 112 | raise NotImplementedError("Saving not supported") | ||
419 | 113 | |||
420 | 114 | |||
421 | 115 | def save_sound_enabled(self, enabled): | ||
422 | 116 | raise NotImplementedError("Saving not supported") | ||
423 | 117 | |||
424 | 118 | |||
425 | 119 | def save_sound_file(self, sound_file): | ||
426 | 120 | raise NotImplementedError("Saving not supported") | ||
427 | 121 | |||
428 | 122 | |||
429 | 123 | def save_preferred_port(self, preferred_port): | ||
430 | 124 | print("Ignoring save_preferred_port") | ||
431 | 125 | |||
432 | 126 | def remove_all_settings(self): | ||
433 | 127 | raise NotImplementedError("Removing not supported") | ||
434 | 128 | |||
435 | 0 | 129 | ||
436 | === modified file 'data/net.launchpad.gm-notify.gschema.xml' | |||
437 | --- data/net.launchpad.gm-notify.gschema.xml 2015-12-05 00:25:32 +0000 | |||
438 | +++ data/net.launchpad.gm-notify.gschema.xml 2015-12-05 00:25:32 +0000 | |||
439 | @@ -1,25 +1,37 @@ | |||
440 | 1 | <?xml version="1.0" encoding="UTF-8"?> | 1 | <?xml version="1.0" encoding="UTF-8"?> |
441 | 2 | <schemalist> | 2 | <schemalist> |
464 | 3 | <schema path="/net/launchpad/gm-notify/" id="net.launchpad.gm-notify" gettext-domain="gm-notify"> | 3 | <schema id="net.launchpad.gm-notify.account" gettext-domain="gm-notify"> |
465 | 4 | <key type="b" name="play-sound"> | 4 | <key type="b" name="play-sound"> |
466 | 5 | <default>false</default> | 5 | <default>false</default> |
467 | 6 | <summary>Shall we play a sound on new mail?</summary> | 6 | <summary>Shall we play a sound on new mail?</summary> |
468 | 7 | </key> | 7 | </key> |
469 | 8 | <key type="b" name="ignore-inbox"> | 8 | <key type="b" name="ignore-inbox"> |
470 | 9 | <default>false</default> | 9 | <default>false</default> |
471 | 10 | <summary>If true, there won't be notifications regarding just your inbox.</summary> | 10 | <summary>If true, there won't be notifications regarding |
472 | 11 | </key> | 11 | just your |
473 | 12 | <key type="s" name="soundfile"> | 12 | inbox. |
474 | 13 | <default>''</default> | 13 | </summary> |
475 | 14 | <summary>Path to the soundfile which should be played when a new mail arrives</summary> | 14 | </key> |
476 | 15 | </key> | 15 | <key type="s" name="soundfile"> |
477 | 16 | <key type="as" name="mailboxes"> | 16 | <default>''</default> |
478 | 17 | <default>[]</default> | 17 | <summary>Path to the soundfile which should be played when a |
479 | 18 | <summary>List containing mailboxes to check</summary> | 18 | new mail |
480 | 19 | </key> | 19 | arrives |
481 | 20 | <key type="b" name="openclient"> | 20 | </summary> |
482 | 21 | <default>false</default> | 21 | </key> |
483 | 22 | <summary>Shall we open the default mail client or webinterface?</summary> | 22 | <key type="as" name="labels"> |
484 | 23 | </key> | 23 | <default>[]</default> |
485 | 24 | </schema> | 24 | <summary>List containing labels to check</summary> |
486 | 25 | </key> | ||
487 | 26 | <key type="b" name="openclient"> | ||
488 | 27 | <default>false</default> | ||
489 | 28 | <summary>Shall we open the default mail client or | ||
490 | 29 | webinterface? | ||
491 | 30 | </summary> | ||
492 | 31 | </key> | ||
493 | 32 | <key type="i" name="preferred-port"> | ||
494 | 33 | <default>443</default> | ||
495 | 34 | <summary>Port used to connect to Google services</summary> | ||
496 | 35 | </key> | ||
497 | 36 | </schema> | ||
498 | 25 | </schemalist> | 37 | </schemalist> |
499 | 26 | 38 | ||
500 | === modified file 'debian/changelog' | |||
501 | --- debian/changelog 2010-05-05 19:51:23 +0000 | |||
502 | +++ debian/changelog 2015-12-05 00:25:32 +0000 | |||
503 | @@ -1,3 +1,47 @@ | |||
504 | 1 | gm-notify (1.0.0-0ubuntu1) vivid; urgency=medium | ||
505 | 2 | |||
506 | 3 | * Added multi-account support | ||
507 | 4 | * Added support for multiple ports (default port 443) | ||
508 | 5 | * Added error messages | ||
509 | 6 | * Refactored the code | ||
510 | 7 | * Made app a single instance | ||
511 | 8 | |||
512 | 9 | -- Mateusz Balbus <balbusm@gmail.com> Thu, 15 Oct 2015 15:43:32 +0200 | ||
513 | 10 | |||
514 | 11 | gm-notify (0.10.3-0ubuntu3) trusty; urgency=low | ||
515 | 12 | |||
516 | 13 | * Convert to dh_python2. | ||
517 | 14 | * Wrap and sort. | ||
518 | 15 | * Bump Standards-Version to 3.9.4. | ||
519 | 16 | * Update copyright fields. | ||
520 | 17 | |||
521 | 18 | -- Logan Rosen <logan@ubuntu.com> Tue, 19 Nov 2013 23:47:50 -0500 | ||
522 | 19 | |||
523 | 20 | gm-notify (0.10.3-0ubuntu2) trusty; urgency=low | ||
524 | 21 | |||
525 | 22 | * debian/control: Add missing dependency on | ||
526 | 23 | python-glade2 (LP: # 863855). | ||
527 | 24 | * debian/copyright: Tweak to bring up to latest | ||
528 | 25 | DEP-5 spec. | ||
529 | 26 | |||
530 | 27 | -- Andrew Starr-Bochicchio <a.starr.b@gmail.com> Sat, 01 Oct 2011 03:03:20 -0400 | ||
531 | 28 | |||
532 | 29 | gm-notify (0.10.3-0ubuntu1) maverick; urgency=low | ||
533 | 30 | |||
534 | 31 | * New upstream bug fix release. | ||
535 | 32 | - Fix CPU usage issue with subprocess.call. (LP: #616781) | ||
536 | 33 | * debian/patches/scripts-with-extensions-in-PATH: | ||
537 | 34 | - Drop, included upstream. | ||
538 | 35 | * debian/control: Bump Standards-Version to 3.9.1. | ||
539 | 36 | |||
540 | 37 | -- Andrew Starr-Bochicchio <a.starr.b@gmail.com> Sat, 18 Sep 2010 15:41:02 -0400 | ||
541 | 38 | |||
542 | 39 | gm-notify (0.10.2-0ubuntu1) maverick; urgency=low | ||
543 | 40 | |||
544 | 41 | * Initial release (LP: #596313). | ||
545 | 42 | |||
546 | 43 | -- Andrew Starr-Bochicchio <a.starr.b@gmail.com> Thu, 01 Jul 2010 11:28:14 -0400 | ||
547 | 44 | |||
548 | 1 | gm-notify (0.10.2-1~ppa1) lucid; urgency=low | 45 | gm-notify (0.10.2-1~ppa1) lucid; urgency=low |
549 | 2 | 46 | ||
550 | 3 | * Release 0.10.2 / see http://bleedingpaper.com/gm-notify | 47 | * Release 0.10.2 / see http://bleedingpaper.com/gm-notify |
551 | 4 | 48 | ||
552 | === modified file 'debian/compat' | |||
553 | --- debian/compat 2009-05-07 06:24:15 +0000 | |||
554 | +++ debian/compat 2015-12-05 00:25:32 +0000 | |||
555 | @@ -1,1 +1,1 @@ | |||
557 | 1 | 7 | 1 | 9 |
558 | 2 | 2 | ||
559 | === modified file 'debian/control' | |||
560 | --- debian/control 2010-04-21 11:56:58 +0000 | |||
561 | +++ debian/control 2015-12-05 00:25:32 +0000 | |||
562 | @@ -1,18 +1,31 @@ | |||
563 | 1 | Source: gm-notify | 1 | Source: gm-notify |
564 | 2 | Section: mail | 2 | Section: mail |
565 | 3 | Priority: extra | 3 | Priority: extra |
570 | 4 | Maintainer: Ken VanDine <ken.vandine@canonical.com> | 4 | Maintainer: Ubuntu Developers <ubuntu-devel-discuss@lists.ubuntu.com> |
571 | 5 | XS-Python-Version: >= 2.6 | 5 | XSBC-Original-Maintainer: Andrew Starr-Bochicchio <a.starr.b@gmail.com> |
572 | 6 | Build-Depends: cdbs, debhelper (>= 7), python-central (>= 0.6), python | 6 | X-Python-Version: >= 2.6 |
573 | 7 | Standards-Version: 3.8.0 | 7 | Build-Depends: debhelper (>= 9), |
574 | 8 | dh-python, | ||
575 | 9 | python (>= 2.6.6-3~), | ||
576 | 10 | python-setuptools | ||
577 | 11 | Standards-Version: 3.9.4 | ||
578 | 8 | Homepage: https://launchpad.net/gm-notify | 12 | Homepage: https://launchpad.net/gm-notify |
579 | 9 | 13 | ||
580 | 10 | Package: gm-notify | 14 | Package: gm-notify |
581 | 11 | Architecture: all | 15 | Architecture: all |
582 | 12 | XB-Python-Version: ${python:Versions} | ||
583 | 13 | Depends: ${shlibs:Depends}, ${misc:Depends}, ${python:Depends}, python-indicate, python-notify, python-gst0.10, python-gtk2, python-gconf, python-twisted-words, python-gnomekeyring | ||
584 | 14 | Provides: ${python:Provides} | 16 | Provides: ${python:Provides} |
589 | 15 | Description: GMail Notifier | 17 | Depends: python-gconf, |
590 | 16 | A simple and lightweight highly Ubuntu 10.04 integrated GMail Notifier which | 18 | python-glade2, |
591 | 17 | takes advantages of the new and nice notify-osd and indicator-applet. | 19 | python-gnomekeyring, |
592 | 18 | Because of this it will not run with older Ubuntu versions. | 20 | python-gtk2, |
593 | 21 | python-indicate, | ||
594 | 22 | python-notify, | ||
595 | 23 | python-twisted-words, | ||
596 | 24 | gir1.2-gstreamer-1.0, | ||
597 | 25 | ${misc:Depends}, | ||
598 | 26 | ${python:Depends}, | ||
599 | 27 | ${shlibs:Depends} | ||
600 | 28 | Description: Highly Ubuntu integrated GMail notifier | ||
601 | 29 | gm-notify is a simple, lightweight, and highly Ubuntu integrated | ||
602 | 30 | GMail notifier which takes advantages of features like notify-osd | ||
603 | 31 | and indicator-applet. | ||
604 | 19 | 32 | ||
605 | === modified file 'debian/copyright' | |||
606 | --- debian/copyright 2009-05-07 06:24:15 +0000 | |||
607 | +++ debian/copyright 2015-12-05 00:25:32 +0000 | |||
608 | @@ -1,19 +1,34 @@ | |||
628 | 1 | This package was debianized by Ken VanDine <ken.vandine@canonical.com> on | 1 | Format: http://www.debian.org/doc/packaging-manuals/copyright-format/1.0/ |
629 | 2 | Thu, 07 May 2009 01:39:52 -0400. | 2 | Upstream-Name: gm-notify |
630 | 3 | 3 | Source: https://launchpad.net/gm-notify | |
631 | 4 | It was downloaded from https://launchpad.net/gm-notify | 4 | Upstream-Contact: Alexander Hungenberg <alexander.hungenberg@gmail.com> |
632 | 5 | 5 | ||
633 | 6 | Upstream Author: | 6 | Files: * |
634 | 7 | 7 | Copyright: 2009-2010, Alexander Hungenberg <alexander.hungenberg@gmail.com> | |
635 | 8 | Alexander Hungenberg <alexander.hungenberg@gmail.com> | 8 | License: GPL-3+ |
636 | 9 | 9 | ||
637 | 10 | Copyright: | 10 | Files: po/* |
638 | 11 | 11 | Copyright: 2009, Rosetta Contributors and Canonical Ltd. | |
639 | 12 | Copyright (c) 2009, Alexander Hungenberg <alexander.hungenberg@gmail.com> | 12 | License: GPL-3+ |
640 | 13 | 13 | ||
641 | 14 | License: | 14 | Files: debian/* |
642 | 15 | 15 | Copyright: 2009, Ken VanDine <ken.vandine@canonical.com>, | |
643 | 16 | GPL-3 | 16 | 2010, Andrew Starr-Bochicchio <a.starr.b@gmail.com> |
644 | 17 | 17 | License: GPL-3+ | |
645 | 18 | The Debian packaging is copyright 2009, Ken VanDine <ken.vandine@canonical.com> and | 18 | |
646 | 19 | is licensed under the GPL, see `/usr/share/common-licenses/GPL'. | 19 | License: GPL-3+ |
647 | 20 | This package is free software; you can redistribute it and/or modify | ||
648 | 21 | it under the terms of the GNU General Public License as published by | ||
649 | 22 | the Free Software Foundation; either version 3 of the License, or | ||
650 | 23 | (at your option) any later version. | ||
651 | 24 | . | ||
652 | 25 | This package is distributed in the hope that it will be useful, | ||
653 | 26 | but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
654 | 27 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
655 | 28 | GNU General Public License for more details. | ||
656 | 29 | . | ||
657 | 30 | You should have received a copy of the GNU General Public License | ||
658 | 31 | along with this package; if not, write to the Free Software | ||
659 | 32 | Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA | ||
660 | 33 | . | ||
661 | 34 | On Debian systems, see `/usr/share/common-licenses/GPL-3'. | ||
662 | 20 | 35 | ||
663 | === modified file 'debian/rules' | |||
664 | --- debian/rules 2009-05-07 13:09:38 +0000 | |||
665 | +++ debian/rules 2015-12-05 00:25:32 +0000 | |||
666 | @@ -1,14 +1,6 @@ | |||
667 | 1 | #!/usr/bin/make -f | 1 | #!/usr/bin/make -f |
668 | 2 | 2 | ||
681 | 3 | DEB_PYTHON_SYSTEM := pycentral | 3 | export PYBUILD_NAME=gm-notify |
682 | 4 | export DH_PYCENTRAL=include-links | 4 | %: |
683 | 5 | 5 | dh $@ --with python2 --buildsystem=pybuild | |
684 | 6 | include /usr/share/cdbs/1/rules/debhelper.mk | 6 | |
673 | 7 | include /usr/share/cdbs/1/class/python-distutils.mk | ||
674 | 8 | |||
675 | 9 | DEB_DH_INSTALL_SOURCEDIR := debian/tmp | ||
676 | 10 | DEB_PYTHON_INSTALL_ARGS_ALL := --no-compile --install-layout=deb | ||
677 | 11 | |||
678 | 12 | # Add here any variable or target overrides you need. | ||
679 | 13 | binary-post-install/gm-notify:: | ||
680 | 14 | dh_gconf -p$(cdbs_curpkg) $(DEB_DH_GCONF_ARGS) | ||
685 | 15 | 7 | ||
686 | === added directory 'debian/source' | |||
687 | === added file 'debian/source/format' | |||
688 | --- debian/source/format 1970-01-01 00:00:00 +0000 | |||
689 | +++ debian/source/format 2015-12-05 00:25:32 +0000 | |||
690 | @@ -0,0 +1,1 @@ | |||
691 | 1 | 3.0 (quilt) | ||
692 | 0 | 2 | ||
693 | === modified file 'gm-config.ui' | |||
694 | --- gm-config.ui 2015-12-05 00:25:32 +0000 | |||
695 | +++ gm-config.ui 2015-12-05 00:25:32 +0000 | |||
696 | @@ -1,7 +1,8 @@ | |||
697 | 1 | <?xml version="1.0" encoding="UTF-8"?> | 1 | <?xml version="1.0" encoding="UTF-8"?> |
698 | 2 | <!-- Generated with glade 3.18.3 --> | ||
699 | 2 | <interface> | 3 | <interface> |
702 | 3 | <!-- interface-requires gtk+ 3.0 --> | 4 | <requires lib="gtk+" version="3.10"/> |
703 | 4 | <object class="GtkWindow" id="gmnotify_config_main"> | 5 | <object class="GtkWindow" id="gmnotify_add_account"> |
704 | 5 | <property name="can_focus">False</property> | 6 | <property name="can_focus">False</property> |
705 | 6 | <property name="border_width">10</property> | 7 | <property name="border_width">10</property> |
706 | 7 | <property name="title" translatable="yes">GMail Notifier</property> | 8 | <property name="title" translatable="yes">GMail Notifier</property> |
707 | @@ -36,8 +37,6 @@ | |||
708 | 36 | <packing> | 37 | <packing> |
709 | 37 | <property name="left_attach">0</property> | 38 | <property name="left_attach">0</property> |
710 | 38 | <property name="top_attach">0</property> | 39 | <property name="top_attach">0</property> |
711 | 39 | <property name="width">1</property> | ||
712 | 40 | <property name="height">1</property> | ||
713 | 41 | </packing> | 40 | </packing> |
714 | 42 | </child> | 41 | </child> |
715 | 43 | <child> | 42 | <child> |
716 | @@ -49,8 +48,6 @@ | |||
717 | 49 | <packing> | 48 | <packing> |
718 | 50 | <property name="left_attach">0</property> | 49 | <property name="left_attach">0</property> |
719 | 51 | <property name="top_attach">1</property> | 50 | <property name="top_attach">1</property> |
720 | 52 | <property name="width">1</property> | ||
721 | 53 | <property name="height">1</property> | ||
722 | 54 | </packing> | 51 | </packing> |
723 | 55 | </child> | 52 | </child> |
724 | 56 | <child> | 53 | <child> |
725 | @@ -58,13 +55,10 @@ | |||
726 | 58 | <property name="visible">True</property> | 55 | <property name="visible">True</property> |
727 | 59 | <property name="can_focus">True</property> | 56 | <property name="can_focus">True</property> |
728 | 60 | <property name="invisible_char">â</property> | 57 | <property name="invisible_char">â</property> |
729 | 61 | <property name="invisible_char_set">True</property> | ||
730 | 62 | </object> | 58 | </object> |
731 | 63 | <packing> | 59 | <packing> |
732 | 64 | <property name="left_attach">1</property> | 60 | <property name="left_attach">1</property> |
733 | 65 | <property name="top_attach">0</property> | 61 | <property name="top_attach">0</property> |
734 | 66 | <property name="width">1</property> | ||
735 | 67 | <property name="height">1</property> | ||
736 | 68 | </packing> | 62 | </packing> |
737 | 69 | </child> | 63 | </child> |
738 | 70 | <child> | 64 | <child> |
739 | @@ -73,14 +67,11 @@ | |||
740 | 73 | <property name="can_focus">True</property> | 67 | <property name="can_focus">True</property> |
741 | 74 | <property name="visibility">False</property> | 68 | <property name="visibility">False</property> |
742 | 75 | <property name="invisible_char">â</property> | 69 | <property name="invisible_char">â</property> |
743 | 76 | <property name="invisible_char_set">True</property> | ||
744 | 77 | <signal name="focus-out-event" handler="on_input_password_focus_out_event" swapped="no"/> | 70 | <signal name="focus-out-event" handler="on_input_password_focus_out_event" swapped="no"/> |
745 | 78 | </object> | 71 | </object> |
746 | 79 | <packing> | 72 | <packing> |
747 | 80 | <property name="left_attach">1</property> | 73 | <property name="left_attach">1</property> |
748 | 81 | <property name="top_attach">1</property> | 74 | <property name="top_attach">1</property> |
749 | 82 | <property name="width">1</property> | ||
750 | 83 | <property name="height">1</property> | ||
751 | 84 | </packing> | 75 | </packing> |
752 | 85 | </child> | 76 | </child> |
753 | 86 | <child> | 77 | <child> |
754 | @@ -92,8 +83,6 @@ | |||
755 | 92 | <packing> | 83 | <packing> |
756 | 93 | <property name="left_attach">0</property> | 84 | <property name="left_attach">0</property> |
757 | 94 | <property name="top_attach">2</property> | 85 | <property name="top_attach">2</property> |
758 | 95 | <property name="width">1</property> | ||
759 | 96 | <property name="height">1</property> | ||
760 | 97 | </packing> | 86 | </packing> |
761 | 98 | </child> | 87 | </child> |
762 | 99 | <child> | 88 | <child> |
763 | @@ -105,8 +94,6 @@ | |||
764 | 105 | <packing> | 94 | <packing> |
765 | 106 | <property name="left_attach">1</property> | 95 | <property name="left_attach">1</property> |
766 | 107 | <property name="top_attach">2</property> | 96 | <property name="top_attach">2</property> |
767 | 108 | <property name="width">1</property> | ||
768 | 109 | <property name="height">1</property> | ||
769 | 110 | </packing> | 97 | </packing> |
770 | 111 | </child> | 98 | </child> |
771 | 112 | </object> | 99 | </object> |
772 | @@ -329,44 +316,6 @@ | |||
773 | 329 | <property name="position">2</property> | 316 | <property name="position">2</property> |
774 | 330 | </packing> | 317 | </packing> |
775 | 331 | </child> | 318 | </child> |
776 | 332 | <child> | ||
777 | 333 | <object class="GtkFrame" id="frame_autostart"> | ||
778 | 334 | <property name="visible">True</property> | ||
779 | 335 | <property name="can_focus">False</property> | ||
780 | 336 | <property name="label_xalign">0</property> | ||
781 | 337 | <property name="shadow_type">none</property> | ||
782 | 338 | <child> | ||
783 | 339 | <object class="GtkAlignment" id="alignment4"> | ||
784 | 340 | <property name="visible">True</property> | ||
785 | 341 | <property name="can_focus">False</property> | ||
786 | 342 | <property name="left_padding">12</property> | ||
787 | 343 | <child> | ||
788 | 344 | <object class="GtkCheckButton" id="checkbutton_autostart"> | ||
789 | 345 | <property name="label" translatable="yes">Start automatically on logon</property> | ||
790 | 346 | <property name="visible">True</property> | ||
791 | 347 | <property name="can_focus">True</property> | ||
792 | 348 | <property name="receives_default">False</property> | ||
793 | 349 | <property name="xalign">0.5</property> | ||
794 | 350 | <property name="draw_indicator">True</property> | ||
795 | 351 | </object> | ||
796 | 352 | </child> | ||
797 | 353 | </object> | ||
798 | 354 | </child> | ||
799 | 355 | <child type="label"> | ||
800 | 356 | <object class="GtkLabel" id="label1"> | ||
801 | 357 | <property name="visible">True</property> | ||
802 | 358 | <property name="can_focus">False</property> | ||
803 | 359 | <property name="label" translatable="yes"><b>Autostart:</b></property> | ||
804 | 360 | <property name="use_markup">True</property> | ||
805 | 361 | </object> | ||
806 | 362 | </child> | ||
807 | 363 | </object> | ||
808 | 364 | <packing> | ||
809 | 365 | <property name="expand">False</property> | ||
810 | 366 | <property name="fill">True</property> | ||
811 | 367 | <property name="position">3</property> | ||
812 | 368 | </packing> | ||
813 | 369 | </child> | ||
814 | 370 | </object> | 319 | </object> |
815 | 371 | <packing> | 320 | <packing> |
816 | 372 | <property name="position">1</property> | 321 | <property name="position">1</property> |
817 | 373 | 322 | ||
818 | === added file 'gm-list.ui' | |||
819 | --- gm-list.ui 1970-01-01 00:00:00 +0000 | |||
820 | +++ gm-list.ui 2015-12-05 00:25:32 +0000 | |||
821 | @@ -0,0 +1,224 @@ | |||
822 | 1 | <?xml version="1.0" encoding="UTF-8"?> | ||
823 | 2 | <!-- Generated with glade 3.18.3 --> | ||
824 | 3 | <interface> | ||
825 | 4 | <requires lib="gtk+" version="3.10"/> | ||
826 | 5 | <object class="GtkListStore" id="accounts"> | ||
827 | 6 | <columns> | ||
828 | 7 | <!-- column-name mail --> | ||
829 | 8 | <column type="gchararray"/> | ||
830 | 9 | </columns> | ||
831 | 10 | </object> | ||
832 | 11 | <object class="GtkWindow" id="gmnotify_config_main"> | ||
833 | 12 | <property name="width_request">400</property> | ||
834 | 13 | <property name="height_request">250</property> | ||
835 | 14 | <property name="can_focus">False</property> | ||
836 | 15 | <property name="border_width">10</property> | ||
837 | 16 | <property name="title" translatable="yes">GMail Notifier</property> | ||
838 | 17 | <property name="resizable">False</property> | ||
839 | 18 | <property name="window_position">center</property> | ||
840 | 19 | <property name="default_width">512</property> | ||
841 | 20 | <property name="default_height">512</property> | ||
842 | 21 | <property name="icon_name">evolution</property> | ||
843 | 22 | <property name="gravity">center</property> | ||
844 | 23 | <child> | ||
845 | 24 | <object class="GtkBox" id="box1"> | ||
846 | 25 | <property name="visible">True</property> | ||
847 | 26 | <property name="can_focus">False</property> | ||
848 | 27 | <property name="opacity">0.97999999999999998</property> | ||
849 | 28 | <property name="orientation">vertical</property> | ||
850 | 29 | <child> | ||
851 | 30 | <object class="GtkNotebook" id="notebook_main"> | ||
852 | 31 | <property name="visible">True</property> | ||
853 | 32 | <property name="can_focus">True</property> | ||
854 | 33 | <child> | ||
855 | 34 | <object class="GtkBox" id="box2"> | ||
856 | 35 | <property name="visible">True</property> | ||
857 | 36 | <property name="can_focus">False</property> | ||
858 | 37 | <property name="margin_left">1</property> | ||
859 | 38 | <property name="margin_right">1</property> | ||
860 | 39 | <property name="margin_top">1</property> | ||
861 | 40 | <property name="margin_bottom">1</property> | ||
862 | 41 | <property name="spacing">5</property> | ||
863 | 42 | <child> | ||
864 | 43 | <object class="GtkToolbar" id="add_remove_toolbar"> | ||
865 | 44 | <property name="visible">True</property> | ||
866 | 45 | <property name="can_focus">False</property> | ||
867 | 46 | <property name="orientation">vertical</property> | ||
868 | 47 | <property name="toolbar_style">both</property> | ||
869 | 48 | <child> | ||
870 | 49 | <object class="GtkToolButton" id="add_button"> | ||
871 | 50 | <property name="use_action_appearance">False</property> | ||
872 | 51 | <property name="visible">True</property> | ||
873 | 52 | <property name="can_focus">False</property> | ||
874 | 53 | <property name="has_tooltip">True</property> | ||
875 | 54 | <property name="tooltip_markup" translatable="yes">Add...</property> | ||
876 | 55 | <property name="tooltip_text" translatable="yes">Add...</property> | ||
877 | 56 | <property name="use_underline">True</property> | ||
878 | 57 | <property name="stock_id">gtk-add</property> | ||
879 | 58 | </object> | ||
880 | 59 | <packing> | ||
881 | 60 | <property name="expand">False</property> | ||
882 | 61 | <property name="homogeneous">True</property> | ||
883 | 62 | </packing> | ||
884 | 63 | </child> | ||
885 | 64 | <child> | ||
886 | 65 | <object class="GtkToolButton" id="remove_button"> | ||
887 | 66 | <property name="use_action_appearance">False</property> | ||
888 | 67 | <property name="visible">True</property> | ||
889 | 68 | <property name="can_focus">False</property> | ||
890 | 69 | <property name="has_tooltip">True</property> | ||
891 | 70 | <property name="tooltip_markup" translatable="yes">Remove...</property> | ||
892 | 71 | <property name="tooltip_text" translatable="yes">Remove...</property> | ||
893 | 72 | <property name="use_underline">True</property> | ||
894 | 73 | <property name="stock_id">gtk-remove</property> | ||
895 | 74 | </object> | ||
896 | 75 | <packing> | ||
897 | 76 | <property name="expand">False</property> | ||
898 | 77 | <property name="homogeneous">True</property> | ||
899 | 78 | </packing> | ||
900 | 79 | </child> | ||
901 | 80 | </object> | ||
902 | 81 | <packing> | ||
903 | 82 | <property name="expand">False</property> | ||
904 | 83 | <property name="fill">True</property> | ||
905 | 84 | <property name="position">0</property> | ||
906 | 85 | </packing> | ||
907 | 86 | </child> | ||
908 | 87 | <child> | ||
909 | 88 | <object class="GtkTreeView" id="accounts_treeview"> | ||
910 | 89 | <property name="visible">True</property> | ||
911 | 90 | <property name="can_focus">True</property> | ||
912 | 91 | <property name="margin_left">1</property> | ||
913 | 92 | <property name="margin_right">1</property> | ||
914 | 93 | <property name="margin_top">1</property> | ||
915 | 94 | <property name="margin_bottom">1</property> | ||
916 | 95 | <property name="hscroll_policy">natural</property> | ||
917 | 96 | <property name="vscroll_policy">natural</property> | ||
918 | 97 | <property name="model">accounts</property> | ||
919 | 98 | <property name="search_column">1</property> | ||
920 | 99 | <property name="show_expanders">False</property> | ||
921 | 100 | <property name="enable_grid_lines">horizontal</property> | ||
922 | 101 | <child internal-child="selection"> | ||
923 | 102 | <object class="GtkTreeSelection" id="treeview-selection"> | ||
924 | 103 | <property name="mode">browse</property> | ||
925 | 104 | </object> | ||
926 | 105 | </child> | ||
927 | 106 | <child> | ||
928 | 107 | <object class="GtkTreeViewColumn" id="treeviewcolumn_name"> | ||
929 | 108 | <property name="sizing">fixed</property> | ||
930 | 109 | <property name="title" translatable="yes">Accounts List</property> | ||
931 | 110 | <child> | ||
932 | 111 | <object class="GtkCellRendererText" id="cellrenderertext_name"/> | ||
933 | 112 | <attributes> | ||
934 | 113 | <attribute name="text">0</attribute> | ||
935 | 114 | </attributes> | ||
936 | 115 | </child> | ||
937 | 116 | </object> | ||
938 | 117 | </child> | ||
939 | 118 | </object> | ||
940 | 119 | <packing> | ||
941 | 120 | <property name="expand">True</property> | ||
942 | 121 | <property name="fill">True</property> | ||
943 | 122 | <property name="position">1</property> | ||
944 | 123 | </packing> | ||
945 | 124 | </child> | ||
946 | 125 | </object> | ||
947 | 126 | </child> | ||
948 | 127 | <child type="tab"> | ||
949 | 128 | <object class="GtkLabel" id="label_accounts"> | ||
950 | 129 | <property name="visible">True</property> | ||
951 | 130 | <property name="can_focus">False</property> | ||
952 | 131 | <property name="label" translatable="yes">Accounts</property> | ||
953 | 132 | </object> | ||
954 | 133 | <packing> | ||
955 | 134 | <property name="tab_fill">False</property> | ||
956 | 135 | </packing> | ||
957 | 136 | </child> | ||
958 | 137 | <child> | ||
959 | 138 | <object class="GtkFrame" id="frame_autostart"> | ||
960 | 139 | <property name="visible">True</property> | ||
961 | 140 | <property name="can_focus">False</property> | ||
962 | 141 | <property name="label_xalign">0</property> | ||
963 | 142 | <property name="shadow_type">none</property> | ||
964 | 143 | <child> | ||
965 | 144 | <object class="GtkAlignment" id="alignment4"> | ||
966 | 145 | <property name="visible">True</property> | ||
967 | 146 | <property name="can_focus">False</property> | ||
968 | 147 | <property name="left_padding">12</property> | ||
969 | 148 | <child> | ||
970 | 149 | <object class="GtkCheckButton" id="checkbutton_autostart"> | ||
971 | 150 | <property name="label" translatable="yes">Start automatically on logon</property> | ||
972 | 151 | <property name="visible">True</property> | ||
973 | 152 | <property name="can_focus">True</property> | ||
974 | 153 | <property name="receives_default">False</property> | ||
975 | 154 | <property name="valign">start</property> | ||
976 | 155 | <property name="xalign">0</property> | ||
977 | 156 | <property name="yalign">0</property> | ||
978 | 157 | <property name="draw_indicator">True</property> | ||
979 | 158 | </object> | ||
980 | 159 | </child> | ||
981 | 160 | </object> | ||
982 | 161 | </child> | ||
983 | 162 | <child type="label"> | ||
984 | 163 | <object class="GtkLabel" id="label_autostart"> | ||
985 | 164 | <property name="visible">True</property> | ||
986 | 165 | <property name="can_focus">False</property> | ||
987 | 166 | <property name="label" translatable="yes"><b>Autostart:</b></property> | ||
988 | 167 | <property name="use_markup">True</property> | ||
989 | 168 | </object> | ||
990 | 169 | </child> | ||
991 | 170 | </object> | ||
992 | 171 | <packing> | ||
993 | 172 | <property name="position">1</property> | ||
994 | 173 | </packing> | ||
995 | 174 | </child> | ||
996 | 175 | <child type="tab"> | ||
997 | 176 | <object class="GtkLabel" id="label_preferences"> | ||
998 | 177 | <property name="visible">True</property> | ||
999 | 178 | <property name="can_focus">False</property> | ||
1000 | 179 | <property name="label" translatable="yes">Preferences</property> | ||
1001 | 180 | </object> | ||
1002 | 181 | <packing> | ||
1003 | 182 | <property name="position">1</property> | ||
1004 | 183 | <property name="tab_fill">False</property> | ||
1005 | 184 | </packing> | ||
1006 | 185 | </child> | ||
1007 | 186 | <child> | ||
1008 | 187 | <placeholder/> | ||
1009 | 188 | </child> | ||
1010 | 189 | <child type="tab"> | ||
1011 | 190 | <placeholder/> | ||
1012 | 191 | </child> | ||
1013 | 192 | </object> | ||
1014 | 193 | <packing> | ||
1015 | 194 | <property name="expand">True</property> | ||
1016 | 195 | <property name="fill">True</property> | ||
1017 | 196 | <property name="position">0</property> | ||
1018 | 197 | </packing> | ||
1019 | 198 | </child> | ||
1020 | 199 | <child> | ||
1021 | 200 | <object class="GtkButton" id="button_ok"> | ||
1022 | 201 | <property name="label">gtk-ok</property> | ||
1023 | 202 | <property name="width_request">100</property> | ||
1024 | 203 | <property name="visible">True</property> | ||
1025 | 204 | <property name="can_focus">True</property> | ||
1026 | 205 | <property name="receives_default">True</property> | ||
1027 | 206 | <property name="halign">end</property> | ||
1028 | 207 | <property name="valign">center</property> | ||
1029 | 208 | <property name="margin_left">5</property> | ||
1030 | 209 | <property name="margin_right">5</property> | ||
1031 | 210 | <property name="margin_top">5</property> | ||
1032 | 211 | <property name="margin_bottom">5</property> | ||
1033 | 212 | <property name="use_stock">True</property> | ||
1034 | 213 | <property name="always_show_image">True</property> | ||
1035 | 214 | </object> | ||
1036 | 215 | <packing> | ||
1037 | 216 | <property name="expand">False</property> | ||
1038 | 217 | <property name="fill">True</property> | ||
1039 | 218 | <property name="position">1</property> | ||
1040 | 219 | </packing> | ||
1041 | 220 | </child> | ||
1042 | 221 | </object> | ||
1043 | 222 | </child> | ||
1044 | 223 | </object> | ||
1045 | 224 | </interface> | ||
1046 | 0 | 225 | ||
1047 | === modified file 'gm-notify' | |||
1048 | --- gm-notify 2015-12-05 00:25:32 +0000 | |||
1049 | +++ gm-notify 2015-12-05 00:25:32 +0000 | |||
1050 | @@ -1,10 +1,11 @@ | |||
1051 | 1 | #!/usr/bin/python | 1 | #!/usr/bin/python |
1052 | 2 | # -*- coding: utf-8 -*- | 2 | # -*- coding: utf-8 -*- |
1053 | 3 | 3 | ||
1055 | 4 | # gm-notify v0.10.3 | 4 | # gm-notify v1.0 |
1056 | 5 | # a simple and lightweight GMail-Notifier for ubuntu and notify-osd | 5 | # a simple and lightweight GMail-Notifier for ubuntu and notify-osd |
1057 | 6 | # | 6 | # |
1058 | 7 | # Copyright (c) 2009-2010, Alexander Hungenberg <alexander.hungenberg@gmail.com> | 7 | # Copyright (c) 2009-2010, Alexander Hungenberg <alexander.hungenberg@gmail.com> |
1059 | 8 | # Copyright (c) 2015, Mateusz Balbus <balbusm@gmail.com> | ||
1060 | 8 | # | 9 | # |
1061 | 9 | # This program is free software: you can redistribute it and/or modify | 10 | # This program is free software: you can redistribute it and/or modify |
1062 | 10 | # it under the terms of the GNU General Public License as published by | 11 | # it under the terms of the GNU General Public License as published by |
1063 | @@ -27,7 +28,9 @@ | |||
1064 | 27 | import gettext | 28 | import gettext |
1065 | 28 | import webbrowser | 29 | import webbrowser |
1066 | 29 | 30 | ||
1068 | 30 | from gi.repository import Gio, GLib, MessagingMenu, Notify | 31 | import gi |
1069 | 32 | gi.require_version('Gst', '1.0') | ||
1070 | 33 | from gi.repository import Gio, GLib, Gst, MessagingMenu, Notify | ||
1071 | 31 | 34 | ||
1072 | 32 | from twisted.internet import gireactor | 35 | from twisted.internet import gireactor |
1073 | 33 | gireactor.install() | 36 | gireactor.install() |
1074 | @@ -35,6 +38,7 @@ | |||
1075 | 35 | from twisted.words.protocols.jabber import jid | 38 | from twisted.words.protocols.jabber import jid |
1076 | 36 | 39 | ||
1077 | 37 | from gtalk import MailChecker | 40 | from gtalk import MailChecker |
1078 | 41 | import account_settings_provider | ||
1079 | 38 | import gm_notify_keyring as keyring | 42 | import gm_notify_keyring as keyring |
1080 | 39 | 43 | ||
1081 | 40 | _ = gettext.translation('gm-notify', fallback=True).ugettext | 44 | _ = gettext.translation('gm-notify', fallback=True).ugettext |
1082 | @@ -43,6 +47,10 @@ | |||
1083 | 43 | 47 | ||
1084 | 44 | MAILBOXES_URLS = { "inbox": "" } | 48 | MAILBOXES_URLS = { "inbox": "" } |
1085 | 45 | 49 | ||
1086 | 50 | GMAIL_DOMAINS = ['gmail.com','googlemail.com'] | ||
1087 | 51 | |||
1088 | 52 | EMAIL_RETRIEVAL_ERROR = "--retrieval-error" | ||
1089 | 53 | |||
1090 | 46 | class PathNotFound(Exception): pass | 54 | class PathNotFound(Exception): pass |
1091 | 47 | 55 | ||
1092 | 48 | def get_executable_path(name): | 56 | def get_executable_path(name): |
1093 | @@ -54,28 +62,15 @@ | |||
1094 | 54 | if os.path.exists(path) and os.access(path, os.X_OK): return path | 62 | if os.path.exists(path) and os.access(path, os.X_OK): return path |
1095 | 55 | raise PathNotFound("%s not found" % name) | 63 | raise PathNotFound("%s not found" % name) |
1096 | 56 | 64 | ||
1119 | 57 | def play_sound(name): | 65 | class Account: |
1120 | 58 | '''Spawns a canberra-gtk-play process to play the sound''' | 66 | def __init__(self): |
1121 | 59 | if name is None: | 67 | self.username = None |
1122 | 60 | return | 68 | self.client = None |
1123 | 61 | player_path = "/usr/bin/canberra-gtk-play" | 69 | self.soundfile = None |
1124 | 62 | # Not installed? | 70 | self.ignore_inbox = None |
1125 | 63 | if not os.path.exists(player_path): | 71 | self.domain = None |
1126 | 64 | return | 72 | self.labels = None |
1127 | 65 | command = [player_path] | 73 | self.use_mail_client = None |
1106 | 66 | # File exists, so use the file flag | ||
1107 | 67 | if os.path.exists(name): | ||
1108 | 68 | command.extend(["-f", name]) | ||
1109 | 69 | # Assume it is a sound id | ||
1110 | 70 | else: | ||
1111 | 71 | command.extend(["-i", name]) | ||
1112 | 72 | try: | ||
1113 | 73 | result = GLib.spawn_async(command) | ||
1114 | 74 | except: | ||
1115 | 75 | return | ||
1116 | 76 | # Does nothing but the documentation says to do it anyway | ||
1117 | 77 | if len(result) > 0: | ||
1118 | 78 | GLib.spawn_close_pid(result[0]) | ||
1128 | 79 | 74 | ||
1129 | 80 | class CheckMail(Gio.Application): | 75 | class CheckMail(Gio.Application): |
1130 | 81 | def __init__(self): | 76 | def __init__(self): |
1131 | @@ -84,14 +79,36 @@ | |||
1132 | 84 | super(CheckMail, self).__init__(application_id="net.launchpad.gm-notify", | 79 | super(CheckMail, self).__init__(application_id="net.launchpad.gm-notify", |
1133 | 85 | flags=Gio.ApplicationFlags.FLAGS_NONE) | 80 | flags=Gio.ApplicationFlags.FLAGS_NONE) |
1134 | 86 | 81 | ||
1135 | 82 | self.player = None | ||
1136 | 87 | self._has_activated = False | 83 | self._has_activated = False |
1137 | 88 | self._counts = {} | 84 | self._counts = {} |
1138 | 89 | self.connect("activate", self.on_activate) | 85 | self.connect("activate", self.on_activate) |
1139 | 90 | 86 | ||
1140 | 87 | def play_sound(self, name): | ||
1141 | 88 | '''Uses GSound to play the music''' | ||
1142 | 89 | if name is None: | ||
1143 | 90 | return | ||
1144 | 91 | |||
1145 | 92 | if not self.player: | ||
1146 | 93 | Gst.init() | ||
1147 | 94 | self.player = Gst.ElementFactory.make("playbin", "player") | ||
1148 | 95 | |||
1149 | 96 | self.player.set_property("uri", "file://" + name) | ||
1150 | 97 | result = self.player.set_state(Gst.State.PLAYING) | ||
1151 | 98 | if result == Gst.StateChangeReturn.FAILURE: | ||
1152 | 99 | print("Unable to play " + name) | ||
1153 | 100 | |||
1154 | 91 | def on_remote_quit(self, action, args): | 101 | def on_remote_quit(self, action, args): |
1155 | 92 | '''Stops the application when the "remote-quit" action is activated''' | 102 | '''Stops the application when the "remote-quit" action is activated''' |
1156 | 93 | reactor.stop() | 103 | reactor.stop() |
1157 | 94 | 104 | ||
1158 | 105 | def run(self, args): | ||
1159 | 106 | self.register(); | ||
1160 | 107 | if self.get_is_remote(): | ||
1161 | 108 | self.activate() | ||
1162 | 109 | else: | ||
1163 | 110 | super(CheckMail, self).run(args) | ||
1164 | 111 | |||
1165 | 95 | def on_activate(self, app): | 112 | def on_activate(self, app): |
1166 | 96 | '''When first receiving the activate signal, initialize the primary | 113 | '''When first receiving the activate signal, initialize the primary |
1167 | 97 | instance. On subsequent activate signals, we are being activated from a | 114 | instance. On subsequent activate signals, we are being activated from a |
1168 | @@ -113,8 +130,8 @@ | |||
1169 | 113 | sys.exit(-1) | 130 | sys.exit(-1) |
1170 | 114 | 131 | ||
1171 | 115 | keys = keyring.Keyring("GMail", "mail.google.com", "http") | 132 | keys = keyring.Keyring("GMail", "mail.google.com", "http") |
1174 | 116 | if keys.has_credentials(): | 133 | if keys.has_any_credentials(): |
1175 | 117 | self.creds = keys.get_credentials() | 134 | creds = keys.get_all_credentials() |
1176 | 118 | else: | 135 | else: |
1177 | 119 | print("Failed to get credentials") | 136 | print("Failed to get credentials") |
1178 | 120 | # Start gm-notify-config if no credentials are found | 137 | # Start gm-notify-config if no credentials are found |
1179 | @@ -122,110 +139,140 @@ | |||
1180 | 122 | subprocess.call(get_executable_path("gm-notify-config")) | 139 | subprocess.call(get_executable_path("gm-notify-config")) |
1181 | 123 | except PathNotFound: | 140 | except PathNotFound: |
1182 | 124 | print(_("gm-notify-config utility was not found")) | 141 | print(_("gm-notify-config utility was not found")) |
1183 | 142 | |||
1184 | 125 | sys.exit(-1) | 143 | sys.exit(-1) |
1185 | 126 | 144 | ||
1186 | 127 | # check if we use Google Apps to start the correct webinterface | ||
1187 | 128 | gmail_domains = ['gmail.com','googlemail.com'] | ||
1188 | 129 | self.jid = jid.JID(self.creds[0]) | ||
1189 | 130 | if self.jid.host in gmail_domains: | ||
1190 | 131 | self.domain = None | ||
1191 | 132 | else: | ||
1192 | 133 | self.domain = self.jid.host | ||
1193 | 134 | |||
1194 | 135 | self.client = Gio.Settings("net.launchpad.gm-notify") | ||
1195 | 136 | |||
1196 | 137 | # Set up the sound file | ||
1197 | 138 | self._soundfile = self.client.get_string("soundfile") | ||
1198 | 139 | if self._soundfile == '': | ||
1199 | 140 | self._soundfile = "message-new-instant" | ||
1200 | 141 | if not self.client.get_boolean("play-sound"): | ||
1201 | 142 | self._soundfile = None | ||
1202 | 143 | |||
1203 | 144 | # Messaging Menu integration | 145 | # Messaging Menu integration |
1204 | 145 | self._m_menu = MessagingMenu.App.new("gm-notify.desktop") | 146 | self._m_menu = MessagingMenu.App.new("gm-notify.desktop") |
1205 | 146 | self._m_menu.register() | 147 | self._m_menu.register() |
1206 | 147 | self._m_menu.connect("activate-source", self.source_clicked) | 148 | self._m_menu.connect("activate-source", self.source_clicked) |
1207 | 148 | 149 | ||
1218 | 149 | # Read ignore-inbox value. If true you will only receive notifications | 150 | self.accounts = self.init_accounts(creds) |
1219 | 150 | # about configured labels | 151 | |
1220 | 151 | self.ignore_inbox = self.client.get_boolean("ignore-inbox") | 152 | |
1221 | 152 | 153 | def init_accounts(self, creds): | |
1222 | 153 | # Retrieve the mailbox we're gonna check | 154 | accounts = {} |
1223 | 154 | self.mailboxes = self.client.get_strv("mailboxes") | 155 | for credentails in creds: |
1224 | 155 | self.mailboxes.insert(0, "inbox") | 156 | account = Account() |
1225 | 156 | self.checker = MailChecker(self.jid, self.creds[1], self.mailboxes[1:], self.new_mail, self.update_count) | 157 | account.username = credentails.username |
1226 | 157 | self.checker.connect() | 158 | local_jid = jid.JID(account.username) |
1227 | 158 | 159 | # check if we use Google Apps to start the correct webinterface | |
1228 | 160 | if local_jid.host not in GMAIL_DOMAINS: | ||
1229 | 161 | account.domain = local_jid.host | ||
1230 | 162 | |||
1231 | 163 | client = account_settings_provider.create_settings_provider(account.username) | ||
1232 | 164 | account.client = client | ||
1233 | 165 | |||
1234 | 166 | account.use_mail_client = client.retrieve_use_mail_client() | ||
1235 | 167 | |||
1236 | 168 | # Set up the sound file | ||
1237 | 169 | if client.retrieve_sound_enabled(): | ||
1238 | 170 | account.soundfile = client.retrieve_sound_file("message-new-instant") | ||
1239 | 171 | # Read ignore-inbox value. If true you will only receive notifications | ||
1240 | 172 | # about configured labels | ||
1241 | 173 | account.ignore_inbox = client.retrieve_ignore_inbox() | ||
1242 | 174 | |||
1243 | 175 | # Retrieve the mailbox we're gonna check | ||
1244 | 176 | account.labels = client.retrieve_labels() | ||
1245 | 177 | account.labels.insert(0, "inbox") | ||
1246 | 178 | account.checker = MailChecker(local_jid, credentails.password, client, account.labels[1:], self.new_mail, self.update_count) | ||
1247 | 179 | account.checker.setOnConnectionErrorCB(self.checker_connection_error) | ||
1248 | 180 | account.checker.setOnAuthFailed(self.checker_auth_failed) | ||
1249 | 181 | account.checker.setOnAuthSucceeded(self.checker_auth_succeeded) | ||
1250 | 182 | account.checker.connect() | ||
1251 | 183 | |||
1252 | 184 | accounts[credentails.username] = account | ||
1253 | 185 | return accounts | ||
1254 | 186 | |||
1255 | 159 | def indicator_clicked(self): | 187 | def indicator_clicked(self): |
1256 | 160 | '''called when "Google Mail" is clicked in indicator-messages and | 188 | '''called when "Google Mail" is clicked in indicator-messages and |
1257 | 161 | performs a Mail Check''' | 189 | performs a Mail Check''' |
1262 | 162 | for label in self.mailboxes: | 190 | for username, account in self.accounts.items(): |
1263 | 163 | self.remove_attention(label) | 191 | for label in account.labels: |
1264 | 164 | 192 | self.remove_attention(self.compose_id(username, label)) | |
1265 | 165 | self.checker.queryInbox() | 193 | |
1266 | 194 | for username, account in self.accounts.items(): | ||
1267 | 195 | account.checker.queryInbox() | ||
1268 | 196 | |||
1269 | 197 | def compose_label(self, username, label): | ||
1270 | 198 | return "%s (%s)" % (username, label) | ||
1271 | 199 | |||
1272 | 200 | def compose_id(self, username, label): | ||
1273 | 201 | return "%s&%s" % (username, label) | ||
1274 | 202 | |||
1275 | 203 | def decompose_id(self, id): | ||
1276 | 204 | decomposed = id.split("&") | ||
1277 | 205 | return (decomposed[0], decomposed[1]) | ||
1278 | 166 | 206 | ||
1279 | 167 | def remove_attention(self, label): | 207 | def remove_attention(self, label): |
1280 | 168 | '''Removes attention from the label source if it exists''' | 208 | '''Removes attention from the label source if it exists''' |
1281 | 169 | if self._m_menu.has_source(label): | 209 | if self._m_menu.has_source(label): |
1282 | 170 | self._m_menu.remove_attention(label) | 210 | self._m_menu.remove_attention(label) |
1283 | 171 | 211 | ||
1285 | 172 | def has_source(self, label): | 212 | def has_source(self, username, label): |
1286 | 173 | '''Returns true if we have this label, or if we don't and it is in our | 213 | '''Returns true if we have this label, or if we don't and it is in our |
1287 | 174 | mailboxes list, create it''' | 214 | mailboxes list, create it''' |
1289 | 175 | if label == "inbox" and self.ignore_inbox: | 215 | account = self.accounts[username] |
1290 | 216 | if label == "inbox" and account.ignore_inbox: | ||
1291 | 176 | return False | 217 | return False |
1294 | 177 | elif label in self.mailboxes: | 218 | elif label in account.labels: |
1295 | 178 | if not self._m_menu.has_source(label): | 219 | mail_label = self.compose_id(username, label) |
1296 | 220 | if not self._m_menu.has_source(mail_label): | ||
1297 | 179 | if label in MAILBOXES_NAMES: | 221 | if label in MAILBOXES_NAMES: |
1298 | 180 | name = MAILBOXES_NAMES[label] | 222 | name = MAILBOXES_NAMES[label] |
1299 | 181 | else: | 223 | else: |
1300 | 182 | name = label | 224 | name = label |
1301 | 225 | name_label = self.compose_label(username, name) | ||
1302 | 183 | if label == "inbox": | 226 | if label == "inbox": |
1304 | 184 | self._m_menu.insert_source_with_string(0, label, None, name, _("empty")) | 227 | self._m_menu.insert_source_with_string(0, mail_label, None, name_label, _("empty")) |
1305 | 185 | else: | 228 | else: |
1309 | 186 | self._m_menu.append_source_with_string(label, None, name, _("empty")) | 229 | self._m_menu.append_source_with_string(mail_label, None, name_label, _("empty")) |
1310 | 187 | if label in self._counts: | 230 | if mail_label in self._counts: |
1311 | 188 | self._m_menu.set_source_count(label, self._counts[label]) | 231 | self._m_menu.set_source_count(mail_label, self._counts[mail_label]) |
1312 | 189 | return True | 232 | return True |
1313 | 190 | else: | 233 | else: |
1314 | 191 | return False | 234 | return False |
1315 | 192 | 235 | ||
1317 | 193 | def update_count(self, count): | 236 | def update_count(self, username, count): |
1318 | 194 | '''Updates the count for all the mailboxes''' | 237 | '''Updates the count for all the mailboxes''' |
1319 | 238 | account = self.accounts[username] | ||
1320 | 195 | for mailbox in count.iteritems(): | 239 | for mailbox in count.iteritems(): |
1322 | 196 | if mailbox[0] == "inbox" and self.ignore_inbox: | 240 | if mailbox[0] == "inbox" and account.ignore_inbox: |
1323 | 197 | continue | 241 | continue |
1324 | 198 | 242 | ||
1326 | 199 | if self.has_source(mailbox[0]): | 243 | if self.has_source(username, mailbox[0]): |
1327 | 200 | # Get the last count | 244 | # Get the last count |
1328 | 201 | last_count = 0 | 245 | last_count = 0 |
1331 | 202 | if mailbox[0] in self._counts: | 246 | mail_label = self.compose_id(username, mailbox[0]) |
1332 | 203 | last_count = self._counts[mailbox[0]] | 247 | if mail_label in self._counts: |
1333 | 248 | last_count = self._counts[mail_label] | ||
1334 | 204 | current_count = int(mailbox[1]) | 249 | current_count = int(mailbox[1]) |
1335 | 205 | 250 | ||
1336 | 206 | # Remove attention if the count has decreased | 251 | # Remove attention if the count has decreased |
1337 | 207 | if last_count > current_count: | 252 | if last_count > current_count: |
1339 | 208 | self._m_menu.remove_attention(mailbox[0]) | 253 | self._m_menu.remove_attention(mail_label) |
1340 | 209 | if current_count > 0: | 254 | if current_count > 0: |
1342 | 210 | self._m_menu.set_source_count(mailbox[0], current_count) | 255 | self._m_menu.set_source_count(mail_label, current_count) |
1343 | 211 | # Remove the source if 0 messages, to save space | 256 | # Remove the source if 0 messages, to save space |
1344 | 212 | else: | 257 | else: |
1347 | 213 | self._m_menu.remove_source(mailbox[0]) | 258 | self._m_menu.remove_source(mail_label) |
1348 | 214 | self._counts[mailbox[0]] = current_count | 259 | self._counts[mail_label] = current_count |
1349 | 215 | 260 | ||
1351 | 216 | def new_mail(self, mails): | 261 | def new_mail(self, username, mails): |
1352 | 217 | '''Takes mailbox name and titles of mails, to display notification and add indicators''' | 262 | '''Takes mailbox name and titles of mails, to display notification and add indicators''' |
1353 | 263 | account = self.accounts[username] | ||
1354 | 218 | text = "" | 264 | text = "" |
1355 | 219 | # aggregate the titles of the messages... cut the string if longer than 30 chars | 265 | # aggregate the titles of the messages... cut the string if longer than 30 chars |
1356 | 220 | for mail in mails: | 266 | for mail in mails: |
1357 | 221 | got_label = False | 267 | got_label = False |
1358 | 222 | for label in mail['labels']: | 268 | for label in mail['labels']: |
1359 | 223 | if label == u"^i": label = "inbox" | 269 | if label == u"^i": label = "inbox" |
1361 | 224 | if label == "inbox" and self.ignore_inbox: | 270 | if label == "inbox" and account.ignore_inbox: |
1362 | 225 | continue | 271 | continue |
1364 | 226 | if self.has_source(label): | 272 | if self.has_source(username, label): |
1365 | 227 | got_label = True | 273 | got_label = True |
1367 | 228 | self._m_menu.draw_attention(label) | 274 | mail_label = self.compose_id(username, label) |
1368 | 275 | self._m_menu.draw_attention(mail_label) | ||
1369 | 229 | if not got_label: continue | 276 | if not got_label: continue |
1370 | 230 | 277 | ||
1371 | 231 | if "sender_name" in mail: text += mail['sender_name'] + ":\n" | 278 | if "sender_name" in mail: text += mail['sender_name'] + ":\n" |
1372 | @@ -242,32 +289,75 @@ | |||
1373 | 242 | text += "- " + title + "\n" | 289 | text += "- " + title + "\n" |
1374 | 243 | 290 | ||
1375 | 244 | if text: | 291 | if text: |
1378 | 245 | self.showNotification(_("Incoming message"), text.strip("\n")) | 292 | self.show_notification("{0}".format(username), text.strip("\n")) |
1379 | 246 | play_sound(self._soundfile) | 293 | self.play_sound(self.accounts[username].soundfile) |
1380 | 247 | 294 | ||
1381 | 248 | def source_clicked(self, app, source_id): | 295 | def source_clicked(self, app, source_id): |
1382 | 249 | '''called when a label is clicked in the indicator-applet and opens the corresponding gmail page''' | 296 | '''called when a label is clicked in the indicator-applet and opens the corresponding gmail page''' |
1392 | 250 | if self.domain: | 297 | # TODO: missing username |
1393 | 251 | url = "https://mail.google.com/a/"+self.domain+"/" | 298 | username, label = self.decompose_id(source_id) |
1394 | 252 | else: | 299 | if label == EMAIL_RETRIEVAL_ERROR: |
1395 | 253 | url = "https://mail.google.com/mail/" | 300 | return |
1396 | 254 | 301 | account = self.accounts[username] | |
1388 | 255 | try: | ||
1389 | 256 | url += "#%s" % MAILBOXES_URLS[source_id] | ||
1390 | 257 | except KeyError: | ||
1391 | 258 | url += "#label/%s" % source_id | ||
1397 | 259 | 302 | ||
1398 | 260 | # Open mail client | 303 | # Open mail client |
1400 | 261 | if self.client.get_boolean("openclient"): | 304 | if account.use_mail_client: |
1401 | 262 | try: | 305 | try: |
1402 | 263 | info = Gio.AppInfo.get_default_for_type("x-scheme-handler/mailto", False) | 306 | info = Gio.AppInfo.get_default_for_type("x-scheme-handler/mailto", False) |
1403 | 264 | info.launch(None, None) | 307 | info.launch(None, None) |
1404 | 265 | except: | 308 | except: |
1405 | 266 | pass | 309 | pass |
1406 | 267 | else: | 310 | else: |
1407 | 311 | url = self.prep_url(account, label) | ||
1408 | 268 | webbrowser.open(url) | 312 | webbrowser.open(url) |
1409 | 269 | 313 | ||
1411 | 270 | def showNotification(self, title, message): | 314 | def prep_url(self, account, label): |
1412 | 315 | url_domain = self.prep_url_domain(account.domain) | ||
1413 | 316 | url_label = self.prep_url_label(label) | ||
1414 | 317 | |||
1415 | 318 | return ("https://accounts.google.com/AccountChooser?" | ||
1416 | 319 | "Email={0}" | ||
1417 | 320 | "&continue=https%3A%2F%2Fmail.google.com%2Fmail%2F{1}" | ||
1418 | 321 | "&service=mail" | ||
1419 | 322 | "&hd={2}").format(account.username, url_label, url_domain) | ||
1420 | 323 | |||
1421 | 324 | def prep_url_label(self, label): | ||
1422 | 325 | if label in MAILBOXES_URLS: | ||
1423 | 326 | return "%23{0}".format(MAILBOXES_URLS[label]) | ||
1424 | 327 | else: | ||
1425 | 328 | return "%23label%2F{0}".format(label) | ||
1426 | 329 | |||
1427 | 330 | def prep_url_domain(self, domain): | ||
1428 | 331 | if domain: | ||
1429 | 332 | return domain | ||
1430 | 333 | else: | ||
1431 | 334 | return "default" | ||
1432 | 335 | |||
1433 | 336 | def checker_auth_succeeded(self, username): | ||
1434 | 337 | error_label = self.compose_id(username, EMAIL_RETRIEVAL_ERROR) | ||
1435 | 338 | self._m_menu.remove_source(error_label) | ||
1436 | 339 | |||
1437 | 340 | def checker_connection_error(self, username, error): | ||
1438 | 341 | self.check_failed(username, _("Cannot retrieve emails")) | ||
1439 | 342 | |||
1440 | 343 | def checker_auth_failed(self, username, error): | ||
1441 | 344 | self.check_failed(username, _("Authentication failed")) | ||
1442 | 345 | |||
1443 | 346 | def check_failed(self, username, feed): | ||
1444 | 347 | |||
1445 | 348 | for id in self._counts: | ||
1446 | 349 | local_username, local_label = self.decompose_id(id) | ||
1447 | 350 | if local_username == username: | ||
1448 | 351 | self._m_menu.remove_source(id) | ||
1449 | 352 | |||
1450 | 353 | error_id = self.compose_id(username, EMAIL_RETRIEVAL_ERROR) | ||
1451 | 354 | if self._m_menu.has_source(error_id): | ||
1452 | 355 | return | ||
1453 | 356 | self._m_menu.append_source(error_id, None, username + _(" - Cannot retrieve emails")) | ||
1454 | 357 | n = Notify.Notification.new(_("Error for ") + username, feed, "messagebox_critical") | ||
1455 | 358 | n.show() | ||
1456 | 359 | |||
1457 | 360 | def show_notification(self, title, message): | ||
1458 | 271 | '''takes a title and a message to display the email notification. Returns the | 361 | '''takes a title and a message to display the email notification. Returns the |
1459 | 272 | created notification object''' | 362 | created notification object''' |
1460 | 273 | 363 | ||
1461 | @@ -276,6 +366,13 @@ | |||
1462 | 276 | 366 | ||
1463 | 277 | return n | 367 | return n |
1464 | 278 | 368 | ||
1465 | 369 | def shutdown(self): | ||
1466 | 370 | if self.accounts: | ||
1467 | 371 | for username, account in self.accounts.items(): | ||
1468 | 372 | if account.checker : account.checker.die() | ||
1469 | 373 | |||
1470 | 279 | cm = CheckMail() | 374 | cm = CheckMail() |
1471 | 280 | reactor.registerGApplication(cm) | 375 | reactor.registerGApplication(cm) |
1472 | 376 | reactor.addSystemEventTrigger('before', 'shutdown', cm.shutdown) | ||
1473 | 281 | reactor.run() | 377 | reactor.run() |
1474 | 378 | |||
1475 | 282 | 379 | ||
1476 | === modified file 'gm-notify-config' | |||
1477 | --- gm-notify-config 2015-12-05 00:25:32 +0000 | |||
1478 | +++ gm-notify-config 2015-12-05 00:25:32 +0000 | |||
1479 | @@ -1,10 +1,11 @@ | |||
1480 | 1 | #!/usr/bin/env python | 1 | #!/usr/bin/env python |
1481 | 2 | # -*- coding: utf-8 -*- | 2 | # -*- coding: utf-8 -*- |
1482 | 3 | 3 | ||
1484 | 4 | # gm-notify-config v0.10.3 | 4 | # gm-notify-config v1.0 |
1485 | 5 | # GMail Notifier Configuration Utility | 5 | # GMail Notifier Configuration Utility |
1486 | 6 | # | 6 | # |
1487 | 7 | # Copyright (c) 2009-2010, Alexander Hungenberg <alexander.hungenberg@gmail.com> | 7 | # Copyright (c) 2009-2010, Alexander Hungenberg <alexander.hungenberg@gmail.com> |
1488 | 8 | # Copyright (c) 2015, Mateusz Balbus <balbusm@gmail.com> | ||
1489 | 8 | # | 9 | # |
1490 | 9 | # This program is free software: you can redistribute it and/or modify | 10 | # This program is free software: you can redistribute it and/or modify |
1491 | 10 | # it under the terms of the GNU General Public License as published by | 11 | # it under the terms of the GNU General Public License as published by |
1492 | @@ -27,15 +28,17 @@ | |||
1493 | 27 | import subprocess | 28 | import subprocess |
1494 | 28 | import shutil | 29 | import shutil |
1495 | 29 | 30 | ||
1497 | 30 | from gi.repository import Gio, Gtk | 31 | from gi.repository import Gio, Gtk, Gdk |
1498 | 31 | 32 | ||
1499 | 32 | from twisted.internet import gtk3reactor | 33 | from twisted.internet import gtk3reactor |
1500 | 33 | gtk3reactor.install() | 34 | gtk3reactor.install() |
1501 | 34 | from twisted.internet import reactor | 35 | from twisted.internet import reactor |
1502 | 35 | from twisted.words.protocols.jabber import jid | ||
1503 | 36 | 36 | ||
1504 | 37 | import gm_notify_keyring as keyring | 37 | import gm_notify_keyring as keyring |
1506 | 38 | from gtalk import MailChecker | 38 | from gm_notify_keyring import Credentials |
1507 | 39 | from account_config import AccountConfig | ||
1508 | 40 | import account_settings_provider | ||
1509 | 41 | |||
1510 | 39 | 42 | ||
1511 | 40 | _ = gettext.translation('gm-notify', fallback=True).ugettext | 43 | _ = gettext.translation('gm-notify', fallback=True).ugettext |
1512 | 41 | 44 | ||
1513 | @@ -57,6 +60,13 @@ | |||
1514 | 57 | self.window = None | 60 | self.window = None |
1515 | 58 | self.connect("activate", self.on_activate) | 61 | self.connect("activate", self.on_activate) |
1516 | 59 | 62 | ||
1517 | 63 | def run(self, args): | ||
1518 | 64 | self.register(); | ||
1519 | 65 | if self.get_is_remote(): | ||
1520 | 66 | self.activate() | ||
1521 | 67 | else: | ||
1522 | 68 | super(Window, self).run(args) | ||
1523 | 69 | |||
1524 | 60 | def on_activate(self, app): | 70 | def on_activate(self, app): |
1525 | 61 | '''Setup the application''' | 71 | '''Setup the application''' |
1526 | 62 | if self.window is not None: | 72 | if self.window is not None: |
1527 | @@ -66,59 +76,39 @@ | |||
1528 | 66 | # GUI initialization | 76 | # GUI initialization |
1529 | 67 | ##### | 77 | ##### |
1530 | 68 | self.keys = keyring.Keyring("GMail", "mail.google.com", "http") | 78 | self.keys = keyring.Keyring("GMail", "mail.google.com", "http") |
1531 | 69 | self.client = Gio.Settings("net.launchpad.gm-notify") | ||
1532 | 70 | 79 | ||
1539 | 71 | if os.path.exists("gm-config.ui"): | 80 | if os.path.exists("gm-list.ui"): |
1540 | 72 | builder_file = "gm-config.ui" | 81 | builder_file = "gm-list.ui" |
1541 | 73 | elif os.path.exists("/usr/local/share/gm-notify/gm-config.ui"): | 82 | elif os.path.exists("/usr/local/share/gm-notify/gm-list.ui"): |
1542 | 74 | builder_file = "/usr/local/share/gm-notify/gm-config.ui" | 83 | builder_file = "/usr/local/share/gm-notify/gm-list.ui" |
1543 | 75 | elif os.path.exists("/usr/share/gm-notify/gm-config.ui"): | 84 | elif os.path.exists("/usr/share/gm-notify/gm-list.ui"): |
1544 | 76 | builder_file = "/usr/share/gm-notify/gm-config.ui" | 85 | builder_file = "/usr/share/gm-notify/gm-list.ui" |
1545 | 86 | |||
1546 | 77 | 87 | ||
1547 | 78 | self.wTree = Gtk.Builder.new() | 88 | self.wTree = Gtk.Builder.new() |
1548 | 79 | self.wTree.add_from_file(builder_file) | 89 | self.wTree.add_from_file(builder_file) |
1549 | 80 | self.wTree.set_translation_domain("gm-notify") | 90 | self.wTree.set_translation_domain("gm-notify") |
1550 | 81 | self.window = self.wTree.get_object("gmnotify_config_main") | 91 | self.window = self.wTree.get_object("gmnotify_config_main") |
1551 | 82 | self.window.show_all() | 92 | self.window.show_all() |
1552 | 93 | self.window.connect("delete_event", self.terminate) | ||
1553 | 94 | self.window.connect("window-state-event", self.on_update_accounts) | ||
1554 | 83 | self.add_window(self.window) | 95 | self.add_window(self.window) |
1555 | 96 | |||
1556 | 97 | self.accounts = self.wTree.get_object("accounts") | ||
1557 | 84 | 98 | ||
1558 | 99 | self.accounts_treeview = self.wTree.get_object("accounts_treeview") | ||
1559 | 100 | self.accounts_treeview.connect("row-activated", self.on_modify_account); | ||
1560 | 101 | |||
1561 | 85 | self.wTree.get_object("notebook_main").set_current_page(0) | 102 | self.wTree.get_object("notebook_main").set_current_page(0) |
1562 | 86 | 103 | ||
1597 | 87 | ##### | 104 | |
1598 | 88 | # Init with stored values | 105 | self.add_button = self.wTree.get_object("add_button") |
1599 | 89 | ##### | 106 | self.add_button.connect("clicked", self.on_add_account) |
1566 | 90 | |||
1567 | 91 | # Credentials | ||
1568 | 92 | if self.keys.has_credentials(): | ||
1569 | 93 | self.creds = self.keys.get_credentials() | ||
1570 | 94 | else: | ||
1571 | 95 | self.creds = ("", "") | ||
1572 | 96 | self.wTree.get_object("input_user").set_text(self.creds[0]) | ||
1573 | 97 | self.wTree.get_object("input_password").set_text(self.creds[1]) | ||
1574 | 98 | |||
1575 | 99 | self.api = MailChecker("", "") | ||
1576 | 100 | self.api.cb_auth_successful = self.credentials_valid | ||
1577 | 101 | self.api.cb_auth_failed = self.credentials_invalid | ||
1578 | 102 | |||
1579 | 103 | self.check_credentials(None, None) | ||
1580 | 104 | |||
1581 | 105 | # Sound | ||
1582 | 106 | self.wTree.get_object("checkbutton_sound").set_active(self.client.get_boolean("play-sound")) | ||
1583 | 107 | if self.client.get_string("soundfile"): | ||
1584 | 108 | self.wTree.get_object("fcbutton_sound").set_filename(self.client.get_string("soundfile")) | ||
1585 | 109 | self.on_checkbutton_sound_toggled(self.wTree.get_object("checkbutton_sound")) | ||
1586 | 110 | |||
1587 | 111 | # ClickAction | ||
1588 | 112 | if self.client.get_boolean("openclient"): | ||
1589 | 113 | self.wTree.get_object("radiobutton_openclient").set_active(True) | ||
1590 | 114 | else: | ||
1591 | 115 | self.wTree.get_object("radiobutton_openweb").set_active(True) | ||
1592 | 116 | |||
1593 | 117 | # Mailboxes | ||
1594 | 118 | mailboxes = self.client.get_strv("mailboxes") | ||
1595 | 119 | self.wTree.get_object("checkbutton_inbox").set_active(self.client.get_boolean("ignore-inbox")) | ||
1596 | 120 | self.wTree.get_object("entry_labels").set_text(", ".join(mailboxes)) | ||
1600 | 121 | 107 | ||
1601 | 108 | self.remove_button = self.wTree.get_object("remove_button") | ||
1602 | 109 | self.remove_button.connect("clicked", self.on_remove_account) | ||
1603 | 110 | |||
1604 | 111 | self.wTree.get_object("button_ok").connect("clicked", self.terminate) | ||
1605 | 122 | # Autorun | 112 | # Autorun |
1606 | 123 | if os.path.exists("data/gm-notify.desktop"): | 113 | if os.path.exists("data/gm-notify.desktop"): |
1607 | 124 | self.gm_notify_autostart_file = "data/gm-notify.desktop" | 114 | self.gm_notify_autostart_file = "data/gm-notify.desktop" |
1608 | @@ -129,36 +119,48 @@ | |||
1609 | 129 | self.autostart_file = os.path.expanduser("~/.config/autostart/gm-notify.desktop") | 119 | self.autostart_file = os.path.expanduser("~/.config/autostart/gm-notify.desktop") |
1610 | 130 | self.wTree.get_object("checkbutton_autostart").set_active(os.path.exists(self.autostart_file)) | 120 | self.wTree.get_object("checkbutton_autostart").set_active(os.path.exists(self.autostart_file)) |
1611 | 131 | 121 | ||
1618 | 132 | # Connect signals | 122 | def update_accounts(self, accounts, keys): |
1619 | 133 | self.wTree.get_object("button_close").connect("clicked", self.terminate) | 123 | accounts.clear() |
1620 | 134 | self.wTree.get_object("button_apply").connect("clicked", self.save) | 124 | if not self.keys.has_any_users(): |
1621 | 135 | self.wTree.get_object("input_password").connect("focus-out-event", self.check_credentials) | 125 | return |
1622 | 136 | self.wTree.get_object("checkbutton_sound").connect("toggled", self.on_checkbutton_sound_toggled) | 126 | for user in keys.get_all_users(): |
1623 | 137 | 127 | accounts.append((user,)) | |
1624 | 128 | |||
1625 | 129 | def on_update_accounts(self, widget, event): | ||
1626 | 130 | print("Event %s WindowState %s" % (str(event.type), str(event.new_window_state))) | ||
1627 | 131 | if (event.type == Gdk.EventType.WINDOW_STATE | ||
1628 | 132 | and event.new_window_state == Gdk.WindowState.FOCUSED): | ||
1629 | 133 | self.update_accounts(self.accounts, self.keys) | ||
1630 | 134 | |||
1631 | 135 | def on_modify_account(self, widget, path, column): | ||
1632 | 136 | iter = self.accounts.get_iter(path) | ||
1633 | 137 | account = self.accounts[iter] | ||
1634 | 138 | username = account[0] | ||
1635 | 139 | credentials = self.keys.get_credentials(username) | ||
1636 | 140 | account_config = AccountConfig(self.keys, credentials) | ||
1637 | 141 | window = account_config.init_window(self.window) | ||
1638 | 142 | |||
1639 | 143 | def on_add_account(self, widget): | ||
1640 | 144 | account_config = AccountConfig(self.keys, Credentials()) | ||
1641 | 145 | window = account_config.init_window(self.window) | ||
1642 | 146 | |||
1643 | 147 | def on_remove_account(self, widget): | ||
1644 | 148 | tree_selection = self.accounts_treeview.get_selection() | ||
1645 | 149 | accounts,iter = tree_selection.get_selected() | ||
1646 | 150 | if iter is None: | ||
1647 | 151 | return | ||
1648 | 152 | |||
1649 | 153 | account = accounts[iter] | ||
1650 | 154 | username = account[0] | ||
1651 | 155 | settings_provider = account_settings_provider.create_settings_provider(username) | ||
1652 | 156 | settings_provider.remove_all_settings() | ||
1653 | 157 | self.keys.delete_credentials(username) | ||
1654 | 158 | |||
1655 | 159 | accounts.remove(iter) | ||
1656 | 160 | |||
1657 | 138 | def save(self, widget, data=None): | 161 | def save(self, widget, data=None): |
1658 | 139 | '''saves the entered data and closes the app''' | 162 | '''saves the entered data and closes the app''' |
1681 | 140 | # Credentials | 163 | |
1660 | 141 | self.keys.delete_credentials() | ||
1661 | 142 | self.keys.set_credentials(self.wTree.get_object("input_user").get_text(), | ||
1662 | 143 | self.wTree.get_object("input_password").get_text()) | ||
1663 | 144 | |||
1664 | 145 | # Mailboxes | ||
1665 | 146 | mailboxes = [] | ||
1666 | 147 | for label in self.wTree.get_object("entry_labels").get_text().split(","): | ||
1667 | 148 | mailboxes.append(label.strip()) | ||
1668 | 149 | self.client.set_strv("mailboxes", mailboxes) | ||
1669 | 150 | self.client.set_boolean("ignore-inbox", self.wTree.get_object("checkbutton_inbox").get_active()) | ||
1670 | 151 | |||
1671 | 152 | # ClickAction | ||
1672 | 153 | self.client.set_boolean("openclient", self.wTree.get_object("radiobutton_openclient").get_active()) | ||
1673 | 154 | |||
1674 | 155 | # Soundfile | ||
1675 | 156 | if self.wTree.get_object("checkbutton_sound").get_active() and self.wTree.get_object("fcbutton_sound").get_filename(): | ||
1676 | 157 | self.client.set_boolean("play-sound", True) | ||
1677 | 158 | self.client.set_string("soundfile", str(self.wTree.get_object("fcbutton_sound").get_filename())) | ||
1678 | 159 | else: | ||
1679 | 160 | self.client.set_boolean("play-sound", False) | ||
1680 | 161 | |||
1682 | 162 | # Autorun | 164 | # Autorun |
1683 | 163 | if self.wTree.get_object("checkbutton_autostart").get_active(): | 165 | if self.wTree.get_object("checkbutton_autostart").get_active(): |
1684 | 164 | if not os.path.exists(self.autostart_file): | 166 | if not os.path.exists(self.autostart_file): |
1685 | @@ -182,62 +184,9 @@ | |||
1686 | 182 | # Start gm-notify itself | 184 | # Start gm-notify itself |
1687 | 183 | subprocess.Popen(get_executable_path("gm-notify")) | 185 | subprocess.Popen(get_executable_path("gm-notify")) |
1688 | 184 | 186 | ||
1690 | 185 | def terminate(self, widget): | 187 | def terminate(self, widget, event = None): |
1691 | 186 | reactor.stop() | 188 | reactor.stop() |
1692 | 187 | 189 | ||
1693 | 188 | def on_checkbutton_sound_toggled(self, widget): | ||
1694 | 189 | self.wTree.get_object("fcbutton_sound").set_sensitive(self.wTree.get_object("checkbutton_sound").get_active()) | ||
1695 | 190 | |||
1696 | 191 | def check_credentials(self, widget, event, data=None): | ||
1697 | 192 | '''check if the given credentials are valid''' | ||
1698 | 193 | |||
1699 | 194 | input_user = self.wTree.get_object("input_user") | ||
1700 | 195 | input_password = self.wTree.get_object("input_password") | ||
1701 | 196 | image_credentials = self.wTree.get_object("image_credentials") | ||
1702 | 197 | label_credentials = self.wTree.get_object("label_credentials") | ||
1703 | 198 | button_apply = self.wTree.get_object("button_apply") | ||
1704 | 199 | button_apply.set_sensitive(False) | ||
1705 | 200 | |||
1706 | 201 | # Change status text and disable input fields | ||
1707 | 202 | if input_user.get_text() and input_password.get_text(): | ||
1708 | 203 | image_credentials.set_from_file("/usr/share/gm-notify/checking.gif") | ||
1709 | 204 | label_credentials.set_text(_("checking...")) | ||
1710 | 205 | input_user.set_sensitive(False) | ||
1711 | 206 | input_password.set_sensitive(False) | ||
1712 | 207 | |||
1713 | 208 | self.api.jid = jid.JID(input_user.get_text()) | ||
1714 | 209 | self.api.password = input_password.get_text() | ||
1715 | 210 | self.api.connect() | ||
1716 | 211 | return False | ||
1717 | 212 | |||
1718 | 213 | def credentials_valid(self): | ||
1719 | 214 | input_user = self.wTree.get_object("input_user") | ||
1720 | 215 | input_password = self.wTree.get_object("input_password") | ||
1721 | 216 | image_credentials = self.wTree.get_object("image_credentials") | ||
1722 | 217 | label_credentials = self.wTree.get_object("label_credentials") | ||
1723 | 218 | button_apply = self.wTree.get_object("button_apply") | ||
1724 | 219 | |||
1725 | 220 | image_credentials.set_from_icon_name("gtk-yes", Gtk.IconSize.MENU) | ||
1726 | 221 | label_credentials.set_text(_("Valid credentials")) | ||
1727 | 222 | button_apply.set_sensitive(True) | ||
1728 | 223 | input_user.set_sensitive(True) | ||
1729 | 224 | input_password.set_sensitive(True) | ||
1730 | 225 | |||
1731 | 226 | self.api.die() | ||
1732 | 227 | |||
1733 | 228 | def credentials_invalid(self): | ||
1734 | 229 | input_user = self.wTree.get_object("input_user") | ||
1735 | 230 | input_password = self.wTree.get_object("input_password") | ||
1736 | 231 | image_credentials = self.wTree.get_object("image_credentials") | ||
1737 | 232 | label_credentials = self.wTree.get_object("label_credentials") | ||
1738 | 233 | |||
1739 | 234 | image_credentials.set_from_icon_name("gtk-stop", Gtk.IconSize.MENU) | ||
1740 | 235 | label_credentials.set_text(_("Invalid credentials")) | ||
1741 | 236 | input_user.set_sensitive(True) | ||
1742 | 237 | input_password.set_sensitive(True) | ||
1743 | 238 | |||
1744 | 239 | self.api.die() | ||
1745 | 240 | |||
1746 | 241 | t = Window() | 190 | t = Window() |
1747 | 242 | reactor.registerGApplication(t) | 191 | reactor.registerGApplication(t) |
1748 | 243 | reactor.run() | 192 | reactor.run() |
1749 | 244 | 193 | ||
1750 | === modified file 'gm_notify_keyring.py' | |||
1751 | --- gm_notify_keyring.py 2015-12-05 00:25:32 +0000 | |||
1752 | +++ gm_notify_keyring.py 2015-12-05 00:25:32 +0000 | |||
1753 | @@ -3,6 +3,9 @@ | |||
1754 | 3 | __version__ = "$Revision: 14294 $" | 3 | __version__ = "$Revision: 14294 $" |
1755 | 4 | 4 | ||
1756 | 5 | from gi.repository import GnomeKeyring, Gtk | 5 | from gi.repository import GnomeKeyring, Gtk |
1757 | 6 | from collections import namedtuple | ||
1758 | 7 | Credentials = namedtuple("Credentials", ["username", "password"]) | ||
1759 | 8 | Credentials.__new__.__defaults__ = ("", "") | ||
1760 | 6 | 9 | ||
1761 | 7 | def attributes(d): | 10 | def attributes(d): |
1762 | 8 | '''Converts a dictionary to a GnomeKeyring.Attribute array''' | 11 | '''Converts a dictionary to a GnomeKeyring.Attribute array''' |
1763 | @@ -28,26 +31,66 @@ | |||
1764 | 28 | self._protocol = protocol | 31 | self._protocol = protocol |
1765 | 29 | result, self._keyring = GnomeKeyring.get_default_keyring_sync() | 32 | result, self._keyring = GnomeKeyring.get_default_keyring_sync() |
1766 | 30 | 33 | ||
1768 | 31 | def has_credentials(self): | 34 | def has_any_credentials(self): |
1769 | 32 | attrs = attributes({"server": self._server, "protocol": self._protocol}) | 35 | attrs = attributes({"server": self._server, "protocol": self._protocol}) |
1770 | 33 | result, items = GnomeKeyring.find_items_sync(GnomeKeyring.ItemType.NETWORK_PASSWORD, attrs) | 36 | result, items = GnomeKeyring.find_items_sync(GnomeKeyring.ItemType.NETWORK_PASSWORD, attrs) |
1771 | 34 | if result in (GnomeKeyring.Result.NO_MATCH, GnomeKeyring.Result.DENIED): | 37 | if result in (GnomeKeyring.Result.NO_MATCH, GnomeKeyring.Result.DENIED): |
1772 | 35 | return False | 38 | return False |
1773 | 36 | return len(items) > 0 | 39 | return len(items) > 0 |
1774 | 37 | 40 | ||
1777 | 38 | def get_credentials(self): | 41 | def get_all_credentials(self): |
1778 | 39 | attrs = attributes({"server": self._server, "protocol": self._protocol}) | 42 | attrs = attributes({"server": self._server, "protocol": self._protocol}) |
1779 | 43 | result, items = GnomeKeyring.find_items_sync(GnomeKeyring.ItemType.NETWORK_PASSWORD, attrs) | ||
1780 | 44 | if len(items) == 0: | ||
1781 | 45 | raise KeyringException("Credentials not found") | ||
1782 | 46 | credentials_list = [] | ||
1783 | 47 | for item in items: | ||
1784 | 48 | d = dict_from_attributes(item.attributes) | ||
1785 | 49 | credentials_list.append(Credentials(d["user"], item.secret)) | ||
1786 | 50 | return credentials_list | ||
1787 | 51 | |||
1788 | 52 | def has_any_users(self): | ||
1789 | 53 | return self.has_any_credentials() | ||
1790 | 54 | |||
1791 | 55 | def get_all_users(self): | ||
1792 | 56 | attrs = attributes({"server": self._server, "protocol": self._protocol}) | ||
1793 | 57 | result, items = GnomeKeyring.find_items_sync(GnomeKeyring.ItemType.NETWORK_PASSWORD, attrs) | ||
1794 | 58 | if len(items) == 0: | ||
1795 | 59 | raise KeyringException("Credentials not found") | ||
1796 | 60 | users_list = [] | ||
1797 | 61 | for item in items: | ||
1798 | 62 | d = dict_from_attributes(item.attributes) | ||
1799 | 63 | users_list.append(d["user"]) | ||
1800 | 64 | return users_list | ||
1801 | 65 | |||
1802 | 66 | def get_credentials(self, user): | ||
1803 | 67 | attrs = attributes({ | ||
1804 | 68 | "user" : user, | ||
1805 | 69 | "server": self._server, | ||
1806 | 70 | "protocol": self._protocol | ||
1807 | 71 | }) | ||
1808 | 40 | result, items = GnomeKeyring.find_items_sync(GnomeKeyring.ItemType.NETWORK_PASSWORD, attrs) | 72 | result, items = GnomeKeyring.find_items_sync(GnomeKeyring.ItemType.NETWORK_PASSWORD, attrs) |
1809 | 41 | if len(items) == 0: | 73 | if len(items) == 0: |
1810 | 42 | raise KeyringException("Credentials not found") | 74 | raise KeyringException("Credentials not found") |
1811 | 43 | d = dict_from_attributes(items[0].attributes) | 75 | d = dict_from_attributes(items[0].attributes) |
1813 | 44 | return (d["user"], items[0].secret) | 76 | return Credentials(d["user"], items[0].secret) |
1814 | 45 | 77 | ||
1816 | 46 | def delete_credentials(self): | 78 | def delete_all_credentials(self): |
1817 | 47 | attrs = attributes({"server": self._server, "protocol": self._protocol}) | 79 | attrs = attributes({"server": self._server, "protocol": self._protocol}) |
1818 | 48 | result, items = GnomeKeyring.find_items_sync(GnomeKeyring.ItemType.NETWORK_PASSWORD, attrs) | 80 | result, items = GnomeKeyring.find_items_sync(GnomeKeyring.ItemType.NETWORK_PASSWORD, attrs) |
1819 | 49 | for item in items: | 81 | for item in items: |
1820 | 50 | GnomeKeyring.item_delete_sync(self._keyring, item.item_id) | 82 | GnomeKeyring.item_delete_sync(self._keyring, item.item_id) |
1821 | 83 | |||
1822 | 84 | def delete_credentials(self, user): | ||
1823 | 85 | attrs = attributes({ | ||
1824 | 86 | "user": user, | ||
1825 | 87 | "server": self._server, | ||
1826 | 88 | "protocol": self._protocol | ||
1827 | 89 | }) | ||
1828 | 90 | result, items = GnomeKeyring.find_items_sync(GnomeKeyring.ItemType.NETWORK_PASSWORD, attrs) | ||
1829 | 91 | for item in items: | ||
1830 | 92 | GnomeKeyring.item_delete_sync(self._keyring, item.item_id) | ||
1831 | 93 | |||
1832 | 51 | 94 | ||
1833 | 52 | def set_credentials(self, user, pw): | 95 | def set_credentials(self, user, pw): |
1834 | 53 | attrs = attributes({ | 96 | attrs = attributes({ |
1835 | 54 | 97 | ||
1836 | === modified file 'gtalk.py' | |||
1837 | --- gtalk.py 2015-12-05 00:25:32 +0000 | |||
1838 | +++ gtalk.py 2015-12-05 00:25:32 +0000 | |||
1839 | @@ -1,10 +1,11 @@ | |||
1840 | 1 | #!/usr/bin/env python | 1 | #!/usr/bin/env python |
1841 | 2 | # -*- coding: utf-8 -*- | 2 | # -*- coding: utf-8 -*- |
1842 | 3 | 3 | ||
1844 | 4 | # gtalk.py v0.10.3 | 4 | # gtalk.py v1.0 |
1845 | 5 | # Google Talk mail notification client library | 5 | # Google Talk mail notification client library |
1846 | 6 | # | 6 | # |
1847 | 7 | # Copyright (c) 2009-2010, Alexander Hungenberg <alexander.hungenberg@gmail.com> | 7 | # Copyright (c) 2009-2010, Alexander Hungenberg <alexander.hungenberg@gmail.com> |
1848 | 8 | # Copyright (c) 2015, Mateusz Balbus <balbusm@gmail.com> | ||
1849 | 8 | # | 9 | # |
1850 | 9 | # This program is free software: you can redistribute it and/or modify | 10 | # This program is free software: you can redistribute it and/or modify |
1851 | 10 | # it under the terms of the GNU General Public License as published by | 11 | # it under the terms of the GNU General Public License as published by |
1852 | @@ -21,38 +22,86 @@ | |||
1853 | 21 | # | 22 | # |
1854 | 22 | from __future__ import print_function | 23 | from __future__ import print_function |
1855 | 23 | 24 | ||
1856 | 24 | from threading import Event | ||
1857 | 25 | |||
1858 | 26 | from twisted.words.protocols.jabber import xmlstream, client, jid | 25 | from twisted.words.protocols.jabber import xmlstream, client, jid |
1859 | 27 | from twisted.words.xish import domish | 26 | from twisted.words.xish import domish |
1863 | 28 | from twisted.internet import reactor, task, error | 27 | from twisted.internet import reactor, task, error, ssl |
1864 | 29 | 28 | from twisted.internet.error import TimeoutError, ConnectionRefusedError | |
1865 | 30 | _DEBUG = False | 29 | |
1866 | 30 | from datetime import datetime | ||
1867 | 31 | |||
1868 | 32 | GTALK_HOST = "talk.google.com" | ||
1869 | 33 | |||
1870 | 34 | _DEBUG = True | ||
1871 | 31 | COLOR_GREEN = "\033[92m" | 35 | COLOR_GREEN = "\033[92m" |
1872 | 32 | COLOR_END = "\033[0m" | 36 | COLOR_END = "\033[0m" |
1873 | 33 | def DEBUG(msg): | 37 | def DEBUG(msg): |
1875 | 34 | if _DEBUG: print(COLOR_GREEN + str(msg) + COLOR_END) | 38 | if _DEBUG: |
1876 | 39 | curent_time = datetime.now() | ||
1877 | 40 | |||
1878 | 41 | print(COLOR_GREEN + str(curent_time) + " " +str(msg) + COLOR_END) | ||
1879 | 35 | 42 | ||
1880 | 36 | class GTalkClientFactory(xmlstream.XmlStreamFactory): | 43 | class GTalkClientFactory(xmlstream.XmlStreamFactory): |
1882 | 37 | def __init__(self, jid, password): | 44 | |
1883 | 45 | def __init__(self, jid, password, settings_provider): | ||
1884 | 38 | a = client.XMPPAuthenticator(jid, password) | 46 | a = client.XMPPAuthenticator(jid, password) |
1885 | 39 | xmlstream.XmlStreamFactory.__init__(self, a) | 47 | xmlstream.XmlStreamFactory.__init__(self, a) |
1886 | 48 | self.addBootstrap(xmlstream.STREAM_CONNECTED_EVENT, self.clientConnected) | ||
1887 | 40 | 49 | ||
1888 | 50 | self.jid = jid | ||
1889 | 41 | self.reconnect = True | 51 | self.reconnect = True |
1890 | 52 | self.connection_failed = False | ||
1891 | 53 | self.connection_lost = False | ||
1892 | 54 | self.cb_connection_error = None | ||
1893 | 55 | self.settings_provider = settings_provider | ||
1894 | 56 | self.port = self.settings_provider.retrieve_preferred_port() | ||
1895 | 42 | 57 | ||
1896 | 43 | def clientConnectionLost(self, connector, reason): | 58 | def clientConnectionLost(self, connector, reason): |
1898 | 44 | if self.reconnect: xmlstream.XmlStreamFactory.clientConnectionLost(self, connector, reason) | 59 | DEBUG("clientConnectionLost %s" % str(reason)) |
1899 | 60 | self.connection_lost = True | ||
1900 | 61 | if not self.reconnect: | ||
1901 | 62 | return | ||
1902 | 63 | |||
1903 | 64 | DEBUG("clientConnectionLost: Reconnecting with the same settings (port %d)" % connector.port) | ||
1904 | 65 | if self.cb_connection_error : self.cb_connection_error(self.jid.full(), reason) | ||
1905 | 66 | # reconnect on the same port as it used to work | ||
1906 | 67 | xmlstream.XmlStreamFactory.clientConnectionLost(self, connector, reason) | ||
1907 | 68 | |||
1908 | 69 | def clientConnectionFailed(self, connector, reason): | ||
1909 | 70 | DEBUG("clientConnectionFailed %s on port: %d reconnecting: %s" % (str(reason), connector.port, str(self.reconnect))) | ||
1910 | 71 | self.connection_failed = True | ||
1911 | 72 | if not self.reconnect: | ||
1912 | 73 | return | ||
1913 | 74 | |||
1914 | 75 | DEBUG("clientConnectionFailed: Connection failed"); | ||
1915 | 76 | if self.cb_connection_error : self.cb_connection_error(self.jid.full(), reason) | ||
1916 | 77 | |||
1917 | 78 | def clientConnected(self, xmlstream): | ||
1918 | 79 | DEBUG("clientConnected") | ||
1919 | 80 | self.connection_failed = False | ||
1920 | 81 | self.connection_lost = False | ||
1921 | 82 | |||
1922 | 83 | def getCurrentPort(self): | ||
1923 | 84 | return self.port | ||
1924 | 85 | |||
1925 | 86 | def hasConnectionFailedOrLost(self): | ||
1926 | 87 | return self.connection_failed or self.connection_lost | ||
1927 | 88 | |||
1928 | 89 | def setOnConnectionErrorCB(self, cb_connection_error): | ||
1929 | 90 | self.cb_connection_error = cb_connection_error | ||
1930 | 91 | |||
1931 | 45 | 92 | ||
1932 | 46 | class MailChecker(): | 93 | class MailChecker(): |
1936 | 47 | def __init__(self, jid, password, labels=[], cb_new=None, cb_count=None): | 94 | def __init__(self, jid, password, settings_provider, labels=[], cb_new=None, cb_count=None): |
1937 | 48 | self.host = "talk.google.com" | 95 | self.host = GTALK_HOST |
1938 | 49 | self.port = 5222 | 96 | self.settings_provider = settings_provider |
1939 | 50 | self.jid = jid | 97 | self.jid = jid |
1940 | 51 | self.password = password | 98 | self.password = password |
1941 | 52 | self.cb_new = cb_new | 99 | self.cb_new = cb_new |
1942 | 53 | self.cb_count = cb_count | 100 | self.cb_count = cb_count |
1944 | 54 | self.cb_auth_successful = None | 101 | self.cb_auth_succeeded = None |
1945 | 55 | self.cb_auth_failed = None | 102 | self.cb_auth_failed = None |
1946 | 103 | self.cb_connection_error = None | ||
1947 | 104 | self.cb_connected = None | ||
1948 | 56 | 105 | ||
1949 | 57 | self.last_tids = {} | 106 | self.last_tids = {} |
1950 | 58 | self.labels = labels | 107 | self.labels = labels |
1951 | @@ -66,14 +115,33 @@ | |||
1952 | 66 | self.ready_for_query_state = False | 115 | self.ready_for_query_state = False |
1953 | 67 | self.timeout_call_id = None | 116 | self.timeout_call_id = None |
1954 | 68 | self.disconnected = True | 117 | self.disconnected = True |
1955 | 118 | self.running = False | ||
1956 | 119 | |||
1957 | 120 | def setOnConnectionErrorCB(self, cb_connection_error): | ||
1958 | 121 | self.cb_connection_error = cb_connection_error | ||
1959 | 122 | |||
1960 | 123 | def setOnConnectedCB(self, cb_connected): | ||
1961 | 124 | self.cb_connected = cb_connected | ||
1962 | 125 | |||
1963 | 126 | def setOnAuthFailed(self, cb_auth_failed): | ||
1964 | 127 | self.cb_auth_failed = cb_auth_failed | ||
1965 | 128 | |||
1966 | 129 | def setOnAuthSucceeded(self, cb_auth_succeeded): | ||
1967 | 130 | self.cb_auth_succeeded = cb_auth_succeeded | ||
1968 | 131 | |||
1969 | 132 | def is_running(self): | ||
1970 | 133 | return self.running | ||
1971 | 69 | 134 | ||
1972 | 70 | def die(self): | 135 | def die(self): |
1973 | 136 | DEBUG("Dying...") | ||
1974 | 71 | self.factory.reconnect = False | 137 | self.factory.reconnect = False |
1975 | 72 | self.query_task.stop() | 138 | self.query_task.stop() |
1976 | 73 | self.connector.disconnect() | 139 | self.connector.disconnect() |
1977 | 140 | self.running = False | ||
1978 | 74 | 141 | ||
1979 | 75 | def connect(self): | 142 | def connect(self): |
1981 | 76 | self.factory = GTalkClientFactory(self.jid, self.password) | 143 | self.factory = GTalkClientFactory(self.jid, self.password, self.settings_provider) |
1982 | 144 | self.factory.setOnConnectionErrorCB(self.cb_connection_error) | ||
1983 | 77 | self.factory.addBootstrap(xmlstream.STREAM_END_EVENT, self.disconnectCB) | 145 | self.factory.addBootstrap(xmlstream.STREAM_END_EVENT, self.disconnectCB) |
1984 | 78 | self.factory.addBootstrap(xmlstream.STREAM_ERROR_EVENT, self.disconnectCB) | 146 | self.factory.addBootstrap(xmlstream.STREAM_ERROR_EVENT, self.disconnectCB) |
1985 | 79 | self.factory.addBootstrap(xmlstream.INIT_FAILED_EVENT, self.init_failedCB) | 147 | self.factory.addBootstrap(xmlstream.INIT_FAILED_EVENT, self.init_failedCB) |
1986 | @@ -84,10 +152,12 @@ | |||
1987 | 84 | 152 | ||
1988 | 85 | self.query_task = task.LoopingCall(self.queryInbox) | 153 | self.query_task = task.LoopingCall(self.queryInbox) |
1989 | 86 | self.query_task.start(60) | 154 | self.query_task.start(60) |
1990 | 155 | self.running = True | ||
1991 | 87 | 156 | ||
1993 | 88 | self.connector = reactor.connectTCP(self.host, self.port, self.factory) | 157 | self.connector = reactor.connectSSL(self.host, self.factory.getCurrentPort(), self.factory, ssl.ClientContextFactory()) |
1994 | 89 | 158 | ||
1995 | 90 | def reply_timeout(self): | 159 | def reply_timeout(self): |
1996 | 160 | DEBUG("reply_timeout") | ||
1997 | 91 | self.connector.disconnect() # Our reconnecting factory will try the reconnecting | 161 | self.connector.disconnect() # Our reconnecting factory will try the reconnecting |
1998 | 92 | 162 | ||
1999 | 93 | def send_callback_handler(self, data, callback=None, **kargs): | 163 | def send_callback_handler(self, data, callback=None, **kargs): |
2000 | @@ -115,16 +185,19 @@ | |||
2001 | 115 | self.xmlstream.send(data) | 185 | self.xmlstream.send(data) |
2002 | 116 | 186 | ||
2003 | 117 | def disconnectCB(self, xmlstream): | 187 | def disconnectCB(self, xmlstream): |
2004 | 118 | self.ready_for_query_state = False | ||
2005 | 119 | self.disconnected = True | ||
2006 | 120 | DEBUG("disconnected") | 188 | DEBUG("disconnected") |
2008 | 121 | 189 | self.ready_for_query_state = False | |
2009 | 190 | self.factory.reconnect = False | ||
2010 | 191 | self.disconnected = True | ||
2011 | 192 | |||
2012 | 122 | def init_failedCB(self, xmlstream): | 193 | def init_failedCB(self, xmlstream): |
2014 | 123 | if self.cb_auth_failed: self.cb_auth_failed() | 194 | DEBUG("init_failedCB") |
2015 | 195 | if self.cb_auth_failed: self.cb_auth_failed(self.jid.full(), xmlstream.value) | ||
2016 | 124 | self.disconnectCB(xmlstream) | 196 | self.disconnectCB(xmlstream) |
2017 | 125 | 197 | ||
2018 | 126 | def authenticationCB(self, xmlstream): | 198 | def authenticationCB(self, xmlstream): |
2020 | 127 | if self.cb_auth_successful: self.cb_auth_successful() | 199 | DEBUG("authenticationCB") |
2021 | 200 | if self.cb_auth_succeeded: self.cb_auth_succeeded(self.jid.full()) | ||
2022 | 128 | self.factory.resetDelay() | 201 | self.factory.resetDelay() |
2023 | 129 | 202 | ||
2024 | 130 | # We set the usersetting mail-notification | 203 | # We set the usersetting mail-notification |
2025 | @@ -135,12 +208,19 @@ | |||
2026 | 135 | self.send(iq, "/iq", self.usersettingIQ) | 208 | self.send(iq, "/iq", self.usersettingIQ) |
2027 | 136 | 209 | ||
2028 | 137 | def usersettingIQ(self, iq): | 210 | def usersettingIQ(self, iq): |
2029 | 211 | DEBUG("usersettingIQ") | ||
2030 | 138 | self.ready_for_query_state = True | 212 | self.ready_for_query_state = True |
2031 | 139 | self.queryInbox() | 213 | self.queryInbox() |
2032 | 140 | 214 | ||
2033 | 141 | def queryInbox(self): | 215 | def queryInbox(self): |
2035 | 142 | if not self.ready_for_query_state: return | 216 | DEBUG("queryInbox") |
2036 | 217 | if not(self.ready_for_query_state or self.factory.hasConnectionFailedOrLost()): | ||
2037 | 218 | DEBUG("queryInbox: ready for query: %s connection_failed_or_lost: %s" % (str(self.ready_for_query_state), str(self.factory.hasConnectionFailedOrLost()))) | ||
2038 | 219 | DEBUG("queryInbox: skipping query request") | ||
2039 | 220 | return | ||
2040 | 143 | if self.disconnected: | 221 | if self.disconnected: |
2041 | 222 | DEBUG("queryInbox: disconnected") | ||
2042 | 223 | self.factory.reconnect = True | ||
2043 | 144 | self.connector.connect() | 224 | self.connector.connect() |
2044 | 145 | return | 225 | return |
2045 | 146 | self.ready_for_query_state = False | 226 | self.ready_for_query_state = False |
2046 | @@ -149,21 +229,23 @@ | |||
2047 | 149 | 229 | ||
2048 | 150 | iq = domish.Element((None, "iq"), attribs={"type": "get", "id": "mail-request-1"}) | 230 | iq = domish.Element((None, "iq"), attribs={"type": "get", "id": "mail-request-1"}) |
2049 | 151 | query = iq.addElement(("google:mail:notify", "query")) | 231 | query = iq.addElement(("google:mail:notify", "query")) |
2050 | 232 | DEBUG("queryInbox: requesting for label") | ||
2051 | 152 | self.send(iq, "/iq", self.gotLabel) | 233 | self.send(iq, "/iq", self.gotLabel) |
2052 | 153 | 234 | ||
2053 | 154 | def queryLabel(self): | 235 | def queryLabel(self): |
2054 | 155 | try: | 236 | try: |
2055 | 156 | label = self.labels_iter.next() | 237 | label = self.labels_iter.next() |
2057 | 157 | 238 | DEBUG("queryLabel " + label) | |
2058 | 158 | iq = domish.Element((None, "iq"), attribs={"type": "get", "id": "mail-request-1"}) | 239 | iq = domish.Element((None, "iq"), attribs={"type": "get", "id": "mail-request-1"}) |
2059 | 159 | query = iq.addElement(("google:mail:notify", "query")) | 240 | query = iq.addElement(("google:mail:notify", "query")) |
2060 | 160 | query.attributes['q'] = "label:%s AND is:unread" % label | 241 | query.attributes['q'] = "label:%s AND is:unread" % label |
2061 | 161 | self.send(iq, "/iq", self.gotLabel, label=label) | 242 | self.send(iq, "/iq", self.gotLabel, label=label) |
2062 | 162 | except StopIteration: | 243 | except StopIteration: |
2063 | 244 | DEBUG("queryLabel: end of iteration") | ||
2064 | 163 | self.labels_iter = iter(self.labels) | 245 | self.labels_iter = iter(self.labels) |
2065 | 164 | self.xmlstream.addObserver("/iq", self.gotNewMail) | 246 | self.xmlstream.addObserver("/iq", self.gotNewMail) |
2068 | 165 | if self.cb_count: self.cb_count(self.count) | 247 | if self.cb_count: self.cb_count(self.jid.full(), self.count) |
2069 | 166 | if self.mails and self.cb_new: self.cb_new(self.mails) | 248 | if self.mails and self.cb_new: self.cb_new(self.jid.full(), self.mails) |
2070 | 167 | self.mails = [] | 249 | self.mails = [] |
2071 | 168 | self.ready_for_query_state = True | 250 | self.ready_for_query_state = True |
2072 | 169 | 251 | ||
2073 | @@ -242,21 +324,25 @@ | |||
2074 | 242 | mail['snippet'] = unicode(child) | 324 | mail['snippet'] = unicode(child) |
2075 | 243 | mails.append(mail) | 325 | mails.append(mail) |
2076 | 244 | 326 | ||
2078 | 245 | self.cb_new(mails) | 327 | self.cb_new(self.jid.full(), mails) |
2079 | 246 | 328 | ||
2080 | 247 | self.ready_for_query_state = True | 329 | self.ready_for_query_state = True |
2081 | 248 | if iq: self.queryInbox() | 330 | if iq: self.queryInbox() |
2082 | 249 | 331 | ||
2083 | 250 | def rawDataIn(self, buf): | 332 | def rawDataIn(self, buf): |
2085 | 251 | print(u"< %s" % unicode(buf, "utf-8")) | 333 | print("> %s" % buf) |
2086 | 252 | 334 | ||
2087 | 253 | def rawDataOut(self, buf): | 335 | def rawDataOut(self, buf): |
2089 | 254 | print(u"> %s" % unicode(buf, "utf-8")) | 336 | print("< %s" % buf) |
2090 | 255 | 337 | ||
2091 | 256 | def connectedCB(self, xmlstream): | 338 | def connectedCB(self, xmlstream): |
2092 | 257 | self.xmlstream = xmlstream | 339 | self.xmlstream = xmlstream |
2093 | 258 | self.disconnected = False | 340 | self.disconnected = False |
2094 | 341 | if self.cb_connected: self.cb_connected(self.jid.full()) | ||
2095 | 259 | 342 | ||
2096 | 260 | if _DEBUG: | 343 | if _DEBUG: |
2097 | 261 | xmlstream.rawDataInFn = self.rawDataIn | 344 | xmlstream.rawDataInFn = self.rawDataIn |
2098 | 262 | xmlstream.rawDataOutFn = self.rawDataOut | 345 | xmlstream.rawDataOutFn = self.rawDataOut |
2099 | 346 | |||
2100 | 347 | def getCurrentPort(self): | ||
2101 | 348 | return self.factory.getCurrentPort() | ||
2102 | 263 | 349 | ||
2103 | === modified file 'setup.py' | |||
2104 | --- setup.py 2015-12-05 00:25:32 +0000 | |||
2105 | +++ setup.py 2015-12-05 00:25:32 +0000 | |||
2106 | @@ -3,16 +3,17 @@ | |||
2107 | 3 | from distutils.core import setup | 3 | from distutils.core import setup |
2108 | 4 | 4 | ||
2109 | 5 | setup( name='gm-notify', | 5 | setup( name='gm-notify', |
2111 | 6 | version='0.10.3', | 6 | version='1.0.0', |
2112 | 7 | description='Highly Ubuntu integrated GMail Notifier', | 7 | description='Highly Ubuntu integrated GMail Notifier', |
2116 | 8 | author='Alexander Hungenberg', | 8 | author='Mateusz Balbus', |
2117 | 9 | author_email='alexander.hungenberg@gmail.com', | 9 | author_email='balbusmg@gmail.com', |
2118 | 10 | py_modules=['gtalk', 'gm_notify_keyring'], | 10 | py_modules=['gtalk', 'gm_notify_keyring', 'account_settings_provider', 'account_config'], |
2119 | 11 | scripts=['gm-notify', 'gm-notify-config'], | 11 | scripts=['gm-notify', 'gm-notify-config'], |
2120 | 12 | data_files=[('/usr/share/applications', ['data/gm-notify-config.desktop']), | 12 | data_files=[('/usr/share/applications', ['data/gm-notify-config.desktop']), |
2121 | 13 | ('/usr/share/applications', ['data/gm-notify.desktop']), | 13 | ('/usr/share/applications', ['data/gm-notify.desktop']), |
2122 | 14 | ('/usr/share/gm-notify', ['data/checking.gif']), | 14 | ('/usr/share/gm-notify', ['data/checking.gif']), |
2123 | 15 | ('/usr/share/gm-notify', ['gm-config.ui']), | 15 | ('/usr/share/gm-notify', ['gm-config.ui']), |
2124 | 16 | ('/usr/share/gm-notify', ['gm-list.ui']), | ||
2125 | 16 | ('/usr/share/glib-2.0/schemas', ['data/net.launchpad.gm-notify.gschema.xml']), | 17 | ('/usr/share/glib-2.0/schemas', ['data/net.launchpad.gm-notify.gschema.xml']), |
2126 | 17 | ('/usr/share/locale/da/LC_MESSAGES', ['po/da/gm-notify.mo']), | 18 | ('/usr/share/locale/da/LC_MESSAGES', ['po/da/gm-notify.mo']), |
2127 | 18 | ('/usr/share/locale/bg/LC_MESSAGES', ['po/bg/gm-notify.mo']), | 19 | ('/usr/share/locale/bg/LC_MESSAGES', ['po/bg/gm-notify.mo']), |