/* 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 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_locus_t *locus, grecs_value_t *value, unsigned n, int type)
{
if (n >= value->v.arg.c)
{
grecs_error (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_locus_t *locus,
void *varptr,
grecs_value_t *value,
void *cb_data)
{
int rc;
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_locus_t *locus,
void *varptr,
grecs_value_t *value,
void *cb_data)
{
int rc = 1;
mu_address_t addr = NULL;
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:
{
struct grecs_list_entry *ep;
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 (&a);
if (rc)
break;
}
}
break;
case GRECS_TYPE_ARRAY:
grecs_error (locus, 0, _("too many arguments"));
return 1;
}
*(mu_address_t*) varptr = addr;
return rc;
}
static int
cb_interval (enum grecs_callback_command cmd,
grecs_locus_t *locus,
void *varptr,
grecs_value_t *value,
void *cb_data)
{
int rc;
time_t interval;
const char *endp;
/* FIXME 1: Support arrays */
if (assert_string_arg (locus, cmd, value))
return 1;
/* FIXME 2: Support ISO intervals? */
rc = parse_time_interval (value->v.string, &interval, &endp);
if (rc)
grecs_error (&value->locus, 0,
_("unrecognized interval format (near `%s')"),
endp);
else
*(time_t*) varptr = interval;
return 0;
}
static int
cb_absolute_name (enum grecs_callback_command cmd,
grecs_locus_t *locus,
void *varptr,
grecs_value_t *value,
void *cb_data)
{
char *word;
/* FIXME 1: Support arrays */
if (assert_string_arg (locus, cmd, value))
return 1;
word = safe_file_name ((char*)value->v.string);
if (!word || word[0] != '/')
grecs_error (&value->locus, 0, _("must be an abs
|