aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorSergey Poznyakoff <gray@gnu.org.ua>2007-08-26 13:03:32 +0000
committerSergey Poznyakoff <gray@gnu.org.ua>2007-08-26 13:03:32 +0000
commitb89ece30bebb5b9dd06e4e061537b6bf068566f2 (patch)
treec7f65b64fccfac2f0e7e134e5f7666a4fb1907d1 /src
parent338c0d9c35c56f35c636df1c1da0852a466e731f (diff)
downloadwydawca-b89ece30bebb5b9dd06e4e061537b6bf068566f2.tar.gz
wydawca-b89ece30bebb5b9dd06e4e061537b6bf068566f2.tar.bz2
Implement project owner notifications.
* wydawca/wydawca.c (syslog_printer): Reduce the number of memory reallocations (make_stat_expansion): Update * wydawca/method.c: Implement a new framework: methods may return 2-dimensional arrays of strings. * wydawca/sql.c, wydawca/sql.h: Implement the new method framework. * wydawca/verify.c (expand_param): kw_expansion may provide expansion functions. An additional argument supplies user-defined data for expansions. (escape_kwexp): Extern (free_kwexp): Improve (get_project_name): New function (make_default_kwexp): New function (check_access_rights): Call get_project_name. Use make_default_kwexp to initialize expansions (verify_directive_file): Use make_default_kwexp to initialize expansions * wydawca/wydawca.h (NITEMS): New macro (enum access_method_type): New members: ncol, nrow (struct directory_pair): New members url,project_owner_method, user_data_method (struct file_info): Replace mtime, uid with struct stat sb (struct file_triplet): New members project, dpair, user_data (TRIPLET_UID): Take into account the changes to struct file_info (enum notification_event): New data type (notify_project_owner, notify_admin, notify): New functions (struct kw_expansion): New members static_p, expand and data. (escape_kwexp,make_default_kwexp): New proto (expand_param): Change signature (triplet_expand_param): New function (method_result): Change prototype (method_num_rows,method_num_cols): New functions * wydawca/config.c: New statements project-owner, user-data, admin-address, mail-user, user-message directory can take an optional argument specifying base URL for notification messages * wydawca/gpg.c (verify_directive_signature): Expand directives even if the signature does not match. Useful for notifications. Add notifications. * wydawca/process.c: Add notifications. * wydawca/directive.c: Add notifications * wydawca/wydawca.rc: Update * wydawca/mail.c, wydawca/mail.h: Implement project owner notifications * wydawca/triplet.c (triplet_expand_param): New function * lib/cfg.c (read_cont_line): Fix counting of input lines. git-svn-id: file:///svnroot/wydawca/trunk@297 6bb4bd81-ecc2-4fd4-a2d4-9571d19c0d33
Diffstat (limited to 'src')
-rw-r--r--src/config.c162
-rw-r--r--src/directive.c1
-rw-r--r--src/gpg.c26
-rw-r--r--src/mail.c103
-rw-r--r--src/mail.h3
-rw-r--r--src/method.c36
-rw-r--r--src/process.c5
-rw-r--r--src/sql.c73
-rw-r--r--src/sql.h3
-rw-r--r--src/triplet.c324
-rw-r--r--src/verify.c145
-rw-r--r--src/wydawca.c34
-rw-r--r--src/wydawca.h55
-rw-r--r--src/wydawca.rc80
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;
}
diff --git a/src/gpg.c b/src/gpg.c
index 3372c7b..eb2a64a 100644
--- a/src/gpg.c
+++ b/src/gpg.c
@@ -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:
diff --git a/src/mail.c b/src/mail.c
index 0f91ebd..81bf085 100644
--- a/src/mail.c
+++ b/src/mail.c
@@ -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 */
+}
diff --git a/src/mail.h b/src/mail.h
index 7faedfb..02c95b7 100644
--- a/src/mail.h
+++ b/src/mail.h
@@ -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 */
diff --git a/src/sql.c b/src/sql.c
index c1de81d..ff358a5 100644
--- a/src/sql.c
+++ b/src/sql.c
@@ -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;
}
-
-
diff --git a/src/sql.h b/src/sql.h
index 6b2e6c4..2af0b97 100644
--- a/src/sql.h
+++ b/src/sql.h
@@ -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++)