milter 2.0.0 → 3.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/constants.ts","../src/frames.ts","../src/context.ts","../src/decisions.ts","../src/dkim.ts","../src/logger.ts","../src/server.ts","../src/spf.ts","../src/utils.ts"],"sourcesContent":["export const SMFI_VERSION = 6;\n\nexport const SMFIA = {\n UNKNOWN: 'U',\n UNIX: 'L',\n INET: '4',\n INET6: '6',\n} as const;\n\nexport const SMFIC = {\n CONNECT: 'C',\n OPTNEG: 'O',\n HELO: 'H',\n HEADER: 'L',\n EOH: 'N',\n BODY: 'B',\n BODYEOB: 'E',\n MAIL: 'M',\n RCPT: 'R',\n ABORT: 'A',\n MACRO: 'D',\n QUIT: 'Q',\n DATA: 'T',\n UNKNOWN: 'U',\n} as const;\n\nexport const SMFIR = {\n ACCEPT: 'a',\n CONTINUE: 'c',\n DISCARD: 'd',\n REJECT: 'r',\n TEMPFAIL: 't',\n REPLYCODE: 'y',\n ADDRCPT: '+',\n DELRCPT: '-',\n REPLBODY: 'b',\n ADDHEADER: 'h',\n INSHEADER: 'i',\n CHGHEADER: 'm',\n PROGRESS: 'p',\n QUARANTINE: 'q',\n SETSENDER: 's',\n} as const;\n\nexport const SMFIF = {\n ADDHDRS: 0x01,\n CHGBODY: 0x02,\n ADDRCPT: 0x04,\n DELRCPT: 0x08,\n CHGHDRS: 0x10,\n QUARANTINE: 0x20,\n SETSENDER: 0x40,\n} as const;\n\nexport const ACTION_ALL\n = SMFIF.ADDHDRS\n | SMFIF.CHGBODY\n | SMFIF.ADDRCPT\n | SMFIF.DELRCPT\n | SMFIF.CHGHDRS\n | SMFIF.QUARANTINE\n | SMFIF.SETSENDER;\n\nexport const SMFIP = {\n NOCONNECT: 0x00000001,\n NOHELO: 0x00000002,\n NOMAIL: 0x00000004,\n NORCPT: 0x00000008,\n NOBODY: 0x00000010,\n NOHDRS: 0x00000020,\n NOEOH: 0x00000040,\n NONE: 0x0000007f,\n} as const;\n\nexport const DISABLE_ALL_CALLBACKS\n = SMFIP.NOCONNECT\n | SMFIP.NOHELO\n | SMFIP.NOMAIL\n | SMFIP.NORCPT\n | SMFIP.NOBODY\n | SMFIP.NOHDRS\n | SMFIP.NOEOH;\n","import type net from 'node:net';\n\nexport function frame(command: string, data: Buffer = Buffer.alloc(0)): Buffer {\n\n const len = Buffer.alloc(4);\n\n len.writeUInt32BE(data.length + 1, 0);\n return Buffer.concat([len, Buffer.from(command, 'ascii'), data]);\n}\n\nexport function send(socket: net.Socket | null, command: string, data?: Buffer): void {\n\n if (!socket) {\n return;\n }\n\n socket.write(frame(command, data ?? Buffer.alloc(0)));\n}\n","import type net from 'node:net';\n\nimport { SMFIF, SMFIR } from './constants.js';\nimport type { DkimVerifyResult } from './dkim.js';\nimport { send } from './frames.js';\nimport type { SpfResult } from './spf.js';\nimport type { ConnectInfo, MilterActionFlag, MilterPhase } from './types.js';\n\nexport class MilterActionError extends Error {\n\n readonly code: 'E_MILTER_ACTION_STAGE' | 'E_MILTER_ACTION_CAPABILITY';\n\n constructor(code: 'E_MILTER_ACTION_STAGE' | 'E_MILTER_ACTION_CAPABILITY', message: string) {\n\n super(message);\n this.name = 'MilterActionError';\n this.code = code;\n }\n}\n\nconst EOM_ONLY_PHASES = new Set<MilterPhase>(['bodyEnd']);\n\nexport class MilterContext {\n\n readonly id: number;\n private readonly collectBody: boolean;\n private readonly maxBodyBytes: number;\n private readonly enforceActionStages: boolean;\n private chunks: Buffer[] = [];\n private bodyBytes = 0;\n private phase: MilterPhase = 'connect';\n private negotiatedActions = 0;\n socket: net.Socket | null;\n headers: Record<string, string[]> = {};\n headerLines: Array<[string, string]> = [];\n macros: Record<string, Record<string, string>> = {};\n connection: ConnectInfo | null = null;\n helo: string | null = null;\n dkim: DkimVerifyResult | null = null;\n spf: SpfResult | null = null;\n from: string[] | null = null;\n to: string[] | null = null;\n\n constructor(\n id: number,\n socket: net.Socket,\n collectBody: boolean,\n maxBodyBytes: number,\n enforceActionStages: boolean,\n ) {\n\n this.id = id;\n this.socket = socket;\n this.collectBody = collectBody;\n this.maxBodyBytes = maxBodyBytes;\n this.enforceActionStages = enforceActionStages;\n }\n\n setPhase(phase: MilterPhase): void {\n\n this.phase = phase;\n }\n\n getPhase(): MilterPhase {\n\n return this.phase;\n }\n\n setNegotiatedActions(actions: number): void {\n\n this.negotiatedActions = actions >>> 0;\n }\n\n can(action: MilterActionFlag): boolean {\n\n return (this.negotiatedActions & action) === action;\n }\n\n appendHeader(name: string, value: string): void {\n\n const key = name.toLowerCase();\n\n this.headerLines.push([name, value]);\n if (!this.headers[key]) {\n this.headers[key] = [];\n }\n this.headers[key].push(value);\n }\n\n appendBody(chunk: Buffer): void {\n\n if (!this.collectBody) {\n return;\n }\n\n this.bodyBytes += chunk.length;\n if (this.bodyBytes > this.maxBodyBytes) {\n throw new Error(\n `Body limit exceeded for connection ${this.id}: ${this.bodyBytes} > ${this.maxBodyBytes}`,\n );\n }\n\n this.chunks.push(Buffer.from(chunk));\n }\n\n getBody(): Buffer {\n\n return Buffer.concat(this.chunks);\n }\n\n resetMessage(): void {\n\n this.headers = {};\n this.headerLines = [];\n this.macros = {};\n this.dkim = null;\n this.spf = null;\n this.from = null;\n this.to = null;\n this.chunks = [];\n this.bodyBytes = 0;\n this.phase = 'connect';\n }\n\n private requireAction(action: MilterActionFlag, actionName: string, phases?: ReadonlySet<MilterPhase>): void {\n\n if ((this.negotiatedActions & action) !== action) {\n throw new MilterActionError(\n 'E_MILTER_ACTION_CAPABILITY',\n `${actionName} not permitted by negotiated action flags`,\n );\n }\n\n if (this.enforceActionStages && phases && !phases.has(this.phase)) {\n throw new MilterActionError(\n 'E_MILTER_ACTION_STAGE',\n `${actionName} is not allowed during phase '${this.phase}'`,\n );\n }\n }\n\n send(command: string, data?: Buffer): void {\n\n send(this.socket, command, data);\n }\n\n continue(): void {\n\n this.send(SMFIR.CONTINUE);\n }\n\n accept(): void {\n\n this.send(SMFIR.ACCEPT);\n }\n\n reject(): void {\n\n this.send(SMFIR.REJECT);\n }\n\n discard(): void {\n\n this.send(SMFIR.DISCARD);\n }\n\n tempfail(): void {\n\n this.send(SMFIR.TEMPFAIL);\n }\n\n progress(): void {\n\n this.send(SMFIR.PROGRESS);\n }\n\n addHeader(name: string, value: string): void {\n\n this.requireAction(SMFIF.ADDHDRS, 'addHeader', EOM_ONLY_PHASES);\n this.send(SMFIR.ADDHEADER, Buffer.from(`${name}\\0${value}\\0`, 'utf8'));\n }\n\n insertHeader(index: number, name: string, value: string): void {\n\n this.requireAction(SMFIF.ADDHDRS, 'insertHeader', EOM_ONLY_PHASES);\n\n const suffix = Buffer.from(`${name}\\0${value}\\0`, 'utf8');\n const data = Buffer.alloc(4 + suffix.length);\n\n data.writeUInt32BE(index >>> 0, 0);\n suffix.copy(data, 4);\n this.send(SMFIR.INSHEADER, data);\n }\n\n changeHeader(name: string, index: number, value = ''): void {\n\n this.requireAction(SMFIF.CHGHDRS, 'changeHeader', EOM_ONLY_PHASES);\n\n const suffix = Buffer.from(`${name}\\0${value}\\0`, 'utf8');\n const data = Buffer.alloc(4 + suffix.length);\n\n data.writeUInt32BE(index >>> 0, 0);\n suffix.copy(data, 4);\n this.send(SMFIR.CHGHEADER, data);\n }\n\n addRecipient(address: string): void {\n\n this.requireAction(SMFIF.ADDRCPT, 'addRecipient', EOM_ONLY_PHASES);\n this.send(SMFIR.ADDRCPT, Buffer.from(`${address}\\0`, 'utf8'));\n }\n\n deleteRecipient(address: string): void {\n\n this.requireAction(SMFIF.DELRCPT, 'deleteRecipient', EOM_ONLY_PHASES);\n this.send(SMFIR.DELRCPT, Buffer.from(`${address}\\0`, 'utf8'));\n }\n\n replaceBody(chunk: Buffer | string): void {\n\n this.requireAction(SMFIF.CHGBODY, 'replaceBody', EOM_ONLY_PHASES);\n\n const payload = typeof chunk === 'string' ? Buffer.from(chunk, 'utf8') : chunk;\n\n this.send(SMFIR.REPLBODY, payload);\n }\n\n quarantine(reason = ''): void {\n\n this.requireAction(SMFIF.QUARANTINE, 'quarantine', EOM_ONLY_PHASES);\n this.send(SMFIR.QUARANTINE, Buffer.from(`${reason}\\0`, 'utf8'));\n }\n\n setSender(sender: string): void {\n\n this.requireAction(SMFIF.SETSENDER, 'setSender', EOM_ONLY_PHASES);\n this.send(SMFIR.SETSENDER, Buffer.from(`${sender}\\0`, 'utf8'));\n }\n\n replyCode(code: string, message: string, enhancedCode = '5.7.1'): void {\n\n this.send(SMFIR.REPLYCODE, Buffer.from(`${code} ${enhancedCode} ${message}\\0`, 'utf8'));\n }\n}\n","import { SMFIR } from './constants.js';\nimport type { MilterDecision, MilterDecisionKeyword, MilterReply } from './types.js';\n\nconst DECISION_TO_COMMAND: Record<MilterDecisionKeyword, string> = {\n continue: SMFIR.CONTINUE,\n accept: SMFIR.ACCEPT,\n reject: SMFIR.REJECT,\n discard: SMFIR.DISCARD,\n tempfail: SMFIR.TEMPFAIL,\n};\n\nexport const Decision = {\n continue(): MilterReply {\n\n return { command: SMFIR.CONTINUE };\n },\n accept(): MilterReply {\n\n return { command: SMFIR.ACCEPT };\n },\n reject(): MilterReply {\n\n return { command: SMFIR.REJECT };\n },\n discard(): MilterReply {\n\n return { command: SMFIR.DISCARD };\n },\n tempfail(): MilterReply {\n\n return { command: SMFIR.TEMPFAIL };\n },\n replyCode(code: string, message: string, enhancedCode = '5.7.1'): MilterReply {\n\n const payload = Buffer.from(`${code} ${enhancedCode} ${message}\\0`, 'utf8');\n\n return {\n command: SMFIR.REPLYCODE,\n data: payload,\n };\n },\n};\n\nexport function normalizeDecision(\n decision: MilterDecision,\n fallback: MilterDecisionKeyword,\n): MilterReply | null {\n\n if (decision === null) {\n return null;\n }\n\n if (decision === undefined) {\n return { command: DECISION_TO_COMMAND[fallback] };\n }\n\n if (typeof decision === 'string') {\n return { command: DECISION_TO_COMMAND[decision] };\n }\n\n if (decision.data === undefined) {\n return { command: decision.command };\n }\n\n return {\n command: decision.command,\n data: decision.data,\n };\n}\n","import type {\n DKIMSignOptions,\n DKIMSignResult,\n DKIMVerifyOptions,\n DKIMVerifyResult,\n MessageInput,\n} from 'mailauth';\nimport { dkimSign } from 'mailauth/lib/dkim/sign.js';\nimport { dkimVerify } from 'mailauth/lib/dkim/verify.js';\n\nimport type { MilterContext } from './context.js';\n\nexport type DkimSignOptions = DKIMSignOptions;\nexport type DkimSignResult = DKIMSignResult;\nexport type DkimVerifyOptions = DKIMVerifyOptions;\nexport type DkimVerifyResult = DKIMVerifyResult;\n\nfunction normalizeSender(from: readonly string[] | null): string | undefined {\n\n const value = from?.[0]?.trim();\n\n if (!value || value === '<>') {\n return undefined;\n }\n\n if (value.startsWith('<') && value.endsWith('>')) {\n return value.slice(1, -1).trim() || undefined;\n }\n\n return value;\n}\n\nfunction buildMessage(ctx: MilterContext, body: Buffer): Buffer {\n\n const headers = ctx.headerLines.map(([name, value]) => `${name}: ${value}`).join('\\r\\n');\n\n return Buffer.concat([\n Buffer.from(`${headers}\\r\\n\\r\\n`, 'utf8'),\n body,\n ]);\n}\n\nexport async function verifyDkim(\n body: Buffer,\n ctx: MilterContext,\n options: DkimVerifyOptions = {},\n): Promise<DkimVerifyResult> {\n\n const sender = options.sender ?? normalizeSender(ctx.from);\n const result = await dkimVerify(buildMessage(ctx, body), {\n ...options,\n ...(sender === undefined ? {} : { sender }),\n });\n\n ctx.dkim = result;\n return result;\n}\n\nexport async function signDkim(\n message: MessageInput,\n options: DkimSignOptions,\n): Promise<DkimSignResult> {\n\n const signatureData = options.signatureData?.length\n ? options.signatureData.map((entry) => ({ ...entry }))\n : [{\n signingDomain: options.signingDomain,\n selector: options.selector,\n privateKey: options.privateKey,\n ...(options.canonicalization === undefined ? {} : { canonicalization: options.canonicalization }),\n ...(options.algorithm === undefined ? {} : { algorithm: options.algorithm }),\n ...(options.maxBodyLength === undefined ? {} : { maxBodyLength: options.maxBodyLength }),\n ...(options.identity === undefined ? {} : { identity: options.identity }),\n }];\n\n return dkimSign(message, {\n ...options,\n signatureData,\n });\n}\n","import type { Logger } from './types.js';\n\nexport class ConsoleLogger implements Logger {\n\n private readonly prefix: string;\n\n constructor(prefix = '[MILTER]') {\n\n this.prefix = prefix;\n }\n\n debug(...args: unknown[]): void {\n\n console.debug(this.prefix, ...args);\n }\n\n info(...args: unknown[]): void {\n\n console.info(this.prefix, ...args);\n }\n\n warn(...args: unknown[]): void {\n\n console.warn(this.prefix, ...args);\n }\n\n error(...args: unknown[]): void {\n\n console.error(this.prefix, ...args);\n }\n}\n","import fs from 'node:fs';\nimport net from 'node:net';\n\nimport {\n ACTION_ALL,\n DISABLE_ALL_CALLBACKS,\n SMFIC,\n SMFIF,\n SMFI_VERSION,\n SMFIP,\n} from './constants.js';\nimport { MilterContext } from './context.js';\nimport { normalizeDecision } from './decisions.js';\nimport { verifyDkim } from './dkim.js';\nimport type { DkimVerifyOptions } from './dkim.js';\nimport { ConsoleLogger } from './logger.js';\nimport { send } from './frames.js';\nimport { checkSpf } from './spf.js';\nimport type { SpfOptions } from './spf.js';\nimport type {\n HeaderMap,\n Logger,\n MilterDecision,\n MilterEventMap,\n MilterEventName,\n MilterOptions,\n} from './types.js';\nimport {\n cloneHeaders, parseConnectInfo, readCString, readCStringList, readCStringPairs,\n} from './utils.js';\n\ntype GenericHandler = (...args: unknown[]) => unknown | Promise<unknown>;\ntype HandlerMap = Record<MilterEventName, Set<GenericHandler>>;\n\ninterface ResolvedOptions {\n socketPath: string | undefined;\n host: string;\n port: number | undefined;\n actions: number;\n unlinkOnStart: boolean;\n chmod: number | false;\n collectBody: boolean;\n maxBodyBytes: number;\n enforceActionStages: boolean;\n defaultDecision: 'continue' | 'accept' | 'reject' | 'discard' | 'tempfail';\n logger: Logger;\n}\n\nconst DEFAULT_OPTIONS: ResolvedOptions = {\n socketPath: undefined,\n host: '127.0.0.1',\n port: undefined,\n actions: SMFIF.ADDHDRS | SMFIF.CHGBODY | SMFIF.ADDRCPT,\n unlinkOnStart: true,\n chmod: 0o777,\n collectBody: true,\n maxBodyBytes: 32 * 1024 * 1024,\n enforceActionStages: true,\n defaultDecision: 'continue',\n logger: new ConsoleLogger('[MILTER]'),\n};\n\nfunction createHandlerMap(): HandlerMap {\n\n return {\n connect: new Set(),\n helo: new Set(),\n mail: new Set(),\n rcpt: new Set(),\n headerLine: new Set(),\n headers: new Set(),\n header: new Set(),\n bodyChunk: new Set(),\n bodyEnd: new Set(),\n data: new Set(),\n macro: new Set(),\n abort: new Set(),\n close: new Set(),\n unknown: new Set(),\n error: new Set(),\n };\n}\n\nexport class MilterServer {\n\n private readonly server: net.Server;\n private readonly handlers: HandlerMap;\n private readonly options: ResolvedOptions;\n private nextConnectionId = 1;\n private dkimOptions: DkimVerifyOptions | null = null;\n private spfOptions: SpfOptions | null = null;\n\n constructor(options: MilterOptions = {}) {\n\n this.options = {\n ...DEFAULT_OPTIONS,\n ...options,\n logger: options.logger ?? DEFAULT_OPTIONS.logger,\n };\n\n if (!this.options.socketPath && this.options.port === undefined) {\n throw new Error('Either socketPath or port must be provided.');\n }\n\n this.handlers = createHandlerMap();\n this.server = net.createServer((socket) => {\n\n this.handleConnection(socket);\n });\n\n this.server.on('error', (err) => {\n\n this.options.logger.error('Server error', err);\n this.emitInternalError(undefined, err);\n });\n }\n\n on<K extends MilterEventName>(event: K, handler: MilterEventMap[K]): this {\n\n this.handlers[event].add(handler as GenericHandler);\n return this;\n }\n\n off<K extends MilterEventName>(event: K, handler: MilterEventMap[K]): this {\n\n this.handlers[event].delete(handler as GenericHandler);\n return this;\n }\n\n once<K extends MilterEventName>(event: K, handler: MilterEventMap[K]): this {\n\n const wrap = (async (...args: unknown[]) => {\n\n this.off(event, wrap as MilterEventMap[K]);\n return (handler as GenericHandler)(...args);\n }) as MilterEventMap[K];\n\n this.on(event, wrap);\n return this;\n }\n\n useSpf(options: SpfOptions = {}): this {\n\n this.spfOptions = options;\n return this;\n }\n\n useDkim(options: DkimVerifyOptions = {}): this {\n\n if (!this.options.collectBody) {\n throw new Error('DKIM verification requires collectBody to be enabled.');\n }\n\n this.dkimOptions = options;\n return this;\n }\n\n async listen(socketPathOverride?: string): Promise<void> {\n\n if (socketPathOverride) {\n this.options.socketPath = socketPathOverride;\n }\n\n if (this.options.socketPath) {\n if (this.options.unlinkOnStart && fs.existsSync(this.options.socketPath)) {\n fs.unlinkSync(this.options.socketPath);\n }\n\n await new Promise<void>((resolve, reject) => {\n\n this.server.listen(this.options.socketPath, (err?: Error) => {\n\n if (err) {\n reject(err);\n return;\n }\n\n if (this.options.chmod !== false) {\n fs.chmodSync(this.options.socketPath as string, this.options.chmod);\n }\n\n this.options.logger.info('Listening on', this.options.socketPath);\n resolve();\n });\n });\n return;\n }\n\n await new Promise<void>((resolve, reject) => {\n\n this.server.listen(this.options.port, this.options.host, (err?: Error) => {\n\n if (err) {\n reject(err);\n return;\n }\n\n this.options.logger.info('Listening on', `${this.options.host}:${this.options.port}`);\n resolve();\n });\n });\n }\n\n async close(): Promise<void> {\n\n await new Promise<void>((resolve, reject) => {\n\n this.server.close((err?: Error) => {\n\n if (err) {\n reject(err);\n return;\n }\n resolve();\n });\n });\n }\n\n private handleConnection(socket: net.Socket): void {\n\n const ctx = new MilterContext(\n this.nextConnectionId++,\n socket,\n this.options.collectBody,\n this.options.maxBodyBytes,\n this.options.enforceActionStages,\n );\n\n this.options.logger.info('MTA connected', ctx.id);\n\n let closed = false;\n let buffer = Buffer.alloc(0);\n let pipeline: Promise<void> = Promise.resolve();\n const emitClose = () => {\n\n if (closed) {\n return;\n }\n closed = true;\n void this.emit('close', ctx);\n };\n\n socket.on('error', (err) => {\n\n this.options.logger.error('Socket error', ctx.id, err);\n this.emitInternalError(ctx, err);\n });\n\n socket.on('close', () => {\n\n ctx.socket = null;\n emitClose();\n });\n\n socket.on('end', () => {\n\n this.options.logger.info('MTA disconnected', ctx.id);\n ctx.socket = null;\n emitClose();\n });\n\n socket.on('data', (chunk) => {\n\n buffer = Buffer.concat([buffer, chunk]);\n\n while (buffer.length >= 5) {\n const len = buffer.readUInt32BE(0);\n\n if (len < 1) {\n this.options.logger.warn('Invalid frame length', len, 'connection', ctx.id);\n socket.destroy();\n return;\n }\n\n const total = 4 + len;\n\n if (buffer.length < total) {\n break;\n }\n\n const command = buffer.toString('ascii', 4, 5);\n const data = buffer.subarray(5, total);\n\n buffer = buffer.subarray(total);\n\n pipeline = pipeline\n .then(() => this.parsePacket(ctx, command, data))\n .catch((err) => {\n\n this.options.logger.error('Packet handling error', ctx.id, err);\n this.emitInternalError(ctx, err);\n send(ctx.socket, 't');\n });\n }\n });\n }\n\n private async parsePacket(ctx: MilterContext, command: string, data: Buffer): Promise<void> {\n\n switch (command) {\n case SMFIC.OPTNEG:\n {\n if (data.length < 12) {\n this.options.logger.warn('Invalid OPTNEG payload length', data.length, 'connection', ctx.id);\n return;\n }\n\n const mtaVersion = data.readUInt32BE(0);\n const mtaActions = data.readUInt32BE(4);\n const mtaProtocol = data.readUInt32BE(8);\n const version = Math.min(mtaVersion, SMFI_VERSION);\n const actions = (this.options.actions & mtaActions) >>> 0;\n const protocol = (this.computeProtocolMask() & mtaProtocol) >>> 0;\n\n ctx.setNegotiatedActions(actions);\n\n const payload = Buffer.alloc(12);\n\n payload.writeUInt32BE(version, 0);\n payload.writeUInt32BE(actions, 4);\n payload.writeUInt32BE(protocol, 8);\n send(ctx.socket, SMFIC.OPTNEG, payload);\n return;\n }\n\n case SMFIC.CONNECT:\n {\n ctx.setPhase('connect');\n\n const info = parseConnectInfo(data);\n\n ctx.connection = info;\n\n const decision = (await this.emit('connect', info, ctx)) as MilterDecision;\n\n this.respond(ctx, decision);\n return;\n }\n\n case SMFIC.HELO:\n {\n ctx.setPhase('helo');\n\n const helo = readCString(data, 0).value;\n\n ctx.helo = helo;\n\n const decision = (await this.emit('helo', helo, ctx)) as MilterDecision;\n\n this.respond(ctx, decision);\n return;\n }\n\n case SMFIC.MAIL:\n {\n ctx.setPhase('mail');\n\n const from = readCStringList(data);\n\n ctx.from = from;\n\n if (this.spfOptions) {\n await checkSpf(from, ctx, this.spfOptions);\n }\n\n const decision = (await this.emit('mail', from, ctx)) as MilterDecision;\n\n this.respond(ctx, decision);\n return;\n }\n\n case SMFIC.RCPT:\n {\n ctx.setPhase('rcpt');\n\n const to = readCStringList(data);\n\n ctx.to = to;\n\n const decision = (await this.emit('rcpt', to, ctx)) as MilterDecision;\n\n this.respond(ctx, decision);\n return;\n }\n\n case SMFIC.HEADER:\n {\n ctx.setPhase('headerLine');\n\n const p = readCString(data, 0);\n const q = readCString(data, p.next);\n\n if (p.value.length > 0) {\n ctx.appendHeader(p.value, q.value);\n\n const decision = (await this.emit('headerLine', p.value, q.value, ctx)) as MilterDecision;\n\n this.respond(ctx, decision);\n return;\n }\n\n this.respond(ctx, undefined);\n return;\n }\n\n case SMFIC.EOH:\n {\n ctx.setPhase('eoh');\n\n const headers = cloneHeaders(ctx.headers) as HeaderMap;\n const legacy = (await this.emit('header', headers, ctx)) as MilterDecision;\n const modern = (await this.emit('headers', headers, ctx)) as MilterDecision;\n\n this.respond(ctx, modern ?? legacy);\n return;\n }\n\n case SMFIC.BODY:\n {\n ctx.setPhase('bodyChunk');\n try {\n ctx.appendBody(data);\n } catch (err) {\n this.emitInternalError(ctx, err);\n this.respond(ctx, 'tempfail');\n return;\n }\n\n const decision = (await this.emit('bodyChunk', Buffer.from(data), ctx)) as MilterDecision;\n\n this.respond(ctx, decision);\n return;\n }\n\n case SMFIC.BODYEOB:\n {\n ctx.setPhase('bodyEnd');\n\n const body = ctx.getBody();\n\n if (this.dkimOptions) {\n await verifyDkim(body, ctx, this.dkimOptions);\n }\n\n const decision = (await this.emit('bodyEnd', body, ctx)) as MilterDecision;\n\n this.respond(ctx, decision);\n return;\n }\n\n case SMFIC.DATA:\n {\n ctx.setPhase('data');\n\n const decision = (await this.emit('data', data.toString('utf8'), ctx)) as MilterDecision;\n\n this.respond(ctx, decision);\n return;\n }\n\n case SMFIC.MACRO:\n {\n ctx.setPhase('macro');\n\n const cmd = data.toString('ascii', 0, 1);\n const map = readCStringPairs(data, 1);\n\n ctx.macros[cmd] = map;\n await this.emit('macro', cmd, map, ctx);\n return;\n }\n\n case SMFIC.ABORT:\n {\n ctx.setPhase('abort');\n ctx.resetMessage();\n await this.emit('abort', ctx);\n return;\n }\n\n case SMFIC.QUIT:\n {\n ctx.setPhase('close');\n await this.emit('close', ctx);\n if (ctx.socket) {\n ctx.socket.end();\n ctx.socket = null;\n }\n return;\n }\n\n case SMFIC.UNKNOWN:\n {\n ctx.setPhase('unknown');\n\n const decision = (await this.emit('unknown', command, data, ctx)) as MilterDecision;\n\n this.respond(ctx, decision);\n return;\n }\n\n default:\n {\n ctx.setPhase('unknown');\n this.options.logger.warn('Unknown command', command, 'connection', ctx.id);\n\n const decision = (await this.emit('unknown', command, data, ctx)) as MilterDecision;\n\n this.respond(ctx, decision);\n }\n }\n }\n\n private computeProtocolMask(): number {\n\n let flags = DISABLE_ALL_CALLBACKS;\n\n if (this.handlers.connect.size > 0) {\n flags &= ~SMFIP.NOCONNECT;\n }\n if (this.handlers.helo.size > 0) {\n flags &= ~SMFIP.NOHELO;\n }\n if (this.handlers.mail.size > 0 || this.spfOptions) {\n flags &= ~SMFIP.NOMAIL;\n }\n if (this.handlers.rcpt.size > 0) {\n flags &= ~SMFIP.NORCPT;\n }\n if (this.handlers.bodyChunk.size > 0 || this.handlers.bodyEnd.size > 0 || this.dkimOptions) {\n flags &= ~SMFIP.NOBODY;\n }\n if (\n this.handlers.headerLine.size > 0\n || this.handlers.headers.size > 0\n || this.handlers.header.size > 0\n || this.dkimOptions\n ) {\n flags &= ~SMFIP.NOHDRS;\n }\n if (this.handlers.headers.size > 0 || this.handlers.header.size > 0) {\n flags &= ~SMFIP.NOEOH;\n }\n\n return flags >>> 0;\n }\n\n private respond(ctx: MilterContext, decision: MilterDecision): void {\n\n const normalized = normalizeDecision(decision, this.options.defaultDecision);\n\n if (!normalized) {\n return;\n }\n send(ctx.socket, normalized.command, normalized.data);\n }\n\n private async emit<K extends MilterEventName>(\n event: K,\n ...args: Parameters<MilterEventMap[K]>\n ): Promise<unknown> {\n\n const callbacks = this.handlers[event];\n\n if (!callbacks || callbacks.size === 0) {\n return undefined;\n }\n\n let result: unknown;\n\n for (const fn of callbacks) {\n result = await fn(...(args as unknown[]));\n }\n\n return result;\n }\n\n private emitInternalError(ctx: MilterContext | undefined, err: unknown): void {\n\n const callbacks = this.handlers.error;\n\n if (callbacks.size === 0) {\n return;\n }\n\n for (const fn of callbacks) {\n Promise.resolve(fn(err, ctx)).catch(() => {\n\n this.options.logger.error('Error handler failed', err);\n });\n }\n }\n}\n\nexport class Milter extends MilterServer {\n\n constructor(options: MilterOptions = {}) {\n\n const resolved = {\n actions: ACTION_ALL,\n ...options,\n };\n\n super(resolved);\n }\n}\n","import type { DNSResolver, SPFResult } from 'mailauth';\nimport { spf as verifySpf } from 'mailauth/lib/spf/index.js';\n\nimport type { MilterContext } from './context.js';\nimport type { MilterDecisionKeyword } from './types.js';\n\nexport type SpfResultCode\n = | 'pass'\n | 'fail'\n | 'softfail'\n | 'neutral'\n | 'none'\n | 'temperror'\n | 'permerror';\n\nexport type SpfPolicy = Partial<Record<SpfResultCode, MilterDecisionKeyword>>;\n\nexport interface SpfOptions {\n mta?: string;\n maxResolveCount?: number;\n maxVoidCount?: number;\n resolver?: DNSResolver;\n}\n\nexport type SpfResult = SPFResult;\n\nconst DEFAULT_POLICY: Record<SpfResultCode, MilterDecisionKeyword> = {\n pass: 'continue',\n fail: 'reject',\n softfail: 'continue',\n neutral: 'continue',\n none: 'continue',\n temperror: 'tempfail',\n permerror: 'continue',\n};\n\nfunction normalizeSender(from: string | readonly string[]): string | undefined {\n\n const value = (typeof from === 'string' ? from : from[0])?.trim();\n\n if (!value || value === '<>') {\n return undefined;\n }\n\n if (value.startsWith('<') && value.endsWith('>')) {\n return value.slice(1, -1).trim() || undefined;\n }\n\n return value;\n}\n\nexport async function checkSpf(\n from: string | readonly string[],\n ctx: MilterContext,\n options: SpfOptions = {},\n): Promise<SpfResult> {\n\n const ip = ctx.connection?.address;\n\n if (!ip) {\n throw new Error('SPF requires the client IP from the Milter CONNECT event.');\n }\n\n const sender = normalizeSender(from);\n const helo = ctx.helo ?? ctx.connection?.hostname;\n const result = await verifySpf({\n ip,\n ...(sender === undefined ? {} : { sender }),\n ...(helo === undefined ? {} : { helo }),\n ...(options.mta === undefined ? {} : { mta: options.mta }),\n ...(options.maxResolveCount === undefined ? {} : { maxResolveCount: options.maxResolveCount }),\n ...(options.maxVoidCount === undefined ? {} : { maxVoidCount: options.maxVoidCount }),\n ...(options.resolver === undefined ? {} : { resolver: options.resolver }),\n });\n\n ctx.spf = result;\n return result;\n}\n\nexport function spfDecision(result: SpfResult, policy: SpfPolicy = {}): MilterDecisionKeyword {\n\n const code = result.status.result as SpfResultCode;\n\n return policy[code] ?? DEFAULT_POLICY[code] ?? 'continue';\n}\n","import { SMFIA } from './constants.js';\nimport type { ConnectInfo } from './types.js';\n\nexport function readCString(buf: Buffer, start = 0): { value: string;\n next: number; } {\n\n if (start >= buf.length) {\n return {\n value: '',\n next: buf.length,\n };\n }\n\n const end = buf.indexOf(0x00, start);\n\n if (end === -1) {\n return {\n value: buf.toString('utf8', start),\n next: buf.length,\n };\n }\n\n return {\n value: buf.toString('utf8', start, end),\n next: end + 1,\n };\n}\n\nexport function readCStringList(buf: Buffer, start = 0): string[] {\n\n const out: string[] = [];\n let offset = start;\n\n while (offset < buf.length) {\n const { value, next } = readCString(buf, offset);\n\n if (next === offset) {\n break;\n }\n\n offset = next;\n if (value.length === 0) {\n if (offset >= buf.length) {\n break;\n }\n continue;\n }\n\n out.push(value);\n }\n\n return out;\n}\n\nexport function readCStringPairs(buf: Buffer, start = 0): Record<string, string> {\n\n const list = readCStringList(buf, start);\n const map: Record<string, string> = {};\n\n for (let i = 0; i + 1 < list.length; i += 2) {\n const key = list[i];\n const value = list[i + 1];\n\n if (key && value) {\n map[key] = value;\n }\n }\n\n return map;\n}\n\nexport function parseConnectInfo(payload: Buffer): ConnectInfo {\n\n const host = readCString(payload, 0);\n const familyByte = payload.toString('ascii', host.next, host.next + 1);\n\n if (familyByte === SMFIA.UNKNOWN || host.next >= payload.length) {\n return {\n hostname: host.value,\n family: 'UNKNOWN',\n };\n }\n\n if (host.next + 3 > payload.length) {\n return {\n hostname: host.value,\n family: 'UNKNOWN',\n };\n }\n\n const port = payload.readUInt16BE(host.next + 1);\n const addr = readCString(payload, host.next + 3).value;\n const family\n = familyByte === SMFIA.UNIX\n ? 'UNIX'\n : familyByte === SMFIA.INET\n ? 'INET'\n : familyByte === SMFIA.INET6\n ? 'INET6'\n : 'UNKNOWN';\n\n return {\n hostname: host.value,\n family,\n address: addr,\n port,\n };\n}\n\nexport function cloneHeaders(headers: Record<string, string[]>): Record<string, string[]> {\n\n const out: Record<string, string[]> = {};\n\n for (const [name, values] of Object.entries(headers)) {\n out[name] = [...values];\n }\n return out;\n}\n"],"mappings":"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","names":["normalizeSender"]}
package/package.json CHANGED
@@ -1,26 +1,120 @@
1
1
  {
2
- "name": "milter",
3
- "description": "bindings for writing postfix milter daemons",
4
- "version": "2.0.0",
5
- "author" : "jon shusta",
6
- "maintainers":[
7
- {
8
- "name":"jons",
9
- "email":"jon@wroth.org"
10
- }
11
- ],
12
- "repository" : {
13
- "type" : "git",
14
- "url" : "http://github.com/jons/node-milter.git"
2
+ "name": "milter",
3
+ "title": "Milter",
4
+ "version": "3.0.0",
5
+ "description": "Milter server for asynchronous Node.js mail filters and message mutation",
6
+ "homepage": "https://raw.org/software/libraries/milter-js/",
7
+ "bugs": {
8
+ "url": "https://github.com/rawify/Milter.js/issues"
9
+ },
10
+ "keywords": [
11
+ "milter",
12
+ "protocol",
13
+ "smtp",
14
+ "mail",
15
+ "server",
16
+ "postfix",
17
+ "sendmail",
18
+ "opensmtpd",
19
+ "spam-filter",
20
+ "email-security"
21
+ ],
22
+ "private": false,
23
+ "type": "module",
24
+ "main": "./dist/index.cjs",
25
+ "module": "./dist/index.js",
26
+ "types": "./dist/index.d.ts",
27
+ "files": [
28
+ "dist",
29
+ "res",
30
+ "README.md",
31
+ "LICENSE"
32
+ ],
33
+ "exports": {
34
+ ".": {
35
+ "import": {
36
+ "types": "./dist/index.d.ts",
37
+ "default": "./dist/index.js"
38
+ },
39
+ "require": {
40
+ "types": "./dist/index.d.cts",
41
+ "default": "./dist/index.cjs"
42
+ },
43
+ "default": "./dist/index.js"
15
44
  },
16
- "scripts":{
17
- "test": "nodeunit test"
45
+ "./constants": {
46
+ "import": {
47
+ "types": "./dist/constants.d.ts",
48
+ "default": "./dist/constants.js"
49
+ },
50
+ "require": {
51
+ "types": "./dist/constants.d.cts",
52
+ "default": "./dist/constants.cjs"
53
+ },
54
+ "default": "./dist/constants.js"
18
55
  },
19
- "main" : "./lib/milter",
20
- "license" : "CC-BY-4.0",
21
- "devDependencies": {
22
- "nodeunit": "*"
23
- },
24
- "engines": ["node =7.7.4"],
25
- "keywords": ["postfix", "milter"]
56
+ "./package.json": "./package.json"
57
+ },
58
+ "repository": {
59
+ "type": "git",
60
+ "url": "git+https://github.com/rawify/Milter.js.git"
61
+ },
62
+ "funding": {
63
+ "type": "github",
64
+ "url": "https://github.com/sponsors/rawify"
65
+ },
66
+ "author": {
67
+ "name": "Robert Eisele",
68
+ "email": "robert@raw.org",
69
+ "url": "https://raw.org/"
70
+ },
71
+ "license": "MIT",
72
+ "engines": {
73
+ "node": ">=22.19.0"
74
+ },
75
+ "packageManager": "npm@10.9.7",
76
+ "sideEffects": false,
77
+ "scripts": {
78
+ "clean": "rm -rf dist",
79
+ "build": "tsup",
80
+ "typecheck": "tsc --noEmit",
81
+ "lint": "eslint .",
82
+ "lint:fix": "eslint . --fix",
83
+ "example:dmarc": "npm run build && node examples/dmarc-collector.mjs",
84
+ "example:dmarc:db": "npm run build && node examples/dmarc-collector-db.mjs",
85
+ "example:chatgpt-spamfilter": "npm run build && node examples/spamfilter/chatgpt-spamfilter.mjs",
86
+ "example:ollama-spamfilter": "npm run build && node examples/spamfilter/ollama-spamfilter.mjs",
87
+ "example:simple-spamfilter": "npm run build && node examples/spamfilter/simple-spamfilter.mjs",
88
+ "example:domaindata-holo-check": "npm run build && node examples/domaindata-holo-check.mjs",
89
+ "test": "npm run build && node --test tests/*.test.mjs",
90
+ "check": "npm run lint && npm run typecheck && npm run test",
91
+ "prepack": "npm run build",
92
+ "prepublishOnly": "npm run check"
93
+ },
94
+ "devDependencies": {
95
+ "@eslint/js": "^9.34.0",
96
+ "@stylistic/eslint-plugin": "^5.10.0",
97
+ "@types/node": "^22.18.0",
98
+ "dotenv": "^17.2.2",
99
+ "eslint": "^9.34.0",
100
+ "globals": "^16.4.0",
101
+ "mailparser": "^3.9.17",
102
+ "ollama": "^0.6.3",
103
+ "openai": "^5.20.3",
104
+ "tldts": "^7.0.13",
105
+ "tsup": "^8.5.0",
106
+ "typescript": "^5.9.2",
107
+ "typescript-eslint": "^8.41.0"
108
+ },
109
+ "overrides": {
110
+ "esbuild": "0.27.2",
111
+ "joi": "18.2.9",
112
+ "nodemailer": "10.0.10"
113
+ },
114
+ "publishConfig": {
115
+ "access": "public"
116
+ },
117
+ "dependencies": {
118
+ "mailauth": "^5.0.3"
119
+ }
26
120
  }
@@ -0,0 +1,11 @@
1
+ <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 200 200" role="img" aria-labelledby="title">
2
+ <title>Maail</title>
3
+ <defs>
4
+ <linearGradient id="background" x1="0" y1="0" x2="1" y2="1">
5
+ <stop offset="0" stop-color="#f4f6ef"/>
6
+ <stop offset="1" stop-color="#bbff72"/>
7
+ </linearGradient>
8
+ </defs>
9
+ <rect width="200" height="200" rx="24" fill="url(#background)"/>
10
+ <path fill="#10201a" d="M10 37.7h36.2l1.3 14.6C56.3 41.1 68.1 35.5 83 35.5c15.7 0 26.3 6.2 31.8 18.7 8.5-12.5 20.7-18.7 36.6-18.7 25.1 0 38 15.2 38.7 45.6V162h-38.7V83.6c0-6.4-1.1-11-3.2-13.9-2.2-2.9-6.1-4.4-11.8-4.4-7.7 0-13.4 3.4-17.1 10.2l.1 1.5v85h-38.8V83.8c0-6.5-1-11.1-3-14-2.1-2.9-6-4.4-11.9-4.4-7.4 0-13 3.4-16.9 10.2V162H10V37.7Z"/>
11
+ </svg>
@@ -0,0 +1,79 @@
1
+ <?xml version="1.0" encoding="UTF-8" standalone="no"?>
2
+ <svg
3
+ width="900"
4
+ height="320"
5
+ viewBox="0 0 900 320"
6
+ role="img"
7
+ aria-labelledby="title desc"
8
+ version="1.1"
9
+ id="svg18"
10
+ xmlns="http://www.w3.org/2000/svg"
11
+ xmlns:svg="http://www.w3.org/2000/svg">
12
+ <defs
13
+ id="defs22" />
14
+ <title
15
+ id="title">milter.js</title>
16
+ <g
17
+ fill="none"
18
+ stroke="#0b1020"
19
+ stroke-width="14"
20
+ stroke-linecap="round"
21
+ stroke-linejoin="round"
22
+ id="g8">
23
+ <path
24
+ d="m 196,231 h 63 c 18,0 31,-13 31,-31 V 95 C 290,77 277,64 259,64 H 61 C 43,64 30,77 30,95 v 105 c 0,18 13,31 31,31 h 63"
25
+ id="path4" />
26
+ <path
27
+ d="m 45,84 98,77 c 10,8 24,8 34,0 l 98,-77"
28
+ id="path6" />
29
+ </g>
30
+
31
+ <path
32
+ d="M160 174v31"
33
+ fill="none"
34
+ stroke="#FF4F2E"
35
+ stroke-width="14"
36
+ stroke-linecap="round"
37
+ id="path10" />
38
+ <path
39
+ d="M122 202h76l-28 35v29h-20v-29Z"
40
+ fill="#FF4F2E"
41
+ id="path12" />
42
+ <g
43
+ aria-label="milter"
44
+ id="text14"
45
+ style="font-weight:700;font-size:122px;font-family:'Nimbus Sans', Arial, sans-serif;letter-spacing:-5;fill:#0b1020">
46
+ <path
47
+ d="M 367.32,152.12 V 218 h 17.08 v -39.528 c 0,-8.174 4.392,-12.932 11.712,-12.932 5.734,0 9.272,3.294 9.272,8.54 V 218 h 17.08 v -39.528 c 0,-8.052 4.392,-12.932 11.712,-12.932 5.734,0 9.272,3.294 9.272,8.54 V 218 h 17.08 v -46.604 c 0,-12.81 -7.808,-20.374 -20.984,-20.374 -8.418,0 -14.152,2.928 -19.276,9.76 -3.172,-6.222 -9.76,-9.76 -17.934,-9.76 -7.564,0 -12.444,2.44 -18.056,9.272 v -8.174 z"
48
+ id="path24" />
49
+ <path
50
+ d="m 488.71199,152.12 h -17.08 V 218 h 17.08 z m 0,-23.058 h -17.08 v 15.25 h 17.08 z"
51
+ id="path26" />
52
+ <path
53
+ d="m 517.62798,129.062 h -17.08 V 218 h 17.08 z"
54
+ id="path28" />
55
+ <path
56
+ d="m 558.01196,153.462 h -9.516 v -17.69 h -17.08 v 17.69 h -8.418 v 11.346 h 8.418 v 40.504 c 0,10.37 5.49,15.494 16.714,15.494 3.904,0 6.71,-0.366 9.882,-1.342 v -11.956 c -1.708,0.244 -2.562,0.366 -3.904,0.366 -4.514,0 -5.612,-1.342 -5.612,-7.198 v -35.868 h 9.516 z"
57
+ id="path30" />
58
+ <path
59
+ d="m 620.35602,190.428 c 0.122,-1.464 0.122,-2.074 0.122,-2.928 0,-6.588 -0.976,-12.688 -2.562,-17.324 -4.392,-11.956 -15.006,-19.154 -28.304,-19.154 -18.91,0 -30.5,13.664 -30.5,35.746 0,21.106 11.468,34.038 30.134,34.038 14.762,0 26.718,-8.296 30.5,-21.35 h -16.836 c -2.074,5.246 -6.832,8.296 -13.054,8.296 -4.88,0 -8.784,-2.074 -11.224,-5.734 -1.586,-2.44 -2.196,-5.368 -2.44,-11.59 z m -43.92,-11.346 c 1.098,-10.126 5.368,-15.006 12.932,-15.006 7.808,0 12.444,5.246 13.298,15.006 z"
60
+ id="path32" />
61
+ <path
62
+ d="M 626.94598,152.12 V 218 h 17.08 v -35.014 c 0,-10.004 5.002,-15.006 15.006,-15.006 1.83,0 3.05,0.122 5.368,0.488 v -17.324 c -0.976,-0.122 -1.342,-0.122 -2.074,-0.122 -7.808,0 -14.518,5.124 -18.3,14.03 V 152.12 Z"
63
+ id="path34" />
64
+ </g>
65
+ <g
66
+ aria-label=".js"
67
+ id="text16"
68
+ style="font-weight:700;font-size:48px;font-family:'DejaVu Sans Mono', monospace;letter-spacing:-3;fill:#ff4f2e">
69
+ <path
70
+ d="m 675.52344,134.39844 h 7.80468 V 143 h -7.80468 z"
71
+ id="path37" />
72
+ <path
73
+ d="m 710.82031,141.99219 q 0,5.90625 -2.13281,8.41406 -2.10938,2.53125 -7.03125,2.53125 H 694.25 v -5.27344 h 5.48437 q 2.29688,0 3.25782,-1.28906 0.96093,-1.28906 0.96093,-4.38281 v -19.96875 h -6.96093 V 116.75 h 13.82812 z m 0,-30 h -6.86719 v -8.01563 h 6.86719 z"
74
+ id="path39" />
75
+ <path
76
+ d="m 740.02344,117.57031 v 6 q -1.89844,-1.21875 -3.96094,-1.82812 -2.03906,-0.63281 -4.10156,-0.63281 -2.29688,0 -3.46875,0.67968 -1.17188,0.65625 -1.17188,1.96875 0,1.875 5.01563,3.04688 l 0.25781,0.0703 1.96875,0.46875 q 3.75,0.89062 5.48437,2.88281 1.75782,1.96875 1.75782,5.34375 0,4.05469 -2.67188,6.09375 -2.64844,2.01563 -7.99219,2.01563 -2.36718,0 -4.85156,-0.42188 -2.48437,-0.39844 -5.03906,-1.21875 v -6 q 2.27344,1.28906 4.61719,1.96875 2.36718,0.67969 4.57031,0.67969 2.41406,0 3.65625,-0.70313 1.24219,-0.70312 1.24219,-2.03906 0,-1.3125 -0.89063,-2.01562 -0.86719,-0.70313 -4.17187,-1.5 l -1.89844,-0.42188 q -3.9375,-0.89062 -5.74219,-2.78906 -1.80469,-1.89844 -1.80469,-5.08594 0,-3.79687 2.74219,-5.90625 2.74219,-2.10937 7.71094,-2.10937 2.22656,0 4.42969,0.375 2.22656,0.35156 4.3125,1.07812 z"
77
+ id="path41" />
78
+ </g>
79
+ </svg>
package/.npmignore DELETED
@@ -1,7 +0,0 @@
1
- /node-v7.7.4/
2
- /node_modules/
3
- /build/
4
- npm-debug.log
5
- test.js
6
- test-async.js
7
- test-udp.js
package/binding.gyp DELETED
@@ -1,13 +0,0 @@
1
- {
2
- "targets": [
3
- {
4
- "target_name": "milter",
5
- "sources": [ "src/envelope.cc", "src/events.cc", "src/milter.cc" ],
6
- "include_dirs": [ "/usr/include/libmilter" ],
7
- "cflags": [ "-g -Wall" ],
8
- "cflags!": [ "-O3" ],
9
- "ldflags": [ "-L/usr/lib/libmilter" ],
10
- "libraries" : [ "-lmilter" ]
11
- }
12
- ]
13
- }
package/lib/milter.js DELETED
@@ -1 +0,0 @@
1
- module.exports = require('../build/Release/milter');