mail-notification

Fork of Jean-Yves Lefort's mail-notification, a tray icon to notify of new mail
git clone https://code.djc.id.au/git/mail-notification/

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 }