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