Commit 0beef50e 0beef50e329d8cfe8c68103e4d7d6dc579f4855f by Sergey Poznyakoff

User-defined actions for comsat.

1 parent 1a6af882
/* GNU mailutils - a suite of utilities for electronic mail
Copyright (C) 1999, 2000, 2001 Free Software Foundation, Inc.
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, 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 details.
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., 675 Mass Ave, Cambridge, MA 02139, USA. */
#include "comsat.h"
#define obstack_chunk_alloc malloc
#define obstack_chunk_free free
#include <obstack.h>
/* This module implements user-configurable actions for comsat. The
actions are kept in file .biffrc in the user's home directory and
are executed in sequence. Possible actions:
beep -- Produce audible signal
echo STRING -- Output STRING to the user's tty
exec PROG ARGS... -- Execute given program (not yet implemented)
Following metacharacters are accepted in strings:
$u -- Expands to username
$h -- Expands to hostname
$H{name} -- Expands to value of message header `name'
$B(C,L) -- Expands to message body. C and L give maximum
number of characters and line in the expansion.
When omitted, they default to 400, 5. */
int
act_getline (FILE *fp, char **sptr, size_t *size)
{
char buf[256];
int cont = 1;
size_t used = 0;
while (cont && fgets (buf, sizeof buf, fp))
{
int len = strlen (buf);
if (buf[len-1] == '\n')
{
buf[--len] = 0;
if (buf[len-1] == '\\')
{
buf[--len] = 0;
cont = 1;
}
else
cont = 0;
}
else
cont = 1;
if (len + used + 1 > *size)
{
*sptr = realloc (*sptr, len + used + 1);
if (!*sptr)
return -1;
*size = len + used + 1;
}
memcpy (*sptr + used, buf, len);
used += len;
}
if (*sptr)
(*sptr)[used] = 0;
return used;
}
/* Convert second character of a backslash sequence to its ASCII
value: */
int
backslash(int c)
{
static char transtab[] = "a\ab\bf\fn\nr\rt\t";
char *p;
for (p = transtab; *p; p += 2)
{
if (*p == c)
return p[1];
}
return c;
}
int
expand_escape (char **pp, message_t msg, struct obstack *stk)
{
char *p = *pp;
char *start, *sval, *namep;
int len;
header_t hdr;
body_t body;
stream_t stream;
int rc = 1;
size_t size, lncount;
switch (*++p) /* skip past $ */
{
case 'u':
len = strlen (username);
obstack_grow (stk, username, len);
*pp = p;
rc = 0;
break;
case 'h':
len = strlen (hostname);
obstack_grow (stk, hostname, len);
*pp = p;
rc = 0;
break;
case 'H':
/* Header field */
if (*++p != '{')
break;
start = ++p;
p = strchr (p, '}');
if (!p)
break;
len = p - start;
if (len == 0
|| (namep = malloc (len + 1)) == NULL)
break;
memcpy (namep, start, len);
namep[len] = 0;
if (message_get_header (msg, &hdr) == 0
&& header_aget_value (hdr, namep, &sval) == 0)
{
len = strlen (sval);
obstack_grow (stk, sval, len);
}
free (namep);
*pp = p;
rc = 0;
break;
case 'B':
/* Message body */
if (*++p == '(')
{
size = strtoul (p + 1, &p, 0);
if (*p == ',')
lncount = strtoul (p + 1, &p, 0);
if (*p != ')')
break;
p++;
}
if (size == 0)
size = 400;
if (lncount == 0)
lncount = 5;
if (message_get_body (msg, &body) == 0
&& body_get_stream (body, &stream) == 0)
{
size_t nread;
char *buf = malloc (size+1);
if (!buf)
break;
if (stream_read (stream, buf, size, 0, &nread) == 0)
{
char *q;
buf[nread] = 0;
q = buf;
size = 0;
while (lncount--)
{
char *s = strchr (q, '\n');
if (!q)
break;
size += s - q + 1;
q = s + 1;
}
obstack_grow (stk, buf, size);
}
free (buf);
}
*pp = p;
rc = 0;
}
return rc;
}
char *
expand_line (char *str, char *cr, message_t msg)
{
char *p;
int i, c, len;
size_t size;
struct obstack stk;
if (!*str)
return NULL;
obstack_init (&stk);
for (p = str; *p; p++)
{
switch (*p)
{
case '\n':
len = strlen (cr);
obstack_grow (&stk, cr, len);
break;
case '\\':
p++;
switch (*p)
{
case 0:
obstack_1grow (&stk, c);
break;
case 'n':
len = strlen (cr);
obstack_grow (&stk, cr, len);
break;
default:
c = backslash (*p);
obstack_1grow (&stk, c);
}
break;
case '$':
if (expand_escape (&p, msg, &stk) == 0)
break;
/*FALLTHRU*/
default:
obstack_1grow (&stk, *p);
}
}
obstack_1grow (&stk, 0);
str = strdup (obstack_finish (&stk));
obstack_free (&stk, NULL);
return str;
}
char *default_action =
"Mail to \a$u@$h\a ---\n"
"From: $H{from}\n"
"Subject: $H{Subject}\n"
"---\n"
"$B(,5)\n"
"---\n";
/* Take care to clear eighth bit, so we won't upset some stupid terminals */
#define LB(c) ((c)&0x7f)
void
action_beep (FILE *tty)
{
fprintf (tty, "\a\a");
}
void
action_echo (FILE *tty, char *str)
{
char *p;
if (!str)
return;
for (p = str; *p; p++)
*p = LB (*p);
fprintf (tty, "%s", str);
}
FILE *
open_rc (char *filename, FILE *tty)
{
struct stat stb;
struct passwd *pw = getpwnam (username);
if (stat (filename, &stb) == 0)
{
if (stb.st_uid != pw->pw_uid)
{
syslog (LOG_NOTICE, "%s's %s is not owned by %s",
username, filename, username);
return NULL;
}
if ((stb.st_mode & 0777) != 0600)
{
fprintf (tty, "Warning: your .biffrc has wrong permissions\r\n");
syslog (LOG_NOTICE, "%s's %s has wrong permissions",
username, filename);
return NULL;
}
}
return fopen (filename, "r");
}
void
run_user_action (FILE *tty, char *cr, message_t msg)
{
FILE *fp;
int nact = 0;
char *stmt = NULL;
size_t size = 0;
fp = open_rc (BIFF_RC, tty);
if (fp)
{
int line = 0;
while (act_getline (fp, &stmt, &size))
{
char *str;
int argc;
char **argv;
line++;
str = expand_line (stmt, cr, msg);
if (!str)
continue;
if (argcv_get (str, "", &argc, &argv)
|| argc == 0
|| argv[0][0] == '#')
{
free (str);
argcv_free (argc, argv);
continue;
}
if (strcmp (argv[0], "beep") == 0)
{
action_beep (tty);
nact++;
}
else if (strcmp (argv[0], "echo") == 0)
{
action_echo (tty, argv[1]);
nact++;
}
else
{
syslog (LOG_ERR, "%s:.biffrc:%d: unknown keyword %s",
username, line, argv[0]);
break;
}
}
fclose (fp);
}
if (nact == 0)
{
char *str = expand_line (default_action, cr, msg);
action_echo (tty, str);
}
}