1 /* This file is part of gacopyz.
2    Copyright (C) 2006-2021 Sergey Poznyakoff
3 
4    This program is free software; you can redistribute it and/or modify
5    it under the terms of the GNU General Public License as published by
6    the Free Software Foundation; either version 3, or (at your option)
7    any later version.
8 
9    This program is distributed in the hope that it will be useful,
10    but WITHOUT ANY WARRANTY; without even the implied warranty of
11    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12    GNU General Public License for more details.
13 
14    You should have received a copy of the GNU General Public License
15    along with this program.  If not, see <http://www.gnu.org/licenses/>. */
16 
17 #include <gacopyz_priv.h>
18 
19 int
gacopyz_setpriv(SMFICTX * ctx,void * data)20 gacopyz_setpriv (SMFICTX *ctx, void *data)
21 {
22 	if (!ctx)
23 		return MI_FAILURE;
24 	ctx->privdata = data;
25 	return MI_SUCCESS;
26 }
27 
28 void *
gacopyz_getpriv(SMFICTX * ctx)29 gacopyz_getpriv (SMFICTX *ctx)
30 {
31 	if (!ctx)
32 		return NULL;
33 	return ctx->privdata;
34 }
35 
36 void *
gacopyz_getclosure(SMFICTX * ctx)37 gacopyz_getclosure(SMFICTX *ctx)
38 {
39 	if (!ctx)
40 		return NULL;
41 	return ctx->closure;
42 }
43 
44 int
gacopyz_server_sockname(SMFICTX * ctx,milter_sockaddr_t * name,socklen_t * namelen)45 gacopyz_server_sockname(SMFICTX *ctx,
46 			milter_sockaddr_t *name, socklen_t *namelen)
47 {
48 	if (!ctx)
49 		return EINVAL;
50 	return getsockname(ctx->sd, (struct sockaddr*) name, namelen);
51 }
52 
53 int
gacopyz_client_sockname(SMFICTX * ctx,milter_sockaddr_t * name,socklen_t * namelen)54 gacopyz_client_sockname(SMFICTX *ctx,
55 			milter_sockaddr_t *name, socklen_t *namelen)
56 {
57 	if (!ctx)
58 		return EINVAL;
59 	*name = ctx->addr;
60 	*namelen = ctx->addrlen;
61 	return 0;
62 }
63 
64 const char *
gacopyz_getsymval(SMFICTX * ctx,const char * name)65 gacopyz_getsymval(SMFICTX *ctx, const char *name)
66 {
67 	int i;
68 	int len;
69 
70         if (!ctx || !name)
71 		return NULL;
72 
73 	if (name[0] == '{')
74 		name++;
75 	len = strlen(name);
76 	if (len == 0)
77 		return NULL;
78 	if (name[len-1] == '}')
79 		len--;
80 
81 	for (i = gacopyz_stage_max - 1; i >= 0; i--) {
82 		if (ctx->macros[i].argv) {
83 			char **p;
84 
85 			for (p = ctx->macros[i].argv; *p; p += 2) {
86 				if (strlen(*p) == len
87 				    && memcmp(*p, name, len) == 0)
88 					return *++p;
89 			}
90 		}
91 	}
92 	return NULL;
93 }
94 
95 static int
enhanced_code_p(const char * p)96 enhanced_code_p(const char *p)
97 {
98 	int count = 0;
99 
100 	if (!((*p == '2' || *p == '4' || *p == '5') && p[1] == '.'))
101 		return 0;
102 
103 	while (*p) {
104 		int i;
105 		for (i = 0; isascii(*p) && isdigit(*p); i++, p++) {
106 			if (i == 3)
107 				return 0;
108 		}
109 		if (*p) {
110 			if (*p++ != '.')
111 				return 0;
112 		}
113 		count++;
114 	}
115 	return count == 3;
116 }
117 
118 static int
format_message(char ** buf,const char * rcode,const char * xcode,const char * message)119 format_message(char **buf, const char *rcode, const char *xcode,
120 	       const char *message)
121 {
122 	size_t xcodelen = 0, pfxlen, len, numlines, i;
123 	const char *p;
124 	char *q;
125 
126 	pfxlen = strlen(rcode) + 1;
127         if (xcode != NULL)
128 		pfxlen += (xcodelen = strlen(xcode)) + 1;
129 
130 	numlines = 0;
131 	len = 0;
132 	for (p = message; *p; p++) {
133 		if (*p == '\r') {
134 			if (p[1] == '\n')
135 				p++;
136 			numlines++;
137 		} else if (*p == '\n')
138 			numlines++;
139 		else if (*p == '%')
140 			len += 2;
141 		else
142 			len++;
143 	}
144 
145 	if (p > message && p[-1] != '\n')
146 		numlines++;
147 
148 	len += numlines * (pfxlen + 2);
149 
150 	*buf = malloc(len + 1);
151 	if (!*buf)
152 		return MI_FAILURE;
153 
154 	q = *buf;
155 	p = message;
156 	for (i = 1; i <= numlines; i++) {
157 		strcpy(q, rcode);
158 		q += 3;
159 		if (xcode) {
160 			*q++ = (i < numlines) ? '-' : ' ';
161 			memcpy(q, xcode, xcodelen);
162 			q += xcodelen;
163 			*q++ = ' ';
164 		} else
165 			*q++ = (i < numlines) ? '-' : ' ';
166 		while (*p && !(*p == '\r' || *p == '\n')) {
167 			if ((*q++ = *p++) == '%')
168 				*q++ = '%';
169 		}
170 		if (numlines > 1 && i != numlines) {
171 			*q++ = '\r';
172 			*q++ = '\n';
173 		}
174 		if (*p == '\r')
175 			p++;
176 		if (*p == '\n')
177 			p++;
178 	}
179 	*q = 0;
180 	return MI_SUCCESS;
181 }
182 
183 #define MLBUF_INIT_ALLOC 512
184 #define MLBUF_INCR_ALLOC 128
185 int
_gacopyz_setmlreply_va(SMFICTX * ctx,size_t max,const char * rcode,const char * xcode,va_list ap)186 _gacopyz_setmlreply_va(SMFICTX *ctx, size_t max, const char *rcode,
187 			const char *xcode, va_list ap)
188 {
189 	size_t bufsize = MLBUF_INIT_ALLOC, i, lasti, numlines;
190 	char *buf, *p;
191 	size_t xcodelen, pfxlen;
192 
193 	if (rcode == NULL || ctx == NULL)
194 		return MI_FAILURE;
195 
196         if ((rcode[0] != '4' && rcode[0] != '5') ||
197 	    !isascii(rcode[1]) || !isdigit(rcode[1]) ||
198 	    !isascii(rcode[2]) || !isdigit(rcode[2]))
199 		return MI_FAILURE;
200 
201 	pfxlen = strlen(rcode);
202 
203         if (strlen(rcode) != 3)
204 		return MI_FAILURE;
205 
206         if (xcode != NULL) {
207 		if (!enhanced_code_p(xcode))
208 			return MI_FAILURE;
209 	} else {
210 		if (rcode[0] == '4')
211 			xcode = "4.0.0";
212 		else
213 			xcode = "5.0.0";
214 	}
215         if (xcode != NULL)
216 		pfxlen += (xcodelen = strlen(xcode)) + 1;
217 
218 	free(ctx->reply);
219 	ctx->reply = NULL;
220 	buf = malloc(bufsize);
221 	if (!buf)
222 		return MI_FAILURE;
223 	i = 0;
224 	lasti = 0;
225 	numlines = 0;
226 	for (p = va_arg(ap, char*);
227 	     p && (max == 0 || numlines < max); numlines++) {
228 		size_t s = strlen(p) + pfxlen + 2;
229 		if (strpbrk(p, "\r\n") != NULL)
230 			break;
231 		if (i + s > bufsize) {
232 			char *newbuf;
233 			size_t delta = (i + s - bufsize +
234 					MLBUF_INCR_ALLOC) /
235 				        MLBUF_INCR_ALLOC;
236 			bufsize += delta * MLBUF_INCR_ALLOC;
237 			newbuf = realloc(buf, bufsize);
238 			if (!newbuf) {
239 				free(buf);
240 				return MI_FAILURE;
241 			}
242 			buf = newbuf;
243 		}
244 
245 		strcpy(buf + i, rcode);
246 		i += 3;
247 		buf[i] = '-';
248 		lasti= i++;
249 		memcpy(buf + i, xcode, xcodelen);
250 		i += xcodelen;
251 		buf[i++] = ' ';
252 		strcpy(buf + i, p);
253 		i += strlen(p);
254 
255 		p = va_arg(ap, char*);
256 		if (p) {
257 			buf[i++] = '\r';
258 			buf[i++] = '\n';
259 		}
260 	}
261 	buf[i] = 0;
262 	buf[lasti] = ' ';
263 
264 	ctx->reply = buf;
265 
266 	return MI_SUCCESS;
267 }
268 
269 int
gacopyz_setreply(SMFICTX * ctx,const char * rcode,const char * xcode,const char * message)270 gacopyz_setreply(SMFICTX *ctx, const char *rcode, const char *xcode,
271 		 const char *message)
272 {
273 	if (rcode == NULL || ctx == NULL)
274 		return MI_FAILURE;
275 
276         if ((rcode[0] != '4' && rcode[0] != '5') ||
277 	    !isascii(rcode[1]) || !isdigit(rcode[1]) ||
278 	    !isascii(rcode[2]) || !isdigit(rcode[2]))
279 		return MI_FAILURE;
280 
281         if (strlen(rcode) != 3)
282 		return MI_FAILURE;
283 
284         if (xcode != NULL) {
285 		if (!enhanced_code_p(xcode))
286 			return MI_FAILURE;
287 	}
288 
289 	free(ctx->reply);
290 	ctx->reply = NULL;
291 	return format_message(&ctx->reply, rcode, xcode, message);
292 }
293 
294 
295 int
gacopyz_setmlreply_va(SMFICTX * ctx,const char * rcode,const char * xcode,va_list ap)296 gacopyz_setmlreply_va(SMFICTX *ctx, const char *rcode, const char *xcode,
297 		      va_list ap)
298 {
299 	return _gacopyz_setmlreply_va(ctx, 0, rcode, xcode, ap);
300 }
301 
302 int
gacopyz_setmlreply_v(SMFICTX * ctx,const char * rcode,const char * xcode,...)303 gacopyz_setmlreply_v(SMFICTX *ctx, const char *rcode, const char *xcode, ...)
304 {
305 	int rc;
306 	va_list ap;
307 
308 	va_start(ap, xcode);
309 	rc = gacopyz_setmlreply_va(ctx, rcode, xcode, ap);
310 	va_end(ap);
311 
312 	return rc;
313 }
314 
315