/* This file is part of gacopyz.
Copyright (C) 2006-2020 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, 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 . */
#include
int
gacopyz_setpriv (SMFICTX *ctx, void *data)
{
if (!ctx)
return MI_FAILURE;
ctx->privdata = data;
return MI_SUCCESS;
}
void *
gacopyz_getpriv (SMFICTX *ctx)
{
if (!ctx)
return NULL;
return ctx->privdata;
}
void *
gacopyz_getclosure(SMFICTX *ctx)
{
if (!ctx)
return NULL;
return ctx->closure;
}
int
gacopyz_server_sockname(SMFICTX *ctx,
milter_sockaddr_t *name, socklen_t *namelen)
{
if (!ctx)
return EINVAL;
return getsockname(ctx->sd, (struct sockaddr*) name, namelen);
}
int
gacopyz_client_sockname(SMFICTX *ctx,
milter_sockaddr_t *name, socklen_t *namelen)
{
if (!ctx)
return EINVAL;
*name = ctx->addr;
*namelen = ctx->addrlen;
return 0;
}
const char *
gacopyz_getsymval(SMFICTX *ctx, const char *name)
{
int i;
int len;
if (!ctx || !name)
return NULL;
if (name[0] == '{')
name++;
len = strlen(name);
if (len == 0)
return NULL;
if (name[len-1] == '}')
len--;
for (i = gacopyz_stage_max - 1; i >= 0; i--) {
if (ctx->macros[i].argv) {
char **p;
for (p = ctx->macros[i].argv; *p; p += 2) {
if (strlen(*p) == len
&& memcmp(*p, name, len) == 0)
return *++p;
}
}
}
return NULL;
}
static int
enhanced_code_p(const char *p)
{
int count = 0;
if (!((*p == '2' || *p == '4' || *p == '5') && p[1] == '.'))
return 0;
while (*p) {
int i;
for (i = 0; isascii(*p) && isdigit(*p); i++, p++) {
if (i == 3)
return 0;
}
if (*p) {
if (*p++ != '.')
return 0;
}
count++;
}
return count == 3;
}
static int
format_message(char **buf, const char *rcode, const char *xcode,
const char *message)
{
size_t xcodelen = 0, pfxlen, len, numlines, i;
const char *p;
char *q;
pfxlen = strlen(rcode) + 1;
if (xcode != NULL)
pfxlen += (xcodelen = strlen(xcode)) + 1;
numlines = 0;
len = 0;
for (p = message; *p; p++) {
if (*p == '\r') {
if (p[1] == '\n')
p++;
numlines++;
} else if (*p == '\n')
numlines++;
else if (*p == '%')
len += 2;
else
len++;
}
if (p > message && p[-1] != '\n')
numlines++;
len += numlines * (pfxlen + 2);
*buf = malloc(len + 1);
if (!*buf)
return MI_FAILURE;
q = *buf;
p = message;
for (i = 1; i <= numlines; i++) {
strcpy(q, rcode);
q += 3;
if (xcode) {
*q++ = (i < numlines) ? '-' : ' ';
memcpy(q, xcode, xcodelen);
q += xcodelen;
*q++ = ' ';
} else
*q++ = (i < numlines) ? '-' : ' ';
while (*p && !(*p == '\r' || *p == '\n')) {
if ((*q++ = *p++) == '%')
*q++ = '%';
}
if (numlines > 1 && i != numlines) {
*q++ = '\r';
*q++ = '\n';
}
if (*p == '\r')
p++;
if (*p == '\n')
p++;
}
*q = 0;
return MI_SUCCESS;
}
#define MLBUF_INIT_ALLOC 512
#define MLBUF_INCR_ALLOC 128
int
_gacopyz_setmlreply_va(SMFICTX *ctx, size_t max, const char *rcode,
const char *xcode, va_list ap)
{
size_t bufsize = MLBUF_INIT_ALLOC, i, lasti, numlines;
char *buf, *p;
size_t xcodelen, pfxlen;
if (rcode == NULL || ctx == NULL)
return MI_FAILURE;
if ((rcode[0] != '4' && rcode[0] != '5') ||
!isascii(rcode[1]) || !isdigit(rcode[1]) ||
!isascii(rcode[2]) || !isdigit(rcode[2]))
return MI_FAILURE;
pfxlen = strlen(rcode);
if (strlen(rcode) != 3)
return MI_FAILURE;
if (xcode != NULL) {
if (!enhanced_code_p(xcode))
return MI_FAILURE;
} else {
if (rcode[0] == '4')
xcode = "4.0.0";
else
xcode = "5.0.0";
}
if (xcode != NULL)
pfxlen += (xcodelen = strlen(xcode)) + 1;
free(ctx->reply);
ctx->reply = NULL;
buf = malloc(bufsize);
if (!buf)
return MI_FAILURE;
i = 0;
lasti = 0;
numlines = 0;
for (p = va_arg(ap, char*);
p && (max == 0 || numlines < max); numlines++) {
size_t s = strlen(p) + pfxlen + 2;
if (strpbrk(p, "\r\n") != NULL)
break;
if (i + s > bufsize) {
char *newbuf;
size_t delta = (i + s - bufsize +
MLBUF_INCR_ALLOC) /
MLBUF_INCR_ALLOC;
bufsize += delta * MLBUF_INCR_ALLOC;
newbuf = realloc(buf, bufsize);
if (!newbuf) {
free(buf);
return MI_FAILURE;
}
buf = newbuf;
}
strcpy(buf + i, rcode);
i += 3;
buf[i] = '-';
lasti= i++;
memcpy(buf + i, xcode, xcodelen);
i += xcodelen;
buf[i++] = ' ';
strcpy(buf + i, p);
i += strlen(p);
p = va_arg(ap, char*);
if (p) {
buf[i++] = '\r';
buf[i++] = '\n';
}
}
buf[i] = 0;
buf[lasti] = ' ';
ctx->reply = buf;
return MI_SUCCESS;
}
int
gacopyz_setreply(SMFICTX *ctx, const char *rcode, const char *xcode,
const char *message)
{
if (rcode == NULL || ctx == NULL)
return MI_FAILURE;
if ((rcode[0] != '4' && rcode[0] != '5') ||
!isascii(rcode[1]) || !isdigit(rcode[1]) ||
!isascii(rcode[2]) || !isdigit(rcode[2]))
return MI_FAILURE;
if (strlen(rcode) != 3)
return MI_FAILURE;
if (xcode != NULL) {
if (!enhanced_code_p(xcode))
return MI_FAILURE;
}
free(ctx->reply);
ctx->reply = NULL;
return format_message(&ctx->reply, rcode, xcode, message);
}
int
gacopyz_setmlreply_va(SMFICTX *ctx, const char *rcode, const char *xcode,
va_list ap)
{
return _gacopyz_setmlreply_va(ctx, 0, rcode, xcode, ap);
}
int
gacopyz_setmlreply_v(SMFICTX *ctx, const char *rcode, const char *xcode, ...)
{
int rc;
va_list ap;
va_start(ap, xcode);
rc = gacopyz_setmlreply_va(ctx, rcode, xcode, ap);
va_end(ap);
return rc;
}