aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--doc/Makefile.am9
-rw-r--r--src/config.c9
-rw-r--r--src/gpg.c9
-rw-r--r--src/mail.c244
-rw-r--r--src/wydawca.c1
-rw-r--r--src/wydawca.h4
6 files changed, 249 insertions, 27 deletions
diff --git a/doc/Makefile.am b/doc/Makefile.am
index f53e47c..721ea29 100644
--- a/doc/Makefile.am
+++ b/doc/Makefile.am
@@ -90,24 +90,33 @@ check-writeme:
check-unrevised:
@grep -Hn @UNREVISED $(info_TEXINFOS) > $@-t; \
if [ -s $@-t ]; then \
echo "Unrevised nodes:"; \
cat $@-t; \
rm $@-t; \
false;\
else \
rm $@-t; \
fi
+check-config:
+ @check-docs.sh 'configuration statements' \
+ '/wydawca_keywords\[\] *= *{/,/^}/s/[ \t]*{ *"\([^,"]*\)".*/\1/pg' \
+ 's/@deffnx\{0,1\} {Config} *\([^@,]*\).*/\1/p' \
+ $(top_srcdir)/src/config.c -- \
+ $(MAKEINFO) $(AM_MAKEINFOFLAGS) $(MAKEINFOFLAGS) -I $(srcdir) -E - \
+ $(info_TEXINFOS)
+
+
all-check-docs: check-format check-options check-refs check-fixmes check-unrevised check-writeme
check-docs:
$(MAKE) -k all-check-docs
master-menu:
emacs -batch -l mastermenu.el -f make-master-menu $(info_TEXINFOS)
untabify:
emacs -batch -l untabify.el $(info_TEXINFOS) $(wydawca_TEXINFOS)
fix-sentence-spacing:
diff --git a/src/config.c b/src/config.c
index 559f766..f0a7042 100644
--- a/src/config.c
+++ b/src/config.c
@@ -854,24 +854,27 @@ cb_archive (enum grecs_callback_command cmd,
}
return 0;
}
static struct grecs_keyword mail_statistics_kw[] = {
{ "message", N_("text"), N_("Message text"),
grecs_type_string, &admin_stat_message },
{ "statistics",
N_("items"), N_("Send mail if one or more of these items are set"),
grecs_type_string, &mail_admin_mask, 0, cb_statistics },
+ { "gpg-sign",
+ N_("key"), N_("Sign message with this key"),
+ grecs_type_string, &admin_stat_sign_key },
{ NULL }
};
static int
cb_event (enum grecs_callback_command cmd,
grecs_locus_t *locus,
void *varptr,
grecs_value_t *value,
void *cb_data)
{
@@ -898,24 +901,27 @@ cb_recipient (enum grecs_callback_command cmd,
return 0;
}
static struct grecs_keyword notify_event_kw[] = {
{ "event", N_("ev-id"), N_("Event on which to notify"),
grecs_type_string, NULL, offsetof(struct notification, ev), cb_event },
{ "recipient", N_("who"), N_("Notify this recipient"),
grecs_type_string, NULL, offsetof(struct notification, tgt),
cb_recipient },
{ "message", N_("text-or-id"),
N_("Text of the notification or identifier of a defined message template"),
grecs_type_string, NULL, offsetof(struct notification, msg) },
+ { "gpg-sign", N_("key"),
+ N_("Sign message with this key"),
+ grecs_type_string, NULL, offsetof(struct notification, sign_keys) },
{ NULL }
};
static int
cb_notify_event (enum grecs_callback_command cmd,
grecs_locus_t *locus,
void *varptr,
grecs_value_t *value,
void *cb_data)
{
struct notification *ntf;
void **pdata = cb_data;
@@ -1447,24 +1453,27 @@ static struct grecs_keyword wydawca_kw[] = {
cb_notify_event, NULL, notify_event_kw },
{ "access-method", N_("ident"), N_("Define access method"),
grecs_type_section, default_access_method, 0,
cb_access_method, NULL, access_method_kw },
{ "spool", N_("tag: string"), N_("Define distribution spool"),
grecs_type_section, NULL, 0,
cb_spool, NULL, spool_kw },
{ "all-spools", NULL, N_("Service names that request scanning all spools"),
grecs_type_string|GRECS_LIST, &all_spool_aliases },
+
+ { "gpg-homedir", NULL, N_("GPG home directory"),
+ grecs_type_string, &wydawca_gpg_homedir },
{ NULL }
};
void
config_help ()
{
static char docstring[] =
N_("Configuration file structure for wydawca.\n"
"For more information, use `info wydawca configuration'.");
grecs_format_docstring (stdout, docstring, 0);
grecs_format_statement_array (stdout, wydawca_kw, 1, 0);
diff --git a/src/gpg.c b/src/gpg.c
index 71be53c..1389845 100644
--- a/src/gpg.c
+++ b/src/gpg.c
@@ -119,26 +119,26 @@ rmdir_r (const char *name)
/* Remove temporary GPG home directory */
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);
}
/* Create a temporary GPG home directory */
-int
-wydawca_gpg_homedir ()
+static int
+create_gpg_homedir ()
{
if (homedir)
return 0;
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);
@@ -213,30 +213,29 @@ gpg_verify_signature (gpgme_ctx_t ctx, gpgme_signature_t sig,
}
return 1;
}
/* Verify the directive file from TRP using public key PUBKEY */
/* FIXME: spool currently unused */
int
verify_directive_signature (struct file_triplet *trp,
const struct spool *spool)
{
gpgme_ctx_t ctx;
gpgme_data_t key_data, directive_data, plain;
- off_t size;
gpgme_error_t ec;
int rc;
struct uploader_info *uptr;
- wydawca_gpg_homedir ();
+ create_gpg_homedir ();
fail_if_err (gpgme_new (&ctx));
for (uptr = trp->uploader_list; uptr; uptr = uptr->next)
{
gpgme_import_result_t res;
gpgme_import_status_t pstat;
fail_if_err (gpgme_data_new_from_mem (&key_data,
uptr->gpg_key,
strlen (uptr->gpg_key),
0));
fail_if_err (gpgme_op_import (ctx, key_data));
@@ -292,25 +291,25 @@ verify_detached_signature (struct file_triplet *trp,
fail_if_err (gpgme_get_engine_info (&info));
while (info && info->protocol != GPGME_PROTOCOL_OpenPGP)
info = info->next;
if (!info)
{
logmsg (LOG_CRIT,
_("cannot find path to gpg binary (attempting to verify "
"the detached signature for %s"), trp->name);
return 1;
}
- wydawca_gpg_homedir ();
+ create_gpg_homedir ();
argv[0] = info->file_name;
argv[1] = "--verify";
argv[2] = trp->file[file_signature].name;
argv[3] = trp->file[file_dist].name;
argv[4] = NULL;
switch (wydawca_exec (5, argv, NULL))
{
case exec_success:
if (debug_level)
logmsg (LOG_DEBUG, _("good detached signature for %s"), trp->name);
return 0;
diff --git a/src/mail.c b/src/mail.c
index 91ea502..15c2937 100644
--- a/src/mail.c
+++ b/src/mail.c
@@ -8,53 +8,250 @@
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 <http://www.gnu.org/licenses/>. */
#include "wydawca.h"
#include <mail.h>
#include <hash.h>
+#include <gpgme.h>
int mailer_opened;
mu_mailer_t mailer;
mu_address_t admin_address;
mu_address_t from_address;
unsigned long mail_admin_mask;
unsigned long owner_notification_flags;
char *user_message_template[MAX_EVENT];
char *admin_stat_message;
+char *admin_stat_sign_key;
void
mail_init ()
{
if (!mailer)
{
int rc;
if ((rc = mu_mailer_create (&mailer, NULL)))
{
const char *url = NULL;
mu_mailer_get_url_default (&url);
logmsg (LOG_ERR, _("cannot create default mailer `%s': %s"),
url, mu_strerror (rc));
}
}
}
+struct mu_stream_handle
+{
+ mu_stream_t str;
+ mu_off_t off;
+};
+
+static ssize_t
+mu_stream_data_read_cb (void *handle, void *buffer, size_t size)
+{
+ struct mu_stream_handle *mhp = handle;
+ size_t nread;
+ int rc;
+
+ rc = mu_stream_read (mhp->str, buffer, size, mhp->off, &nread);
+ if (rc)
+ {
+ logmsg (LOG_ERR, "mu_stream_read: %s", mu_strerror (rc));
+ errno = EIO;
+ return -1;
+ }
+
+ mhp->off += nread;
+ return nread;
+}
+
+static int
+gpg_sign (gpgme_data_t *output, gpgme_data_t input, const char *sign_keys)
+{
+ gpgme_ctx_t ctx;
+ gpgme_error_t err = 0;
+ gpgme_key_t key;
+
+ err = gpgme_new (&ctx);
+ if (err)
+ {
+ logmsg (LOG_ERR, _("GPGME: cannot create context: %s"),
+ gpgme_strerror (err));
+ return 1;
+ }
+
+ err = gpgme_op_keylist_start (ctx, sign_keys, 0);
+ if (!err)
+ {
+ while ((err = gpgme_op_keylist_next (ctx, &key)) == 0)
+ {
+ err = gpgme_signers_add (ctx, key);
+ gpgme_key_release (key);
+ }
+ }
+
+ if (err && gpg_err_code (err) != GPG_ERR_EOF)
+ {
+ logmsg (LOG_ERR, _("GPGME: cannot list keys: %s"),
+ gpgme_strerror (err));
+ gpgme_release (ctx);
+ return 1;
+ }
+
+ err = gpgme_data_new (output);
+ if (err)
+ {
+ logmsg (LOG_ERR, _("%s: GPGME error: %s"),
+ "gpgme_data_new",
+ gpgme_strerror (err));
+ gpgme_release (ctx);
+ return 1;
+ }
+
+ /* FIXME: Passphrase */
+ gpgme_set_textmode (ctx, 1);
+ gpgme_set_armor (ctx, 1);
+
+ err = gpgme_op_sign (ctx, input, *output, GPGME_SIG_MODE_CLEAR);
+ if (err)
+ logmsg (LOG_ERR, _("%s: GPGME error: %s"),
+ "gpgme_op_sign",
+ gpgme_strerror (err));
+#if 0
+ /* FIXME: */
+ else if (debug_level > 1)
+ gpgme_debug_info (ctx);
+#endif
+
+ gpgme_release (ctx);
+ return err != 0;
+}
+
+static int
+sign_message (mu_message_t *pmsg, const char *key)
+{
+ mu_message_t msg = *pmsg;
+ mu_message_t newmsg;
+ mu_body_t body;
+ mu_header_t hdr;
+ struct mu_stream_handle mhn;
+ mu_stream_t istr, ostr;
+ int rc;
+ struct gpgme_data_cbs cbs;
+ gpgme_data_t input, output;
+ gpgme_error_t err;
+ char *buf = NULL;
+ size_t size = 0;
+ size_t nread;
+
+ if (debug_level)
+ logmsg (LOG_DEBUG, _("signing message as %s"), key);
+
+ if (wydawca_gpg_homedir)
+ {
+ if (debug_level > 1)
+ logmsg (LOG_DEBUG, _("setting GNUPG home directory: %s"),
+ wydawca_gpg_homedir);
+ setenv ("GNUPGHOME", wydawca_gpg_homedir, 1);
+ }
+
+ if ((rc = mu_message_get_body (msg, &body)))
+ {
+ logmsg (LOG_ERR, "mu_message_get_body: %s", mu_strerror (rc));
+ return 1;
+ }
+
+ if ((rc = mu_body_get_stream (body, &mhn.str)))
+ {
+ logmsg (LOG_ERR, "mu_message_get_stream: %s", mu_strerror (rc));
+ return 1;
+ }
+
+ mu_stream_seek (mhn.str, 0, SEEK_SET);
+ mhn.off = 0;
+
+ memset (&cbs, 0, sizeof (cbs));
+ cbs.read = mu_stream_data_read_cb;
+
+ err = gpgme_data_new_from_cbs (&input, &cbs, &mhn);
+ if (err)
+ {
+ logmsg (LOG_ERR, "gpgme_data_new_from_cbs: %s",
+ gpgme_strerror (rc));
+ return 1;
+ }
+
+ rc = gpg_sign (&output, input, key);
+
+ if (gpgme_data_seek (output, 0, SEEK_SET) == -1)
+ {
+ logmsg (LOG_ERR, "gpgme_data_seek: %s", strerror (errno));
+ return 1;
+ }
+
+ mu_message_create (&newmsg, NULL);
+ mu_message_get_stream (newmsg, &ostr);
+
+ /* Copy headers */
+ mu_message_get_header (msg, &hdr);
+ mu_header_get_stream (hdr, &istr);
+ mu_stream_seek (istr, 0, SEEK_SET);
+ while ((rc = mu_stream_sequential_getline (istr, &buf, &size, &nread)) == 0
+ && nread)
+ {
+ rc = mu_stream_sequential_write (ostr, buf, nread);
+ if (rc)
+ {
+ logmsg (LOG_ERR, "mu_stream_sequential_write: %s",
+ mu_strerror (rc));
+ break;
+ }
+ }
+
+ if (rc == 0)
+ {
+ while ((nread = gpgme_data_read (output, buf, size)) > 0)
+ {
+ rc = mu_stream_sequential_write (ostr, buf, nread);
+ if (rc)
+ {
+ logmsg (LOG_ERR, "mu_stream_sequential_write: %s",
+ mu_strerror (rc));
+ break;
+ }
+ }
+
+ if (rc == 0)
+ {
+ mu_message_destroy (&msg, mu_message_get_owner (msg));
+ *pmsg = newmsg;
+ }
+ }
+
+ gpgme_data_release (output);
+ free (buf);
+
+ return rc;
+}
+
void
-mail_send_message (mu_address_t rcpt, const char *text)
+mail_send_message (mu_address_t rcpt, const char *text,
+ const char *signer_key)
{
int rc;
mu_message_t msg;
mu_stream_t stream = NULL;
mu_header_t hdr;
int mailer_flags = 0;
static char *x_mailer = "wydawca (" PACKAGE_STRING ")";
size_t size;
char *buf;
const char *sval;
mu_message_create (&msg, NULL);
@@ -82,43 +279,49 @@ mail_send_message (mu_address_t rcpt, const char *text)
free (buf);
}
}
if (debug_level > 1)
{
mu_debug_t debug;
mu_mailer_get_debug (mailer, &debug);
mu_debug_set_level (debug, MU_DEBUG_TRACE | MU_DEBUG_PROT);
if (debug_level > 2)
mailer_flags = MAILER_FLAG_DEBUG_DATA;
}
-
+
if (!mailer_opened)
{
if ((rc = mu_mailer_open (mailer, mailer_flags)))
{
mu_url_t url = NULL;
mu_mailer_get_url (mailer, &url);
logmsg (LOG_CRIT, _("opening mailer `%s' failed: %s"),
url ? mu_url_to_string (url) : "(unknown URL)",
mu_strerror (rc));
return;
}
mailer_opened = 1;
}
- rc = mu_mailer_send_message (mailer, msg, from_address, rcpt);
- if (rc)
- logmsg (LOG_CRIT, _("cannot send message: %s"), mu_strerror (rc));
-
+ if (signer_key)
+ sign_message (&msg, signer_key);
+
+ if (!dry_run_mode)
+ {
+ rc = mu_mailer_send_message (mailer, msg, from_address, rcpt);
+ if (rc)
+ logmsg (LOG_CRIT, _("cannot send message: %s"), mu_strerror (rc));
+ }
+
mu_message_destroy (&msg, mu_message_get_owner (msg));
}
void
mail_finish ()
{
if (mailer_opened)
{
mu_mailer_close (mailer);
mailer_opened = 0;
}
}
@@ -235,45 +438,42 @@ mail_stats ()
if (debug_level)
{
size_t size;
char *buf;
mu_address_to_string (admin_address, NULL, 0, &size);
buf = xmalloc (size + 1);
mu_address_to_string (admin_address, buf, size + 1, NULL);
logmsg (LOG_DEBUG, _("sending stats to %s"), buf);
free (buf);
}
- if (dry_run_mode)
- return;
-
tc = timer_get_count () * 3;
exp = make_stat_expansion (tc + 1);
time (&t);
exp[0].kw = "date";
exp[0].value = exp[0].storage = xstrdup (ctime (&t));
exp[0].value [strlen (exp[0].value) - 1] = 0;
timer_fill_meta (exp + 1, tc);
tmpl = resolve_message_template (admin_stat_message);
if (!tmpl)
{
logmsg (LOG_ERR, _("undefined message reference: %s"),
admin_stat_message);
return;
}
text = meta_expand_string (tmpl, exp, NULL, NULL, NULL);
- mail_send_message (admin_address, text);
+ mail_send_message (admin_address, text, admin_stat_sign_key);
free (text);
meta_free (exp);
timer_free_meta (exp + 1, tc);
free (exp);
}
mu_address_t
get_uploader_email (struct uploader_info *info, struct file_triplet *trp,
const char **errp)
{
mu_address_t addr;
@@ -357,24 +557,26 @@ get_recipient (struct access_method *method, struct file_triplet *trp,
}
method_close (method, md);
return rcpt;
}
void
do_notify (struct file_triplet *trp, enum notification_event ev,
struct notification *ntf)
{
mu_address_t rcpt = NULL;
const char *errp;
+ const char *msg;
+
switch (ntf->tgt)
{
case notify_read:
rcpt = NULL;
break;
case notify_admin:
rcpt = mu_address_dup (admin_address);
break;
case notify_user:
rcpt = get_uploader_email (trp->uploader, trp, &errp);
@@ -399,39 +601,37 @@ do_notify (struct file_triplet *trp, enum notification_event ev,
if (rcpt)
{
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 %s (project %s) about %s"),
buf, trp->project, notification_event_str (ev));
free (buf);
}
else
- logmsg (LOG_DEBUG, _("notifying message recipients (project %s) about %s"),
+ logmsg (LOG_DEBUG,
+ _("notifying message recipients (project %s) about %s"),
trp->project, notification_event_str (ev));
}
-
- if (!dry_run_mode)
+
+ msg = resolve_message_template (ntf->msg);
+ if (!msg)
+ logmsg (LOG_ERR, _("undefined message reference: %s"), ntf->msg);
+ else
{
- const char *msg = resolve_message_template (ntf->msg);
- if (!msg)
- logmsg (LOG_ERR, _("undefined message reference: %s"), ntf->msg);
- else
- {
- char *text = triplet_expand_param (msg, trp);
- mail_send_message (rcpt, text);
- free (text);
- }
+ char *text = triplet_expand_param (msg, trp);
+ mail_send_message (rcpt, text, ntf->sign_keys);
+ free (text);
}
mu_address_destroy (&rcpt);
}
void
notify (struct notification *notification_list,
struct file_triplet *trp, enum notification_event ev)
{
struct notification *p;
for (p = notification_list; p; p = p->next)
diff --git a/src/wydawca.c b/src/wydawca.c
index 07cac5c..f098bb1 100644
--- a/src/wydawca.c
+++ b/src/wydawca.c
@@ -36,24 +36,25 @@ char *tar_command_name = "tar";
int archive_signatures = 1; /* Archive sig files by default */
int lint_mode = 0;
int preprocess_only = 0;
int cron_option = 0;
int foreground_option = -1;
int single_process_option = -1;
int daemon_mode = 0;
int foreground;
int single_process;
time_t wakeup_interval;
gl_list_t all_spool_aliases;
+char *wydawca_gpg_homedir;
struct grecs_sockaddr listen_sockaddr;
unsigned wydawca_stat[MAX_STAT];
void
initstats ()
{
memset (wydawca_stat, 0, sizeof wydawca_stat);
}
diff --git a/src/wydawca.h b/src/wydawca.h
index 805b72b..96fd336 100644
--- a/src/wydawca.h
+++ b/src/wydawca.h
@@ -272,24 +272,25 @@ enum notification_target
{
notify_read, /* Read recipients from the message headers */
notify_admin, /* System administrator */
notify_owner, /* Project admin */
notify_user /* User (uploader) */
};
struct notification
{
struct notification *next;
enum notification_event ev;
enum notification_target tgt;
+ const char *sign_keys;
const char *msg;
};
void register_message_template (const char *name, const char *text);
void notify (struct notification *,
struct file_triplet *, enum notification_event);
const char *notification_event_str (enum notification_event evt);
const char *notification_target_str (enum notification_target tgt);
@@ -318,41 +319,44 @@ extern int dry_run_mode; /* Dry run indicator */
extern int log_to_stderr; /* Log to stderr instead of the syslog */
extern int log_facility; /* Syslog facility to use if !log_to_stderr */
extern char *syslog_tag; /* Syslog tag */
extern int syslog_include_prio;/* Syslog priority indication */
extern time_t file_sweep_time; /* Unlink stale file after this amount of time
*/
extern char *tar_command_name; /* Name of the tar command */
extern unsigned wydawca_stat[MAX_STAT];
extern unsigned long print_stats;
extern int archive_signatures;
extern char *admin_stat_message;
+extern char *admin_stat_sign_key;
extern char *pidfile;
extern int force_startup;
extern char *lockdir;
extern time_t lock_expire_time;
extern time_t lock_timeout;
extern int enable_locking;
extern int daemon_mode;
extern time_t wakeup_interval;
extern int foreground;
extern int single_process;
extern struct grecs_sockaddr listen_sockaddr;
extern gl_list_t all_spool_aliases;
+extern char *wydawca_gpg_homedir;
+
#define UPDATE_STATS(what) \
do \
{ \
if (what >= MAX_STAT) abort(); \
wydawca_stat[what]++; \
} \
while (0)
int stat_mask_p (unsigned long mask);
struct metadef *make_stat_expansion (size_t count);
void initstats (void);
void logstats (void);

Return to:

Send suggestions and report system problems to the System administrator.