1 /*
2  *      sendmail.c
3  *
4  *      Copyright 2007-2016 Frank Lanitz <frank(at)frank(dot)uvena(dot)de>
5  *      Copyright 2007 Enrico Tröger <enrico(dot)troeger(at)uvena(dot)de>
6  *      Copyright 2007, 2008 Nick Treleaven <nick(dot)treleaven(at)btinternet(dot)com>
7  *      Copyright 2008, 2009 Timothy Boronczyk <tboronczyk(at)gmail(dot)com>
8  *
9  *      This program is free software; you can redistribute it and/or modify
10  *      it under the terms of the GNU General Public License as published by
11  *      the Free Software Foundation; either version 2 of the License, or
12  *      (at your option) any later version.
13  *
14  *      This program is distributed in the hope that it will be useful,
15  *      but WITHOUT ANY WARRANTY; without even the implied warranty of
16  *      MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
17  *      GNU General Public License for more details.
18  *
19  *      You should have received a copy of the GNU General Public License
20  *      along with this program; if not, write to the Free Software
21  *      Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
22  */
23 
24 /* A little plugin to send a document as attachment using the preferred mail client */
25 
26 
27 #ifdef HAVE_CONFIG_H
28 	#include "config.h" /* for the gettext domain */
29 #endif
30 
31 #include <geanyplugin.h>
32 #include "mail-icon.xpm"
33 #include <glib.h>
34 #include <glib/gstdio.h>
35 #include <errno.h>
36 
37 GeanyPlugin		*geany_plugin;
38 GeanyData		*geany_data;
39 
40 PLUGIN_VERSION_CHECK(224)
41 
42 PLUGIN_SET_TRANSLATABLE_INFO(
43 	LOCALEDIR,
44 	GETTEXT_PACKAGE,
45 	_("SendMail"),
46 	_("Sends the current file as attachment with your favorite mailer"),
47 	VERSION,
48 	"Frank Lanitz <frank@frank.uvena.de>")
49 
50 /* Keybinding(s) */
51 enum
52 {
53 	SENDMAIL_KB,
54 	COUNT_KB
55 };
56 
57 static gchar *config_file = NULL;
58 static gchar *mailer = NULL;
59 static gchar *address = NULL;
60 gboolean icon_in_toolbar = FALSE;
61 gboolean use_address_dialog = FALSE;
62 /* Needed global to remove from toolbar again */
63 GtkWidget *mailbutton = NULL;
64 static GtkWidget *main_menu_item = NULL;
65 
66 
67 /* Callback for sending file as attachment */
68 static void
send_as_attachment(G_GNUC_UNUSED GtkMenuItem * menuitem,G_GNUC_UNUSED gpointer gdata)69 send_as_attachment(G_GNUC_UNUSED GtkMenuItem *menuitem, G_GNUC_UNUSED gpointer gdata)
70 {
71 	GeanyDocument *doc;
72 	gchar	*locale_filename = NULL;
73 	gchar	*command = NULL;
74 	GError	*error = NULL;
75 	GString	*cmd_str = NULL;
76 	gchar 		*data;
77 
78 	doc = document_get_current();
79 
80 	if (doc->file_name == NULL)
81 	{
82 		dialogs_show_save_as();
83 	}
84 	else
85 	{
86 		document_save_file(doc, FALSE);
87 	}
88 
89     if (doc->file_name != NULL)
90 	{
91 		if (mailer)
92 		{
93 			locale_filename = utils_get_locale_from_utf8(doc->file_name);
94 			cmd_str = g_string_new(mailer);
95 			if ((use_address_dialog == TRUE) && (g_strrstr(mailer, "%r") != NULL))
96 			{
97 				GKeyFile *config = NULL;
98 				gchar *config_dir = NULL;
99  				gchar *input = dialogs_show_input(_("Recipient's Address"),
100 										GTK_WINDOW(geany->main_widgets->window),
101 										_("Enter the recipient's e-mail address:"),
102 										address);
103 
104 				if (input)
105 				{
106 					config = g_key_file_new();
107 					g_key_file_load_from_file(config, config_file, G_KEY_FILE_NONE, NULL);
108 
109 					g_free(address);
110  					address = input;
111 
112  					g_key_file_set_string(config, "tools", "address", address);
113  				}
114  				else
115  				{
116 					g_string_free(cmd_str, TRUE);
117 					g_free(locale_filename);
118 					return;
119 				}
120 
121 				config_dir = g_path_get_dirname(config_file);
122 
123 
124 				if (! g_file_test(config_dir, G_FILE_TEST_IS_DIR) &&
125 				      utils_mkdir(config_dir, TRUE) != 0)
126  				{
127  					dialogs_show_msgbox(GTK_MESSAGE_ERROR,
128  						_("Plugin configuration directory could not be created."));
129  				}
130  				else
131  				{
132  					/* write config to file */
133  					data = g_key_file_to_data(config, NULL, NULL);
134  					utils_write_file(config_file, data);
135 					g_free(data);
136  				}
137 				g_key_file_free(config);
138 				g_free(config_dir);
139  			}
140 
141 			if (! utils_string_replace_all(cmd_str, "%f", locale_filename))
142 				ui_set_statusbar(FALSE,
143 				_("Filename placeholder not found. The executed command might have failed."));
144 
145 			if (use_address_dialog == TRUE && address != NULL)
146 			{
147 				if (! utils_string_replace_all(cmd_str, "%r", address))
148  					ui_set_statusbar(FALSE,
149 					_("Recipient address placeholder not found. The executed command might have failed."));
150 			}
151 			else
152 			{
153 				/* Removes %r if option was not activ but was included into command */
154 				utils_string_replace_all(cmd_str, "%r", "");
155 			}
156 
157 			utils_string_replace_all(cmd_str, "%b", g_path_get_basename(locale_filename));
158 
159 			command = g_string_free(cmd_str, FALSE);
160 			g_spawn_command_line_async(command, &error);
161 			if (error != NULL)
162 			{
163 				ui_set_statusbar(FALSE, _("Could not execute mailer. Please check your configuration."));
164 				g_error_free(error);
165 			}
166 
167 			g_free(locale_filename);
168 			g_free(command);
169 		}
170 		else
171 		{
172 			ui_set_statusbar(FALSE, _("Please define a mail client first."));
173 		}
174 	}
175 	else
176 	{
177 		ui_set_statusbar(FALSE, _("File has to be saved before sending."));
178 	}
179 }
180 
key_send_as_attachment(G_GNUC_UNUSED guint key_id)181 static void key_send_as_attachment(G_GNUC_UNUSED guint key_id)
182 {
183 	send_as_attachment(NULL, NULL);
184 }
185 
186 #define GEANYSENDMAIL_STOCK_MAIL "geanysendmail-mail"
187 
add_stock_item(void)188 static void add_stock_item(void)
189 {
190 	GtkIconSet *icon_set;
191 	GtkIconFactory *factory = gtk_icon_factory_new();
192 	GtkIconTheme *theme = gtk_icon_theme_get_default();
193 	GtkStockItem item = { (gchar*)(GEANYSENDMAIL_STOCK_MAIL), (gchar*)(N_("Mail")), 0, 0, (gchar*)(GETTEXT_PACKAGE) };
194 
195 	if (gtk_icon_theme_has_icon(theme, "mail-message-new"))
196 	{
197 		GtkIconSource *icon_source = gtk_icon_source_new();
198 		icon_set = gtk_icon_set_new();
199 		gtk_icon_source_set_icon_name(icon_source, "mail-message-new");
200 		gtk_icon_set_add_source(icon_set, icon_source);
201 		gtk_icon_source_free(icon_source);
202 	}
203 	else
204 	{
205 		GdkPixbuf *pb = gdk_pixbuf_new_from_xpm_data(mail_icon);
206 		icon_set = gtk_icon_set_new_from_pixbuf(pb);
207 		g_object_unref(pb);
208 	}
209 	gtk_icon_factory_add(factory, item.stock_id, icon_set);
210 	gtk_stock_add(&item, 1);
211 	gtk_icon_factory_add_default(factory);
212 
213 	g_object_unref(factory);
214 	gtk_icon_set_unref(icon_set);
215 }
216 
217 
show_icon(void)218 static void show_icon(void)
219 {
220 	mailbutton = GTK_WIDGET(gtk_tool_button_new_from_stock(GEANYSENDMAIL_STOCK_MAIL));
221 	plugin_add_toolbar_item(geany_plugin, GTK_TOOL_ITEM(mailbutton));
222 	ui_add_document_sensitive(mailbutton);
223 #if GTK_CHECK_VERSION(2, 12, 0)
224 	gtk_tool_item_set_tooltip_text(GTK_TOOL_ITEM(mailbutton), _("Send by mail"));
225 #endif
226 	g_signal_connect (G_OBJECT(mailbutton), "clicked", G_CALLBACK(send_as_attachment), NULL);
227 	gtk_widget_show_all (mailbutton);
228 	icon_in_toolbar = TRUE;
229 }
230 
cleanup_icon(void)231 static void cleanup_icon(void)
232 {
233 	if (mailbutton != NULL)
234 	{
235 		gtk_container_remove(GTK_CONTAINER (geany->main_widgets->toolbar), mailbutton);
236 	}
237 	icon_in_toolbar = FALSE;
238 }
239 
240 
241 static struct
242 {
243 	GtkWidget *entry;
244 	GtkWidget *checkbox_icon_to_toolbar;
245 	GtkWidget *checkbox_use_addressdialog;
246 }
247 pref_widgets;
248 
249 
250 static void
on_configure_response(G_GNUC_UNUSED GtkDialog * dialog,gint response,G_GNUC_UNUSED gpointer user_data)251 on_configure_response(G_GNUC_UNUSED GtkDialog *dialog, gint response, G_GNUC_UNUSED  gpointer user_data)
252 {
253 	if (response == GTK_RESPONSE_OK || response == GTK_RESPONSE_APPLY)
254 	{
255 		GKeyFile 	*config = g_key_file_new();
256 		gchar 		*config_dir = g_path_get_dirname(config_file);
257 		gboolean	configure_toogle_status;
258 
259 		g_free(mailer);
260 		mailer = g_strdup(gtk_entry_get_text(GTK_ENTRY(pref_widgets.entry)));
261 
262 		configure_toogle_status =
263 			gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(pref_widgets.checkbox_icon_to_toolbar));
264 
265 		if (icon_in_toolbar ^ configure_toogle_status)
266 		/* Only do anything if a status change is needed */
267 		{
268 			if (icon_in_toolbar == TRUE)
269 			{
270 				/* We need to remove the toolbar icon */
271 				cleanup_icon();
272 			}
273 			else
274 			{
275 				/* We need to show the toolbar icon */
276 				show_icon();
277 			}
278 		}
279 
280 		if (gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(pref_widgets.checkbox_use_addressdialog)) == TRUE)
281 			use_address_dialog = TRUE;
282 		else
283 			use_address_dialog = FALSE;
284 
285 		g_key_file_load_from_file(config, config_file, G_KEY_FILE_NONE, NULL);
286 		g_key_file_set_string(config, "tools", "mailer", mailer);
287 		g_key_file_set_boolean(config, "tools", "address_usage", use_address_dialog);
288 		g_key_file_set_boolean(config, "icon", "show_icon", icon_in_toolbar);
289 
290 		if (! g_file_test(config_dir, G_FILE_TEST_IS_DIR) && utils_mkdir(config_dir, TRUE) != 0)
291 		{
292 			dialogs_show_msgbox(GTK_MESSAGE_ERROR,
293 				_("Plugin configuration directory could not be created."));
294 		}
295 		else
296 		{
297 			/* write config to file */
298 			gchar *data = g_key_file_to_data(config, NULL, NULL);
299 			utils_write_file(config_file, data);
300 			g_free(data);
301 		}
302 		g_key_file_free(config);
303 		g_free(config_dir);
304 	}
305 }
306 
plugin_configure(GtkDialog * dialog)307 GtkWidget *plugin_configure(GtkDialog *dialog)
308 {
309 	GtkWidget	*label1, *label2, *vbox;
310 
311 	vbox = gtk_vbox_new(FALSE, 6);
312 
313 	/* add a label and a text entry to the dialog */
314 	label1 = gtk_label_new(_("Path and options for the mail client:"));
315 	gtk_widget_show(label1);
316 	gtk_misc_set_alignment(GTK_MISC(label1), 0, 0.5);
317 	pref_widgets.entry = gtk_entry_new();
318 	gtk_widget_show(pref_widgets.entry);
319 	if (mailer != NULL)
320 		gtk_entry_set_text(GTK_ENTRY(pref_widgets.entry), mailer);
321 
322 	label2 = gtk_label_new(_("Note: \n\t%f will be replaced by your file."\
323 		"\n\t%r will be replaced by recipient's email address."\
324 		"\n\t%b will be replaced by basename of a file"\
325 		"\n\tExamples:"\
326 		"\n\tsylpheed --attach \"%f\" --compose \"%r\""\
327 		"\n\tmutt -s \"Sending \'%b\'\" -a \"%f\" \"%r\""));
328 	gtk_label_set_selectable(GTK_LABEL(label2), TRUE);
329 	gtk_widget_show(label2);
330 	gtk_misc_set_alignment(GTK_MISC(label2), 0, 0.5);
331 
332 	pref_widgets.checkbox_icon_to_toolbar = gtk_check_button_new_with_label(_("Show toolbar icon"));
333 	gtk_widget_set_tooltip_text(pref_widgets.checkbox_icon_to_toolbar,
334 		_("Shows a icon in the toolbar to send file more easy."));
335 	gtk_button_set_focus_on_click(GTK_BUTTON(pref_widgets.checkbox_icon_to_toolbar), FALSE);
336 	gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(pref_widgets.checkbox_icon_to_toolbar), icon_in_toolbar);
337 	gtk_widget_show(pref_widgets.checkbox_icon_to_toolbar);
338 
339 	pref_widgets.checkbox_use_addressdialog = gtk_check_button_new_with_label(_
340 		("Use dialog for entering email address of recipients"));
341 
342 	gtk_button_set_focus_on_click(GTK_BUTTON(pref_widgets.checkbox_use_addressdialog), FALSE);
343 	gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(pref_widgets.checkbox_use_addressdialog), use_address_dialog);
344 	gtk_widget_show(pref_widgets.checkbox_use_addressdialog);
345 
346 	gtk_box_pack_start(GTK_BOX(vbox), label1, FALSE, FALSE, 0);
347 	gtk_box_pack_start(GTK_BOX(vbox), pref_widgets.entry, FALSE, FALSE, 0);
348 	gtk_box_pack_start(GTK_BOX(vbox), label2, FALSE, FALSE, 0);
349 	gtk_box_pack_start(GTK_BOX(vbox), pref_widgets.checkbox_icon_to_toolbar, FALSE, FALSE, 0);
350 	gtk_box_pack_start(GTK_BOX(vbox), pref_widgets.checkbox_use_addressdialog, FALSE, FALSE, 0);
351 
352 	gtk_widget_show(vbox);
353 
354 	g_signal_connect(dialog, "response", G_CALLBACK(on_configure_response), NULL);
355 	return vbox;
356 }
357 
358 /* Called by Geany to initialize the plugin */
plugin_init(GeanyData G_GNUC_UNUSED * data)359 void plugin_init(GeanyData G_GNUC_UNUSED *data)
360 {
361 	GKeyFile *config = g_key_file_new();
362 	gchar *config_file_old = NULL;
363 	gchar *config_dir = NULL;
364 	gchar *config_dir_old = NULL;
365 	gchar *kb_label = _("Send file by mail");
366 	GtkWidget *menu_mail = NULL;
367 	GeanyKeyGroup *key_group;
368 
369 	config_file = g_strconcat(geany->app->configdir, G_DIR_SEPARATOR_S, "plugins", G_DIR_SEPARATOR_S,
370 		"sendmail", G_DIR_SEPARATOR_S, "mail.conf", NULL);
371 
372 	#ifndef G_OS_WIN32
373 	/* We try only to move if we are on not Windows platform */
374 	config_file_old = g_strconcat(geany->app->configdir, G_DIR_SEPARATOR_S,
375 		"plugins", G_DIR_SEPARATOR_S,
376 		"geanysendmail", G_DIR_SEPARATOR_S, "mail.conf", NULL);
377 	config_dir = g_strconcat(geany->app->configdir, G_DIR_SEPARATOR_S,
378 		"plugins", G_DIR_SEPARATOR_S, "sendmail", NULL);
379 	config_dir_old = g_strconcat(geany->app->configdir, G_DIR_SEPARATOR_S,
380 		"plugins", G_DIR_SEPARATOR_S, "geanysendmail", NULL);
381 
382 	if (g_file_test(config_file_old, G_FILE_TEST_EXISTS))
383 	{
384 		if (dialogs_show_question(
385 			_("Renamed plugin detected!\n"
386 			  "\n"
387 			  "GeanySendMail has been renamed to sendmail -- you surely have "
388 			  "already recognised it. \n"
389 			  "Geany is able to migrate your old plugin configuration by "
390 			  "moving the old configuration file to new location.\n"
391 			  "Move now?")))
392 		{
393 			if (g_rename(config_dir_old, config_dir) == 0)
394 			{
395 				dialogs_show_msgbox(GTK_MESSAGE_INFO,
396 					_("Your configuration directory has been "
397 					  "successfully moved from \"%s\" to \"%s\"."),
398 					config_dir_old, config_dir);
399 			}
400 			else
401 			{
402 				/* If there was an error on migrating we need
403 				 * to load from original one.
404 				 * When saving new configuration it will go to
405 				 * new folder so migration should
406 				 * be implicit. */
407 				g_free(config_file);
408 				config_file = g_strdup(config_file_old);
409 				dialogs_show_msgbox(
410 					GTK_MESSAGE_WARNING,
411 					_("Your old configuration directory \"%s\" could "
412 					  "not be moved to \"%s\" (%s). "
413 					  "Please move manually the directory to the new location."),
414 					config_dir_old,
415 					config_dir,
416 					g_strerror(errno));
417 			}
418 		}
419 	}
420 
421 	g_free(config_dir_old);
422 	g_free(config_dir);
423 	g_free(config_file_old);
424 	#endif
425 
426 	/* Initialising options from config file */
427 	g_key_file_load_from_file(config, config_file, G_KEY_FILE_NONE, NULL);
428 	mailer = g_key_file_get_string(config, "tools", "mailer", NULL);
429 	address = g_key_file_get_string(config, "tools", "address", NULL);
430 	use_address_dialog = g_key_file_get_boolean(config, "tools", "address_usage", NULL);
431 	icon_in_toolbar = g_key_file_get_boolean(config, "icon", "show_icon", NULL);
432 
433 	g_key_file_free(config);
434 
435 	add_stock_item();
436 	if (icon_in_toolbar == TRUE)
437 	{
438 		show_icon();
439 	}
440 
441 	/* Build up menu entry */
442 	menu_mail = gtk_menu_item_new_with_mnemonic(_("_Mail document"));
443 	gtk_container_add(GTK_CONTAINER(geany->main_widgets->tools_menu), menu_mail);
444 	gtk_widget_set_tooltip_text(menu_mail,
445 		_("Sends the opened file as unzipped attachment by any mailer from your $PATH"));
446 	g_signal_connect(G_OBJECT(menu_mail), "activate", G_CALLBACK(send_as_attachment), NULL);
447 
448 	/* setup keybindings */
449 	key_group = plugin_set_key_group(geany_plugin, "sendmail", COUNT_KB, NULL);
450 	keybindings_set_item(key_group, SENDMAIL_KB, key_send_as_attachment,
451 		0, 0, "send_file_as_attachment", kb_label, menu_mail);
452 
453 	gtk_widget_show_all(menu_mail);
454 	ui_add_document_sensitive(menu_mail);
455 	main_menu_item = menu_mail;
456 }
457 
458 
plugin_cleanup(void)459 void plugin_cleanup(void)
460 {
461 	gtk_widget_destroy(main_menu_item);
462 	cleanup_icon();
463 	g_free(mailer);
464 	g_free(address);
465 	g_free(config_file);
466 }
467