/* 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 . */ #include "wydawca.h" #include "sql.h" #include 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 absolute file name")); else *(char**) varptr = word; return 0; } static int cb_set_umask (enum grecs_callback_command cmd, grecs_locus_t *locus, void *varptr, grecs_value_t *value, void *cb_data) { char *p; mode_t m; if (assert_string_arg (locus, cmd, value)) return 1; m = strtoul (value->v.string, &p, 8) & 0777; if (*p) grecs_error (&value->locus, 0, _("invalid umask (near %s)"), p); else umask (m); return 0; } static struct keyword stat_tab[] = { { "errors", STAT_ERRORS }, { "warnings", STAT_WARNINGS }, { "bad-signatures", STAT_BAD_SIGNATURE }, { "access-violations", STAT_ACCESS_VIOLATIONS }, { "complete-triplets", STAT_COMPLETE_TRIPLETS }, { "incomplete-triplets", STAT_INCOMPLETE_TRIPLETS }, { "bad-triplets", STAT_BAD_TRIPLETS }, { "expired-triplets", STAT_EXPIRED_TRIPLETS }, { "triplet-success", STAT_TRIPLET_SUCCESS }, { "uploads", STAT_UPLOADS }, { "archives", STAT_ARCHIVES }, { "symlinks", STAT_SYMLINKS }, { "rmsymlinks", STAT_RMSYMLINKS }, { NULL }, }; static int parse_single_statmask (grecs_locus_t *locus, const grecs_value_t *val, unsigned long *pmask, int *invert) { const char *arg; int x; if (val->type != GRECS_TYPE_STRING) { grecs_error (&val->locus, 0, _("expected scalar value but found list")); return 1; } arg = val->v.string; if (strcmp (arg, "all") == 0) { *pmask = STAT_MASK_ALL; *invert = 1; return 0; } else if (strcmp (arg, "none") == 0) { *pmask = STAT_MASK_NONE; *invert = 0; return 0; } if (keyword_to_tok (arg, stat_tab, &x)) { grecs_error (&val->locus, 0, _("unknown statistics type: %s"), arg); return 1; } *pmask = STAT_MASK (x); return 0; } static int parse_statmask (grecs_locus_t *loc, grecs_value_t *val, unsigned long *pmask) { int err = 0; int invert = 0; unsigned long mask = 0; switch (val->type) { case GRECS_TYPE_STRING: err = parse_single_statmask (loc, val, &mask, &invert); break; case GRECS_TYPE_ARRAY: { int i; for (i = 0; i < val->v.arg.c; i++) { unsigned long x; if (parse_single_statmask (loc, val->v.arg.v[i], &x, &invert)) err = 1; else if (invert) mask &= ~x; else mask |= x; } } break; case GRECS_TYPE_LIST: { struct grecs_list_entry *ep; for (ep = val->v.list->head; ep; ep = ep->next) { const grecs_value_t *vp = ep->data; unsigned long x; if (parse_single_statmask (loc, vp, &x, &invert)) err = 1; else if (invert) mask &= ~x; else mask |= x; } } break; } if (!err) *pmask = mask; return err; } static int cb_statistics (enum grecs_callback_command cmd, grecs_locus_t *locus, void *varptr, grecs_value_t *value, void *cb_data) { return parse_statmask (locus, value, varptr); } static int cb_sql_host (enum grecs_callback_command cmd, grecs_locus_t *locus, void *varptr, grecs_value_t *value, void *cb_data) { struct sqlconn *pconn = varptr; char *p; if (assert_string_arg (locus, cmd, value)) return 1; p = strchr (value->v.string, ':'); if (p) { /* FIXME: Modifies constant string */ *p++ = 0; if (p[0] == '/') { pconn->socket = grecs_strdup (p); pconn->host = grecs_strdup ("localhost"); } else { char *end; unsigned long n = strtoul (p, &end, 10); if (*end) { grecs_error (&value->locus, 0, _("invalid port number (near %s)"), end); return 0; } if (n == 0 || n > USHRT_MAX) { grecs_error (&value->locus, 0, _("port number out of range 1..%d"), USHRT_MAX); return 0; } pconn->port = n; /* Save host name */ pconn->host = grecs_strdup (value->v.string); } } else pconn->host = grecs_strdup (value->v.string); return 0; } static int cb_sql (enum grecs_callback_command cmd, grecs_locus_t *locus, void *varptr, grecs_value_t *value, void *cb_data) { struct sqlconn *pconn; void **pdata = cb_data; switch (cmd) { case grecs_callback_section_begin: if (!value || value->type != GRECS_TYPE_STRING) { grecs_error(value ? &value->locus : locus, 0, _("tag must be a string")); return 0; } pconn = grecs_zalloc (sizeof (*pconn)); pconn->ident = strdup (value->v.string); *pdata = pconn; break; case grecs_callback_section_end: pconn = *pdata; sql_register_conn (pconn); free (pconn); *pdata = NULL; break; case grecs_callback_set_value: grecs_error (locus, 0, _("invalid use of block statement")); } return 0; } static struct grecs_keyword sql_kw[] = { { "config-file", N_("file"), N_("Read MySQL configuration from "), grecs_type_string, GRECS_DFLT, NULL, offsetof(struct sqlconn, config_file) }, { "config-group", N_("name"), N_("Read the named group from the SQL configuration file"), grecs_type_string, GRECS_DFLT, NULL, offsetof(struct sqlconn, config_group) }, { "host", N_("host"), N_("Set SQL server hostname or IP address"), grecs_type_string, GRECS_DFLT, NULL, 0, cb_sql_host }, { "database", N_("dbname"), N_("Set database name"), grecs_type_string, GRECS_DFLT, NULL, offsetof(struct sqlconn, database), }, { "user", N_("name"), N_("Set SQL user name"), grecs_type_string, GRECS_DFLT, NULL, offsetof(struct sqlconn, user) }, { "password", N_("arg"), N_("Set SQL user password"), grecs_type_string, GRECS_DFLT, NULL, offsetof(struct sqlconn, password) }, { "ssl-ca", N_("file"), N_("File name of the Certificate Authority (CA) certificate"), grecs_type_string, GRECS_DFLT, NULL, offsetof(struct sqlconn, cacert) }, { NULL } }; static int cb_syslog_facility (enum grecs_callback_command cmd, grecs_locus_t *locus, void *varptr, grecs_value_t *value, void *cb_data) { if (assert_string_arg (locus, cmd, value)) return 1; if (mu_string_to_syslog_facility (value->v.string, varptr)) grecs_error (&value->locus, 0, _("Unknown syslog facility `%s'"), value->v.string); return 0; } static int cb_define_message (enum grecs_callback_command cmd, grecs_locus_t *locus, void *varptr, grecs_value_t *value, void *cb_data) { const char *ident; if (cmd != grecs_callback_set_value) { grecs_error (locus, 0, _("Unexpected block statement")); return 1; } if (!value || value->type != GRECS_TYPE_ARRAY || value->v.arg.c != 2) { grecs_error (locus, 0, _("expected two arguments")); return 1; } if (value->v.arg.v[0]->type != GRECS_TYPE_STRING) { grecs_error (&value->v.arg.v[0]->locus, 0, _("first argument not a string")); return 1; } ident = value->v.arg.v[0]->v.string; if (value->v.arg.v[1]->type != GRECS_TYPE_STRING) { grecs_error (&value->v.arg.v[1]->locus, 0, _("second argument not a string")); return 1; } register_message_template (ident, value->v.arg.v[1]->v.string); return 0; } static struct grecs_keyword syslog_kw[] = { { "facility", N_("name"), N_("Set syslog facility. Arg is one of the following: user, daemon, " "auth, authpriv, mail, cron, local0 through local7 (case-insensitive), " "or a facility number."), grecs_type_string, GRECS_DFLT, &log_facility, 0, cb_syslog_facility }, { "tag", N_("string"), N_("Tag syslog messages with this string"), grecs_type_string, GRECS_DFLT, &syslog_tag }, { "print-priority", N_("arg"), N_("Prefix each message with its priority"), grecs_type_bool, GRECS_DFLT, &syslog_include_prio }, { NULL }, }; static struct keyword backup_tab[] = { { "none", no_backups }, { "off", no_backups }, { "simple", simple_backups }, { "never", simple_backups }, { "existing", numbered_existing_backups }, { "nil", numbered_existing_backups }, { "numbered", numbered_backups }, { "t", numbered_backups }, { NULL } }; static enum backup_type get_backup_version (grecs_locus_t *locus, const char *ctx, const char *version) { int d; if (version == 0 || *version == 0) return numbered_existing_backups; else if (keyword_to_tok (version, backup_tab, &d)) { if (ctx) grecs_error (locus, 0, _("%s: ambiguous backup type `%s'"), ctx, version); else grecs_error (locus, 0, _("ambiguous backup type `%s'"), version); return no_backups; } return d; } static int cb_backup (enum grecs_callback_command cmd, grecs_locus_t *locus, void *varptr, grecs_value_t *value, void *cb_data) { enum backup_type *ptype = varptr; if (assert_string_arg (locus, cmd, value)) return 1; *ptype = get_backup_version (&value->locus, NULL, value->v.string); return 0; } static struct grecs_keyword archive_kw[] = { { "name", N_("file-or-dir"), N_("Name of archive file or directory"), grecs_type_string, GRECS_DFLT, NULL, offsetof(struct archive_descr, name) }, { "backup", N_("type"), N_("Define backup type"), grecs_type_string, GRECS_DFLT, NULL, offsetof(struct archive_descr, backup_type), cb_backup }, { NULL } }; static int cb_archive (enum grecs_callback_command cmd, grecs_locus_t *locus, void *varptr, grecs_value_t *value, void *cb_data) { struct archive_descr *arch = varptr; void **pdata = cb_data; switch (cmd) { case grecs_callback_section_begin: *pdata = arch; /* fallthrough */ case grecs_callback_set_value: if (!value) { grecs_error (locus, 0, _("expected tag")); return 1; } if (value->type != GRECS_TYPE_STRING) { grecs_error (&value->locus, 0, _("expected scalar value but found list")); return 1; } if (strcmp (value->v.string, "none") == 0) arch->type = archive_none; else if (strcmp (value->v.string, "tar") == 0) arch->type = archive_tar; else if (strcmp (value->v.string, "directory") == 0) arch->type = archive_directory; else { grecs_error (&value->locus, 0, _("unknown archive type")); return 1; } if (cmd == grecs_callback_section_begin) return 0; break; case grecs_callback_section_end: break; } if (arch->type == archive_none) return 0; if (arch->name == NULL) { grecs_error (locus, 0, _("at least archive name must be set")); return 1; } if (arch->type == archive_tar && arch->backup_type != no_backups) { grecs_warning (locus, 0, _("backup type ignored for this archive type")); return 1; } return 0; } static struct grecs_keyword mail_statistics_kw[] = { { "message", N_("text"), N_("Message text"), grecs_type_string, GRECS_DFLT, &admin_stat_message }, { "statistics", N_("items"), N_("Send mail if one or more of these items are set"), grecs_type_string, GRECS_DFLT, &mail_admin_mask, 0, cb_statistics }, { "gpg-sign", N_("key"), N_("Sign message with this key"), grecs_type_string, GRECS_DFLT, &admin_stat_sign_key }, { NULL } }; static int cb_event (enum grecs_callback_command cmd, grecs_locus_t *locus, void *varptr, grecs_value_t *value, void *cb_data) { enum notification_event *pev = varptr; if (assert_string_arg (locus, cmd, value)) return 1; string_to_notification_event (&value->locus, value->v.string, pev); return 0; } static int cb_recipient (enum grecs_callback_command cmd, grecs_locus_t *locus, void *varptr, grecs_value_t *value, void *cb_data) { enum notification_target *tgt = varptr; if (assert_string_arg (locus, cmd, value)) return 1; string_to_notification_target (&value->locus, value->v.string, tgt); return 0; } static struct grecs_keyword notify_event_kw[] = { { "event", N_("ev-id"), N_("Event on which to notify"), grecs_type_string, GRECS_DFLT, NULL, offsetof(struct notification, ev), cb_event }, { "recipient", N_("who"), N_("Notify this recipient"), grecs_type_string, GRECS_DFLT, NULL, offsetof(struct notification, tgt), cb_recipient }, { "message", N_("text-or-id"), N_("Text of the notification or identifier of a defined message template"), grecs_type_string, GRECS_DFLT, NULL, offsetof(struct notification, msg) }, { "gpg-sign", N_("key"), N_("Sign message with this key"), grecs_type_string, GRECS_DFLT, NULL, offsetof(struct notification, sign_keys) }, { NULL } }; static int cb_notify_event (enum grecs_callback_command cmd, grecs_locus_t *locus, void *varptr, grecs_value_t *value, void *cb_data) { struct notification *ntf; void **pdata = cb_data; switch (cmd) { case grecs_callback_section_begin: ntf = grecs_zalloc (sizeof (*ntf)); *pdata = ntf; break; case grecs_callback_section_end: ntf = *pdata; if (!ntf->msg) grecs_error (locus, 0, _("missing message definition")); else { struct notification **p = (struct notification **) varptr; ntf->next = *p; *p = ntf; /* FIXME: check ev and tgt? */ } break; case grecs_callback_set_value: grecs_error (locus, 0, _("invalid use of block statement")); } return 0; } static enum dictionary_type string_to_dictionary_type (const char *str) { if (strcmp (str, "sql") == 0) return dictionary_sql; else if (strcmp (str, "builtin") == 0) return dictionary_builtin; else if (strcmp (str, "external") == 0) return dictionary_external; else return dictionary_none; } static int cb_dictionary_type (enum grecs_callback_command cmd, grecs_locus_t *locus, void *varptr, grecs_value_t *value, void *cb_data) { enum dictionary_type *ptype = varptr; if (assert_string_arg (locus, cmd, value)) return 1; *ptype = string_to_dictionary_type (value->v.string); if (*ptype == dictionary_none) grecs_error (&value->locus, 0, _("unknown dictionary type: %s"), value->v.string); return 0; } static int cb_dictionary_params (enum grecs_callback_command cmd, grecs_locus_t *locus, void *varptr, grecs_value_t *value, void *cb_data) { struct dictionary *meth = varptr; size_t size; if (cmd != grecs_callback_set_value) { grecs_error (locus, 0, _("Unexpected block statement")); return 1; } if (!value || value->type != GRECS_TYPE_LIST) { grecs_error (value ? &value->locus : locus, 0, _("expected list value")); return 1; } size = grecs_list_size (value->v.list); if (size == 0) { meth->parmc = 0; meth->parmv = NULL; } else { int i; struct grecs_list_entry *ep; meth->parmc = size; meth->parmv = grecs_calloc (size + 1, sizeof (meth->parmv[0])); for (i = 0, ep = value->v.list->head; ep; ep = ep->next, i++) { const grecs_value_t *vp = ep->data; if (assert_string_arg (locus, cmd, vp)) break; meth->parmv[i] = grecs_strdup (vp->v.string); } meth->parmv[i] = NULL; } return 0; } static struct grecs_keyword dictionary_kw[] = { { "type", N_("type"), N_("Dictionary type"), grecs_type_string, GRECS_DFLT, NULL, offsetof(struct dictionary, type), cb_dictionary_type }, { "query", N_("string"), N_("Query template"), grecs_type_string, GRECS_DFLT, NULL, offsetof(struct dictionary, query) }, { "params", N_("arg"), N_("Set dictionary parameters"), grecs_type_string, GRECS_LIST, NULL, 0, cb_dictionary_params }, { NULL } }; int string_to_dictionary_id (grecs_locus_t *locus, const char *str, enum dictionary_id *idp) { static struct keyword id_tab[] = { { "project-uploader", project_uploader_dict }, { "project-owner", project_owner_dict }, { NULL } }; int res; if (keyword_to_tok (str, id_tab, &res)) { grecs_error (locus, 0, _("unknown dictionary ID: %s"), str); return 1; } *idp = res; return 0; } static int cb_dictionary (enum grecs_callback_command cmd, grecs_locus_t *locus, void *varptr, grecs_value_t *value, void *cb_data) { struct dictionary **pmeth, *meth; void **pdata = cb_data; enum dictionary_id id; switch (cmd) { case grecs_callback_section_begin: if (!value || value->type != GRECS_TYPE_STRING) { grecs_error(value ? &value->locus : locus, 0, _("tag must be a string")); return 0; } if (string_to_dictionary_id (&value->locus, value->v.string, &id)) return 1; pmeth = (struct dictionary **) varptr + id; *pmeth = dictionary_new (id, dictionary_builtin); *pdata = *pmeth; break; case grecs_callback_section_end: meth = *pdata; switch (meth->type) { case dictionary_sql: if (meth->parmc == 0 || !meth->parmv[0]) { grecs_error (locus, 0, _("SQL connection is not declared")); meth->type = dictionary_none; } else if (!sql_connection_exists_p (meth->parmv[0])) { grecs_error (locus, 0, _("SQL connection `%s' not declared"), meth->parmv[0]); meth->type = dictionary_none; } break; default: /* FIXME: More checks ? */ break; } *pdata = NULL; break; case grecs_callback_set_value: grecs_error (locus, 0, _("invalid use of block statement")); } return 0; } static int cb_url (enum grecs_callback_command cmd, grecs_locus_t *locus, void *varptr, grecs_value_t *value, void *cb_data) { mu_url_t *purl = varptr, url; int rc; if (assert_string_arg (locus, cmd, value)) return 1; rc = mu_url_create (&url, value->v.string); if (rc) { grecs_error (&value->locus, 0, _("cannot create URL `%s': %s"), value->v.string, mu_strerror (rc)); return rc; } *purl = url; return 0; } static struct grecs_keyword spool_kw[] = { { "url", N_("arg"), N_("URL corresponding to this spool"), grecs_type_string, GRECS_DFLT, NULL, offsetof(struct spool, url) }, { "alias", N_("arg"), N_("Aliases"), grecs_type_string, GRECS_LIST, NULL, offsetof(struct spool, aliases) }, { "source", N_("dir"), N_("Source directory"), grecs_type_string, GRECS_DFLT, NULL, offsetof(struct spool, source_dir) }, { "destination", N_("dir"), N_("Destination directory"), grecs_type_string, GRECS_DFLT, NULL, offsetof(struct spool, dest_url), cb_url }, { "file-sweep-time", N_("interval"), N_("Define file sweep time"), grecs_type_string, GRECS_DFLT, NULL, offsetof(struct spool, file_sweep_time), cb_interval }, { "dictionary", N_("ident"), N_("Define data dictionary"), grecs_type_section, GRECS_DFLT, NULL, offsetof(struct spool, dictionary), cb_dictionary, NULL, dictionary_kw }, { "archive", N_("type: string"), N_("Set up archivation"), grecs_type_section, GRECS_DFLT, NULL, offsetof(struct spool, archive), cb_archive, NULL, archive_kw }, { "notify-event", NULL, N_("Configure notification"), grecs_type_section, GRECS_DFLT, NULL, offsetof(struct spool, notification), cb_notify_event, NULL, notify_event_kw }, { "check-script", NULL, N_("A /bin/sh script to verify the tarball"), grecs_type_string, GRECS_DFLT, NULL, offsetof(struct spool, check_script) }, { NULL } }; static int cb_spool (enum grecs_callback_command cmd, grecs_locus_t *locus, void *varptr, grecs_value_t *value, void *cb_data) { struct spool *spool; void **pdata = cb_data; int rc, ec, i; switch (cmd) { case grecs_callback_section_begin: if (!value || value->type != GRECS_TYPE_STRING) { grecs_error (value ? &value->locus : locus, 0, _("tag must be a string")); return 1; } spool = grecs_zalloc (sizeof (*spool)); spool->tag = grecs_strdup (value->v.string); spool->file_sweep_time = file_sweep_time; for (i = 0; i < NITEMS (spool->dictionary); i++) spool->dictionary[i] = default_dictionary[i]; spool->archive = default_archive_descr; *pdata = spool; break; case grecs_callback_section_end: rc = 0; spool = *pdata; if (!spool->source_dir) { grecs_error (locus, 0, _("source is not given")); rc = 1; } else if (test_dir (spool->source_dir, &ec)) { if (ec) grecs_error (locus, ec, _("cannot access %s"), spool->source_dir); else grecs_error (locus, 0, _("%s is not a directory"), spool->source_dir); rc = 1; } if (!spool->dest_url) { grecs_error (locus, 0, _("destination is not given")); rc = 1; } else if (url_to_vtab (spool->dest_url, &spool->vtab)) { grecs_error (locus, 0, _("unsupported url: %s"), mu_url_to_string (spool->dest_url)); rc = 1; } else if (spool->vtab.test_url && spool->vtab.test_url (spool->dest_url, locus)) rc = 1; for (i = 0; i < dictionary_count; i++) if (spool->dictionary[i]->type == dictionary_external) { grecs_error (locus, 0, _("Sorry, the dictionary type `external' is not yet supported")); rc = 1; } if (rc) return rc; if (!spool->notification) spool->notification = default_notification; mu_url_sget_path (spool->dest_url, &spool->dest_dir); register_spool (spool); free (spool); *pdata = NULL; break; case grecs_callback_set_value: grecs_error (locus, 0, _("invalid use of block statement")); } return 0; } static int cb_user (enum grecs_callback_command cmd, grecs_locus_t *locus, void *varptr, grecs_value_t *value, void *cb_data) { struct passwd *pw; if (assert_string_arg (locus, cmd, value)) return 1; pw = getpwnam (value->v.string); if (!pw) { grecs_error (&value->locus, 0, _("no such user: %s"), value->v.string); return 1; } wydawca_uid = pw->pw_uid; wydawca_gid = pw->pw_gid; return 0; } static int cb_supp_groups (enum grecs_callback_command cmd, grecs_locus_t *locus, void *varptr, grecs_value_t *value, void *cb_data) { if (cmd != grecs_callback_set_value) { grecs_error (locus, 0, _("Unexpected block statement")); return 1; } if (!value || value->type != GRECS_TYPE_LIST) { grecs_error (value ? &value->locus : locus, 0, _("expected list value")); return 1; } wydawca_supp_groupc = grecs_list_size (value->v.list); if (wydawca_supp_groupc == 0) wydawca_supp_groups = NULL; else { int i; struct grecs_list_entry *ep; wydawca_supp_groups = grecs_calloc (wydawca_supp_groupc, sizeof (wydawca_supp_groups[0])); for (i = 0, ep = value->v.list->head; ep; ep = ep->next, i++) { const grecs_value_t *vp = ep->data; struct group *grp; if (assert_string_arg (locus, cmd, vp)) break; grp = getgrnam (vp->v.string); if (!grp) { grecs_error (&value->locus, 0, _("no such group: %s"), value->v.string); break; } wydawca_supp_groups[i] = grp->gr_gid; } } return 0; } static struct grecs_keyword locking_kw[] = { { "enable", NULL, N_("Enable or disable locking"), grecs_type_bool, GRECS_DFLT, &enable_locking }, { "directory", N_("dir"), N_("Set directory for lock files"), grecs_type_string, GRECS_DFLT, &lockdir }, { "expire-time", N_("interval"), N_("Define lock expiration interval"), grecs_type_string, GRECS_DFLT, &lock_expire_time, 0, cb_interval }, { "timeout", N_("interval"), N_("Locking timeout"), grecs_type_string, GRECS_DFLT, &lock_timeout, 0, cb_interval }, { NULL } }; static int cb_locking (enum grecs_callback_command cmd, grecs_locus_t *locus, void *varptr, grecs_value_t *value, void *cb_data) { if (cmd == grecs_callback_set_value) { if (!value || value->type != GRECS_TYPE_STRING) { grecs_error (value ? &value->locus : locus, 0, _("expected scalar value as a tag")); return 1; } grecs_string_convert (&enable_locking, grecs_type_bool, value->v.string, locus); } return 0; } static int cb_upload_version (enum grecs_callback_command cmd, grecs_locus_t *locus, void *varptr, grecs_value_t *value, void *cb_data) { unsigned *pversion = varptr, n; if (assert_string_arg (locus, cmd, value)) return 1; if (directive_pack_version (value->v.string, &n)) { grecs_error (&value->locus, 0, _("invalid version number")); return 0; } if (n < MIN_DIRECTIVE_VERSION) { grecs_error (&value->locus, 0, _("version number too low")); return 0; } if (n > MAX_DIRECTIVE_VERSION) { grecs_error (&value->locus, 0, _("version number too high")); return 0; } *pversion = n; return 0; } static struct grecs_keyword wydawca_kw[] = { { "daemon", NULL, N_("Enable daemon mode"), grecs_type_bool, GRECS_DFLT, &daemon_mode }, { "foreground", NULL, N_("Start in foreground even in daemon mode"), grecs_type_bool, GRECS_DFLT, &foreground }, { "single-process", NULL, N_("Do not spawn subprocesses"), grecs_type_bool, GRECS_DFLT, &single_process }, { "wakeup-interval", N_("time"), N_("Set wake-up interval"), grecs_type_string, GRECS_DFLT, &wakeup_interval, 0, cb_interval }, { "pidfile", N_("file"), N_("Set pid file name"), grecs_type_string, GRECS_DFLT, &pidfile }, { "user", N_("name"), N_("Run with UID and GID of this user"), grecs_type_string, GRECS_DFLT, NULL, 0, cb_user }, { "group", NULL, N_("Retain these supplementary groups"), grecs_type_string, GRECS_LIST, NULL, 0, cb_supp_groups }, { "min-version", N_("major.minor"), N_("Set minimal allowed directive file version"), grecs_type_string, GRECS_DFLT, &min_directive_version, 0, cb_upload_version }, { "max-version", N_("major.minor"), N_("Set maximal allowed directive file version"), grecs_type_string, GRECS_DFLT, &max_directive_version, 0, cb_upload_version }, { "locking", NULL, N_("Configure locking"), grecs_type_section, GRECS_DFLT, NULL, 0, cb_locking, NULL, locking_kw }, { "listen", N_("socket"), N_("Listen on this address"), grecs_type_sockaddr, GRECS_DFLT, &listen_sockaddr, }, #ifdef WITH_LIBWRAP { "tcp-wrapper", NULL, N_("Configure TCP wrappers"), grecs_type_section, GRECS_DFLT, NULL, 0, NULL, NULL, tcpwrapper_kw }, #endif { "mailer", N_("url"), N_("Set mailer URL"), grecs_type_string, GRECS_DFLT, &mailer, 0, cb_mailer }, { "admin-address", N_("email"), N_("Set admin email address"), grecs_type_string, GRECS_DFLT, &admin_address, 0, cb_email_address }, { "from-address", N_("email"), N_("Set sender email address"), grecs_type_string, GRECS_DFLT, &from_address, 0, cb_email_address }, /* FIXME: Must be a built-in type? */ { "file-sweep-time", N_("interval"), N_("Define file sweep time"), grecs_type_string, GRECS_DFLT, &file_sweep_time, 0, cb_interval }, { "tar-program", N_("prog"), N_("Set tar invocation command line"), grecs_type_string, GRECS_DFLT, &tar_command_name, 0, cb_absolute_name }, { "umask", N_("mask: octal"), N_("Set umask"), grecs_type_string, GRECS_DFLT, NULL, 0, cb_set_umask }, { "archive-signatures", NULL, N_("Control implicit signature archivation"), grecs_type_bool, GRECS_DFLT, &archive_signatures }, { "statistics", N_("items"), N_("Print these stats at the end of run"), grecs_type_string, GRECS_DFLT, &print_stats, 0, cb_statistics }, { "sql", N_("id: string"), N_("Define SQL database"), grecs_type_section, GRECS_DFLT, NULL, 0, cb_sql, NULL, sql_kw }, { "syslog", NULL, N_("Configure syslog logging"), grecs_type_section, GRECS_DFLT, NULL, 0, NULL, NULL, syslog_kw }, { "define-message", N_("ident: string> s_port; for (i = 0; i < dictionary_count; i++) default_dictionary[i] = dictionary_new (i, dictionary_builtin); } void config_finish (struct grecs_node *tree) { if (grecs_tree_process (tree, wydawca_kw)) exit (EX_CONFIG); }