1/*++ 2/* NAME 3/* smtp_reply_footer 3 4/* SUMMARY 5/* SMTP reply footer text support 6/* SYNOPSIS 7/* #include <smtp_reply_footer.h> 8/* 9/* int smtp_reply_footer(buffer, start, template, filter, 10/* lookup, context) 11/* VSTRING *buffer; 12/* ssize_t start; 13/* char *template; 14/* const char *filter; 15/* const char *(*lookup) (const char *name, char *context); 16/* char *context; 17/* DESCRIPTION 18/* smtp_reply_footer() expands a reply template, and appends 19/* the result to an existing reply text. 20/* 21/* Arguments: 22/* .IP buffer 23/* Result buffer. This should contain a properly formatted 24/* one-line or multi-line SMTP reply, with or without the final 25/* <CR><LF>. The reply code and optional enhanced status code 26/* will be replicated in the footer text. One space character 27/* after the SMTP reply code is replaced by '-'. If the existing 28/* reply ends in <CR><LF>, the result text will also end in 29/* <CR><LF>. 30/* .IP start 31/* The beginning of the SMTP reply that the footer will be 32/* appended to. This supports applications that buffer up 33/* multiple responses in one buffer. 34/* .IP template 35/* Template text, with optional $name attributes that will be 36/* expanded. The two-character sequence "\n" is replaced by a 37/* line break followed by a copy of the original SMTP reply 38/* code and optional enhanced status code. 39/* The two-character sequence "\c" at the start of the template 40/* suppresses the line break between the reply text and the 41/* template text. 42/* .IP filter 43/* The set of characters that are allowed in attribute expansion. 44/* .IP lookup 45/* Attribute name/value lookup function. The result value must 46/* be a null for a name that is not found, otherwise a pointer 47/* to null-terminated string. 48/* .IP context 49/* Call-back context for the lookup function. 50/* SEE ALSO 51/* mac_expand(3) macro expansion 52/* DIAGNOSTICS 53/* smtp_reply_footer() returns 0 upon success, -1 if the 54/* existing reply text is malformed. 55/* 56/* Fatal errors: memory allocation problem. 57/* LICENSE 58/* .ad 59/* .fi 60/* The Secure Mailer license must be distributed with this software. 61/* AUTHOR(S) 62/* Wietse Venema 63/* IBM T.J. Watson Research 64/* P.O. Box 704 65/* Yorktown Heights, NY 10598, USA 66/*--*/ 67 68/* System library. */ 69 70#include <sys_defs.h> 71#include <string.h> 72#include <ctype.h> 73 74/* Utility library. */ 75 76#include <msg.h> 77#include <vstring.h> 78 79/* Global library. */ 80 81#include <dsn_util.h> 82#include <smtp_reply_footer.h> 83 84/* SLMs. */ 85 86#define STR vstring_str 87 88int smtp_reply_footer(VSTRING *buffer, ssize_t start, 89 char *template, 90 const char *filter, 91 MAC_EXP_LOOKUP_FN lookup, 92 char *context) 93{ 94 const char *myname = "smtp_reply_footer"; 95 char *cp; 96 char *next; 97 char *end; 98 ssize_t dsn_len; 99 int crlf_at_end = 0; 100 int reply_patch_undo_offs = -1; 101 102 /* 103 * Sanity check. 104 */ 105 if (start < 0 || start > VSTRING_LEN(buffer)) 106 msg_panic("%s: bad start: %ld", myname, (long) start); 107 if (*template == 0) 108 msg_panic("%s: empty template", myname); 109 110 /* 111 * Scan and patch the original response. If the response is not what we 112 * expect, we stop making changes. 113 */ 114 for (cp = STR(buffer) + start, end = cp + strlen(cp);;) { 115 if (!ISDIGIT(cp[0]) || !ISDIGIT(cp[1]) || !ISDIGIT(cp[2]) 116 || (cp[3] != ' ' && cp[3] != '-')) 117 return (-1); 118 cp[3] = '-'; 119 reply_patch_undo_offs = cp + 3 - STR(buffer); 120 if ((next = strstr(cp, "\r\n")) == 0) { 121 next = end; 122 break; 123 } 124 cp = next + 2; 125 if (cp == end) { 126 crlf_at_end = 1; 127 break; 128 } 129 } 130 131 /* 132 * Truncate text after the first null, and truncate the trailing CRLF. 133 */ 134 if (next < vstring_end(buffer)) 135 vstring_truncate(buffer, next - STR(buffer)); 136 137 /* 138 * Append the footer text one line at a time. Caution: before we append 139 * parts from the buffer to itself, we must extend the buffer first, 140 * otherwise we would have a dangling pointer "read" bug. 141 */ 142 dsn_len = dsn_valid(STR(buffer) + start + 4); 143 for (cp = template, end = cp + strlen(cp);;) { 144 if ((next = strstr(cp, "\\n")) != 0) { 145 *next = 0; 146 } else { 147 next = end; 148 } 149 if (cp == template && strncmp(cp, "\\c", 2) == 0) { 150 /* Handle \c at start of template. */ 151 cp += 2; 152 } else { 153 /* Append a clone of the SMTP reply code. */ 154 vstring_strcat(buffer, "\r\n"); 155 VSTRING_SPACE(buffer, 3); 156 vstring_strncat(buffer, STR(buffer) + start, 3); 157 vstring_strcat(buffer, next != end ? "-" : " "); 158 /* Append a clone of the optional enhanced status code. */ 159 if (dsn_len > 0) { 160 VSTRING_SPACE(buffer, dsn_len); 161 vstring_strncat(buffer, STR(buffer) + start + 4, (int) dsn_len); 162 vstring_strcat(buffer, " "); 163 } 164 reply_patch_undo_offs = -1; 165 } 166 /* Append one line of footer text. */ 167 mac_expand(buffer, cp, MAC_EXP_FLAG_APPEND, filter, lookup, context); 168 if (next < end) { 169 *next = '\\'; 170 cp = next + 2; 171 } else 172 break; 173 } 174 if (reply_patch_undo_offs > 0) 175 STR(buffer)[reply_patch_undo_offs] = ' '; 176 if (crlf_at_end) 177 vstring_strcat(buffer, "\r\n"); 178 return (0); 179} 180