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 }