src/mn-gmail-mailbox.gob (9465B) - raw
1 /*
2 * Mail Notification
3 * Copyright (C) 2003-2008 Jean-Yves Lefort <jylefort@brutele.be>
4 *
5 * This program is free software; you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License as published by
7 * the Free Software Foundation; either version 3 of the License, or
8 * (at your option) any later version.
9 *
10 * This program is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU General Public License for more details.
14 *
15 * You should have received a copy of the GNU General Public License along
16 * with this program; if not, write to the Free Software Foundation, Inc.,
17 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
18 */
19
20 %headertop{
21 #include "mn-authenticated-mailbox.h"
22 %}
23
24 %privateheader{
25 #include <libgnomevfs/gnome-vfs.h>
26 %}
27
28 %{
29 #include <string.h>
30 #include <stdlib.h>
31 #include <time.h>
32 #include <glib/gi18n.h>
33 #include <libxml/parser.h>
34 #include <libxml/tree.h>
35 #include "mn-mailbox-private.h"
36 #include "mn-authenticated-mailbox-private.h"
37 #include "mn-vfs.h"
38 #include "mn-stock.h"
39 #include "mn-message.h"
40 #include "mn-util.h"
41 %}
42
43 class MN:Gmail:Mailbox from MN:Authenticated:Mailbox
44 {
45 public char *location destroywith g_free;
46 property STRING location (link, flags = CONSTRUCT
47 | MN_MAILBOX_PARAM_LOAD_SAVE
48 | MN_MAILBOX_PARAM_REQUIRED,
49 default_value = "https://mail.google.com/mail/feed/atom");
50
51 public char *label destroywith g_free;
52 property STRING label (link, flags = MN_MAILBOX_PARAM_LOAD_SAVE);
53
54 private GnomeVFSURI *uri unrefwith gnome_vfs_uri_unref;
55
56 class_init (class)
57 {
58 MN_MAILBOX_CLASS(class)->type = "gmail";
59
60 /* 5 minutes is a good default */
61 MN_MAILBOX_CLASS(class)->default_check_delay = 60 * 5;
62 }
63
64 init (self)
65 {
66 mn_mailbox_set_format(MN_MAILBOX(self), "Gmail");
67 mn_mailbox_set_stock_id(MN_MAILBOX(self), MN_STOCK_GMAIL);
68 }
69
70 override (MN:Mailbox) void
71 seal (MNMailbox *mailbox)
72 {
73 MNAuthenticatedMailbox *auth_mailbox = MN_AUTHENTICATED_MAILBOX(mailbox);
74 Self *self = SELF(mailbox);
75 GnomeVFSURI *uri;
76
77 PARENT_HANDLER(mailbox);
78
79 if (! mailbox->runtime_name)
80 mailbox->runtime_name = self_build_name(auth_mailbox->username, self->label);
81
82 uri = self_build_uri(self);
83 if (uri)
84 {
85 /*
86 * Use keyring attributes that are compatible with what
87 * GnomeVFS uses. This allows the password entered by the user
88 * at the GnomeVFS password prompt to be recognized by Mail
89 * Notification and displayed in the mailbox properties
90 * dialog.
91 */
92 auth_mailbox->keyring_server = g_strdup(gnome_vfs_uri_get_host_name(uri));
93 auth_mailbox->keyring_protocol = g_strdup("http");
94 auth_mailbox->keyring_authtype = g_strdup("basic");
95 auth_mailbox->keyring_port = gnome_vfs_uri_get_host_port(uri);
96 if (auth_mailbox->keyring_port == 0)
97 {
98 const char *scheme;
99
100 scheme = gnome_vfs_uri_get_scheme(uri);
101 if (! strcmp(scheme, "http"))
102 auth_mailbox->keyring_port = 80;
103 else if (! strcmp(scheme, "https"))
104 auth_mailbox->keyring_port = 443;
105 }
106
107 gnome_vfs_uri_unref(uri);
108 }
109 else
110 /* fallback */
111 auth_mailbox->keyring_domain = g_strdup("gmail.com");
112 }
113
114 private GnomeVFSURI *
115 build_uri (self)
116 {
117 GnomeVFSURI *uri;
118
119 uri = gnome_vfs_uri_new(self->location);
120 if (uri)
121 {
122 GnomeVFSURI *new_uri;
123
124 if (self->label)
125 {
126 new_uri = gnome_vfs_uri_append_file_name(uri, self->label);
127 gnome_vfs_uri_unref(uri);
128 uri = new_uri;
129 }
130 }
131
132 return uri;
133 }
134
135 override (MN:Authenticated:Mailbox) void
136 authenticated_check (MNAuthenticatedMailbox *mailbox)
137 {
138 Self *self = SELF(mailbox);
139 GnomeVFSResult result;
140 char *atom;
141 int atom_size;
142 xmlDoc *doc;
143 xmlNode *node;
144
145 PARENT_HANDLER(mailbox);
146
147 if (! selfp->uri)
148 {
149 selfp->uri = self_build_uri(self);
150 if (! selfp->uri)
151 {
152 GDK_THREADS_ENTER();
153
154 mn_mailbox_set_error(MN_MAILBOX(self), _("invalid feed location"));
155 /* error is not recoverable, disable the mailbox */
156 mn_mailbox_set_poll(MN_MAILBOX(self), FALSE);
157
158 gdk_flush();
159 GDK_THREADS_LEAVE();
160
161 return;
162 }
163
164 /*
165 * Obtain the password from the keyring but do not prompt
166 * (GnomeVFS will prompt on our behalf if the password is
167 * missing).
168 */
169 mn_authenticated_mailbox_fill_password(mailbox, FALSE);
170
171 gnome_vfs_uri_set_user_name(selfp->uri, mailbox->username);
172 gnome_vfs_uri_set_password(selfp->uri, mailbox->runtime_password);
173 }
174
175 mn_mailbox_notice(MN_MAILBOX(self), _("retrieving feed from %s"), self->location);
176
177 result = mn_vfs_read_entire_file_uri(selfp->uri, &atom_size, &atom);
178 if (result != GNOME_VFS_OK)
179 {
180 GDK_THREADS_ENTER();
181
182 mn_mailbox_set_error(MN_MAILBOX(self), _("unable to retrieve feed: %s"), gnome_vfs_result_to_string(result));
183
184 gdk_flush();
185 GDK_THREADS_LEAVE();
186
187 return;
188 }
189
190 mn_mailbox_notice(MN_MAILBOX(self),
191 ngettext("feed retrieved successfully (%i byte)",
192 "feed retrieved successfully (%i bytes)",
193 atom_size),
194 atom_size);
195
196 doc = xmlParseMemory(atom, atom_size);
197 g_free(atom);
198
199 if (! doc)
200 {
201 GDK_THREADS_ENTER();
202
203 mn_mailbox_set_error(MN_MAILBOX(self), _("unable to parse feed"));
204
205 gdk_flush();
206 GDK_THREADS_LEAVE();
207
208 return;
209 }
210
211 node = xmlDocGetRootElement(doc);
212 if (node && ! strcmp(node->name, "feed"))
213 {
214 GSList *messages = NULL;
215
216 for (node = node->children; node; node = node->next)
217 if (node->type == XML_ELEMENT_NODE && ! strcmp(node->name, "entry"))
218 {
219 time_t sent_time;
220 char *id;
221 char *from;
222 char *subject;
223 char *url;
224
225 self_parse_entry(node, &sent_time, &id, &from, &subject, &url);
226
227 messages = g_slist_prepend(messages, mn_message_new(MN_MAILBOX(self),
228 sent_time,
229 id,
230 NULL,
231 from,
232 subject,
233 url,
234 MN_MESSAGE_NEW));
235
236 g_free(id);
237 g_free(from);
238 g_free(subject);
239 g_free(url);
240 }
241
242 GDK_THREADS_ENTER();
243
244 mn_mailbox_set_messages(MN_MAILBOX(self), messages);
245 mn_g_object_slist_free(messages);
246
247 gdk_flush();
248 GDK_THREADS_LEAVE();
249 }
250 else
251 {
252 GDK_THREADS_ENTER();
253
254 mn_mailbox_set_error(MN_MAILBOX(self), _("unable to parse feed"));
255
256 gdk_flush();
257 GDK_THREADS_LEAVE();
258 }
259
260 xmlFreeDoc(doc);
261 }
262
263 private void
264 parse_entry (xmlNode *node (check null),
265 time_t *sent_time (check null),
266 char **id (check null),
267 char **from (check null),
268 char **subject (check null),
269 char **url (check null))
270 {
271 *sent_time = 0;
272 *id = NULL;
273 *from = NULL;
274 *subject = NULL;
275 *url = NULL;
276
277 for (node = node->children; node; node = node->next)
278 if (node->type == XML_ELEMENT_NODE)
279 {
280 if (! strcmp(node->name, "title"))
281 {
282 g_free(*subject);
283 *subject = xmlNodeGetContent(node);
284 }
285 else if (! strcmp(node->name, "id"))
286 {
287 g_free(*id);
288 *id = xmlNodeGetContent(node);
289 }
290 else if (! strcmp(node->name, "author"))
291 {
292 g_free(*from);
293 *from = self_parse_author(node);
294 }
295 else if (! strcmp(node->name, "link"))
296 {
297 g_free(*url);
298 *url = xmlGetProp(node, "href");
299 }
300 else if (! strcmp(node->name, "issued"))
301 {
302 char *w3c_datetime;
303
304 w3c_datetime = xmlNodeGetContent(node);
305 if (w3c_datetime)
306 {
307 *sent_time = self_parse_date(w3c_datetime);
308 g_free(w3c_datetime);
309 }
310 }
311 }
312 }
313
314 private char *
315 parse_author (xmlNode *node (check null))
316 {
317 char *from;
318 char *name = NULL;
319 char *email = NULL;
320
321 for (node = node->children; node; node = node->next)
322 if (node->type == XML_ELEMENT_NODE)
323 {
324 if (! strcmp(node->name, "name"))
325 {
326 g_free(name);
327 name = xmlNodeGetContent(node);
328 }
329 else if (! strcmp(node->name, "email"))
330 {
331 g_free(email);
332 email = xmlNodeGetContent(node);
333 }
334 }
335
336 from = name && email
337 ? g_strdup_printf("%s <%s>", name, email)
338 : g_strdup(name ? name : email);
339
340 g_free(name);
341 g_free(email);
342
343 return from;
344 }
345
346 private time_t
347 parse_date (const char *w3c_datetime (check null))
348 {
349 #ifdef HAVE_TIMEGM
350 time_t t = 0;
351 char year[5];
352 char month[3];
353 char day[3];
354 char hour[3];
355 char minute[3];
356 char second[3];
357
358 if (sscanf(w3c_datetime, "%4s-%2s-%2sT%2s:%2s:%2sZ", year, month, day, hour, minute, second) == 6
359 && mn_str_isnumeric(year)
360 && mn_str_isnumeric(month)
361 && mn_str_isnumeric(day)
362 && mn_str_isnumeric(hour)
363 && mn_str_isnumeric(minute)
364 && mn_str_isnumeric(second))
365 {
366 struct tm tm = { 0 };
367
368 tm.tm_sec = atoi(second);
369 tm.tm_min = atoi(minute);
370 tm.tm_hour = atoi(hour);
371 tm.tm_mday = atoi(day);
372 tm.tm_mon = atoi(month) - 1;
373 tm.tm_year = atoi(year) - 1900;
374
375 t = timegm(&tm);
376 if (t == -1)
377 t = 0;
378 }
379
380 return t;
381 #else
382 return 0;
383 #endif
384 }
385
386 public char *
387 build_name (const char *username (check null), const char *label)
388 {
389 GString *name;
390
391 name = g_string_new(NULL);
392
393 if (g_str_has_suffix(username, "@gmail.com"))
394 g_string_append(name, username);
395 else
396 g_string_append_printf(name, "%s@gmail.com", username);
397
398 if (label)
399 g_string_append_printf(name, "/%s", label);
400
401 return g_string_free(name, FALSE);
402 }
403 }