ossp-pkg/lmtp2nntp/lmtp2nntp_config.c
1.9
/*
** Copyright (c) 2001-2002 The OSSP Project <http://www.ossp.org/>
** Copyright (c) 2001-2002 Cable & Wireless Deutschland <http://www.cw.com/de/>
**
** This file is part of OSSP lmtp2nntp, an LMTP speaking local
** mailer which forwards mails as Usenet news articles via NNTP.
** It can be found at http://www.ossp.org/pkg/lmtp2nntp/.
**
** 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
** 2.0 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 file; if not, write to the Free Software
** Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
** USA, or contact the OSSP project <ossp@ossp.org>.
**
** FIXME lmtp2nntp_config.c: LMTP to NNTP configuration parsing
*/
#include <stdlib.h>
#include <stdio.h>
#include <sys/stat.h>
#if 0
#include <unistd.h>
#include <errno.h>
#include <string.h>
#include <fcntl.h>
#include <sys/utsname.h>
#include <sys/time.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <sys/stat.h>
#include <signal.h>
#include <pwd.h>
#endif
/* third party (included) */
#if 0
#include "lmtp2nntp_argz.h"
#include "lmtp2nntp_shpat.h"
#include "lmtp2nntp_daemon.h"
#endif
#include "lmtp2nntp_popt.h"
#include "val.h"
/* third party (linked in) */
#include "str.h"
#if 0
#include "l2.h"
#include "var.h"
#endif
/* library version check (compile-time) */
#define L2_VERSION_HEX_REQ 0x001200
#define L2_VERSION_STR_REQ "0.1.0"
#define STR_VERSION_HEX_REQ 0x009206
#define STR_VERSION_STR_REQ "0.9.6"
#ifdef L2_VERSION_HEX
#if L2_VERSION_HEX < L2_VERSION_HEX_REQ
#error "require a newer version of OSSP L2"
#endif
#endif
#ifdef STR_VERSION_HEX
#if STR_VERSION_HEX < STR_VERSION_HEX_REQ
#error "require a newer version of OSSP Str"
#endif
#endif
/* own headers */
#include "lmtp2nntp_global.h"
#ifdef HAVE_CONFIG_H
#include "config.h"
#endif
#if defined(HAVE_DMALLOC_H) && defined(DMALLOC)
#include "dmalloc.h"
#endif
#include "lmtp2nntp_config.h"
#include "lmtp2nntp_lmtp.h"
#include "lmtp2nntp_nntp.h"
#include "lmtp2nntp_msg.h"
#include "sa.h"
#define _LMTP2NNTP_VERSION_C_AS_HEADER_
#include "lmtp2nntp_version.c"
#undef _LMTP2NNTP_VERSION_C_AS_HEADER_
#ifndef FALSE
#define FALSE (1 != 1)
#endif
#ifndef TRUE
#define TRUE (!FALSE)
#endif
#ifndef NUL
#define NUL '\0'
#endif
void die(char *); /* FIXME */
void die(char *msg)
{
printf("ERROR: %s\n", msg);
exit(-1);
}
#if 0
static val_rc_t dumper(void *ctx, const char *name, int type, const char *desc, void *data)
{
switch (type) {
case VAL_TYPE_VAL:
fprintf(stderr, "DEBUG: <%10s>, name=<%10s>, VAL_TYPE_VAL, desc=<%20s>, data@%.8lx INTERNAL\n",
(char *)ctx, name, desc, (long)data);
break;
case VAL_TYPE_PTR:
fprintf(stderr, "DEBUG: <%10s>, name=<%10s>, VAL_TYPE_PTR, desc=<%20s>, data@%.8lx=%.8lx\n",
(char *)ctx, name, desc, (long)data, *(long *)data);
break;
case VAL_TYPE_CHAR:
fprintf(stderr, "DEBUG: <%10s>, name=<%10s>, VAL_TYPE_CHAR, desc=<%20s>, data@%.8lx='%c'\n",
(char *)ctx, name, desc, (long)data, *(char *)data);
break;
case VAL_TYPE_SHORT:
fprintf(stderr, "DEBUG: <%10s>, name=<%10s>, VAL_TYPE_SHORT, desc=<%20s>, data@%.8lx=%8d\n",
(char *)ctx, name, desc, (long)data, *(short *)data);
break;
case VAL_TYPE_INT:
fprintf(stderr, "DEBUG: <%10s>, name=<%10s>, VAL_TYPE_INT, desc=<%20s>, data@%.8lx=%8d\n",
(char *)ctx, name, desc, (long)data, *(int *)data);
break;
case VAL_TYPE_LONG:
fprintf(stderr, "DEBUG: <%10s>, name=<%10s>, VAL_TYPE_LONG, desc=<%20s>, data@%.8lx=%8ld\n",
(char *)ctx, name, desc, (long)data, *(long *)data);
break;
case VAL_TYPE_FLOAT:
fprintf(stderr, "DEBUG: <%10s>, name=<%10s>, VAL_TYPE_FLOAT, desc=<%20s>, data@%.8lx=%8f\n",
(char *)ctx, name, desc, (long)data, *(float *)data);
break;
case VAL_TYPE_DOUBLE:
fprintf(stderr, "DEBUG: <%10s>, name=<%10s>, VAL_TYPE_DOUBLE, desc=<%20s>, data@%.8lx=%8f\n",
(char *)ctx, name, desc, (long)data, *(double *)data);
break;
default:
fprintf(stderr, "DEBUG: <%10s>, name=<%10s>, type = %.8lx, desc=<%20s>, data@%.8lx\n",
(char *)ctx, name, (long)type, desc, (long)data);
}
return VAL_OK;
}
FIXME pass(optionconfig_t *o, int pass);
/* pass=0 for preinitialization !? */
void dotconftest(int argc, char **argv) /*FIXME*/
{
popt_context poptCon; /* context for parsing command-line options */
lmtp2nntp_config_t *ctx;
char *cpBuf;
struct popt_option poptTable[] = {
{ "childsmax", 'C', POPT_ARG_STRING, NULL, 'C', "foo", "childsmax" },
{ "daemonize", 'D', POPT_ARG_NONE, NULL, 'D', "foo", NULL },
{ "kill", 'K', POPT_ARG_NONE, NULL, 'K', "foo", NULL },
{ "pidfile", 'P', POPT_ARG_STRING, NULL, 'P', "foo", "pidfile" },
{ "veryverbose", 'V', POPT_ARG_NONE, NULL, 'V', "foo", NULL },
{ "acl", 'a', POPT_ARG_STRING, NULL, 'a', "foo", "addr[/mask]" },
{ "bind", 'b', POPT_ARG_STRING, NULL, 'b', "foo", "addr[:port]|-|path[:perms]" },
{ "client", 'c', POPT_ARG_STRING, NULL, 'c', "foo", "addr[:port]" },
{ "destination", 'd', POPT_ARG_STRING, NULL, 'd', "foo", "addr[:port]" },
{ "groupmode", 'g', POPT_ARG_STRING, NULL, 'g', "foo", "groupmode" },
{ "headervalue", 'h', POPT_ARG_STRING, NULL, 'h', "foo", "header: value" },
{ "include", 'i', POPT_ARG_STRING, NULL, 'i', "foo", "configfile" },
{ "size", 's', POPT_ARG_STRING, NULL, 's', "foo", "bytes" },
{ "timeout", 't', POPT_ARG_STRING, NULL, 't', "foo", "name=sec" },
{ "mailfrom", 'm', POPT_ARG_STRING, NULL, 'm', "foo", "regex" },
{ "nodename", 'n', POPT_ARG_STRING, NULL, 'n', "foo", "nodename" },
{ "operationmode", 'o', POPT_ARG_STRING, NULL, 'o', "foo", "post|feed" },
{ "l2spec", 'l', POPT_ARG_STRING, NULL, 'l', "foo", "spec" },
{ "uid", 'u', POPT_ARG_STRING, NULL, 'u', "foo", "number|name" },
{ "restrictheader", 'r', POPT_ARG_STRING, NULL, 'r', "foo", "regex" },
{ "newsgroup", NUL, POPT_ARG_STRING
|POPT_ARGFLAG_DOC_HIDDEN, NULL, 1, NULL, NULL },
POPT_AUTOHELP
{ NULL, 0, 0, NULL, 0 }
};
ctx = malloc(sizeof(lmtp2nntp_config_t));
ctx->option_childsmax = 10;
{
int testint = 10;
int testintout = 100;
int testint2 = 20;
int testintout2 = 200;
val_rc_t rc;
val_t *v, *v2, *v3;
if ((rc = val_create(&v)) != VAL_OK)
die("val_create");
printf("DEBUG: testint = %d, testintout = %d\n", testint, testintout);
if ((rc = val_reg(v, "foo", VAL_TYPE_INT, "foo variable", (void *)&testint)) != VAL_OK)
die("val_reg");
testint++;
if ((rc = val_get(v, "foo", &testintout)) != VAL_OK)
die("val_get");
printf("DEBUG: testint = %d, testintout = %d\n", testint, testintout);
if ((rc = val_set(v, "foo", 2)) != VAL_OK)
die("val_set");
if ((rc = val_get(v, "foo", &testintout)) != VAL_OK)
die("val_get");
printf("DEBUG: testint = %d, testintout = %d\n", testint, testintout);
if ((rc = val_create(&v2)) != VAL_OK)
die("val_create 2");
if ((rc = val_reg(v, "bar", VAL_TYPE_VAL, "bar variable", (void *)&v2)) != VAL_OK)
die("val_reg 2");
val_reg(v, "bar", VAL_TYPE_VAL, "bar variable", NULL);
val_set(v, "bar", v2);
if ((rc = val_get(v, "bar", &v3)) != VAL_OK)
die("val_get v3");
if ((rc = val_reg(v2, "quux", VAL_TYPE_INT, "quux variable", (void *)&testint2)) != VAL_OK)
die("val_reg 2b");
testint2++;
if ((rc = val_get(v2, "quux", &testintout2)) != VAL_OK)
die("val_get 2c");
printf("DEBUG: testint2 = %d, testintout2 = %d\n", testint2, testintout2);
if ((rc = val_get(v, "bar.quux", &testintout2)) != VAL_OK) {
fprintf(stderr, "rc=%d\n", rc);
die("val_get 2d");
}
printf("DEBUG: testint2 = %d, testintout2 = %d\n", testint2, testintout2);
printf("DEBUG: testint at address %lx\n", (long)&testint);
printf("DEBUG: testintout at address %lx\n", (long)&testintout);
printf("DEBUG: testint2 at address %lx\n", (long)&testint2);
printf("DEBUG: testint2out at address %lx\n", (long)&testintout2);
if ((rc = val_reg(v, "bar.ptr", VAL_TYPE_PTR, "bar ptr (&testint)", NULL)) != VAL_OK)
die("val_ret for bar.ptr using inline data");
if ((rc = val_set(v, "bar.ptr", &testint)) != VAL_OK)
die("val_set for bar.ptr using inline data");
if ((rc = val_reg(v, "bar.char", VAL_TYPE_CHAR, "bar character (!)", NULL)) != VAL_OK)
die("val_ret for bar.char using inline data");
if ((rc = val_set(v, "bar.char", '!')) != VAL_OK)
die("val_set for bar.char using inline data");
if ((rc = val_reg(v, "bar.short", VAL_TYPE_SHORT, "bar short (555)", NULL)) != VAL_OK)
die("val_ret for bar.short using inline data");
if ((rc = val_set(v, "bar.short", 555)) != VAL_OK)
die("val_set for bar.short using inline data");
if ((rc = val_reg(v, "bar.int", VAL_TYPE_INT, "bar integer (76543)", NULL)) != VAL_OK)
die("val_ret for bar.int using inline data");
if ((rc = val_set(v, "bar.int", 76543)) != VAL_OK)
die("val_set for bar.int using inline data");
if ((rc = val_reg(v, "bar.long", VAL_TYPE_LONG, "bar long (2097152)", NULL)) != VAL_OK)
die("val_ret for bar.long using inline data");
if ((rc = val_set(v, "bar.long", 2097152)) != VAL_OK)
die("val_set for bar.long using inline data");
if ((rc = val_reg(v, "bar.float", VAL_TYPE_FLOAT, "bar float (1.955830)", NULL)) != VAL_OK)
die("val_ret for bar.float using inline data");
if ((rc = val_set(v, "bar.float", 1.95583)) != VAL_OK)
die("val_set for bar.float using inline data");
if ((rc = val_reg(v, "bar.double", VAL_TYPE_DOUBLE, "bar double (3.1415+)", NULL)) != VAL_OK)
die("val_ret for bar.double using inline data");
if ((rc = val_set(v, "bar.double", 3.14159265358979)) != VAL_OK)
die("val_set for bar.double using inline data");
OK val_apply(v, "", 9, dumper, "v" );
OK val_apply(v2, "", 9, dumper, "v2");
OK val_apply(v, "", 0, dumper, "v" );
OK val_apply(v, "bar", 1, dumper, "v" );
OK val_apply(v2, "", 0, dumper, "v" );
OK val_apply(v, "", 1, dumper, "v" );
OK val_apply(v, "foo", 0, dumper, "v" );
OK val_apply(v2, "char", 2, dumper, "v" );
OK val_apply(v, "bar.char", 2, dumper, "v" );
if ((rc = val_destroy(v2)) != VAL_OK)
die("val_destroy 2");
if ((rc = val_destroy(v)) != VAL_OK)
die("val_destroy");
return;
}
{
char c;
char *cp;
poptCon = popt_getcontext(NULL, argc, (const char **)argv, poptTable, 0);
popt_setotheroptionhelp(poptCon, "[OPTIONS]* [newsgroup ...]");
printf("DEBUG: argc=%d\n", argc);
if (argc < 2) {
popt_printusage(poptCon, stderr, 0);
exit(1);
}
while ((c = popt_getnextopt(poptCon)) >= 0) {
printf("DEBUG: popt_getnextopt returned %d='%c'\n", (int)c, c);
}
while ((cp = popt_getarg(poptCon)) != NULL) {
printf("DEBUG: popt_getarg returned \"%s\"\n", cp);
}
printf("DEBUG: popt_getnextopt ended with \"%s\"(%d)\n", popt_strerror((int)c), (int)c);
popt_freecontext(poptCon);
return;
}
/* Braindump
fuer configure
ich brauche eine Tabelle, die alle optionen in langer und kurzer Form
enthaelt, Angabe ob flag, single- oder multivalue, callbacks fuer die
syntaxpruefung des human-readable inputs, die conversion ins interne binaryformat sowie rueckwandlung in
human-readable format. Dazu informationen fuer help.
*/
{
const char *filename = "example.conf";
struct stat sb;
int fd;
if (stat(filename, &sb) == -1)
die("stat");
if ((cpBuf = (char *)malloc((size_t)sb.st_size + 1)) == NULL)
die("malloc");
if ((fd = open(filename, O_RDONLY)) == -1)
die("open");
if (read(fd, (void *)cpBuf, (size_t)sb.st_size) != (ssize_t)sb.st_size)
die("read");
cpBuf[(int)sb.st_size] = '\0';
if (close(fd) == -1)
die("close");
}
printf("DEBUG: *** 1 *** file as it was just read in ***\n%s***\n", cpBuf);
{
char *cpI; /* pointer to next character to be read */
char *cpO; /* pointer to next character to be written. Used for eliminating
backslash+newline at a line continuation */
char *cpL; /* pointer to start of line */
int pline; /* current physical (disregarding line continuation) line number */
int lline; /* current logical lines first physical line number */
int eline; /* flag signaling empty or just whitespace-filled line */
char c; /* current character */
char p; /* previous character */
int eof; /* flag signaling end of file detected */
cpI = cpBuf;
cpO = cpBuf;
eof = FALSE;
pline = 1;
p = NUL;
cpL = cpO;
lline = pline;
eline = TRUE;
while(!eof) {
c = *cpI++;
*cpO++ = c;
if (c == NUL)
eof = TRUE;
else
if (!isspace(c))
eline = FALSE;
if (eof || (c == '\n')) {
pline++;
if (!eof && (p == '\\')) { /* line continuation situation */
cpO-=2; /* need to remove both backslash+newline */
}
else {
if (!eline) { /* process logical line unless it's empty */
*(cpO-1) = NUL;
if (lline == (pline-1))
printf("DEBUG: line[%3d] = ***%s***\n", lline, cpL);
else
printf("DEBUG: [%3d-%3d] = ***%s***\n", lline, pline-1, cpL);
{
char *cp = cpL;
char *command;
char *value;
if ((command = str_token(&cp, " \t", "\"'", "#", STR_STRIPQUOTES|STR_BACKSLASHESC)) == NULL)
printf("DEBUG: no command - comment only\n");
else {
printf("DEBUG: command = ***%s***\n", command);
if ((value = str_token(&cp, " \t", "\"'", "#", STR_STRIPQUOTES|STR_BACKSLASHESC)) == NULL)
printf("DEBUG: no value - section\n");
else {
while(isspace((int)*value)) value++;
printf("DEBUG: value = ***%s***\n", value);
}
}
}
}
cpL = cpO;
lline = pline;
eline = TRUE;
}
}
p = c;
}
}
}
#endif
typedef enum {
OPT_FLAG,
OPT_SINGLE,
OPT_MULTI
} optiontype_t;
typedef void (*optionloop_cb_t)(void);
struct optionconfig_s;
typedef struct optionconfig_s optionconfig_t;
struct optionconfig_s {
optionconfig_t *next; /* cleanup chain for destroy */
/**/
char *longname; /* the long name (optional if shortname given) */
char shortname; /* the short name (optional if longname given) */
char *descrip; /* description for autohelp */
char *argdescrip; /* argument description for autohelp */
optiontype_t type; /* OPT_FLAG, OPT_SINGLE, OPT_MULTI */
optionloop_cb_t *cb; /* callback for first iteration - syntax check and include */
void *cbctx; /* context for pass1 */
void *pass1default; /* default for pass1 */
val_t *val; /* val we are registered under */
/**/
int number; /* number of this option for popt */
union { /* option data as read from configuration */
int f; /* OPT_FLAG */
char *s; /* OPT_SINGLE */
char **m; /* OPT_MULTI */
} data;
};
static void stdsyntax(void)
{
return;
}
static void includeit(void)
{
return;
}
static void option_register(lmtp2nntp_option_t *o, char *longname, char shortname, optiontype_t type, optionloop_cb_t cb, char *cbctx, char *descrip, char *argdescrip)
{
return;
}
lmtp2nntp_option_rc_t option_create(lmtp2nntp_option_t **op)
{
if (op == NULL)
return OPTION_ERR_ARG;
if ((*op = (lmtp2nntp_option_t *)malloc(sizeof(lmtp2nntp_option_t))) == NULL)
return OPTION_ERR_MEM;
(*op)->childsmax = 0;
(*op)->daemonize = 0;
(*op)->kill = 0;
(*op)->pidfile = 0;
(*op)->acl.as = 0;
(*op)->acl.az = NULL;
(*op)->bind = NULL;
(*op)->client = NULL;
(*op)->destination.as = 0;
(*op)->destination.az = NULL;
(*op)->groupmode = NULL;
(*op)->headervalue.as = 0;
(*op)->headervalue.az = NULL;
(*op)->include.as = 0;
(*op)->include.az = NULL;
(*op)->size = 0;
(*op)->timeoutlmtpaccept = 0;
(*op)->timeoutlmtpread = 0;
(*op)->timeoutlmtpwrite = 0;
(*op)->timeoutnntpconnect = 0;
(*op)->timeoutnntpread = 0;
(*op)->timeoutnntpwrite = 0;
(*op)->mailfrom = NULL;
(*op)->nodename = NULL;
(*op)->operationmode = NULL;
(*op)->l2spec = NULL;
(*op)->uid = NULL;
(*op)->restrictheader.as = 0;
(*op)->restrictheader.az = NULL;
(*op)->newsgroup.as = 0;
(*op)->newsgroup.az = NULL;
/**/
(*op)->vo = NULL;
if (val_create(&((*op)->vo)) != VAL_OK) {
free(*op);
return OPTION_ERR_VAL;
}
return OPTION_OK;
}
lmtp2nntp_option_rc_t option_parse(lmtp2nntp_option_t *o, int argc, char **argv)
{
if (o == NULL)
return OPTION_ERR_ARG;
option_register(o, "childsmax", 'C', OPT_SINGLE, stdsyntax, "[0-9]{,5}", "foo", "childsmax" );
option_register(o, "daemonize", 'D', OPT_FLAG, NULL, NULL, "foo", NULL );
option_register(o, "kill", 'K', OPT_FLAG, NULL, NULL, "foo", NULL );
option_register(o, "pidfile", 'P', OPT_SINGLE, stdsyntax, ".{,255}", "foo", "pidfile" );
option_register(o, "veryverbose", 'V', OPT_FLAG, NULL, NULL, "foo", NULL );
option_register(o, "acl", 'a', OPT_MULTI, stdsyntax, "[0-9.](/[0-9]2)?", "foo", "addr[/mask]" );
option_register(o, "bind", 'b', OPT_SINGLE, stdsyntax, ".*", "foo", "addr[:port]|-|path[:perms]" );
option_register(o, "client", 'c', OPT_SINGLE, stdsyntax, ".*", "foo", "addr[:port]" );
option_register(o, "destination", 'd', OPT_MULTI, stdsyntax, ".*", "foo", "addr[:port]" );
option_register(o, "groupmode", 'g', OPT_SINGLE, stdsyntax, ".*", "foo", "groupmode" );
option_register(o, "headervalue", 'h', OPT_MULTI, stdsyntax, ".*", "foo", "header: value" );
option_register(o, "include", 'i', OPT_MULTI, includeit, ".*", "foo", "configfile" );
option_register(o, "size", 's', OPT_SINGLE, stdsyntax, ".*", "foo", "bytes" );
option_register(o, "timeoutlmtpaccept", NUL, OPT_MULTI, stdsyntax, ".*", "foo", "sec" );
option_register(o, "timeoutlmtpread", NUL, OPT_MULTI, stdsyntax, ".*", "foo", "sec" );
option_register(o, "timeoutlmtpwrite", NUL, OPT_MULTI, stdsyntax, ".*", "foo", "sec" );
option_register(o, "timeoutnntpconnect", NUL, OPT_MULTI, stdsyntax, ".*", "foo", "sec" );
option_register(o, "timeoutnntpread", NUL, OPT_MULTI, stdsyntax, ".*", "foo", "sec" );
option_register(o, "timeoutnntpwrite", NUL, OPT_MULTI, stdsyntax, ".*", "foo", "sec" );
option_register(o, "mailfrom", 'm', OPT_SINGLE, stdsyntax, ".*", "foo", "regex" );
option_register(o, "nodename", 'n', OPT_SINGLE, stdsyntax, ".*", "foo", "nodename" );
option_register(o, "operationmode", 'o', OPT_SINGLE, stdsyntax, ".*", "foo", "post|feed" );
option_register(o, "l2spec", 'l', OPT_SINGLE, stdsyntax, ".*", "foo", "spec" );
option_register(o, "uid", 'u', OPT_SINGLE, stdsyntax, ".*", "foo", "number|name" );
option_register(o, "restrictheader", 'r', OPT_MULTI, stdsyntax, ".*", "foo", "regex" );
option_register(o, "newsgroup", NUL, OPT_MULTI, stdsyntax, ".*", "foo", "newsgroup");
return OPTION_OK;
}
lmtp2nntp_option_rc_t option_destroy(lmtp2nntp_option_t *o)
{
if (o == NULL)
return OPTION_ERR_ARG;
if (o->vo != NULL)
val_destroy(o->vo);
free(o);
return OPTION_OK;
}