/* $Id$ */
/*
* Copyright (c) 2006 Nicholas Marriott <nicholas.marriott@gmail.com>
*
* Permission to use, copy, modify, and distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
* copyright notice and this permission notice appear in all copies.
*
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
* WHATSOEVER RESULTING FROM LOSS OF MIND, USE, DATA OR PROFITS, WHETHER
* IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING
* OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*/
#include <sys/types.h>
#include <fnmatch.h>
#include <stdarg.h>
#include <string.h>
#include <time.h>
#include "fdm.h"
#define ALIAS_IDX(ch) /* LINTED */ \
(((ch) >= 'a' && (ch) <= 'z') ? (ch) - 'a' : \
(((ch) >= 'A' && (ch) <= 'Z') ? 26 + (ch) - 'A' : -1))
static const char *aliases[] = {
"account", /* a */
NULL, /* b */
NULL, /* c */
"day", /* d */
NULL, /* e */
NULL, /* f */
NULL, /* g */
"home", /* h */
NULL, /* i */
NULL, /* j */
NULL, /* l */
NULL, /* l */
"month", /* m */
"uid", /* n */
NULL, /* o */
NULL, /* p */
NULL, /* q */
NULL, /* r */
"source", /* s */
"action", /* t */
"user", /* u */
NULL, /* v */
NULL, /* w */
NULL, /* x */
"year", /* y */
NULL, /* z */
NULL, /* A */
NULL, /* B */
NULL, /* C */
NULL, /* D */
NULL, /* E */
NULL, /* F */
NULL, /* G */
"hour", /* H */
NULL, /* I */
NULL, /* J */
NULL, /* K */
NULL, /* L */
"minute", /* M */
NULL, /* N */
NULL, /* O */
NULL, /* P */
"quarter", /* Q */
NULL, /* R */
"second", /* S */
NULL, /* T */
NULL, /* U */
NULL, /* V */
"dayofweek", /* W */
NULL, /* X */
"dayofyear", /* Y */
NULL, /* Z */
};
char *replace(char *, struct strb *, struct mail *, struct rmlist *);
const char *submatch(char, struct mail *, struct rmlist *, size_t *);
void printflike3
add_tag(struct strb **tags, const char *key, const char *value, ...)
{
va_list ap;
va_start(ap, value);
strb_vadd(tags, key, value, ap);
va_end(ap);
}
const char *
find_tag(struct strb *tags, const char *key)
{
struct strbent *sbe;
sbe = strb_find(tags, key);
if (sbe == NULL)
return (NULL);
return (STRB_VALUE(tags, sbe));
}
const char *
match_tag(struct strb *tags, const char *pattern)
{
struct strbent *sbe;
sbe = strb_match(tags, pattern);
if (sbe == NULL)
return (NULL);
return (STRB_VALUE(tags, sbe));
}
void
default_tags(struct strb **tags, const char *src)
{
char rtime[128];
struct tm *tm;
time_t t;
strb_clear(tags);
if (src != NULL)
add_tag(tags, "source", "%s", src);
if (conf.host_name != NULL)
add_tag(tags, "hostname", "%s", conf.host_name);
t = time(NULL);
if ((tm = localtime(&t)) != NULL) {
/*
* Okay, in a struct tm, everything is zero-based (including
* month!) except day of the month which is one-based.
*
* To make thing clearer, strftime(3) measures everything as
* you would expect... except that day of the week runs from
* 0-6 but day of the year runs from 1-366.
*
* Fun fun fun.
*/
add_tag(tags, "hour", "%.2d", tm->tm_hour);
add_tag(tags, "minute", "%.2d", tm->tm_min);
add_tag(tags, "second", "%.2d", tm->tm_sec);
add_tag(tags, "day", "%.2d", tm->tm_mday);
add_tag(tags, "month", "%.2d", tm->tm_mon + 1);
add_tag(tags, "year", "%.4d", 1900 + tm->tm_year);
add_tag(tags, "year2", "%.2d", tm->tm_year % 100);
add_tag(tags, "dayofweek", "%d", tm->tm_wday);
add_tag(tags, "dayofyear", "%.2d", tm->tm_yday + 1);
add_tag(tags, "quarter", "%d", tm->tm_mon / 3 + 1);
}
if (rfc822time(t, rtime, sizeof rtime) != NULL)
add_tag(tags, "rfc822date", "%s", rtime);
}
void
update_tags(struct strb **tags, struct userdata *ud)
{
add_tag(tags, "user", "%s", ud->name);
add_tag(tags, "home", "%s", ud->home);
add_tag(tags, "uid", "%lu", (u_long) ud->uid);
add_tag(tags, "gid", "%lu", (u_long) ud->gid);
}
void
reset_tags(struct strb **tags)
{
add_tag(tags, "user", "%s", "");
add_tag(tags, "home", "%s", "");
add_tag(tags, "uid", "%s", "");
add_tag(tags, "gid", "%s", "");
}
char *
replacestr(struct replstr *rs, struct strb *tags, struct mail *m,
struct rmlist *rml)
{
return (replace(rs->str, tags, m, rml));
}
char *
replacepath(struct replpath *rp, struct strb *tags, struct mail *m,
struct rmlist *rml, const char *home)
{
char *s, *t;
s = replace(rp->str, tags, m, rml);
if ((t = expand_path(s, home)) == NULL)
return (s);
xfree(s);
return (t);
}
const char *
submatch(char ch, struct mail *m, struct rmlist *rml, size_t *len)
{
struct rm *rm;
if (rml == NULL || !rml->valid || m == NULL)
return (NULL);
rm = &rml->list[((u_char) ch) - '0'];
if (!rm->valid)
return (NULL);
*len = rm->eo - rm->so;
return (m->data + rm->so);
}
char *
replace(char *src, struct strb *tags, struct mail *m, struct rmlist *rml)
{
const char *tptr, *alias;
char *ptr, *tend, *dst, ch;
size_t i, off, len, tlen;
int strip;
if (src == NULL)
return (NULL);
if (*src == '\0')
return (xstrdup(""));
off = 0;
len = REPLBUFSIZE;
dst = xmalloc(len);
strip = 1;
for (ptr = src; *ptr != '\0'; ptr++) {
switch (*ptr) {
case '%':
break;
default:
ENSURE_FOR(dst, len, off, 1);
dst[off++] = *ptr;
continue;
}
switch (ch = *++ptr) {
case '\0':
goto out;
case '%':
ENSURE_FOR(dst, len, off, 1);
dst[off++] = '%';
continue;
case '[':
if ((tend = strchr(ptr, ']')) == NULL) {
ENSURE_FOR(dst, len, off, 2);
dst[off++] = '%';
dst[off++] = '[';
continue;
}
ptr++;
if (*ptr == ':') {
strip = 0;
ptr++;
}
if (ptr == tend)
continue;
*tend = '\0';
if ((tptr = find_tag(tags, ptr)) == NULL) {
*tend = ']';
ptr = tend;
continue;
}
tlen = strlen(tptr);
*tend = ']';
ptr = tend;
break;
case ':':
ch = *++ptr;
if (ch >= '0' && ch <= '9') {
tptr = submatch(ch, m, rml, &tlen);
if (tptr == NULL)
continue;
strip = 0;
break;
}
ENSURE_FOR(dst, len, off, 1);
dst[off++] = ch;
continue;
default:
if (ch >= '0' && ch <= '9') {
tptr = submatch(ch, m, rml, &tlen);
if (tptr == NULL)
continue;
break;
}
alias = NULL;
if (ALIAS_IDX((u_char) ch) != -1)
alias = aliases[ALIAS_IDX((u_char) ch)];
if (alias == NULL)
continue;
if ((tptr = find_tag(tags, alias)) == NULL)
continue;
tlen = strlen(tptr);
break;
}
if (tlen == 0)
continue;
ENSURE_FOR(dst, len, off, tlen);
if (!strip) {
memcpy(dst + off, tptr, tlen);
off += tlen;
continue;
}
for (i = 0; i < tlen; i++) {
if (strchr(conf.strip_chars, tptr[i]) == NULL)
dst[off++] = tptr[i];
}
}
out:
ENSURE_FOR(dst, len, off, 1);
dst[off] = '\0';
return (dst);
}