aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorSergey Poznyakoff <gray@gnu.org.ua>2007-08-19 13:30:35 +0000
committerSergey Poznyakoff <gray@gnu.org.ua>2007-08-19 13:30:35 +0000
commit32d5cff1b7c2266779b63fb97eb6b91cab34e7ad (patch)
treea6334d3ceff71716e9e1399fb376963782a5404b
parent106472e9039e46a5468ed15f118a2cf7d74f6ec8 (diff)
downloadwydawca-32d5cff1b7c2266779b63fb97eb6b91cab34e7ad.tar.gz
wydawca-32d5cff1b7c2266779b63fb97eb6b91cab34e7ad.tar.bz2
New module: wydawca
git-svn-id: file:///svnroot/wydawca/trunk@279 6bb4bd81-ecc2-4fd4-a2d4-9571d19c0d33
-rw-r--r--src/Makefile.am16
-rw-r--r--src/config.c357
-rw-r--r--src/gpg.c188
-rw-r--r--src/method.c115
-rw-r--r--src/process.c179
-rw-r--r--src/register.c152
-rw-r--r--src/sql.c150
-rw-r--r--src/sql.h38
-rw-r--r--src/triplet.c156
-rw-r--r--src/verify.c188
-rw-r--r--src/wydawca.c153
-rw-r--r--src/wydawca.h123
-rw-r--r--src/wydawca.rc22
13 files changed, 1837 insertions, 0 deletions
diff --git a/src/Makefile.am b/src/Makefile.am
new file mode 100644
index 0000000..7d8c00e
--- /dev/null
+++ b/src/Makefile.am
@@ -0,0 +1,16 @@
+sbin_PROGRAMS=@MODULE_WYDAWCA@
+EXTRA_PROGRAMS=wydawca
+wydawca_SOURCES=\
+ config.c\
+ gpg.c\
+ method.c\
+ process.c\
+ sql.c\
+ sql.h\
+ triplet.c\
+ verify.c\
+ wydawca.c\
+ wydawca.h
+LDADD=../lib/libgsc.a ../gnu/libgnu.a @SQLLIB@ @GPGMELIB@
+INCLUDES = -I../lib -I../gnu
+AM_CPPFLAGS=-DSYSCONFDIR=\"$(sysconfdir)\" \ No newline at end of file
diff --git a/src/config.c b/src/config.c
new file mode 100644
index 0000000..31ff99c
--- /dev/null
+++ b/src/config.c
@@ -0,0 +1,357 @@
+/* wydawca - FTP release synchronisation daemon
+ Copyright (C) 2007 Sergey Poznyakoff
+
+ This program 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.
+
+ This program 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 this program. If not, see <http://www.gnu.org/licenses/>. */
+
+#include "wydawca.h"
+#include "sql.h"
+
+/* Configuration file handling (application-specific) */
+
+static struct access_method default_verify_method;
+static struct access_method default_gpg_key_method;
+
+static void
+cfg_syslog_tag (gsc_config_file_t *file, char *kw, char *val, void *unused)
+{
+ syslog_tag = strdup (val);
+}
+
+static void
+cfg_syslog_facility (gsc_config_file_t *file, char *kw, char *val,
+ void *unused)
+{
+ if (gsc_str_to_syslog_facility (val, &log_facility))
+ file->error_msg (file->file_name, file->line, "Unknown facility `%s'",
+ val);
+}
+
+static void
+cfg_source (gsc_config_file_t *file, char *kw, char *val, void *data)
+{
+ struct directory_pair *dp = data;
+ dp->source_dir = xstrdup (val);
+}
+
+static void
+cfg_destination (gsc_config_file_t *file, char *kw, char *val, void *data)
+{
+ struct directory_pair *dp = data;
+ dp->dest_dir = xstrdup (val);
+}
+
+static void
+cfg_file_sweep_time (gsc_config_file_t *file, char *kw, char *val, void *data)
+{
+ struct directory_pair *dp = data;
+ time_t interval;
+ const char *endp;
+
+ if (parse_time_interval (val, &interval, &endp))
+ file->error_msg (file->file_name, file->line,
+ "unrecognized time format (near `%s')",
+ endp);
+ if (data)
+ dp->file_sweep_time = interval;
+ else
+ file_sweep_time = interval;
+}
+
+static char *
+get_word (char **pstr)
+{
+ char *word;
+ char *str = *pstr;
+
+ while (*str && isspace (*str))
+ str++;
+
+ word = str;
+ if (*word)
+ {
+ while (*str && !isspace (*str))
+ str++;
+
+ if (*str)
+ {
+ *str++ = 0;
+ while (*str && isspace (*str))
+ str++;
+ }
+ }
+ *pstr = str;
+ return word;
+}
+
+static enum access_method_type
+str_to_access_method (const char *str)
+{
+ if (strcmp (str, "sql") == 0)
+ return method_sql;
+ else if (strcmp (str, "builtin") == 0)
+ return method_builtin;
+ else if (strcmp (str, "external") == 0)
+ return method_external;
+ else
+ return method_none;
+}
+
+static void
+_cfg_access_method (gsc_config_file_t *file, char *kw, char *val, void *data)
+{
+ struct access_method *ap = data;
+ char *word = get_word (&val);
+
+ if (*word == 0)
+ {
+ file->error_msg (file->file_name, file->line,
+ "access method not specified");
+ return;
+ }
+ ap->type = str_to_access_method (word);
+ switch (ap->type)
+ {
+ case method_none:
+ file->error_msg (file->file_name, file->line,
+ "unknown access method: %s", word);
+ return;
+
+ case method_sql:
+ word = get_word (&val);
+ if (!sql_connection_exists_p (word))
+ {
+ file->error_msg (file->file_name, file->line,
+ "SQL connection `%s' not declared", word);
+ ap->type = method_none;
+ return;
+ }
+ ap->param[0] = xstrdup (word);
+ ap->param[1] = xstrdup (val);
+ break;
+
+ case method_builtin:
+ case method_external:
+ ap->param[0] = xstrdup (val);
+ break;
+ }
+}
+
+static void
+cfg_verify_user (gsc_config_file_t *file, char *kw, char *val, void *data)
+{
+ struct directory_pair *dp = data;
+ _cfg_access_method (file, kw, val,
+ dp ? &dp->verify_method : &default_verify_method);
+}
+
+static void
+cfg_gpg_key (gsc_config_file_t *file, char *kw, char *val, void *data)
+{
+ struct directory_pair *dp = data;
+ _cfg_access_method (file, kw, val,
+ dp ? &dp->gpg_key_method : &default_gpg_key_method);
+}
+
+static void
+cfg_directory (gsc_config_file_t *file, char *kw, char *val, void *unused)
+{
+ int ec;
+ struct directory_pair dpair;
+ static struct gsc_config_keyword keywords[] = {
+ { "source", cfg_source },
+ { "destination", cfg_destination },
+ { "file-sweep-time", cfg_file_sweep_time },
+ { "verify-user", cfg_verify_user },
+ { "gpg-key", cfg_gpg_key },
+ { NULL }
+ };
+
+ if (val)
+ file->error_msg (file->file_name, file->line, "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;
+ gsc_config_parse_block (file, &dpair, keywords, "end");
+
+ if (test_dir (dpair.source_dir, &ec))
+ {
+ if (ec)
+ file->error_msg (file->file_name, file->line,
+ "cannot access %s: %s",
+ dpair.source_dir, strerror (ec));
+ else
+ file->error_msg (file->file_name, file->line,
+ "%s is not a directory",
+ dpair.source_dir);
+ }
+ else if (test_dir (dpair.dest_dir, &ec))
+ {
+ if (ec)
+ file->error_msg (file->file_name, file->line,
+ "cannot access %s: %s",
+ dpair.dest_dir, strerror (ec));
+ else
+ file->error_msg (file->file_name, file->line,
+ "%s is not a directory",
+ dpair.dest_dir);
+ }
+ else if (dpair.verify_method.type != method_none
+ && dpair.gpg_key_method.type != method_none)
+ register_directory_pair (&dpair);
+}
+
+static int
+get_bool (char *str, int *pval)
+{
+ if (strcmp (str, "yes") == 0
+ || strcmp (str, "t") == 0)
+ *pval = 1;
+ else if (strcmp (str, "no") == 0
+ || strcmp (str, "nil") == 0)
+ *pval = 0;
+ else
+ {
+ char *p;
+ int n = strtoul (str, &p, 10);
+ if (*p == 0)
+ *pval = n;
+ else
+ return 1;
+ }
+ return 0;
+}
+
+static void
+cfg_unlink_invalid_files (gsc_config_file_t *file, char *kw, char *val,
+ void *unused)
+{
+ if (get_bool (val, &unlink_invalid_files))
+ file->error_msg (file->file_name, file->line, "invalid boolean value");
+}
+
+static void
+cfg_sql_host (gsc_config_file_t *file, char *kw, char *val, void *data)
+{
+ struct sqlconn *pconn = data;
+ char *p = strchr (val, ':');
+ if (p)
+ {
+ *p++ = 0;
+ if (p[0] == '/')
+ {
+ pconn->socket = xstrdup (p);
+ pconn->host = xstrdup ("localhost");
+ }
+ else
+ {
+ char *end;
+ unsigned long n = strtoul (p, &end, 10);
+ if (*end)
+ {
+ file->error_msg (file->file_name, file->line,
+ "invalid port number (near %s)", end);
+ return;
+ }
+ if (n == 0 || n > USHRT_MAX)
+ {
+ file->error_msg (file->file_name, file->line,
+ "port number out of range 1..%d", USHRT_MAX);
+ return;
+ }
+ pconn->port = n;
+ /* Save host name */
+ pconn->host = xstrdup (val);
+ }
+ }
+ else
+ pconn->host = xstrdup (val);
+}
+
+static void
+cfg_sql_database (gsc_config_file_t *file, char *kw, char *val, void *data)
+{
+ struct sqlconn *pconn = data;
+ pconn->database = xstrdup (val);
+}
+
+static void
+cfg_sql_user (gsc_config_file_t *file, char *kw, char *val, void *data)
+{
+ struct sqlconn *pconn = data;
+ pconn->user = xstrdup (val);
+}
+
+static void
+cfg_sql_password (gsc_config_file_t *file, char *kw, char *val, void *data)
+{
+ struct sqlconn *pconn = data;
+ pconn->password = xstrdup (val);
+}
+
+static void
+cfg_sql (gsc_config_file_t *file, char *kw, char *val, void *unused)
+{
+ struct sqlconn sql;
+ static struct gsc_config_keyword keywords[] = {
+ { "host", cfg_sql_host },
+ { "database", cfg_sql_database },
+ { "user", cfg_sql_user },
+ { "password", cfg_sql_password },
+ { NULL }
+ };
+
+ memset (&sql, 0, sizeof sql);
+ if (!val)
+ file->error_msg (file->file_name, file->line, "missing block identifier");
+ else
+ sql.ident = xstrdup (val);
+
+ gsc_config_parse_block (file, &sql, keywords, "end");
+ if (sql.ident)
+ sql_register_conn (&sql);
+}
+
+static struct gsc_config_keyword kw_handler[] = {
+ { "syslog-tag", cfg_syslog_tag },
+ { "syslog-facility", cfg_syslog_facility },
+ { "directory", cfg_directory },
+ { "unlink-invalid-files", cfg_unlink_invalid_files },
+ { "file-sweep-time", cfg_file_sweep_time },
+ { "sql", cfg_sql },
+ { "verify-user", cfg_verify_user },
+ { "gpg-key", cfg_gpg_key },
+ { NULL }
+};
+
+void
+parse_config ()
+{
+ default_verify_method.type = method_builtin;
+ default_gpg_key_method.type = method_builtin;
+
+ switch (gsc_config_parse (conffile, NULL, kw_handler))
+ {
+ case gsc_config_success:
+ break;
+
+ case gsc_config_open:
+ error (1, errno, "cannot open config file `%s'", conffile);
+
+ case gsc_config_error:
+ exit (1);
+ }
+}
+
diff --git a/src/gpg.c b/src/gpg.c
new file mode 100644
index 0000000..ae81989
--- /dev/null
+++ b/src/gpg.c
@@ -0,0 +1,188 @@
+/* wydawca - FTP release synchronisation daemon
+ Copyright (C) 2007 Sergey Poznyakoff
+
+ This program 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.
+
+ This program 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 this program. If not, see <http://www.gnu.org/licenses/>. */
+
+#include "wydawca.h"
+#include "save-cwd.h"
+#include <gpgme.h>
+
+#define fail_if_err(expr) \
+ do \
+ { \
+ int a = expr; \
+ if (a) \
+ { \
+ logmsg (LOG_ERR, "%s: GPGME error: %s", #expr, gpgme_strerror (a)); \
+ return 1; \
+ } \
+ } \
+ while (0)
+
+static char *homedir;
+
+static int rmdir_r (const char *name);
+
+static int
+recursive_rmdir (const char *name)
+{
+ int rc;
+ DIR *dir;
+ struct dirent *ent;
+
+ if (chdir (name))
+ {
+ logmsg (LOG_ERR, "cannot change to directory %s: %s",
+ name, strerror (errno));
+ return 1;
+ }
+
+ dir = opendir (".");
+ if (!dir)
+ {
+ logmsg (LOG_ERR, "cannot open directory %s: %s",
+ name, strerror (errno));
+ return 1;
+ }
+
+ for (rc = 0; rc == 0 && (ent = readdir (dir));)
+ {
+ struct stat st;
+
+ if (strcmp (ent->d_name, ".") == 0
+ || strcmp (ent->d_name, "..") == 0)
+ continue;
+
+ if (stat (ent->d_name, &st) && errno != ENOENT)
+ {
+ logmsg (LOG_ERR, "cannot stat file `%s': %s",
+ name, strerror (errno));
+ rc = 1;
+ }
+ else if (S_ISDIR (st.st_mode))
+ rc = rmdir_r (ent->d_name);
+ else if ((rc = unlink (ent->d_name)) != 0 && errno != ENOENT)
+ logmsg (LOG_ERR, "cannot unlink %s: %s",
+ ent->d_name, strerror (errno));
+ }
+ closedir (dir);
+ return rc;
+}
+
+static int
+rmdir_r (const char *name)
+{
+ int rc;
+ struct saved_cwd cwd;
+
+ if (save_cwd (&cwd))
+ {
+ logmsg (LOG_ERR, "cannot save current directory: %s",
+ strerror (errno));
+ return 1;
+ }
+ rc = recursive_rmdir (name);
+ if (restore_cwd (&cwd))
+ {
+ logmsg (LOG_ERR, "cannot restore current directory: %s",
+ strerror (errno));
+ rc = 1;
+ }
+
+ if (rc == 0 && rmdir (name))
+ {
+ logmsg (LOG_ERR, "cannot remove directory %s: %s",
+ name, strerror (errno));
+ return 1;
+ }
+
+ return rc;
+}
+
+static void
+remove_homedir ()
+{
+ if (debug_level > 1)
+ logmsg (LOG_DEBUG, "Removing GNUPG home directory: %s", homedir);
+ if (rmdir_r (homedir))
+ logmsg (LOG_CRIT, "Failed to remove GPG directory %s", homedir);
+}
+
+int
+wydawca_gpg_homedir ()
+{
+ if (homedir)
+ return;
+
+ homedir = xstrdup ("/tmp/wydawca-XXXXXX");
+ if (!mkdtemp (homedir))
+ {
+ logmsg (LOG_CRIT, "cannot create GPG home directory (%s): %s",
+ homedir, strerror (errno));
+ return 1;
+ }
+ atexit (remove_homedir);
+ if (debug_level > 1)
+ logmsg (LOG_DEBUG, "GNUPG home directory: %s", homedir);
+ setenv ("GNUPGHOME", homedir, 1);
+}
+
+int
+verify_signature (struct file_register *reg, struct directory_pair *dpair,
+ const char *pubkey)
+{
+ gpgme_ctx_t ctx;
+ gpgme_data_t key_data, directive_data, plain;
+ off_t size;
+ size_t dcount, i;
+ char *p;
+
+ wydawca_gpg_homedir ();
+ fail_if_err (gpgme_new (&ctx));
+ fail_if_err (gpgme_data_new_from_mem (&key_data, pubkey, strlen (pubkey), 0));
+ fail_if_err (gpgme_op_import (ctx, key_data));
+
+ fail_if_err (gpgme_data_new_from_file (&directive_data,
+ reg->file[file_directive].name, 1));
+ gpgme_data_new (&plain);
+ fail_if_err (gpgme_op_verify (ctx, directive_data, NULL, plain));
+
+ gpgme_data_release (directive_data);
+ gpgme_data_release (key_data);
+
+ size = gpgme_data_seek (plain, 0, SEEK_END);
+ gpgme_data_seek (plain, 0, SEEK_SET);
+ reg->blurb = xmalloc (size + 1);
+ gpgme_data_read (plain, reg->blurb, size);
+ reg->blurb[size] = 0;
+
+ dcount = 1;
+ for (p = reg->blurb; *p; p++)
+ if (*p == '\n')
+ dcount++;
+
+ reg->directive = xcalloc (dcount, sizeof reg->directive[0]);
+ p = reg->blurb;
+ for (i = 0; i < dcount; i++)
+ {
+ reg->directive[i] = p;
+ p = strchr (p, '\n');
+ if (!p)
+ break;
+ *p++ = 0;
+ }
+ reg->directive[i] = NULL;
+
+ return 0;
+}
diff --git a/src/method.c b/src/method.c
new file mode 100644
index 0000000..433bf6a
--- /dev/null
+++ b/src/method.c
@@ -0,0 +1,115 @@
+/* wydawca - FTP release synchronisation daemon
+ Copyright (C) 2007 Sergey Poznyakoff
+
+ This program 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.
+
+ This program 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 this program. If not, see <http://www.gnu.org/licenses/>. */
+
+#include "wydawca.h"
+#include "sql.h"
+
+struct method_descr
+{
+ const char *name;
+ int (*init) (struct access_method *);
+ int (*done) (struct access_method *);
+ 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 }
+};
+
+int
+method_init (struct access_method *method)
+{
+ struct method_descr *mp = method_tab + method->type;
+ int rc = 0;
+
+ if (debug_level > 1)
+ logmsg (LOG_DEBUG, "Initializing method: %s \"%s\" \"%s\"",
+ mp->name, method->param[0],
+ method->param[1] ? method->param[1] : "");
+ if (method->init_passed)
+ return 0;
+ if (mp->init)
+ rc = mp->init (method);
+ if (rc == 0)
+ method->init_passed = 1;
+ return rc;
+}
+
+int
+method_done (struct access_method *method)
+{
+ struct method_descr *mp = method_tab + method->type;
+ int rc = 0;
+
+ if (debug_level > 1)
+ logmsg (LOG_DEBUG, "Closing method: %s \"%s\" \"%s\"",
+ mp->name, method->param[0],
+ method->param[1] ? method->param[1] : "");
+ if (!method->init_passed)
+ return 0;
+ if (mp->done)
+ rc = mp->done (method);
+ free (method->result);
+ method->result = NULL;
+ method->result_size = 0;
+ if (rc == 0)
+ method->init_passed = 0;
+ return rc;
+}
+
+int
+method_run (struct access_method *method, const char *cmd)
+{
+ struct method_descr *mp = method_tab + method->type;
+
+ if (debug_level > 1)
+ logmsg (LOG_DEBUG, "Running method: %s \"%s\"", mp->name, cmd);
+ if (!method->init_passed)
+ {
+ logmsg (LOG_CRIT, "INTERNAL ERROR: Method %s \"%s\" not initialized",
+ mp->name, method->param[0]);
+ return 1;
+ }
+ if (!mp->run)
+ {
+ logmsg (LOG_CRIT, "INTERNAL ERROR: No run function for method %s \"%s\"",
+ mp->name, method->param[0]);
+ return 1;
+ }
+
+ return mp->run (method, cmd);
+}
+
+const char *
+method_result (struct access_method *method)
+{
+ return method->result;
+}
+
+void
+method_copy_result (struct access_method *method, const char *res, size_t size)
+{
+ if (method->result_size < size + 1)
+ {
+ method->result_size = size + 1;
+ method->result = x2realloc (method->result, &method->result_size);
+ }
+ memcpy (method->result, res, size);
+ method->result[size] = 0;
+}
diff --git a/src/process.c b/src/process.c
new file mode 100644
index 0000000..afb985d
--- /dev/null
+++ b/src/process.c
@@ -0,0 +1,179 @@
+/* wydawca - FTP release synchronisation daemon
+ Copyright (C) 2007 Sergey Poznyakoff
+
+ This program 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.
+
+ This program 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 this program. If not, see <http://www.gnu.org/licenses/>. */
+
+#include "wydawca.h"
+
+struct directory_list
+{
+ struct directory_list *next;
+ struct directory_pair pair;
+};
+
+static struct directory_list *dlist;
+
+void
+register_directory_pair (struct directory_pair *dpair)
+{
+ struct directory_list *dl = xmalloc (sizeof *dl);
+ dl->pair = *dpair;
+ dl->next = dlist;
+ dlist = dl;
+}
+
+int
+test_dir (const char *name, int *ec)
+{
+ struct stat st;
+
+ *ec = 0;
+ if (stat (name, &st))
+ {
+ *ec = errno;
+ return 1;
+ }
+ return S_ISDIR (st.st_mode) == 0;
+}
+
+const char *
+file_type_str (enum file_type type)
+{
+ switch (type)
+ {
+ case file_dist:
+ return "distributive";
+
+ case file_signature:
+ return "detached signature";
+
+ case file_directive:
+ return "signed upload directive";
+ }
+}
+
+#define SUF_SIG ".sig"
+#define SUF_DIR ".directive.asc"
+
+int
+parse_file_name (const char *name, struct file_info *finfo)
+{
+ static struct suffix
+ {
+ const char *suf;
+ unsigned len;
+ enum file_type type;
+ } suftab[] = {
+ { SUF_SIG, sizeof SUF_SIG - 1, file_signature },
+ { SUF_DIR, sizeof SUF_DIR - 1, file_directive },
+ { "", 0, file_dist }
+ };
+ int i;
+ unsigned len = strlen (name);
+
+ for (i = 0; i < sizeof suftab / sizeof suftab[0]; i++)
+ {
+ if (len >= suftab[i].len
+ && memcmp (name + len - suftab[i].len,
+ suftab[i].suf, suftab[i].len) == 0)
+ {
+ finfo->name = xstrdup (name);
+ finfo->type = suftab[i].type;
+ finfo->root_len = len - suftab[i].len;
+ return;
+ }
+ }
+ abort (); /* should not happen */
+}
+
+void
+scan_directory_pair (struct directory_pair *dpair)
+{
+ DIR *dir;
+ struct dirent *ent;
+
+ if (debug_level)
+ logmsg (LOG_DEBUG, "%s -> %s", dpair->source_dir, dpair->dest_dir);
+
+ if (chdir (dpair->source_dir))
+ {
+ logmsg (LOG_ERR, "cannot chdir to %s: %s", dpair->source_dir,
+ strerror (errno));
+ return;
+ }
+
+ dir = opendir (".");
+ if (!dir)
+ {
+ logmsg (LOG_ERR, "cannot open directory %s: %s", dpair->source_dir,
+ strerror (errno));
+ return;
+ }
+
+ while ((ent = readdir (dir)))
+ {
+ struct stat st;
+ struct file_info finfo;
+
+ if (strcmp (ent->d_name, ".") == 0 || strcmp (ent->d_name, "..") == 0)
+ continue;
+
+ if (stat (ent->d_name, &st))
+ {
+ logmsg (LOG_ERR, "cannot stat file %s/%s: %s",
+ dpair->source_dir, ent->d_name,
+ strerror (errno));
+ continue;
+ }
+
+ if (!S_ISREG (st.st_mode))
+ {
+ logmsg (LOG_NOTICE, "not a regular file: %s/%s",
+ dpair->source_dir, ent->d_name);
+ continue;
+ }
+
+ finfo.mtime = st.st_mtime;
+ finfo.uid = st.st_uid;
+ parse_file_name (ent->d_name, &finfo);
+
+ if (debug_level)
+ {
+ const char *s = file_type_str (finfo.type);
+ logmsg (LOG_DEBUG, "file %s is %s %s, root %.*s", ent->d_name,
+ strchr ("aeiou", s[0]) ? "an" : "a", s,
+ finfo.root_len, finfo.name);
+ }
+
+ register_file (&finfo);
+ }
+
+ closedir (dir);
+
+ if (method_init (&dpair->verify_method) == 0
+ && method_init (&dpair->gpg_key_method) == 0)
+ enumerate_triplets (dpair);
+
+ method_done (&dpair->verify_method);
+ method_done (&dpair->gpg_key_method);
+}
+
+void
+scan_directories ()
+{
+ struct directory_list *dp;
+ for (dp = dlist; dp; dp = dp->next)
+ scan_directory_pair (&dp->pair);
+}
+
diff --git a/src/register.c b/src/register.c
new file mode 100644
index 0000000..2529a79
--- /dev/null
+++ b/src/register.c
@@ -0,0 +1,152 @@
+/* wydawca - FTP release synchronisation daemon
+ Copyright (C) 2007 Sergey Poznyakoff
+
+ This program 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.
+
+ This program 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 this program. If not, see <http://www.gnu.org/licenses/>. */
+
+#include "wydawca.h"
+#include "hash.h"
+
+static Hash_table *file_table;
+
+static size_t
+hash_symbol_hasher (void const *data, unsigned n_buckets)
+{
+ struct file_register const *t = data;
+ return hash_string (t->name, n_buckets);
+}
+
+/* Compare two strings for equality. */
+static bool
+hash_symbol_compare (void const *data1, void const *data2)
+{
+ struct file_register const *t1 = data1;
+ struct file_register const *t2 = data2;
+ return t1->name && t2->name && strcmp (t1->name, t2->name) == 0;
+}
+
+void
+register_file (struct file_info *finfo)
+{
+ struct file_register *tp, *ret;
+
+ tp = xmalloc (sizeof(*tp) + finfo->root_len + 1);
+ memset (tp, 0, sizeof (*tp));
+ tp->name = (char*)(tp + 1);
+ memcpy (tp->name, finfo->name, finfo->root_len);
+ tp->name[finfo->root_len] = 0;
+
+ if (! ((file_table
+ || (file_table = hash_initialize (0, 0,
+ hash_symbol_hasher,
+ hash_symbol_compare, 0)))
+ && (ret = hash_insert (file_table, tp))))
+ xalloc_die ();
+
+ if (ret != tp)
+ free (tp);
+ ret->file[finfo->type] = *finfo;
+}
+
+#define SP(s) ((s) ? (s) : "NONE")
+
+static bool
+triplet_expired_p (struct file_register *reg, time_t file_sweep_time)
+{
+ int i;
+ time_t now = time (NULL);
+
+ if (file_sweep_time == 0)
+ return false;
+
+ for (i = 0; i < FILE_TYPE_COUNT; i++)
+ {
+ if (reg->file[i].name
+ && (now - reg->file[i].mtime) > file_sweep_time)
+ {
+ if (debug_level)
+ logmsg (LOG_DEBUG, "File %s expired", reg->file[i].name);
+ return true;
+ }
+ }
+ return false;
+}
+
+static bool
+valid_triplet_p (struct file_register *reg)
+{
+ if (reg->file[file_dist].name
+ && reg->file[file_signature].name
+ && reg->file[file_directive].name)
+ {
+ if (reg->file[file_dist].uid == reg->file[file_signature].uid
+ && reg->file[file_dist].uid == reg->file[file_directive].uid)
+ return true;
+ else if (debug_level)
+ logmsg (LOG_DEBUG, "%s: invalid triplet: UIDs differ", reg->name);
+ }
+ else if (debug_level)
+ logmsg (LOG_DEBUG, "%s: incomplete triplet", reg->name);
+ return false;
+}
+
+static void
+remove_triplet (struct file_register *reg)
+{
+ int i;
+
+ for (i = 0; i < FILE_TYPE_COUNT; i++)
+ {
+ if (reg->file[i].name)
+ {
+ if (unlink (reg->file[i].name))
+ logmsg (LOG_ERR, "Cannot remove %s: %s",
+ reg->file[i].name, strerror (errno));
+ else
+ logmsg (LOG_NOTICE, "Removed %s", reg->file[i].name);
+ }
+ }
+}
+
+static bool
+file_processor (void *data, void *proc_data)
+{
+ struct file_register *reg = data;
+ const struct directory_pair *dpair = proc_data;
+
+ if (debug_level)
+ logmsg (LOG_DEBUG, "FILE %s, DIST=%s, SIG=%s, DIRECTIVE=%s",
+ reg->name,
+ SP (reg->file[file_dist].name),
+ SP (reg->file[file_signature].name),
+ SP (reg->file[file_directive].name));
+
+ if (valid_triplet_p (reg))
+ {
+ if (debug_level)
+ logmsg (LOG_DEBUG, "Processing triplet `%s'", reg->name);
+ /* FIXME */
+ }
+ else if (triplet_expired_p (reg, dpair->file_sweep_time))
+ remove_triplet (reg);
+
+ return true;
+}
+
+void
+enumerate_files (const struct directory_pair *dpair)
+{
+ if (debug_level)
+ logmsg (LOG_DEBUG, "Processing files for %s", dpair->dest_dir);
+ hash_do_for_each (file_table, file_processor, (void*)dpair);
+}
diff --git a/src/sql.c b/src/sql.c
new file mode 100644
index 0000000..2ceb724
--- /dev/null
+++ b/src/sql.c
@@ -0,0 +1,150 @@
+/* wydawca - FTP release synchronisation daemon
+ Copyright (C) 2007 Sergey Poznyakoff
+
+ This program 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.
+
+ This program 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 this program. If not, see <http://www.gnu.org/licenses/>. */
+
+#include "wydawca.h"
+#include "sql.h"
+
+struct sql_list
+{
+ struct sql_list *next;
+ struct sqlconn conn;
+};
+
+static struct sql_list *sql_list;
+
+void
+sql_register_conn (struct sqlconn *conn)
+{
+ struct sql_list *ent = xmalloc (sizeof *ent);
+ ent->conn = *conn;
+ ent->next = sql_list;
+ sql_list = ent;
+}
+
+struct sqlconn *
+sql_find_connection (const char *ident)
+{
+ struct sql_list *p;
+ for (p = sql_list; p; p = p->next)
+ if (strcmp (p->conn.ident, ident) == 0)
+ return &p->conn;
+ return NULL;
+}
+
+int
+sql_connection_exists_p (const char *ident)
+{
+ return sql_find_connection (ident) != NULL;
+}
+
+int
+sql_init_method (struct access_method *method)
+{
+ struct sqlconn *conn = sql_find_connection (method->param[0]);
+
+ if (!conn)
+ {
+ logmsg (LOG_EMERG, "INTERNAL ERROR: cannot find SQL connection %s",
+ method->param[0]);
+ abort ();
+ }
+
+ mysql_init (&conn->mysql);
+ if (!mysql_real_connect (&conn->mysql, conn->host, conn->user,
+ conn->password, conn->database, conn->port,
+ conn->socket, 0))
+ {
+ logmsg (LOG_ERR, "Failed to connect to database %s: Error: %s\n",
+ method->param[0], mysql_error (&conn->mysql));
+ return 1;
+ }
+ method->v.sqlconn = conn;
+ return 0;
+}
+
+int
+sql_done_method (struct access_method *method)
+{
+ mysql_close (&method->v.sqlconn->mysql);
+ return 0;