2 * Claws Mail -- a GTK based, lightweight, and fast e-mail client
3 * Copyright (C) 2006-2022 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 "attachwarner.h"
29 #include "attachwarner_prefs.h"
31 #include "prefs_common.h"
33 /** Identifier for the hook. */
34 static gulong hook_id
= HOOK_NONE
;
36 static AttachWarnerMention
*aw_matcherlist_string_match(MatcherList
*matchers
, gchar
*str
, gchar
*sig_separator
)
42 AttachWarnerMention
*awm
= NULL
;
44 if (str
== NULL
|| *str
== '\0') {
48 lines
= g_strsplit(str
, "\n", -1);
49 if (attwarnerprefs
.skip_quotes
50 && *prefs_common_get_prefs()->quote_chars
!= '\0') {
51 debug_print("checking without quotes\n");
52 for (i
= 0; lines
[i
] != NULL
&& ret
== FALSE
; i
++) {
53 if(attwarnerprefs
.skip_signature
54 && sig_separator
!= NULL
55 && *sig_separator
!= '\0'
56 && strcmp(lines
[i
], sig_separator
) == 0) {
57 debug_print("reached signature delimiter at line %d\n", i
);
60 if (line_has_quote_char(lines
[i
],
61 prefs_common_get_prefs()->quote_chars
) == NULL
) {
62 debug_print("testing line %d\n", i
);
63 info
.subject
= lines
[i
];
64 ret
= matcherlist_match(matchers
, &info
);
65 debug_print("line %d: %d\n", i
, ret
);
69 debug_print("checking with quotes\n");
70 for (i
= 0; lines
[i
] != NULL
&& ret
== FALSE
; i
++) {
71 if(attwarnerprefs
.skip_signature
72 && sig_separator
!= NULL
73 && *sig_separator
!= '\0'
74 && strcmp(lines
[i
], sig_separator
) == 0) {
75 debug_print("reached signature delimiter at line %d\n", i
);
78 debug_print("testing line %d\n", i
);
79 info
.subject
= lines
[i
];
80 ret
= matcherlist_match(matchers
, &info
);
81 debug_print("line %d: %d\n", i
, ret
);
85 awm
= g_new0(AttachWarnerMention
, 1);
86 awm
->line
= i
; /* usual humans count lines from 1 */
87 awm
->context
= g_strdup(lines
[i
- 1]);
88 debug_print("found at line %d, context \"%s\"\n", awm
->line
, awm
->context
);
96 * Looks for attachment references in the composer text.
98 * @param compose The composer object to inspect.
100 * @return A pointer to an AttachWarnerMention if attachment references
101 * are found, or NULL otherwise.
103 AttachWarnerMention
*are_attachments_mentioned(Compose
*compose
)
105 GtkTextView
*textview
= NULL
;
106 GtkTextBuffer
*textbuffer
= NULL
;
107 GtkTextIter start
, end
;
109 AttachWarnerMention
*mention
= NULL
;
110 MatcherList
*matchers
= NULL
;
112 if (attwarnerprefs
.match_strings
!= NULL
113 && attwarnerprefs
.match_strings
[0] != '\0') {
114 matchers
= matcherlist_new_from_lines(attwarnerprefs
.match_strings
, FALSE
, attwarnerprefs
.case_sensitive
);
117 textview
= GTK_TEXT_VIEW(compose
->text
);
118 textbuffer
= gtk_text_view_get_buffer(textview
);
119 gtk_text_buffer_get_start_iter(textbuffer
, &start
);
120 gtk_text_buffer_get_end_iter(textbuffer
, &end
);
121 text
= gtk_text_buffer_get_text(textbuffer
, &start
, &end
, FALSE
);
123 debug_print("checking text for attachment mentions\n");
125 mention
= aw_matcherlist_string_match(matchers
, text
, compose
->account
->sig_sep
);
128 matcherlist_free(matchers
);
129 debug_print("done\n");
131 g_warning("couldn't allocate matcher");
137 * Looks for files attached in the composer.
139 * @param compose The composer object to inspect.
141 * @return TRUE if there is one or more files attached, FALSE otherwise.
143 gboolean
does_not_have_attachments(Compose
*compose
)
145 GtkTreeView
*tree_view
= GTK_TREE_VIEW(compose
->attach_clist
);
149 model
= gtk_tree_view_get_model(tree_view
);
151 debug_print("checking for attachments existence\n");
152 if (!gtk_tree_model_get_iter_first(model
, &iter
))
159 * Check whether not check while redirecting or forwarding.
161 * @param mode The current compose->mode.
163 * @return TRUE for cancel further checking because it's being redirected or
164 * forwarded and user configured not to check, FALSE otherwise.
166 gboolean
do_not_check_redirect_forward(int mode
)
169 case COMPOSE_FORWARD
:
170 case COMPOSE_FORWARD_AS_ATTACH
:
171 case COMPOSE_FORWARD_INLINE
:
172 case COMPOSE_REDIRECT
:
173 if (attwarnerprefs
.skip_forwards_and_redirections
)
181 * Callback function to be called before sending the mail.
183 * @param source The composer to be checked.
184 * @param data Additional data.
186 * @return TRUE if no attachments are mentioned or files are attached,
187 * FALSE if attachments are mentioned and no files are attached.
189 static gboolean
attwarn_before_send_hook(gpointer source
, gpointer data
)
191 Compose
*compose
= (Compose
*)source
;
192 AttachWarnerMention
*mention
= NULL
;
193 gboolean ret
= FALSE
; /* continue sending if FALSE */
195 debug_print("AttachWarner invoked\n");
197 return FALSE
; /* do not check while queuing */
199 if (do_not_check_redirect_forward(compose
->mode
))
202 mention
= are_attachments_mentioned(compose
);
203 if (does_not_have_attachments(compose
) && mention
!= NULL
) {
208 bold_text
= g_strdup_printf("<span weight=\"bold\">%.20s</span>...",
210 message
= g_strdup_printf(
211 _("An attachment is mentioned in the mail you're sending, "
212 "but no file was attached. Mention appears on line %d, "
213 "which begins with text: %s\n\n%s"),
216 compose
->sending
?_("Send it anyway?"):_("Queue it anyway?"));
217 aval
= alertpanel(_("Attachment warning"), message
,
219 NULL
, compose
->sending
? _("_Send") : _("Queue"),
220 NULL
, NULL
, ALERTFOCUS_SECOND
);
223 if (aval
!= G_ALERTALTERNATE
)
226 if (mention
!= NULL
) {
227 if (mention
->context
!= NULL
)
228 g_free(mention
->context
);
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
, "AttachWarner", error
))
248 hook_id
= hooks_register_hook(COMPOSE_CHECK_BEFORE_SEND_HOOKLIST
,
249 attwarn_before_send_hook
, NULL
);
251 if (hook_id
== HOOK_NONE
) {
252 *error
= g_strdup(_("Failed to register check before send hook"));
256 attachwarner_prefs_init();
258 debug_print("AttachWarner 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 attachwarner_prefs_done();
271 debug_print("AttachWarner plugin unloaded\n");
276 * Get the name of the plugin.
278 * @return The plugin name (maybe translated).
280 const gchar
*plugin_name(void)
282 return _("AttachWarner");
286 * Get the description of the plugin.
288 * @return The plugin description (maybe translated).
290 const gchar
*plugin_desc(void)
292 return _("Warns user if some reference to attachments is found in the "
293 "message text and no file is attached.");
297 * Get the kind of plugin.
299 * @return The "GTK3" constant.
301 const gchar
*plugin_type(void)
307 * Get the license acronym the plugin is released under.
309 * @return The "GPL" constant.
311 const gchar
*plugin_licence(void)
317 * Get the version of the plugin.
319 * @return The current version string.
321 const gchar
*plugin_version(void)
327 * Get the features implemented by the plugin.
329 * @return A constant PluginFeature structure with the features.
331 struct PluginFeature
*plugin_provides(void)
333 static struct PluginFeature features
[] =
334 { {PLUGIN_OTHER
, N_("AttachWarner")},
335 {PLUGIN_NOTHING
, NULL
}};