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
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.
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.
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.
23 #include "claws-features.h"
27 #include <glib/gi18n.h>
30 #include "address_keeper.h"
31 #include "address_keeper_prefs.h"
32 #include "addr_compl.h"
35 #include "prefs_common.h"
37 /** Identifier for the hook. */
41 * Extracts name from an address.
43 * @param addr The full address.
44 * @return The name found in the address as a newly allocated string, or NULL if
47 gchar *get_name_from_addr(const gchar *addr)
51 if (addr == NULL || *addr == '\0')
53 name = strchr(addr, '@');
57 while (name >= addr && !g_ascii_isspace(*name)) --name;
58 while (name >= addr && g_ascii_isspace(*name)) --name;
60 ++name; /* recover non-space char */
61 return g_strndup(addr, name - addr);
67 * Extracts comment from an address.
69 * @param addr The full address.
70 * @return The comment found in the address as a newly allocated string, or NULL if
73 gchar *get_comment_from_addr(const gchar *addr)
77 if (addr == NULL || *addr == '\0')
79 comm = strchr(addr, '@');
83 while (*comm && !g_ascii_isspace(*comm)) ++comm;
84 while (*comm && g_ascii_isspace(*comm)) ++comm;
86 return g_strdup(comm);
91 * Checks an address for matching a blocked address pattern.
93 * @param addr The full address.
94 * @param blocked The regexp matching blocked addresses.
96 * @return TRUE if given address matches any of the patterns, FALSE otherwise.
98 gboolean matches_blocked_address(gchar *addr, MatcherList *blocked)
100 if (blocked != NULL) {
104 return matcherlist_match(blocked, &info);
110 * Saves an address to the configured addressbook folder if not known.
112 * @param abf The address book file containing target folder.
113 * @param folder The address book folder where addresses are added.
114 * @param addr The address to be added.
115 * @param blocked The regexp matching blocked addresses.
117 void keep_if_unknown(AddressBookFile * abf, ItemFolder * folder, gchar *addr, MatcherList *blocked)
119 gchar *clean_addr = NULL;
120 gchar *keepto = addkeeperprefs.addressbook_folder;
122 debug_print("checking addr '%s'\n", addr);
123 if (matches_blocked_address(addr, blocked)) {
124 debug_print("addr '%s' is blocked by regexp\n", addr);
127 clean_addr = g_strdup(addr);
128 extract_address(clean_addr);
129 start_address_completion(NULL);
130 if (complete_matches_found(clean_addr) == 0) {
133 debug_print("adding addr '%s' to addressbook '%s'\n",
135 a_name = get_name_from_addr(addr);
136 a_comment = get_comment_from_addr(addr);
137 if (!addrbook_add_contact(abf, folder, a_name, clean_addr, a_comment)) {
138 g_warning("contact could not be added\n");
140 addressbook_refresh();
144 if (a_comment != NULL)
147 debug_print("found addr '%s' in addressbook '%s', skipping\n",
150 end_address_completion();
155 * Callback function to be called before sending the mail.
157 * @param source The composer to be checked.
158 * @param data Additional data.
160 * @return FALSE always: we're only annotating addresses.
162 static gboolean addrk_before_send_hook(gpointer source, gpointer data)
164 Compose *compose = (Compose *)source;
165 AddressDataSource *book = NULL;
166 AddressBookFile *abf = NULL;
167 ItemFolder *folder = NULL;
168 gchar *keepto = addkeeperprefs.addressbook_folder;
172 const gchar *bcc_hdr;
173 MatcherList *blocked = NULL;
175 debug_print("address_keeper invoked!\n");
177 return FALSE; /* do not check while queuing */
179 if (keepto == NULL || *keepto == '\0') {
180 g_warning("addressbook folder not configured");
184 if (!addressbook_peek_folder_exists(keepto, &book, &folder)) {
185 g_warning("addressbook folder not found '%s'\n", keepto);
189 g_warning("addressbook_peek_folder_exists: NULL book\n");
192 abf = book->rawDataSource;
194 to_hdr = prefs_common_translated_header_name("To:");
195 cc_hdr = prefs_common_translated_header_name("Cc:");
196 bcc_hdr = prefs_common_translated_header_name("Bcc:");
198 if (addkeeperprefs.block_matching_addrs != NULL
199 && addkeeperprefs.block_matching_addrs[0] != '\0') {
200 blocked = matcherlist_new_from_lines(addkeeperprefs.block_matching_addrs, FALSE, FALSE);
202 g_warning("couldn't allocate matcher");
204 for (cur = compose->header_list; cur != NULL; cur = cur->next) {
207 header = gtk_editable_get_chars(GTK_EDITABLE(
208 gtk_bin_get_child(GTK_BIN(
209 (((ComposeHeaderEntry *)cur->data)->combo)))), 0, -1);
210 entry = gtk_editable_get_chars(GTK_EDITABLE(
211 ((ComposeHeaderEntry *)cur->data)->entry), 0, -1);
214 if (*entry != '\0') {
215 if (!g_ascii_strcasecmp(header, to_hdr)
216 && addkeeperprefs.keep_to_addrs == TRUE) {
217 keep_if_unknown(abf, folder, entry, blocked);
219 if (!g_ascii_strcasecmp(header, cc_hdr)
220 && addkeeperprefs.keep_cc_addrs == TRUE) {
221 keep_if_unknown(abf, folder, entry, blocked);
223 if (!g_ascii_strcasecmp(header, bcc_hdr)
224 && addkeeperprefs.keep_bcc_addrs == TRUE) {
225 keep_if_unknown(abf, folder, entry, blocked);
232 matcherlist_free(blocked);
234 return FALSE; /* continue sending */
240 * @param error For storing the returned error message.
242 * @return 0 if initialization succeeds, -1 on failure.
244 gint plugin_init(gchar **error)
246 if (!check_plugin_version(MAKE_NUMERIC_VERSION(2,9,2,72),
247 VERSION_NUMERIC, PLUGIN_NAME, error))
250 hook_id = hooks_register_hook(COMPOSE_CHECK_BEFORE_SEND_HOOKLIST,
251 addrk_before_send_hook, NULL);
254 *error = g_strdup(_("Failed to register check before send hook"));
258 address_keeper_prefs_init();
260 debug_print("Address Keeper plugin loaded\n");
266 * Destructor for the plugin.
267 * Unregister the callback function and frees matcher.
269 gboolean plugin_done(void)
271 hooks_unregister_hook(COMPOSE_CHECK_BEFORE_SEND_HOOKLIST, hook_id);
272 address_keeper_prefs_done();
273 debug_print("Address Keeper plugin unloaded\n");
278 * Get the name of the plugin.
280 * @return The plugin name (maybe translated).
282 const gchar *plugin_name(void)
288 * Get the description of the plugin.
290 * @return The plugin description (maybe translated).
292 const gchar *plugin_desc(void)
294 return _("Keeps all recipient addresses in an addressbook folder.");
298 * Get the kind of plugin.
300 * @return The "GTK2" constant.
302 const gchar *plugin_type(void)
308 * Get the license acronym the plugin is released under.
310 * @return The "GPL3+" constant.
312 const gchar *plugin_licence(void)
318 * Get the version of the plugin.
320 * @return The current version string.
322 const gchar *plugin_version(void)
328 * Get the features implemented by the plugin.
330 * @return A constant PluginFeature structure with the features.
332 struct PluginFeature *plugin_provides(void)
334 static struct PluginFeature features[] =
335 { {PLUGIN_OTHER, N_("Address Keeper")},
336 {PLUGIN_NOTHING, NULL}};