add .cvsignore files
[claws.git] / src / plugins / address_keeper / address_keeper.c
1 /*
2  * Claws Mail -- a GTK+ based, lightweight, and fast e-mail client
3  * Copyright (C) 1999-2009 Hiroyuki Yamamoto and the Claws Mail Team
4  * Copyright (C) 2009-2010 Ricardo Mones
5  *
6  * This program is free software; you can redistribute it and/or modify
7  * it under the terms of the GNU General Public License as published by
8  * the Free Software Foundation; either version 3 of the License, or
9  * (at your option) any later version.
10  *
11  * This program is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14  * GNU General Public License for more details.
15  *
16  * You should have received a copy of the GNU General Public License
17  * along with this program; if not, write to the Free Software Foundation, 
18  * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
19  */
20
21 #ifdef HAVE_CONFIG_H
22 #  include "config.h"
23 #include "claws-features.h"
24 #endif
25
26 #include <glib.h>
27 #include <glib/gi18n.h>
28
29 #include "version.h"
30 #include "address_keeper.h"
31 #include "address_keeper_prefs.h"
32 #include "addr_compl.h"
33 #include "addrbook.h"
34 #include "codeconv.h"
35 #include "prefs_common.h"
36
37 /** Identifier for the hook. */
38 static guint hook_id;
39
40 /**
41  * Extracts name from an address.
42  *
43  * @param addr The full address.
44  * @return The name found in the address as a newly allocated string, or NULL if
45  * not found.
46  */
47 gchar *get_name_from_addr(const gchar *addr)
48 {
49         gchar *name = NULL;
50
51         if (addr == NULL || *addr == '\0')
52                 return NULL;
53         name = strchr(addr, '@');
54         if (name == NULL)
55                 return NULL;
56         --name;
57         while (name >= addr && !g_ascii_isspace(*name)) --name;
58         while (name >= addr && g_ascii_isspace(*name)) --name;
59         if (name > addr) {
60                 ++name; /* recover non-space char */
61                 return g_strndup(addr, name - addr);
62         }
63         return NULL;
64 }
65
66 /**
67  * Extracts comment from an address.
68  *
69  * @param addr The full address.
70  * @return The comment found in the address as a newly allocated string, or NULL if
71  * not found.
72  */
73 gchar *get_comment_from_addr(const gchar *addr)
74 {
75         gchar *comm = NULL;
76
77         if (addr == NULL || *addr == '\0')
78                 return NULL;
79         comm = strchr(addr, '@');
80         if (comm == NULL)
81                 return NULL;
82         ++comm;
83         while (*comm && !g_ascii_isspace(*comm)) ++comm;
84         while (*comm && g_ascii_isspace(*comm)) ++comm;
85         if (*comm)
86                 return g_strdup(comm);
87         return NULL;
88 }
89
90 /**
91  * Saves an address to the configured addressbook folder if not known.
92  *
93  * @param abf The address book file containing target folder.
94  * @param folder The address book folder where addresses are added.
95  * @param addr The address to be added.
96  */
97 void keep_if_unknown(AddressBookFile * abf, ItemFolder * folder, gchar *addr)
98 {
99         gchar *clean_addr = NULL;
100         gchar *keepto = addkeeperprefs.addressbook_folder;
101
102         debug_print("checking addr '%s'\n", addr);
103         clean_addr = g_strdup(addr);
104         extract_address(clean_addr);
105         start_address_completion(NULL);
106         if (complete_matches_found(clean_addr) == 0) {
107                 gchar *a_name;
108                 gchar *a_comment;
109                 debug_print("adding addr '%s' to addressbook '%s'\n",
110                             clean_addr, keepto);
111                 a_name = get_name_from_addr(addr);
112                 a_comment = get_comment_from_addr(addr);
113                 if (!addrbook_add_contact(abf, folder, a_name, clean_addr, a_comment)) {
114                         g_warning("contact could not be added\n");
115                 } else {
116                         addressbook_refresh();
117                 }
118                 if (a_name != NULL)
119                         g_free(a_name);
120                 if (a_comment != NULL)
121                         g_free(a_comment);
122         } else {
123                 debug_print("found addr '%s' in addressbook '%s', skipping\n",
124                             clean_addr, keepto);
125         }
126         end_address_completion();
127         g_free(clean_addr);
128 }
129
130 /**
131  * Callback function to be called before sending the mail.
132  * 
133  * @param source The composer to be checked.
134  * @param data Additional data.
135  *
136  * @return FALSE always: we're only annotating addresses.
137  */
138 static gboolean addrk_before_send_hook(gpointer source, gpointer data)
139 {
140         Compose *compose = (Compose *)source;
141         AddressDataSource *book = NULL;
142         AddressBookFile *abf = NULL;
143         ItemFolder *folder = NULL;
144         gchar *keepto = addkeeperprefs.addressbook_folder;
145         GSList *cur;
146         const gchar *to_hdr;
147         const gchar *cc_hdr;
148         const gchar *bcc_hdr;
149
150         debug_print("address_keeper invoked!\n");
151         if (compose->batch)
152                 return FALSE;   /* do not check while queuing */
153
154         if (keepto == NULL || *keepto == '\0') {
155                 g_warning("addressbook folder not configured");
156                 return FALSE;
157         }
158
159         if (!addressbook_peek_folder_exists(keepto, &book, &folder)) {
160                 g_warning("addressbook folder not found '%s'\n", keepto);
161                 return FALSE;
162         }
163         if (!book) {
164                 g_warning("addressbook_peek_folder_exists: NULL book\n");
165                 return FALSE;
166         }
167         abf = book->rawDataSource;
168
169         to_hdr = prefs_common_translated_header_name("To:");
170         cc_hdr = prefs_common_translated_header_name("Cc:");
171         bcc_hdr = prefs_common_translated_header_name("Bcc:");
172
173         for (cur = compose->header_list; cur != NULL; cur = cur->next) {
174                 gchar *header;
175                 gchar *entry;
176                 header = gtk_editable_get_chars(GTK_EDITABLE(
177                                 gtk_bin_get_child(GTK_BIN(
178                                         (((ComposeHeaderEntry *)cur->data)->combo)))), 0, -1);
179                 entry = gtk_editable_get_chars(GTK_EDITABLE(
180                                 ((ComposeHeaderEntry *)cur->data)->entry), 0, -1);
181                 g_strstrip(entry);
182                 g_strstrip(header);
183                 if (*entry != '\0') {
184                         if (!g_ascii_strcasecmp(header, to_hdr)
185                                 && addkeeperprefs.keep_to_addrs == TRUE) {
186                                 keep_if_unknown(abf, folder, entry);
187                         }
188                         if (!g_ascii_strcasecmp(header, cc_hdr)
189                                 && addkeeperprefs.keep_cc_addrs == TRUE) {
190                                 keep_if_unknown(abf, folder, entry);
191                         }
192                         if (!g_ascii_strcasecmp(header, bcc_hdr)
193                                 && addkeeperprefs.keep_bcc_addrs == TRUE) {
194                                 keep_if_unknown(abf, folder, entry);
195                         }
196                 }
197                 g_free(header);
198                 g_free(entry);
199         }       
200
201         return FALSE;   /* continue sending */
202 }
203
204 /**
205  * Initialize plugin.
206  *
207  * @param error  For storing the returned error message.
208  *
209  * @return 0 if initialization succeeds, -1 on failure.
210  */
211 gint plugin_init(gchar **error)
212 {
213         if (!check_plugin_version(MAKE_NUMERIC_VERSION(2,9,2,72),
214                                   VERSION_NUMERIC, PLUGIN_NAME, error))
215                 return -1;
216
217         hook_id = hooks_register_hook(COMPOSE_CHECK_BEFORE_SEND_HOOKLIST, 
218                                       addrk_before_send_hook, NULL);
219         
220         if (hook_id == -1) {
221                 *error = g_strdup(_("Failed to register check before send hook"));
222                 return -1;
223         }
224
225         address_keeper_prefs_init();
226
227         debug_print("Address Keeper plugin loaded\n");
228
229         return 0;
230 }
231
232 /**
233  * Destructor for the plugin.
234  * Unregister the callback function and frees matcher.
235  */
236 gboolean plugin_done(void)
237 {       
238         hooks_unregister_hook(COMPOSE_CHECK_BEFORE_SEND_HOOKLIST, hook_id);
239         address_keeper_prefs_done();
240         debug_print("Address Keeper plugin unloaded\n");
241         return TRUE;
242 }
243
244 /**
245  * Get the name of the plugin.
246  *
247  * @return The plugin name (maybe translated).
248  */
249 const gchar *plugin_name(void)
250 {
251         return PLUGIN_NAME;
252 }
253
254 /**
255  * Get the description of the plugin.
256  *
257  * @return The plugin description (maybe translated).
258  */
259 const gchar *plugin_desc(void)
260 {
261         return _("Keeps all recipient addresses in an addressbook folder.");
262 }
263
264 /**
265  * Get the kind of plugin.
266  *
267  * @return The "GTK2" constant.
268  */
269 const gchar *plugin_type(void)
270 {
271         return "GTK2";
272 }
273
274 /**
275  * Get the license acronym the plugin is released under.
276  *
277  * @return The "GPL3+" constant.
278  */
279 const gchar *plugin_licence(void)
280 {
281         return "GPL3+";
282 }
283
284 /**
285  * Get the version of the plugin.
286  *
287  * @return The current version string.
288  */
289 const gchar *plugin_version(void)
290 {
291         return VERSION;
292 }
293
294 /**
295  * Get the features implemented by the plugin.
296  *
297  * @return A constant PluginFeature structure with the features.
298  */
299 struct PluginFeature *plugin_provides(void)
300 {
301         static struct PluginFeature features[] = 
302                 { {PLUGIN_OTHER, N_("Address Keeper")},
303                   {PLUGIN_NOTHING, NULL}};
304
305         return features;
306 }
307