xref: /netbsd-src/external/ibm-public/postfix/dist/src/global/post_mail.c (revision a24efa7dea9f1f56c3bdb15a927d3516792ace1c)
1 /*	$NetBSD: post_mail.c,v 1.1.1.1 2009/06/23 10:08:47 tron Exp $	*/
2 
3 /*++
4 /* NAME
5 /*	post_mail 3
6 /* SUMMARY
7 /*	convenient mail posting interface
8 /* SYNOPSIS
9 /*	#include <post_mail.h>
10 /*
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;
18 /*
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;
26 /*
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;
37 /*
38 /*	int	post_mail_fprintf(stream, format, ...)
39 /*	VSTREAM	*stream;
40 /*	const char *format;
41 /*
42 /*	int	post_mail_fputs(stream, str)
43 /*	VSTREAM	*stream;
44 /*	const char *str;
45 /*
46 /*	int	post_mail_buffer(stream, buf, len)
47 /*	VSTREAM	*stream;
48 /*	const char *buffer;
49 /*
50 /*	int	POST_MAIL_BUFFER(stream, buf)
51 /*	VSTREAM	*stream;
52 /*	VSTRING	*buffer;
53 /*
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.
61 /*
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.
67 /*
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().
71 /*
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.
78 /*
79 /*	post_mail_fprintf() formats message content (header or body)
80 /*	and sends it to the cleanup service.
81 /*
82 /*	post_mail_fputs() sends pre-formatted content (header or body)
83 /*	to the cleanup service.
84 /*
85 /*	post_mail_buffer() sends a pre-formatted buffer to the
86 /*	cleanup service.
87 /*
88 /*	POST_MAIL_BUFFER() is a wrapper for post_mail_buffer() that
89 /*	evaluates its buffer argument more than once.
90 /*
91 /*	post_mail_fclose() completes the posting of a message.
92 /*
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.
119 /*
120 /*	post_mail_fopen_async() returns a null pointer when the
121 /*	attempt to contact the cleanup service fails immediately.
122 /*
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.
126 /*
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 /*--*/
143 
144 /* System library. */
145 
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>
151 
152 /* Utility library. */
153 
154 #include <msg.h>
155 #include <vstream.h>
156 #include <vstring.h>
157 #include <mymalloc.h>
158 #include <events.h>
159 
160 /* Global library. */
161 
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>
169 
170  /*
171   * Call-back state for asynchronous connection requests.
172   */
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;
183 
184 /* post_mail_init - initial negotiations */
185 
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)
190 {
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;
196 
197     GETTIMEOFDAY(&now);
198     date = mail_date(now.tv_sec);
199 
200     /*
201      * Negotiate with the cleanup service. Give up if we can't agree.
202      */
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);
210 
211     /*
212      * Generate a minimal envelope section. The cleanup service will add a
213      * size record.
214      */
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, "");
224 
225     /*
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.
228      */
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);
235 }
236 
237 /* post_mail_fopen - prepare for posting a message */
238 
239 VSTREAM *post_mail_fopen(const char *sender, const char *recipient,
240 			         int filter_class, int trace_flags,
241 			         VSTRING *queue_id)
242 {
243     VSTREAM *stream;
244 
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);
249 }
250 
251 /* post_mail_fopen_nowait - prepare for posting a message */
252 
253 VSTREAM *post_mail_fopen_nowait(const char *sender, const char *recipient,
254 				        int filter_class, int trace_flags,
255 				        VSTRING *queue_id)
256 {
257     VSTREAM *stream;
258 
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);
264 }
265 
266 /* post_mail_open_event - handle asynchronous connection events */
267 
268 static void post_mail_open_event(int event, char *context)
269 {
270     POST_MAIL_STATE *state = (POST_MAIL_STATE *) context;
271     const char *myname = "post_mail_open_event";
272 
273     switch (event) {
274 
275 	/*
276 	 * Initial server reply. Stop the watchdog timer, disable further
277 	 * read events that end up calling this function, and notify the
278 	 * requestor.
279 	 */
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;
294 
295 	/*
296 	 * No connection or no initial reply within a conservative time
297 	 * limit. The system is broken and we give up.
298 	 */
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);
306 	}
307 	myfree(state->sender);
308 	myfree(state->recipient);
309 	state->notify((VSTREAM *) 0, state->context);
310 	myfree((char *) state);
311 	return;
312 
313 	/*
314 	 * Some exception.
315 	 */
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;
326 
327 	/*
328 	 * Broken software or hardware.
329 	 */
330     default:
331 	msg_panic("%s: unknown event type %d", myname, event);
332     }
333 }
334 
335 /* post_mail_fopen_async - prepare for posting a message */
336 
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)
342 {
343     VSTREAM *stream;
344     POST_MAIL_STATE *state;
345 
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;
356 
357     /*
358      * To keep interfaces as simple as possible we report all errors via the
359      * same interface as all successes.
360      */
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);
368     }
369 }
370 
371 /* post_mail_fprintf - format and send message content */
372 
373 int     post_mail_fprintf(VSTREAM *cleanup, const char *format,...)
374 {
375     int     status;
376     va_list ap;
377 
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);
382 }
383 
384 /* post_mail_buffer - send pre-formatted buffer */
385 
386 int     post_mail_buffer(VSTREAM *cleanup, const char *buf, int len)
387 {
388     return (rec_put(cleanup, REC_TYPE_NORM, buf, len) != REC_TYPE_NORM ?
389 	    CLEANUP_STAT_WRITE : 0);
390 }
391 
392 /* post_mail_fputs - send pre-formatted message content */
393 
394 int     post_mail_fputs(VSTREAM *cleanup, const char *str)
395 {
396     ssize_t len = str ? strlen(str) : 0;
397 
398     return (rec_put(cleanup, REC_TYPE_NORM, str, len) != REC_TYPE_NORM ?
399 	    CLEANUP_STAT_WRITE : 0);
400 }
401 
402 /* post_mail_fclose - finish posting of message */
403 
404 int     post_mail_fclose(VSTREAM *cleanup)
405 {
406     int     status = 0;
407 
408     /*
409      * Send the message end marker only when there were no errors.
410      */
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;
421     }
422     (void) vstream_fclose(cleanup);
423     return (status);
424 }
425