/* wydawca - automatic release submission daemon Copyright (C) 2007-2011 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" /* Return the length of the string without trailing whitespace */ size_t trim_length (const char *str) { size_t len; for (len = strlen (str); len > 0 && isspace (str[len-1]); len--) ; return len; } /* Chop off any trailing whitespace. Return the length of the resulting string. */ size_t trim (char *str) { size_t len = trim_length (str); str[len] = 0; return len; } #define MSG_BEGIN_MARKER_STR "-----BEGIN PGP SIGNED MESSAGE-----\n" #define MSG_BEGIN_MARKER_LEN (sizeof (MSG_BEGIN_MARKER_STR) - 1) #define SIG_BEGIN_MARKER_STR "-----BEGIN PGP SIGNATURE-----\n" #define SIG_BEGIN_MARKER_LEN (sizeof (SIG_BEGIN_MARKER_STR) - 1) static int extract_plaintext (char *blurb) { char *start, *p; if (memcmp (blurb, MSG_BEGIN_MARKER_STR, MSG_BEGIN_MARKER_LEN)) return 1; p = blurb + MSG_BEGIN_MARKER_LEN; while (*p) { if (*p == '\n') { p++; break; } p = strchr (p, '\n'); if (!p) return 1; p++; } if (!*p) return 1; start = p; while (*p) { if (strncmp (p, SIG_BEGIN_MARKER_STR, SIG_BEGIN_MARKER_LEN) == 0) { *p++ = 0; memmove (blurb, start, p - start); return 0; } p = strchr (p, '\n'); if (!p) return 1; p++; } return 1; } int fill_project_name (struct file_triplet *trp) { char *blurb; size_t size; FILE *fp; char *p; const char *directory; int rc; size = trp->file[file_directive].sb.st_size; if (size <= MSG_BEGIN_MARKER_LEN) { logmsg (LOG_ERR, _("too small directive file %s"), trp->file[file_directive].name); return 1; } fp = fopen (trp->file[file_directive].name, "r"); if (!fp) { logmsg (LOG_ERR, _("cannot open file %s: %s"), trp->file[file_directive].name, strerror (errno)); return 1; } blurb = grecs_malloc (size + 1); rc = fread (blurb, size, 1, fp); fclose (fp); if (rc != 1) { logmsg (LOG_ERR, _("error reading file %s: %s"), trp->file[file_directive].name, strerror (errno)); free (blurb); return 1; } blurb[size] = 0; if (extract_plaintext (blurb)) { logmsg (LOG_ERR, _("%s: unrecognized format"), trp->file[file_directive].name); free (blurb); return 1; } trp->blurb = blurb; if (directive_parse (trp)) { free (blurb); trp->blurb = NULL; return 1; } if (directive_get_value (trp, "directory", &directory)) { logmsg (LOG_ERR, _("%s: missing `directory' directive"), trp->file[file_directive].name); return 1; } trp->relative_dir = safe_file_name (triplet_strdup (trp, directory)); if (!trp->relative_dir || trp->relative_dir[0] == '/') { logmsg (LOG_ERR, _("%s: invalid directory: %s"), trp->file[file_directive].name, directory); return 1; } p = strchr (trp->relative_dir, '/'); if (p) { size_t len = p - trp->relative_dir; if (len == 0) { logmsg (LOG_ERR, _("%s: empty `directory' directive"), trp->file[file_directive].name); return 1; } txtacc_grow (trp->acc, trp->relative_dir, len); txtacc_1grow (trp->acc, 0); trp->project = txtacc_finish (trp->acc, 0); } else trp->project = trp->relative_dir; return 0; } struct uploader_info * new_uploader_info (struct uploader_info *src) { struct uploader_info *p = grecs_malloc (sizeof (*p)); p->next = NULL; p->name = src->name; p->realname = src->realname; p->gpg_key = src->gpg_key; p->email = src->email; p->fpr = NULL; return p; } struct uploader_info * uploader_find_frp (struct uploader_info *list, const char *fpr) { for (; list; list = list->next) if (list->fpr && strcmp (list->fpr, fpr) == 0) break; return list; } int verify_directive_file (struct file_triplet *trp) { char *command; int rc; void *md; size_t nrows, ncols, i; struct uploader_info *head, *tail; const struct spool *spool; struct dictionary *dict; ASGN_SPOOL (spool, trp, return 1); dict = spool->dictionary[project_uploader_dict]; if (!trp->file[file_directive].name) return 1; if (fill_project_name (trp)) return 1; md = dictionary_open (dict); if (!md) return 1; command = triplet_expand_dictionary_query (dict, md, trp); rc = dictionary_lookup (dict, md, command); free (command); if (rc) { logmsg (LOG_ERR, _("cannot get uploaders for %s"), trp->name); dictionary_close (dict, md); return 1; } nrows = dictionary_num_rows (dict); if (nrows == 0) { logmsg (LOG_ERR, _("found no uploaders for %s"), trp->name); dictionary_close (dict, md); return 1; } ncols = dictionary_num_cols (dict); if (ncols < 4) { logmsg (LOG_ERR, _("project-uploader dictionary error: too few columns (%lu)"), (unsigned long) ncols); dictionary_close (dict, md); return 1; } head = tail = NULL; for (i = 0; i < nrows; i++) { const char *p; struct uploader_info info, *ptr; memset (&info, 0, sizeof (info)); p = dictionary_result (dict, md, i, 0); if (p) info.name = triplet_strdup (trp, p); p = dictionary_result (dict, md, i, 1); if (p) info.realname = triplet_strdup (trp, p); p = dictionary_result (dict, md, i, 2); if (p) info.email = triplet_strdup (trp, p); p = dictionary_result (dict, md, i, 3); if (p) info.gpg_key = triplet_strdup (trp, p); if (debug_level > 3) { logmsg (LOG_DEBUG, _("name: %s"), SP (info.name)); logmsg (LOG_DEBUG, _("realname: %s"), SP (info.realname)); logmsg (LOG_DEBUG, _("gpg-key: %s"), SP (info.gpg_key)); logmsg (LOG_DEBUG, _("email: %s"), SP (info.email)); } if (!info.name || !info.realname || !info.gpg_key || !info.email) { logmsg (LOG_ERR, _("project-uploader dictionary error: malformed row %lu"), (unsigned long) i); /* FIXME: Memory not reclaimed */ continue; } ptr = new_uploader_info (&info); if (tail) tail->next = ptr; else head = ptr; tail = ptr; } dictionary_close (dict, md); if (!head) { logmsg (LOG_ERR, _("no valid uploaders found for %s"), trp->name); return 1; } trp->uploader_list = head; trp->uploader = NULL; if (verify_directive_signature (trp)) { /*FIXME: Update stats */ logmsg (LOG_ERR, _("invalid signature for %s"), trp->name ? trp->name : "[unknown]"); return 1; } else if (debug_level) logmsg (LOG_DEBUG, _("%s: directive file signature OK"), trp->name); if (debug_level > 1) { int i; for (i = 0; trp->directive[i]; i++) logmsg (LOG_DEBUG, "directive[%d] = %s", i, trp->directive[i]); } if (verify_directive_format (trp)) return 1; return 0; }