21 Commits

Author SHA1 Message Date
88a3f1f3a5 „gomilter.go“ ändern 2018-12-29 13:21:33 +00:00
Dolf Schimmel
ec6c4f3113 Merge branch 'master' of https://github.com/davrux/gomilter into HEAD 2015-12-31 23:08:11 +01:00
Dolf Schimmel (Freeaqingme)
c60841ef06 Revert "Make gobencode() optional in SetPriv(), return errors instead of integers"
This reverts commit 6a57e8c3fc.
2015-11-25 20:42:41 +01:00
Dolf Schimmel (Freeaqingme)
01964e5391 Merge with upstream 2015-11-24 17:49:00 +01:00
Dolf Schimmel (Freeaqingme)
0c9e549560 go fmt 2015-11-24 17:46:44 +01:00
Dolf Schimmel (Freeaqingme)
d6bf69609f Merge branch 'master' of https://github.com/leonrbaker/gomilter 2015-11-24 17:44:53 +01:00
Dolf Schimmel (Freeaqingme)
6a57e8c3fc Make gobencode() optional in SetPriv(), return errors instead of integers 2015-11-24 17:39:31 +01:00
Dolf Schimmel (Freeaqingme)
39a4baf02e Recover from errors in Go_xxfi_connect()
There appears to be a race condition in libmilter in which
hostaddr is set to nil before or while calling the connect
callback if the client aborts its connection during that
window.
2015-11-24 17:38:59 +01:00
Meik Kreyenkoetter
f724fd7bcb Linker path for Debian/Ubuntu.
Libmilter is installed in /usr/lib/libmilter on Debian and Ubuntu
systems. Added additional Linker path for cgo.
2015-10-02 11:51:58 +02:00
Leon Baker
c99ec2226f Merge pull request #7 from Freeaqingme/master
Fix IPv6 addresses that sometimes caused memory stomping
2015-09-25 09:36:22 +02:00
Dolf Schimmel
00d59ae5ab Fix IPv6 addresses that sometimes caused memory stomping 2015-09-16 19:42:24 +02:00
Leon Baker
dc84b56af1 Merge pull request #6 from leonrbaker/logging
Implemented the Log package for logging
2015-09-03 12:14:40 +02:00
Leon Baker
64fb9af89b Merge pull request #5 from Freeaqingme/master
Max IPv6 address length is 45 characters, not 16
2015-09-03 12:07:19 +02:00
Dolf Schimmel (Freeaqingme)
385e5106c2 Max IPv6 address length is 45 characters, not 16
The idea was to reserve 16 bytes for an IPv6 address. But given that a human representation is used rather than a
128 bit integer, we need 45 characters at most.
2015-09-01 21:15:14 +02:00
Leon Baker
882ca25f76 Implemented the Log package for logging 2015-06-16 12:34:30 +02:00
Leon Baker
8746179952 Update README and fix sample filter 2015-06-15 16:44:41 +02:00
Leon Baker
4dc71bdbbc Merge pull request #4 from Freeaqingme/master
Implement stop(), Support IPv6, Custom log callbacks, reformatted code
2015-06-13 18:22:12 +02:00
Dolf Schimmel
a6870a91fe Also include netinet/in.h necessary for FreeBSD 2015-06-11 11:40:55 +02:00
Dolf Schimmel (Freeaqingme)
24ed8dcbc3 Add support for ipv6 2015-06-01 22:16:02 +02:00
Dolf Schimmel (Freeaqingme)
c0f4b47c5e Implement stop() 2015-06-01 00:04:36 +02:00
Dolf Schimmel (Freeaqingme)
13b82e7dd5 Reformatted code 2015-05-31 01:02:17 +02:00
5 changed files with 730 additions and 611 deletions

3
.gitignore vendored Normal file
View File

@@ -0,0 +1,3 @@
# Compiled test programs
samplefilter
samplefilter2

View File

@@ -1,2 +1,95 @@
# gomilter # gomilter
Go Bindings for libmilter Go Bindings for Sendmail's libmilter
Tested on Linux and FreeBSD
## Installation
The Sendmail header file libmilter/mfapi.h is required. For Redhat/CentOS, install the sendmail-devel package:
```sh
yum install sendmail-devel
```
Install the gomilter package:
```sh
go get github.com/leonrbaker/gomilter
```
##Usage
The milter is implemented in a struct. Start by defining your own struct type and embeding the gomilter MilterRaw struct.
```go
type Mymilter struct {
gomilter.MilterRaw // Embed the basic functionality.
}
```
Milter callbacks are added by implementing methods for the struct with matching predefined names.
### Callbacks
* Connect
* Helo
* EnvFrom
* EnvRcpt
* Header
* Eoh
* Body
* Eom
* Abort
* Close
Not all the callbacks need to be defined. The callbacks are explained on the milter.org site. Unfortunately the milter.org site has been shut down but it is still on [web.archive.org](http://web.archive.org/web/20150510034154/https://www.milter.org/developers/api/index)
### Message Modification Functions
* AddHeader
* ChgHeader
* InsHeader
* ChgFrom
* AddRcpt
* AddRcpt_Par
* DelRcpt
* ReplaceBody
### Other Message Handling Functions
* progress
### Startup
The Socket field of the milter struct must be set. For example:
```go
mymilter.Socket = "unix:/var/gomilter/socket"
```
Control is handed over to the libmilter smfi_main function by calling the Run method and passing it a pointer to your milter struct
```go
gomilter.Run(mymilter)
```
The milter has a Stop method which calls the libmilter smfi_stop function.
### Private Data
libmilter is able to store private data for a connection. This data can be accessed from other functions and callbacks for the same connection. You can pass a pointer to any data structure to SetPriv. The data is retrieved with GetPriv
```go
t := T{1, 2, 3}
m.SetPriv(ctx, &t)
```
Retrieve the data with
```go
var t T
m.GetPriv(ctx, &t))
```
GetPriv should only be called once. If the private data is needed in another function or callback then call SetPriv again.
## Sample Programs
There are two sample programs included, samplefilter.go and samplefilter2.go
##Other Libraries
A usefull MIME parsing library is [go.enmime](https://github.com/jhillyerd/go.enmime)

View File

@@ -10,28 +10,33 @@ gomilter.go
*/ */
//+build cgo
package gomilter package gomilter
/* /*
#cgo LDFLAGS: -lmilter #cgo LDFLAGS: -L/usr/lib/libmilter -lmilter
#include <stdlib.h> #include <stdlib.h>
//#include <string.h> #include <netinet/in.h>
#include <arpa/inet.h>
#include "libmilter/mfapi.h" #include "libmilter/mfapi.h"
#include "filter.h" #include "filter.h"
*/ */
import "C" import "C"
import ( import (
"fmt"
"unsafe"
"encoding/binary"
"reflect"
"os"
"strings"
"bytes" "bytes"
"encoding/binary"
"encoding/gob" "encoding/gob"
"fmt"
"log"
"net"
"os"
"reflect"
"strings"
"unsafe"
) )
type sockaddr_in struct { type sockaddr_in struct {
@@ -73,6 +78,7 @@ type Milter interface {
GetDebug() bool GetDebug() bool
GetFlags() int GetFlags() int
GetSocket() string GetSocket() string
GetLogger() *log.Logger
} }
// An "empty" Milter with no callback functions // An "empty" Milter with no callback functions
@@ -81,6 +87,7 @@ type MilterRaw struct {
Debug bool Debug bool
Flags int Flags int
Socket string Socket string
Logger *log.Logger
} }
func (m *MilterRaw) GetFilterName() string { func (m *MilterRaw) GetFilterName() string {
@@ -99,9 +106,13 @@ func (m *MilterRaw) GetSocket() string {
return m.Socket return m.Socket
} }
func (m *MilterRaw) GetLogger() *log.Logger {
return m.Logger
}
// ********* Callback checking types ********* // ********* Callback checking types *********
type checkForConnect interface { type checkForConnect interface {
Connect(ctx uintptr, hostname, ip string) (sfsistat int8) Connect(ctx uintptr, hostname string, ip net.IP) (sfsistat int8)
} }
type checkForHelo interface { type checkForHelo interface {
@@ -142,6 +153,7 @@ type checkForClose interface {
// ********* Global Milter variable ********* // ********* Global Milter variable *********
var milter Milter var milter Milter
var logger *log.Logger
// ********* Utility Functions (not exported) ********* // ********* Utility Functions (not exported) *********
@@ -193,37 +205,44 @@ func GobDecode(buf []byte, data interface{}) error {
return nil return nil
} }
// ********* Filter Callback functions ********* // ********* Filter Callback functions *********
// These are registered with Milter // These are registered with Milter
// They are only called if they get registered but need to be defined anyway // They are only called if they get registered but need to be defined anyway
//export Go_xxfi_connect //export Go_xxfi_connect
func Go_xxfi_connect(ctx *C.SMFICTX, hostname *C.char, hostaddr *C._SOCK_ADDR) C.sfsistat { func Go_xxfi_connect(ctx *C.SMFICTX, hostname *C.char, hostaddr *C._SOCK_ADDR) (sfsistat C.sfsistat) {
ctxptr := ctx2int(ctx) defer func(sfsistat *C.sfsistat) {
// Check if the host address is a regular ipv4 address if r := recover(); r != nil {
if hostaddr.sa_family == C.AF_INET { logger.Printf("Panic caught in Go_xxfi_connect(): %s", r)
//LoggerPrintln(hostaddr.sa_data) *sfsistat = 75 // tempfail
}
}(&sfsistat)
// hostaddrin is a parallel data structure of the C type hostaddr ctxptr := ctx2int(ctx)
//var hostaddrin *sockaddr_in var ip net.IP
if hostaddr.sa_family == C.AF_INET {
hostaddrin := (*sockaddr_in)(unsafe.Pointer(hostaddr)) hostaddrin := (*sockaddr_in)(unsafe.Pointer(hostaddr))
//LoggerPrintln(hostaddrin)
ip_addr := make([]byte, 4) ip_addr := make([]byte, 4)
binary.LittleEndian.PutUint32(ip_addr, hostaddrin.sin_addr) binary.LittleEndian.PutUint32(ip_addr, hostaddrin.sin_addr)
ip = net.IPv4(ip_addr[0], ip_addr[1], ip_addr[2], ip_addr[3])
// Call our application's callback } else if hostaddr.sa_family == C.AF_INET6 {
m := milter.(checkForConnect) sa_in := (*C.struct_sockaddr_in6)(unsafe.Pointer(hostaddr))
code := m.Connect(ctxptr, C.GoString(hostname), fmt.Sprintf("%d.%d.%d.%d", ip_addr[0], ip_addr[1], ip_addr[2], ip_addr[3])) ip = net.IP(C.GoBytes(unsafe.Pointer(&sa_in.sin6_addr), 16))
} else {
if milter.GetDebug() { if milter.GetDebug() {
LoggerPrintf("Connect callback returned: %d\n", code) logger.Println("hostaddr.sa_family value not implemented")
}
ip = net.ParseIP("::")
}
m := milter.(checkForConnect)
code := m.Connect(ctxptr, C.GoString(hostname), ip)
if milter.GetDebug() {
logger.Printf("Connect callback returned: %d\n", code)
} }
return C.sfsistat(code) return C.sfsistat(code)
}
if milter.GetDebug() {
LoggerPrintln("hostaddr.sa_family value not implemented")
}
return C.SMFIS_CONTINUE
} }
//export Go_xxfi_helo //export Go_xxfi_helo
@@ -231,7 +250,7 @@ func Go_xxfi_helo(ctx *C.SMFICTX, helohost *C.char) C.sfsistat {
m := milter.(checkForHelo) m := milter.(checkForHelo)
code := m.Helo(ctx2int(ctx), C.GoString(helohost)) code := m.Helo(ctx2int(ctx), C.GoString(helohost))
if milter.GetDebug() { if milter.GetDebug() {
LoggerPrintf("Helo callback returned: %d\n", code) logger.Printf("Helo callback returned: %d\n", code)
} }
return C.sfsistat(code) return C.sfsistat(code)
} }
@@ -242,7 +261,7 @@ func Go_xxfi_envfrom(ctx *C.SMFICTX, argv **C.char) C.sfsistat {
m := milter.(checkForEnvFrom) m := milter.(checkForEnvFrom)
code := m.EnvFrom(ctx2int(ctx), cStringArrayToSlice(argv)) code := m.EnvFrom(ctx2int(ctx), cStringArrayToSlice(argv))
if milter.GetDebug() { if milter.GetDebug() {
LoggerPrintf("EnvFrom callback returned: %d\n", code) logger.Printf("EnvFrom callback returned: %d\n", code)
} }
return C.sfsistat(code) return C.sfsistat(code)
} }
@@ -253,7 +272,7 @@ func Go_xxfi_envrcpt(ctx *C.SMFICTX, argv **C.char) C.sfsistat {
m := milter.(checkForEnvRcpt) m := milter.(checkForEnvRcpt)
code := m.EnvRcpt(ctx2int(ctx), cStringArrayToSlice(argv)) code := m.EnvRcpt(ctx2int(ctx), cStringArrayToSlice(argv))
if milter.GetDebug() { if milter.GetDebug() {
LoggerPrintf("EnvRcpt callback returned: %d\n", code) logger.Printf("EnvRcpt callback returned: %d\n", code)
} }
return C.sfsistat(code) return C.sfsistat(code)
} }
@@ -263,7 +282,7 @@ func Go_xxfi_header(ctx *C.SMFICTX, headerf, headerv *C.char) C.sfsistat {
m := milter.(checkForHeader) m := milter.(checkForHeader)
code := m.Header(ctx2int(ctx), C.GoString(headerf), C.GoString(headerv)) code := m.Header(ctx2int(ctx), C.GoString(headerf), C.GoString(headerv))
if milter.GetDebug() { if milter.GetDebug() {
LoggerPrintf("Header callback returned: %d\n", code) logger.Printf("Header callback returned: %d\n", code)
} }
return C.sfsistat(code) return C.sfsistat(code)
} }
@@ -274,7 +293,7 @@ func Go_xxfi_eoh(ctx *C.SMFICTX) C.sfsistat {
m := milter.(checkForEoh) m := milter.(checkForEoh)
code := m.Eoh(ctx2int(ctx)) code := m.Eoh(ctx2int(ctx))
if milter.GetDebug() { if milter.GetDebug() {
LoggerPrintf("Eoh callback returned: %d\n", code) logger.Printf("Eoh callback returned: %d\n", code)
} }
return C.sfsistat(code) return C.sfsistat(code)
} }
@@ -286,12 +305,12 @@ func Go_xxfi_body(ctx *C.SMFICTX, bodyp *C.uchar, bodylen C.size_t) C.sfsistat {
// Our bit slice is backed by the original body. No copy is made here // Our bit slice is backed by the original body. No copy is made here
// Be aware that converting the byte slice to string will make a copy // Be aware that converting the byte slice to string will make a copy
var b []byte var b []byte
b = (*[1<<30]byte)(unsafe.Pointer(bodyp))[0:bodylen] b = (*[1 << 30]byte)(unsafe.Pointer(bodyp))[0:bodylen]
// Call our application's callback // Call our application's callback
m := milter.(checkForBody) m := milter.(checkForBody)
code := m.Body(ctx2int(ctx), b) code := m.Body(ctx2int(ctx), b)
if milter.GetDebug() { if milter.GetDebug() {
LoggerPrintf("Body callback returned: %d\n", code) logger.Printf("Body callback returned: %d\n", code)
} }
return C.sfsistat(code) return C.sfsistat(code)
} }
@@ -302,7 +321,7 @@ func Go_xxfi_eom(ctx *C.SMFICTX) C.sfsistat {
m := milter.(checkForEom) m := milter.(checkForEom)
code := m.Eom(ctx2int(ctx)) code := m.Eom(ctx2int(ctx))
if milter.GetDebug() { if milter.GetDebug() {
LoggerPrintf("Eom callback returned: %d\n", code) logger.Printf("Eom callback returned: %d\n", code)
} }
return C.sfsistat(code) return C.sfsistat(code)
} }
@@ -313,7 +332,7 @@ func Go_xxfi_abort(ctx *C.SMFICTX) C.sfsistat {
m := milter.(checkForAbort) m := milter.(checkForAbort)
code := m.Abort(ctx2int(ctx)) code := m.Abort(ctx2int(ctx))
if milter.GetDebug() { if milter.GetDebug() {
LoggerPrintf("Abort callback returned: %d\n", code) logger.Printf("Abort callback returned: %d\n", code)
} }
return C.sfsistat(code) return C.sfsistat(code)
} }
@@ -324,7 +343,7 @@ func Go_xxfi_close(ctx *C.SMFICTX) C.sfsistat {
m := milter.(checkForClose) m := milter.(checkForClose)
code := m.Close(ctx2int(ctx)) code := m.Close(ctx2int(ctx))
if milter.GetDebug() { if milter.GetDebug() {
LoggerPrintf("Close callback returned: %d\n", code) logger.Printf("Close callback returned: %d\n", code)
} }
return C.sfsistat(code) return C.sfsistat(code)
} }
@@ -340,6 +359,7 @@ func GetSymVal(ctx uintptr, symname string) string {
return C.GoString(cval) return C.GoString(cval)
} }
// See also: http://bit.ly/1HVWA9I
func SetPriv(ctx uintptr, privatedata interface{}) int { func SetPriv(ctx uintptr, privatedata interface{}) int {
// privatedata seems to work for any data type // privatedata seems to work for any data type
// Structs must have exported fields // Structs must have exported fields
@@ -438,7 +458,7 @@ func GetPriv(ctx uintptr, privatedata interface{}) int {
return 0 return 0
} }
func SetReply (ctx uintptr, rcode, xcode, message string) int { func SetReply(ctx uintptr, rcode, xcode, message string) int {
type CtxPtr *C.struct_smfi_str type CtxPtr *C.struct_smfi_str
crcode := C.CString(rcode) crcode := C.CString(rcode)
defer C.free(unsafe.Pointer(crcode)) defer C.free(unsafe.Pointer(crcode))
@@ -450,7 +470,7 @@ func SetReply (ctx uintptr, rcode, xcode, message string) int {
return int(C.smfi_setreply(int2ctx(ctx), crcode, cxcode, cmessage)) return int(C.smfi_setreply(int2ctx(ctx), crcode, cxcode, cmessage))
} }
func SetMLReply (ctx uintptr, rcode, xcode string, message ...string) int { func SetMLReply(ctx uintptr, rcode, xcode string, message ...string) int {
/* Allows up to 32 lines of message /* Allows up to 32 lines of message
*/ */
@@ -468,7 +488,7 @@ func SetMLReply (ctx uintptr, rcode, xcode string, message ...string) int {
defer C.free(argv) defer C.free(argv)
// Assign each line to its address offset // Assign each line to its address offset
for i := 0; i < len(message); i++ { for i := 0; i < len(message); i++ {
linePtr := (**C.char)(unsafe.Pointer(uintptr(argv) + uintptr(i) * ptrSize)) linePtr := (**C.char)(unsafe.Pointer(uintptr(argv) + uintptr(i)*ptrSize))
line := C.CString(message[i]) line := C.CString(message[i])
defer C.free(unsafe.Pointer(line)) defer C.free(unsafe.Pointer(line))
*linePtr = line *linePtr = line
@@ -491,7 +511,7 @@ func AddHeader(ctx uintptr, headerf, headerv string) int {
defer C.free(unsafe.Pointer(cheaderv)) defer C.free(unsafe.Pointer(cheaderv))
// Call smfi_addheader // Call smfi_addheader
return int(C.smfi_addheader(int2ctx(ctx), cheaderf, cheaderv)); return int(C.smfi_addheader(int2ctx(ctx), cheaderf, cheaderv))
} }
func ChgHeader(ctx uintptr, headerf string, hdridx int, headerv string) int { func ChgHeader(ctx uintptr, headerf string, hdridx int, headerv string) int {
@@ -504,7 +524,7 @@ func ChgHeader(ctx uintptr, headerf string, hdridx int, headerv string) int {
defer C.free(unsafe.Pointer(cheaderv)) defer C.free(unsafe.Pointer(cheaderv))
// Call smfi_chgheader // Call smfi_chgheader
return int(C.smfi_chgheader(int2ctx(ctx), cheaderf, C.int(hdridx), cheaderv)); return int(C.smfi_chgheader(int2ctx(ctx), cheaderf, C.int(hdridx), cheaderv))
} }
func InsHeader(ctx uintptr, hdridx int, headerf, headerv string) int { func InsHeader(ctx uintptr, hdridx int, headerf, headerv string) int {
@@ -517,10 +537,9 @@ func InsHeader(ctx uintptr, hdridx int, headerf, headerv string) int {
defer C.free(unsafe.Pointer(cheaderv)) defer C.free(unsafe.Pointer(cheaderv))
// Call smfi_insheader // Call smfi_insheader
return int(C.smfi_insheader(int2ctx(ctx), C.int(hdridx), cheaderf, cheaderv)); return int(C.smfi_insheader(int2ctx(ctx), C.int(hdridx), cheaderf, cheaderv))
} }
func ChgFrom(ctx uintptr, mail, args string) int { func ChgFrom(ctx uintptr, mail, args string) int {
/* Change the envelope sender address. SMFIF_CHGFROM /* Change the envelope sender address. SMFIF_CHGFROM
*/ */
@@ -595,12 +614,17 @@ func progress(ctx uintptr) int {
// ********* Run the milter ********* // ********* Run the milter *********
func Run(amilter Milter) int { func Run(amilter Milter) int {
milter = amilter milter = amilter
// Setup logging
logger = milter.GetLogger()
if logger == nil {
// No logger defined so create a simple logger to Stdout
logger = log.New(os.Stdout, "", 0)
}
if milter.GetDebug() { if milter.GetDebug() {
LoggerPrintf ("Debugging enabled") logger.Println("Debugging enabled")
} }
// Declare an empty smfiDesc structure // Declare an empty smfiDesc structure
@@ -611,7 +635,7 @@ func Run(amilter Milter) int {
defer C.free(unsafe.Pointer(fname)) defer C.free(unsafe.Pointer(fname))
smfilter.xxfi_name = fname smfilter.xxfi_name = fname
if milter.GetDebug() { if milter.GetDebug() {
LoggerPrintf("Filter Name: %s\n", C.GoString(smfilter.xxfi_name)) logger.Printf("Filter Name: %s\n", C.GoString(smfilter.xxfi_name))
} }
// Set version code // Set version code
@@ -620,7 +644,7 @@ func Run(amilter Milter) int {
// Set Flags // Set Flags
smfilter.xxfi_flags = C.ulong(milter.GetFlags()) smfilter.xxfi_flags = C.ulong(milter.GetFlags())
if milter.GetDebug() { if milter.GetDebug() {
LoggerPrintf("Flags: 0x%b\n", smfilter.xxfi_flags) logger.Printf("Flags: 0x%b\n", smfilter.xxfi_flags)
} }
// Set Callbacks if they are implemented // Set Callbacks if they are implemented
@@ -628,117 +652,117 @@ func Run(amilter Milter) int {
// Check if Connect method was implemented // Check if Connect method was implemented
if _, ok := milter.(checkForConnect); ok { if _, ok := milter.(checkForConnect); ok {
if milter.GetDebug() { if milter.GetDebug() {
LoggerPrintln("Connect callback implemented") logger.Println("Connect callback implemented")
} }
C.setConnect(&smfilter) C.setConnect(&smfilter)
} else { } else {
if milter.GetDebug() { if milter.GetDebug() {
LoggerPrintln("Connect callback not implemented") logger.Println("Connect callback not implemented")
} }
} }
// Check if Helo method was implemented // Check if Helo method was implemented
if _, ok := milter.(checkForHelo); ok { if _, ok := milter.(checkForHelo); ok {
if milter.GetDebug() { if milter.GetDebug() {
LoggerPrintln("Helo callback implemented") logger.Println("Helo callback implemented")
} }
C.setHelo(&smfilter) C.setHelo(&smfilter)
} else { } else {
if milter.GetDebug() { if milter.GetDebug() {
LoggerPrintln("Helo callback not implemented") logger.Println("Helo callback not implemented")
} }
} }
// Check if EnvFrom method was implemented // Check if EnvFrom method was implemented
if _, ok := milter.(checkForEnvFrom); ok { if _, ok := milter.(checkForEnvFrom); ok {
if milter.GetDebug() { if milter.GetDebug() {
LoggerPrintln("EnvFrom callback implemented") logger.Println("EnvFrom callback implemented")
} }
C.setEnvFrom(&smfilter) C.setEnvFrom(&smfilter)
} else { } else {
if milter.GetDebug() { if milter.GetDebug() {
LoggerPrintln("EnvFrom callback not implemented") logger.Println("EnvFrom callback not implemented")
} }
} }
// Check if EnvRcpt method was implemented // Check if EnvRcpt method was implemented
if _, ok := milter.(checkForEnvRcpt); ok { if _, ok := milter.(checkForEnvRcpt); ok {
if milter.GetDebug() { if milter.GetDebug() {
LoggerPrintln("EnvRcpt callback implemented") logger.Println("EnvRcpt callback implemented")
} }
C.setEnvRcpt(&smfilter) C.setEnvRcpt(&smfilter)
} else { } else {
if milter.GetDebug() { if milter.GetDebug() {
LoggerPrintln("EnvRcpt callback not implemented") logger.Println("EnvRcpt callback not implemented")
} }
} }
// Check if Header method was implemented // Check if Header method was implemented
if _, ok := milter.(checkForHeader); ok { if _, ok := milter.(checkForHeader); ok {
if milter.GetDebug() { if milter.GetDebug() {
LoggerPrintln("Header callback implemented") logger.Println("Header callback implemented")
} }
C.setHeader(&smfilter) C.setHeader(&smfilter)
} else { } else {
if milter.GetDebug() { if milter.GetDebug() {
LoggerPrintln("Header callback not implemented") logger.Println("Header callback not implemented")
} }
} }
// Check if Eoh method was implemented // Check if Eoh method was implemented
if _, ok := milter.(checkForEoh); ok { if _, ok := milter.(checkForEoh); ok {
if milter.GetDebug() { if milter.GetDebug() {
LoggerPrintln("Eoh callback implemented") logger.Println("Eoh callback implemented")
} }
C.setEoh(&smfilter) C.setEoh(&smfilter)
} else { } else {
if milter.GetDebug() { if milter.GetDebug() {
LoggerPrintln("Eoh callback not implemented") logger.Println("Eoh callback not implemented")
} }
} }
// Check if Body method was implemented // Check if Body method was implemented
if _, ok := milter.(checkForBody); ok { if _, ok := milter.(checkForBody); ok {
if milter.GetDebug() { if milter.GetDebug() {
LoggerPrintln("Body callback implemented") logger.Println("Body callback implemented")
} }
C.setBody(&smfilter) C.setBody(&smfilter)
} else { } else {
if milter.GetDebug() { if milter.GetDebug() {
LoggerPrintln("Body callback not implemented") logger.Println("Body callback not implemented")
} }
} }
// Check if Eom method was implemented // Check if Eom method was implemented
if _, ok := milter.(checkForEom); ok { if _, ok := milter.(checkForEom); ok {
if milter.GetDebug() { if milter.GetDebug() {
LoggerPrintln("Eom callback implemented") logger.Println("Eom callback implemented")
} }
C.setEom(&smfilter) C.setEom(&smfilter)
} else { } else {
if milter.GetDebug() { if milter.GetDebug() {
LoggerPrintln("Eom callback not implemented") logger.Println("Eom callback not implemented")
} }
} }
// Check if Abort method was implemented // Check if Abort method was implemented
if _, ok := milter.(checkForAbort); ok { if _, ok := milter.(checkForAbort); ok {
if milter.GetDebug() { if milter.GetDebug() {
LoggerPrintln("Abort callback implemented") logger.Println("Abort callback implemented")
} }
C.setAbort(&smfilter) C.setAbort(&smfilter)
} else { } else {
if milter.GetDebug() { if milter.GetDebug() {
LoggerPrintln("Abort callback not implemented") logger.Println("Abort callback not implemented")
} }
} }
// Check if Close method was implemented // Check if Close method was implemented
if _, ok := milter.(checkForClose); ok { if _, ok := milter.(checkForClose); ok {
if milter.GetDebug() { if milter.GetDebug() {
LoggerPrintln("Close callback implemented") logger.Println("Close callback implemented")
} }
C.setClose(&smfilter) C.setClose(&smfilter)
} else { } else {
if milter.GetDebug() { if milter.GetDebug() {
LoggerPrintln("Close callback not implemented") logger.Println("Close callback not implemented")
} }
} }
if milter.GetDebug() { if milter.GetDebug() {
LoggerPrintln("smfilter:") logger.Println("smfilter:")
LoggerPrintln(fmt.Sprint(smfilter)) logger.Println(fmt.Sprint(smfilter))
} }
// Setup socket connection // Setup socket connection
@@ -755,29 +779,25 @@ func Run(amilter Milter) int {
csocket := C.CString(socket) csocket := C.CString(socket)
defer C.free(unsafe.Pointer(csocket)) defer C.free(unsafe.Pointer(csocket))
if code := C.smfi_setconn(csocket); code != 0 { if code := C.smfi_setconn(csocket); code != 0 {
LoggerPrintf("smfi_setconn failed: %d\n", code) logger.Printf("smfi_setconn failed: %d\n", code)
} }
// Register the filter // Register the filter
if code := C.smfi_register(smfilter); code == C.MI_FAILURE { if code := C.smfi_register(smfilter); code == C.MI_FAILURE {
LoggerPrintf("smfi_register failed: %d\n", code) logger.Printf("smfi_register failed: %d\n", code)
} }
// Hand control to libmilter // Hand control to libmilter
if milter.GetDebug() { if milter.GetDebug() {
LoggerPrintln("Handing over to libmilter") logger.Println("Handing over to libmilter")
} }
result := C.smfi_main() result := C.smfi_main()
if milter.GetDebug() { if milter.GetDebug() {
LoggerPrintf("smfi_main returned: %v\n", result) logger.Printf("smfi_main returned: %v\n", result)
} }
return int(result) return int(result)
} }
var LoggerPrintln func(...interface {}) func Stop() {
var LoggerPrintf func(string, ...interface {}) C.smfi_stop()
func init() {
LoggerPrintln = func(i ...interface {}) { fmt.Println(i...) }
LoggerPrintf = func(i string, j ...interface {}) { fmt.Printf(i, j...) }
} }

View File

@@ -15,13 +15,10 @@ Sample Milter
package main package main
import ( import (
"." "."
"fmt" "fmt"
) )
type Mymilter struct { type Mymilter struct {
gomilter.MilterRaw // Embed the basic functionality. No callbacks yet gomilter.MilterRaw // Embed the basic functionality. No callbacks yet
} }
@@ -43,7 +40,8 @@ func main() {
mymilter := new(Mymilter) mymilter := new(Mymilter)
mymilter.FilterName = "TestFilter" mymilter.FilterName = "TestFilter"
mymilter.Debug = true mymilter.Debug = true
mymilter.Flags = gomilter.AddHdrs|gomilter.AddRcpt mymilter.Flags = gomilter.ADDHDRS | gomilter.ADDRCPT
mymilter.Socket = "unix:/var/milterattachcheck/socket"
// Start Milter // Start Milter
gomilter.Run(mymilter) gomilter.Run(mymilter)

View File

@@ -7,10 +7,9 @@ Copyright (c) 2015 Leon Baker
package main package main
import ( import (
m "github.com/leonrbaker/gomilter" m "github.com/leonrbaker/gomilter"
"fmt" "log"
"os"
) )
type Mymilter struct { type Mymilter struct {
@@ -24,11 +23,13 @@ type T struct {
C string C string
} }
var logger *log.Logger
// Define the callback functions we are going to use // Define the callback functions we are going to use
func (milter *Mymilter) Connect(ctx uintptr, hostname, ip string) (sfsistat int8) { func (milter *Mymilter) Connect(ctx uintptr, hostname, ip string) (sfsistat int8) {
fmt.Println("mymilter.Connect was called") logger.Println("mymilter.Connect was called")
fmt.Printf("hostname: %s\n", hostname) logger.Printf("hostname: %s\n", hostname)
fmt.Printf("ip: %s\n", ip) logger.Printf("ip: %s\n", ip)
t := T{1, hostname, "Test"} t := T{1, hostname, "Test"}
m.SetPriv(ctx, &t) m.SetPriv(ctx, &t)
@@ -37,56 +38,56 @@ func (milter *Mymilter) Connect(ctx uintptr, hostname, ip string) (sfsistat int8
} }
func (milter *Mymilter) Helo(ctx uintptr, helohost string) (sfsistat int8) { func (milter *Mymilter) Helo(ctx uintptr, helohost string) (sfsistat int8) {
fmt.Println("mymilter.Helo was called") logger.Println("mymilter.Helo was called")
fmt.Printf("helohost: %s\n", helohost) logger.Printf("helohost: %s\n", helohost)
return return
} }
func (milter *Mymilter) EnvFrom(ctx uintptr, myargv []string) (sfsistat int8) { func (milter *Mymilter) EnvFrom(ctx uintptr, myargv []string) (sfsistat int8) {
fmt.Println("mymilter.EnvFrom was called") logger.Println("mymilter.EnvFrom was called")
fmt.Printf("myargv: %s\n", myargv) logger.Printf("myargv: %s\n", myargv)
// Show the value of a symbol // Show the value of a symbol
fmt.Printf("{mail_addr}: %v\n", m.GetSymVal(ctx, "{mail_addr}")) logger.Printf("{mail_addr}: %v\n", m.GetSymVal(ctx, "{mail_addr}"))
return return
} }
func (milter *Mymilter) EnvRcpt(ctx uintptr, myargv []string) (sfsistat int8) { func (milter *Mymilter) EnvRcpt(ctx uintptr, myargv []string) (sfsistat int8) {
fmt.Println("mymilter.EnvRcpt was called") logger.Println("mymilter.EnvRcpt was called")
fmt.Printf("myargv: %s\n", myargv) logger.Printf("myargv: %s\n", myargv)
// Show the value of a symbol // Show the value of a symbol
fmt.Printf("{rcpt_addr}: %v\n", m.GetSymVal(ctx, "{rcpt_addr}")) logger.Printf("{rcpt_addr}: %v\n", m.GetSymVal(ctx, "{rcpt_addr}"))
return return
} }
func (milter *Mymilter) Header(ctx uintptr, headerf, headerv string) (sfsistat int8) { func (milter *Mymilter) Header(ctx uintptr, headerf, headerv string) (sfsistat int8) {
fmt.Println("mymilter.Header was called") logger.Println("mymilter.Header was called")
fmt.Printf("header field: %s\n", headerf) logger.Printf("header field: %s\n", headerf)
fmt.Printf("header value: %s\n", headerv) logger.Printf("header value: %s\n", headerv)
return return
} }
func (milter *Mymilter) Eoh(ctx uintptr) (sfsistat int8) { func (milter *Mymilter) Eoh(ctx uintptr) (sfsistat int8) {
fmt.Println("mymilter.Eoh was called") logger.Println("mymilter.Eoh was called")
return return
} }
func (milter *Mymilter) Body(ctx uintptr, body []byte) (sfsistat int8) { func (milter *Mymilter) Body(ctx uintptr, body []byte) (sfsistat int8) {
// Be careful as a conversion of body to string will make a copy of body // Be careful as a conversion of body to string will make a copy of body
fmt.Println("mymilter.Body was called") logger.Println("mymilter.Body was called")
fmt.Println(string(body)) logger.Println(string(body))
fmt.Printf("Body Length: %d\n", len(body)) logger.Printf("Body Length: %d\n", len(body))
return return
} }
func (milter *Mymilter) Eom(ctx uintptr) (sfsistat int8) { func (milter *Mymilter) Eom(ctx uintptr) (sfsistat int8) {
fmt.Println("mymilter.Eom was called") logger.Println("mymilter.Eom was called")
var t T var t T
fmt.Println("m.GetPri:", m.GetPriv(ctx, &t)) logger.Println("m.GetPri:", m.GetPriv(ctx, &t))
fmt.Println("t:", t) logger.Println("t:", t)
m.AddHeader(ctx, "LEONUX-Mailer", m.AddHeader(ctx, "LEONUX-Mailer",
"test server;\n\ttest1=\"foobar\"") "test server;\n\ttest1=\"foobar\"")
newBody := []byte("This is a new body") newBody := []byte("This is a new body")
m.ReplaceBody(ctx, newBody) m.ReplaceBody(ctx, newBody)
@@ -94,20 +95,24 @@ func (milter *Mymilter) Eom(ctx uintptr) (sfsistat int8) {
} }
func (milter *Mymilter) Abort(ctx uintptr) (sfsistat int8) { func (milter *Mymilter) Abort(ctx uintptr) (sfsistat int8) {
fmt.Println("mymilter.Abort was called") logger.Println("mymilter.Abort was called")
return return
} }
func (milter *Mymilter) Close(ctx uintptr) (sfsistat int8) { func (milter *Mymilter) Close(ctx uintptr) (sfsistat int8) {
fmt.Println("mymilter.Close was called") logger.Println("mymilter.Close was called")
return return
} }
func main() { func main() {
mymilter := new(Mymilter) mymilter := new(Mymilter)
mymilter.FilterName = "TestFilter" mymilter.FilterName = "TestFilter"
logger = log.New(os.Stdout, "", log.LstdFlags)
mymilter.Logger = logger
mymilter.Debug = true mymilter.Debug = true
mymilter.Flags = m.ADDHDRS|m.ADDRCPT|m.CHGFROM|m.CHGBODY
mymilter.Flags = m.ADDHDRS | m.ADDRCPT | m.CHGFROM | m.CHGBODY
mymilter.Socket = "unix:/var/milterattachcheck/socket" mymilter.Socket = "unix:/var/milterattachcheck/socket"
// Start Milter // Start Milter