Fossil

Check-in [e6c27d3dab]
Login

Many hyperlinks are disabled.
Use anonymous login to enable hyperlinks.

Overview
Comment:Attempt to provide improved error message outputs for failures while trying to send notification via relay to an MTA.
Downloads: Tarball | ZIP archive
Timelines: family | ancestors | descendants | both | trunk
Files: files | file ages | folders
SHA3-256: e6c27d3dabf0c3631e09f8212002c7d06b7bed16e7893fd7e5da7a7bdb797fa2
User & Date: drh 2025-04-15 23:34:25.954
Context
2025-04-16
00:48
Automatic retry on an SMTP relay failure. check-in: 2b96941c4c user: drh tags: trunk
2025-04-15
23:34
Attempt to provide improved error message outputs for failures while trying to send notification via relay to an MTA. check-in: e6c27d3dab user: drh tags: trunk
15:56
Two new settings "show-repolist-desc" and "show-repolist-lg" control whether or not the description and the login-group are shown on the repolist, respectively. These settings must be global to be effective. They default to "off". check-in: 1760fa2bf4 user: drh tags: trunk
Changes
Unified Diff Ignore Whitespace Patch
Changes to src/alerts.c.
1031
1032
1033
1034
1035
1036
1037

1038
1039
1040







1041
1042
1043
1044
1045
1046
1047
  }else if( p->zDir ){
    char *zFile = file_time_tempname(p->zDir, ".email");
    blob_write_to_file(&all, zFile);
    fossil_free(zFile);
  }else if( p->pSmtp ){
    char **azTo = 0;
    int nTo = 0;

    email_header_to(pHdr, &nTo, &azTo);
    if( nTo>0 ){
      smtp_send_msg(p->pSmtp, p->zFrom, nTo, (const char**)azTo,blob_str(&all));







      email_header_to_free(nTo, azTo);
    }
  }else if( strcmp(p->zDest, "stdout")==0 ){
    char **azTo = 0;
    int nTo = 0;
    int i;
    email_header_to(pHdr, &nTo, &azTo);







>

|
|
>
>
>
>
>
>
>







1031
1032
1033
1034
1035
1036
1037
1038
1039
1040
1041
1042
1043
1044
1045
1046
1047
1048
1049
1050
1051
1052
1053
1054
1055
  }else if( p->zDir ){
    char *zFile = file_time_tempname(p->zDir, ".email");
    blob_write_to_file(&all, zFile);
    fossil_free(zFile);
  }else if( p->pSmtp ){
    char **azTo = 0;
    int nTo = 0;
    SmtpSession *pSmtp = p->pSmtp;
    email_header_to(pHdr, &nTo, &azTo);
    if( nTo>0 && !pSmtp->bFatal ){
      smtp_send_msg(pSmtp,p->zFrom,nTo,(const char**)azTo,blob_str(&all));
      if( pSmtp->zErr && !pSmtp->bFatal ){
        smtp_send_msg(pSmtp,p->zFrom,nTo,(const char**)azTo,blob_str(&all));
      }
      if( pSmtp->zErr ){
        fossil_errorlog("SMTP: (%s) %s", pSmtp->bFatal ? "fatal" : "retry",
                        pSmtp->zErr);
      }
      email_header_to_free(nTo, azTo);
    }
  }else if( strcmp(p->zDest, "stdout")==0 ){
    char **azTo = 0;
    int nTo = 0;
    int i;
    email_header_to(pHdr, &nTo, &azTo);
Changes to src/printf.c.
1075
1076
1077
1078
1079
1080
1081

1082
1083
1084
1085
1086
1087
1088
void fossil_errorlog(const char *zFormat, ...){
  struct tm *pNow;
  time_t now;
  FILE *out;
  const char *z;
  int i;
  int bDetail = 0;

  va_list ap;
  static const char *const azEnv[] = { "HTTP_HOST", "HTTP_REFERER",
      "HTTP_USER_AGENT",
      "PATH_INFO", "QUERY_STRING", "REMOTE_ADDR", "REQUEST_METHOD",
      "REQUEST_URI", "SCRIPT_NAME" };
  if( g.zErrlog==0 ) return;
  if( g.zErrlog[0]=='-' && g.zErrlog[1]==0 ){







>







1075
1076
1077
1078
1079
1080
1081
1082
1083
1084
1085
1086
1087
1088
1089
void fossil_errorlog(const char *zFormat, ...){
  struct tm *pNow;
  time_t now;
  FILE *out;
  const char *z;
  int i;
  int bDetail = 0;
  int bBrief = 0;
  va_list ap;
  static const char *const azEnv[] = { "HTTP_HOST", "HTTP_REFERER",
      "HTTP_USER_AGENT",
      "PATH_INFO", "QUERY_STRING", "REMOTE_ADDR", "REQUEST_METHOD",
      "REQUEST_URI", "SCRIPT_NAME" };
  if( g.zErrlog==0 ) return;
  if( g.zErrlog[0]=='-' && g.zErrlog[1]==0 ){
1096
1097
1098
1099
1100
1101
1102


1103
1104
1105
1106
1107


1108
1109
1110
1111
1112
1113
1114
1115
  fprintf(out, "------------- %04d-%02d-%02d %02d:%02d:%02d UTC ------------\n",
          pNow->tm_year+1900, pNow->tm_mon+1, pNow->tm_mday,
          pNow->tm_hour, pNow->tm_min, pNow->tm_sec);
  va_start(ap, zFormat);
  if( zFormat[0]=='X' ){
    bDetail = 1;
    zFormat++;


  }
  vfprintf(out, zFormat, ap);
  fprintf(out, " (pid %d)\n", (int)getpid());
  va_end(ap);
  if( g.zPhase!=0 ) fprintf(out, "while in %s\n", g.zPhase);


  if( bDetail ){
    cgi_print_all(1,3,out);
  }else{
    for(i=0; i<count(azEnv); i++){
      char *p;
      if( (p = fossil_getenv(azEnv[i]))!=0 && p[0]!=0 ){
        fprintf(out, "%s=%s\n", azEnv[i], p);
        fossil_path_free(p);







>
>





>
>
|







1097
1098
1099
1100
1101
1102
1103
1104
1105
1106
1107
1108
1109
1110
1111
1112
1113
1114
1115
1116
1117
1118
1119
1120
  fprintf(out, "------------- %04d-%02d-%02d %02d:%02d:%02d UTC ------------\n",
          pNow->tm_year+1900, pNow->tm_mon+1, pNow->tm_mday,
          pNow->tm_hour, pNow->tm_min, pNow->tm_sec);
  va_start(ap, zFormat);
  if( zFormat[0]=='X' ){
    bDetail = 1;
    zFormat++;
  }else if( strncmp(zFormat,"SMTP:",5)==0 ){
    bBrief = 1;
  }
  vfprintf(out, zFormat, ap);
  fprintf(out, " (pid %d)\n", (int)getpid());
  va_end(ap);
  if( g.zPhase!=0 ) fprintf(out, "while in %s\n", g.zPhase);
  if( bBrief ){
    /* Say nothing more */
  }else if( bDetail ){
    cgi_print_all(1,3,out);
  }else{
    for(i=0; i<count(azEnv); i++){
      char *p;
      if( (p = fossil_getenv(azEnv[i]))!=0 && p[0]!=0 ){
        fprintf(out, "%s=%s\n", azEnv[i], p);
        fossil_path_free(p);
Changes to src/smtp.c.
155
156
157
158
159
160
161

162
163
164
165
166
167
168
  const char *zFrom;        /* Domain from which we are sending */
  const char *zDest;        /* Domain that will receive the email */
  char *zHostname;          /* Hostname of SMTP server for zDest */
  u32 smtpFlags;            /* Flags changing the operation */
  FILE *logFile;            /* Write session transcript to this log file */
  Blob *pTranscript;        /* Record session transcript here */
  int atEof;                /* True after connection closes */

  char *zErr;               /* Error message */
  Blob inbuf;               /* Input buffer */
};

/* Allowed values for SmtpSession.smtpFlags */
#define SMTP_TRACE_STDOUT   0x00001     /* Debugging info to console */
#define SMTP_TRACE_FILE     0x00002     /* Debugging info to logFile */







>







155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
  const char *zFrom;        /* Domain from which we are sending */
  const char *zDest;        /* Domain that will receive the email */
  char *zHostname;          /* Hostname of SMTP server for zDest */
  u32 smtpFlags;            /* Flags changing the operation */
  FILE *logFile;            /* Write session transcript to this log file */
  Blob *pTranscript;        /* Record session transcript here */
  int atEof;                /* True after connection closes */
  int bFatal;               /* Error is fatal.  Do not retry */
  char *zErr;               /* Error message */
  Blob inbuf;               /* Input buffer */
};

/* Allowed values for SmtpSession.smtpFlags */
#define SMTP_TRACE_STDOUT   0x00001     /* Debugging info to console */
#define SMTP_TRACE_FILE     0x00002     /* Debugging info to logFile */
178
179
180
181
182
183
184




















185
186
187
188
189
190
191
void smtp_session_free(SmtpSession *pSession){
  socket_close();
  blob_reset(&pSession->inbuf);
  fossil_free(pSession->zHostname);
  fossil_free(pSession->zErr);
  fossil_free(pSession);
}





















/*
** Allocate a new SmtpSession object.
**
** Both zFrom and zDest must be specified.  smtpFlags may not contain
** either SMTP_TRACE_FILE or SMTP_TRACE_BLOB as those settings must be
** added by a subsequent call to smtp_session_config().







>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>







179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
void smtp_session_free(SmtpSession *pSession){
  socket_close();
  blob_reset(&pSession->inbuf);
  fossil_free(pSession->zHostname);
  fossil_free(pSession->zErr);
  fossil_free(pSession);
}

/*
** Set an error message on the SmtpSession
*/
static void smtp_set_error(
  SmtpSession *p,             /* The SMTP context */
  int bFatal,                 /* Fatal error.  Reset and retry is pointless */
  const char *zFormat,        /* Error message. */
  ...
){
  if( bFatal ) p->bFatal = 1;
  if( p->zErr==0 ){
    va_list ap;
    va_start(ap, zFormat);
    p->zErr = vmprintf(zFormat, ap);
    va_end(ap);
  }
  socket_close();
  p->atEof = 1;
}

/*
** Allocate a new SmtpSession object.
**
** Both zFrom and zDest must be specified.  smtpFlags may not contain
** either SMTP_TRACE_FILE or SMTP_TRACE_BLOB as those settings must be
** added by a subsequent call to smtp_session_config().
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
      p->zHostname[i] = 0;
      url.port = atoi(&p->zHostname[i+1]);
    }
  }else{
    p->zHostname = smtp_mx_host(zDest);
  }
  if( p->zHostname==0 ){
    p->atEof = 1;
    p->zErr = mprintf("cannot locate SMTP server for \"%s\"", zDest);
    return p;
  }
  url.name = p->zHostname;
  socket_global_init();
  if( socket_open(&url) ){
    p->atEof = 1;
    p->zErr = socket_errmsg();
    socket_close();
  }
  return p;
}

/*
** Configure debugging options on SmtpSession.  Add all bits in
** smtpFlags to the settings.  The following bits can be added:







<
|





<
|
<







241
242
243
244
245
246
247

248
249
250
251
252
253

254

255
256
257
258
259
260
261
      p->zHostname[i] = 0;
      url.port = atoi(&p->zHostname[i+1]);
    }
  }else{
    p->zHostname = smtp_mx_host(zDest);
  }
  if( p->zHostname==0 ){

    smtp_set_error(p, 1, "cannot locate SMTP server for \"%s\"", zDest);
    return p;
  }
  url.name = p->zHostname;
  socket_global_init();
  if( socket_open(&url) ){

    smtp_set_error(p, 1, "can't open socket: %z", socket_errmsg());

  }
  return p;
}

/*
** Configure debugging options on SmtpSession.  Add all bits in
** smtpFlags to the settings.  The following bits can be added:
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
        z[n] = 0;
        if( n>0 && z[n-1]=='\n' ) break;
        if( got==1000 ) continue;
      }
      nDelay++;
      if( nDelay>100 ){
        blob_init(in, 0, 0);
        p->zErr = mprintf("timeout");
        socket_close();
        p->atEof = 1;
        return;
      }else{
        sqlite3_sleep(100);
      }
    }while( n<1 || z[n-1]!='\n' );
    blob_truncate(&p->inbuf, n);
    blob_line(&p->inbuf, in);







|
<
<







336
337
338
339
340
341
342
343


344
345
346
347
348
349
350
        z[n] = 0;
        if( n>0 && z[n-1]=='\n' ) break;
        if( got==1000 ) continue;
      }
      nDelay++;
      if( nDelay>100 ){
        blob_init(in, 0, 0);
        smtp_set_error(p, 1, "client times out waiting on server response");


        return;
      }else{
        sqlite3_sleep(100);
      }
    }while( n<1 || z[n-1]!='\n' );
    blob_truncate(&p->inbuf, n);
    blob_line(&p->inbuf, in);
401
402
403
404
405
406
407
408


409
410
411
412

413
414
415
416
417
418
419
420

421
422
423
424
425
426
427
** Return 0 on success and non-zero for a failure.
*/
int smtp_client_startup(SmtpSession *p){
  Blob in = BLOB_INITIALIZER;
  int iCode = 0;
  int bMore = 0;
  char *zArg = 0;
  if( p==0 || p->atEof ) return 1;


  do{
    smtp_get_reply_from_server(p, &in, &iCode, &bMore, &zArg);
  }while( bMore );
  if( iCode!=220 ){

    smtp_client_quit(p);
    return 1;
  }
  smtp_send_line(p, "EHLO %s\r\n", p->zFrom);
  do{
    smtp_get_reply_from_server(p, &in, &iCode, &bMore, &zArg);
  }while( bMore );
  if( iCode!=250 ){

    smtp_client_quit(p);
    return 1;
  }
  return 0;
}

/*







|
>
>




>








>







417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
** Return 0 on success and non-zero for a failure.
*/
int smtp_client_startup(SmtpSession *p){
  Blob in = BLOB_INITIALIZER;
  int iCode = 0;
  int bMore = 0;
  char *zArg = 0;
  if( p==0 || p->bFatal ) return 1;
  fossil_free(p->zErr);
  p->zErr = 0;
  do{
    smtp_get_reply_from_server(p, &in, &iCode, &bMore, &zArg);
  }while( bMore );
  if( iCode!=220 ){
    smtp_set_error(p, 1, "server opens conversation with: %b", &in);
    smtp_client_quit(p);
    return 1;
  }
  smtp_send_line(p, "EHLO %s\r\n", p->zFrom);
  do{
    smtp_get_reply_from_server(p, &in, &iCode, &bMore, &zArg);
  }while( bMore );
  if( iCode!=250 ){
    smtp_set_error(p, 1, "server responds to EHLO with: %b", &in);
    smtp_client_quit(p);
    return 1;
  }
  return 0;
}

/*
548
549
550
551
552
553
554




555
556
557
558
559



560
561
562
563
564
565



566
567
568
569
570
571



572
573
574
575
576
577
578
579
580
581
582
583
584
585



586
587
588
589
590
591
592
){
  int i;
  int iCode = 0;
  int bMore = 0;
  char *zArg = 0;
  Blob in;
  blob_init(&in, 0, 0);




  smtp_send_line(p, "MAIL FROM:<%s>\r\n", zFrom);
  do{
    smtp_get_reply_from_server(p, &in, &iCode, &bMore, &zArg);
  }while( bMore );
  if( iCode!=250 ) return 1;



  for(i=0; i<nTo; i++){
    smtp_send_line(p, "RCPT TO:<%s>\r\n", azTo[i]);
    do{
      smtp_get_reply_from_server(p, &in, &iCode, &bMore, &zArg);
    }while( bMore );
    if( iCode!=250 ) return 1;



  }
  smtp_send_line(p, "DATA\r\n");
  do{
    smtp_get_reply_from_server(p, &in, &iCode, &bMore, &zArg);
  }while( bMore );
  if( iCode!=354 ) return 1;



  smtp_send_email_body(zMsg, socket_send, 0);
  if( p->smtpFlags & SMTP_TRACE_STDOUT ){
    fossil_print("C: # message content\nC: .\n");
  }
  if( p->smtpFlags & SMTP_TRACE_FILE ){
    fprintf(p->logFile, "C: # message content\nC: .\n");
  }
  if( p->smtpFlags & SMTP_TRACE_BLOB ){
    blob_appendf(p->pTranscript, "C: # message content\nC: .\n");
  }
  do{
    smtp_get_reply_from_server(p, &in, &iCode, &bMore, &zArg);
  }while( bMore );
  if( iCode!=250 ) return 1;



  return 0;
}

/*
** The input is a base email address of the form "local@domain".
** Return a pointer to just the "domain" part, or 0 if the string
** contains no "@".







>
>
>
>




|
>
>
>





|
>
>
>





|
>
>
>













|
>
>
>







568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
){
  int i;
  int iCode = 0;
  int bMore = 0;
  char *zArg = 0;
  Blob in;
  blob_init(&in, 0, 0);
  if( p->atEof && !p->bFatal ){
    smtp_client_startup(p);
    if( p->atEof ) return 1;
  }
  smtp_send_line(p, "MAIL FROM:<%s>\r\n", zFrom);
  do{
    smtp_get_reply_from_server(p, &in, &iCode, &bMore, &zArg);
  }while( bMore );
  if( iCode!=250 ){
    smtp_set_error(p, 0, "server replies to MAIL FROM with: %b", &in);
    return 1;
  }
  for(i=0; i<nTo; i++){
    smtp_send_line(p, "RCPT TO:<%s>\r\n", azTo[i]);
    do{
      smtp_get_reply_from_server(p, &in, &iCode, &bMore, &zArg);
    }while( bMore );
    if( iCode!=250 ){
      smtp_set_error(p, 0, "server replies to RCPT TO with: %b", &in);
      return 1;
    }
  }
  smtp_send_line(p, "DATA\r\n");
  do{
    smtp_get_reply_from_server(p, &in, &iCode, &bMore, &zArg);
  }while( bMore );
  if( iCode!=354 ){
    smtp_set_error(p, 0, "server replies to DATA with: %b", &in);
    return 1;
  }
  smtp_send_email_body(zMsg, socket_send, 0);
  if( p->smtpFlags & SMTP_TRACE_STDOUT ){
    fossil_print("C: # message content\nC: .\n");
  }
  if( p->smtpFlags & SMTP_TRACE_FILE ){
    fprintf(p->logFile, "C: # message content\nC: .\n");
  }
  if( p->smtpFlags & SMTP_TRACE_BLOB ){
    blob_appendf(p->pTranscript, "C: # message content\nC: .\n");
  }
  do{
    smtp_get_reply_from_server(p, &in, &iCode, &bMore, &zArg);
  }while( bMore );
  if( iCode!=250 ){
    smtp_set_error(p, 0, "server replies to end-of-DATA with: %b", &in);
    return 1;
  }
  return 0;
}

/*
** The input is a base email address of the form "local@domain".
** Return a pointer to just the "domain" part, or 0 if the string
** contains no "@".
644
645
646
647
648
649
650

651

652
653
654
655
656
657
658
  }
  p = smtp_session_new(zFromDomain, zToDomain, smtpFlags, smtpPort);
  if( p->zErr ){
    fossil_fatal("%s", p->zErr);
  }
  fossil_print("Connection to \"%s\"\n", p->zHostname);
  smtp_client_startup(p);

  smtp_send_msg(p, zFrom, nTo, azTo, blob_str(&body));

  smtp_client_quit(p);
  if( p->zErr ){
    fossil_fatal("ERROR: %s\n", p->zErr);
  }
  smtp_session_free(p);
  blob_reset(&body);
}







>
|
>







680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
  }
  p = smtp_session_new(zFromDomain, zToDomain, smtpFlags, smtpPort);
  if( p->zErr ){
    fossil_fatal("%s", p->zErr);
  }
  fossil_print("Connection to \"%s\"\n", p->zHostname);
  smtp_client_startup(p);
  if( !p->atEof ){
    smtp_send_msg(p, zFrom, nTo, azTo, blob_str(&body));
  }
  smtp_client_quit(p);
  if( p->zErr ){
    fossil_fatal("ERROR: %s\n", p->zErr);
  }
  smtp_session_free(p);
  blob_reset(&body);
}