diff --git a/gomilter.go b/gomilter.go index d91755b..3246d2c 100644 --- a/gomilter.go +++ b/gomilter.go @@ -18,126 +18,126 @@ package gomilter #include //#include -#include "libmilter/mfapi.h" +#include "libmilter/mfapi.h" #include "filter.h" */ import "C" import ( - "fmt" - "unsafe" - "encoding/binary" - "reflect" - "os" - "strings" - "bytes" - "encoding/gob" + "bytes" + "encoding/binary" + "encoding/gob" + "fmt" + "os" + "reflect" + "strings" + "unsafe" ) type sockaddr_in struct { - sin_family int8 - sin_port uint8 - sin_addr uint32 - sin_zero uint64 + 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 + 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 + 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 + GetFilterName() string + GetDebug() bool + GetFlags() int + GetSocket() string } // An "empty" Milter with no callback functions type MilterRaw struct { - FilterName string - Debug bool - Flags int - Socket string + FilterName string + Debug bool + Flags int + Socket string } func (m *MilterRaw) GetFilterName() string { - return m.FilterName + return m.FilterName } func (m *MilterRaw) GetDebug() bool { - return m.Debug + return m.Debug } func (m *MilterRaw) GetFlags() int { - return m.Flags + return m.Flags } func (m *MilterRaw) GetSocket() string { - return m.Socket + return m.Socket } // ********* Callback checking types ********* type checkForConnect interface { - Connect(ctx uintptr, hostname, ip string) (sfsistat int8) + Connect(ctx uintptr, hostname, ip string) (sfsistat int8) } type checkForHelo interface { - Helo(ctx uintptr, helohost string) (sfsistat int8) + Helo(ctx uintptr, helohost string) (sfsistat int8) } type checkForEnvFrom interface { - EnvFrom(ctx uintptr, myargv []string) (sfsistat int8) + EnvFrom(ctx uintptr, myargv []string) (sfsistat int8) } type checkForEnvRcpt interface { - EnvRcpt(ctx uintptr, myargv []string) (sfsistat int8) + EnvRcpt(ctx uintptr, myargv []string) (sfsistat int8) } type checkForHeader interface { - Header(ctx uintptr, headerf, headerv string) (sfsistat int8) + Header(ctx uintptr, headerf, headerv string) (sfsistat int8) } type checkForEoh interface { - Eoh(ctx uintptr) (sfsistat int8) + Eoh(ctx uintptr) (sfsistat int8) } type checkForBody interface { - Body(ctx uintptr, body []byte) (sfsistat int8) + Body(ctx uintptr, body []byte) (sfsistat int8) } type checkForEom interface { - Eom(ctx uintptr) (sfsistat int8) + Eom(ctx uintptr) (sfsistat int8) } type checkForAbort interface { - Abort(ctx uintptr) (sfsistat int8) + Abort(ctx uintptr) (sfsistat int8) } type checkForClose interface { - Close(ctx uintptr) (sfsistat int8) + Close(ctx uintptr) (sfsistat int8) } // ********* Global Milter variable ********* @@ -148,636 +148,632 @@ var milter Milter type CtxPtr *C.struct_smfi_str func int2ctx(ptr uintptr) CtxPtr { - return CtxPtr(unsafe.Pointer(ptr)) + return CtxPtr(unsafe.Pointer(ptr)) } func ctx2int(ctx CtxPtr) uintptr { - return uintptr(unsafe.Pointer(ctx)) + 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 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 + // 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 + 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 +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) - // Check if the host address is a regular ipv4 address - if hostaddr.sa_family == C.AF_INET { - //LoggerPrintln(hostaddr.sa_data) + ctxptr := ctx2int(ctx) + // Check if the host address is a regular ipv4 address + if hostaddr.sa_family == C.AF_INET { + //LoggerPrintln(hostaddr.sa_data) - // hostaddrin is a parallel data structure of the C type hostaddr - //var hostaddrin *sockaddr_in - hostaddrin := (*sockaddr_in)(unsafe.Pointer(hostaddr)) - //LoggerPrintln(hostaddrin) - ip_addr := make([]byte, 4) - binary.LittleEndian.PutUint32(ip_addr, hostaddrin.sin_addr) + // hostaddrin is a parallel data structure of the C type hostaddr + //var hostaddrin *sockaddr_in + hostaddrin := (*sockaddr_in)(unsafe.Pointer(hostaddr)) + //LoggerPrintln(hostaddrin) + ip_addr := make([]byte, 4) + binary.LittleEndian.PutUint32(ip_addr, hostaddrin.sin_addr) - // Call our application's callback - m := milter.(checkForConnect) - 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])) - if milter.GetDebug() { - LoggerPrintf("Connect callback returned: %d\n", code) - } - return C.sfsistat(code) - } - if milter.GetDebug() { - LoggerPrintln("hostaddr.sa_family value not implemented") - } - return C.SMFIS_CONTINUE + // Call our application's callback + m := milter.(checkForConnect) + 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])) + if milter.GetDebug() { + LoggerPrintf("Connect callback returned: %d\n", code) + } + return C.sfsistat(code) + } + if milter.GetDebug() { + LoggerPrintln("hostaddr.sa_family value not implemented") + } + return C.SMFIS_CONTINUE } //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() { - LoggerPrintf("Helo callback returned: %d\n", code) - } - return C.sfsistat(code) + m := milter.(checkForHelo) + code := m.Helo(ctx2int(ctx), C.GoString(helohost)) + if milter.GetDebug() { + LoggerPrintf("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() { - LoggerPrintf("EnvFrom callback returned: %d\n", code) - } - return C.sfsistat(code) + // Call our application's callback + m := milter.(checkForEnvFrom) + code := m.EnvFrom(ctx2int(ctx), cStringArrayToSlice(argv)) + if milter.GetDebug() { + LoggerPrintf("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() { - LoggerPrintf("EnvRcpt callback returned: %d\n", code) - } - return C.sfsistat(code) + // Call our application's callback + m := milter.(checkForEnvRcpt) + code := m.EnvRcpt(ctx2int(ctx), cStringArrayToSlice(argv)) + if milter.GetDebug() { + LoggerPrintf("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() { - LoggerPrintf("Header callback returned: %d\n", code) - } - return C.sfsistat(code) + m := milter.(checkForHeader) + code := m.Header(ctx2int(ctx), C.GoString(headerf), C.GoString(headerv)) + if milter.GetDebug() { + LoggerPrintf("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() { - LoggerPrintf("Eoh callback returned: %d\n", code) - } - return C.sfsistat(code) + // Call our application's callback + m := milter.(checkForEoh) + code := m.Eoh(ctx2int(ctx)) + if milter.GetDebug() { + LoggerPrintf("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() { - LoggerPrintf("Body callback returned: %d\n", code) - } - return C.sfsistat(code) + // 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() { + LoggerPrintf("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() { - LoggerPrintf("Eom callback returned: %d\n", code) - } - return C.sfsistat(code) + // Call our application's callback + m := milter.(checkForEom) + code := m.Eom(ctx2int(ctx)) + if milter.GetDebug() { + LoggerPrintf("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() { - LoggerPrintf("Abort callback returned: %d\n", code) - } - return C.sfsistat(code) + // Call our application's callback + m := milter.(checkForAbort) + code := m.Abort(ctx2int(ctx)) + if milter.GetDebug() { + LoggerPrintf("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() { - LoggerPrintf("Close callback returned: %d\n", code) - } - return C.sfsistat(code) + // Call our application's callback + m := milter.(checkForClose) + code := m.Close(ctx2int(ctx)) + if milter.GetDebug() { + LoggerPrintf("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) + 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) } func SetPriv(ctx uintptr, privatedata interface{}) int { - // privatedata seems to work for any data type - // Structs must have exported fields + // privatedata seems to work for any data type + // Structs must have exported fields - // Serialize Go privatedata into a byte slice - bytedata, _ := GobEncode(privatedata) + // 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() + // 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))) + // 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) + var lenStart, seqStart uintptr + lenStart = uintptr(unsafe.Pointer(CArray)) + seqStart = lenStart + uintptr(lengthsize) - CArray = (*C.uchar)(unsafe.Pointer(lenStart)) + 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]) - } + 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]) - } + // 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))) + // 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 - */ + /* 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))) + // 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 - } + // 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) + // 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) + 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 - } + 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 - } + // 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 - } + // 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) + // 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 - } + // Unserialize the data bytes back into a data structure + err = GobDecode(databytes, privatedata) + if err != nil { + return -1 + } - return 0 + 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 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 - */ +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)) + 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) + // 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 - } + // 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))) + // 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 - */ + /* 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)) + 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)); + // 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 - */ + /* 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)) + 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)); + // 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 - */ + /* 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)) + 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)); + // 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 - */ + /* 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)) + 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)) + 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)) + /* 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)) + /* 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)) + /* 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) + /* Replace the body of the message. SMFIF_CHGBODY + */ - // Allocate memory for the length and byte sequence - cbody := (*C.uchar)(C.malloc(C.size_t(length))) - start := uintptr(unsafe.Pointer(cbody)) + // Allocate memory + length := len(body) - 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)) + // Allocate memory for the length and byte sequence + cbody := (*C.uchar)(C.malloc(C.size_t(length))) + start := uintptr(unsafe.Pointer(cbody)) - // Call smfi_replacebody - return int(C.smfi_replacebody(int2ctx(ctx), cbody, C.int(length))) + 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. - */ + /* Report operation in progress. + */ - // Call smfi_progress - return int(C.smfi_progress(int2ctx(ctx))) + // Call smfi_progress + return int(C.smfi_progress(int2ctx(ctx))) } // ********* Run the milter ********* - - func Run(amilter Milter) int { - milter = amilter - if milter.GetDebug() { - LoggerPrintf ("Debugging enabled") - } - - // Declare an empty smfiDesc structure - var smfilter C.smfiDesc_str + milter = amilter + if milter.GetDebug() { + LoggerPrintf("Debugging enabled") + } - // Set filter name - fname := C.CString(milter.GetFilterName()) - defer C.free(unsafe.Pointer(fname)) - smfilter.xxfi_name = fname - if milter.GetDebug() { - LoggerPrintf("Filter Name: %s\n", C.GoString(smfilter.xxfi_name)) - } + // Declare an empty smfiDesc structure + var smfilter C.smfiDesc_str - // Set version code - smfilter.xxfi_version = C.SMFI_VERSION + // Set filter name + fname := C.CString(milter.GetFilterName()) + defer C.free(unsafe.Pointer(fname)) + smfilter.xxfi_name = fname + if milter.GetDebug() { + LoggerPrintf("Filter Name: %s\n", C.GoString(smfilter.xxfi_name)) + } - // Set Flags - smfilter.xxfi_flags = C.ulong(milter.GetFlags()) - if milter.GetDebug() { - LoggerPrintf("Flags: 0x%b\n", smfilter.xxfi_flags) - } + // Set version code + smfilter.xxfi_version = C.SMFI_VERSION - // Set Callbacks if they are implemented + // Set Flags + smfilter.xxfi_flags = C.ulong(milter.GetFlags()) + if milter.GetDebug() { + LoggerPrintf("Flags: 0x%b\n", smfilter.xxfi_flags) + } - // Check if Connect method was implemented - if _, ok := milter.(checkForConnect); ok { - if milter.GetDebug() { - LoggerPrintln("Connect callback implemented") - } - C.setConnect(&smfilter) - } else { - if milter.GetDebug() { - LoggerPrintln("Connect callback not implemented") - } - } - // Check if Helo method was implemented - if _, ok := milter.(checkForHelo); ok { - if milter.GetDebug() { - LoggerPrintln("Helo callback implemented") - } - C.setHelo(&smfilter) - } else { - if milter.GetDebug() { - LoggerPrintln("Helo callback not implemented") - } - } - // Check if EnvFrom method was implemented - if _, ok := milter.(checkForEnvFrom); ok { - if milter.GetDebug() { - LoggerPrintln("EnvFrom callback implemented") - } - C.setEnvFrom(&smfilter) - } else { - if milter.GetDebug() { - LoggerPrintln("EnvFrom callback not implemented") - } - } - // Check if EnvRcpt method was implemented - if _, ok := milter.(checkForEnvRcpt); ok { - if milter.GetDebug() { - LoggerPrintln("EnvRcpt callback implemented") - } - C.setEnvRcpt(&smfilter) - } else { - if milter.GetDebug() { - LoggerPrintln("EnvRcpt callback not implemented") - } - } - // Check if Header method was implemented - if _, ok := milter.(checkForHeader); ok { - if milter.GetDebug() { - LoggerPrintln("Header callback implemented") - } - C.setHeader(&smfilter) - } else { - if milter.GetDebug() { - LoggerPrintln("Header callback not implemented") - } - } - // Check if Eoh method was implemented - if _, ok := milter.(checkForEoh); ok { - if milter.GetDebug() { - LoggerPrintln("Eoh callback implemented") - } - C.setEoh(&smfilter) - } else { - if milter.GetDebug() { - LoggerPrintln("Eoh callback not implemented") - } - } - // Check if Body method was implemented - if _, ok := milter.(checkForBody); ok { - if milter.GetDebug() { - LoggerPrintln("Body callback implemented") - } - C.setBody(&smfilter) - } else { - if milter.GetDebug() { - LoggerPrintln("Body callback not implemented") - } - } - // Check if Eom method was implemented - if _, ok := milter.(checkForEom); ok { - if milter.GetDebug() { - LoggerPrintln("Eom callback implemented") - } - C.setEom(&smfilter) - } else { - if milter.GetDebug() { - LoggerPrintln("Eom callback not implemented") - } - } - // Check if Abort method was implemented - if _, ok := milter.(checkForAbort); ok { - if milter.GetDebug() { - LoggerPrintln("Abort callback implemented") - } - C.setAbort(&smfilter) - } else { - if milter.GetDebug() { - LoggerPrintln("Abort callback not implemented") - } - } - // Check if Close method was implemented - if _, ok := milter.(checkForClose); ok { - if milter.GetDebug() { - LoggerPrintln("Close callback implemented") - } - C.setClose(&smfilter) - } else { - if milter.GetDebug() { - LoggerPrintln("Close callback not implemented") - } - } + // Set Callbacks if they are implemented - if milter.GetDebug() { - LoggerPrintln("smfilter:") - LoggerPrintln(fmt.Sprint(smfilter)) - } + // Check if Connect method was implemented + if _, ok := milter.(checkForConnect); ok { + if milter.GetDebug() { + LoggerPrintln("Connect callback implemented") + } + C.setConnect(&smfilter) + } else { + if milter.GetDebug() { + LoggerPrintln("Connect callback not implemented") + } + } + // Check if Helo method was implemented + if _, ok := milter.(checkForHelo); ok { + if milter.GetDebug() { + LoggerPrintln("Helo callback implemented") + } + C.setHelo(&smfilter) + } else { + if milter.GetDebug() { + LoggerPrintln("Helo callback not implemented") + } + } + // Check if EnvFrom method was implemented + if _, ok := milter.(checkForEnvFrom); ok { + if milter.GetDebug() { + LoggerPrintln("EnvFrom callback implemented") + } + C.setEnvFrom(&smfilter) + } else { + if milter.GetDebug() { + LoggerPrintln("EnvFrom callback not implemented") + } + } + // Check if EnvRcpt method was implemented + if _, ok := milter.(checkForEnvRcpt); ok { + if milter.GetDebug() { + LoggerPrintln("EnvRcpt callback implemented") + } + C.setEnvRcpt(&smfilter) + } else { + if milter.GetDebug() { + LoggerPrintln("EnvRcpt callback not implemented") + } + } + // Check if Header method was implemented + if _, ok := milter.(checkForHeader); ok { + if milter.GetDebug() { + LoggerPrintln("Header callback implemented") + } + C.setHeader(&smfilter) + } else { + if milter.GetDebug() { + LoggerPrintln("Header callback not implemented") + } + } + // Check if Eoh method was implemented + if _, ok := milter.(checkForEoh); ok { + if milter.GetDebug() { + LoggerPrintln("Eoh callback implemented") + } + C.setEoh(&smfilter) + } else { + if milter.GetDebug() { + LoggerPrintln("Eoh callback not implemented") + } + } + // Check if Body method was implemented + if _, ok := milter.(checkForBody); ok { + if milter.GetDebug() { + LoggerPrintln("Body callback implemented") + } + C.setBody(&smfilter) + } else { + if milter.GetDebug() { + LoggerPrintln("Body callback not implemented") + } + } + // Check if Eom method was implemented + if _, ok := milter.(checkForEom); ok { + if milter.GetDebug() { + LoggerPrintln("Eom callback implemented") + } + C.setEom(&smfilter) + } else { + if milter.GetDebug() { + LoggerPrintln("Eom callback not implemented") + } + } + // Check if Abort method was implemented + if _, ok := milter.(checkForAbort); ok { + if milter.GetDebug() { + LoggerPrintln("Abort callback implemented") + } + C.setAbort(&smfilter) + } else { + if milter.GetDebug() { + LoggerPrintln("Abort callback not implemented") + } + } + // Check if Close method was implemented + if _, ok := milter.(checkForClose); ok { + if milter.GetDebug() { + LoggerPrintln("Close callback implemented") + } + C.setClose(&smfilter) + } else { + if milter.GetDebug() { + LoggerPrintln("Close callback not implemented") + } + } - // 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]) - } + if milter.GetDebug() { + LoggerPrintln("smfilter:") + LoggerPrintln(fmt.Sprint(smfilter)) + } - csocket := C.CString(socket) - defer C.free(unsafe.Pointer(csocket)) - if code := C.smfi_setconn(csocket); code != 0 { - LoggerPrintf("smfi_setconn failed: %d\n", code) - } + // 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]) + } - // Register the filter - if code := C.smfi_register(smfilter); code == C.MI_FAILURE { - LoggerPrintf("smfi_register failed: %d\n", code) - } + csocket := C.CString(socket) + defer C.free(unsafe.Pointer(csocket)) + if code := C.smfi_setconn(csocket); code != 0 { + LoggerPrintf("smfi_setconn failed: %d\n", code) + } - // Hand control to libmilter - if milter.GetDebug() { - LoggerPrintln("Handing over to libmilter") - } - result := C.smfi_main() - if milter.GetDebug() { - LoggerPrintf("smfi_main returned: %v\n", result) - } - return int(result) + // Register the filter + if code := C.smfi_register(smfilter); code == C.MI_FAILURE { + LoggerPrintf("smfi_register failed: %d\n", code) + } + + // Hand control to libmilter + if milter.GetDebug() { + LoggerPrintln("Handing over to libmilter") + } + result := C.smfi_main() + if milter.GetDebug() { + LoggerPrintf("smfi_main returned: %v\n", result) + } + return int(result) } -var LoggerPrintln func(...interface {}) -var LoggerPrintf func(string, ...interface {}) +var LoggerPrintln func(...interface{}) +var LoggerPrintf func(string, ...interface{}) func init() { - LoggerPrintln = func(i ...interface {}) { fmt.Println(i...) } - LoggerPrintf = func(i string, j ...interface {}) { fmt.Printf(i, j...) } + LoggerPrintln = func(i ...interface{}) { fmt.Println(i...) } + LoggerPrintf = func(i string, j ...interface{}) { fmt.Printf(i, j...) } } diff --git a/samplefilter.go b/samplefilter.go index 1fba6c1..0b28bd5 100644 --- a/samplefilter.go +++ b/samplefilter.go @@ -15,36 +15,33 @@ Sample Milter package main import ( - - "." - "fmt" - + "." + "fmt" ) - type Mymilter struct { - gomilter.MilterRaw // Embed the basic functionality. No callbacks yet + gomilter.MilterRaw // Embed the basic functionality. No callbacks yet } // Define the callback functions we are going to use func (m *Mymilter) Connect(ctx uintptr, hostname, ip string) (sfsistat int8) { - fmt.Println("mymilter.connect was called") - fmt.Printf("hostname: %s\n", hostname) - fmt.Printf("ip: %s\n", ip) - return gomilter.Reject + fmt.Println("mymilter.connect was called") + fmt.Printf("hostname: %s\n", hostname) + fmt.Printf("ip: %s\n", ip) + return gomilter.Reject } func (m *Mymilter) Helo(ctx uintptr, helohost string) (sfsistat int8) { - fmt.Println("mymilter.helo was called") - return + fmt.Println("mymilter.helo was called") + return } func main() { - mymilter := new(Mymilter) - mymilter.FilterName = "TestFilter" - mymilter.Debug = true - mymilter.Flags = gomilter.AddHdrs|gomilter.AddRcpt + mymilter := new(Mymilter) + mymilter.FilterName = "TestFilter" + mymilter.Debug = true + mymilter.Flags = gomilter.AddHdrs | gomilter.AddRcpt - // Start Milter - gomilter.Run(mymilter) + // Start Milter + gomilter.Run(mymilter) } diff --git a/samplefilter2.go b/samplefilter2.go index b877910..0f2a939 100644 --- a/samplefilter2.go +++ b/samplefilter2.go @@ -7,109 +7,107 @@ Copyright (c) 2015 Leon Baker package main import ( - - m "github.com/leonrbaker/gomilter" - "fmt" - + "fmt" + m "github.com/leonrbaker/gomilter" ) type Mymilter struct { - m.MilterRaw // Embed the basic functionality. No callbacks defined yet + m.MilterRaw // Embed the basic functionality. No callbacks defined yet } // Data type I want use for private data. Can be any type type T struct { - A uint8 - B string - C string + A uint8 + B string + C string } // Define the callback functions we are going to use func (milter *Mymilter) Connect(ctx uintptr, hostname, ip string) (sfsistat int8) { - fmt.Println("mymilter.Connect was called") - fmt.Printf("hostname: %s\n", hostname) - fmt.Printf("ip: %s\n", ip) + fmt.Println("mymilter.Connect was called") + fmt.Printf("hostname: %s\n", hostname) + fmt.Printf("ip: %s\n", ip) - t := T{1, hostname, "Test"} - m.SetPriv(ctx, &t) + t := T{1, hostname, "Test"} + m.SetPriv(ctx, &t) - return m.Continue + return m.Continue } func (milter *Mymilter) Helo(ctx uintptr, helohost string) (sfsistat int8) { - fmt.Println("mymilter.Helo was called") - fmt.Printf("helohost: %s\n", helohost) - return + fmt.Println("mymilter.Helo was called") + fmt.Printf("helohost: %s\n", helohost) + return } func (milter *Mymilter) EnvFrom(ctx uintptr, myargv []string) (sfsistat int8) { - fmt.Println("mymilter.EnvFrom was called") - fmt.Printf("myargv: %s\n", myargv) - // Show the value of a symbol - fmt.Printf("{mail_addr}: %v\n", m.GetSymVal(ctx, "{mail_addr}")) - return + fmt.Println("mymilter.EnvFrom was called") + fmt.Printf("myargv: %s\n", myargv) + // Show the value of a symbol + fmt.Printf("{mail_addr}: %v\n", m.GetSymVal(ctx, "{mail_addr}")) + return } func (milter *Mymilter) EnvRcpt(ctx uintptr, myargv []string) (sfsistat int8) { - fmt.Println("mymilter.EnvRcpt was called") - fmt.Printf("myargv: %s\n", myargv) - // Show the value of a symbol - fmt.Printf("{rcpt_addr}: %v\n", m.GetSymVal(ctx, "{rcpt_addr}")) - return + fmt.Println("mymilter.EnvRcpt was called") + fmt.Printf("myargv: %s\n", myargv) + // Show the value of a symbol + fmt.Printf("{rcpt_addr}: %v\n", m.GetSymVal(ctx, "{rcpt_addr}")) + return } func (milter *Mymilter) Header(ctx uintptr, headerf, headerv string) (sfsistat int8) { - fmt.Println("mymilter.Header was called") - fmt.Printf("header field: %s\n", headerf) - fmt.Printf("header value: %s\n", headerv) - return + fmt.Println("mymilter.Header was called") + fmt.Printf("header field: %s\n", headerf) + fmt.Printf("header value: %s\n", headerv) + return } func (milter *Mymilter) Eoh(ctx uintptr) (sfsistat int8) { - fmt.Println("mymilter.Eoh was called") - return + fmt.Println("mymilter.Eoh was called") + return } 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 - fmt.Println("mymilter.Body was called") - fmt.Println(string(body)) - fmt.Printf("Body Length: %d\n", len(body)) - return + // Be careful as a conversion of body to string will make a copy of body + fmt.Println("mymilter.Body was called") + fmt.Println(string(body)) + fmt.Printf("Body Length: %d\n", len(body)) + return } func (milter *Mymilter) Eom(ctx uintptr) (sfsistat int8) { - fmt.Println("mymilter.Eom was called") + fmt.Println("mymilter.Eom was called") - var t T - fmt.Println("m.GetPri:", m.GetPriv(ctx, &t)) - fmt.Println("t:", t) + var t T + fmt.Println("m.GetPri:", m.GetPriv(ctx, &t)) + fmt.Println("t:", t) - m.AddHeader(ctx, "LEONUX-Mailer", -"test server;\n\ttest1=\"foobar\"") + m.AddHeader(ctx, "LEONUX-Mailer", + "test server;\n\ttest1=\"foobar\"") - newBody := []byte("This is a new body") - m.ReplaceBody(ctx, newBody) - return + newBody := []byte("This is a new body") + m.ReplaceBody(ctx, newBody) + return } func (milter *Mymilter) Abort(ctx uintptr) (sfsistat int8) { - fmt.Println("mymilter.Abort was called") - return + fmt.Println("mymilter.Abort was called") + return } func (milter *Mymilter) Close(ctx uintptr) (sfsistat int8) { - fmt.Println("mymilter.Close was called") - return + fmt.Println("mymilter.Close was called") + return } func main() { - mymilter := new(Mymilter) - mymilter.FilterName = "TestFilter" - mymilter.Debug = true - mymilter.Flags = m.ADDHDRS|m.ADDRCPT|m.CHGFROM|m.CHGBODY - mymilter.Socket = "unix:/var/milterattachcheck/socket" + mymilter := new(Mymilter) + mymilter.FilterName = "TestFilter" + mymilter.Debug = true + mymilter.Flags = m.ADDHDRS | m.ADDRCPT | m.CHGFROM | m.CHGBODY + mymilter.Socket = "unix:/var/milterattachcheck/socket" - // Start Milter - m.Run(mymilter) -} \ No newline at end of file + // Start Milter + m.Run(mymilter) +}