1/* $NetBSD$ */ 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 */ 173typedef 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 186static 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 239VSTREAM *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 253VSTREAM *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 268static 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 337void 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 373int 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 386int 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 394int 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 404int 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