Expand PMF_FN_* macros.
[netbsd-mini2440.git] / external / ibm-public / postfix / dist / src / global / post_mail.c
blob8738f1a2acab0ac8c1e560f932f96ef6e7d7d9c0
1 /* $NetBSD$ */
3 /*++
4 /* NAME
5 /* post_mail 3
6 /* SUMMARY
7 /* convenient mail posting interface
8 /* SYNOPSIS
9 /* #include <post_mail.h>
11 /* VSTREAM *post_mail_fopen(sender, recipient, filter_class, trace_flags,
12 /* queue_id)
13 /* const char *sender;
14 /* const char *recipient;
15 /* int filter_class;
16 /* int trace_flags;
17 /* VSTRING *queue_id;
19 /* VSTREAM *post_mail_fopen_nowait(sender, recipient,
20 /* filter_class, trace_flags, queue_id)
21 /* const char *sender;
22 /* const char *recipient;
23 /* int filter_class;
24 /* int trace_flags;
25 /* VSTRING *queue_id;
27 /* void post_mail_fopen_async(sender, recipient,
28 /* filter_class, trace_flags,
29 /* queue_id, notify, context)
30 /* const char *sender;
31 /* const char *recipient;
32 /* int filter_class;
33 /* int trace_flags;
34 /* VSTRING *queue_id;
35 /* void (*notify)(VSTREAM *stream, char *context);
36 /* char *context;
38 /* int post_mail_fprintf(stream, format, ...)
39 /* VSTREAM *stream;
40 /* const char *format;
42 /* int post_mail_fputs(stream, str)
43 /* VSTREAM *stream;
44 /* const char *str;
46 /* int post_mail_buffer(stream, buf, len)
47 /* VSTREAM *stream;
48 /* const char *buffer;
50 /* int POST_MAIL_BUFFER(stream, buf)
51 /* VSTREAM *stream;
52 /* VSTRING *buffer;
54 /* int post_mail_fclose(stream)
55 /* VSTREAM *STREAM;
56 /* DESCRIPTION
57 /* This module provides a convenient interface for the most
58 /* common case of sending one message to one recipient. It
59 /* allows the application to concentrate on message content,
60 /* without having to worry about queue file structure details.
62 /* post_mail_fopen() opens a connection to the cleanup service
63 /* and waits until the service is available, does some option
64 /* negotiation, generates message envelope records, and generates
65 /* Received: and Date: message headers. The result is a stream
66 /* handle that can be used for sending message records.
68 /* post_mail_fopen_nowait() tries to contact the cleanup service
69 /* only once, and does not wait until the cleanup service is
70 /* available. Otherwise it is identical to post_mail_fopen().
72 /* post_mail_fopen_async() contacts the cleanup service and
73 /* invokes the caller-specified notify routine, with the
74 /* open stream and the caller-specified context when the
75 /* service responds, or with a null stream and the caller-specified
76 /* context when the request could not be completed. It is the
77 /* responsability of the application to close an open stream.
79 /* post_mail_fprintf() formats message content (header or body)
80 /* and sends it to the cleanup service.
82 /* post_mail_fputs() sends pre-formatted content (header or body)
83 /* to the cleanup service.
85 /* post_mail_buffer() sends a pre-formatted buffer to the
86 /* cleanup service.
88 /* POST_MAIL_BUFFER() is a wrapper for post_mail_buffer() that
89 /* evaluates its buffer argument more than once.
91 /* post_mail_fclose() completes the posting of a message.
93 /* Arguments:
94 /* .IP sender
95 /* The sender envelope address. It is up to the application
96 /* to produce From: headers.
97 /* .IP recipient
98 /* The recipient envelope address. It is up to the application
99 /* to produce To: headers.
100 /* .IP filter_class
101 /* The internal mail filtering class, as defined in
102 /* \fB<int_filt.h>\fR. Depending on the setting of the
103 /* internal_mail_filter_classes parameter the message will or
104 /* won't be subject to content inspection.
105 /* .IP trace_flags
106 /* Message tracing flags as specified in \fB<deliver_request.h>\fR.
107 /* .IP queue_id
108 /* Null pointer, or pointer to buffer that receives the queue
109 /* ID of the new message.
110 /* .IP stream
111 /* A stream opened by mail_post_fopen().
112 /* .IP notify
113 /* Application call-back routine.
114 /* .IP context
115 /* Application call-back context.
116 /* DIAGNOSTICS
117 /* post_mail_fopen_nowait() returns a null pointer when the
118 /* cleanup service is not available immediately.
120 /* post_mail_fopen_async() returns a null pointer when the
121 /* attempt to contact the cleanup service fails immediately.
123 /* post_mail_fprintf(), post_mail_fputs() post_mail_fclose(),
124 /* and post_mail_buffer() return the binary OR of the error
125 /* status codes defined in \fI<cleanup_user.h>\fR.
127 /* Fatal errors: cleanup initial handshake errors. This means
128 /* the client and server speak incompatible protocols.
129 /* SEE ALSO
130 /* cleanup_user(3h) cleanup options and results
131 /* cleanup_strerror(3) translate results to text
132 /* cleanup(8) cleanup service
133 /* LICENSE
134 /* .ad
135 /* .fi
136 /* The Secure Mailer license must be distributed with this software.
137 /* AUTHOR(S)
138 /* Wietse Venema
139 /* IBM T.J. Watson Research
140 /* P.O. Box 704
141 /* Yorktown Heights, NY 10598, USA
142 /*--*/
144 /* System library. */
146 #include <sys_defs.h>
147 #include <sys/time.h>
148 #include <stdlib.h> /* 44BSD stdarg.h uses abort() */
149 #include <stdarg.h>
150 #include <string.h>
152 /* Utility library. */
154 #include <msg.h>
155 #include <vstream.h>
156 #include <vstring.h>
157 #include <mymalloc.h>
158 #include <events.h>
160 /* Global library. */
162 #include <mail_params.h>
163 #include <record.h>
164 #include <rec_type.h>
165 #include <mail_proto.h>
166 #include <cleanup_user.h>
167 #include <post_mail.h>
168 #include <mail_date.h>
171 * Call-back state for asynchronous connection requests.
173 typedef struct {
174 char *sender;
175 char *recipient;
176 int filter_class;
177 int trace_flags;
178 POST_MAIL_NOTIFY notify;
179 void *context;
180 VSTREAM *stream;
181 VSTRING *queue_id;
182 } POST_MAIL_STATE;
184 /* post_mail_init - initial negotiations */
186 static void post_mail_init(VSTREAM *stream, const char *sender,
187 const char *recipient,
188 int filter_class, int trace_flags,
189 VSTRING *queue_id)
191 VSTRING *id = queue_id ? queue_id : vstring_alloc(100);
192 struct timeval now;
193 const char *date;
194 int cleanup_flags =
195 int_filt_flags(filter_class) | CLEANUP_FLAG_MASK_INTERNAL;
197 GETTIMEOFDAY(&now);
198 date = mail_date(now.tv_sec);
201 * Negotiate with the cleanup service. Give up if we can't agree.
203 if (attr_scan(stream, ATTR_FLAG_STRICT,
204 ATTR_TYPE_STR, MAIL_ATTR_QUEUEID, id,
205 ATTR_TYPE_END) != 1
206 || attr_print(stream, ATTR_FLAG_NONE,
207 ATTR_TYPE_INT, MAIL_ATTR_FLAGS, cleanup_flags,
208 ATTR_TYPE_END) != 0)
209 msg_fatal("unable to contact the %s service", var_cleanup_service);
212 * Generate a minimal envelope section. The cleanup service will add a
213 * size record.
215 rec_fprintf(stream, REC_TYPE_TIME, REC_TYPE_TIME_FORMAT,
216 REC_TYPE_TIME_ARG(now));
217 rec_fprintf(stream, REC_TYPE_ATTR, "%s=%s",
218 MAIL_ATTR_LOG_ORIGIN, MAIL_ATTR_ORG_LOCAL);
219 rec_fprintf(stream, REC_TYPE_ATTR, "%s=%d",
220 MAIL_ATTR_TRACE_FLAGS, trace_flags);
221 rec_fputs(stream, REC_TYPE_FROM, sender);
222 rec_fputs(stream, REC_TYPE_RCPT, recipient);
223 rec_fputs(stream, REC_TYPE_MESG, "");
226 * Do the Received: and Date: header lines. This allows us to shave a few
227 * cycles by using the expensive date conversion result for both.
229 post_mail_fprintf(stream, "Received: by %s (%s)",
230 var_myhostname, var_mail_name);
231 post_mail_fprintf(stream, "\tid %s; %s", vstring_str(id), date);
232 post_mail_fprintf(stream, "Date: %s", date);
233 if (queue_id == 0)
234 vstring_free(id);
237 /* post_mail_fopen - prepare for posting a message */
239 VSTREAM *post_mail_fopen(const char *sender, const char *recipient,
240 int filter_class, int trace_flags,
241 VSTRING *queue_id)
243 VSTREAM *stream;
245 stream = mail_connect_wait(MAIL_CLASS_PUBLIC, var_cleanup_service);
246 post_mail_init(stream, sender, recipient, filter_class, trace_flags,
247 queue_id);
248 return (stream);
251 /* post_mail_fopen_nowait - prepare for posting a message */
253 VSTREAM *post_mail_fopen_nowait(const char *sender, const char *recipient,
254 int filter_class, int trace_flags,
255 VSTRING *queue_id)
257 VSTREAM *stream;
259 if ((stream = mail_connect(MAIL_CLASS_PUBLIC, var_cleanup_service,
260 BLOCKING)) != 0)
261 post_mail_init(stream, sender, recipient, filter_class, trace_flags,
262 queue_id);
263 return (stream);
266 /* post_mail_open_event - handle asynchronous connection events */
268 static void post_mail_open_event(int event, char *context)
270 POST_MAIL_STATE *state = (POST_MAIL_STATE *) context;
271 const char *myname = "post_mail_open_event";
273 switch (event) {
276 * Initial server reply. Stop the watchdog timer, disable further
277 * read events that end up calling this function, and notify the
278 * requestor.
280 case EVENT_READ:
281 if (msg_verbose)
282 msg_info("%s: read event", myname);
283 event_cancel_timer(post_mail_open_event, context);
284 event_disable_readwrite(vstream_fileno(state->stream));
285 non_blocking(vstream_fileno(state->stream), BLOCKING);
286 post_mail_init(state->stream, state->sender,
287 state->recipient, state->filter_class,
288 state->trace_flags, state->queue_id);
289 myfree(state->sender);
290 myfree(state->recipient);
291 state->notify(state->stream, state->context);
292 myfree((char *) state);
293 return;
296 * No connection or no initial reply within a conservative time
297 * limit. The system is broken and we give up.
299 case EVENT_TIME:
300 if (state->stream) {
301 msg_warn("timeout connecting to service: %s", var_cleanup_service);
302 event_disable_readwrite(vstream_fileno(state->stream));
303 vstream_fclose(state->stream);
304 } else {
305 msg_warn("connect to service: %s: %m", var_cleanup_service);
307 myfree(state->sender);
308 myfree(state->recipient);
309 state->notify((VSTREAM *) 0, state->context);
310 myfree((char *) state);
311 return;
314 * Some exception.
316 case EVENT_XCPT:
317 msg_warn("error connecting to service: %s", var_cleanup_service);
318 event_cancel_timer(post_mail_open_event, context);
319 event_disable_readwrite(vstream_fileno(state->stream));
320 vstream_fclose(state->stream);
321 myfree(state->sender);
322 myfree(state->recipient);
323 state->notify((VSTREAM *) 0, state->context);
324 myfree((char *) state);
325 return;
328 * Broken software or hardware.
330 default:
331 msg_panic("%s: unknown event type %d", myname, event);
335 /* post_mail_fopen_async - prepare for posting a message */
337 void post_mail_fopen_async(const char *sender, const char *recipient,
338 int filter_class, int trace_flags,
339 VSTRING *queue_id,
340 void (*notify) (VSTREAM *, void *),
341 void *context)
343 VSTREAM *stream;
344 POST_MAIL_STATE *state;
346 stream = mail_connect(MAIL_CLASS_PUBLIC, var_cleanup_service, NON_BLOCKING);
347 state = (POST_MAIL_STATE *) mymalloc(sizeof(*state));
348 state->sender = mystrdup(sender);
349 state->recipient = mystrdup(recipient);
350 state->filter_class = filter_class;
351 state->trace_flags = trace_flags;
352 state->notify = notify;
353 state->context = context;
354 state->stream = stream;
355 state->queue_id = queue_id;
358 * To keep interfaces as simple as possible we report all errors via the
359 * same interface as all successes.
361 if (stream != 0) {
362 event_enable_read(vstream_fileno(stream), post_mail_open_event,
363 (void *) state);
364 event_request_timer(post_mail_open_event, (void *) state,
365 var_daemon_timeout);
366 } else {
367 event_request_timer(post_mail_open_event, (void *) state, 0);
371 /* post_mail_fprintf - format and send message content */
373 int post_mail_fprintf(VSTREAM *cleanup, const char *format,...)
375 int status;
376 va_list ap;
378 va_start(ap, format);
379 status = rec_vfprintf(cleanup, REC_TYPE_NORM, format, ap);
380 va_end(ap);
381 return (status != REC_TYPE_NORM ? CLEANUP_STAT_WRITE : 0);
384 /* post_mail_buffer - send pre-formatted buffer */
386 int post_mail_buffer(VSTREAM *cleanup, const char *buf, int len)
388 return (rec_put(cleanup, REC_TYPE_NORM, buf, len) != REC_TYPE_NORM ?
389 CLEANUP_STAT_WRITE : 0);
392 /* post_mail_fputs - send pre-formatted message content */
394 int post_mail_fputs(VSTREAM *cleanup, const char *str)
396 ssize_t len = str ? strlen(str) : 0;
398 return (rec_put(cleanup, REC_TYPE_NORM, str, len) != REC_TYPE_NORM ?
399 CLEANUP_STAT_WRITE : 0);
402 /* post_mail_fclose - finish posting of message */
404 int post_mail_fclose(VSTREAM *cleanup)
406 int status = 0;
409 * Send the message end marker only when there were no errors.
411 if (vstream_ferror(cleanup) != 0) {
412 status = CLEANUP_STAT_WRITE;
413 } else {
414 rec_fputs(cleanup, REC_TYPE_XTRA, "");
415 rec_fputs(cleanup, REC_TYPE_END, "");
416 if (vstream_fflush(cleanup)
417 || attr_scan(cleanup, ATTR_FLAG_MISSING,
418 ATTR_TYPE_INT, MAIL_ATTR_STATUS, &status,
419 ATTR_TYPE_END) != 1)
420 status = CLEANUP_STAT_WRITE;
422 (void) vstream_fclose(cleanup);
423 return (status);