/* wydawca - automatic release submission daemon Copyright (C) 2007, 2008 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" /* Directive file support */ /* Parse directives from TRP->blurb. Fill TRP->directive */ int directive_parse (struct file_triplet *trp) { size_t dcount, i, j; char *p; if (debug_level > 2) logmsg (LOG_DEBUG, _("%s: parsing directive blurb: %s"), trp->file[file_directive].name, trp->blurb); dcount = 0; for (p = trp->blurb; *p; p++) if (*p == '\n') dcount++; trp->directive = xcalloc (dcount + 1, sizeof trp->directive[0]); p = trp->blurb; for (i = j = 0; i < dcount; i++) { trp->directive[j] = p; p = strchr (p, '\n'); if (p) *p++ = 0; if (trim (trp->directive[j]) == 0) /* ignore empty lines */ continue; if (strchr (trp->directive[j], ':') == NULL) { logmsg (LOG_ERR, _("%s: invalid line: %s"), trp->file[file_directive].name, trp->directive[j]); free (trp->directive); trp->directive = NULL; return 1; } j++; if (!p) break; } trp->directive[j] = NULL; return 0; } /* If a directive KEY exists in the triplet TRP, return 0 and point PVAL (unless it is NULL) to its value. Othervise, return 1. */ int directive_get_value (struct file_triplet *trp, const char *key, const char **pval) { int keylen = strlen (key); int i; for (i = 0; trp->directive[i]; i++) { char *str = trp->directive[i]; int len = strlen (str); if (len > keylen && memcmp (str, key, keylen) == 0 && str[keylen] == ':') { str += keylen + 1; while (*str && isspace (*str)) str++; if (pval) *pval = str; return 0; } } return 1; } /* Auxiliary function for sequential access to directories from TRP. Arguments: N - Index of the current directive, TRP - Triplet, PKEY, PVAL - Return addresses. The function points PKEY and PVAL to the keyword and value of the Nth directive, and returns N + 1. If N points past all the directive, the function returns 0. */ static int _directive_seq_get (int n, struct file_triplet *trp, const char **pkey, const char **pval) { char *p; size_t len; if (trp->directive[n] == NULL) return 0; p = strchr (trp->directive[n], ':'); len = p - trp->directive[n]; if (len + 1 > trp->tmpsize) { trp->tmpsize = len + 1; trp->tmp = x2realloc (trp->tmp, &trp->tmpsize); } memcpy (trp->tmp, trp->directive[n], len); trp->tmp[len] = 0; *pkey = trp->tmp; for (p++; *p && isspace (*p); p++) ; if (pval) *pval = p; return ++n; } /* Get the first directive from TRP. Point *PKEY to its keyword and *PVAL to its value. Return 1 on success, 0 on failure. */ int directive_first (struct file_triplet *trp, const char **pkey, const char **pval) { int n = 0; return _directive_seq_get (n, trp, pkey, pval); } /* Get the first directive from TRP. Point *PKEY to its keyword and *PVAL to its value. Return 1 on success, 0 on failure. Return non-0 on success, 0 on failure */ int directive_next (struct file_triplet *trp, int n, const char **pkey, const char **pval) { return _directive_seq_get (n, trp, pkey, pval); } /* Pack a directive string VAL into an unsigned number */ int directive_pack_version (const char *val, unsigned *pversion) { char *p; unsigned v; v = strtoul (val, &p, 10); if (*p != '.') return 1; p++; v *= 100; v += strtoul (p, &p, 10); if (*p && *p != '.') return 1; *pversion = v; return 0; } /* Return true if the directory file version of the triplet TRP is within the inclusive range FROM and TO (packed) */ int directive_version_in_range_p (struct file_triplet *trp, unsigned from, unsigned to) { const char *val; unsigned version; if (directive_get_value (trp, "version", &val)) { logmsg (LOG_ERR, _("%s: missing `version' directive"), trp->file[file_directive].name); return 0; } if (directive_pack_version (val, &version)) { logmsg (LOG_ERR, _("%s: unparsable version: %s"), trp->file[file_directive].name, val); return 0; } if (from <= version && version <= to) return 1; logmsg (LOG_ERR, _("%s: version %s is not in the allowed range"), trp->file[file_directive].name, val); return 0; } enum directive { unknown_dir, comment_dir, directory_dir, version_dir, filename_dir, rmsymlink_dir, archive_dir, symlink_dir }; struct directive_table { const char *name; enum directive dir; }; static struct directive_table directive_table[] = { { "comment", comment_dir }, { "directory", directory_dir }, { "version", version_dir }, { "filename", filename_dir }, { "symlink", symlink_dir }, { "rmsymlink", rmsymlink_dir }, { "archive", archive_dir }, { NULL } }; static enum directive find_directive (const char *key) { int i; for (i = 0; directive_table[i].name; i++) if (strcmp (directive_table[i].name, key) == 0) return directive_table[i].dir; return unknown_dir; } /* Return 0 if the directory file format of the triplet TRP is OK. */ int verify_directive_format (struct file_triplet *trp) { int n, dnum; const char *key; if (!directive_version_in_range_p (trp, MIN_DIRECTIVE_VERSION, MAX_DIRECTIVE_VERSION)) return 1; dnum = 0; for (n = directive_first (trp, &key, NULL); n; n = directive_next (trp, n, &key, NULL)) { if (strcmp (key, "comment") == 0) continue; dnum++; switch (dnum) { case 1: if (strcmp (key, "version")) { logmsg (LOG_ERR, _("%s:%d: expected `%s' but found `%s'"), trp->file[file_directive].name, n, "version", key); return 1; } break; case 2: if (strcmp (key, "directory")) { logmsg (LOG_ERR, _("%s:%d: expected `%s' but found `%s'"), trp->file[file_directive].name, n, "directory", key); return 1; } break; default: if (find_directive (key) == unknown_dir) { logmsg (LOG_ERR, _("%s:%d: unknown directive `%s'"), trp->file[file_directive].name, n, key); return 1; } } } if (trp->file[file_dist].name && trp->file[file_signature].name) { const char *filename; if (directive_get_value (trp, "filename", &filename)) { logmsg (LOG_ERR, _("%s: missing `filename' directive"), trp->file[file_directive].name); return 1; } if (strcmp (filename, trp->file[file_dist].name)) { logmsg (LOG_ERR, _("%s: filename %s does not match actual name"), trp->file[file_dist].name, filename); return 1; } } return 0; } /* Process the directives from TRP, using given SPOOL */ int process_directives (struct file_triplet *trp, const struct spool *spool) { int rc, n; const char *key, *val; char *relative_dir; UPDATE_STATS (STAT_COMPLETE_TRIPLETS); timer_start ("triplet"); report_init (); for (n = directive_first (trp, &key, &val); n; n = directive_next (trp, n, &key, &val)) { enum directive d = find_directive (key); switch (d) { case unknown_dir: /* should not happen */ abort (); case comment_dir: logmsg (LOG_NOTICE, _("%s: COMMENT: %s"), trp->file[file_directive].name, val); break; case directory_dir: relative_dir = safe_file_name_alloc (val); if (!relative_dir || relative_dir[0] == '/') { logmsg (LOG_ERR, _("%s: invalid directory: %s"), trp->file[file_directive].name, val); return 1; } break; case filename_dir: rc = verify_detached_signature (trp, spool); if (rc == 0) { if (move_file (trp, spool, file_dist, relative_dir) || move_file (trp, spool, file_signature, relative_dir)) return 1; } else { logmsg (LOG_ERR, _("invalid detached signature for %s"), trp->name); return 1; } break; case version_dir: /* Already processed */ break; case archive_dir: if (archive_file (trp, spool, relative_dir, val)) return 1; break; case symlink_dir: { int rc = 0; struct wordsplit ws; if (wordsplit (val, &ws, WRDSF_DEFFLAGS)) { logmsg (LOG_ERR, _("cannot parse symlink value `%s'"), val); return 1; } if (ws.ws_wordc != 2) { rc = 1; logmsg (LOG_ERR, _("wrong number of arguments to %s directive: `%s'"), key, val); } else rc = symlink_file (trp, spool, relative_dir, ws.ws_wordv[0], ws.ws_wordv[1]); wordsplit_free (&ws); if (rc) return 1; } break; case rmsymlink_dir: if (rmsymlink_file (trp, spool, relative_dir, val)) return 1; } } if (!dry_run_mode && unlink (trp->file[file_directive].name)) { logmsg (LOG_CRIT, _("%s: cannot unlink directive file: %s"), trp->file[file_directive].name, strerror (errno)); } free (relative_dir); UPDATE_STATS (STAT_TRIPLET_SUCCESS); report_finish (); timer_stop ("triplet"); notify (spool->notification, trp, ev_success); return 0; }