Blame view

libproto/mailer/prog.c 8.76 KB
1
/* GNU Mailutils -- a suite of utilities for electronic mail
2
   Copyright (C) 2008, 2009, 2010 Free Software Foundation, Inc.
3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34

   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 <http://www.gnu.org/licenses/>. */

#ifdef HAVE_CONFIG_H
# include <config.h>
#endif

#ifdef ENABLE_PROG
#include <stdlib.h>
#include <errno.h>
#include <unistd.h>

#include <mailutils/address.h>
#include <mailutils/argcv.h>
#include <mailutils/debug.h>
#include <mailutils/errno.h>
#include <mailutils/header.h>
#include <mailutils/message.h>
#include <mailutils/observer.h>
#include <mailutils/progmailer.h>
35
#include <mailutils/wordsplit.h>
36

37 38 39
#include <mailutils/sys/url.h>
#include <mailutils/sys/mailer.h>
#include <mailutils/sys/registrar.h>
40 41 42 43 44 45 46

static int _url_prog_init     (mu_url_t);

static struct _mu_record _prog_record =
{
  MU_PROG_PRIO,
  MU_PROG_SCHEME,
47 48 49
  MU_RECORD_DEFAULT,
  /* FIXME: MU_URL_USER could be used to request running with this
     user privileges. */
50
  MU_URL_SCHEME | MU_URL_PATH | MU_URL_QUERY, 
51
  MU_URL_PATH,
52
  _url_prog_init,    /* url init.  */
53 54 55
  _mu_mailer_mailbox_init,  /* Mailbox entry.  */
  _mu_mailer_prog_init, /* Mailer entry.  */
  _mu_mailer_folder_init, /* Folder entry.  */
56 57 58 59 60 61 62 63 64 65 66 67
  NULL, /* No need for a back pointer.  */
  NULL, /* _is_scheme method.  */
  NULL, /* _get_url method.  */
  NULL, /* _get_mailbox method.  */
  NULL, /* _get_mailer method.  */
  NULL  /* _get_folder method.  */
};

mu_record_t mu_prog_record = &_prog_record;


static int
68 69 70 71 72 73
_url_prog_uplevel (const mu_url_t orig, mu_url_t *up)
{
  return MU_ERR_NOENT;
}

static int
74 75
_url_prog_init (mu_url_t url)
{
76
  url->_uplevel = _url_prog_uplevel;
77 78 79 80 81 82 83 84 85 86
  return 0;
}


static void prog_destroy (mu_mailer_t);
static int prog_open (mu_mailer_t, int);
static int prog_close (mu_mailer_t);
static int prog_send_message (mu_mailer_t, mu_message_t, mu_address_t,
			      mu_address_t);

87 88
int
_mu_mailer_prog_init (mu_mailer_t mailer)
89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131
{
  int status;
  mu_progmailer_t pm;

  status = mu_progmailer_create (&pm);
  if (status)
    return status;

  mailer->data = pm;
  mailer->_destroy = prog_destroy;
  mailer->_open = prog_open;
  mailer->_close = prog_close;
  mailer->_send_message = prog_send_message;
  
  return 0;
}

static void
prog_destroy (mu_mailer_t mailer)
{
  mu_progmailer_destroy ((mu_progmailer_t*)&mailer->data);
}

static int
prog_open (mu_mailer_t mailer, int flags)
{
  mu_progmailer_t pm = mailer->data;
  int status;
  const char *path;
  
  /* Sanity checks.  */
  if (pm == NULL)
    return EINVAL;

  mailer->flags = flags;

  if ((status = mu_url_sget_path (mailer->url, &path)))
    return status;
  
  if (access (path, X_OK) == -1)
    return errno;

  status = mu_progmailer_set_command (pm, path);
132
  mu_debug (MU_DEBCAT_MAILER, MU_DEBUG_TRACE, ("prog (%s)", path));
133 134 135 136 137 138 139 140 141
  return status;
}

static int
prog_close (mu_mailer_t mailer)
{
  return mu_progmailer_close (mailer->data);
}

142
struct prog_exp
143 144
{
  mu_message_t msg;
145 146 147 148
  mu_address_t sender_addr;
  char *sender_str;
  mu_address_t rcpt_addr;
  char *rcpt_str;
149 150
};

151 152 153 154 155 156 157 158 159
static const char *
_expand_sender (struct prog_exp *pe)
{
  if (!pe->sender_str &&
      mu_address_aget_email (pe->sender_addr, 1, &pe->sender_str))
    return NULL;
  return pe->sender_str;
}

160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205
static int
address_add (mu_address_t *paddr, const char *value)
{
  mu_address_t addr = NULL;
  int status;
  
  status = mu_address_create (&addr, value);
  if (status)
    return status;
  status = mu_address_union (paddr, addr);
  mu_address_destroy (&addr);
  return status;
}

static int
message_read_rcpt (mu_message_t msg, mu_address_t *paddr)
{
  mu_header_t header = NULL;
  const char *value;
  int status;
  
  status = mu_message_get_header (msg, &header);
  if (status)
    return status;
  
  status = mu_header_sget_value (header, MU_HEADER_TO, &value);

  if (status == 0)
    address_add (paddr, value);
  else if (status != MU_ERR_NOENT)
    return status;

  status = mu_header_sget_value (header, MU_HEADER_CC, &value);
  if (status == 0)
    address_add (paddr, value);
  else if (status != MU_ERR_NOENT)
    return status;

  status = mu_header_sget_value (header, MU_HEADER_BCC, &value);
  if (status == 0)
    address_add (paddr, value);
  else if (status != MU_ERR_NOENT)
    return status;
  return 0;
}

206 207
static const char *
_expand_rcpt (struct prog_exp *pe)
208 209 210
{
  int status;

211
  if (!pe->rcpt_str)
212 213 214 215 216 217
    {
      size_t i, count = 0;
      size_t len = 0;
      char *str;
      mu_address_t tmp_addr = NULL, addr;
      
218 219
      if (pe->rcpt_addr)
	addr = pe->rcpt_addr;
220 221
      else
	{
222
	  status = message_read_rcpt (pe->msg, &tmp_addr);
223 224 225
	  if (status)
	    {
	      mu_address_destroy (&tmp_addr);
226
	      return NULL;
227 228 229 230 231 232 233 234 235 236 237 238 239
	    }
	  addr = tmp_addr;
	}
	    
      mu_address_get_count (addr, &count);
      for (i = 1; i <= count; i++)
	{
	  const char *email;
	  if (i > 1)
	    len++;
	  if ((status = mu_address_sget_email (addr, i, &email)) != 0)
	    {
	      mu_address_destroy (&tmp_addr);
240
	      return NULL;
241 242 243 244 245 246 247 248
	    }
	  len += strlen (email);
	}

      str = malloc (len + 1);
      if (!str)
	{
	  mu_address_destroy (&tmp_addr);
249
	  return NULL;
250
	}
251
      pe->rcpt_str = str;
252 253 254 255 256 257 258 259 260 261 262 263 264 265
      
      for (i = 1; i <= count; i++)
	{
	  const char *email;
	  if (i > 1)
	    *str++ = ' ';
	  if (mu_address_sget_email (addr, i, &email))
	    continue;
	  strcpy (str, email);
	  str += strlen (email);
	}
      *str = 0;
      mu_address_destroy (&tmp_addr);
    }  
266
  return pe->rcpt_str;
267 268
}

269 270 271 272 273
#define SEQ(s, n, l) \
  (((l) == (sizeof(s) - 1)) && memcmp (s, n, l) == 0)

static const char *
prog_getvar (const char *name, size_t nlen, void *data)
274
{
275 276 277 278 279 280 281
  struct prog_exp *pe = data;
  
  if (SEQ ("sender", name, nlen))
    return _expand_sender (pe);
  if (SEQ ("rcpt", name, nlen))
    return _expand_rcpt (pe);
  return NULL;
282 283 284 285 286 287 288 289
}

static int
url_to_argv (mu_url_t url, mu_message_t msg,
	     mu_address_t from, mu_address_t to,
	     int *pargc, char ***pargv)
{
  int rc;
290
  struct prog_exp pe;
291 292 293
  char **query;
  size_t i;
  size_t argc;
294
  char **argv;
295 296
  struct mu_wordsplit ws;
  int wsflags;
297
  
298 299 300 301 302 303 304 305 306
  pe.msg = msg;
  pe.rcpt_addr = to;
  pe.sender_addr = from;
  pe.sender_str = pe.rcpt_str = NULL;

  ws.ws_getvar = prog_getvar;
  ws.ws_closure = &pe;
  wsflags = MU_WRDSF_NOSPLIT | MU_WRDSF_NOCMD |
            MU_WRDSF_GETVAR | MU_WRDSF_CLOSURE;
307

308
  rc = mu_url_sget_query (url, &argc, &query);
309 310
  if (rc)
    return rc;
311

312
  argv = calloc (argc + 2, sizeof (argv[0]));
313 314 315
  if (!argv)
    return ENOMEM;

316 317 318 319 320 321 322
  rc = mu_url_aget_path (url, &argv[0]);
  if (rc)
    {
      free (argv);
      return rc;
    }
  
323 324
  for (i = 0; i < argc; i++)
    {
325
      if (mu_wordsplit (query[i], &ws, wsflags))
326 327
	{
	  mu_argcv_free (i, argv);
328 329
	  mu_wordsplit_free (&ws);
	  return errno;
330
	}
331 332 333 334 335
      if (ws.ws_wordc == 0)
	argv[i+1] = strdup ("");
      else
	argv[i+1] = strdup (ws.ws_wordv[0]);
      wsflags |= MU_WRDSF_REUSE;
336
    }
337 338 339 340
  argv[i+1] = NULL;
  mu_wordsplit_free (&ws);
  free (pe.sender_str);
  free (pe.rcpt_str);
341
  
342
  *pargc = argc;
343 344 345 346 347 348 349 350 351 352 353 354
  *pargv = argv;
  return 0;
}

static int
prog_send_message (mu_mailer_t mailer, mu_message_t msg, mu_address_t from,
		   mu_address_t to)
{
  mu_progmailer_t pm = mailer->data;
  int argc;
  char **argv;
  int status;
355
  const char *command;
356

357 358 359
  status = mu_url_sget_path (mailer->url, &command);
  if (status && status != MU_ERR_NOENT)
    {
360 361 362
      mu_debug (MU_DEBCAT_MAILER, MU_DEBUG_ERROR,
		("cannot get path from URL: %s",
		 mu_strerror (status)));
363 364 365 366 367
      return status;
    }
  status = mu_progmailer_set_command (pm, command);
  if (status)
    {
368 369 370
      mu_debug (MU_DEBCAT_MAILER, MU_DEBUG_ERROR,
		("cannot set progmailer command: %s",
		 mu_strerror (status)));
371 372 373 374
      return status;
    }
      
  status = url_to_argv (mailer->url, msg, from, to, &argc, &argv);
375 376
  if (status)
    {
377 378 379
      mu_debug (MU_DEBCAT_MAILER, MU_DEBUG_ERROR,
		("cannot convert URL to command line: %s",
		 mu_strerror (status)));
380 381 382 383 384 385 386 387 388 389 390
      return status;
    }

  status = mu_progmailer_open (pm, argv);
  if (status == 0)
    {
      status = mu_progmailer_send (pm, msg);
      if (status == 0)
	mu_observable_notify (mailer->observable, MU_EVT_MAILER_MESSAGE_SENT,
			      msg);
      else
391 392
	mu_debug (MU_DEBCAT_MAILER, MU_DEBUG_ERROR,
		  ("progmailer error: %s", mu_strerror (status)));
393 394 395 396 397 398 399 400
    }
  
  mu_argcv_free (argc, argv);
  return status;
}

#else
#include <stdio.h>
401
#include <mailutils/sys/registrar.h>
402 403
mu_record_t mu_prog_record = NULL;
#endif