Patrick Welche <prlw1@cam.ac.uk>
[netbsd-mini2440.git] / external / ibm-public / postfix / dist / src / global / smtp_stream.c
blobdec84b5e88bbfd0282ec5a67b6ac01735d2f7f19
1 /* $NetBSD$ */
3 /*++
4 /* NAME
5 /* smtp_stream 3
6 /* SUMMARY
7 /* smtp stream I/O support
8 /* SYNOPSIS
9 /* #include <smtp_stream.h>
11 /* void smtp_timeout_setup(stream, timeout)
12 /* VSTREAM *stream;
13 /* int timeout;
15 /* void smtp_printf(stream, format, ...)
16 /* VSTREAM *stream;
17 /* const char *format;
19 /* void smtp_flush(stream)
20 /* VSTREAM *stream;
22 /* int smtp_fgetc(stream)
23 /* VSTREAM *stream;
25 /* int smtp_get(vp, stream, maxlen)
26 /* VSTRING *vp;
27 /* VSTREAM *stream;
28 /* ssize_t maxlen;
30 /* void smtp_fputs(str, len, stream)
31 /* const char *str;
32 /* ssize_t len;
33 /* VSTREAM *stream;
35 /* void smtp_fwrite(str, len, stream)
36 /* const char *str;
37 /* ssize_t len;
38 /* VSTREAM *stream;
40 /* void smtp_fputc(ch, stream)
41 /* int ch;
42 /* VSTREAM *stream;
44 /* void smtp_vprintf(stream, format, ap)
45 /* VSTREAM *stream;
46 /* char *format;
47 /* va_list ap;
48 /* DESCRIPTION
49 /* This module reads and writes text records delimited by CR LF,
50 /* with error detection: timeouts or unexpected end-of-file.
51 /* A trailing CR LF is added upon writing and removed upon reading.
53 /* smtp_timeout_setup() arranges for a time limit on the smtp read
54 /* and write operations described below.
55 /* This routine alters the behavior of streams as follows:
56 /* .IP \(bu
57 /* The read/write timeout is set to the specified value.
58 /* .IP \f(bu
59 /* The stream is configured to use double buffering.
60 /* .IP \f(bu
61 /* The stream is configured to enable exception handling.
62 /* .PP
63 /* smtp_printf() formats its arguments and writes the result to
64 /* the named stream, followed by a CR LF pair. The stream is NOT flushed.
65 /* Long lines of text are not broken.
67 /* smtp_flush() flushes the named stream.
69 /* smtp_fgetc() reads one character from the named stream.
71 /* smtp_get() reads the named stream up to and including
72 /* the next LF character and strips the trailing CR LF. The
73 /* \fImaxlen\fR argument limits the length of a line of text,
74 /* and protects the program against running out of memory.
75 /* Specify a zero bound to turn off bounds checking.
76 /* The result is the last character read, or VSTREAM_EOF.
78 /* smtp_fputs() writes its string argument to the named stream.
79 /* Long strings are not broken. Each string is followed by a
80 /* CR LF pair. The stream is not flushed.
82 /* smtp_fwrite() writes its string argument to the named stream.
83 /* Long strings are not broken. No CR LF is appended. The stream
84 /* is not flushed.
86 /* smtp_fputc() writes one character to the named stream.
87 /* The stream is not flushed.
89 /* smtp_vprintf() is the machine underneath smtp_printf().
90 /* DIAGNOSTICS
91 /* .fi
92 /* .ad
93 /* In case of error, a vstream_longjmp() call is performed to the
94 /* context specified with vstream_setjmp().
95 /* Error codes passed along with vstream_longjmp() are:
96 /* .IP SMTP_ERR_EOF
97 /* An I/O error happened, or the peer has disconnected unexpectedly.
98 /* .IP SMTP_ERR_TIME
99 /* The time limit specified to smtp_timeout_setup() was exceeded.
100 /* .PP
101 /* Additional error codes that may be used by applications:
102 /* .IP SMTP_ERR_QUIET
103 /* Perform silent cleanup; the error was already reported by
104 /* the application.
105 /* This error is never generated by the smtp_stream(3) module, but
106 /* is defined for application-specific use.
107 /* .IP SMTP_ERR_NONE
108 /* A non-error code that makes setjmp()/longjmp() convenient
109 /* to use.
110 /* BUGS
111 /* The timeout deadline affects all I/O on the named stream, not
112 /* just the I/O done on behalf of this module.
114 /* The timeout deadline overwrites any previously set up state on
115 /* the named stream.
116 /* LICENSE
117 /* .ad
118 /* .fi
119 /* The Secure Mailer license must be distributed with this software.
120 /* AUTHOR(S)
121 /* Wietse Venema
122 /* IBM T.J. Watson Research
123 /* P.O. Box 704
124 /* Yorktown Heights, NY 10598, USA
125 /*--*/
127 /* System library. */
129 #include <sys_defs.h>
130 #include <sys/socket.h>
131 #include <sys/time.h>
132 #include <setjmp.h>
133 #include <stdlib.h>
134 #include <stdarg.h>
135 #include <unistd.h>
136 #include <string.h> /* FD_ZERO() needs bzero() prototype */
137 #include <errno.h>
139 /* Utility library. */
141 #include <vstring.h>
142 #include <vstream.h>
143 #include <vstring_vstream.h>
144 #include <msg.h>
145 #include <iostuff.h>
147 /* Application-specific. */
149 #include "smtp_stream.h"
151 /* smtp_timeout_reset - reset per-stream timeout flag */
153 static void smtp_timeout_reset(VSTREAM *stream)
155 vstream_clearerr(stream);
158 /* smtp_timeout_detect - test the per-stream timeout flag */
160 static void smtp_timeout_detect(VSTREAM *stream)
162 if (vstream_ftimeout(stream))
163 vstream_longjmp(stream, SMTP_ERR_TIME);
166 /* smtp_timeout_setup - configure timeout trap */
168 void smtp_timeout_setup(VSTREAM *stream, int maxtime)
170 vstream_control(stream,
171 VSTREAM_CTL_DOUBLE,
172 VSTREAM_CTL_TIMEOUT, maxtime,
173 VSTREAM_CTL_EXCEPT,
174 VSTREAM_CTL_END);
177 /* smtp_flush - flush stream */
179 void smtp_flush(VSTREAM *stream)
181 int err;
184 * Do the I/O, protected against timeout.
186 smtp_timeout_reset(stream);
187 err = vstream_fflush(stream);
188 smtp_timeout_detect(stream);
191 * See if there was a problem.
193 if (err != 0) {
194 if (msg_verbose)
195 msg_info("smtp_flush: EOF");
196 vstream_longjmp(stream, SMTP_ERR_EOF);
200 /* smtp_vprintf - write one line to SMTP peer */
202 void smtp_vprintf(VSTREAM *stream, const char *fmt, va_list ap)
204 int err;
207 * Do the I/O, protected against timeout.
209 smtp_timeout_reset(stream);
210 vstream_vfprintf(stream, fmt, ap);
211 vstream_fputs("\r\n", stream);
212 err = vstream_ferror(stream);
213 smtp_timeout_detect(stream);
216 * See if there was a problem.
218 if (err != 0) {
219 if (msg_verbose)
220 msg_info("smtp_vprintf: EOF");
221 vstream_longjmp(stream, SMTP_ERR_EOF);
225 /* smtp_printf - write one line to SMTP peer */
227 void smtp_printf(VSTREAM *stream, const char *fmt,...)
229 va_list ap;
231 va_start(ap, fmt);
232 smtp_vprintf(stream, fmt, ap);
233 va_end(ap);
236 /* smtp_fgetc - read one character from SMTP peer */
238 int smtp_fgetc(VSTREAM *stream)
240 int ch;
243 * Do the I/O, protected against timeout.
245 smtp_timeout_reset(stream);
246 ch = VSTREAM_GETC(stream);
247 smtp_timeout_detect(stream);
250 * See if there was a problem.
252 if (vstream_feof(stream) || vstream_ferror(stream)) {
253 if (msg_verbose)
254 msg_info("smtp_fgetc: EOF");
255 vstream_longjmp(stream, SMTP_ERR_EOF);
257 return (ch);
260 /* smtp_get - read one line from SMTP peer */
262 int smtp_get(VSTRING *vp, VSTREAM *stream, ssize_t bound)
264 int last_char;
265 int next_char;
268 * It's painful to do I/O with records that may span multiple buffers.
269 * Allow for partial long lines (we will read the remainder later) and
270 * allow for lines ending in bare LF. The idea is to be liberal in what
271 * we accept, strict in what we send.
273 * XXX 2821: Section 4.1.1.4 says that an SMTP server must not recognize
274 * bare LF as record terminator.
276 smtp_timeout_reset(stream);
277 last_char = (bound == 0 ? vstring_get(vp, stream) :
278 vstring_get_bound(vp, stream, bound));
280 switch (last_char) {
283 * Do some repair in the rare case that we stopped reading in the
284 * middle of the CRLF record terminator.
286 case '\r':
287 if ((next_char = VSTREAM_GETC(stream)) == '\n') {
288 VSTRING_ADDCH(vp, '\n');
289 last_char = '\n';
290 /* FALLTRHOUGH */
291 } else {
292 if (next_char != VSTREAM_EOF)
293 vstream_ungetc(stream, next_char);
294 break;
298 * Strip off the record terminator: either CRLF or just bare LF.
300 * XXX RFC 2821 disallows sending bare CR everywhere. We remove bare CR
301 * if received before CRLF, and leave it alone otherwise.
303 case '\n':
304 vstring_truncate(vp, VSTRING_LEN(vp) - 1);
305 while (VSTRING_LEN(vp) > 0 && vstring_end(vp)[-1] == '\r')
306 vstring_truncate(vp, VSTRING_LEN(vp) - 1);
307 VSTRING_TERMINATE(vp);
310 * Partial line: just read the remainder later. If we ran into EOF,
311 * the next test will deal with it.
313 default:
314 break;
316 smtp_timeout_detect(stream);
319 * EOF is bad, whether or not it happens in the middle of a record. Don't
320 * allow data that was truncated because of EOF.
322 if (vstream_feof(stream) || vstream_ferror(stream)) {
323 if (msg_verbose)
324 msg_info("smtp_get: EOF");
325 vstream_longjmp(stream, SMTP_ERR_EOF);
327 return (last_char);
330 /* smtp_fputs - write one line to SMTP peer */
332 void smtp_fputs(const char *cp, ssize_t todo, VSTREAM *stream)
334 ssize_t err;
336 if (todo < 0)
337 msg_panic("smtp_fputs: negative todo %ld", (long) todo);
340 * Do the I/O, protected against timeout.
342 smtp_timeout_reset(stream);
343 err = (vstream_fwrite(stream, cp, todo) != todo
344 || vstream_fputs("\r\n", stream) == VSTREAM_EOF);
345 smtp_timeout_detect(stream);
348 * See if there was a problem.
350 if (err != 0) {
351 if (msg_verbose)
352 msg_info("smtp_fputs: EOF");
353 vstream_longjmp(stream, SMTP_ERR_EOF);
357 /* smtp_fwrite - write one string to SMTP peer */
359 void smtp_fwrite(const char *cp, ssize_t todo, VSTREAM *stream)
361 ssize_t err;
363 if (todo < 0)
364 msg_panic("smtp_fwrite: negative todo %ld", (long) todo);
367 * Do the I/O, protected against timeout.
369 smtp_timeout_reset(stream);
370 err = (vstream_fwrite(stream, cp, todo) != todo);
371 smtp_timeout_detect(stream);
374 * See if there was a problem.
376 if (err != 0) {
377 if (msg_verbose)
378 msg_info("smtp_fwrite: EOF");
379 vstream_longjmp(stream, SMTP_ERR_EOF);
383 /* smtp_fputc - write to SMTP peer */
385 void smtp_fputc(int ch, VSTREAM *stream)
387 int stat;
390 * Do the I/O, protected against timeout.
392 smtp_timeout_reset(stream);
393 stat = VSTREAM_PUTC(ch, stream);
394 smtp_timeout_detect(stream);
397 * See if there was a problem.
399 if (stat == VSTREAM_EOF) {
400 if (msg_verbose)
401 msg_info("smtp_fputc: EOF");
402 vstream_longjmp(stream, SMTP_ERR_EOF);