Fossil SCM

Incremental check-in of initial code for client-side SMTP. Does not work.

drh 2018-06-28 15:23 UTC smtp
Commit 20006a866c8dbb59ba596f1ea31e62803847bb63cb2dddbe14dd0810efa4f0f6
2 files changed +4 -2 +223
--- src/http_socket.c
+++ src/http_socket.c
@@ -79,12 +79,14 @@
7979
}
8080
8181
/*
8282
** Return the current socket error message
8383
*/
84
-const char *socket_errmsg(void){
85
- return socketErrMsg;
84
+char *socket_errmsg(void){
85
+ char *zResult = socketErrMsg;
86
+ socketErrMsg = 0;
87
+ return zResult;
8688
}
8789
8890
/*
8991
** Call this routine once before any other use of the socket interface.
9092
** This routine does initial configuration of the socket module.
9193
--- src/http_socket.c
+++ src/http_socket.c
@@ -79,12 +79,14 @@
79 }
80
81 /*
82 ** Return the current socket error message
83 */
84 const char *socket_errmsg(void){
85 return socketErrMsg;
 
 
86 }
87
88 /*
89 ** Call this routine once before any other use of the socket interface.
90 ** This routine does initial configuration of the socket module.
91
--- src/http_socket.c
+++ src/http_socket.c
@@ -79,12 +79,14 @@
79 }
80
81 /*
82 ** Return the current socket error message
83 */
84 char *socket_errmsg(void){
85 char *zResult = socketErrMsg;
86 socketErrMsg = 0;
87 return zResult;
88 }
89
90 /*
91 ** Call this routine once before any other use of the socket interface.
92 ** This routine does initial configuration of the socket module.
93
+223
--- src/smtp.c
+++ src/smtp.c
@@ -104,5 +104,228 @@
104104
char *z = smtp_mx_host(g.argv[i]);
105105
fossil_print("%s: %s\n", g.argv[i], z);
106106
fossil_free(z);
107107
}
108108
}
109
+
110
+#if INTERFACE
111
+/*
112
+** Information about a single SMTP connection.
113
+*/
114
+struct SmtpSession {
115
+ const char *zFrom; /* Domain from which we are sending */
116
+ const char *zDest; /* Domain that will receive the email */
117
+ char *zHostname; /* Hostname of SMTP server for zDest */
118
+ u32 smtpFlags; /* Flags changing the operation */
119
+ FILE *logFile; /* Write session transcript to this log file */
120
+ Blob *pTranscript; /* Record session transcript here */
121
+ const char *zLabel; /* Either "CS" or "SC" */
122
+ char *zErr; /* Error message */
123
+};
124
+
125
+/* Allowed values for SmtpSession.smtpFlags */
126
+#define SMTP_TRACE_STDOUT 0x00001 /* Debugging info to console */
127
+#define SMTP_TRACE_FILE 0x00002 /* Debugging info to logFile */
128
+#define SMTP_TRACE_BLOB 0x00004 /* Record transcript */
129
+
130
+#endif
131
+
132
+/*
133
+** Shutdown an SmtpSession
134
+*/
135
+void smtp_session_free(SmtpSession *pSession){
136
+ socket_close();
137
+ fossil_free(pSession->zHostname);
138
+ fossil_free(pSession->zErr);
139
+ fossil_free(pSession);
140
+}
141
+
142
+/*
143
+** Allocate a new SmtpSession object.
144
+**
145
+** Both zFrom and zDest must be specified for a client side SMTP connection.
146
+** For a server-side, specify only zFrom.
147
+*/
148
+SmtpSession *smtp_session_new(
149
+ const char *zFrom, /* Domain name of our end. */
150
+ const char *zDest, /* Domain of the other end. */
151
+ u32 smtpFlags, /* Flags */
152
+ ... /* Arguments depending on the flags */
153
+){
154
+ SmtpSession *p;
155
+ va_list ap;
156
+ UrlData url;
157
+
158
+ p = fossil_malloc( sizeof(*p) );
159
+ memset(p, 0, sizeof(*p));
160
+ p->zFrom = zFrom;
161
+ p->zDest = zDest;
162
+ p->zLabel = zDest==0 ? "CS" : "SC";
163
+ p->smtpFlags = smtpFlags;
164
+ va_start(ap, smtpFlags);
165
+ if( smtpFlags & SMTP_TRACE_FILE ){
166
+ p->logFile = va_arg(ap, FILE*);
167
+ }else if( smtpFlags & SMTP_TRACE_BLOB ){
168
+ p->pTranscript = va_arg(ap, Blob*);
169
+ }
170
+ va_end(ap);
171
+ p->zHostname = smtp_mx_host(zDest);
172
+ if( p->zHostname==0 ){
173
+ p->zErr = mprintf("cannot locate SMTP server for \"%s\"", zDest);
174
+ return p;
175
+ }
176
+ memset(&url, 0, sizeof(url));
177
+ url.name = p->zHostname;
178
+ url.port = 25;
179
+ socket_global_init();
180
+ if( socket_open(&url) ){
181
+ p->zErr = socket_errmsg();
182
+ socket_close();
183
+ }
184
+ return p;
185
+}
186
+
187
+/*
188
+** Send a single line of output the SMTP client to the server.
189
+*/
190
+static void smtp_send_line(SmtpSession *p, const char *zFormat, ...){
191
+ Blob b = empty_blob;
192
+ va_list ap;
193
+ char *z;
194
+ int n;
195
+ va_start(ap, zFormat);
196
+ blob_vappendf(&b, zFormat, ap);
197
+ va_end(ap);
198
+ z = blob_buffer(&b);
199
+ n = blob_size(&b);
200
+ assert( n>=2 );
201
+ assert( z[n-1]=='\n' );
202
+ assert( z[n-2]=='\r' );
203
+ if( p->smtpFlags & SMTP_TRACE_STDOUT ){
204
+ fossil_print("%c: %.*s\n", p->zLabel[1], n-2, z);
205
+ }
206
+ if( p->smtpFlags & SMTP_TRACE_FILE ){
207
+ fprintf(p->logFile, "%c: %.*s\n", p->zLabel[1], n-2, z);
208
+ }
209
+ if( p->smtpFlags & SMTP_TRACE_BLOB ){
210
+ blob_appendf(p->pTranscript, "%c: %.*s\n", p->zLabel[1], n-2, z);
211
+ }
212
+ socket_send(0, z, n);
213
+ blob_zero(&b);
214
+}
215
+
216
+/*
217
+** Read a line of input received from the SMTP server. Append
218
+** the received line onto the end of the blob.
219
+*/
220
+static void smtp_recv_line(SmtpSession *p, Blob *in){
221
+ int n = blob_size(in);
222
+ int iStart = n;
223
+ char *z;
224
+ do{
225
+ size_t got;
226
+ blob_resize(in, n+1000);
227
+ z = blob_buffer(in);
228
+ got = socket_receive(0, z+n, 1000);
229
+ in->nUsed += got;
230
+ n += got;
231
+ }while( n<1 || z[n-1]!='\n' );
232
+ z = blob_buffer(in) + iStart;
233
+ n = blob_size(in) - iStart - 1;
234
+ if( n && z[n-1]=='\r' ) n--;
235
+ if( p->smtpFlags & SMTP_TRACE_STDOUT ){
236
+ fossil_print("%c: %.*s\n", p->zLabel[0], n, z);
237
+ }
238
+ if( p->smtpFlags & SMTP_TRACE_FILE ){
239
+ fprintf(p->logFile, "%c: %.*s\n", p->zLabel[0], n, z);
240
+ }
241
+ if( p->smtpFlags & SMTP_TRACE_BLOB ){
242
+ blob_appendf(p->pTranscript, "%c: %.*s\n", p->zLabel[0], n-2, z);
243
+ }
244
+}
245
+
246
+/*
247
+** Capture a single-line server reply.
248
+*/
249
+static void smtp_get_reply_from_server(
250
+ SmtpSession *p, /* The SMTP connection */
251
+ Blob *in, /* Buffer used to hold the reply */
252
+ int *piCode, /* The return code */
253
+ int *pbMore, /* True if the reply is not complete */
254
+ char **pzArg /* Argument */
255
+){
256
+ blob_truncate(in, 0);
257
+ smtp_recv_line(p, in);
258
+ *piCode = atoi(blob_str(in));
259
+ *pbMore = blob_size(in)>=4 && blob_str(in)[3]=='-';
260
+ *pzArg = blob_size(in)>=4 ? blob_str(in)+4 : "";
261
+}
262
+
263
+/*
264
+** Have the client send a QUIT message.
265
+*/
266
+int smtp_client_quit(SmtpSession *p){
267
+ Blob in = BLOB_INITIALIZER;
268
+ int iCode = 0;
269
+ int bMore = 0;
270
+ char *zArg = 0;
271
+ smtp_send_line(p, "QUIT\r\n");
272
+ do{
273
+ smtp_get_reply_from_server(p, &in, &iCode, &bMore, &zArg);
274
+ }while( bMore );
275
+ socket_close();
276
+ return 0;
277
+}
278
+
279
+/*
280
+** Begin a client SMTP session. Wait for the initial 220 then send
281
+** the EHLO and wait for a 250.
282
+**
283
+** Return 0 on success and non-zero for a failure.
284
+*/
285
+int smtp_client_startup(SmtpSession *p){
286
+ Blob in = BLOB_INITIALIZER;
287
+ int iCode = 0;
288
+ int bMore = 0;
289
+ char *zArg = 0;
290
+ do{
291
+ smtp_get_reply_from_server(p, &in, &iCode, &bMore, &zArg);
292
+ }while( bMore );
293
+ if( iCode!=220 ){
294
+ smtp_client_quit(p);
295
+ return 1;
296
+ }
297
+ smtp_send_line(p, "EHLO %s\r\n", p->zFrom);
298
+ do{
299
+ smtp_get_reply_from_server(p, &in, &iCode, &bMore, &zArg);
300
+ }while( bMore );
301
+ if( iCode!=250 ){
302
+ smtp_client_quit(p);
303
+ return 1;
304
+ }
305
+ return 0;
306
+}
307
+
308
+/*
309
+** COMMAND: test-smtp-probe
310
+**
311
+** Usage: %fossil test-smtp-probe DOMAIN ME
312
+**
313
+** Interact with the SMTP server for DOMAIN by setting up a connection
314
+** and then immediately shutting it back down. Log all interaction
315
+** on the console. Use ME as the domain name of the sender.
316
+*/
317
+void test_smtp_probe(void){
318
+ char *zHost;
319
+ SmtpSession *p;
320
+ int rc;
321
+ if( g.argc!=4 ) usage("DOMAIN ME");
322
+ zHost = smtp_mx_host(g.argv[2]);
323
+ if( zHost==0 ){
324
+ fossil_fatal("cannot resolve the MX for \"%s\"", g.argv[2]);
325
+ }
326
+ fossil_print("Contacting host \"%s\"\n", zHost);
327
+ p = smtp_session_new(g.argv[3], zHost, SMTP_TRACE_STDOUT);
328
+ rc = smtp_client_startup(p);
329
+ if( !rc ) smtp_client_quit(p);
330
+ smtp_session_free(p);
331
+}
109332
--- src/smtp.c
+++ src/smtp.c
@@ -104,5 +104,228 @@
104 char *z = smtp_mx_host(g.argv[i]);
105 fossil_print("%s: %s\n", g.argv[i], z);
106 fossil_free(z);
107 }
108 }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
109
--- src/smtp.c
+++ src/smtp.c
@@ -104,5 +104,228 @@
104 char *z = smtp_mx_host(g.argv[i]);
105 fossil_print("%s: %s\n", g.argv[i], z);
106 fossil_free(z);
107 }
108 }
109
110 #if INTERFACE
111 /*
112 ** Information about a single SMTP connection.
113 */
114 struct SmtpSession {
115 const char *zFrom; /* Domain from which we are sending */
116 const char *zDest; /* Domain that will receive the email */
117 char *zHostname; /* Hostname of SMTP server for zDest */
118 u32 smtpFlags; /* Flags changing the operation */
119 FILE *logFile; /* Write session transcript to this log file */
120 Blob *pTranscript; /* Record session transcript here */
121 const char *zLabel; /* Either "CS" or "SC" */
122 char *zErr; /* Error message */
123 };
124
125 /* Allowed values for SmtpSession.smtpFlags */
126 #define SMTP_TRACE_STDOUT 0x00001 /* Debugging info to console */
127 #define SMTP_TRACE_FILE 0x00002 /* Debugging info to logFile */
128 #define SMTP_TRACE_BLOB 0x00004 /* Record transcript */
129
130 #endif
131
132 /*
133 ** Shutdown an SmtpSession
134 */
135 void smtp_session_free(SmtpSession *pSession){
136 socket_close();
137 fossil_free(pSession->zHostname);
138 fossil_free(pSession->zErr);
139 fossil_free(pSession);
140 }
141
142 /*
143 ** Allocate a new SmtpSession object.
144 **
145 ** Both zFrom and zDest must be specified for a client side SMTP connection.
146 ** For a server-side, specify only zFrom.
147 */
148 SmtpSession *smtp_session_new(
149 const char *zFrom, /* Domain name of our end. */
150 const char *zDest, /* Domain of the other end. */
151 u32 smtpFlags, /* Flags */
152 ... /* Arguments depending on the flags */
153 ){
154 SmtpSession *p;
155 va_list ap;
156 UrlData url;
157
158 p = fossil_malloc( sizeof(*p) );
159 memset(p, 0, sizeof(*p));
160 p->zFrom = zFrom;
161 p->zDest = zDest;
162 p->zLabel = zDest==0 ? "CS" : "SC";
163 p->smtpFlags = smtpFlags;
164 va_start(ap, smtpFlags);
165 if( smtpFlags & SMTP_TRACE_FILE ){
166 p->logFile = va_arg(ap, FILE*);
167 }else if( smtpFlags & SMTP_TRACE_BLOB ){
168 p->pTranscript = va_arg(ap, Blob*);
169 }
170 va_end(ap);
171 p->zHostname = smtp_mx_host(zDest);
172 if( p->zHostname==0 ){
173 p->zErr = mprintf("cannot locate SMTP server for \"%s\"", zDest);
174 return p;
175 }
176 memset(&url, 0, sizeof(url));
177 url.name = p->zHostname;
178 url.port = 25;
179 socket_global_init();
180 if( socket_open(&url) ){
181 p->zErr = socket_errmsg();
182 socket_close();
183 }
184 return p;
185 }
186
187 /*
188 ** Send a single line of output the SMTP client to the server.
189 */
190 static void smtp_send_line(SmtpSession *p, const char *zFormat, ...){
191 Blob b = empty_blob;
192 va_list ap;
193 char *z;
194 int n;
195 va_start(ap, zFormat);
196 blob_vappendf(&b, zFormat, ap);
197 va_end(ap);
198 z = blob_buffer(&b);
199 n = blob_size(&b);
200 assert( n>=2 );
201 assert( z[n-1]=='\n' );
202 assert( z[n-2]=='\r' );
203 if( p->smtpFlags & SMTP_TRACE_STDOUT ){
204 fossil_print("%c: %.*s\n", p->zLabel[1], n-2, z);
205 }
206 if( p->smtpFlags & SMTP_TRACE_FILE ){
207 fprintf(p->logFile, "%c: %.*s\n", p->zLabel[1], n-2, z);
208 }
209 if( p->smtpFlags & SMTP_TRACE_BLOB ){
210 blob_appendf(p->pTranscript, "%c: %.*s\n", p->zLabel[1], n-2, z);
211 }
212 socket_send(0, z, n);
213 blob_zero(&b);
214 }
215
216 /*
217 ** Read a line of input received from the SMTP server. Append
218 ** the received line onto the end of the blob.
219 */
220 static void smtp_recv_line(SmtpSession *p, Blob *in){
221 int n = blob_size(in);
222 int iStart = n;
223 char *z;
224 do{
225 size_t got;
226 blob_resize(in, n+1000);
227 z = blob_buffer(in);
228 got = socket_receive(0, z+n, 1000);
229 in->nUsed += got;
230 n += got;
231 }while( n<1 || z[n-1]!='\n' );
232 z = blob_buffer(in) + iStart;
233 n = blob_size(in) - iStart - 1;
234 if( n && z[n-1]=='\r' ) n--;
235 if( p->smtpFlags & SMTP_TRACE_STDOUT ){
236 fossil_print("%c: %.*s\n", p->zLabel[0], n, z);
237 }
238 if( p->smtpFlags & SMTP_TRACE_FILE ){
239 fprintf(p->logFile, "%c: %.*s\n", p->zLabel[0], n, z);
240 }
241 if( p->smtpFlags & SMTP_TRACE_BLOB ){
242 blob_appendf(p->pTranscript, "%c: %.*s\n", p->zLabel[0], n-2, z);
243 }
244 }
245
246 /*
247 ** Capture a single-line server reply.
248 */
249 static void smtp_get_reply_from_server(
250 SmtpSession *p, /* The SMTP connection */
251 Blob *in, /* Buffer used to hold the reply */
252 int *piCode, /* The return code */
253 int *pbMore, /* True if the reply is not complete */
254 char **pzArg /* Argument */
255 ){
256 blob_truncate(in, 0);
257 smtp_recv_line(p, in);
258 *piCode = atoi(blob_str(in));
259 *pbMore = blob_size(in)>=4 && blob_str(in)[3]=='-';
260 *pzArg = blob_size(in)>=4 ? blob_str(in)+4 : "";
261 }
262
263 /*
264 ** Have the client send a QUIT message.
265 */
266 int smtp_client_quit(SmtpSession *p){
267 Blob in = BLOB_INITIALIZER;
268 int iCode = 0;
269 int bMore = 0;
270 char *zArg = 0;
271 smtp_send_line(p, "QUIT\r\n");
272 do{
273 smtp_get_reply_from_server(p, &in, &iCode, &bMore, &zArg);
274 }while( bMore );
275 socket_close();
276 return 0;
277 }
278
279 /*
280 ** Begin a client SMTP session. Wait for the initial 220 then send
281 ** the EHLO and wait for a 250.
282 **
283 ** Return 0 on success and non-zero for a failure.
284 */
285 int smtp_client_startup(SmtpSession *p){
286 Blob in = BLOB_INITIALIZER;
287 int iCode = 0;
288 int bMore = 0;
289 char *zArg = 0;
290 do{
291 smtp_get_reply_from_server(p, &in, &iCode, &bMore, &zArg);
292 }while( bMore );
293 if( iCode!=220 ){
294 smtp_client_quit(p);
295 return 1;
296 }
297 smtp_send_line(p, "EHLO %s\r\n", p->zFrom);
298 do{
299 smtp_get_reply_from_server(p, &in, &iCode, &bMore, &zArg);
300 }while( bMore );
301 if( iCode!=250 ){
302 smtp_client_quit(p);
303 return 1;
304 }
305 return 0;
306 }
307
308 /*
309 ** COMMAND: test-smtp-probe
310 **
311 ** Usage: %fossil test-smtp-probe DOMAIN ME
312 **
313 ** Interact with the SMTP server for DOMAIN by setting up a connection
314 ** and then immediately shutting it back down. Log all interaction
315 ** on the console. Use ME as the domain name of the sender.
316 */
317 void test_smtp_probe(void){
318 char *zHost;
319 SmtpSession *p;
320 int rc;
321 if( g.argc!=4 ) usage("DOMAIN ME");
322 zHost = smtp_mx_host(g.argv[2]);
323 if( zHost==0 ){
324 fossil_fatal("cannot resolve the MX for \"%s\"", g.argv[2]);
325 }
326 fossil_print("Contacting host \"%s\"\n", zHost);
327 p = smtp_session_new(g.argv[3], zHost, SMTP_TRACE_STDOUT);
328 rc = smtp_client_startup(p);
329 if( !rc ) smtp_client_quit(p);
330 smtp_session_free(p);
331 }
332

Keyboard Shortcuts

Open search /
Next entry (timeline) j
Previous entry (timeline) k
Open focused entry Enter
Show this help ?
Toggle theme Top nav button