~ubuntu-branches/ubuntu/trusty/postfix/trusty-proposed

« back to all changes in this revision

Viewing changes to src/lmtp/lmtp_chat.c

  • Committer: Bazaar Package Importer
  • Author(s): LaMont Jones
  • Date: 2005-02-27 09:33:07 UTC
  • Revision ID: james.westby@ubuntu.com-20050227093307-cn789t27ibnlh6tf
Tags: upstream-2.1.5
ImportĀ upstreamĀ versionĀ 2.1.5

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
/*++
 
2
/* NAME
 
3
/*      lmtp_chat 3
 
4
/* SUMMARY
 
5
/*      LMTP client request/response support
 
6
/* SYNOPSIS
 
7
/*      #include "lmtp.h"
 
8
/*
 
9
/*      typedef struct {
 
10
/* .in +4
 
11
/*              int code;
 
12
/*              char *str;
 
13
/*              VSTRING *buf;
 
14
/* .in -4
 
15
/*      } LMTP_RESP;
 
16
/*
 
17
/*      void    lmtp_chat_cmd(state, format, ...)
 
18
/*      LMTP_STATE *state;
 
19
/*      char    *format;
 
20
/*
 
21
/*      LMTP_RESP *lmtp_chat_resp(state)
 
22
/*      LMTP_STATE *state;
 
23
/*
 
24
/*      void    lmtp_chat_notify(state)
 
25
/*      LMTP_STATE *state;
 
26
/*
 
27
/*      void    lmtp_chat_reset(state)
 
28
/*      LMTP_STATE *state;
 
29
/* DESCRIPTION
 
30
/*      This module implements LMTP client support for request/reply
 
31
/*      conversations, and maintains a limited LMTP transaction log.
 
32
/*
 
33
/*      lmtp_chat_cmd() formats a command and sends it to an LMTP server.
 
34
/*      Optionally, the command is logged.
 
35
/*
 
36
/*      lmtp_chat_resp() read one LMTP server response. It separates the
 
37
/*      numerical status code from the text, and concatenates multi-line
 
38
/*      responses to one string, using a newline as separator.
 
39
/*      Optionally, the server response is logged.
 
40
/*
 
41
/*      lmtp_chat_notify() sends a copy of the LMTP transaction log
 
42
/*      to the postmaster for review. The postmaster notice is sent only
 
43
/*      when delivery is possible immediately. It is an error to call
 
44
/*      lmtp_chat_notify() when no LMTP transaction log exists.
 
45
/*
 
46
/*      lmtp_chat_reset() resets the transaction log. This is
 
47
/*      typically done at the beginning or end of an LMTP session,
 
48
/*      or within a session to discard non-error information.
 
49
/*      In addition, lmtp_chat_reset() resets the per-session error
 
50
/*      status bits and flags.
 
51
/* DIAGNOSTICS
 
52
/*      Fatal errors: memory allocation problem, server response exceeds
 
53
/*      configurable limit.
 
54
/*      All other exceptions are handled by long jumps (see smtp_stream(3)).
 
55
/* SEE ALSO
 
56
/*      smtp_stream(3) LMTP session I/O support
 
57
/*      msg(3) generic logging interface
 
58
/* LICENSE
 
59
/* .ad
 
60
/* .fi
 
61
/*      The Secure Mailer license must be distributed with this software.
 
62
/* AUTHOR(S)
 
63
/*      Wietse Venema
 
64
/*      IBM T.J. Watson Research
 
65
/*      P.O. Box 704
 
66
/*      Yorktown Heights, NY 10598, USA
 
67
/*
 
68
/*      Alterations for LMTP by:
 
69
/*      Philip A. Prindeville
 
70
/*      Mirapoint, Inc.
 
71
/*      USA.
 
72
/*
 
73
/*      Additional work on LMTP by:
 
74
/*      Amos Gouaux
 
75
/*      University of Texas at Dallas
 
76
/*      P.O. Box 830688, MC34
 
77
/*      Richardson, TX 75083, USA
 
78
/*--*/
 
79
 
 
80
/* System library. */
 
81
 
 
82
#include <sys_defs.h>
 
83
#include <stdlib.h>                     /* 44BSD stdarg.h uses abort() */
 
84
#include <stdarg.h>
 
85
#include <ctype.h>
 
86
#include <stdlib.h>
 
87
#include <setjmp.h>
 
88
 
 
89
/* Utility library. */
 
90
 
 
91
#include <msg.h>
 
92
#include <vstring.h>
 
93
#include <vstream.h>
 
94
#include <argv.h>
 
95
#include <stringops.h>
 
96
#include <line_wrap.h>
 
97
#include <mymalloc.h>
 
98
 
 
99
/* Global library. */
 
100
 
 
101
#include <recipient_list.h>
 
102
#include <deliver_request.h>
 
103
#include <smtp_stream.h>
 
104
#include <mail_params.h>
 
105
#include <mail_addr.h>
 
106
#include <post_mail.h>
 
107
#include <mail_error.h>
 
108
 
 
109
/* Application-specific. */
 
110
 
 
111
#include "lmtp.h"
 
112
 
 
113
#define STR(x)  ((char *) vstring_str(x))
 
114
#define LEN     VSTRING_LEN
 
115
 
 
116
/* lmtp_chat_reset - reset LMTP transaction log */
 
117
 
 
118
void    lmtp_chat_reset(LMTP_STATE *state)
 
119
{
 
120
    if (state->history) {
 
121
        argv_free(state->history);
 
122
        state->history = 0;
 
123
    }
 
124
    /* What's status without history? */
 
125
    state->status = 0;
 
126
    state->error_mask = 0;
 
127
}
 
128
 
 
129
/* lmtp_chat_append - append record to LMTP transaction log */
 
130
 
 
131
static void lmtp_chat_append(LMTP_STATE *state, char *direction, char *data)
 
132
{
 
133
    char   *line;
 
134
 
 
135
    if (state->history == 0)
 
136
        state->history = argv_alloc(10);
 
137
    line = concatenate(direction, data, (char *) 0);
 
138
    argv_add(state->history, line, (char *) 0);
 
139
    myfree(line);
 
140
}
 
141
 
 
142
/* lmtp_chat_cmd - send an LMTP command */
 
143
 
 
144
void    lmtp_chat_cmd(LMTP_STATE *state, char *fmt,...)
 
145
{
 
146
    LMTP_SESSION *session = state->session;
 
147
    va_list ap;
 
148
 
 
149
    /*
 
150
     * Format the command, and update the transaction log.
 
151
     */
 
152
    va_start(ap, fmt);
 
153
    vstring_vsprintf(state->buffer, fmt, ap);
 
154
    va_end(ap);
 
155
    lmtp_chat_append(state, "Out: ", STR(state->buffer));
 
156
 
 
157
    /*
 
158
     * Optionally log the command first, so we can see in the log what the
 
159
     * program is trying to do.
 
160
     */
 
161
    if (msg_verbose)
 
162
        msg_info("> %s: %s", session->namaddr, STR(state->buffer));
 
163
 
 
164
    /*
 
165
     * Send the command to the LMTP server.
 
166
     */
 
167
    smtp_fputs(STR(state->buffer), LEN(state->buffer), session->stream);
 
168
}
 
169
 
 
170
/* lmtp_chat_resp - read and process LMTP server response */
 
171
 
 
172
LMTP_RESP *lmtp_chat_resp(LMTP_STATE *state)
 
173
{
 
174
    LMTP_SESSION *session = state->session;
 
175
    static LMTP_RESP rdata;
 
176
    char   *cp;
 
177
    int     last_char;
 
178
 
 
179
    /*
 
180
     * Initialize the response data buffer.
 
181
     */
 
182
    if (rdata.buf == 0)
 
183
        rdata.buf = vstring_alloc(100);
 
184
 
 
185
    /*
 
186
     * Censor out non-printable characters in server responses. Concatenate
 
187
     * multi-line server responses. Separate the status code from the text.
 
188
     * Leave further parsing up to the application.
 
189
     */
 
190
    VSTRING_RESET(rdata.buf);
 
191
    for (;;) {
 
192
        last_char = smtp_get(state->buffer, session->stream, var_line_limit);
 
193
        printable(STR(state->buffer), '?');
 
194
        if (last_char != '\n')
 
195
            msg_warn("%s: response longer than %d: %.30s...",
 
196
                     session->namaddr, var_line_limit, STR(state->buffer));
 
197
        if (msg_verbose)
 
198
            msg_info("< %s: %s", session->namaddr, STR(state->buffer));
 
199
 
 
200
        /*
 
201
         * Defend against a denial of service attack by limiting the amount
 
202
         * of multi-line text that we are willing to store.
 
203
         */
 
204
        if (LEN(rdata.buf) < var_line_limit) {
 
205
            if (VSTRING_LEN(rdata.buf))
 
206
                VSTRING_ADDCH(rdata.buf, '\n');
 
207
            vstring_strcat(rdata.buf, STR(state->buffer));
 
208
            lmtp_chat_append(state, "In:  ", STR(state->buffer));
 
209
        }
 
210
 
 
211
        /*
 
212
         * Parse into code and text. Ignore unrecognized garbage. This means
 
213
         * that any character except space (or end of line) will have the
 
214
         * same effect as the '-' line continuation character.
 
215
         */
 
216
        for (cp = STR(state->buffer); *cp && ISDIGIT(*cp); cp++)
 
217
             /* void */ ;
 
218
        if (cp - STR(state->buffer) == 3) {
 
219
            if (*cp == '-')
 
220
                continue;
 
221
            if (*cp == ' ' || *cp == 0)
 
222
                break;
 
223
        }
 
224
        state->error_mask |= MAIL_ERROR_PROTOCOL;
 
225
    }
 
226
    rdata.code = atoi(STR(state->buffer));
 
227
    VSTRING_TERMINATE(rdata.buf);
 
228
    rdata.str = STR(rdata.buf);
 
229
    return (&rdata);
 
230
}
 
231
 
 
232
/* print_line - line_wrap callback */
 
233
 
 
234
static void print_line(const char *str, int len, int indent, char *context)
 
235
{
 
236
    VSTREAM *notice = (VSTREAM *) context;
 
237
 
 
238
    post_mail_fprintf(notice, " %*s%.*s", indent, "", len, str);
 
239
}
 
240
 
 
241
/* lmtp_chat_notify - notify postmaster */
 
242
 
 
243
void    lmtp_chat_notify(LMTP_STATE *state)
 
244
{
 
245
    char   *myname = "lmtp_chat_notify";
 
246
    LMTP_SESSION *session = state->session;
 
247
    VSTREAM *notice;
 
248
    char  **cpp;
 
249
 
 
250
    /*
 
251
     * Sanity checks.
 
252
     */
 
253
    if (state->history == 0)
 
254
        msg_panic("%s: no conversation history", myname);
 
255
    if (msg_verbose)
 
256
        msg_info("%s: notify postmaster", myname);
 
257
 
 
258
    /*
 
259
     * Construct a message for the postmaster, explaining what this is all
 
260
     * about. This is junk mail: don't send it when the mail posting service
 
261
     * is unavailable, and use the double bounce sender address, to prevent
 
262
     * mail bounce wars. Always prepend one space to message content that we
 
263
     * generate from untrusted data.
 
264
     */
 
265
#define NULL_TRACE_FLAGS        0
 
266
#define LENGTH  78
 
267
#define INDENT  4
 
268
 
 
269
    notice = post_mail_fopen_nowait(mail_addr_double_bounce(),
 
270
                                    var_error_rcpt,
 
271
                                    CLEANUP_FLAG_MASK_INTERNAL,
 
272
                                    NULL_TRACE_FLAGS);
 
273
    if (notice == 0) {
 
274
        msg_warn("postmaster notify: %m");
 
275
        return;
 
276
    }
 
277
    post_mail_fprintf(notice, "From: %s (Mail Delivery System)",
 
278
                      mail_addr_mail_daemon());
 
279
    post_mail_fprintf(notice, "To: %s (Postmaster)", var_error_rcpt);
 
280
    post_mail_fprintf(notice, "Subject: %s LMTP client: errors from %s",
 
281
                      var_mail_name, session->namaddr);
 
282
    post_mail_fputs(notice, "");
 
283
    post_mail_fprintf(notice, "Unexpected response from %s.", session->namaddr);
 
284
    post_mail_fputs(notice, "");
 
285
    post_mail_fputs(notice, "Transcript of session follows.");
 
286
    post_mail_fputs(notice, "");
 
287
    argv_terminate(state->history);
 
288
    for (cpp = state->history->argv; *cpp; cpp++)
 
289
        line_wrap(printable(*cpp, '?'), LENGTH, INDENT, print_line,
 
290
                  (char *) notice);
 
291
    (void) post_mail_fclose(notice);
 
292
}