/*
* Claws Mail -- a GTK+ based, lightweight, and fast e-mail client
* Copyright (C) 1999-2007 Hiroyuki Yamamoto and the Claws Mail Team
* Copyright (C) 2006-2007 Ricardo Mones
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software Foundation,
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
#include "attachwarner.h"
#include "attachwarner_prefs.h"
#include "codeconv.h"
#include "prefs_common.h"
#include "pluginconfig.h"
/** Identifier for the hook. */
static guint hook_id;
/**
* Builds a single regular expresion from an array of srings.
*
* @param strings The lines containing the different sub-regexp.
*
* @return The newly allocated regexp.
*/
static gchar *build_complete_regexp(gchar **strings)
{
int i = 0;
gchar *expr = NULL;
while (strings && strings[i] && *strings[i]) {
int old_len = expr ? strlen(expr):0;
int new_len = 0;
gchar *tmpstr = NULL;
if (g_utf8_validate(strings[i], -1, NULL))
tmpstr = g_strdup(strings[i]);
else
tmpstr = conv_codeset_strdup(strings[i],
conv_get_locale_charset_str_no_utf8(),
CS_INTERNAL);
if (strstr(tmpstr, "\n"))
*(strstr(tmpstr, "\n")) = '\0';
new_len = strlen(tmpstr);
expr = g_realloc(expr,
expr ? (old_len + strlen("|()") + new_len + 1)
: (strlen("()") + new_len + 1));
if (old_len) {
strcpy(expr + old_len, "|(");
strcpy(expr + old_len + 2, tmpstr);
strcpy(expr + old_len + 2 + new_len, ")");
} else {
strcpy(expr+old_len, "(");
strcpy(expr+old_len + 1, tmpstr);
strcpy(expr+old_len + 1 + new_len, ")");
}
g_free(tmpstr);
i++;
}
return expr;
}
/**
* Creates the matcher.
*
* @return A newly allocated regexp matcher or null if no memory is available.
*/
MatcherProp * new_matcherprop(void)
{
MatcherProp *m = NULL;
gchar **strings = g_strsplit(attwarnerprefs.match_strings, "\n", -1);
gchar *expr = NULL;
expr = build_complete_regexp(strings);
g_strfreev(strings);
debug_print("building matcherprop for expr '%s'\n", expr);
m = matcherprop_new(MATCHCRITERIA_SUBJECT, NULL, MATCHTYPE_REGEXP,
expr, 0);
if (m == NULL) {
/* print error message */
debug_print("failed to allocate memory for matcherprop\n");
}
g_free(expr);
return m;
}
/**
* Searches the str for matches.
*
* @param mp The matcher to use for searching.
* @param str The string to search for matches.
*
* @return TRUE if the string given matches, FALSE otherwise.
*/
static gboolean aw_matcherprop_string_match(MatcherProp *mp, gchar *str)
{
MsgInfo info;
gboolean ret = FALSE;
if (attwarnerprefs.skip_quotes
&& *str != '\0'
&& *prefs_common.quote_chars != '\0') {
gchar **lines = g_strsplit(str, "\n", -1);
int i;
for (i = 0; lines[i] != NULL && ret == FALSE; i++) {
if (line_has_quote_char(lines[i],
prefs_common.quote_chars) == NULL) {
info.subject = lines[i];
ret = matcherprop_match(mp, &info);
}
}
g_strfreev(lines);
} else {
info.subject = str;
ret = matcherprop_match(mp, &info);
}
return ret;
}
/**
* Looks for attachment references in the composer text.
*
* @param compose The composer object to inspect.
*
* @return TRUE if attachment references are found, FALSE otherwise.
*/
gboolean are_attachments_mentioned(Compose *compose)
{
GtkTextView *textview = NULL;
GtkTextBuffer *textbuffer = NULL;
GtkTextIter start, end;
gchar *text = NULL;
gboolean mentioned = FALSE;
MatcherProp *matcher = new_matcherprop();
if (matcher == NULL) {
g_warning("couldn't allocate matcher");
return FALSE;
}
textview = GTK_TEXT_VIEW(compose->text);
textbuffer = gtk_text_view_get_buffer(textview);
gtk_text_buffer_get_start_iter(textbuffer, &start);
gtk_text_buffer_get_end_iter(textbuffer, &end);
text = gtk_text_buffer_get_text(textbuffer, &start, &end, FALSE);
debug_print("checking text for attachment mentions\n");
if (text != NULL) {
mentioned = aw_matcherprop_string_match(matcher, text);
g_free(text);
}
if (matcher != NULL)
matcherprop_free(matcher);
return mentioned;
}
/**
* Looks for files attached in the composer.
*
* @param compose The composer object to inspect.
*
* @return TRUE if there is one or more files attached, FALSE otherwise.
*/
gboolean does_not_have_attachments(Compose *compose)
{
GtkTreeView *tree_view = GTK_TREE_VIEW(compose->attach_clist);
GtkTreeModel *model;
GtkTreeIter iter;
model = gtk_tree_view_get_model(tree_view);
debug_print("checking for attachments existence\n");
if (!gtk_tree_model_get_iter_first(model, &iter))
return TRUE;
return FALSE;
}
/**
* Check whether not check while redirecting or forwarding.
*
* @param mode The current compose->mode.
*
* @return TRUE for cancel further checking because it's being redirected or
* forwarded and user configured not to check, FALSE otherwise.
*/
gboolean do_not_check_redirect_forward(int mode)
{
switch (mode) {
case COMPOSE_FORWARD:
case COMPOSE_FORWARD_AS_ATTACH:
case COMPOSE_FORWARD_INLINE:
case COMPOSE_REDIRECT:
if (attwarnerprefs.skip_forwards_and_redirections)
return TRUE;
default:
return FALSE;
}
}
/**
* Callback function to be called before sending the mail.
*
* @param source The composer to be checked.
* @param data Additional data.
*
* @return TRUE if no attachments are mentioned or files are attached,
* FALSE if attachments are mentioned and no files are attached.
*/
gboolean my_before_send_hook(gpointer source, gpointer data)
{
Compose *compose = (Compose *)source;
gboolean askuser = FALSE;
debug_print("attachwarner invoked\n");
if (compose->batch)
return FALSE; /* do not check while queuing */
if (do_not_check_redirect_forward(compose->mode))
return FALSE;
askuser = (does_not_have_attachments(compose)
&& are_attachments_mentioned(compose));
if (askuser) {
AlertValue aval;
aval = alertpanel(_("Attachment warning"),
_("An attachment is mentioned in the mail you're sending, but no file was attached. Send it anyway?"),
GTK_STOCK_CANCEL, _("+_Send"), NULL);
if (aval != G_ALERTALTERNATE)
return TRUE;
}
return FALSE; /* continue sending */
}
/**
* Initialize plugin.
*
* @param error For storing the returned error message.
*
* @return 0 if initialization succeeds, -1 on failure.
*/
gint plugin_init(gchar **error)
{
bindtextdomain(TEXTDOMAIN, LOCALEDIR);
bind_textdomain_codeset(TEXTDOMAIN, "UTF-8");
if (!check_plugin_version(MAKE_NUMERIC_VERSION(2,9,2,72),
VERSION_NUMERIC, _("Attach warner"), error))
return -1;
hook_id = hooks_register_hook(COMPOSE_CHECK_BEFORE_SEND_HOOKLIST,
my_before_send_hook, NULL);
if (hook_id == -1) {
*error = g_strdup(_("Failed to register check before send hook"));
return -1;
}
attachwarner_prefs_init();
debug_print("Attachment warner plugin loaded\n");
return 0;
}
/**
* Destructor for the plugin.
* Unregister the callback function and frees matcher.
*/
gboolean plugin_done(void)
{
hooks_unregister_hook(COMPOSE_CHECK_BEFORE_SEND_HOOKLIST, hook_id);
attachwarner_prefs_done();
debug_print("Attachment warner plugin unloaded\n");
return TRUE;
}
/**
* Get the name of the plugin.
*
* @return The plugin name (maybe translated).
*/
const gchar *plugin_name(void)
{
return _("Attach warner");
}
/**
* Get the description of the plugin.
*
* @return The plugin description (maybe translated).
*/
const gchar *plugin_desc(void)
{
return _("Warns user if some reference to attachments is found in the "
"message text and no file is attached.");
}
/**
* Get the kind of plugin.
*
* @return The "GTK2" constant.
*/
const gchar *plugin_type(void)
{
return "GTK2";
}
/**
* Get the license acronym the plugin is released under.
*
* @return The "GPL" constant.
*/
const gchar *plugin_licence(void)
{
return "GPL3+";
}
/**
* Get the version of the plugin.
*
* @return The current version string.
*/
const gchar *plugin_version(void)
{
return PLUGINVERSION;
}
/**
* Get the features implemented by the plugin.
*
* @return A constant PluginFeature structure with the features.
*/
struct PluginFeature *plugin_provides(void)
{
static struct PluginFeature features[] =
{ {PLUGIN_OTHER, N_("Attach warner")},
{PLUGIN_NOTHING, NULL}};
return features;
}
syntax highlighted by Code2HTML, v. 0.9.1