/* ** Copyright (c) 2001-2002 The OSSP Project ** Copyright (c) 2001-2002 Cable & Wireless Deutschland ** ** 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 . ** ** lmtp2nntp_config.c: config handling */ #include #include #include #include /* third party (included) */ #include "lmtp2nntp_argz.h" /* third party (linked in) */ #include "str.h" #include "val.h" #include "popt.h" #include "l2.h" /* library version check (compile-time) */ #define STR_VERSION_HEX_REQ 0x009206 #define STR_VERSION_STR_REQ "0.9.6" #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_option.h" #include "lmtp2nntp_config.h" #include "lmtp2nntp_lmtp.h" #include "lmtp2nntp_nntp.h" #include "lmtp2nntp_msg.h" #include "fixme.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 static l2_result_t formatter_prefix(l2_context_t *_ctx, const char id, const char *param, char *bufptr, size_t bufsize, size_t *buflen, va_list *ap) { lmtp2nntp_t *ctx = (lmtp2nntp_t *)_ctx->vp; if ((ctx->msg != NULL) && (ctx->msg->cpFid != NULL)) { sprintf(bufptr, "%s: ", ctx->msg->cpFid); *buflen = strlen(bufptr); } else *buflen = 0; return L2_OK; } static l2_result_t formatter_errno(l2_context_t *_ctx, const char id, const char *param, char *bufptr, size_t bufsize, size_t *buflen, va_list *ap) { sprintf(bufptr, "(%d) %s", errno, strerror(errno)); *buflen = strlen(bufptr); return L2_OK; } void config_context(lmtp2nntp_t *ctx) { ex_t ex; optionval_t *ov; //char *cp; int rc; /* create L2 environment */ if (l2_env_create(&ctx->l2_env) != L2_OK) { fprintf(stderr, "%s:Error: failed to create L2 environment\n", ctx->progname); CU(ERR_EXECUTION); } if (l2_env_formatter(ctx->l2_env, 'P', formatter_prefix, &ctx->ctx) != L2_OK) { fprintf(stderr, "%s:Error: logging failed to register prefix formatter\n", ctx->progname); CU(ERR_EXECUTION); } if (l2_env_formatter(ctx->l2_env, 'D', l2_util_fmt_dump, NULL) != L2_OK) { fprintf(stderr, "%s:Error: logging failed to register dump formatter\n", ctx->progname); CU(ERR_EXECUTION); } if (l2_env_formatter(ctx->l2_env, 'S', l2_util_fmt_string, NULL) != L2_OK) { fprintf(stderr, "%s:Error: logging failed to register string formatter\n", ctx->progname); CU(ERR_EXECUTION); } if (l2_env_formatter(ctx->l2_env, 'm', formatter_errno, NULL) != L2_OK) { fprintf(stderr, "%s:Error: logging failed to register errno formatter\n", ctx->progname); CU(ERR_EXECUTION); } if (val_get(ctx->val, "option.l2spec", &ov) != VAL_OK) { fprintf(stderr, "%s:Error: (internal) config did not register 'l2spec' option\n", ctx->progname); CU(ERR_EXECUTION); } if (ov->data.s != NULL) { if ((rc = l2_spec(&ctx->l2, ctx->l2_env, ov->data.s)) != L2_OK) { fprintf(stderr, "%s:Error: logging failed to create stream\n", ctx->progname); CU(ERR_EXECUTION); } if (l2_channel_levels(ctx->l2, L2_LEVEL_ALL, L2_LEVEL_NONE) != L2_OK) { /* FIXME should this globalmask and flushmask be user-configurable? */ fprintf(stderr, "%s:Error: logging failed to set global logging level\n", ctx->progname); CU(ERR_EXECUTION); } if (l2_channel_open(ctx->l2) != L2_OK) { fprintf(stderr, "%s:Error: logging failed to open channel stream\n", ctx->progname); CU(ERR_EXECUTION); } } /* from this point on logging is up and running and fprintf(stderr, ...) * should not be used in the remainder of the program flow. */ log1(ctx, NOTICE, "startup, version %s", lmtp2nntp_version.v_gnu); /* --childsmax SINGLE */ try { if ( (val_get(ctx->val, "option.childsmax", &ov) != VAL_OK) || (ov->ndata != 1) || (ov->data.s == NULL) ) throw(0,0,0); log1(ctx, TRACE, "--childsmax = \"%s\"", ov->data.s); if ((ctx->option_childsmax = atoi(ov->data.s)) <= 0) { log1(ctx, ERROR, "option --childsmax, number (%d) out of range", ctx->option_childsmax); throw(0,0,0); } } catch (ex) { rethrow; } /* --daemonize FLAG */ try { if ( (val_get(ctx->val, "option.daemonize", &ov) != VAL_OK) || (ov->ndata != 1) || (ov->data.f != 1) ) throw(0,0,0); log1(ctx, TRACE, "--daemonize = %d", ov->data.f); ctx->option_daemon = TRUE; } catch (ex) rethrow; /* --kill FLAG */ try { if ( (val_get(ctx->val, "option.kill", &ov) != VAL_OK) || (ov->ndata != 1) || (ov->data.f != 1) ) throw(0,0,0); log1(ctx, TRACE, "--kill = %d", ov->data.f); ctx->option_killflag = TRUE; } catch (ex) rethrow; /* --pidfile SINGLE */ try { if ( (val_get(ctx->val, "option.pidfile", &ov) != VAL_OK) || (ov->ndata != 1) || (ov->data.s == NULL) ) throw(0,0,0); log1(ctx, TRACE, "--pidfile = \"%s\"", ov->data.s); ctx->option_pidfile = ov->data.s; } catch (ex) rethrow; /* --acl MULTI */ try { char *cp; int i; if ( (val_get(ctx->val, "option.acl", &ov) != VAL_OK) || ((ov->ndata >= 1) && (ov->data.m == NULL)) ) throw(0,0,0); log1(ctx, DEBUG, "ov->ndata = %d", ov->ndata); for (i = 0; i < ov->ndata; i++) log2(ctx, TRACE, "--acl[%d] = \"%s\"", i, (ov->data.m)[i]); if ((ctx->pacl = (struct acl *)malloc(ov->ndata * sizeof(struct acl))) == NULL) throw(0,0,0); for (i = 0; i < ov->ndata; i++) { cp = (ov->data.m)[i]; log2(ctx, DEBUG, "cp = (data.m)[%d] = \"%s\"", i, cp); if (cp[0] == '!') { ctx->pacl[i].acl = strdup(cp + 1); ctx->pacl[i].not = TRUE; } else { ctx->pacl[i].acl = strdup(cp); ctx->pacl[i].not = TRUE; } log2(ctx, DEBUG, "ctx->pacl[%d].not = %s", i, ctx->pacl[i].not == TRUE ? "TRUE" : "FALSE"); log2(ctx, DEBUG, "ctx->pacl[%d].acl = %s", i, ctx->pacl[i].acl); if ((cp = strrchr(ctx->pacl[i].acl, '/')) != NULL) *cp++ = NUL; else cp = "-1"; ctx->pacl[i].prefixlen = atoi(cp); log2(ctx, DEBUG, "ctx->pacl[%d].prefixlen = %d", i, ctx->pacl[i].prefixlen); if ((rc = sa_addr_create(&(ctx->pacl[i].saa))) != SA_OK) { log1(ctx, ERROR, "option --acl, create address (internal) failed with \"%s\"", sa_error(rc)); throw(0,0,0); } if ((rc = sa_addr_u2a(ctx->pacl[i].saa, "inet://%s:0", ctx->pacl[i].acl)) != SA_OK) { log2(ctx, ERROR, "option --acl, parsing address (%s) failed with \"%s\"", ctx->pacl[i].acl, sa_error(rc)); throw(0,0,0); } } ctx->nacl = i; } catch (ex) rethrow; /* --bind SINGLE */ try { if ( (val_get(ctx->val, "option.bind", &ov) != VAL_OK) || (ov->ndata != 1) || (ov->data.s == NULL) ) throw(0,0,0); log1(ctx, TRACE, "--bind = \"%s\"", ov->data.s); /* dash means stdio */ if (strcmp(ov->data.s, "-") != 0) { //FIXME does this work with popt()? if ((rc = sa_create(&ctx->saServerbind)) != SA_OK) { log1(ctx, ERROR, "option --bind, creating TCP socket (internal) failed with \"%s\"", sa_error(rc)); throw(0,0,0); } if ((rc = sa_addr_create(&ctx->saaServerbind)) != SA_OK) { log1(ctx, ERROR, "option --bind, create address (internal) failed with \"%s\"", sa_error(rc)); throw(0,0,0); } /* slash means UNIX socket */ if (ov->data.s[0] == '/') { char *cpPath; char *cpPerm; int nPerm; int n; int i; cpPath = strdup(optarg); cpPerm = NULL; nPerm = -1; if ((cpPerm = strrchr(cpPath, ':')) != NULL) { *cpPerm++ = '\0'; nPerm = 0; for (i = 0; i < 4 && cpPerm[i] != '\0'; i++) { if (!isdigit((int)cpPerm[i])) { nPerm = -1; break; } n = cpPerm[i] - '0'; if (n > 7) { nPerm = -1; break; } nPerm = ((nPerm << 3) | n); } if (nPerm == -1 || cpPerm[i] != '\0') { log1(ctx, ERROR, "option --bind, invalid permissions \"%s\"", cpPerm); throw(0,0,0); } } if ((rc = sa_addr_u2a(ctx->saaServerbind, "unix:%s", cpPath)) != SA_OK) { log2(ctx, ERROR, "option --bind, parsing alternate IO guessing UNIX socket (%s) failed with \"%s\"", cpPath, sa_error(rc)); throw(0,0,0); } if ((rc = sa_bind(ctx->saServerbind, ctx->saaServerbind)) != SA_OK) { log2(ctx, ERROR, "option --bind, bind (%s) failed with \"%s\"", cpPath, sa_error(rc)); throw(0,0,0); } if (nPerm != -1) { if (chmod(cpPath, nPerm) == -1) { log3(ctx, ERROR, "option --bind, chmod (%s, 0%o) failed with \"%s\"", cpPath, nPerm, strerror(errno)); throw(0,0,0); } } if (getuid() == 0 && getuid() != ctx->option_uid) { if (chown(cpPath, ctx->option_uid, -1) == -1) { log3(ctx, ERROR, "option --bind, chown (%s, %d) failed with \"%s\"", cpPath, ctx->option_uid, strerror(errno)); throw(0,0,0); } } free(cpPath); } /* otherwise assume INET socket */ else { if ((rc = sa_addr_u2a(ctx->saaServerbind, "inet://%s", ov->data.s)) != SA_OK) { log2(ctx, ERROR, "option --bind, parsing alternate IO guessing INET socket (%s) failed with \"%s\"", ov->data.s, sa_error(rc)); throw(0,0,0); } if ((rc = sa_bind(ctx->saServerbind, ctx->saaServerbind)) != SA_OK) { log2(ctx, ERROR, "option --bind, bind (%s) failed with \"%s\"", ov->data.s, sa_error(rc)); throw(0,0,0); } } /* for either sockets */ if ((rc = sa_listen(ctx->saServerbind, -1)) != SA_OK) { log2(ctx, ERROR, "option --bind, listen (%s) failed with \"%s\"", ov->data.s, sa_error(rc)); throw(0,0,0); } } } catch (ex) rethrow; CUS: return; }