/* GNU Mailutils -- a suite of utilities for electronic mail
Copyright (C) 1999, 2000, 2001, 2003, 2004, 2005, 2006, 2007, 2008,
2009, 2010, 2011 Free Software Foundation, Inc.
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public
License as published by the Free Software Foundation; either
version 3 of the License, or (at your option) any later version.
This library 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
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General
Public License along with this library. If not, see
. */
#ifdef HAVE_CONFIG_H
# include
#endif
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
char *mu_ticket_file = "~/.mu-tickets";
static char *_mu_mailbox_pattern;
static char *_default_folder_dir = "Mail";
static char *_mu_folder_dir;
static int
mu_normalize_mailbox_url (char **pout, const char *dir)
{
int len;
int addslash = 0;
#define USERSUFFIX "${user}"
if (!pout)
return MU_ERR_OUT_PTR_NULL;
len = strlen (dir);
if (dir[len-1] == '=')
{
if (len > 5 && strcmp (dir + len - 5, "user=") == 0)
*pout = strdup (dir);
else
return MU_ERR_BAD_FILENAME;
}
else if (dir[len-1] != '/')
addslash = 1;
*pout = malloc (strlen (dir) + (addslash ? 1 : 0) + sizeof USERSUFFIX);
if (!*pout)
return ENOMEM;
strcpy (*pout, dir);
if (addslash)
strcat (*pout, "/");
strcat (*pout, USERSUFFIX);
#undef USERSUFFIX
return 0;
}
int
mu_set_mail_directory (const char *p)
{
if (_mu_mailbox_pattern)
free (_mu_mailbox_pattern);
if (!p)
{
_mu_mailbox_pattern = NULL;
return 0;
}
return mu_normalize_mailbox_url (&_mu_mailbox_pattern, p);
}
int
mu_set_mailbox_pattern (const char *pat)
{
if (_mu_mailbox_pattern)
free (_mu_mailbox_pattern);
if (!pat)
{
_mu_mailbox_pattern = NULL;
return 0;
}
_mu_mailbox_pattern = strdup (pat);
return _mu_mailbox_pattern ? 0 : ENOMEM;
}
void
mu_set_folder_directory (const char *p)
{
if (_mu_folder_dir != _default_folder_dir)
free (_mu_folder_dir);
_mu_folder_dir = strdup (p);
}
const char *
mu_mailbox_url ()
{
if (!_mu_mailbox_pattern)
mu_set_mail_directory (MU_PATH_MAILDIR);
return _mu_mailbox_pattern;
}
const char *
mu_folder_directory ()
{
if (!_mu_folder_dir)
_mu_folder_dir = _default_folder_dir;
return _mu_folder_dir;
}
int
mu_construct_user_mailbox_url (char **pout, const char *name)
{
const char *pat = mu_mailbox_url ();
const char *env[3];
struct mu_wordsplit ws;
env[0] = "user";
env[1] = (char*) name;
env[2] = NULL;
ws.ws_env = env;
if (mu_wordsplit (pat, &ws,
MU_WRDSF_NOSPLIT | MU_WRDSF_NOCMD |
MU_WRDSF_ENV | MU_WRDSF_ENV_KV))
{
mu_error (_("cannot expand line `%s': %s"), pat,
mu_wordsplit_strerror (&ws));
return errno;
}
if (ws.ws_wordc == 0)
/* FIXME: a special return code maybe? */
*pout = strdup ("");
else
*pout = strdup (ws.ws_wordv[0]);
mu_wordsplit_free (&ws);
if (!*pout)
return ENOMEM;
return 0;
}
/* Is this a security risk? */
#define USE_ENVIRON 1
static int
split_shortcut (const char *file, const char pfx[], char **user, char **rest)
{
*user = NULL;
*rest = NULL;
if (!strchr (pfx, file[0]))
return 0;
if (*++file == 0)
return 0;
else
{
char *p = strchr (file, '/');
int len;
if (p)
len = p - file + 1;
else
len = strlen (file) + 1;
if (len == 1)
*user = NULL;
else
{
*user = calloc (1, len);
if (!*user)
return ENOMEM;
memcpy (*user, file, len);
(*user)[len-1] = 0;
}
file += len-1;
if (file[0] == '/')
file++;
}
if (file[0])
{
*rest = strdup (file);
if (!*rest)
{
free (*user);
return ENOMEM;
}
}
return 0;
}
static char *
get_homedir (const char *user)
{
char *homedir = NULL;
struct mu_auth_data *auth = NULL;
if (user)
{
auth = mu_get_auth_by_name (user);
if (auth)
homedir = auth->dir;
}
else
{
#ifdef USE_ENVIRON
/* NOTE: Should we honor ${HOME}? */
homedir = getenv ("HOME");
if (homedir == NULL)
{
auth = mu_get_auth_by_name (user);
if (auth)
homedir = auth->dir;
}
#else
auth = mu_get_auth_by_name (user);
if (auth)
homedir = auth->dir;
#endif
}
if (homedir)
homedir = strdup (homedir);
mu_auth_data_free (auth);
return homedir;
}
static int
user_mailbox_name (const char *user, char **mailbox_name)
{
#ifdef USE_ENVIRON
if (!user)
user = (getenv ("LOGNAME")) ? getenv ("LOGNAME") : getenv ("USER");
#endif
if (user)
{
int rc = mu_construct_user_mailbox_url (mailbox_name, user);
if (rc)
return rc;
}
else
{
struct mu_auth_data *auth = mu_get_auth_by_uid (getuid ());
if (!auth)
{
mu_error ("Who am I?");
return EINVAL;
}
*mailbox_name = strdup (auth->mailbox);
mu_auth_data_free (auth);
}
return 0;
}
static int
plus_expand (const char *file, char **buf)
{
char *home;
const char *folder_dir = mu_folder_directory ();
int len;
home = get_homedir (NULL);
if (!home)
return ENOENT;
file++;
if (folder_dir[0] == '/' || mu_is_proto (folder_dir))
{
len = strlen (folder_dir) + strlen (file) + 2;
*buf = malloc (len);
sprintf (*buf, "%s/%s", folder_dir, file);
}
else
{
len = strlen (home) + strlen (folder_dir) + strlen (file) + 3;
*buf = malloc (len);
sprintf (*buf, "%s/%s/%s", home, folder_dir, file);
}
(*buf)[len-1] = 0;
free (home);
return 0;
}
static int
percent_expand (const char *file, char **mbox)
{
char *user = NULL;
char *path = NULL;
int status;
if ((status = split_shortcut (file, "%", &user, &path)))
return status;
if (path)
{
free (user);
free (path);
return ENOENT;
}
status = user_mailbox_name (user, mbox);
free (user);
return status;
}
static void
attach_auth_ticket (mu_mailbox_t mbox)
{
mu_folder_t folder = NULL;
mu_authority_t auth = NULL;
if (mu_mailbox_get_folder (mbox, &folder) == 0
&& mu_folder_get_authority (folder, &auth) == 0
&& auth)
{
char *filename = mu_tilde_expansion (mu_ticket_file, "/", NULL);
mu_wicket_t wicket;
int rc;
mu_debug (MU_DEBCAT_MAILBOX, MU_DEBUG_TRACE1,
("Reading user ticket file %s", filename));
if ((rc = mu_file_wicket_create (&wicket, filename)) == 0)
{
mu_ticket_t ticket;
if ((rc = mu_wicket_get_ticket (wicket, NULL, &ticket)) == 0)
{
rc = mu_authority_set_ticket (auth, ticket);
mu_debug (MU_DEBCAT_MAILBOX, MU_DEBUG_TRACE1,
("Retrieved and set ticket: %d", rc));
}
else
mu_debug (MU_DEBCAT_MAILBOX, MU_DEBUG_ERROR,
("Error retrieving ticket: %s\n",
mu_strerror (rc)));
mu_wicket_destroy (&wicket);
}
else
mu_debug (MU_DEBCAT_MAILBOX, MU_DEBUG_ERROR,
("Error creating wicket: %s\n", mu_strerror (rc)));
free (filename);
}
}
/* We are trying to be smart about the location of the mail.
mu_mailbox_create() is not doing this.
% --> system mailbox for the real uid
%user --> system mailbox for the given user
~/file --> /home/user/file
~user/file --> /home/user/file
+file --> /home/user/Mail/file
=file --> /home/user/Mail/file
*/
int
mu_mailbox_create_default (mu_mailbox_t *pmbox, const char *mail)
{
char *mbox = NULL;
char *tmp_mbox = NULL;
char *p;
int status = 0;
/* Sanity. */
if (pmbox == NULL)
return MU_ERR_OUT_PTR_NULL;
if (mail && *mail == 0)
mail = NULL;
if (mail == NULL)
{
if (!_mu_mailbox_pattern)
{
/* Other utilities may not understand GNU mailutils url namespace, so
use FOLDER instead, to not confuse others by using MAIL. */
mail = getenv ("FOLDER");
if (!mail)
{
/* Fallback to well-known environment. */
mail = getenv ("MAIL");
}
}
if (!mail)
{
if ((status = user_mailbox_name (NULL, &tmp_mbox)))
return status;
mail = tmp_mbox;
}
}
p = mu_tilde_expansion (mail, "/", NULL);
if (tmp_mbox)
free (tmp_mbox);
tmp_mbox = p;
mail = tmp_mbox;
if (!mail)
return ENOMEM;
switch (mail[0])
{
case '%':
status = percent_expand (mail, &mbox);
break;
case '+':
case '=':
status = plus_expand (mail, &mbox);
break;
case '/':
mbox = strdup (mail);
break;
default:
if (!mu_is_proto (mail))
{
p = mu_getcwd();
mbox = malloc (strlen (p) + strlen (mail) + 2);
sprintf (mbox, "%s/%s", p, mail);
free (p);
}
else
mbox = strdup (mail);
break;
}
if (tmp_mbox)
free (tmp_mbox);
if (status)
return status;
status = mu_mailbox_create (pmbox, mbox);
free (mbox);
if (status == 0)
attach_auth_ticket (*pmbox);
return status;
}