diff options
-rw-r--r-- | src/config.c | 162 | ||||
-rw-r--r-- | src/directive.c | 1 | ||||
-rw-r--r-- | src/gpg.c | 26 | ||||
-rw-r--r-- | src/mail.c | 103 | ||||
-rw-r--r-- | src/mail.h | 3 | ||||
-rw-r--r-- | src/method.c | 36 | ||||
-rw-r--r-- | src/process.c | 5 | ||||
-rw-r--r-- | src/sql.c | 73 | ||||
-rw-r--r-- | src/sql.h | 3 | ||||
-rw-r--r-- | src/triplet.c | 324 | ||||
-rw-r--r-- | src/verify.c | 145 | ||||
-rw-r--r-- | src/wydawca.c | 34 | ||||
-rw-r--r-- | src/wydawca.h | 55 | ||||
-rw-r--r-- | src/wydawca.rc | 80 |
14 files changed, 917 insertions, 133 deletions
diff --git a/src/config.c b/src/config.c index 559e995..81db423 100644 --- a/src/config.c +++ b/src/config.c @@ -27,9 +27,13 @@ static struct obstack cfg_stk; static struct access_method default_verify_method; static struct access_method default_gpg_key_method; +static struct access_method default_project_owner_method; +static struct access_method default_user_data_method; static struct archive_descr default_archive_descr; +#define ISHSPACE(c) ((c)==' ' || (c)=='\t') #define skip_ws(s) while (*(s) && isspace (*(s))) (s)++; +#define skip_hws(s) while (*(s) && ISHSPACE (*(s))) (s)++; #define skip_word(s) while (*(s) && !isspace (*(s))) (s)++; /* Return a pointer to the next word from *PSTR. Advanse *PSTR to the beginning @@ -40,6 +44,12 @@ get_word (char **pstr) char *word; char *str = *pstr; + if (!str) + { + *pstr = ""; + return *pstr; + } + skip_ws (str); word = str; @@ -319,6 +329,30 @@ cfg_gpg_key (gsc_config_file_t *file, char *kw, char *val, void *data) _cfg_access_method (file, kw, val, method); } +static void +cfg_project_owner (gsc_config_file_t *file, char *kw, char *val, void *data) +{ + struct directory_pair *dp = data; + struct access_method *method; + if (dp) + method = dp->project_owner_method = method_new (method_builtin); + else + method = &default_project_owner_method; + _cfg_access_method (file, kw, val, method); +} + +static void +cfg_user_data (gsc_config_file_t *file, char *kw, char *val, void *data) +{ + struct directory_pair *dp = data; + struct access_method *method; + if (dp) + method = dp->user_data_method = method_new (method_builtin); + else + method = &default_user_data_method; + _cfg_access_method (file, kw, val, method); +} + static char const * const backup_args[] = { /* In a series of synonyms, present the most meaningful first, so @@ -447,6 +481,7 @@ static void cfg_directory (gsc_config_file_t *file, char *kw, char *val, void *unused) { int ec; + char *word; struct directory_pair dpair; static struct gsc_config_keyword keywords[] = { { "source", cfg_source }, @@ -454,18 +489,27 @@ cfg_directory (gsc_config_file_t *file, char *kw, char *val, void *unused) { "file-sweep-time", cfg_file_sweep_time }, { "verify-user", cfg_verify_user }, { "gpg-key", cfg_gpg_key }, + { "project-owner", cfg_project_owner }, + { "user-data", cfg_user_data }, { "archive", cfg_archive }, { NULL } }; - if (val) + memset (&dpair, 0, sizeof dpair); + word = get_word (&val); + if (!word) + dpair.url = NULL; + else + dpair.url = xstrdup (word); + if (val && val[0]) file->error_msg (file->file_name, file->line, "warning: junk in line ignored"); - memset (&dpair, 0, sizeof dpair); dpair.file_sweep_time = file_sweep_time; dpair.verify_method = &default_verify_method; dpair.gpg_key_method = &default_gpg_key_method; + dpair.project_owner_method = &default_project_owner_method; + dpair.user_data_method = &default_user_data_method; dpair.archive = default_archive_descr; gsc_config_parse_block (file, &dpair, keywords, "end"); @@ -799,21 +843,32 @@ _cfg_raw_read (gsc_config_file_t *file, char *val, struct obstack *stk) char *word = val ? get_word (&val) : ""; size_t wlen; size_t start_line = file->line; - + int skip_initial_ws = 0; + if (word[0] == 0) word = "end"; + if (word[0] == '-') + { + skip_initial_ws = 1; + word++; + } wlen = strlen (word); while (getline (&buf, &size, file->fp) > 0) { + char *p = buf; + file->line++; - if (end_marker_p (buf, word, wlen)) + + if (skip_initial_ws) + skip_hws (p); + if (end_marker_p (p, word, wlen)) { obstack_1grow (stk, 0); free (buf); return 0; } - obstack_grow (stk, buf, strlen (buf)); + obstack_grow (stk, p, strlen (p)); } file->error_msg (file->file_name, start_line, "missing end marker"); @@ -831,6 +886,95 @@ cfg_admin_stat_message (gsc_config_file_t *file, char *kw, char *val, +static const char *event_args[] = { + "success", + "bad_ownership", + "bad-ownership", + "bad_directive_signature", + "bad-directive-signature", + "bad_detached_signature", + "bad-detached-signature", + NULL +}; + +static enum notification_event event_types[] = { + ev_success, + ev_bad_ownership, + ev_bad_ownership, + ev_bad_directive_signature, + ev_bad_directive_signature, + ev_bad_detached_signature, + ev_bad_detached_signature +}; + +ARGMATCH_VERIFY (event_args, event_types); + +int +string_to_notification_event (gsc_config_file_t *file, char *val, + enum notification_event *pret) +{ + ptrdiff_t x = ARGMATCH (val, event_args, event_types); + + if (x == (ptrdiff_t)-1) + { + file->error_msg (file->file_name, file->line, + "unknown notification type: %s", val); + file->error_count++; + return 1; + } + *pret = event_types[x]; + return 0; +} + +static void +cfg_mail_user (gsc_config_file_t *file, char *kw, char *val, void *unused) +{ + int i; + int argc; + char **argv; + + if (argcv_get (val, NULL, NULL, &argc, &argv)) + { + file->error_msg (file->file_name, file->line, + "cannot parse value: %s", strerror (errno)); + file->error_count++; + return; + } + + for (i = 0; i < argc; i++) + { + enum notification_event evt; + + if (string_to_notification_event (file, argv[i], &evt)) + break; + owner_notification_flags |= STAT_MASK (evt); + } + argcv_free (argc, argv); +} + +static void +cfg_user_message (gsc_config_file_t *file, char *kw, char *val, void *unused) +{ + enum notification_event evt; + char *word = get_word (&val); + + if (!*word) + { + file->error_msg (file->file_name, file->line, + "not enough arguments"); + file->error_count++; + return; + } + + if (string_to_notification_event (file, word, &evt)) + return; + + if (_cfg_raw_read (file, val, &cfg_stk) == 0) + user_message_template[evt] = obstack_finish (&cfg_stk); +} + + + static struct gsc_config_keyword kw_handler[] = { { "syslog-tag", cfg_syslog_tag }, { "syslog-facility", cfg_syslog_facility }, @@ -841,14 +985,18 @@ static struct gsc_config_keyword kw_handler[] = { { "sql", cfg_sql }, { "verify-user", cfg_verify_user }, { "gpg-key", cfg_gpg_key }, + { "project-owner", cfg_project_owner }, + { "user-data", cfg_user_data }, { "umask", cfg_umask }, { "tar-program", cfg_tar_program }, { "archive", cfg_archive }, { "mailer", cfg_mailer }, - { "admin-addres", cfg_admin_address }, + { "admin-address", cfg_admin_address }, { "from-address", cfg_from_address }, { "mail-admin-stat", cfg_mail_admin_stat }, { "admin-stat-message", cfg_admin_stat_message }, + { "mail-user", cfg_mail_user }, + { "user-message", cfg_user_message }, { NULL } }; @@ -857,6 +1005,8 @@ parse_config () { default_verify_method.type = method_builtin; default_gpg_key_method.type = method_builtin; + default_project_owner_method.type = method_none; + default_user_data_method.type = method_none; default_archive_descr.type = archive_none; default_archive_descr.backup_type = no_backups; diff --git a/src/directive.c b/src/directive.c index cc9ed1d..24e31f1 100644 --- a/src/directive.c +++ b/src/directive.c @@ -400,5 +400,6 @@ process_directives (struct file_triplet *trp, struct directory_pair *dpair) free (relative_dir); UPDATE_STATS (STAT_TRIPLET_SUCCESS); + notify (trp, ev_success); return 0; } @@ -224,20 +224,22 @@ verify_directive_signature (struct file_triplet *trp, { gpgme_verify_result_t result; + size = gpgme_data_seek (plain, 0, SEEK_END); + gpgme_data_seek (plain, 0, SEEK_SET); + trp->blurb = xmalloc (size + 1); + gpgme_data_read (plain, trp->blurb, size); + trp->blurb[size] = 0; + gpgme_data_release (plain); + + rc = directive_parse (trp); + result = gpgme_op_verify_result (ctx); - if (gpg_sig_ok_p (ctx, result->signatures)) + if (!gpg_sig_ok_p (ctx, result->signatures)) { - size = gpgme_data_seek (plain, 0, SEEK_END); - gpgme_data_seek (plain, 0, SEEK_SET); - trp->blurb = xmalloc (size + 1); - gpgme_data_read (plain, trp->blurb, size); - trp->blurb[size] = 0; - gpgme_data_release (plain); - - rc = directive_parse (trp); + UPDATE_STATS (STAT_BAD_SIGNATURE); + notify (trp, ev_bad_directive_signature); + rc = 1; } - else - rc = 1; } else { @@ -245,7 +247,6 @@ verify_directive_signature (struct file_triplet *trp, UPDATE_STATS (STAT_BAD_SIGNATURE); logmsg (LOG_ERR, "%s: directive verification failed: %s", trp->name, gpgme_strerror (ec)); - /* FIXME: Send mail to the project maintainer */ } gpgme_data_release (directive_data); @@ -292,6 +293,7 @@ verify_detached_signature (struct file_triplet *trp, case exec_fail: UPDATE_STATS (STAT_BAD_SIGNATURE); logmsg (LOG_ERR, "BAD detached signature for %s", trp->name); + notify (trp, ev_bad_detached_signature); break; case exec_error: @@ -23,6 +23,8 @@ mu_address_t admin_address; mu_address_t from_address; unsigned long mail_admin_mask; char *admin_stat_message_template; +unsigned long owner_notification_flags; +char *user_message_template[MAX_EVENT]; void mail_init () @@ -49,6 +51,8 @@ mail_send_message (mu_address_t rcpt, const char *text) mu_header_t hdr; int mailer_flags = 0; static char *x_mailer = "wydawca (" PACKAGE_STRING ")"; + size_t size; + char *buf; mu_message_create (&msg, NULL); @@ -57,6 +61,13 @@ mail_send_message (mu_address_t rcpt, const char *text) mu_message_get_header (msg, &hdr); mu_header_append (hdr, "X-Mailer", x_mailer); + mu_address_to_string (rcpt, NULL, 0, &size); + buf = xmalloc (size + 1); + mu_address_to_string (rcpt, buf, size + 1, NULL); + + mu_header_set_value (hdr, "To", buf, 1); + free (buf); + if (debug_level > 1) { mu_debug_t debug; @@ -114,10 +125,12 @@ mail_stats () exp[0].kw = "date"; exp[0].value = xstrdup (ctime (&t)); exp[0].value [strlen (exp[0].value) - 1] = 0; + exp[0].expand = NULL; + make_stat_expansion (exp + 1); text = expand_param (admin_stat_message_template, - exp, sizeof (exp) / sizeof (exp[0])); + exp, sizeof (exp) / sizeof (exp[0]), NULL); mail_send_message (admin_address, text); @@ -131,3 +144,91 @@ mail_finish () if (mailer_opened) mu_mailer_close (mailer); } + +void +notify_owner (struct file_triplet *trp, enum notification_event ev) +{ + int rc; + struct access_method *method = trp->dpair->project_owner_method; + char *text; + mu_address_t rcpt = NULL; + unsigned nrows, ncols, i; + struct kw_expansion kwexp[4]; + + if (method->type == method_none) + { + logmsg (LOG_NOTICE, + "NOT notifying project admins: project-owner not configured in " + "[%s, %s]", trp->dpair->source_dir, trp->dpair->dest_dir); + return; + } + + if (method_init (method)) + { + logmsg (LOG_ERR, + "failed to initialize project-owner method: " + "NOT notifying project admins"); + return; + } + + make_default_kwexp (kwexp, NULL, trp->project); + escape_kwexp (method, kwexp, NITEMS (kwexp)); + text = expand_param (method->param[1], kwexp, NITEMS (kwexp), NULL); + + rc = method_run (method, text); + free (text); + if (rc) + { + logmsg (LOG_ERR, "cannot obtain owner emails for %s", + trp->project); + return; + } + + nrows = method_num_rows (method); + ncols = method_num_cols (method); + + for (i = 0; i < nrows; i++) + { + mu_address_t addr; + const char *str = method_result (method, i, 0); + if (mu_address_create (&addr, str)) + continue; + if (ncols > 0) + { + str = method_result (method, i, 1); + if (str) + mu_address_set_personal (addr, 1, str); + } + mu_address_union (&rcpt, addr); + mu_address_destroy (&addr); + } + + if (debug_level) + { + size_t size; + char *buf; + mu_address_to_string (rcpt, NULL, 0, &size); + buf = xmalloc (size + 1); + mu_address_to_string (rcpt, buf, size + 1, NULL); + logmsg (LOG_DEBUG, "Notifying admins of %s: %s", trp->project, + buf); + free (buf); + } + + if (!dry_run_mode) + { + text = triplet_expand_param (user_message_template[ev], trp); + mail_send_message (rcpt, text); + free (text); + } + + mu_address_destroy (&rcpt); +} + +void +notify (struct file_triplet *trp, enum notification_event ev) +{ + if (owner_notification_flags & STAT_MASK (ev)) + notify_owner (trp, ev); + /* FIXME */ +} @@ -21,7 +21,10 @@ extern mu_address_t admin_address; extern mu_address_t from_address; extern unsigned long mail_admin_mask; extern char *admin_stat_message_template; +extern unsigned long owner_notification_flags; +extern char *user_message_template[MAX_EVENT]; void mail_init (void); void mail_finish (void); void mail_stats (void); + diff --git a/src/method.c b/src/method.c index 114328f..d0fa2cb 100644 --- a/src/method.c +++ b/src/method.c @@ -21,15 +21,19 @@ struct method_descr { const char *name; int (*init) (struct access_method *); - int (*done) (struct access_method *); + int (*done) (struct access_method *); + int (*free) (struct access_method *); + int (*get) (struct access_method *method, unsigned nrow, unsigned ncol); + int (*run) (struct access_method *, const char *); }; static struct method_descr method_tab[] = { - { "none", NULL, NULL, NULL }, - { "sql", sql_init_method, sql_done_method, sql_run_method }, - { "builtin", NULL, NULL, NULL }, - { "external", NULL, NULL, NULL } + { "none", NULL, NULL, NULL, NULL, NULL }, + { "sql", sql_init_method, sql_done_method, sql_free_result, + sql_get_method, sql_run_method }, + { "builtin", NULL, NULL, NULL, NULL, NULL }, + { "external", NULL, NULL, NULL, NULL, NULL } }; struct access_method * @@ -101,13 +105,31 @@ method_run (struct access_method *method, const char *cmd) mp->name, method->param[0]); return 1; } - + if (mp->free) + mp->free (method); return mp->run (method, cmd); } +unsigned +method_num_rows (struct access_method *method) +{ + return method->nrow; +} + +unsigned +method_num_cols (struct access_method *method) +{ + return method->ncol; +} + const char * -method_result (struct access_method *method) +method_result (struct access_method *method, unsigned nrow, unsigned ncol) { + struct method_descr *mp = method_tab + method->type; + + if (nrow >= method->nrow || ncol >= method->ncol + || mp->get (method, nrow, ncol)) + return NULL; return method->result; } diff --git a/src/process.c b/src/process.c index ad911a7..a5c6f05 100644 --- a/src/process.c +++ b/src/process.c @@ -152,8 +152,7 @@ scan_directory_pair (struct directory_pair *dpair) continue; } - finfo.mtime = st.st_mtime; - finfo.uid = st.st_uid; + finfo.sb = st; parse_file_name (ent->d_name, &finfo); if (debug_level) @@ -180,6 +179,8 @@ close_methods (struct directory_pair *dpair) { method_done (dpair->verify_method); method_done (dpair->gpg_key_method); + method_done (dpair->project_owner_method); + method_done (dpair->user_data_method); } /* Scan all configured update directories */ @@ -83,6 +83,18 @@ sql_init_method (struct access_method *method) return 0; } +int +sql_free_result (struct access_method *method) +{ + struct sqlconn *conn = method->v.sqlconn; + if (conn->result) + { + mysql_free_result (conn->result); + conn->result = NULL; + } + return 0; +} + /* Finish the initialized MySQL access method */ int sql_done_method (struct access_method *method) @@ -92,7 +104,8 @@ sql_done_method (struct access_method *method) return 0; if (--conn->initcount) return 0; - mysql_close (&method->v.sqlconn->mysql); + sql_free_result (method); + mysql_close (&conn->mysql); return 0; } @@ -100,11 +113,8 @@ sql_done_method (struct access_method *method) int sql_run_method (struct access_method *method, const char *query) { - MYSQL *mysql = &method->v.sqlconn->mysql; - MYSQL_RES *result; - MYSQL_ROW row; - long n; - size_t len; + struct sqlconn *conn = method->v.sqlconn; + MYSQL *mysql = &conn->mysql; if (mysql_query (mysql, query)) { @@ -113,42 +123,43 @@ sql_run_method (struct access_method *method, const char *query) return 1; } - result = mysql_store_result (mysql); - if (!result) + conn->result = mysql_store_result (mysql); + if (!conn->result) { - logmsg (LOG_ERR, "Query returned 0 tuples"); + logmsg (LOG_ERR, "Cannot get result: %s", mysql_error (mysql)); logmsg (LOG_NOTICE, "The failed query was: %s", query); return 1; } - - n = mysql_num_rows (result); - if (n != 1) - { - logmsg (LOG_NOTICE, "Query returned %ld tuples", n); - logmsg (LOG_NOTICE, "The query was: %s", query); - if (n == 0) - return 1; - } - n = mysql_num_fields (result); - if (n != 1) + method->nrow = mysql_num_rows (conn->result); + method->ncol = mysql_num_fields (conn->result); + if (debug_level > 1) { - logmsg (LOG_NOTICE, "Query returned %ld fields", n); - logmsg (LOG_NOTICE, "The query was: %s", query); + logmsg (LOG_DEBUG, "Query returned %u columns in %u rows", + method->ncol, method->nrow); + logmsg (LOG_DEBUG, "The query was: %s", query); } - row = mysql_fetch_row (result); - if (row[0] == NULL) + return 0; +} + +int +sql_get_method (struct access_method *method, unsigned nrow, unsigned ncol) +{ + struct sqlconn *conn = method->v.sqlconn; + MYSQL_ROW row; + size_t len; + + if (!conn->result) + return 1; + mysql_data_seek (conn->result, nrow); + row = mysql_fetch_row (conn->result); + if (row[ncol] == NULL) len = 0; else - len = trim_length (row[0]); + len = trim_length (row[ncol]); - method_copy_result (method, row[0], len); - - mysql_free_result (result); - + method_copy_result (method, row[ncol], len); return 0; } - - @@ -27,6 +27,7 @@ struct sqlconn char *password; size_t initcount; /* Number of initializations */ MYSQL mysql; + MYSQL_RES *result; }; void sql_register_conn (struct sqlconn *); @@ -36,3 +37,5 @@ struct sqlconn *sql_find_connection (const char *ident); int sql_init_method (struct access_method *method); int sql_done_method (struct access_method *method); int sql_run_method (struct access_method *method, const char *cmd); +int sql_get_method (struct access_method *method, unsigned nrow, unsigned ncol); +int sql_free_result (struct access_method *method); diff --git a/src/triplet.c b/src/triplet.c index daf3265..46c6011 100644 --- a/src/triplet.c +++ b/src/triplet.c @@ -51,7 +51,14 @@ hash_triplet_free (void *data) free (tp->directive); free (tp->blurb); free (tp->tmp); - + + if (tp->user_data) + { + for (i = 0; i < NITEMS (tp->user_data); i++) + free (tp->user_data[i]); + free (tp->user_data); + } + free (tp); } @@ -96,7 +103,7 @@ triplet_expired_p (struct file_triplet *trp, time_t ttl) for (i = 0; i < FILE_TYPE_COUNT; i++) { if (trp->file[i].name - && (now - trp->file[i].mtime) > ttl) + && (now - trp->file[i].sb.st_mtime) > ttl) { if (debug_level) logmsg (LOG_DEBUG, "File %s expired", trp->file[i].name); @@ -131,8 +138,10 @@ check_triplet_state (struct file_triplet *trp) else if (trp->file[file_dist].name && trp->file[file_signature].name) { - if (trp->file[file_dist].uid == trp->file[file_signature].uid - && trp->file[file_dist].uid == trp->file[file_directive].uid) + if (trp->file[file_dist].sb.st_uid == + trp->file[file_signature].sb.st_uid + && trp->file[file_dist].sb.st_uid == + trp->file[file_directive].sb.st_uid) return triplet_complete; else { @@ -172,6 +181,8 @@ triplet_processor (void *data, void *proc_data) struct file_triplet *trp = data; struct directory_pair *dpair = proc_data; + trp->dpair = dpair; + if (debug_level) logmsg (LOG_DEBUG, "FILE %s, DIST=%s, SIG=%s, DIRECTIVE=%s", trp->name, @@ -234,3 +245,308 @@ count_collected_triplets () return triplet_table ? hash_get_n_entries (triplet_table) : 0; } + +static char * +expand_project_base (struct kw_expansion *exp, void *data) +{ + struct file_triplet *trp = data; + return trp->project; +} + +static char * +expand_url (struct kw_expansion *exp, void *data) +{ + struct file_triplet *trp = data; + return trp->dpair->url; +} + +static char * +expand_relative_dir (struct kw_expansion *exp, void *data) +{ + struct file_triplet *trp = data; + directive_get_value (trp, "directory", &exp->value); + exp->static_p = 1; + return exp->value; +} + +static char * +expand_dest_dir (struct kw_expansion *exp, void *data) +{ + struct file_triplet *trp = data; + return trp->dpair->dest_dir; +} + +static char * +expand_source_dir (struct kw_expansion *exp, void *data) +{ + struct file_triplet *trp = data; + return trp->dpair->source_dir; +} + +static void +decode_file_mode (mode_t mode, char *string) +{ + *string++ = mode & S_IRUSR ? 'r' : '-'; + *string++ = mode & S_IWUSR ? 'w' : '-'; + *string++ = (mode & S_ISUID + ? (mode & S_IXUSR ? 's' : 'S') + : (mode & S_IXUSR ? 'x' : '-')); + *string++ = mode & S_IRGRP ? 'r' : '-'; + *string++ = mode & S_IWGRP ? 'w' : '-'; + *string++ = (mode & S_ISGID + ? (mode & S_IXGRP ? 's' : 'S') + : (mode & S_IXGRP ? 'x' : '-')); + *string++ = mode & S_IROTH ? 'r' : '-'; + *string++ = mode & S_IWOTH ? 'w' : '-'; + *string++ = (mode & S_ISVTX + ? (mode & S_IXOTH ? 't' : 'T') + : (mode & S_IXOTH ? 'x' : '-')); + *string = '\0'; +} + +/* Width of "user/group size", with initial value chosen + heuristically. This grows as needed, though this may cause some + stairstepping in the output. Make it too small and the output will + almost always look ragged. Make it too large and the output will + be spaced out too far. */ +static int ugswidth = 19; + +static int +format_file_data (struct file_triplet *trp, enum file_type type, char **pret) +{ + char modes[11]; + struct file_info *info = trp->file + type; + char timebuf[sizeof "YYYY-MM-DD HH:MM:SS +0000"]; + struct passwd *pw; + struct group *grp; + char sbuf[INT_BUFSIZE_BOUND (uintmax_t)]; + char *sptr; + size_t slen; + int pad; + char *user_name; + char *group_name; + struct tm *tm; + char *buf; + + if (!info->name) + return 1; + + /* MODE OWNER GROUP SIZE MTIME FILE_NAME MD5SUM? */ + + modes[0] = '-'; /* Only regular files are allowed */ + decode_file_mode (info->sb.st_mode, modes + 1); + + /* File time */ + tm = localtime (&info->sb.st_mtime); + strftime (timebuf, sizeof timebuf, "%Y-%m-%d %H:%M:%S %z", tm); + + /* FIXME: owner and group name should be store in TRP after verification */ + pw = getpwuid (TRIPLET_UID (trp)); + if (!pw) + user_name = "UNKNOWN"; /* should not happen */ + else + user_name = pw->pw_name; + + grp = getgrgid (TRIPLET_GID (trp)); + if (!grp) + group_name = "UNKNOWN"; /* should not happen */ + else + group_name = grp->gr_name; + + /* Size */ + sptr = umaxtostr (info->sb.st_size, sbuf); + + /* Figure out padding and format the buffer */ + slen = strlen (sptr); + pad = strlen (user_name) + 1 + strlen (group_name) + 1 + slen; + if (pad > ugswidth) + ugswidth = pad; + + asprintf (&buf, + "%s %s %s %*s %s %s", + modes, user_name, group_name, ugswidth - pad + slen, sptr, + timebuf, info->name); + *pret = buf; + return 0; +} + +static char * +expand_triplet_full (struct kw_expansion *exp, void *data) +{ + struct file_triplet *trp = data; + char *buf[FILE_TYPE_COUNT] = { NULL, NULL, NULL }; + + format_file_data (trp, file_dist, &buf[file_dist]); + format_file_data (trp, file_signature, &buf[file_signature]); + format_file_data (trp, file_directive, &buf[file_directive]); + + exp->value = xmalloc (strlen (buf[file_dist]) + 1 + + strlen (buf[file_signature]) + 1 + + strlen (buf[file_directive]) + 1); + sprintf (exp->value, "%s\n%s\n%s", buf[file_dist], buf[file_signature], + buf[file_directive]); + + free (buf[file_dist]); + free (buf[file_signature]); + free (buf[file_directive]); + + exp->static_p = 0; + return exp->value; +} + +static char * +expand_triplet_upload (struct kw_expansion *exp, void *data) +{ + struct file_triplet *trp = data; + char *buf[2] = { NULL, NULL }; + + format_file_data (trp, file_dist, &buf[file_dist]); + format_file_data (trp, file_signature, &buf[file_signature]); + + exp->value = xmalloc (strlen (buf[file_dist]) + 1 + + strlen (buf[file_signature]) + 1); + sprintf (exp->value, "%s\n%s", buf[file_dist], buf[file_signature]); + + free (buf[file_dist]); + free (buf[file_signature]); + + exp->static_p = 0; + return exp->value; +} + +static char * +expand_triplet_dist (struct kw_expansion *exp, void *data) +{ + struct file_triplet *trp = data; + format_file_data (trp, file_dist, &exp->value); + exp->static_p = 0; + return exp->value; +} + +static char * +expand_triplet_sig (struct kw_expansion *exp, void *data) +{ + struct file_triplet *trp = data; + format_file_data (trp, file_signature, &exp->value); + exp->static_p = 0; + return exp->value; +} + +static char * +expand_triplet_directive (struct kw_expansion *exp, void *data) +{ + struct file_triplet *trp = data; + format_file_data (trp, file_directive, &exp->value); + exp->static_p = 0; + return exp->value; +} + +static char * +expand_user_name (struct kw_expansion *exp, void *data) +{ + struct passwd *pw; + struct file_triplet *trp = data; + + /* FIXME: should user name be stored in the triplet? */ + pw = getpwuid (TRIPLET_UID (trp)); + if (!pw) + return NULL; + exp->value = xstrdup (pw->pw_name); + exp->static_p = 0; + return exp->value; +} + +static void +fill_user_data (struct file_triplet *trp) +{ + int rc; + struct access_method *method = trp->dpair->user_data_method; + char *text; + unsigned nrows, ncols; + struct kw_expansion kwexp[4]; + struct passwd *pw; + + if (trp->user_data) + return; + + if (method->type == method_none) + return; + + if (method_init (method)) + return; + + pw = getpwuid (TRIPLET_UID (trp)); + if (!pw) + return; + make_default_kwexp (kwexp, pw->pw_name, trp->project); + escape_kwexp (method, kwexp, NITEMS (kwexp)); + text = expand_param (method->param[1], kwexp, NITEMS (kwexp), NULL); + + rc = method_run (method, text); + free (text); + if (rc) + return; + + nrows = method_num_rows (method); + ncols = method_num_cols (method); + + if (nrows > 0) + { + int i; + trp->user_data = xcalloc (ncols, sizeof (trp->user_data[0])); + for (i = 0; i < ncols; i++) + { + const char *str = method_result (method, 0, i); + if (str) + trp->user_data[i] = xstrdup (str); + } + } +} + +static char * +expand_user_real_name (struct kw_expansion *exp, void *data) +{ + struct file_triplet *trp = data; + fill_user_data (trp); + if (trp->user_data[0]) + return trp->user_data[0]; + exp->value = "UNKNOWN"; + exp->static_p = 1; + return exp->value; +} + +static char * +expand_user_email (struct kw_expansion *exp, void *data) +{ + struct file_triplet *trp = data; + fill_user_data (trp); + if (trp->user_data[1]) + return trp->user_data[1]; + exp->value = "UNKNOWN"; + exp->static_p = 1; + return exp->value; +} + +struct kw_expansion triplet_exp[] = { + { "project", NULL, 0, expand_project_base, NULL }, + { "url", NULL, 0, expand_url, NULL }, + { "dir", NULL, 0, expand_relative_dir, NULL }, + { "dest-dir", NULL, 0, expand_dest_dir, NULL }, + { "source-dir", NULL, 0, expand_source_dir, NULL }, + { "triplet:full", NULL, 0, expand_triplet_full, NULL }, + { "triplet:upload", NULL, 0, expand_triplet_upload, NULL }, + { "triplet:dist", NULL, 0, expand_triplet_dist, NULL }, + { "triplet:sig", NULL, 0, expand_triplet_sig, NULL }, + { "triplet:dir", NULL, 0, expand_triplet_directive, NULL }, + { "user", NULL, 0, expand_user_name, NULL }, + { "user:name", NULL, 0, expand_user_name, NULL }, + { "user:real-name", NULL, 0, expand_user_real_name, NULL }, + { "user:email", NULL, 0, expand_user_email, NULL }, +}; + +char * +triplet_expand_param (const char *tmpl, struct file_triplet *trp) +{ + free_kwexp (triplet_exp, NITEMS (triplet_exp)); + return expand_param (tmpl, triplet_exp, NITEMS (triplet_exp), trp); +} diff --git a/src/verify.c b/src/verify.c index 2ccdfaa..70678f3 100644 --- a/src/verify.c +++ b/src/verify.c @@ -39,21 +39,35 @@ trim (char *str) } static const char * -find_expansion_char (int c, const struct kw_expansion *exp, size_t nexp) +expansion_value (struct kw_expansion *exp, void *data) +{ + if (exp->value) + return exp->value; + if (exp->expand) + return exp->expand (exp, data); + exp->value = "INTERNAL ERROR: NONEXPANDABLE DATA"; + exp->static_p = 1; |