User Tools

Site Tools


programming:python:python-libmilter

Differences

This shows you the differences between two versions of the page.

Link to this comparison view

Both sides previous revisionPrevious revision
Next revision
Previous revision
programming:python:python-libmilter [2012/02/18 20:36] jayprogramming:python:python-libmilter [2023/11/10 20:06] (current) – [Donations] jay
Line 1: Line 1:
-====== About ======+====== python-libmilter ====== 
 + 
 +===== About =====
 python-libmilter started as a project to maximize the portability of libmilter.  I know about, and have used, the excellent CPython implementation by Stuart D. Gathman [[http://bmsi.com/python/milter.html|py-milter]].  Though I liked this library for it's thin wrapper around the C libmilter library (and thereby speed), I had some issues with getting it to install, having to find and download the appropriate source code for libmilter version installed, etc.  This is why I went with a ground up implementation of libmilter in pure Python.  I want something I could just "drop in" to a project and have a milter up and running in almost no time, no matter what OS environment I was in.   python-libmilter started as a project to maximize the portability of libmilter.  I know about, and have used, the excellent CPython implementation by Stuart D. Gathman [[http://bmsi.com/python/milter.html|py-milter]].  Though I liked this library for it's thin wrapper around the C libmilter library (and thereby speed), I had some issues with getting it to install, having to find and download the appropriate source code for libmilter version installed, etc.  This is why I went with a ground up implementation of libmilter in pure Python.  I want something I could just "drop in" to a project and have a milter up and running in almost no time, no matter what OS environment I was in.  
  
Line 6: Line 8:
 Note that this documentation assumes a good working knowledge of the SMTP protocol as defined in RFC 2822. Note that this documentation assumes a good working knowledge of the SMTP protocol as defined in RFC 2822.
  
-====== Bug Reports, Issues and Contributions ======+===== Getting It ===== 
 +You have a couple of options for getting this package.  You can head on over to the [[https://github.com/crustymonkey/python-libmilter|Github page]] and download or clone it from there.   
 + 
 +You can also use ''pip'' or ''easy_install'' to install it. 
 +<code bash> 
 +pip install python-libmilter 
 +</code> 
 + 
 +**NOTE:** The RPMs are **not** guaranteed to be the latest versions.  Check [[https://github.com/crustymonkey/python-libmilter|Github]] to find out what the latest version is.  That said, I will try and keep these up to date. 
 + 
 +{{:programming:python:python-libmilter-1.0.1-1.noarch.rpm|python-libmilter-1.0.1-1.noarch.rpm}} \\ 
 +{{:programming:python:python-libmilter-1.0.1-1.src.rpm|python:python-libmilter-1.0.1-1.src.rpm}} 
 + 
 +===== Bug Reports, Issues and Contributions =====
 Please submit any and all bug reports/feature requests to the [[https://github.com/crustymonkey/python-libmilter|Github page]]. Please submit any and all bug reports/feature requests to the [[https://github.com/crustymonkey/python-libmilter|Github page]].
  
Line 13: Line 28:
 If you have any other questions, you can always email me directly at admin@splitstreams.com. If you have any other questions, you can always email me directly at admin@splitstreams.com.
  
-====== The Library ======+===== The Library =====
 You will find that the library file is split up into sections defining the constants for client server options and flags.  Note that there are multiple milter versions (2, 3, 4, 6) and not everything is available in earlier versions than 6.  See libmilter documentation for specifics on what is available in what versions.  I will show where the version 2 compatibility ends so that if you wish to write your app to be compatible with all milter versions, you can do so. You will find that the library file is split up into sections defining the constants for client server options and flags.  Note that there are multiple milter versions (2, 3, 4, 6) and not everything is available in earlier versions than 6.  See libmilter documentation for specifics on what is available in what versions.  I will show where the version 2 compatibility ends so that if you wish to write your app to be compatible with all milter versions, you can do so.
  
-===== Constants =====+==== Constants ====
  
-==== SMFIF_* ====+=== SMFIF_* ===
 These are flags that are set defining mutations that we wish to make in our app.  This first set is compatible with version 2 of the milter protocol. These are flags that are set defining mutations that we wish to make in our app.  This first set is compatible with version 2 of the milter protocol.
  
Line 40: Line 55:
 ^ SMFIF_ALLOPTS | This sets all flags | ^ SMFIF_ALLOPTS | This sets all flags |
  
-==== SMFIP_* ====+=== SMFIP_* ===
 These are protocol options, and can be set in a couple of different ways.  The first way is to simply set them when setting up your factory (TODO: link to the factory section).  The other way, at least for the ''SMFIP_NO*'' and ''SMFIP_NR_*'' options, is to use decorators.  That will be explained later.  First, we will define the protocol options. These are protocol options, and can be set in a couple of different ways.  The first way is to simply set them when setting up your factory (TODO: link to the factory section).  The other way, at least for the ''SMFIP_NO*'' and ''SMFIP_NR_*'' options, is to use decorators.  That will be explained later.  First, we will define the protocol options.
  
Line 75: Line 90:
 ^ SMFIP_ALLPROTOS | Sets all protocol options | ^ SMFIP_ALLPROTOS | Sets all protocol options |
  
-==== Response Constants ====+=== Response Constants ===
 These are what you use to reply to the MTA with what you want it to do currently.  The default is to just reply with a CONTINUE (do nothing).  You should use these as the ''return'' value from your callbacks. These are what you use to reply to the MTA with what you want it to do currently.  The default is to just reply with a CONTINUE (do nothing).  You should use these as the ''return'' value from your callbacks.
  
Line 98: Line 113:
 | SHUTDOWN | 421: shutdown (internal to MTA) | | SHUTDOWN | 421: shutdown (internal to MTA) |
  
-==== All the Rest ====+=== All the Rest ===
 All the rest of the standard constants are defined as well.  I've only documented the ones above as these are the only ones directly used. All the rest of the standard constants are defined as well.  I've only documented the ones above as these are the only ones directly used.
  
-===== Overridable Callbacks =====+==== Overridable Callbacks ====
 Here is the list of the overridable callbacks.  Any of these can be optionally overridden in a subclass of the ''MilterProtocol'' base class. Here is the list of the overridable callbacks.  Any of these can be optionally overridden in a subclass of the ''MilterProtocol'' base class.
  
Line 108: Line 123:
 As an aside, all this information is also available with a ''pydoc libmilter''. As an aside, all this information is also available with a ''pydoc libmilter''.
  
-==== connect(self , hostname , family , ip , port , cmdDict) ====+=== connect(self , hostname , family , ip , port , cmdDict) ===
 This gets the connection info: This gets the connection info:
  
Line 118: Line 133:
 | dict | cmdDict | The raw dictionary of items sent by the MTA | | dict | cmdDict | The raw dictionary of items sent by the MTA |
  
-==== helo(self , heloname) ====+=== helo(self , heloname) ===
 This gets the HELO string sent by the client This gets the HELO string sent by the client
  
Line 124: Line 139:
 | str | heloname | What the client HELOed as | | str | heloname | What the client HELOed as |
  
-==== mailFrom(self , frAddr , cmdDict) ====+=== mailFrom(self , frAddr , cmdDict) ===
 This gets the MAIL FROM envelope address This gets the MAIL FROM envelope address
  
Line 131: Line 146:
 | dict | cmdDict | The raw dictionary of items sent by the MTA | | dict | cmdDict | The raw dictionary of items sent by the MTA |
  
-==== rcpt(self , recip , cmdDict) ====+=== rcpt(self , recip , cmdDict) ===
 This gets the RCPT TO envelope address This gets the RCPT TO envelope address
  
Line 138: Line 153:
 | dict | cmdDict | The raw dictionary of items sent by the MTA | | dict | cmdDict | The raw dictionary of items sent by the MTA |
  
-==== header(self , key , val , cmdDict) ====+=== header(self , key , val , cmdDict) ===
 This gets one header from the email at a time.  The "key" is the LHS of the header and the "val" is RHS. This gets one header from the email at a time.  The "key" is the LHS of the header and the "val" is RHS.
  
Line 148: Line 163:
 | dict | cmdDict | The raw dictionary of items sent by the MTA | | dict | cmdDict | The raw dictionary of items sent by the MTA |
  
-==== eoh(self , cmdDict) ====+=== eoh(self , cmdDict) ===
 This tells you when all the headers have been received This tells you when all the headers have been received
  
Line 154: Line 169:
 | dict | cmdDict | The raw dictionary of items sent by the MTA | | dict | cmdDict | The raw dictionary of items sent by the MTA |
  
-==== data(self , cmdDict) ====+=== data(self , cmdDict) ===
 This is called when the client sends DATA This is called when the client sends DATA
  
Line 160: Line 175:
 | dict | cmdDict | The raw dictionary of items sent by the MTA | | dict | cmdDict | The raw dictionary of items sent by the MTA |
  
-==== body(self , chunk , cmdDict) ====+=== body(self , chunk , cmdDict) ===
 This gets a chunk of the body of the email from the MTA. This will be called many times for a large email. This gets a chunk of the body of the email from the MTA. This will be called many times for a large email.
  
Line 167: Line 182:
 | dict | cmdDict | The raw dictionary of items sent by the MTA | | dict | cmdDict | The raw dictionary of items sent by the MTA |
  
-====  eob(self , cmdDict) ====+===  eob(self , cmdDict) ===
 This signals that the MTA has sent the entire body of the email. This is the callback where you can use [[#modification methods]], such as addHeader(), delRcpt(), etc.  If you return CONTINUE from this method, it will be the same as an returning ACCEPT. This signals that the MTA has sent the entire body of the email. This is the callback where you can use [[#modification methods]], such as addHeader(), delRcpt(), etc.  If you return CONTINUE from this method, it will be the same as an returning ACCEPT.
  
Line 173: Line 188:
 | dict | cmdDict | The raw dictionary of items sent by the MTA | | dict | cmdDict | The raw dictionary of items sent by the MTA |
  
-==== close(self) ====+=== close(self) ===
 Here, you can close any open resources.  Here, you can close any open resources. 
  
 **NOTE:** this method is **always** called when everything is complete. **NOTE:** this method is **always** called when everything is complete.
  
-==== abort(self) ====+=== abort(self) ===
 This is called when an ABORT is received from the MTA. This is called when an ABORT is received from the MTA.
  
 **NOTE:** Postfix will send an ABORT at the end of every message. **NOTE:** Postfix will send an ABORT at the end of every message.
  
-===== Modification Methods =====+==== Modification Methods ====
 These are all the methods that can **only be used in the eob()** callback in your milter to modify the message/senders/recipients themselves.  Note that your MTA must support these methods for them to be used.  You also must tell the MTA you are going to use these in your application with the appropriate [[#SMFIF_*]] options passed into the [[#Factories|Factory]]. These are all the methods that can **only be used in the eob()** callback in your milter to modify the message/senders/recipients themselves.  Note that your MTA must support these methods for them to be used.  You also must tell the MTA you are going to use these in your application with the appropriate [[#SMFIF_*]] options passed into the [[#Factories|Factory]].
  
-==== addRcpt(self , rcpt , esmtpAdd=''====+=== addRcpt(self , rcpt , esmtpAdd='') ===
 This will tell the MTA to add a recipient to the email. This will tell the MTA to add a recipient to the email.
  
Line 198: Line 213:
 **IMPORTANT:**  If you want to use ''esmtpAdd'' to add additional ESMTP parameters, you have to set the ''SMFIF_ADDRCPT_PAR'' option instead of (or in addition to) the ''SMFIF_ADDRCPT'' option. **IMPORTANT:**  If you want to use ''esmtpAdd'' to add additional ESMTP parameters, you have to set the ''SMFIF_ADDRCPT_PAR'' option instead of (or in addition to) the ''SMFIF_ADDRCPT'' option.
  
-==== delRcpt(self , rcpt) ====+=== delRcpt(self , rcpt) ===
 This will tell the MTA to delete a recipient from the email This will tell the MTA to delete a recipient from the email
  
Line 205: Line 220:
 You must have the ''SMFIF_DELRCPT'' option set to use this. You must have the ''SMFIF_DELRCPT'' option set to use this.
  
-==== replBody(self , body) ====+=== replBody(self , body) ===
 This will replace the body of the email with a new body.  This is how you would make changes to the body of an email.  It is up to you to store the email, modify it and send the entire body back to the MTA with this method. This will replace the body of the email with a new body.  This is how you would make changes to the body of an email.  It is up to you to store the email, modify it and send the entire body back to the MTA with this method.
  
 You must have the ''SMFIF_CHGBODY'' option set to use this. You must have the ''SMFIF_CHGBODY'' option set to use this.
  
-==== addHeader(self , key , val) ====+=== addHeader(self , key , val) ===
 This will add a header to the email in the form: "key: val" This will add a header to the email in the form: "key: val"
  
 You must have the ''SMFIF_ADDHDRS'' option set to use this. You must have the ''SMFIF_ADDHDRS'' option set to use this.
  
-==== chgHeader(self , key , val='' , index=1) ====+=== chgHeader(self , key , val='' , index=1) ===
 This will change a header in the email.  The ''key'' should be exectly what was received in header().  If ''val'' is empty, the header will be removed.  ''index'' refers to which header to remove in the case that there are multiple headers with the same ''key'' (//Received:// is one example).  Note that ''index'' starts at "1". This will change a header in the email.  The ''key'' should be exectly what was received in header().  If ''val'' is empty, the header will be removed.  ''index'' refers to which header to remove in the case that there are multiple headers with the same ''key'' (//Received:// is one example).  Note that ''index'' starts at "1".
  
 You must have the ''SMFIF_CHGHDRS'' option set to use this. You must have the ''SMFIF_CHGHDRS'' option set to use this.
  
-==== quarantine(self , msg=''====+=== quarantine(self , msg='') ===
 This tells the MTA to quarantine the message (put it in the HOLD queue in Postfix).  You can optionally set a ''msg'' to be potentially logged by the MTA. This tells the MTA to quarantine the message (put it in the HOLD queue in Postfix).  You can optionally set a ''msg'' to be potentially logged by the MTA.
  
 You must have the ''SMFIF_QUARANTINE'' option set to use this. You must have the ''SMFIF_QUARANTINE'' option set to use this.
  
-==== setReply(self , rcode , xcode , msg) ====+=== setReply(self , rcode , xcode , msg) ===
 Sets the reply that the MTA will use for this message.\\ Sets the reply that the MTA will use for this message.\\
 The ''rcode'' is the 3 digit code to use (ex. 554 or 250).\\ The ''rcode'' is the 3 digit code to use (ex. 554 or 250).\\
Line 236: Line 251:
 </code> </code>
  
-==== chgFrom(self , frAddr , esmtpAdd=''====+=== chgFrom(self , frAddr , esmtpAdd='') ===
 This tells the MTA to change the envelope From address, with optional ESMTP extensions in ''esmtpAdd''. This tells the MTA to change the envelope From address, with optional ESMTP extensions in ''esmtpAdd''.
  
 You must have the ''SMFIF_CHGFROM'' option set to use this. You must have the ''SMFIF_CHGFROM'' option set to use this.
  
-==== skip(self) ====+=== skip(self) ===
 This tells the MTA that we don't want any more of this type of callback. This tells the MTA that we don't want any more of this type of callback.
  
Line 248: Line 263:
 You must have the ''SMFIP_SKIP'' option set to use this. You must have the ''SMFIP_SKIP'' option set to use this.
  
-===== Factories =====+==== Factories ====
 When you are building your own milter, you will have to choose a factory to use to run your milter.  There are 3 different factories built in, they are: When you are building your own milter, you will have to choose a factory to use to run your milter.  There are 3 different factories built in, they are:
  
Line 269: Line 284:
 | sockChmod | int | What to ''chmod'' the unix domain socket to after creation.  Optional, with a default of ''0666'' Obviously, this is ignored when you are creating an "inet" socket. | | sockChmod | int | What to ''chmod'' the unix domain socket to after creation.  Optional, with a default of ''0666'' Obviously, this is ignored when you are creating an "inet" socket. |
  
-==== AsyncFactory ====+=== AsyncFactory ===
 This factory is somewhat modeled after a Twisted implementation, though with some differences.  This will run everything, by default, in a single thread and single process.  This means that things must return very quickly (there is a way around this).  This is not the recommended factory to use if you are doing anything beyond trivial checks of the message.  The advantage to this factory, however, is that it makes sharing of global resources very easy. This factory is somewhat modeled after a Twisted implementation, though with some differences.  This will run everything, by default, in a single thread and single process.  This means that things must return very quickly (there is a way around this).  This is not the recommended factory to use if you are doing anything beyond trivial checks of the message.  The advantage to this factory, however, is that it makes sharing of global resources very easy.
  
Line 290: Line 305:
 **NOTE:** No "mixin" class needs to be inherited with this factory's use. **NOTE:** No "mixin" class needs to be inherited with this factory's use.
  
-==== ThreadFactory ====+=== ThreadFactory ===
 This is a factory, wherein each connection from the MTA ends up in it's own thread.  This is exactly how the C libmilter works. This is a factory, wherein each connection from the MTA ends up in it's own thread.  This is exactly how the C libmilter works.
  
Line 304: Line 319:
 </code> </code>
  
-==== ForkFactory ====+=== ForkFactory ===
 This is a factory that will fork a child process for each incoming connection from the MTA.  This will add some more overhead on your machine, but is recommended for the best concurrency.  See the description of [[#ThreadFactory]] as to why this is. This is a factory that will fork a child process for each incoming connection from the MTA.  This will add some more overhead on your machine, but is recommended for the best concurrency.  See the description of [[#ThreadFactory]] as to why this is.
  
Line 316: Line 331:
 </code> </code>
  
-====== Example ======+===== Example =====
 See the examples directory in the distribution for a complete test milter example using the [[#ForkFactory]]. See the examples directory in the distribution for a complete test milter example using the [[#ForkFactory]].
programming/python/python-libmilter.1329597405.txt.gz · Last modified: 2012/02/18 20:36 by jay