/* wydawca - automatic release submission daemon
Copyright (C) 2007-2012 Sergey Poznyakoff
Wydawca 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 3 of the License, or (at your
option) any later version.
Wydawca 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 wydawca. If not, see <http://www.gnu.org/licenses/>. */
#include "wydawca.h"
#include "sql.h"
#include <mail.h>
struct keyword {
char *name;
int tok;
};
static int
keyword_to_tok(const char *str, struct keyword *kw, int *pres)
{
for (; kw->name; kw++)
if (strcmp(kw->name, str) == 0) {
*pres = kw->tok;
return 0;
}
return 1;
}
static int
tok_to_keyword(int tok, struct keyword *kw, const char **pres)
{
for (; kw->name; kw++)
if (kw->tok == tok) {
*pres = kw->name;
return 0;
}
return 1;
}
static struct keyword kwfac[] = {
{ "USER", LOG_USER },
{ "DAEMON", LOG_DAEMON },
{ "AUTH", LOG_AUTH },
{ "AUTHPRIV",LOG_AUTHPRIV },
{ "MAIL", LOG_MAIL },
{ "CRON", LOG_CRON },
{ "LOCAL0", LOG_LOCAL0 },
{ "LOCAL1", LOG_LOCAL1 },
{ "LOCAL2", LOG_LOCAL2 },
{ "LOCAL3", LOG_LOCAL3 },
{ "LOCAL4", LOG_LOCAL4 },
{ "LOCAL5", LOG_LOCAL5 },
{ "LOCAL6", LOG_LOCAL6 },
{ "LOCAL7", LOG_LOCAL7 },
{ NULL }
};
static struct keyword kwpri[] = {
{ "EMERG", LOG_EMERG },
{ "ALERT", LOG_ALERT },
{ "CRIT", LOG_CRIT },
{ "ERR", LOG_ERR },
{ "WARNING", LOG_WARNING },
{ "NOTICE", LOG_NOTICE },
{ "INFO", LOG_INFO },
{ "DEBUG", LOG_DEBUG },
{ NULL }
};
int
wy_strtofac(const char *str)
{
int res;
if (keyword_to_tok(str, kwfac, &res))
return -1;
return res;
}
int
wy_strtopri(const char *str)
{
int res;
if (keyword_to_tok(str, kwpri, &res))
return -1;
return res;
}
const char *
wy_pritostr(int pri)
{
const char *res;
if (tok_to_keyword(pri, kwpri, &res))
return NULL;
return res;
}
const char *
wy_factostr(int fac)
{
const char *res;
if (tok_to_keyword(fac, kwfac, &res))
return NULL;
return res;
}
static struct archive_descr default_archive_descr = {
archive_none,
NULL,
no_backups
};
static struct dictionary *default_dictionary[dictionary_count];
static struct notification *default_notification = NULL;
/* safe_file_name: convert a file name possibly containig relative
specs (../) into a safer form using only direct descendence.
Strip trailing delimiter if present, unless it is the only character
left. E.g.:
/home/user/../smith --> /home/smith
/home/user/../.. --> /
../file --> NULL
*/
char *
safe_file_name(char *file_name)
{
int len;
char *p;
if (!file_name)
return file_name;
len = strlen(file_name);
/* Empty string is returned as is */
if (len == 0)
return file_name;
/* delete trailing delimiter if any */
if (len && file_name[len - 1] == '/')
file_name[len - 1] = 0;
/* Eliminate any ./ and /../ */
for (p = strchr(file_name, '.'); p; p = strchr(p, '.')) {
if (p[1] == '/' && (p == file_name || p[-1] == '/')) {
char *q, *s;
s = p + 2;
q = p;
while ((*q++ = *s++)) ;
continue;
} else if (p[1] == '.' && (p[2] == 0 || p[2] == '/')) {
if (p == file_name)
return NULL;
if (p[-1] == '/') {
/* found */
char *q, *s;
s = p + 2;
/* Find previous delimiter */
for (q = p - 2; *q != '/' && q >= file_name;
q--) ;
if (q < file_name) {
q = file_name;
s++;
}
/* Copy stuff */
p = q;
while ((*q++ = *s++)) ;
continue;
}
}
p++;
}
if (file_name[0] == 0) {
file_name[0] = '/';
file_name[1] = 0;
}
return file_name;
}
/* Same as safe_file_name, but returns an allocated copy. */
char *
safe_file_name_alloc(const char *file_name)
{
char *s = grecs_strdup(file_name);
char *ns = safe_file_name(s);
if (!ns)
free(s);
return ns;
}
static struct keyword event_tab[] = {
{ "success", ev_success },
{ "bad-ownership", ev_bad_ownership },
{ "bad-directive-signature", ev_bad_directive_signature },
{ "bad-detached-signature", ev_bad_detached_signature },
{ "check-failure", ev_check_fail },
{NULL}
};
const char *
notification_event_str(enum notification_event evt)
{
const char *ret;
if (tok_to_keyword(evt, event_tab, &ret)) {
grecs_error(NULL, 0,
_("INTERNAL ERROR: "
"unknown notification event number: %d"),
evt);
return NULL;
}
return ret;
}
int
string_to_notification_event(grecs_locus_t *locus, const char *val,
enum notification_event *pret)
{
int res;
if (keyword_to_tok(val, event_tab, &res)) {
grecs_error(locus, 0,
_("unknown notification event: %s"), val);
return 1;
}
*pret = res;
return 0;
}
static struct keyword target_tab[] = {
{ "read", notify_read }, /* Read recipients from the message
headers */
{ "message", notify_read },
{ "admin", notify_admin }, /* System administrator */
{ "owner", notify_owner }, /* Project admin */
{ "user", notify_user }, /* User (uploader) */
{ NULL }
};
const char *
notification_target_str(enum notification_target tgt)
{
const char *ret;
if (tok_to_keyword(tgt, target_tab, &ret)) {
grecs_error(NULL, 0,
_("INTERNAL ERROR: "
"unknown notification target number: %d"),
tgt);
return NULL;
}
return ret;
}
int
string_to_notification_target(grecs_locus_t *locus, const char *val,
enum notification_target *pret)
{
int res;
if (keyword_to_tok(val, target_tab, &res)) {
grecs_error(locus, 0,
_("unknown notification target: %s"), val);
return 1;
}
*pret = res;
return 0;
}
int
assert_string_arg(grecs_locus_t *locus,
enum grecs_callback_command cmd, const grecs_value_t *value)
{
if (cmd != grecs_callback_set_value) {
grecs_error(locus, 0, _("Unexpected block statement"));
return 1;
}
if (!value || value->type != GRECS_TYPE_STRING) {
grecs_error(&value->locus, 0,
_("expected scalar value as a tag"));
return 1;
}
return 0;
}
grecs_value_t *
get_arg(grecs_value_t *value, unsigned n, int type)
{
if (!value || value->type != GRECS_TYPE_ARRAY || n >= value->v.arg.c) {
grecs_error(&value->locus, 0, _("not enough arguments"));
return NULL;
}
value = value->v.arg.v[n];
if (value->type != type) {
grecs_error(&value->locus, 0, _("argument %d has wrong type"),
n);
return NULL;
}
return value;
}
static int
cb_mailer(enum grecs_callback_command cmd, grecs_node_t *node,
void *varptr, void *cb_data)
{
int rc;
grecs_locus_t *locus = &node->locus;
grecs_value_t *value = node->v.value;
if (assert_string_arg(locus, cmd, value))
return 1;
rc = mu_mailer_create(&mailer, value->v.string);
if (rc)
grecs_error(&value->locus, 0,
_("cannot create mailer `%s': %s"),
value->v.string,
mu_strerror(rc));
return rc;
}
static int
cb_email_address(enum grecs_callback_command cmd, grecs_node_t *node,
void *varptr, void *cb_data)
{
int rc = 1;
mu_address_t addr = NULL;
grecs_locus_t *locus = &node->locus;
grecs_value_t *value = node->v.value;
struct grecs_list_entry *ep;
switch (value->type) {
case GRECS_TYPE_STRING:
rc = mu_address_create(&addr, value->v.string);
if (rc) {
grecs_error(&value->locus, 0,
_("%s: invalid email address: %s"),
value->v.string, mu_strerror(rc));
return rc;
}
break;
case GRECS_TYPE_LIST:
for (ep = value->v.list->head; ep; ep = ep->next) {
const grecs_value_t *vp = ep->data;
mu_address_t a;
if (assert_string_arg(locus, cmd, vp))
return 1;
rc = mu_address_create(&a, vp->v.string);
if (rc == 0)
rc = mu_address_union(&addr, a);
else {
grecs_error(&value->locus, 0,
_("%s: invalid email address: %s"),
vp->v.string,
mu_strerror(rc));
}
mu_address_destroy(
|