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, &params))
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, &params)) 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