2 * Claws Mail -- a GTK+ based, lightweight, and fast e-mail client
3 * Copyright (C) 2009-2015 Ricardo Mones and the Claws Mail Team
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.
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.
15 * You should have received a copy of the GNU General Public License
16 * along with this program. If not, see <http://www.gnu.org/licenses/>.
21 #include "claws-features.h"
25 #include <glib/gi18n.h>
28 #include "address_keeper.h"
29 #include "address_keeper_prefs.h"
30 #include "addr_compl.h"
33 #include "prefs_common.h"
35 /** Identifier for the hook. */
36 static gulong hook_id = HOOK_NONE;
39 * Extracts name from an address.
41 * @param addr The full address.
42 * @return The name found in the address as a newly allocated string, or NULL if
45 gchar *get_name_from_addr(const gchar *addr)
49 if (addr == NULL || *addr == '\0')
51 name = strchr(addr, '@');
55 while (name >= addr && !g_ascii_isspace(*name)) --name;
56 while (name >= addr && g_ascii_isspace(*name)) --name;
58 ++name; /* recover non-space char */
59 return g_strndup(addr, name - addr);
65 * Extracts comment from an address.
67 * @param addr The full address.
68 * @return The comment found in the address as a newly allocated string, or NULL if
71 gchar *get_comment_from_addr(const gchar *addr)
75 if (addr == NULL || *addr == '\0')
77 comm = strchr(addr, '@');
81 while (*comm && !g_ascii_isspace(*comm)) ++comm;
82 while (*comm && g_ascii_isspace(*comm)) ++comm;
84 return g_strdup(comm);
89 * Checks an address for matching a blocked address pattern.
91 * @param addr The full address.
92 * @param blocked The regexp matching blocked addresses.
94 * @return TRUE if given address matches any of the patterns, FALSE otherwise.
96 gboolean matches_blocked_address(gchar *addr, MatcherList *blocked)
98 if (blocked != NULL) {
102 return matcherlist_match(blocked, &info);
108 * Saves an address to the configured addressbook folder if not known.
110 * @param abf The address book file containing target folder.
111 * @param folder The address book folder where addresses are added.
112 * @param addr The address to be added.
113 * @param blocked The regexp matching blocked addresses.
115 void keep_if_unknown(AddressBookFile * abf, ItemFolder * folder, gchar *addr, MatcherList *blocked)
117 gchar *clean_addr = NULL;
118 gchar *keepto = addkeeperprefs.addressbook_folder;
120 debug_print("checking addr '%s'\n", addr);
121 if (matches_blocked_address(addr, blocked)) {
122 debug_print("addr '%s' is blocked by regexp\n", addr);
125 clean_addr = g_strdup(addr);
126 extract_address(clean_addr);
127 start_address_completion(NULL);
128 if (complete_matches_found(clean_addr) == 0) {
131 debug_print("adding addr '%s' to addressbook '%s'\n",
133 a_name = get_name_from_addr(addr);
134 a_comment = get_comment_from_addr(addr);
135 if (!addrbook_add_contact(abf, folder, a_name, clean_addr, a_comment)) {
136 g_warning("contact could not be added");
138 addressbook_refresh();
142 if (a_comment != NULL)
145 debug_print("found addr '%s' in addressbook '%s', skipping\n",
148 end_address_completion();
153 * Callback function to be called before sending the mail.
155 * @param source The composer to be checked.
156 * @param data Additional data.
158 * @return FALSE always: we're only annotating addresses.
160 static gboolean addrk_before_send_hook(gpointer source, gpointer data)
162 Compose *compose = (Compose *)source;
163 AddressDataSource *book = NULL;
164 AddressBookFile *abf = NULL;
165 ItemFolder *folder = NULL;
166 gchar *keepto = addkeeperprefs.addressbook_folder;
170 const gchar *bcc_hdr;
171 MatcherList *blocked = NULL;
173 debug_print("address_keeper invoked!\n");
175 return FALSE; /* do not check while queuing */
177 if (keepto == NULL || *keepto == '\0') {
178 g_warning("addressbook folder not configured");
182 if (!addressbook_peek_folder_exists(keepto, &book, &folder)) {
183 g_warning("addressbook folder not found '%s'", keepto);
187 g_warning("addressbook_peek_folder_exists: NULL book");
190 abf = book->rawDataSource;
192 to_hdr = prefs_common_translated_header_name("To:");
193 cc_hdr = prefs_common_translated_header_name("Cc:");
194 bcc_hdr = prefs_common_translated_header_name("Bcc:");
196 if (addkeeperprefs.block_matching_addrs != NULL
197 && addkeeperprefs.block_matching_addrs[0] != '\0') {
198 blocked = matcherlist_new_from_lines(addkeeperprefs.block_matching_addrs, FALSE, FALSE);
200 g_warning("couldn't allocate matcher");
202 for (cur = compose->header_list; cur != NULL; cur = cur->next) {
205 header = gtk_editable_get_chars(GTK_EDITABLE(
206 gtk_bin_get_child(GTK_BIN(
207 (((ComposeHeaderEntry *)cur->data)->combo)))), 0, -1);
208 entry = gtk_editable_get_chars(GTK_EDITABLE(
209 ((ComposeHeaderEntry *)cur->data)->entry), 0, -1);
212 if (*entry != '\0') {
213 if (!g_ascii_strcasecmp(header, to_hdr)
214 && addkeeperprefs.keep_to_addrs == TRUE) {
215 keep_if_unknown(abf, folder, entry, blocked);
217 if (!g_ascii_strcasecmp(header, cc_hdr)
218 && addkeeperprefs.keep_cc_addrs == TRUE) {
219 keep_if_unknown(abf, folder, entry, blocked);
221 if (!g_ascii_strcasecmp(header, bcc_hdr)
222 && addkeeperprefs.keep_bcc_addrs == TRUE) {
223 keep_if_unknown(abf, folder, entry, blocked);
230 matcherlist_free(blocked);
232 return FALSE; /* continue sending */
238 * @param error For storing the returned error message.
240 * @return 0 if initialization succeeds, -1 on failure.
242 gint plugin_init(gchar **error)
244 if (!check_plugin_version(MAKE_NUMERIC_VERSION(2,9,2,72),
245 VERSION_NUMERIC, PLUGIN_NAME, error))
248 hook_id = hooks_register_hook(COMPOSE_CHECK_BEFORE_SEND_HOOKLIST,
249 addrk_before_send_hook, NULL);
251 if (hook_id == HOOK_NONE) {
252 *error = g_strdup(_("Failed to register check before send hook"));
256 address_keeper_prefs_init();
258 debug_print("Address Keeper plugin loaded\n");
264 * Destructor for the plugin.
265 * Unregister the callback function and frees matcher.
267 gboolean plugin_done(void)
269 hooks_unregister_hook(COMPOSE_CHECK_BEFORE_SEND_HOOKLIST, hook_id);
270 address_keeper_prefs_done();
271 debug_print("Address Keeper plugin unloaded\n");
276 * Get the name of the plugin.
278 * @return The plugin name (maybe translated).
280 const gchar *plugin_name(void)
286 * Get the description of the plugin.
288 * @return The plugin description (maybe translated).
290 const gchar *plugin_desc(void)
292 return _("Keeps all recipient addresses in an addressbook folder.");
296 * Get the kind of plugin.
298 * @return The "GTK2" constant.
300 const gchar *plugin_type(void)
306 * Get the license acronym the plugin is released under.
308 * @return The "GPL3+" constant.
310 const gchar *plugin_licence(void)
316 * Get the version of the plugin.
318 * @return The current version string.
320 const gchar *plugin_version(void)
326 * Get the features implemented by the plugin.
328 * @return A constant PluginFeature structure with the features.
330 struct PluginFeature *plugin_provides(void)
332 static struct PluginFeature features[] =
333 { {PLUGIN_OTHER, N_("Address Keeper")},
334 {PLUGIN_NOTHING, NULL}};