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