/*
* Copyright (C) 2004 Roberto Majadas
* Copyright (C) 2005-2013 Bastien Nocera
*
* 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 2 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 av.
*
* 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.
*
* Author: Roberto Majadas <roberto.majadas@openshine.com>
* Bastien Nocera <hadess@hadess.net>
*/
#include "config.h"
#include <string.h>
#include <stdlib.h>
#include <glib/gi18n.h>
#include <locale.h>
#include <glib/gstdio.h>
#include <gio/gio.h>
/* Options */
static char **filenames = NULL;
static gboolean run_from_build_dir = FALSE;
static gint64 xid = 0;
static gboolean show_version = FALSE;
typedef enum {
MAILER_UNKNOWN,
MAILER_EVO,
MAILER_BALSA,
MAILER_SYLPHEED,
MAILER_THUNDERBIRD,
} MailerType;
typedef struct {
GList *file_list;
guint num_dirs;
MailerType type;
char *mail_cmd;
} NautilusSendto;
static const GOptionEntry entries[] = {
{ "run-from-build-dir", 'b', 0, G_OPTION_ARG_NONE, &run_from_build_dir, N_("Run from build directory (ignored)"), NULL },
{ "xid", 'x', 0, G_OPTION_ARG_INT64, &xid, N_("Use XID as parent to the send dialogue (ignored)"), NULL },
{ G_OPTION_REMAINING, '\0', 0, G_OPTION_ARG_FILENAME_ARRAY, &filenames, N_("Files to send"), "[FILES...]" },
{"version", 'v', 0, G_OPTION_ARG_NONE, &show_version, N_("Output version information and exit"), NULL},
{ NULL }
};
static char *
get_evo_cmd (void)
{
char *tmp, *retval;
tmp = g_find_program_in_path ("evolution");
if (tmp == NULL)
return NULL;
retval = g_strdup_printf ("%s --component=mail %%s", tmp);
g_free (tmp);
return retval;
}
static gboolean
init_mailer (NautilusSendto *nst)
{
GAppInfo *app_info;
char *needle;
nst->type = MAILER_UNKNOWN;
app_info = g_app_info_get_default_for_uri_scheme ("mailto");
if (app_info) {
nst->mail_cmd = g_strdup (g_app_info_get_commandline (app_info));
g_object_unref (app_info);
} else {
nst->mail_cmd = NULL;
}
if (nst->mail_cmd == NULL || *nst->mail_cmd == '\0') {
g_free (nst->mail_cmd);
nst->mail_cmd = get_evo_cmd ();
nst->type = MAILER_EVO;
} else {
/* Find what the default mailer is */
if (strstr (nst->mail_cmd, "balsa"))
nst->type = MAILER_BALSA;
else if (strstr (nst->mail_cmd, "thunder") || strstr (nst->mail_cmd, "seamonkey") || strstr (nst->mail_cmd, "icedove")) {
char **strv;
nst->type = MAILER_THUNDERBIRD;
/* Thunderbird sucks, see
* https://bugzilla.gnome.org/show_bug.cgi?id=614222 */
strv = g_strsplit (nst->mail_cmd, " ", -1);
g_free (nst->mail_cmd);
nst->mail_cmd = g_strdup_printf ("%s %%s", strv[0]);
g_strfreev (strv);
} else if (strstr (nst->mail_cmd, "sylpheed") || strstr (nst->mail_cmd, "claws"))
nst->type = MAILER_SYLPHEED;
else if (strstr (nst->mail_cmd, "anjal"))
nst->type = MAILER_EVO;
}
if (nst->mail_cmd == NULL)
return FALSE;
/* Replace %U by %s */
while ((needle = g_strrstr (nst->mail_cmd, "%U")) != NULL)
needle[1] = 's';
while ((needle = g_strrstr (nst->mail_cmd, "%u")) != NULL)
needle[1] = 's';
return TRUE;
}
static char *
get_filename_from_list (GList *file_list)
{
GList *l;
GString *common_part = NULL;
gboolean matches = TRUE;
guint offset = 0;
const char *encoding;
gboolean use_utf8 = TRUE;
encoding = g_getenv ("G_FILENAME_ENCODING");
if (encoding != NULL && strcasecmp (encoding, "UTF-8") != 0)
use_utf8 = FALSE;
if (file_list == NULL)
return NULL;
common_part = g_string_new ("");
while (TRUE) {
gunichar cur_char = '\0';
for (l = file_list; l ; l = l->next) {
char *path = NULL, *name = NULL;
char *offset_name = NULL;
path = g_filename_from_uri ((char *) l->data, NULL, NULL);
if (!path)
break;
name = g_path_get_basename (path);
if (!use_utf8) {
char *tmp;
tmp = g_filename_to_utf8 (name, -1, NULL, NULL, NULL);
g_free (name);
name = tmp;
}
if (!name) {
g_free (path);
break;
}
if (offset >= g_utf8_strlen (name, -1)) {
g_free(name);
g_free(path);
matches = FALSE;
break;
}
offset_name = g_utf8_offset_to_pointer (name, offset);
if (offset_name == g_utf8_strrchr (name, -1, '.')) {
g_free (name);
g_free (path);
matches = FALSE;
break;
}
if (cur_char == '\0') {
cur_char = g_utf8_get_char (offset_name);
} else if (cur_char != g_utf8_get_char (offset_name)) {
g_free (name);
g_free (path);
matches = FALSE;
break;
}
g_free (name);
g_free (path);
}
if (matches == TRUE &&
cur_char != '\0' &&
cur_char != '-' &&
cur_char != '_') {
offset++;
common_part = g_string_append_unichar (common_part,
cur_char);
} else {
break;
}
}
if (g_utf8_strlen (common_part->str, -1) < 4) {
g_string_free (common_part, TRUE);
return NULL;
}
return g_string_free (common_part, FALSE);
}
static char *
pack_filename_from_names (GList *file_list)
{
char *filename;
gboolean one_file;
if (file_list != NULL && file_list->next != NULL)
one_file = FALSE;
else if (file_list != NULL)
one_file = TRUE;
if (one_file) {
char *filepath;
filepath = g_filename_from_uri ((char *)file_list->data,
NULL, NULL);
filename = g_path_get_basename (filepath);
g_free (filepath);
} else {
filename = get_filename_from_list (file_list);
}
if (filename == NULL) {
/* Translators: the default archive name if it
* could not be deduced from the provided files */
filename = g_strdup (_("Archive"));
}
return filename;
}
static char *
pack_files (GList *file_list)
{
char *file_roller_cmd;
const char *filename;
GList *l;
GString *cmd, *tmp;
char *pack_type, *tmp_work_dir, *packed_file;
file_roller_cmd = g_find_program_in_path ("file-roller");
filename = pack_filename_from_names (file_list);
g_assert (filename != NULL && *filename != '\0');
tmp_work_dir = g_build_filename (g_get_tmp_dir (),
"nautilus-sendto-XXXXXX",
NULL);
tmp_work_dir = g_mkdtemp (tmp_work_dir);
pack_type = g_strdup (".zip");
cmd = g_string_new ("");
g_string_printf (cmd, "%s --add-to=\"%s/%s%s\"",
file_roller_cmd, tmp_work_dir,
filename,
pack_type);
/* file-roller doesn't understand URIs */
for (l = file_list ; l; l=l->next){
char *file;
file = g_filename_from_uri (l->data, NULL, NULL);
g_string_append_printf (cmd," \"%s\"", file);
g_free (file);
}
g_spawn_command_line_sync (cmd->str, NULL, NULL, NULL, NULL);
g_string_free (cmd, TRUE);
tmp = g_string_new("");
g_string_printf (tmp,"%s/%s%s", tmp_work_dir,
filename,
pack_type);
g_free (tmp_work_dir);
packed_file = g_filename_to_uri (tmp->str, NULL, NULL);
g_string_free(tmp, TRUE);
return packed_file;
}
static void
get_evo_mailto (GString *mailto,
GList *file_list)
{
GList *l;
g_string_append (mailto, "mailto:");
g_string_append (mailto, "\"\"");
g_string_append_printf (mailto,"?attach=\"%s\"", (char *)file_list->data);
for (l = file_list->next ; l; l=l->next)
g_string_append_printf (mailto,"&attach=\"%s\"", (char *)l->data);
}
static void
get_balsa_mailto (NautilusSendto *nst,
GString *mailto,
GList *file_list)
{
GList *l;
if (strstr (nst->mail_cmd, " -m ") == NULL && strstr (nst->mail_cmd, " --compose=") == NULL)
g_string_append (mailto, " --compose=");
g_string_append (mailto, "\"\"");
g_string_append_printf (mailto," --attach=\"%s\"", (char *)file_list->data);
for (l = file_list->next ; l; l=l->next)
g_string_append_printf (mailto," --attach=\"%s\"", (char *)l->data);
}
static void
get_thunderbird_mailto (GString *mailto,
GList *file_list)
{
GList *l;
g_string_append (mailto, "-compose \"");
g_string_append_printf (mailto,"attachment='%s", (char *)file_list->data);
for (l = file_list->next ; l; l=l->next)
g_string_append_printf (mailto,",%s", (char *)l->data);
g_string_append (mailto, "'\"");
}
static void
get_sylpheed_mailto (GString *mailto,
GList *file_list)
{
GList *l;
g_string_append (mailto, "--compose ");
g_string_append (mailto, "\"\"");
g_string_append_printf (mailto,"--attach \"%s\"", (char *)file_list->data);
for (l = file_list->next ; l; l=l->next)
g_string_append_printf (mailto," \"%s\"", (char *)l->data);
}
static void
send_files (NautilusSendto *nst)
{
GString *mailto;
char *cmd;
if (nst->num_dirs > 0) {
char *zip;
zip = pack_files (nst->file_list);
g_list_free_full (nst->file_list, g_free);
nst->file_list = g_list_append (NULL, zip);
}
mailto = g_string_new ("");
switch (nst->type) {
case MAILER_BALSA:
get_balsa_mailto (nst, mailto, nst->file_list);
break;
case MAILER_SYLPHEED:
get_sylpheed_mailto (mailto, nst->file_list);
break;
case MAILER_THUNDERBIRD:
get_thunderbird_mailto (mailto, nst->file_list);
break;
case MAILER_EVO:
default:
get_evo_mailto (mailto, nst->file_list);
}
#pragma GCC diagnostic push
#pragma GCC diagnostic ignored "-Wformat-nonliteral"
cmd = g_strdup_printf (nst->mail_cmd, mailto->str);
g_string_free (mailto, TRUE);
#pragma GCC diagnostic pop
g_debug ("Mailer type: %d", nst->type);
g_debug ("Command: %s", cmd);
/* FIXME: collect errors from this call */
g_spawn_command_line_async (cmd, NULL);
g_free (cmd);
}
static char *
escape_ampersands_and_commas (const char *url)
{
int i;
char *str, *ptr;
/* Count the number of ampersands & commas */
i = 0;
ptr = (char *) url;
while ((ptr = strchr (ptr, '&')) != NULL) {
i++;
ptr++;
}
ptr = (char *) url;
while ((ptr = strchr (ptr, ',')) != NULL) {
i++;
ptr++;
}
/* No ampersands or commas ? */
if (i == 0)
return NULL;
/* Replace the '&' */
str = g_malloc0 (strlen (url) - i + 3 * i + 1);
ptr = str;
for (i = 0; url[i] != '\0'; i++) {
if (url[i] == '&') {
*ptr++ = '%';
*ptr++ = '2';
*ptr++ = '6';
} else if (url[i] == ',') {
*ptr++ = '%';
*ptr++ = '2';
*ptr++ = 'C';
} else {
*ptr++ = url[i];
}
}
return str;
}
static char *
get_target_filename (GFile *file)
{
GFileInfo *info;
const char *target;
GFile *new_file;
char *ret;
info = g_file_query_info (file, G_FILE_ATTRIBUTE_STANDARD_TARGET_URI, G_FILE_QUERY_INFO_NONE, NULL, NULL);
if (info == NULL)
return NULL;
target = g_file_info_get_attribute_string (info, G_FILE_ATTRIBUTE_STANDARD_TARGET_URI);
if (target == NULL) {
g_object_unref (info);
return NULL;
}
new_file = g_file_new_for_uri (target);
g_object_unref (info);
ret = g_file_get_path (new_file);
g_object_unref (new_file);
return ret;
}
static void
nautilus_sendto_init (NautilusSendto *nst)
{
int i;
/* Clean up the URIs passed, and collect whether we have directories */
for (i = 0; filenames != NULL && filenames[i] != NULL; i++) {
GFile *file;
char *filename, *escaped, *uri;
GFileInfo *info;
const char *mimetype;
/* We need a filename */
file = g_file_new_for_commandline_arg (filenames[i]);
filename = g_file_get_path (file);
if (filename == NULL) {
filename = get_target_filename (file);
if (filename == NULL) {
g_object_unref (file);
g_debug ("Could not get a filename for '%s'", filenames[i]);
continue;
}
}
/* Get the mime-type, and whether the file is readable */
info = g_file_query_info (file,
G_FILE_ATTRIBUTE_STANDARD_CONTENT_TYPE","G_FILE_ATTRIBUTE_STANDARD_FAST_CONTENT_TYPE","G_FILE_ATTRIBUTE_ACCESS_CAN_READ,
G_FILE_QUERY_INFO_NONE,
NULL,
NULL);
g_object_unref (file);
if (info == NULL) {
g_debug ("Could not get info for '%s'", filenames[i]);
continue;
}
if (g_file_info_has_attribute (info, G_FILE_ATTRIBUTE_ACCESS_CAN_READ)) {
if (g_file_info_get_attribute_boolean (info, G_FILE_ATTRIBUTE_ACCESS_CAN_READ) == FALSE) {
g_debug ("'%s' is not readable", filenames[i]);
g_object_unref (info);
continue;
}
} else {
g_debug ("No can-read attribute for '%s', assuming it is", filenames[i]);
}
mimetype = g_file_info_get_attribute_string (info, G_FILE_ATTRIBUTE_STANDARD_FAST_CONTENT_TYPE);
if (!mimetype)
mimetype = g_file_info_get_attribute_string (info, G_FILE_ATTRIBUTE_STANDARD_CONTENT_TYPE);
if (!mimetype) {
g_object_unref (info);
g_debug ("Could not get mime-type for '%s'", filenames[i]);
continue;
}
if (g_str_equal (mimetype, "inode/directory"))
nst->num_dirs++;
g_object_unref (info);
uri = g_filename_to_uri (filename, NULL, NULL);
g_free (filename);
escaped = escape_ampersands_and_commas (uri);
if (escaped == NULL) {
nst->file_list = g_list_prepend (nst->file_list, uri);
} else {
nst->file_list = g_list_prepend (nst->file_list, escaped);
g_free (uri);
}
}
nst->file_list = g_list_reverse (nst->file_list);
}
int main (int argc, char **argv)
{
GOptionContext *context;
GError *error = NULL;
NautilusSendto *nst;
int ret = 0;
setlocale (LC_ALL, "");
bindtextdomain (GETTEXT_PACKAGE, LOCALEDIR);
bind_textdomain_codeset (GETTEXT_PACKAGE, "UTF-8");
textdomain (GETTEXT_PACKAGE);
context = g_option_context_new ("");
g_option_context_add_main_entries (context, entries, GETTEXT_PACKAGE);
if (g_option_context_parse (context, &argc, &argv, &error) == FALSE) {
g_print (_("Could not parse command-line options: %s\n"), error->message);
g_error_free (error);
return 1;
}
if (show_version) {
g_print ("%s %s\n", PACKAGE_NAME, PACKAGE_VERSION);
return 0;
}
nst = g_new0 (NautilusSendto, 1);
nautilus_sendto_init (nst);
if (!init_mailer (nst)) {
g_print (_("No mail client installed, not sending files\n"));
goto out;
}
if (nst->file_list == NULL) {
g_print (_("Expects URIs or filenames to be passed as options\n"));
ret = 1;
goto out;
}
send_files (nst);
out:
g_free (nst);
return ret;
}