1 /* Copyright (C) 2001 James Niemira (niemira@colltech.com, urmane@urmane.org)
2 * Portions Copyright (C) 2001,2002,2003,2004,2005,2006,2007
3 * Stuart Gathman (stuart@gathman.org)
4 *
5 * This program is free software: you can redistribute it and/or modify it
6 * under the terms of the GNU General Public License as published by the
7 * Free Software Foundation, either version 2 of the License, or (at your
8 * option) any later version.
9 *
10 * This program is distributed in the hope that it will be useful, but
11 * WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 * General Public License for more details.
14 *
15 * You should have received a copy of the GNU General Public License along
16 * with this program; if not, write to the Free Software Foundation, Inc.,
17 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
18 *
19 * milterContext object and thread interface contributed by
20 * Stuart D. Gathman <stuart@bmsi.com>
21 */
22
23 /* This is a Python extension to use Sendmail's libmilter functionality.
24 It is built using distutils. To install it:
25
26 # python setup.py install
27
28 For additional options:
29
30 $ python setup.py help
31
32 You may need to add additional libraries to setup.py. For instance,
33 Solaris2.6 requires
34
35 libraries=["milter","smutil","resolv"]
36
37 */
38
39 #ifndef MAX_ML_REPLY
40 #define MAX_ML_REPLY 32
41 #endif
42 #if MAX_ML_REPLY != 1 && MAX_ML_REPLY != 32 && MAX_ML_REPLY != 11
43 #error MAX_ML_REPLY must be 1 or 11 or 32
44 #endif
45 #define _FFR_MULTILINE (MAX_ML_REPLY > 1)
46
47 //#include <pthread.h> // shouldn't be needed - use Python API
48 #include <Python.h> // Python C API
49 #include <libmilter/mfapi.h> // libmilter API
50 #include <netinet/in.h> // socket API
51
52
53 /* See if we have IPv4 and/or IPv6 support in this OS and in
54 * libmilter. We need to make several macro tests because some OS's
55 * may define some if IPv6 is only partially supported, and we may
56 * have a sendmail without IPv4 (compiled for IPv6-only).
57 */
58 #ifdef SMFIA_INET
59 #ifdef AF_INET
60 #define HAVE_IPV4_SUPPORT /* use this for #ifdef's later on */
61 #endif
62 #endif
63
64 #ifdef SMFIA_INET6
65 #ifdef AF_INET6
66 #ifdef IN6ADDR_ANY_INIT
67 #ifdef INET6_ADDRSTRLEN
68 #define HAVE_IPV6_SUPPORT /* use this for #ifdef's later on */
69 /* Now see if it supports the RFC-2553 socket's API spec. Early
70 * IPv6 "prototype" implementations existed before the RFC was
71 * published. Unfortunately I know of no good way to do this
72 * other than with OS-specific tests.
73 */
74 #if defined(__FreeBSD__) || defined(__linux__) || defined(__sun__) || defined(__DragonFly__) || \
75 defined(__GLIBC__) || (defined(__APPLE__) && defined(__MACH__))
76 #define HAVE_IPV6_RFC2553
77 #include <arpa/inet.h>
78 #endif
79 #ifdef __HPUX
80 /* only HP-UX 11.1 or greater supports IPv6 */
81 #define HAVE_IPV6_RFC2553
82 #endif
83
84 #endif
85 #endif
86 #endif
87 #endif
88
89 enum callbacks {
90 CONNECT,HELO,ENVFROM,ENVRCPT,HEADER,EOH,BODY,EOM,ABORT,CLOSE,
91 #ifdef SMFIS_ALL_OPTS
92 UNKNOWN,DATA,NEGOTIATE,
93 #endif
94 NUMCALLBACKS
95 };
96
97 #define connect_callback callback[CONNECT].cb
98 #define helo_callback callback[HELO].cb
99 #define envfrom_callback callback[ENVFROM].cb
100 #define envrcpt_callback callback[ENVRCPT].cb
101 #define header_callback callback[HEADER].cb
102 #define eoh_callback callback[EOH].cb
103 #define body_callback callback[BODY].cb
104 #define eom_callback callback[EOM].cb
105 #define abort_callback callback[ABORT].cb
106 #define close_callback callback[CLOSE].cb
107 #define unknown_callback callback[UNKNOWN].cb
108 #define data_callback callback[DATA].cb
109 #define negotiate_callback callback[NEGOTIATE].cb
110
111 /* Yes, these are static. If you need multiple different callbacks,
112 it's cleaner to use multiple filters, or convert to OO method calls. */
113
114 static struct MilterCallback {
115 PyObject *cb;
116 const char *name;
117 } callback[NUMCALLBACKS+1] = {
118 { NULL ,"connect" },
119 { NULL ,"helo" },
120 { NULL ,"envfrom" },
121 { NULL ,"envrcpt" },
122 { NULL ,"header" },
123 { NULL ,"eoh" },
124 { NULL ,"body" },
125 { NULL ,"eom" },
126 { NULL ,"abort" },
127 { NULL ,"close" },
128 #ifdef SMFIS_ALL_OPTS
129 { NULL ,"unknown" },
130 { NULL ,"data" },
131 { NULL ,"negotiate" },
132 #endif
133 { NULL , NULL }
134 };
135
136 #if PY_MAJOR_VERSION >= 3
137 static struct smfiDesc description; /* forward declaration */
138 static PyTypeObject milter_ContextType;
139 #else
140 staticforward struct smfiDesc description; /* forward declaration */
141 staticforward PyTypeObject milter_ContextType;
142 #endif
143
144 static PyObject *MilterError;
145 /* The interpreter instance that called milter.main */
146 static PyInterpreterState *interp;
147 typedef struct {
148 unsigned int contextNew;
149 unsigned int contextDel;
150 } milter_Diag;
151
152 static milter_Diag diag;
153
154 typedef struct {
155 PyObject_HEAD
156 SMFICTX *ctx; /* libmilter thread state */
157 PyObject *priv; /* user python object */
158 PyThreadState *t; /* python thread state */
159 } milter_ContextObject;
160
161 /* Return a borrowed reference to the python Context. Called by callbacks
162 invoked by libmilter. Create a new Context if needed. The new
163 Python Context is owned by the SMFICTX. The python interpreter is locked on
164 successful return, otherwise not. */
165 static milter_ContextObject *
_get_context(SMFICTX * ctx)166 _get_context(SMFICTX *ctx) {
167 milter_ContextObject *self = smfi_getpriv(ctx);
168 if (self) {
169 /* Can't pass on exception since we are called from libmilter */
170 if (self->ctx != ctx) return NULL;
171 PyEval_AcquireThread(self->t);
172 }
173 else {
174 PyThreadState *t = PyThreadState_New(interp);
175 if (t == NULL) return NULL;
176 PyEval_AcquireThread(t); /* lock interp */
177 self = PyObject_New(milter_ContextObject,&milter_ContextType);
178 if (!self) {
179 /* Report and clear exception since we are called from libmilter */
180 if (PyErr_Occurred()) {
181 PyErr_Print();
182 PyErr_Clear();
183 }
184 PyThreadState_Clear(t);
185 PyEval_ReleaseThread(t);
186 PyThreadState_Delete(t);
187 return NULL;
188 }
189 ++diag.contextNew;
190 self->t = t;
191 self->ctx = ctx;
192 Py_INCREF(Py_None);
193 self->priv = Py_None; /* User Python object */
194 smfi_setpriv(ctx, self);
195 }
196 return self;
197 }
198
199 /* Find the SMFICTX from a Python Context. Called by context methods invoked
200 from python. The interpreter must be locked. */
201 static SMFICTX *
_find_context(PyObject * c)202 _find_context(PyObject *c) {
203 SMFICTX *ctx = NULL;
204 if (c->ob_type == &milter_ContextType) {
205 milter_ContextObject *self = (milter_ContextObject *)c;
206 ctx = self->ctx;
207 if (ctx != NULL && smfi_getpriv(ctx) != self)
208 ctx = NULL;
209 }
210 if (ctx == NULL)
211 PyErr_SetString(MilterError, "bad context");
212 return ctx;
213 }
214
215 static void
milter_Context_dealloc(PyObject * s)216 milter_Context_dealloc(PyObject *s) {
217 milter_ContextObject *self = (milter_ContextObject *)s;
218 SMFICTX *ctx = self->ctx;
219 if (ctx) {
220 /* Should never happen. If libmilter closes SMFICTX first, then
221 ctx will be 0. Otherwise, SMFICTX will still hold a reference
222 to the ContextObject. But if it does, make sure SMFICTX can't
223 reach us anymore. */
224 smfi_setpriv(ctx,0);
225 }
226 Py_DECREF(self->priv);
227 PyObject_DEL(self);
228 ++diag.contextDel;
229 }
230
231 /* Throw an exception if an smfi call failed, otherwise return PyNone. */
232 static PyObject *
_generic_return(int val,char * errstr)233 _generic_return(int val, char *errstr) {
234 if (val == MI_SUCCESS) {
235 Py_INCREF(Py_None);
236 return Py_None;
237 } else {
238 PyErr_SetString(MilterError, errstr);
239 return NULL;
240 }
241 }
242
243 static PyObject *
_thread_return(PyThreadState * t,int val,char * errstr)244 _thread_return(PyThreadState *t,int val,char *errstr) {
245 PyEval_RestoreThread(t); /* lock interpreter again */
246 return _generic_return(val,errstr);
247 }
248
249 static const char milter_set_flags__doc__[] =
250 "set_flags(int) -> None\n\
251 Set flags for filter capabilities; OR of one or more of:\n\
252 ADDHDRS - filter may add headers\n\
253 CHGBODY - filter may replace body\n\
254 CHGFROM - filter may replace body\n\
255 ADDRCPT - filter may add recipients\n\
256 DELRCPT - filter may delete recipients\n\
257 CHGHDRS - filter may change/delete headers";
258
259 static PyObject *
milter_set_flags(PyObject * self,PyObject * args)260 milter_set_flags(PyObject *self, PyObject *args) {
261 if (!PyArg_ParseTuple(args, "i:set_flags", &description.xxfi_flags))
262 return NULL;
263 Py_INCREF(Py_None);
264 return Py_None;
265 }
266
267 static PyObject *
generic_set_callback(PyObject * args,char * t,PyObject ** cb)268 generic_set_callback(PyObject *args,char *t,PyObject **cb) {
269 PyObject *callback;
270 PyObject *oldval;
271
272 if (!PyArg_ParseTuple(args, t, &callback)) return NULL;
273 if (callback == Py_None)
274 callback = 0;
275 else {
276 if (!PyCallable_Check(callback)) {
277 PyErr_SetString(PyExc_TypeError, "callback parameter must be callable");
278 return NULL;
279 }
280 Py_INCREF(callback);
281 }
282 oldval = *cb;
283 *cb = callback;
284 if (oldval)
285 return oldval;
286 Py_INCREF(Py_None);
287 return Py_None;
288 }
289
290 static const char milter_set_connect_callback__doc__[] =
291 "set_connect_callback(Function) -> None\n\
292 Sets the Python function invoked when a connection is made to sendmail.\n\
293 Function takes args (ctx, hostname, integer, hostaddr) -> int\n\
294 ctx -> milterContext for connection, also on remaining callbacks\n\
295 hostname -> String - the connecting remote hostname\n\
296 integer -> int - the protocol family, one of socket.AF_* values\n\
297 hostaddr -> ? - the connecting host address in format used by socket:\n\
298 for unix -> pathname - like '/tmp/sockets/s24823'\n\
299 for inet -> (ipaddress, port) - like ('10.1.2.3',3701)\n\
300 for inet6 -> (ip6address, port, flowlabel, scope) - like ('fec0:0:0:2::4e', 3701, 0, 5)\n\
301 \n\
302 The return value on this and remaining callbacks should be one of:\n\
303 CONTINUE - continue processing\n\
304 REJECT - sendmail refuses to accept any more data for message\n\
305 ACCEPT - sendmail accepts the message\n\
306 DISCARD - sendmail accepts the message and discards it\n\
307 TEMPFAIL - milter problem, sendmail will try again later\n\
308 \n\
309 A python exception encountered in a callback will return TEMPFAIL.";
310
311 static PyObject *
milter_set_connect_callback(PyObject * self,PyObject * args)312 milter_set_connect_callback(PyObject *self, PyObject *args) {
313 return generic_set_callback(args,
314 "O:set_connect_callback", &connect_callback);
315 }
316
317 static const char milter_set_helo_callback__doc__[] =
318 "set_helo_callback(Function) -> None\n\
319 Sets the Python function invoked upon SMTP HELO.\n\
320 Function takes args (ctx, hostname) -> int\n\
321 hostname -> String - the name given with the helo command.";
322
323 static PyObject *
milter_set_helo_callback(PyObject * self,PyObject * args)324 milter_set_helo_callback(PyObject *self, PyObject *args) {
325 return generic_set_callback(args, "O:set_helo_callback", &helo_callback);
326 }
327
328 static const char milter_set_envfrom_callback__doc__[] =
329 "set_envfrom_callback(Function) -> None\n\
330 Sets the Python function invoked on envelope from.\n\
331 Function takes args (ctx, from, *str) -> int\n\
332 from -> sender\n\
333 str -> Tuple of additional parameters defined by ESMTP.";
334
335 static PyObject *
milter_set_envfrom_callback(PyObject * self,PyObject * args)336 milter_set_envfrom_callback(PyObject *self, PyObject *args) {
337 return generic_set_callback(args, "O:set_envfrom_callback",
338 &envfrom_callback);
339 }
340
341 static const char milter_set_envrcpt_callback__doc__[] =
342 "set_envrcpt_callback(Function) -> None\n\
343 Sets the Python function invoked on each envelope recipient.\n\
344 Function takes args (ctx, rcpt, *str) -> int\n\
345 tcpt -> string - recipient\n\
346 str -> Tuple of additional parameters defined by ESMTP.";
347
348 static PyObject *
milter_set_envrcpt_callback(PyObject * self,PyObject * args)349 milter_set_envrcpt_callback(PyObject *self, PyObject *args) {
350 return generic_set_callback(args, "O:set_envrcpt_callback",
351 &envrcpt_callback);
352 }
353
354 static const char milter_set_header_callback__doc__[] =
355 "set_header_callback(Function) -> None\n\
356 Sets the Python function invoked on each message header.\n\
357 Function takes args (ctx, field, value) ->int\n\
358 field -> String - the header\n\
359 value -> String - the header's value";
360
361 static PyObject *
milter_set_header_callback(PyObject * self,PyObject * args)362 milter_set_header_callback(PyObject *self, PyObject *args) {
363 return generic_set_callback(args, "O:set_header_callback",
364 &header_callback);
365 }
366
367 static const char milter_set_eoh_callback__doc__[] =
368 "set_eoh_callback(Function) -> None\n\
369 Sets the Python function invoked at end of header.\n\
370 Function takes args (ctx) -> int";
371
372 static PyObject *
milter_set_eoh_callback(PyObject * self,PyObject * args)373 milter_set_eoh_callback(PyObject *self, PyObject *args) {
374 return generic_set_callback(args, "O:set_eoh_callback", &eoh_callback);
375 }
376
377 static const char milter_set_body_callback__doc__[] =
378 "set_body_callback(Function) -> None\n\
379 Sets the Python function invoked for each body chunk. There may\n\
380 be multiple body chunks passed to the filter. End-of-lines are\n\
381 represented as received from SMTP (normally Carriage-Return/Line-Feed).\n\
382 Function takes args (ctx, chunk) -> int\n\
383 chunk -> bytes - body data";
384
385 static PyObject *
milter_set_body_callback(PyObject * self,PyObject * args)386 milter_set_body_callback(PyObject *self, PyObject *args) {
387 return generic_set_callback(args, "O:set_body_callback", &body_callback);
388 }
389
390 static const char milter_set_eom_callback__doc__[] =
391 "set_eom_callback(Function) -> None\n\
392 Sets the Python function invoked at end of message.\n\
393 This routine is the only place where special operations\n\
394 such as modifying the message header, body, or\n\
395 envelope can be used.\n\
396 Function takes args (ctx) -> int";
397
398 static PyObject *
milter_set_eom_callback(PyObject * self,PyObject * args)399 milter_set_eom_callback(PyObject *self, PyObject *args) {
400 return generic_set_callback(args, "O:set_eom_callback", &eom_callback);
401 }
402
403 static const char milter_set_abort_callback__doc__[] =
404 "set_abort_callback(Function) -> None\n\
405 Sets the Python function invoked if message is aborted\n\
406 outside of the control of the filter, for example,\n\
407 if the SMTP sender issues an RSET command. If the \n\
408 abort callback is called, the eom callback will not be\n\
409 called and vice versa.\n\
410 Function takes args (ctx) -> int";
411
412 static PyObject *
milter_set_abort_callback(PyObject * self,PyObject * args)413 milter_set_abort_callback(PyObject *self, PyObject *args) {
414 return generic_set_callback(args, "O:set_abort_callback", &abort_callback);
415 }
416
417 static const char milter_set_close_callback__doc__[] =
418 "set_close_callback(Function) -> None\n\
419 Sets the Python function invoked at end of the connection. This\n\
420 is called on close even if the previous mail transaction was aborted.\n\
421 Function takes args (ctx) -> int";
422
423 static PyObject *
milter_set_close_callback(PyObject * self,PyObject * args)424 milter_set_close_callback(PyObject *self, PyObject *args) {
425 return generic_set_callback(args, "O:set_close_callback", &close_callback);
426 }
427
428 static int exception_policy = SMFIS_TEMPFAIL;
429
430 static const char milter_set_exception_policy__doc__[] =
431 "set_exception_policy(i) -> None\n\
432 Sets the policy for untrapped Python exceptions during a callback.\n\
433 Must be one of TEMPFAIL,REJECT,CONTINUE";
434
435 static PyObject *
milter_set_exception_policy(PyObject * self,PyObject * args)436 milter_set_exception_policy(PyObject *self, PyObject *args) {
437 int i;
438 if (!PyArg_ParseTuple(args, "i:set_exception_policy", &i))
439 return NULL;
440 switch (i) {
441 case SMFIS_REJECT: case SMFIS_TEMPFAIL:
442 case SMFIS_CONTINUE: case SMFIS_ACCEPT:
443 exception_policy = i;
444 Py_INCREF(Py_None);
445 return Py_None;
446 }
447 PyErr_SetString(MilterError,"invalid exception policy");
448 return NULL;
449 }
450
451 static void
_release_thread(PyThreadState * t)452 _release_thread(PyThreadState *t) {
453 if (t != NULL)
454 PyEval_ReleaseThread(t);
455 }
456
457
458 /** Report and clear any python exception before returning to libmilter.
459 The interpreter is locked when we are called, and we unlock it. */
_report_exception(milter_ContextObject * self)460 static int _report_exception(milter_ContextObject *self) {
461 char untrapped_msg[80];
462 if (PyErr_Occurred()) {
463 sprintf(untrapped_msg,"pymilter: untrapped exception in %.40s",
464 description.xxfi_name);
465 PyErr_Print();
466 PyErr_Clear(); /* must clear since not returning to python */
467 _release_thread(self->t);
468 switch (exception_policy) {
469 case SMFIS_REJECT:
470 smfi_setreply(self->ctx, "554", "5.3.0", untrapped_msg);
471 return SMFIS_REJECT;
472 case SMFIS_TEMPFAIL:
473 smfi_setreply(self->ctx, "451", "4.3.0", untrapped_msg);
474 return SMFIS_TEMPFAIL;
475 }
476 return exception_policy;
477 }
478 /* This should never happen, _report_exception is only called when
479 * the caller has already detected a python exception. If it
480 * does somehow happen, pretend nothing is wrong... */
481 _release_thread(self->t);
482 return SMFIS_CONTINUE;
483 }
484
485 /* Return to libmilter. The ctx must have been initialized or
486 checked by a successfull call to _get_context(), thereby locking
487 the interpreter. */
488 static int
_generic_wrapper(milter_ContextObject * self,PyObject * cb,PyObject * arglist)489 _generic_wrapper(milter_ContextObject *self, PyObject *cb, PyObject *arglist) {
490 PyObject *result;
491 int retval;
492
493 if (arglist == NULL) return _report_exception(self);
494 result = PyEval_CallObject(cb, arglist);
495 Py_DECREF(arglist);
496 if (result == NULL) return _report_exception(self);
497 #if PY_MAJOR_VERSION >= 3
498 if (!PyLong_Check(result)) {
499 #else
500 if (!PyInt_Check(result)) {
501 #endif
502 const struct MilterCallback *p;
503 const char *cbname = "milter";
504 char buf[40];
505 Py_DECREF(result);
506 for (p = callback; p->name; ++p) {
507 if (cb == p->cb) {
508 cbname = p->name;
509 break;
510 }
511 }
512 sprintf(buf,"The %s callback must return int",cbname);
513 PyErr_SetString(MilterError,buf);
514 return _report_exception(self);
515 }
516 #if PY_MAJOR_VERSION >= 3
517 retval = PyLong_AS_LONG(result);
518 #else
519 retval = PyInt_AS_LONG(result);
520 #endif
521 Py_DECREF(result);
522 _release_thread(self->t);
523 return retval;
524 }
525
526 /* Create a string object representing an IP address.
527 This is always a string of the form 'dd.dd.dd.dd' (with variable
528 size numbers). Copied from standard socket module. */
529
530 static PyObject *
531 makeipaddr(struct sockaddr_in *addr) {
532 long x = ntohl(addr->sin_addr.s_addr);
533 char buf[100];
534 sprintf(buf, "%d.%d.%d.%d",
535 (int) (x>>24) & 0xff, (int) (x>>16) & 0xff,
536 (int) (x>> 8) & 0xff, (int) (x>> 0) & 0xff);
537 #if PY_MAJOR_VERSION >= 3
538 return PyUnicode_FromString(buf);
539 #else
540 return PyString_FromString(buf);
541 #endif
542 }
543
544 #ifdef HAVE_IPV6_SUPPORT
545 static PyObject *
546 makeip6addr(struct sockaddr_in6 *addr) {
547 char buf[100]; /* must be at least INET6_ADDRSTRLEN + 1 */
548 const char *s = inet_ntop(AF_INET6, &addr->sin6_addr, buf, sizeof buf);
549 #if PY_MAJOR_VERSION >= 3
550 if (s) return PyUnicode_FromString(s);
551 return PyUnicode_FromString("inet6:unknown");
552 #else
553 if (s) return PyString_FromString(s);
554 return PyString_FromString("inet6:unknown");
555 #endif
556 }
557 #endif
558
559 /* These are wrapper functions to call the Python callbacks for each event */
560 static int
561 milter_wrap_connect(SMFICTX *ctx, char *hostname, _SOCK_ADDR *hostaddr) {
562 PyObject *arglist;
563 milter_ContextObject *c;
564 if (connect_callback == NULL) return SMFIS_CONTINUE;
565 c = _get_context(ctx);
566 if (!c) return SMFIS_TEMPFAIL;
567 if (hostaddr != NULL) {
568 switch (hostaddr->sa_family) {
569 case AF_INET:
570 { struct sockaddr_in *sa = (struct sockaddr_in *)hostaddr;
571 PyObject *ipaddr_obj = makeipaddr(sa);
572 arglist = Py_BuildValue("(Osh(Oi))", c, hostname, hostaddr->sa_family,
573 ipaddr_obj, ntohs(sa->sin_port));
574 Py_DECREF(ipaddr_obj);
575 }
576 break;
577 case AF_UNIX:
578 arglist = Py_BuildValue("(Oshs)", c, hostname, hostaddr->sa_family,
579 hostaddr->sa_data);
580 break;
581 #ifdef HAVE_IPV6_SUPPORT
582 case AF_INET6:
583 { struct sockaddr_in6 *sa = (struct sockaddr_in6 *)hostaddr;
584 PyObject *ip6addr_obj = makeip6addr(sa);
585 long scope_id = 0;
586 #ifdef HAVE_IPV6_RFC2553
587 scope_id = ntohl(sa->sin6_scope_id);
588 #endif
589 arglist = Py_BuildValue("(Osh(Oiii))", c, hostname, hostaddr->sa_family,
590 ip6addr_obj,
591 ntohs(sa->sin6_port),
592 ntohl(sa->sin6_flowinfo),
593 scope_id);
594 Py_DECREF(ip6addr_obj);
595 }
596 break;
597 #endif
598 default:
599 arglist = Py_BuildValue("(OshO)", c, hostname, hostaddr->sa_family,
600 Py_None);
601 }
602 }
603 else
604 arglist = Py_BuildValue("(OshO)", c, hostname, 0, Py_None);
605 return _generic_wrapper(c, connect_callback, arglist);
606 }
607
608 static int
609 milter_wrap_helo(SMFICTX *ctx, char *helohost) {
610 PyObject *arglist;
611 milter_ContextObject *c;
612
613 if (helo_callback == NULL) return SMFIS_CONTINUE;
614 c = _get_context(ctx);
615 if (!c) return SMFIS_TEMPFAIL;
616 arglist = Py_BuildValue("(Os)", c, helohost);
617 return _generic_wrapper(c, helo_callback, arglist);
618 }
619
620 static int
621 generic_env_wrapper(SMFICTX *ctx, PyObject*cb, char **argv) {
622 PyObject *arglist;
623 milter_ContextObject *self;
624 int count = 0;
625 int i;
626 char **p = argv;
627
628 if (cb == NULL) return SMFIS_CONTINUE;
629
630 self = _get_context(ctx);
631 if (!self) return SMFIS_TEMPFAIL;
632
633 /* Count how many strings we've been passed. */
634 while (*p++ != NULL) count++;
635 /* how to build the value in steps? Cheat by copying from */
636 /* Python/modsupport.c do_mktuple() and do_mkvalue() */
637 if ((arglist = PyTuple_New(count+1)) == NULL)
638 return _report_exception(self);
639 /* Add in the context first */
640 Py_INCREF(self); /* PyTuple_SetItem takes over reference */
641 PyTuple_SetItem(arglist, 0, (PyObject *)self);
642 /* Now do all the strings */
643 for (i=0;i<count;i++) {
644 /* There's some error checking performed in do_mkvalue() for a string */
645 /* that's not currently done here - it probably should be */
646 #if PY_MAJOR_VERSION >= 3
647 PyObject *o = PyUnicode_FromStringAndSize(argv[i], strlen(argv[i]));
648 #else
649 PyObject *o = PyString_FromStringAndSize(argv[i], strlen(argv[i]));
650 #endif
651 if (o == NULL) { /* out of memory */
652 Py_DECREF(arglist);
653 return _report_exception(self);
654 }
655 PyTuple_SetItem(arglist, i + 1, o);
656 }
657 return _generic_wrapper(self, cb, arglist);
658 }
659
660 static int
661 milter_wrap_envfrom(SMFICTX *ctx, char **argv) {
662 return generic_env_wrapper(ctx,envfrom_callback,argv);
663 }
664
665 static int
666 milter_wrap_envrcpt(SMFICTX *ctx, char **argv) {
667 return generic_env_wrapper(ctx,envrcpt_callback,argv);
668 }
669
670 static int
671 milter_wrap_header(SMFICTX *ctx, char *headerf, char *headerv) {
672 PyObject *arglist;
673 milter_ContextObject *c;
674
675 if (header_callback == NULL) return SMFIS_CONTINUE;
676 c = _get_context(ctx);
677 if (!c) return SMFIS_TEMPFAIL;
678 arglist = Py_BuildValue("(Oss)", c, headerf, headerv);
679 return _generic_wrapper(c, header_callback, arglist);
680 }
681
682 static int
683 generic_noarg_wrapper(SMFICTX *ctx,PyObject *cb) {
684 PyObject *arglist;
685 milter_ContextObject *c;
686 if (cb == NULL) return SMFIS_CONTINUE;
687 c = _get_context(ctx);
688 if (!c) return SMFIS_TEMPFAIL;
689 arglist = Py_BuildValue("(O)", c);
690 return _generic_wrapper(c, cb, arglist);
691 }
692
693 static int
694 milter_wrap_eoh(SMFICTX *ctx) {
695 return generic_noarg_wrapper(ctx,eoh_callback);
696 }
697
698 static int
699 milter_wrap_body(SMFICTX *ctx, u_char *bodyp, size_t bodylen) {
700 PyObject *arglist;
701 milter_ContextObject *c;
702
703 if (body_callback == NULL) return SMFIS_CONTINUE;
704 c = _get_context(ctx);
705 if (!c) return SMFIS_TEMPFAIL;
706 /* Unclear whether this should be s#, z#, or t# */
707 #if PY_MAJOR_VERSION >= 3
708 arglist = Py_BuildValue("(Oy#)", c, bodyp, bodylen);
709 #else
710 arglist = Py_BuildValue("(Os#)", c, bodyp, bodylen);
711 #endif
712 return _generic_wrapper(c, body_callback, arglist);
713 }
714
715 static int
716 milter_wrap_eom(SMFICTX *ctx) {
717 return generic_noarg_wrapper(ctx,eom_callback);
718 }
719
720 static int
721 milter_wrap_abort(SMFICTX *ctx) {
722 /* libmilter still calls close after abort */
723 return generic_noarg_wrapper(ctx,abort_callback);
724 }
725
726 #ifdef SMFIS_ALL_OPTS
727 static int
728 milter_wrap_unknown(SMFICTX *ctx, const char *cmd) {
729 PyObject *arglist;
730 milter_ContextObject *c;
731
732 if (unknown_callback == NULL) return SMFIS_CONTINUE;
733 c = _get_context(ctx);
734 if (!c) return SMFIS_TEMPFAIL;
735 arglist = Py_BuildValue("(Os)", c, cmd);
736 return _generic_wrapper(c, unknown_callback, arglist);
737 }
738
739 static int
740 milter_wrap_data(SMFICTX *ctx) {
741 return generic_noarg_wrapper(ctx,data_callback);
742 }
743
744 static int
745 milter_wrap_negotiate(SMFICTX *ctx,
746 unsigned long f0,
747 unsigned long f1,
748 unsigned long f2,
749 unsigned long f3,
750 unsigned long *pf0,
751 unsigned long *pf1,
752 unsigned long *pf2,
753 unsigned long *pf3) {
754 PyObject *arglist, *optlist;
755 milter_ContextObject *c;
756 int rc;
757
758 if (negotiate_callback == NULL) return SMFIS_ALL_OPTS;
759 c = _get_context(ctx);
760 if (!c)
761 return SMFIS_REJECT; // do not contact us again for current connection
762 optlist = Py_BuildValue("[kkkk]",f0,f1,f2,f3);
763 if (optlist == NULL)
764 arglist = NULL;
765 else
766 arglist = Py_BuildValue("(OO)", c, optlist);
767 PyThreadState *t = c->t;
768 c->t = 0; // do not release thread in _generic_wrapper
769 rc = _generic_wrapper(c, negotiate_callback, arglist);
770 c->t = t;
771 if (rc == SMFIS_CONTINUE) {
772 unsigned long *pa[4] = { pf0,pf1,pf2,pf3 };
773 unsigned long fa[4] = { f0,f1,f2,f3 };
774 int len = PyList_Size(optlist);
775 int i;
776 for (i = 0; i < 4; ++i) {
777 *pa[i] = (i <= len)
778 #if PY_MAJOR_VERSION >= 3
779 ? PyLong_AsUnsignedLongMask(PyList_GET_ITEM(optlist,i))
780 #else
781 ? PyInt_AsUnsignedLongMask(PyList_GET_ITEM(optlist,i))
782 #endif
783 : fa[i];
784 }
785 if (PyErr_Occurred()) {
786 PyErr_Print();
787 PyErr_Clear();
788 rc = SMFIS_REJECT;
789 }
790 }
791 else if (rc != SMFIS_ALL_OPTS)
792 rc = SMFIS_REJECT;
793 Py_DECREF(optlist);
794 _release_thread(t);
795 return rc;
796 }
797 #endif
798
799 static int
800 milter_wrap_close(SMFICTX *ctx) {
801 /* xxfi_close can be called out of order - even before connect.
802 * There may not yet be a private context pointer. To avoid
803 * creating a ThreadContext and allocating a milter context only
804 * to destroy them, and to avoid invoking the python close_callback when
805 * connect has never been called, we don't use generic_noarg_wrapper here. */
806 PyObject *cb = close_callback;
807 milter_ContextObject *self = smfi_getpriv(ctx);
808 int r = SMFIS_CONTINUE;
809 if (self != NULL) {
810 PyThreadState *t = self->t;
811 PyEval_AcquireThread(t);
812 self->t = 0;
813 if (cb != NULL && self->ctx == ctx) {
814 PyObject *arglist = Py_BuildValue("(O)", self);
815 /* Call python close callback, but do not ReleaseThread, because
816 * self->t is NULL */
817 r = _generic_wrapper(self, cb, arglist);
818 }
819 self->ctx = 0;
820 smfi_setpriv(ctx,0);
821 Py_DECREF(self);
822 PyThreadState_Clear(t);
823 PyEval_ReleaseThread(t);
824 PyThreadState_Delete(t);
825 }
826 return r;
827 }
828
829 static const char milter_register__doc__[] =
830 "register(name,unknown=,data=,negotiate=) -> None\n\
831 Registers the milter name with current callbacks, and flags.\n\
832 Required before main() is called.";
833
834 static PyObject *
835 milter_register(PyObject *self, PyObject *args, PyObject *kwds) {
836 static char *kwlist[] = { "name","unknown","data","negotiate", NULL };
837 static PyObject** const cbp[3] =
838 { &unknown_callback, &data_callback, &negotiate_callback };
839 PyObject *cb[3] = { NULL, NULL, NULL };
840 int i;
841 if (!PyArg_ParseTupleAndKeywords(args, kwds, "s|OOO:register", kwlist,
842 &description.xxfi_name, &cb[0],&cb[1],&cb[2]))
843 return NULL;
844 for (i = 0; i < 3; ++i) {
845 PyObject *callback = cb[i];
846 if (callback != NULL && callback != Py_None) {
847 if (!PyCallable_Check(callback)) {
848 char err[80];
849 sprintf(err,"%s parameter must be callable",kwlist[i]);
850 PyErr_SetString(PyExc_TypeError, err);
851 return NULL;
852 }
853 }
854 }
855 for (i = 0; i < 3; ++i) {
856 PyObject *callback = cb[i];
857 if (callback != NULL) { // if keyword specified
858 if (callback == Py_None) {
859 callback = NULL;
860 }
861 else {
862 Py_INCREF(callback);
863 }
864 PyObject *oldval = *cbp[i];
865 *cbp[i] = callback;
866 if (oldval) {
867 Py_DECREF(oldval);
868 }
869 }
870 }
871 return _generic_return(smfi_register(description), "cannot register");
872 }
873
874 static const char milter_opensocket__doc__[] =
875 "opensocket(rmsock) -> None\n\
876 Attempts to create and open the socket provided with setconn.\n\
877 Removes the socket first if rmsock is True.";
878
879 static PyObject *
880 milter_opensocket(PyObject *self, PyObject *args) {
881 char rmsock = 0;
882 if (!PyArg_ParseTuple(args, "b:opensocket", &rmsock))
883 return NULL;
884 return _generic_return(smfi_opensocket(rmsock), "cannot opensocket");
885 }
886
887 static const char milter_main__doc__[] =
888 "main() -> None\n\
889 Main milter routine. Set any callbacks, and flags desired, then call\n\
890 setconn(), then call register(name), and finally call main().";
891
892 static PyObject *
893 milter_main(PyObject *self, PyObject *args) {
894 PyThreadState *_main;
895 PyObject *o;
896 if (!PyArg_ParseTuple(args, ":main")) return NULL;
897 if (interp != NULL) {
898 PyErr_SetString(MilterError,"milter module in use");
899 return NULL;
900 }
901 /* libmilter requires thread support */
902 PyEval_InitThreads();
903 /* let other threads run while in smfi_main() */
904 interp = PyThreadState_Get()->interp;
905 _main = PyEval_SaveThread(); /* must be done before smfi_main() */
906 o = _thread_return(_main,smfi_main(), "cannot run main");
907 interp = NULL;
908 return o;
909 }
910
911 static const char milter_setdbg__doc__[] =
912 "setdbg(int) -> None\n\
913 Sets debug level in sendmail/libmilter source. Dubious usefulness.";
914
915 static PyObject *
916 milter_setdbg(PyObject *self, PyObject *args) {
917 int val;
918 if (!PyArg_ParseTuple(args, "i:setdbg", &val)) return NULL;
919 return _generic_return(smfi_setdbg(val), "cannot set debug value");
920 }
921
922 static const char milter_setbacklog__doc__[] =
923 "setbacklog(int) -> None\n\
924 Set the TCP connection queue size for the milter socket.";
925
926 static PyObject *
927 milter_setbacklog(PyObject *self, PyObject *args) {
928 int val;
929
930 if (!PyArg_ParseTuple(args, "i:setbacklog", &val)) return NULL;
931 return _generic_return(smfi_setbacklog(val), "cannot set backlog");
932 }
933
934 static const char milter_settimeout__doc__[] =
935 "settimeout(int) -> None\n\
936 Set the time (in seconds) that sendmail will wait before\n\
937 considering this filter dead.";
938
939 static PyObject *
940 milter_settimeout(PyObject *self, PyObject *args) {
941 int val;
942
943 if (!PyArg_ParseTuple(args, "i:settimeout", &val)) return NULL;
944 return _generic_return(smfi_settimeout(val), "cannot set timeout");
945 }
946
947 static const char milter_setconn__doc__[] =
948 "setconn(filename) -> None\n\
949 Sets the pathname to the unix, inet, or inet6 socket that\n\
950 sendmail will use to communicate with this filter. By default,\n\
951 a unix domain socket is used. It must not exist,\n\
952 and sendmail will throw warnings if, eg, the file is under a\n\
953 group or world writable directory. This call is \n\
954 mandatory, and is invoked before register() and main().\n\
955 setconn('unix:/var/run/pythonfilter')\n\
956 setconn('inet:8800') # listen on ANY interface\n\
957 setconn('inet:7871@publichost')\n\
958 setconn('inet6:8020')";
959
960 static PyObject *
961 milter_setconn(PyObject *self, PyObject *args) {
962 char *str;
963 if (!PyArg_ParseTuple(args, "s:setconn", &str)) return NULL;
964 return _generic_return(smfi_setconn(str), "cannot set connection");
965 }
966
967 static const char milter_stop__doc__[] =
968 "stop() -> None\n\
969 This function appears to be a controlled method to tell sendmail to\n\
970 stop using this filter. It will close the socket.";
971
972 static PyObject *
973 milter_stop(PyObject *self, PyObject *args) {
974 PyThreadState *t;
975 if (!PyArg_ParseTuple(args, ":stop")) return NULL;
976 t = PyEval_SaveThread();
977 return _thread_return(t,smfi_stop(), "cannot stop");
978 }
979
980 static const char milter_getdiag__doc__[] =
981 "getdiag() -> tuple\n\
982 Return a tuple of diagnostic data. The first two items are context new\n\
983 count and context del count. The rest are yet to be defined.";
984 static PyObject *
985 milter_getdiag(PyObject *self, PyObject *args) {
986 if (!PyArg_ParseTuple(args, ":getdiag")) return NULL;
987 return Py_BuildValue("(kk)", diag.contextNew,diag.contextDel);
988 }
989
990 static const char milter_getversion__doc__[] =
991 "getversion() -> tuple\n\
992 Return runtime libmilter version as a tuple of major,minor,patchlevel.";
993 static PyObject *
994 milter_getversion(PyObject *self, PyObject *args) {
995 unsigned int major, minor, patch;
996 if (!PyArg_ParseTuple(args, ":getversion")) return NULL;
997 if (smfi_version(&major,&minor,&patch) != MI_SUCCESS) {
998 PyErr_SetString(MilterError, "smfi_version failed");
999 return NULL;
1000 }
1001 return Py_BuildValue("(kkk)", major,minor,patch);
1002 }
1003
1004 static const char milter_getsymval__doc__[] =
1005 "getsymval(String) -> String\n\
1006 Returns a symbol's value. Context-dependent, and unclear from the dox.";
1007
1008 static PyObject *
1009 milter_getsymval(PyObject *self, PyObject *args) {
1010 char *str;
1011 SMFICTX *ctx;
1012
1013 if (!PyArg_ParseTuple(args, "s:getsymval", &str)) return NULL;
1014 ctx = _find_context(self);
1015 if (ctx == NULL) return NULL;
1016 return Py_BuildValue("s", smfi_getsymval(ctx, str));
1017 }
1018
1019 static const char milter_setreply__doc__[] =
1020 "setreply(rcode, xcode, message) -> None\n\
1021 Sets the specific reply code to be used in response\n\
1022 to the active command.\n\
1023 rcode - The three-digit (RFC 821) SMTP reply code to be returned\n\
1024 xcode - The extended (RFC 2034) reply code\n\
1025 message - The text part of the SMTP reply\n\
1026 These should all be strings.";
1027
1028 static PyObject *
1029 milter_setreply(PyObject *self, PyObject *args) {
1030 char *rcode;
1031 char *xcode;
1032 char *message[MAX_ML_REPLY];
1033 char fmt[MAX_ML_REPLY + 16];
1034 SMFICTX *ctx;
1035 int i;
1036 strcpy(fmt,"sz|");
1037 for (i = 0; i < MAX_ML_REPLY; ++i) {
1038 message[i] = 0;
1039 fmt[i+3] = 's';
1040 }
1041 strcpy(fmt+i+3,":setreply");
1042 if (!PyArg_ParseTuple(args, fmt,
1043 &rcode, &xcode, message
1044 #if MAX_ML_REPLY > 1
1045 ,message+1,message+2,message+3,message+4,message+5,message+6,
1046 message+7,message+8,message+9,message+10
1047 #if MAX_ML_REPLY > 11
1048 ,message+11,message+12,message+13,message+14,message+15,
1049 message+16,message+17,message+18,message+19,message+20,
1050 message+21,message+22,message+23,message+24,message+25,
1051 message+26,message+27,message+28,message+29,message+30,
1052 message+31
1053 #endif
1054 #endif
1055 ))
1056 return NULL;
1057 ctx = _find_context(self);
1058 if (ctx == NULL) return NULL;
1059 #if MAX_ML_REPLY > 1
1060 /*
1061 * C varargs might be convenient for some things, but they sure are a pain
1062 * when the number of args is not known at compile time.
1063 */
1064 if (message[0] && message[1])
1065 return _generic_return(smfi_setmlreply(ctx, rcode, xcode,
1066 message[0],
1067 message[1],message[2],message[3],message[4],message[5],
1068 message[6],message[7],message[8],message[9],message[10],
1069 #if MAX_ML_REPLY > 11
1070 message[11],message[12],message[13],message[14],message[15],
1071 message[16],message[17],message[18],message[19],message[20],
1072 message[21],message[22],message[23],message[24],message[25],
1073 message[26],message[27],message[28],message[29],message[30],
1074 message[31],
1075 #endif
1076 (char *)0
1077 ), "cannot set reply");
1078 #endif
1079 return _generic_return(smfi_setreply(ctx, rcode, xcode, message[0]),
1080 "cannot set reply");
1081 }
1082
1083 static const char milter_addheader__doc__[] =
1084 "addheader(field, value, idx=-1) -> None\n\
1085 Add a header to the message. This header is not passed to other\n\
1086 filters. It is not checked for standards compliance;\n\
1087 the mail filter must ensure that no protocols are violated\n\
1088 as a result of adding this header.\n\
1089 field - header field name\n\
1090 value - header field value\n\
1091 idx - optional position in internal header list to insert new header\n\
1092 Both are strings. This function can only be called from the EOM callback.";
1093
1094 static PyObject *
1095 milter_addheader(PyObject *self, PyObject *args) {
1096 char *headerf;
1097 char *headerv;
1098 int idx = -1;
1099 SMFICTX *ctx;
1100 PyThreadState *t;
1101
1102 if (!PyArg_ParseTuple(args, "ss|i:addheader", &headerf, &headerv, &idx))
1103 return NULL;
1104 ctx = _find_context(self);
1105 if (ctx == NULL) return NULL;
1106 t = PyEval_SaveThread();
1107 #ifdef SMFIR_INSHEADER
1108 return _thread_return(t, (idx < 0) ? smfi_addheader(ctx, headerf, headerv) :
1109 smfi_insheader(ctx, idx, headerf, headerv), "cannot add header");
1110 #else
1111 if (idx < 0)
1112 return _thread_return(t, smfi_addheader(ctx, headerf, headerv),
1113 "cannot add header");
1114 PyErr_SetString(MilterError, "insheader not supported");
1115 return NULL;
1116 #endif
1117 }
1118
1119 #ifdef SMFIF_CHGFROM
1120 static const char milter_chgfrom__doc__[] =
1121 "chgfrom(sender,params) -> None\n\
1122 Change the envelope sender (MAIL From) of the current message.\n\
1123 A filter which calls smfi_chgfrom must have set the CHGFROM flag\n\
1124 in set_flags() before calling register.\n\
1125 This function can only be called from the EOM callback.";
1126 static PyObject *
1127 milter_chgfrom(PyObject *self, PyObject *args) {
1128 char *sender;
1129 char *params = NULL;
1130 SMFICTX *ctx;
1131 PyThreadState *t;
1132
1133 if (!PyArg_ParseTuple(args, "s|z:chgfrom", &sender, ¶ms))
1134 return NULL;
1135 ctx = _find_context(self);
1136 if (ctx == NULL) return NULL;
1137 t = PyEval_SaveThread();
1138 return _thread_return(t,smfi_chgfrom(ctx, sender, params),
1139 "cannot change sender");
1140 }
1141 #endif
1142
1143 static const char milter_chgheader__doc__[] =
1144 "chgheader(field, int, value) -> None\n\
1145 Change/delete a header in the message. \n\
1146 It is not checked for standards compliance; the mail filter\n\
1147 must ensure that no protocols are violated as a result of adding this header.\n\
1148 field - header field name\n\
1149 int - the Nth occurrence of this header\n\
1150 value - header field value\n\
1151 field and value are strings.\n\
1152 This function can only be called from the EOM callback.";
1153
1154 static PyObject *
1155 milter_chgheader(PyObject *self, PyObject *args) {
1156 char *headerf;
1157 int index;
1158 char *headerv;
1159 SMFICTX *ctx;
1160 PyThreadState *t;
1161
1162 if (!PyArg_ParseTuple(args, "siz:chgheader", &headerf, &index, &headerv))
1163 return NULL;
1164 ctx = _find_context(self);
1165 if (ctx == NULL) return NULL;
1166 t = PyEval_SaveThread();
1167 return _thread_return(t,smfi_chgheader(ctx, headerf, index, headerv),
1168 "cannot change header");
1169 }
1170
1171 static const char milter_addrcpt__doc__[] =
1172 "addrcpt(string,params=None) -> None\n\
1173 Add a recipient to the envelope. It must be in the same format\n\
1174 as is passed to the envrcpt callback in the first tuple element.\n\
1175 If params is used, you must pass ADDRCPT_PAR to set_flags().\n\
1176 This function can only be called from the EOM callback.";
1177
1178 static PyObject *
1179 milter_addrcpt(PyObject *self, PyObject *args) {
1180 char *rcpt;
1181 char *params = 0;
1182 SMFICTX *ctx;
1183 PyThreadState *t;
1184 int rc;
1185
1186 if (!PyArg_ParseTuple(args, "s|z:addrcpt", &rcpt, ¶ms)) return NULL;
1187 ctx = _find_context(self);
1188 if (ctx == NULL) return NULL;
1189 t = PyEval_SaveThread();
1190 if (params)
1191 #ifdef SMFIF_ADDRCPT_PAR
1192 rc = smfi_addrcpt_par(ctx,rcpt,params);
1193 #else
1194 rc = MI_FAILURE;
1195 #endif
1196 else
1197 rc = smfi_addrcpt(ctx,rcpt);
1198 return _thread_return(t,rc, "cannot add recipient");
1199 }
1200
1201 static const char milter_delrcpt__doc__[] =
1202 "delrcpt(string) -> None\n\
1203 Delete a recipient from the envelope.\n\
1204 This function can only be called from the EOM callback.";
1205
1206 static PyObject *
1207 milter_delrcpt(PyObject *self, PyObject *args) {
1208 char *rcpt;
1209 SMFICTX *ctx;
1210 PyThreadState *t;
1211
1212 if (!PyArg_ParseTuple(args, "s:delrcpt", &rcpt)) return NULL;
1213 ctx = _find_context(self);
1214 if (ctx == NULL) return NULL;
1215 t = PyEval_SaveThread();
1216 return _thread_return(t,smfi_delrcpt(ctx, rcpt), "cannot delete recipient");
1217 }
1218
1219 static const char milter_replacebody__doc__[] =
1220 "replacebody(string) -> None\n\
1221 Replace the body of the message. This routine may be called multiple\n\
1222 times if the body is longer than convenient to send in one call. End of\n\
1223 line should be represented as Carriage-Return/Line Feed. This function\n\
1224 can only be called from the EOM callback.";
1225
1226 static PyObject *
1227 milter_replacebody(PyObject *self, PyObject *args) {
1228 char *bodyp;
1229 int bodylen;
1230 SMFICTX *ctx;
1231 PyThreadState *t;
1232
1233 if (!PyArg_ParseTuple(args, "s#", &bodyp, &bodylen)) return NULL;
1234 ctx = _find_context(self);
1235 if (ctx == NULL) return NULL;
1236 t = PyEval_SaveThread();
1237 return _thread_return(t,smfi_replacebody(ctx,
1238 (unsigned char *)bodyp, bodylen), "cannot replace message body");
1239 }
1240
1241 static const char milter_setpriv__doc__[] =
1242 "setpriv(object) -> object\n\
1243 Associates any Python object with this context, and returns\n\
1244 the old value or None. Use this to\n\
1245 provide thread-safe storage for data instead of using global variables\n\
1246 for things like filenames, etc. This function stores only one object\n\
1247 per context, but that object can in turn store many others.";
1248
1249 static PyObject *
1250 milter_setpriv(PyObject *self, PyObject *args) {
1251 PyObject *o;
1252 PyObject *old;
1253 milter_ContextObject *s = (milter_ContextObject *)self;
1254
1255 if (!PyArg_ParseTuple(args, "O:setpriv", &o)) return NULL;
1256 /* PyArg_ParseTuple's O format does not increase the reference count on
1257 the target. Since we're going to save it and almost certainly assign
1258 to another object later, we incref it here, and only decref it in
1259 the dealloc method. */
1260 Py_INCREF(o);
1261 old = s->priv;
1262 s->priv = o;
1263 /* We return the old value. The caller will DECREF it if not used. */
1264 return old;
1265 }
1266
1267 static const char milter_getpriv__doc__[] =
1268 "getpriv() -> None\n\
1269 Returns the Python object associated with the current context (if any).\n\
1270 Use this in conjunction with setpriv to keep track of data in a thread-safe\n\
1271 manner.";
1272
1273 static PyObject *
1274 milter_getpriv(PyObject *self, PyObject *args) {
1275 PyObject *o;
1276 milter_ContextObject *s = (milter_ContextObject *)self;
1277
1278 if (!PyArg_ParseTuple(args, ":getpriv")) return NULL;
1279 o = s->priv;
1280 Py_INCREF(o);
1281 return o;
1282 }
1283
1284 #ifdef SMFIF_QUARANTINE
1285 static const char milter_quarantine__doc__[] =
1286 "quarantine(string) -> None\n\
1287 Place the message in quarantine. A string with a description of the reason\n\
1288 is the only argument.";
1289
1290 static PyObject *
1291 milter_quarantine(PyObject *self, PyObject *args) {
1292 char *reason;
1293 SMFICTX *ctx;
1294 PyThreadState *t;
1295
1296 if (!PyArg_ParseTuple(args, "s:quarantine", &reason)) return NULL;
1297 ctx = _find_context(self);
1298 if (ctx == NULL) return NULL;
1299 t = PyEval_SaveThread();
1300 return _thread_return(t,smfi_quarantine(ctx, reason),
1301 "cannot quarantine message");
1302 }
1303 #endif
1304
1305 #ifdef SMFIR_PROGRESS
1306 static const char milter_progress__doc__[] =
1307 "progress() -> None\n\
1308 Notify the MTA that we are working on a message so it will reset timeouts.";
1309
1310 static PyObject *
1311 milter_progress(PyObject *self, PyObject *args) {
1312 SMFICTX *ctx;
1313 PyThreadState *t;
1314
1315 if (!PyArg_ParseTuple(args, ":progress")) return NULL;
1316 ctx = _find_context(self);
1317 if (ctx == NULL) return NULL;
1318 t = PyEval_SaveThread();
1319 return _thread_return(t,smfi_progress(ctx), "cannot notify progress");
1320 }
1321 #endif
1322
1323 #ifdef SMFIF_SETSYMLIST
1324 static const char milter_setsymlist__doc__[] =
1325 "setsymlist(stage,macrolist) -> None\n\
1326 Tell the MTA which macro values we are interested in for a given stage";
1327
1328 static PyObject *
1329 milter_setsymlist(PyObject *self, PyObject *args) {
1330 SMFICTX *ctx;
1331 PyThreadState *t;
1332 int stage = 0;
1333 char *smlist = 0;
1334
1335 if (!PyArg_ParseTuple(args, "is:setsymlist",&stage, &smlist)) return NULL;
1336 ctx = _find_context(self);
1337 if (ctx == NULL) return NULL;
1338 t = PyEval_SaveThread();
1339 return _thread_return(t,smfi_setsymlist(ctx,stage,smlist),
1340 "cannot set macro list");
1341 }
1342 #endif
1343
1344 static PyMethodDef context_methods[] = {
1345 { "getsymval", milter_getsymval, METH_VARARGS, milter_getsymval__doc__},
1346 { "setreply", milter_setreply, METH_VARARGS, milter_setreply__doc__},
1347 { "addheader", milter_addheader, METH_VARARGS, milter_addheader__doc__},
1348 { "chgheader", milter_chgheader, METH_VARARGS, milter_chgheader__doc__},
1349 { "addrcpt", milter_addrcpt, METH_VARARGS, milter_addrcpt__doc__},
1350 { "delrcpt", milter_delrcpt, METH_VARARGS, milter_delrcpt__doc__},
1351 { "replacebody", milter_replacebody, METH_VARARGS, milter_replacebody__doc__},
1352 { "setpriv", milter_setpriv, METH_VARARGS, milter_setpriv__doc__},
1353 { "getpriv", milter_getpriv, METH_VARARGS, milter_getpriv__doc__},
1354 #ifdef SMFIF_QUARANTINE
1355 { "quarantine", milter_quarantine, METH_VARARGS, milter_quarantine__doc__},
1356 #endif
1357 #ifdef SMFIR_PROGRESS
1358 { "progress", milter_progress, METH_VARARGS, milter_progress__doc__},
1359 #endif
1360 #ifdef SMFIF_CHGFROM
1361 { "chgfrom", milter_chgfrom, METH_VARARGS, milter_chgfrom__doc__},
1362 #endif
1363 #ifdef SMFIF_SETSYMLIST
1364 { "setsymlist", milter_setsymlist, METH_VARARGS, milter_setsymlist__doc__},
1365 #endif
1366 { NULL, NULL }
1367 };
1368
1369 #if PY_MAJOR_VERSION < 3
1370 static PyObject *
1371 milter_Context_getattr(PyObject *self, char *name) {
1372 return Py_FindMethod(context_methods, self, name);
1373 }
1374 #endif
1375
1376 static struct smfiDesc description = { /* Set some reasonable defaults */
1377 "pythonfilter",
1378 SMFI_VERSION,
1379 SMFI_CURR_ACTS,
1380 milter_wrap_connect,
1381 milter_wrap_helo,
1382 milter_wrap_envfrom,
1383 milter_wrap_envrcpt,
1384 milter_wrap_header,
1385 milter_wrap_eoh,
1386 milter_wrap_body,
1387 milter_wrap_eom,
1388 milter_wrap_abort,
1389 milter_wrap_close,
1390 #ifdef SMFIS_ALL_OPTS
1391 milter_wrap_unknown,
1392 milter_wrap_data,
1393 milter_wrap_negotiate
1394 #endif
1395 };
1396
1397 static PyMethodDef milter_methods[] = {
1398 { "set_flags", milter_set_flags, METH_VARARGS, milter_set_flags__doc__},
1399 { "set_connect_callback", milter_set_connect_callback, METH_VARARGS, milter_set_connect_callback__doc__},
1400 { "set_helo_callback", milter_set_helo_callback, METH_VARARGS, milter_set_helo_callback__doc__},
1401 { "set_envfrom_callback", milter_set_envfrom_callback, METH_VARARGS, milter_set_envfrom_callback__doc__},
1402 { "set_envrcpt_callback", milter_set_envrcpt_callback, METH_VARARGS, milter_set_envrcpt_callback__doc__},
1403 { "set_header_callback", milter_set_header_callback, METH_VARARGS, milter_set_header_callback__doc__},
1404 { "set_eoh_callback", milter_set_eoh_callback, METH_VARARGS, milter_set_eoh_callback__doc__},
1405 { "set_body_callback", milter_set_body_callback, METH_VARARGS, milter_set_body_callback__doc__},
1406 { "set_eom_callback", milter_set_eom_callback, METH_VARARGS, milter_set_eom_callback__doc__},
1407 { "set_abort_callback", milter_set_abort_callback, METH_VARARGS, milter_set_abort_callback__doc__},
1408 { "set_close_callback", milter_set_close_callback, METH_VARARGS, milter_set_close_callback__doc__},
1409 { "set_exception_policy", milter_set_exception_policy, METH_VARARGS, milter_set_exception_policy__doc__},
1410 { "register", (PyCFunction)milter_register,METH_VARARGS|METH_KEYWORDS, milter_register__doc__},
1411 { "opensocket", milter_opensocket, METH_VARARGS, milter_opensocket__doc__},
1412 { "main", milter_main, METH_VARARGS, milter_main__doc__},
1413 { "setdbg", milter_setdbg, METH_VARARGS, milter_setdbg__doc__},
1414 { "settimeout", milter_settimeout, METH_VARARGS, milter_settimeout__doc__},
1415 { "setbacklog", milter_setbacklog, METH_VARARGS, milter_setbacklog__doc__},
1416 { "setconn", milter_setconn, METH_VARARGS, milter_setconn__doc__},
1417 { "stop", milter_stop, METH_VARARGS, milter_stop__doc__},
1418 { "getdiag", milter_getdiag, METH_VARARGS, milter_getdiag__doc__},
1419 { "getversion", milter_getversion, METH_VARARGS, milter_getversion__doc__},
1420 { NULL, NULL }
1421 };
1422
1423 static PyTypeObject milter_ContextType = {
1424 #if PY_MAJOR_VERSION >= 3
1425 PyVarObject_HEAD_INIT(&PyType_Type,0)
1426 "milter.Context",
1427 #else
1428 PyObject_HEAD_INIT(&PyType_Type)
1429 0,
1430 "milterContext",
1431 #endif
1432 sizeof(milter_ContextObject),
1433 0,
1434 milter_Context_dealloc, /* tp_dealloc */
1435 0, /* tp_print */
1436 #if PY_MAJOR_VERSION >= 3
1437 0, /* tp_getattr */
1438 #else
1439 milter_Context_getattr, /* tp_getattr */
1440 #endif
1441 0, /* tp_setattr */
1442 0, /* tp_compare */
1443 0, /* tp_repr */
1444 0, /* tp_as_number */
1445 0, /* tp_as_sequence */
1446 0, /* tp_as_mapping */
1447 0, /* tp_hash */
1448 0, /* tp_call */
1449 0, /* tp_str */
1450 0, /* tp_getattro */
1451 0, /* tp_setattro */
1452 0, /* tp_as_buffer */
1453 Py_TPFLAGS_DEFAULT, /* tp_flags */
1454 #if PY_MAJOR_VERSION >= 3
1455 NULL, /* Documentation string */
1456 0, /* call function for all accessible objects */
1457 0, /* delete references to contained objects */
1458 0, /* rich comparisons */
1459 0, /* weak reference enabler */
1460 0, 0, /* Iterators */
1461 context_methods, /* Attribute descriptor and subclassing stuff */
1462 #endif
1463 };
1464
1465 static const char milter_documentation[] =
1466 "This module interfaces with Sendmail's libmilter functionality,\n\
1467 allowing one to write email filters directly in Python.\n\
1468 Libmilter is currently marked FFR, and needs to be explicitly installed.\n\
1469 See <sendmailsource>/libmilter/README for details on setting it up.\n";
1470
1471 static void setitem(PyObject *d,const char *name,long val) {
1472 #if PY_MAJOR_VERSION >= 3
1473 PyObject *v = PyLong_FromLong(val);
1474 #else
1475 PyObject *v = PyInt_FromLong(val);
1476 #endif
1477 PyDict_SetItemString(d,name,v);
1478 Py_DECREF(v);
1479 }
1480
1481 #if PY_MAJOR_VERSION >= 3
1482
1483 static struct PyModuleDef moduledef = {
1484 PyModuleDef_HEAD_INIT,
1485 "milter", /* m_name */
1486 milter_documentation,/* m_doc */
1487 -1, /* m_size */
1488 milter_methods, /* m_methods */
1489 NULL, /* m_reload */
1490 NULL, /* m_traverse */
1491 NULL, /* m_clear */
1492 NULL, /* m_free */
1493 };
1494
1495 PyMODINIT_FUNC PyInit_milter(void) {
1496 PyObject *m, *d;
1497
1498 if (PyType_Ready(&milter_ContextType) < 0)
1499 return NULL;
1500
1501 m = PyModule_Create(&moduledef);
1502 if (m == NULL) return NULL;
1503 #else
1504
1505 void initmilter(void) {
1506 PyObject *m, *d;
1507
1508 m = Py_InitModule4("milter", milter_methods, milter_documentation,
1509 (PyObject*)NULL, PYTHON_API_VERSION);
1510 #endif
1511 d = PyModule_GetDict(m);
1512 MilterError = PyErr_NewException("milter.error", NULL, NULL);
1513 PyDict_SetItemString(d,"error", MilterError);
1514 setitem(d,"SUCCESS", MI_SUCCESS);
1515 setitem(d,"FAILURE", MI_FAILURE);
1516 setitem(d,"VERSION", SMFI_VERSION);
1517 setitem(d,"ADDHDRS", SMFIF_ADDHDRS);
1518 setitem(d,"CHGBODY", SMFIF_CHGBODY);
1519 setitem(d,"MODBODY", SMFIF_MODBODY);
1520 setitem(d,"ADDRCPT", SMFIF_ADDRCPT);
1521 #ifdef SMFIF_ADDRCPT_PAR
1522 setitem(d,"ADDRCPT_PAR", SMFIF_ADDRCPT_PAR);
1523 #endif
1524 setitem(d,"DELRCPT", SMFIF_DELRCPT);
1525 setitem(d,"CHGHDRS", SMFIF_CHGHDRS);
1526 setitem(d,"V1_ACTS", SMFI_V1_ACTS);
1527 setitem(d,"V2_ACTS", SMFI_V2_ACTS);
1528 setitem(d,"CURR_ACTS", SMFI_CURR_ACTS);
1529 #ifdef SMFIF_QUARANTINE
1530 setitem(d,"QUARANTINE",SMFIF_QUARANTINE);
1531 #endif
1532 #ifdef SMFIF_CHGFROM
1533 setitem(d,"CHGFROM",SMFIF_CHGFROM);
1534 #endif
1535 #ifdef SMFIF_SETSYMLIST
1536 setitem(d,"SETSYMLIST",SMFIF_SETSYMLIST);
1537 setitem(d,"M_CONNECT",SMFIM_CONNECT);/* connect */
1538 setitem(d,"M_HELO",SMFIM_HELO); /* HELO/EHLO */
1539 setitem(d,"M_ENVFROM",SMFIM_ENVFROM);/* MAIL From */
1540 setitem(d,"M_ENVRCPT",SMFIM_ENVRCPT);/* RCPT To */
1541 setitem(d,"M_DATA",SMFIM_DATA); /* DATA */
1542 setitem(d,"M_EOM",SMFIM_EOM); /* end of message (final dot) */
1543 setitem(d,"M_EOH",SMFIM_EOH); /* end of header */
1544 #endif
1545 #ifdef SMFIS_ALL_OPTS
1546 setitem(d,"P_RCPT_REJ",SMFIP_RCPT_REJ);
1547 setitem(d,"P_NR_CONN",SMFIP_NR_CONN);
1548 setitem(d,"P_NR_HELO",SMFIP_NR_HELO);
1549 setitem(d,"P_NR_MAIL",SMFIP_NR_MAIL);
1550 setitem(d,"P_NR_RCPT",SMFIP_NR_RCPT);
1551 setitem(d,"P_NR_DATA",SMFIP_NR_DATA);
1552 setitem(d,"P_NR_UNKN",SMFIP_NR_UNKN);
1553 setitem(d,"P_NR_EOH",SMFIP_NR_EOH);
1554 setitem(d,"P_NR_BODY",SMFIP_NR_BODY);
1555 setitem(d,"P_NR_HDR",SMFIP_NR_HDR);
1556 setitem(d,"P_NOCONNECT",SMFIP_NOCONNECT);
1557 setitem(d,"P_NOHELO",SMFIP_NOHELO);
1558 setitem(d,"P_NOMAIL",SMFIP_NOMAIL);
1559 setitem(d,"P_NORCPT",SMFIP_NORCPT);
1560 setitem(d,"P_NODATA",SMFIP_NODATA);
1561 setitem(d,"P_NOUNKNOWN",SMFIP_NOUNKNOWN);
1562 setitem(d,"P_NOEOH",SMFIP_NOEOH);
1563 setitem(d,"P_NOBODY",SMFIP_NOBODY);
1564 setitem(d,"P_NOHDRS",SMFIP_NOHDRS);
1565 setitem(d,"P_HDR_LEADSPC",SMFIP_HDR_LEADSPC);
1566 setitem(d,"P_SKIP",SMFIP_SKIP);
1567 setitem(d,"ALL_OPTS",SMFIS_ALL_OPTS);
1568 setitem(d,"SKIP",SMFIS_SKIP);
1569 setitem(d,"NOREPLY",SMFIS_NOREPLY);
1570 #endif
1571 setitem(d,"CONTINUE", SMFIS_CONTINUE);
1572 setitem(d,"REJECT", SMFIS_REJECT);
1573 setitem(d,"DISCARD", SMFIS_DISCARD);
1574 setitem(d,"ACCEPT", SMFIS_ACCEPT);
1575 setitem(d,"TEMPFAIL", SMFIS_TEMPFAIL);
1576 #if PY_MAJOR_VERSION >= 3
1577 return m;
1578 #endif
1579 }
1580