803 lines
20 KiB
Go
803 lines
20 KiB
Go
/*
|
|
|
|
Copyright (c) 2015 Leon Baker
|
|
This projected is licensed under the terms of the MIT License.
|
|
|
|
gomilter
|
|
Go Bindings for libmilter
|
|
|
|
gomilter.go
|
|
|
|
*/
|
|
|
|
package gomilter
|
|
|
|
/*
|
|
|
|
#cgo LDFLAGS: -lmilter
|
|
|
|
#include <stdlib.h>
|
|
#include <netinet/in.h>
|
|
#include <arpa/inet.h>
|
|
#include "libmilter/mfapi.h"
|
|
#include "filter.h"
|
|
|
|
*/
|
|
import "C"
|
|
import (
|
|
"bytes"
|
|
"encoding/binary"
|
|
"encoding/gob"
|
|
"fmt"
|
|
"net"
|
|
"os"
|
|
"reflect"
|
|
"strings"
|
|
"unsafe"
|
|
"log"
|
|
)
|
|
|
|
type sockaddr_in struct {
|
|
sin_family int8
|
|
sin_port uint8
|
|
sin_addr uint32
|
|
sin_zero uint64
|
|
}
|
|
|
|
// Return values for Callback functions
|
|
const (
|
|
Continue = iota
|
|
Reject
|
|
Discard
|
|
Accept
|
|
Tempfail
|
|
_
|
|
_
|
|
Noreply
|
|
Skip
|
|
)
|
|
|
|
// flags
|
|
const (
|
|
ADDHDRS = 0x00000001 // 000000001
|
|
CHGBODY = 0x00000002 // 000000010
|
|
ADDRCPT = 0x00000004 // 000000100
|
|
DELRCPT = 0x00000008 // 000001000
|
|
CHGHDRS = 0x00000010 // 000010000
|
|
QUARANTINE = 0x00000020 // 000100000
|
|
CHGFROM = 0x00000040 // 001000000
|
|
ADDRCPT_PAR = 0x00000080 // 010000000
|
|
SETSYMLIST = 0x00000100 // 100000000
|
|
)
|
|
|
|
// Interface that must be implemented in order to use gomilter
|
|
type Milter interface {
|
|
GetFilterName() string
|
|
GetDebug() bool
|
|
GetFlags() int
|
|
GetSocket() string
|
|
GetLogger() *log.Logger
|
|
}
|
|
|
|
// An "empty" Milter with no callback functions
|
|
type MilterRaw struct {
|
|
FilterName string
|
|
Debug bool
|
|
Flags int
|
|
Socket string
|
|
Logger *log.Logger
|
|
}
|
|
|
|
func (m *MilterRaw) GetFilterName() string {
|
|
return m.FilterName
|
|
}
|
|
|
|
func (m *MilterRaw) GetDebug() bool {
|
|
return m.Debug
|
|
}
|
|
|
|
func (m *MilterRaw) GetFlags() int {
|
|
return m.Flags
|
|
}
|
|
|
|
func (m *MilterRaw) GetSocket() string {
|
|
return m.Socket
|
|
}
|
|
|
|
func (m *MilterRaw) GetLogger() *log.Logger {
|
|
return m.Logger
|
|
}
|
|
|
|
// ********* Callback checking types *********
|
|
type checkForConnect interface {
|
|
Connect(ctx uintptr, hostname string, ip net.IP) (sfsistat int8)
|
|
}
|
|
|
|
type checkForHelo interface {
|
|
Helo(ctx uintptr, helohost string) (sfsistat int8)
|
|
}
|
|
|
|
type checkForEnvFrom interface {
|
|
EnvFrom(ctx uintptr, myargv []string) (sfsistat int8)
|
|
}
|
|
|
|
type checkForEnvRcpt interface {
|
|
EnvRcpt(ctx uintptr, myargv []string) (sfsistat int8)
|
|
}
|
|
|
|
type checkForHeader interface {
|
|
Header(ctx uintptr, headerf, headerv string) (sfsistat int8)
|
|
}
|
|
|
|
type checkForEoh interface {
|
|
Eoh(ctx uintptr) (sfsistat int8)
|
|
}
|
|
|
|
type checkForBody interface {
|
|
Body(ctx uintptr, body []byte) (sfsistat int8)
|
|
}
|
|
|
|
type checkForEom interface {
|
|
Eom(ctx uintptr) (sfsistat int8)
|
|
}
|
|
|
|
type checkForAbort interface {
|
|
Abort(ctx uintptr) (sfsistat int8)
|
|
}
|
|
|
|
type checkForClose interface {
|
|
Close(ctx uintptr) (sfsistat int8)
|
|
}
|
|
|
|
// ********* Global Milter variable *********
|
|
var milter Milter
|
|
var logger *log.Logger
|
|
|
|
// ********* Utility Functions (not exported) *********
|
|
|
|
type CtxPtr *C.struct_smfi_str
|
|
|
|
func int2ctx(ptr uintptr) CtxPtr {
|
|
return CtxPtr(unsafe.Pointer(ptr))
|
|
}
|
|
|
|
func ctx2int(ctx CtxPtr) uintptr {
|
|
return uintptr(unsafe.Pointer(ctx))
|
|
}
|
|
|
|
func cStringArrayToSlice(argv **C.char) (GoArgv []string) {
|
|
// Build a slice of pointers with C array as a backing
|
|
length := int(C.argv_len(argv))
|
|
hdr := reflect.SliceHeader{
|
|
Data: uintptr(unsafe.Pointer(argv)),
|
|
Len: length,
|
|
Cap: length,
|
|
}
|
|
argvSlice := *(*[]*C.char)(unsafe.Pointer(&hdr))
|
|
|
|
// Build a string slice for Go friendly strings
|
|
GoArgv = make([]string, length, length)
|
|
for i := 0; i < length; i++ {
|
|
GoArgv[i] = C.GoString(argvSlice[i])
|
|
}
|
|
return
|
|
}
|
|
|
|
func GobEncode(data interface{}) ([]byte, error) {
|
|
w := new(bytes.Buffer)
|
|
encoder := gob.NewEncoder(w)
|
|
err := encoder.Encode(data)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
return w.Bytes(), nil
|
|
}
|
|
|
|
func GobDecode(buf []byte, data interface{}) error {
|
|
r := bytes.NewBuffer(buf)
|
|
decoder := gob.NewDecoder(r)
|
|
err := decoder.Decode(data)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
return nil
|
|
}
|
|
|
|
// ********* Filter Callback functions *********
|
|
// These are registered with Milter
|
|
// They are only called if they get registered but need to be defined anyway
|
|
|
|
//export Go_xxfi_connect
|
|
func Go_xxfi_connect(ctx *C.SMFICTX, hostname *C.char, hostaddr *C._SOCK_ADDR) C.sfsistat {
|
|
ctxptr := ctx2int(ctx)
|
|
var ip net.IP
|
|
|
|
if hostaddr.sa_family == C.AF_INET {
|
|
hostaddrin := (*sockaddr_in)(unsafe.Pointer(hostaddr))
|
|
ip_addr := make([]byte, 4)
|
|
binary.LittleEndian.PutUint32(ip_addr, hostaddrin.sin_addr)
|
|
ip = net.IPv4(ip_addr[0], ip_addr[1], ip_addr[2], ip_addr[3])
|
|
|
|
} else if hostaddr.sa_family == C.AF_INET6 {
|
|
sa_in := (*C.struct_sockaddr_in6)(unsafe.Pointer(hostaddr))
|
|
var dst = make([]byte, 16)
|
|
C.inet_ntop(
|
|
C.int(hostaddr.sa_family),
|
|
unsafe.Pointer(&sa_in.sin6_addr),
|
|
(*C.char)(unsafe.Pointer(&dst)),
|
|
16)
|
|
|
|
ip = net.ParseIP(C.GoString((*C.char)(unsafe.Pointer(&dst))))
|
|
} else {
|
|
if milter.GetDebug() {
|
|
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)
|
|
}
|
|
|
|
//export Go_xxfi_helo
|
|
func Go_xxfi_helo(ctx *C.SMFICTX, helohost *C.char) C.sfsistat {
|
|
m := milter.(checkForHelo)
|
|
code := m.Helo(ctx2int(ctx), C.GoString(helohost))
|
|
if milter.GetDebug() {
|
|
logger.Printf("Helo callback returned: %d\n", code)
|
|
}
|
|
return C.sfsistat(code)
|
|
}
|
|
|
|
//export Go_xxfi_envfrom
|
|
func Go_xxfi_envfrom(ctx *C.SMFICTX, argv **C.char) C.sfsistat {
|
|
// Call our application's callback
|
|
m := milter.(checkForEnvFrom)
|
|
code := m.EnvFrom(ctx2int(ctx), cStringArrayToSlice(argv))
|
|
if milter.GetDebug() {
|
|
logger.Printf("EnvFrom callback returned: %d\n", code)
|
|
}
|
|
return C.sfsistat(code)
|
|
}
|
|
|
|
//export Go_xxfi_envrcpt
|
|
func Go_xxfi_envrcpt(ctx *C.SMFICTX, argv **C.char) C.sfsistat {
|
|
// Call our application's callback
|
|
m := milter.(checkForEnvRcpt)
|
|
code := m.EnvRcpt(ctx2int(ctx), cStringArrayToSlice(argv))
|
|
if milter.GetDebug() {
|
|
logger.Printf("EnvRcpt callback returned: %d\n", code)
|
|
}
|
|
return C.sfsistat(code)
|
|
}
|
|
|
|
//export Go_xxfi_header
|
|
func Go_xxfi_header(ctx *C.SMFICTX, headerf, headerv *C.char) C.sfsistat {
|
|
m := milter.(checkForHeader)
|
|
code := m.Header(ctx2int(ctx), C.GoString(headerf), C.GoString(headerv))
|
|
if milter.GetDebug() {
|
|
logger.Printf("Header callback returned: %d\n", code)
|
|
}
|
|
return C.sfsistat(code)
|
|
}
|
|
|
|
//export Go_xxfi_eoh
|
|
func Go_xxfi_eoh(ctx *C.SMFICTX) C.sfsistat {
|
|
// Call our application's callback
|
|
m := milter.(checkForEoh)
|
|
code := m.Eoh(ctx2int(ctx))
|
|
if milter.GetDebug() {
|
|
logger.Printf("Eoh callback returned: %d\n", code)
|
|
}
|
|
return C.sfsistat(code)
|
|
}
|
|
|
|
//export Go_xxfi_body
|
|
func Go_xxfi_body(ctx *C.SMFICTX, bodyp *C.uchar, bodylen C.size_t) C.sfsistat {
|
|
// Create a byte slice from the body pointer.
|
|
// The body pointer just points to a sequence of bytes
|
|
// 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
|
|
var b []byte
|
|
b = (*[1 << 30]byte)(unsafe.Pointer(bodyp))[0:bodylen]
|
|
// Call our application's callback
|
|
m := milter.(checkForBody)
|
|
code := m.Body(ctx2int(ctx), b)
|
|
if milter.GetDebug() {
|
|
logger.Printf("Body callback returned: %d\n", code)
|
|
}
|
|
return C.sfsistat(code)
|
|
}
|
|
|
|
//export Go_xxfi_eom
|
|
func Go_xxfi_eom(ctx *C.SMFICTX) C.sfsistat {
|
|
// Call our application's callback
|
|
m := milter.(checkForEom)
|
|
code := m.Eom(ctx2int(ctx))
|
|
if milter.GetDebug() {
|
|
logger.Printf("Eom callback returned: %d\n", code)
|
|
}
|
|
return C.sfsistat(code)
|
|
}
|
|
|
|
//export Go_xxfi_abort
|
|
func Go_xxfi_abort(ctx *C.SMFICTX) C.sfsistat {
|
|
// Call our application's callback
|
|
m := milter.(checkForAbort)
|
|
code := m.Abort(ctx2int(ctx))
|
|
if milter.GetDebug() {
|
|
logger.Printf("Abort callback returned: %d\n", code)
|
|
}
|
|
return C.sfsistat(code)
|
|
}
|
|
|
|
//export Go_xxfi_close
|
|
func Go_xxfi_close(ctx *C.SMFICTX) C.sfsistat {
|
|
// Call our application's callback
|
|
m := milter.(checkForClose)
|
|
code := m.Close(ctx2int(ctx))
|
|
if milter.GetDebug() {
|
|
logger.Printf("Close callback returned: %d\n", code)
|
|
}
|
|
return C.sfsistat(code)
|
|
}
|
|
|
|
// ********* libmilter Data Access Functions *********
|
|
|
|
func GetSymVal(ctx uintptr, symname string) string {
|
|
csymname := C.CString(symname)
|
|
defer C.free(unsafe.Pointer(csymname))
|
|
type CtxPtr *C.struct_smfi_str
|
|
cval := C.smfi_getsymval(int2ctx(ctx), csymname)
|
|
// Note: If you try to free the cval C string it will crash
|
|
return C.GoString(cval)
|
|
}
|
|
|
|
// See also: http://bit.ly/1HVWA9I
|
|
func SetPriv(ctx uintptr, privatedata interface{}) int {
|
|
// privatedata seems to work for any data type
|
|
// Structs must have exported fields
|
|
|
|
// Serialize Go privatedata into a byte slice
|
|
bytedata, _ := GobEncode(privatedata)
|
|
|
|
// length and size
|
|
// length is a uint32 (usually 4 bytes)
|
|
// the length will be stored in front of the byte sequence
|
|
length := uint32(len(bytedata))
|
|
lengthsize := C.size_t(unsafe.Sizeof(length))
|
|
buf := new(bytes.Buffer)
|
|
err := binary.Write(buf, binary.BigEndian, length)
|
|
if err != nil {
|
|
return -1
|
|
}
|
|
lengthbytes := buf.Bytes()
|
|
|
|
// Allocate memory for the length and byte sequence
|
|
CArray := (*C.uchar)(C.malloc(lengthsize + C.size_t(length)))
|
|
|
|
var lenStart, seqStart uintptr
|
|
lenStart = uintptr(unsafe.Pointer(CArray))
|
|
seqStart = lenStart + uintptr(lengthsize)
|
|
|
|
CArray = (*C.uchar)(unsafe.Pointer(lenStart))
|
|
|
|
for i := uintptr(0); i < uintptr(lengthsize); i++ {
|
|
CArray = (*C.uchar)(unsafe.Pointer(lenStart + i))
|
|
*CArray = C.uchar(lengthbytes[i])
|
|
}
|
|
|
|
// Now copy the data bytes to the position after the length
|
|
for i := uintptr(0); i < uintptr(length); i++ {
|
|
CArray = (*C.uchar)(unsafe.Pointer(seqStart + i))
|
|
*CArray = C.uchar(bytedata[i])
|
|
}
|
|
|
|
// Call libmilter smfi_setpriv
|
|
type CtxPtr *C.struct_smfi_str
|
|
return int(C.smfi_setpriv(int2ctx(ctx), unsafe.Pointer(lenStart)))
|
|
}
|
|
|
|
func GetPriv(ctx uintptr, privatedata interface{}) int {
|
|
/* Retrieve the private data stored by the milter
|
|
Retrieving the data will release the memory allocated for it
|
|
Don't try to retrieve it again unless you call SetPriv first
|
|
*/
|
|
|
|
// Call libmilter smfi_getpriv to get a pointer to our data
|
|
CArray := (*byte)(C.smfi_getpriv(int2ctx(ctx)))
|
|
|
|
// Make sure data has been set with a previous call to SetPriv
|
|
if CArray == nil {
|
|
return -1
|
|
}
|
|
|
|
// Read uint32 size bytes from the start of the pointer
|
|
var length uint32
|
|
lengthsize := unsafe.Sizeof(length)
|
|
lengthbytes := make([]byte, lengthsize)
|
|
|
|
lenStart := uintptr(unsafe.Pointer(CArray))
|
|
seqStart := lenStart + uintptr(lengthsize)
|
|
|
|
for i := uintptr(0); i < uintptr(lengthsize); i++ {
|
|
CArray = (*byte)(unsafe.Pointer(lenStart + i))
|
|
lengthbytes[i] = *CArray
|
|
}
|
|
|
|
// Binary decode the length bytes
|
|
buf := bytes.NewBuffer(lengthbytes)
|
|
err := binary.Read(buf, binary.BigEndian, &length)
|
|
if err != nil {
|
|
return -1
|
|
}
|
|
|
|
// Read byte sequence of data
|
|
databytes := make([]byte, length)
|
|
for i := uintptr(0); i < uintptr(length); i++ {
|
|
CArray = (*byte)(unsafe.Pointer(seqStart + i))
|
|
databytes[i] = *CArray
|
|
}
|
|
|
|
// Free the data malloc'ed by C
|
|
C.free(unsafe.Pointer(lenStart))
|
|
C.smfi_setpriv(int2ctx(ctx), nil)
|
|
|
|
// Unserialize the data bytes back into a data structure
|
|
err = GobDecode(databytes, privatedata)
|
|
if err != nil {
|
|
return -1
|
|
}
|
|
|
|
return 0
|
|
}
|
|
|
|
func SetReply(ctx uintptr, rcode, xcode, message string) int {
|
|
type CtxPtr *C.struct_smfi_str
|
|
crcode := C.CString(rcode)
|
|
defer C.free(unsafe.Pointer(crcode))
|
|
cxcode := C.CString(xcode)
|
|
defer C.free(unsafe.Pointer(cxcode))
|
|
cmessage := C.CString(message)
|
|
defer C.free(unsafe.Pointer(cmessage))
|
|
// Call libmilter setreply
|
|
return int(C.smfi_setreply(int2ctx(ctx), crcode, cxcode, cmessage))
|
|
}
|
|
|
|
func SetMLReply(ctx uintptr, rcode, xcode string, message ...string) int {
|
|
/* Allows up to 32 lines of message
|
|
*/
|
|
|
|
crcode := C.CString(rcode)
|
|
defer C.free(unsafe.Pointer(crcode))
|
|
cxcode := C.CString(xcode)
|
|
defer C.free(unsafe.Pointer(cxcode))
|
|
|
|
// Build message C array
|
|
// Get size of a C pointer on this system
|
|
ptrSize := unsafe.Sizeof(crcode)
|
|
|
|
// Allocate the char** array
|
|
argv := C.malloc(C.size_t(len(message)) * C.size_t(ptrSize))
|
|
defer C.free(argv)
|
|
// Assign each line to its address offset
|
|
for i := 0; i < len(message); i++ {
|
|
linePtr := (**C.char)(unsafe.Pointer(uintptr(argv) + uintptr(i)*ptrSize))
|
|
line := C.CString(message[i])
|
|
defer C.free(unsafe.Pointer(line))
|
|
*linePtr = line
|
|
}
|
|
|
|
// Call our C wrapper for setmlreply
|
|
// The wrapper is needed as cgo doesn't support variadic C function calls
|
|
return int(C.wrap_setmlreply(int2ctx(ctx), crcode, cxcode, C.int(len(message)), (**C.char)(argv)))
|
|
}
|
|
|
|
// ********* libmilter Message Modification Functions *********
|
|
|
|
func AddHeader(ctx uintptr, headerf, headerv string) int {
|
|
/* Add a header to the message. SMFIF_ADDHDRS
|
|
*/
|
|
|
|
cheaderf := C.CString(headerf)
|
|
defer C.free(unsafe.Pointer(cheaderf))
|
|
cheaderv := C.CString(headerv)
|
|
defer C.free(unsafe.Pointer(cheaderv))
|
|
|
|
// Call smfi_addheader
|
|
return int(C.smfi_addheader(int2ctx(ctx), cheaderf, cheaderv))
|
|
}
|
|
|
|
func ChgHeader(ctx uintptr, headerf string, hdridx int, headerv string) int {
|
|
/* Change or delete a header. SMFIF_CHGHDRS
|
|
*/
|
|
|
|
cheaderf := C.CString(headerf)
|
|
defer C.free(unsafe.Pointer(cheaderf))
|
|
cheaderv := C.CString(headerv)
|
|
defer C.free(unsafe.Pointer(cheaderv))
|
|
|
|
// Call smfi_chgheader
|
|
return int(C.smfi_chgheader(int2ctx(ctx), cheaderf, C.int(hdridx), cheaderv))
|
|
}
|
|
|
|
func InsHeader(ctx uintptr, hdridx int, headerf, headerv string) int {
|
|
/* Insert a header into the message. SMFIF_ADDHDRS
|
|
*/
|
|
|
|
cheaderf := C.CString(headerf)
|
|
defer C.free(unsafe.Pointer(cheaderf))
|
|
cheaderv := C.CString(headerv)
|
|
defer C.free(unsafe.Pointer(cheaderv))
|
|
|
|
// Call smfi_insheader
|
|
return int(C.smfi_insheader(int2ctx(ctx), C.int(hdridx), cheaderf, cheaderv))
|
|
}
|
|
|
|
func ChgFrom(ctx uintptr, mail, args string) int {
|
|
/* Change the envelope sender address. SMFIF_CHGFROM
|
|
*/
|
|
|
|
cmail := C.CString(mail)
|
|
defer C.free(unsafe.Pointer(cmail))
|
|
cargs := C.CString(args)
|
|
defer C.free(unsafe.Pointer(cargs))
|
|
|
|
return int(C.smfi_chgfrom(int2ctx(ctx), cmail, cargs))
|
|
}
|
|
|
|
func AddRcpt(ctx uintptr, rcpt string) int {
|
|
/* Add a recipient to the envelope. SMFIF_ADDRCPT
|
|
*/
|
|
crcpt := C.CString(rcpt)
|
|
defer C.free(unsafe.Pointer(crcpt))
|
|
// Call smfi_addrcpt
|
|
return int(C.smfi_addrcpt(int2ctx(ctx), crcpt))
|
|
}
|
|
|
|
func AddRcpt_Par(ctx uintptr, rcpt, args string) int {
|
|
/* Add a recipient including ESMTP parameter to the envelope. SMFIF_ADDRCPT_PAR
|
|
*/
|
|
crcpt := C.CString(rcpt)
|
|
defer C.free(unsafe.Pointer(crcpt))
|
|
cargs := C.CString(args)
|
|
defer C.free(unsafe.Pointer(cargs))
|
|
// Call smfi_addrcpt
|
|
return int(C.smfi_addrcpt_par(int2ctx(ctx), crcpt, cargs))
|
|
}
|
|
|
|
func DelRcpt(ctx uintptr, rcpt string) int {
|
|
/* Delete a recipient from the envelope. SMFIF_DELRCPT
|
|
*/
|
|
crcpt := C.CString(rcpt)
|
|
defer C.free(unsafe.Pointer(crcpt))
|
|
// Call smfi_addrcpt
|
|
return int(C.smfi_delrcpt(int2ctx(ctx), crcpt))
|
|
}
|
|
|
|
func ReplaceBody(ctx uintptr, body []byte) int {
|
|
/* Replace the body of the message. SMFIF_CHGBODY
|
|
*/
|
|
|
|
// Allocate memory
|
|
length := len(body)
|
|
|
|
// Allocate memory for the length and byte sequence
|
|
cbody := (*C.uchar)(C.malloc(C.size_t(length)))
|
|
start := uintptr(unsafe.Pointer(cbody))
|
|
|
|
for i := uintptr(0); i < uintptr(length); i++ {
|
|
cbody = (*C.uchar)(unsafe.Pointer(start + i))
|
|
*cbody = C.uchar(body[i])
|
|
}
|
|
cbody = (*C.uchar)(unsafe.Pointer(start))
|
|
|
|
// Call smfi_replacebody
|
|
return int(C.smfi_replacebody(int2ctx(ctx), cbody, C.int(length)))
|
|
}
|
|
|
|
// ********* Other Message Handling Functions *********
|
|
|
|
func progress(ctx uintptr) int {
|
|
/* Report operation in progress.
|
|
*/
|
|
|
|
// Call smfi_progress
|
|
return int(C.smfi_progress(int2ctx(ctx)))
|
|
}
|
|
|
|
// ********* Run the milter *********
|
|
|
|
func Run(amilter Milter) int {
|
|
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() {
|
|
logger.Println("Debugging enabled")
|
|
}
|
|
|
|
// Declare an empty smfiDesc structure
|
|
var smfilter C.smfiDesc_str
|
|
|
|
// Set filter name
|
|
fname := C.CString(milter.GetFilterName())
|
|
defer C.free(unsafe.Pointer(fname))
|
|
smfilter.xxfi_name = fname
|
|
if milter.GetDebug() {
|
|
logger.Printf("Filter Name: %s\n", C.GoString(smfilter.xxfi_name))
|
|
}
|
|
|
|
// Set version code
|
|
smfilter.xxfi_version = C.SMFI_VERSION
|
|
|
|
// Set Flags
|
|
smfilter.xxfi_flags = C.ulong(milter.GetFlags())
|
|
if milter.GetDebug() {
|
|
logger.Printf("Flags: 0x%b\n", smfilter.xxfi_flags)
|
|
}
|
|
|
|
// Set Callbacks if they are implemented
|
|
|
|
// Check if Connect method was implemented
|
|
if _, ok := milter.(checkForConnect); ok {
|
|
if milter.GetDebug() {
|
|
logger.Println("Connect callback implemented")
|
|
}
|
|
C.setConnect(&smfilter)
|
|
} else {
|
|
if milter.GetDebug() {
|
|
logger.Println("Connect callback not implemented")
|
|
}
|
|
}
|
|
// Check if Helo method was implemented
|
|
if _, ok := milter.(checkForHelo); ok {
|
|
if milter.GetDebug() {
|
|
logger.Println("Helo callback implemented")
|
|
}
|
|
C.setHelo(&smfilter)
|
|
} else {
|
|
if milter.GetDebug() {
|
|
logger.Println("Helo callback not implemented")
|
|
}
|
|
}
|
|
// Check if EnvFrom method was implemented
|
|
if _, ok := milter.(checkForEnvFrom); ok {
|
|
if milter.GetDebug() {
|
|
logger.Println("EnvFrom callback implemented")
|
|
}
|
|
C.setEnvFrom(&smfilter)
|
|
} else {
|
|
if milter.GetDebug() {
|
|
logger.Println("EnvFrom callback not implemented")
|
|
}
|
|
}
|
|
// Check if EnvRcpt method was implemented
|
|
if _, ok := milter.(checkForEnvRcpt); ok {
|
|
if milter.GetDebug() {
|
|
logger.Println("EnvRcpt callback implemented")
|
|
}
|
|
C.setEnvRcpt(&smfilter)
|
|
} else {
|
|
if milter.GetDebug() {
|
|
logger.Println("EnvRcpt callback not implemented")
|
|
}
|
|
}
|
|
// Check if Header method was implemented
|
|
if _, ok := milter.(checkForHeader); ok {
|
|
if milter.GetDebug() {
|
|
logger.Println("Header callback implemented")
|
|
}
|
|
C.setHeader(&smfilter)
|
|
} else {
|
|
if milter.GetDebug() {
|
|
logger.Println("Header callback not implemented")
|
|
}
|
|
}
|
|
// Check if Eoh method was implemented
|
|
if _, ok := milter.(checkForEoh); ok {
|
|
if milter.GetDebug() {
|
|
logger.Println("Eoh callback implemented")
|
|
}
|
|
C.setEoh(&smfilter)
|
|
} else {
|
|
if milter.GetDebug() {
|
|
logger.Println("Eoh callback not implemented")
|
|
}
|
|
}
|
|
// Check if Body method was implemented
|
|
if _, ok := milter.(checkForBody); ok {
|
|
if milter.GetDebug() {
|
|
logger.Println("Body callback implemented")
|
|
}
|
|
C.setBody(&smfilter)
|
|
} else {
|
|
if milter.GetDebug() {
|
|
logger.Println("Body callback not implemented")
|
|
}
|
|
}
|
|
// Check if Eom method was implemented
|
|
if _, ok := milter.(checkForEom); ok {
|
|
if milter.GetDebug() {
|
|
logger.Println("Eom callback implemented")
|
|
}
|
|
C.setEom(&smfilter)
|
|
} else {
|
|
if milter.GetDebug() {
|
|
logger.Println("Eom callback not implemented")
|
|
}
|
|
}
|
|
// Check if Abort method was implemented
|
|
if _, ok := milter.(checkForAbort); ok {
|
|
if milter.GetDebug() {
|
|
logger.Println("Abort callback implemented")
|
|
}
|
|
C.setAbort(&smfilter)
|
|
} else {
|
|
if milter.GetDebug() {
|
|
logger.Println("Abort callback not implemented")
|
|
}
|
|
}
|
|
// Check if Close method was implemented
|
|
if _, ok := milter.(checkForClose); ok {
|
|
if milter.GetDebug() {
|
|
logger.Println("Close callback implemented")
|
|
}
|
|
C.setClose(&smfilter)
|
|
} else {
|
|
if milter.GetDebug() {
|
|
logger.Println("Close callback not implemented")
|
|
}
|
|
}
|
|
|
|
if milter.GetDebug() {
|
|
logger.Println("smfilter:")
|
|
logger.Println(fmt.Sprint(smfilter))
|
|
}
|
|
|
|
// Setup socket connection
|
|
socket := milter.GetSocket()
|
|
if socket == "" {
|
|
panic("No socket name. Set MilterRaw.Socket")
|
|
}
|
|
// Try to delete old socket if it exists
|
|
socketparts := strings.Split(socket, ":")
|
|
if len(socketparts) == 2 {
|
|
os.Remove(socketparts[1])
|
|
}
|
|
|
|
csocket := C.CString(socket)
|
|
defer C.free(unsafe.Pointer(csocket))
|
|
if code := C.smfi_setconn(csocket); code != 0 {
|
|
logger.Printf("smfi_setconn failed: %d\n", code)
|
|
}
|
|
|
|
// Register the filter
|
|
if code := C.smfi_register(smfilter); code == C.MI_FAILURE {
|
|
logger.Printf("smfi_register failed: %d\n", code)
|
|
}
|
|
|
|
// Hand control to libmilter
|
|
if milter.GetDebug() {
|
|
logger.Println("Handing over to libmilter")
|
|
}
|
|
result := C.smfi_main()
|
|
if milter.GetDebug() {
|
|
logger.Printf("smfi_main returned: %v\n", result)
|
|
}
|
|
return int(result)
|
|
}
|
|
|
|
func Stop() {
|
|
C.smfi_stop()
|
|
}
|
|
|