Fossil

Check-in [38165f37]
Login

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

Overview
Comment:Add the --logdir option to the "fossil pop3d" command. Fix some issues with POP3 reply formatting.
Downloads: Tarball | ZIP archive | SQL archive
Timelines: family | ancestors | descendants | both | trunk
Files: files | file ages | folders
SHA3-256:38165f3764caa8e51ac173c9121378a25f477619b2f222f1526db672e05a5742
User & Date: drh 2018-07-18 01:40:02
Context
2018-07-18
01:41
Fix a harmless compiler warning. check-in: 2332efb7 user: drh tags: trunk
01:40
Add the --logdir option to the "fossil pop3d" command. Fix some issues with POP3 reply formatting. check-in: 38165f37 user: drh tags: trunk
2018-07-17
23:50
Initial implementation of the "fossil pop3d" command. check-in: bf13815c user: drh tags: trunk
Changes
Hide Diffs Unified Diffs Show Whitespace Changes Patch

Changes to src/email.c.

362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
...
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
...
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
      x[2] = "0123456789ABCDEF"[c&0xf];
      blob_append(pOut, x, 3);
      iCol += 3;
    }
  }
}

/*
** Come up with a unique filename in the zDir directory.
**
** Space to hold the filename is obtained from mprintf() and must
** be freed using fossil_free() by the caller.
*/
static char *emailTempFilename(const char *zDir){
  char *zFile = db_text(0,
     "SELECT %Q||strftime('/%%Y%%m%%d%%H%%M%%S-','now')||hex(randomblob(8))",
        zDir);
  return zFile;
}

#if defined(_WIN32) || defined(WIN32)
# undef popen
# define popen _popen
# undef pclose
# define pclose _pclose
#endif

................................................................................
    if( out ){
      fwrite(blob_buffer(&all), 1, blob_size(&all), out);
      fclose(out);
    }else{
      emailerError(p, "Could not open output pipe \"%s\"", p->zCmd);
    }
  }else if( p->zDir ){
    char *zFile = emailTempFilename(p->zDir);
    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 ){
................................................................................
    const char *zInboundDir = db_get("email-receive-dir","");
    verify_all_options();
    if( g.argc!=3 && g.argc!=4 ){
      usage("inbound [FILE]");
    }
    blob_read_from_file(&email, g.argc==3 ? "-" : g.argv[3], ExtFILE);
    if( zInboundDir[0] ){
      char *zFN = emailTempFilename(zInboundDir);
      blob_write_to_file(&email, zFN);
      fossil_free(zFN);
    }
    email_receive(&email);
  }else
  if( strncmp(zCmd, "reset", nCmd)==0 ){
    int c;







<
<
<
<
<
<
<
<
<
<
<
<
<







 







|







 







|







362
363
364
365
366
367
368













369
370
371
372
373
374
375
...
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
...
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
      x[2] = "0123456789ABCDEF"[c&0xf];
      blob_append(pOut, x, 3);
      iCol += 3;
    }
  }
}














#if defined(_WIN32) || defined(WIN32)
# undef popen
# define popen _popen
# undef pclose
# define pclose _pclose
#endif

................................................................................
    if( out ){
      fwrite(blob_buffer(&all), 1, blob_size(&all), out);
      fclose(out);
    }else{
      emailerError(p, "Could not open output pipe \"%s\"", p->zCmd);
    }
  }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 ){
................................................................................
    const char *zInboundDir = db_get("email-receive-dir","");
    verify_all_options();
    if( g.argc!=3 && g.argc!=4 ){
      usage("inbound [FILE]");
    }
    blob_read_from_file(&email, g.argc==3 ? "-" : g.argv[3], ExtFILE);
    if( zInboundDir[0] ){
      char *zFN = file_time_tempname(zInboundDir,".email");
      blob_write_to_file(&email, zFN);
      fossil_free(zFN);
    }
    email_receive(&email);
  }else
  if( strncmp(zCmd, "reset", nCmd)==0 ){
    int c;

Changes to src/file.c.

20
21
22
23
24
25
26

27
28
29
30
31
32
33
....
1466
1467
1468
1469
1470
1471
1472

















1473
1474
1475
1476
1477
1478

1479
1480
1481

1482


1483





1484
1485
1486
1487
1488

1489
1490
1491
1492
1493
1494
1495
#include "config.h"
#include <sys/types.h>
#include <sys/stat.h>
#include <unistd.h>
#include <stdio.h>
#include <string.h>
#include <errno.h>

#include "file.h"

/*
** On Windows, include the Platform SDK header file.
*/
#ifdef _WIN32
# include <direct.h>
................................................................................
  z = blob_buffer(pBuf);
  for(i=0; z[i]; i++) if( z[i]=='\\' ) z[i] = '/';
#else
  fossil_path_free((char *)azDirs[0]);
#endif
}



















/*
** COMMAND: test-tempname
** Usage:  fossil test-name BASENAME ...
**
** Generate temporary filenames derived from BASENAME

*/
void file_test_tempname(void){
  int i;

  Blob x = BLOB_INITIALIZER;


  for(i=2; i<g.argc; i++){





    file_tempname(&x, g.argv[i]);
    fossil_print("%s\n", blob_str(&x));
    blob_reset(&x);
  }
}



/*
** Return true if a file named zName exists and has identical content
** to the blob pContent.  If zName does not exist or if the content is
** different in any way, then return false.
**







>







 







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



|

|
>



>

>
>

>
>
>
>
>





>







20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
....
1467
1468
1469
1470
1471
1472
1473
1474
1475
1476
1477
1478
1479
1480
1481
1482
1483
1484
1485
1486
1487
1488
1489
1490
1491
1492
1493
1494
1495
1496
1497
1498
1499
1500
1501
1502
1503
1504
1505
1506
1507
1508
1509
1510
1511
1512
1513
1514
1515
1516
1517
1518
1519
1520
1521
1522
1523
#include "config.h"
#include <sys/types.h>
#include <sys/stat.h>
#include <unistd.h>
#include <stdio.h>
#include <string.h>
#include <errno.h>
#include <time.h>
#include "file.h"

/*
** On Windows, include the Platform SDK header file.
*/
#ifdef _WIN32
# include <direct.h>
................................................................................
  z = blob_buffer(pBuf);
  for(i=0; z[i]; i++) if( z[i]=='\\' ) z[i] = '/';
#else
  fossil_path_free((char *)azDirs[0]);
#endif
}

/*
** Compute a temporary filename in zDir.  The filename is based on
** the current time.
*/
char *file_time_tempname(const char *zDir, const char *zSuffix){
  struct tm *tm;
  unsigned int r;
  static unsigned int cnt = 0;
  time_t t;
  t = time(0);
  tm = gmtime(&t);
  sqlite3_randomness(sizeof(r), &r);
  return mprintf("%s/%04d%02d%02d%02d%02d%02d%04d%06d%s",
      zDir, tm->tm_year+1900, tm->tm_mon+1, tm->tm_mday,
            tm->tm_hour, tm->tm_min, tm->tm_sec, cnt++, r%1000000, zSuffix);
}


/*
** COMMAND: test-tempname
** Usage:  fossil test-name [--time SUFFIX] BASENAME ...
**
** Generate temporary filenames derived from BASENAME.  Use the --time
** option to generate temp names based on the time of day.
*/
void file_test_tempname(void){
  int i;
  char *zSuffix = find_option("time",0,1);
  Blob x = BLOB_INITIALIZER;
  char *z;
  verify_all_options();
  for(i=2; i<g.argc; i++){
    if( zSuffix ){
      z = file_time_tempname(g.argv[i], zSuffix);
      fossil_print("%s\n", z);
      fossil_free(z);
    }else{
      file_tempname(&x, g.argv[i]);
      fossil_print("%s\n", blob_str(&x));
      blob_reset(&x);
    }
  }
}


/*
** Return true if a file named zName exists and has identical content
** to the blob pContent.  If zName does not exist or if the content is
** different in any way, then return false.
**

Changes to src/smtp.c.

1332
1333
1334
1335
1336
1337
1338















1339
1340
1341
1342
1343
1344
1345
1346










1347
1348
1349
1350
1351
1352


1353
1354
1355






1356
1357
1358
1359
1360
1361
1362
1363
1364
1365
1366
1367
1368
1369
1370
1371
....
1406
1407
1408
1409
1410
1411
1412

1413
1414
1415

1416
1417
1418
1419
1420
1421
1422
1423
1424
1425

1426
1427
1428

1429
1430
1431

1432
1433

1434
1435
1436

1437
1438
1439
1440
1441
1442
1443

1444
1445
1446
1447
1448
1449
1450

1451
1452
1453
1454
1455
1456

1457
1458


1459
1460
1461
1462
1463
1464
1465
1466
1467
1468
1469
1470
....
1475
1476
1477
1478
1479
1480
1481

1482
1483
1484

1485
1486
1487

1488
1489

1490
1491
1492

1493
1494
1495
1496
1497
1498
1499
1500
1501
1502
1503
1504
1505
1506
1507
1508
1509

1510
1511

1512
  while( z[0] && !fossil_isspace(z[0]) ){ z++; }
  if( z[0]==0 ) return 0;
  z[0] = 0;
  z++;
  if( z[0]==0 || fossil_isspace(z[0]) ) return 0;
  return z;
}
















/*
** COMMAND: pop3d
**
** Usage: %fossil pop3d [OPTIONS] REPOSITORY
**
** Begin a POP3 conversation with a client using stdin/stdout using
** the mailboxes stored in REPOSITORY.










*/
void pop3d_command(void){
  char *zDbName;
  char *zA1, *zA2, *zCmd, *z;
  int inAuth = 1;
  int i;


  Stmt q;
  char zIn[1000];
  char zUser[100];






  verify_all_options();
  if( g.argc!=3 ) usage("DBNAME");
  zDbName = g.argv[2];
  zDbName = enter_chroot_jail(zDbName, 0);
  db_open_repository(zDbName);
  add_content_sql_commands(g.db);
    printf("+OK POP3 server ready\r\n");
  fflush(stdout);
  while( fgets(zIn, sizeof(zIn), stdin) ){
    zCmd = zIn;
    zA1 = pop3d_arg(zCmd);
    zA2 = zA1 ? pop3d_arg(zA1) : 0;
    for(i=0; zCmd[i]; i++){ zCmd[i] = fossil_tolower(zCmd[i]); }
    if( inAuth ){
      if( strcmp(zCmd,"user")==0 ){
        if( zA1==0 || zA2!=0 ) goto cmd_error;
................................................................................
          " WHERE estate<2 AND ebid IN (SELECT ebid FROM pop3 WHERE isDel);"
        );
        break;
      }
      if( strcmp(zCmd,"stat")==0 ){
        db_prepare(&q, "SELECT count(*), sum(esz) FROM pop3 WHERE NOT isDel");
        if( db_step(&q)==SQLITE_ROW ){

          printf("+OK %d %d\r\n", db_column_int(&q,0), db_column_int(&q,1));
        }else{
          printf("-ERR\r\n");

        }
        db_finalize(&q);
        fflush(stdout);
        continue;
      }
      if( strcmp(zCmd,"list")==0 ){
        if( zA1 ){
          db_prepare(&q, "SELECT id, esz FROM pop3"
                         " WHERE id=%d AND NOT isDel", atoi(zA1));
          if( db_step(&q)==SQLITE_ROW ){

            printf("+OK %d %d\r\n", db_column_int(&q,0), db_column_int(&q,1));
          }else{
            printf("-ERR\r\n");

          }
        }else{
          printf("+OK\r\n");

          db_prepare(&q, "SELECT id, esz FROM pop3 WHERE NOT isDel");
          while( db_step(&q)==SQLITE_ROW ){

            printf("%d %d\r\n", db_column_int(&q,0), db_column_int(&q,1));
          }
          printf(".\r\n");

        }
        fflush(stdout);
        db_finalize(&q);
        continue;
      }
      if( strcmp(zCmd,"retr")==0 ){
        Blob all, line;

        if( zA1==0 ) goto cmd_error;
        z = db_text(0, "SELECT decompress(emailblob.etxt) "
                       "  FROM emailblob, pop3"
                       " WHERE emailblob.emailid=pop3.emailid"
                       "   AND pop3.id=%d AND NOT pop3.isDel",
                       atoi(zA1));
        if( z==0 ) goto cmd_error;

        blob_init(&all, z, -1);
        while( blob_line(&all, &line) ){
          if( blob_buffer(&line)[0]=='.' ){
            fputc('.', stdout);
          }
          fwrite(blob_buffer(&line), 1, blob_size(&line), stdout);

        }
        printf(".\r\n");


        fossil_free(z);
        blob_reset(&all);
        blob_reset(&line);
        fflush(stdout);
        goto cmd_ok;
      }
      if( strcmp(zCmd,"dele")==0 ){
        if( zA1==0 ) goto cmd_error;
        db_multi_exec("UPDATE pop3 SET isDel=1 WHERE id=%d",atoi(zA1));
        goto cmd_ok;
      }
      if( strcmp(zCmd,"rset")==0 ){
................................................................................
        goto cmd_error;
      }
      if( strcmp(zCmd,"uidl")==0 ){
        if( zA1 ){
          db_prepare(&q, "SELECT id, emailid FROM pop3"
                         " WHERE id=%d AND NOT isDel", atoi(zA1));
          if( db_step(&q)==SQLITE_ROW ){

            printf("+OK %d %d\r\n", db_column_int(&q,0), db_column_int(&q,1));
          }else{
            printf("-ERR\r\n");

          }
        }else{
          printf("+OK\r\n");

          db_prepare(&q, "SELECT id, emailid FROM pop3 WHERE NOT isDel");
          while( db_step(&q)==SQLITE_ROW ){

            printf("%d %d\r\n", db_column_int(&q,0), db_column_int(&q,1));
          }
          printf(".\r\n");

        }
        fflush(stdout);
        db_finalize(&q);
        continue;
      }
      if( strcmp(zCmd,"noop")==0 ){
        goto cmd_ok;
      }
      /* Else, fall through into cmd_error */
    }
  cmd_error:
    printf("-ERR\r\n");
    fflush(stdout);
    continue;
  cmd_ok:
    printf("+OK\r\n");
    fflush(stdout);

    continue;
  }

}







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








>
>
>
>
>
>
>
>
>
>






>
>



>
>
>
>
>
>






|
|
|







 







>
|

<
>


<







>
|

<
>


<
>


>
|

<
>

<





>







>






>

<
>
>




|







 







>
|

<
>


<
>


>
|

<
>

<









|
<


<
<
>


>

1332
1333
1334
1335
1336
1337
1338
1339
1340
1341
1342
1343
1344
1345
1346
1347
1348
1349
1350
1351
1352
1353
1354
1355
1356
1357
1358
1359
1360
1361
1362
1363
1364
1365
1366
1367
1368
1369
1370
1371
1372
1373
1374
1375
1376
1377
1378
1379
1380
1381
1382
1383
1384
1385
1386
1387
1388
1389
1390
1391
1392
1393
1394
1395
1396
1397
1398
1399
1400
1401
1402
1403
1404
....
1439
1440
1441
1442
1443
1444
1445
1446
1447
1448

1449
1450
1451

1452
1453
1454
1455
1456
1457
1458
1459
1460
1461

1462
1463
1464

1465
1466
1467
1468
1469
1470

1471
1472

1473
1474
1475
1476
1477
1478
1479
1480
1481
1482
1483
1484
1485
1486
1487
1488
1489
1490
1491
1492
1493
1494

1495
1496
1497
1498
1499
1500
1501
1502
1503
1504
1505
1506
1507
1508
....
1513
1514
1515
1516
1517
1518
1519
1520
1521
1522

1523
1524
1525

1526
1527
1528
1529
1530
1531

1532
1533

1534
1535
1536
1537
1538
1539
1540
1541
1542
1543

1544
1545


1546
1547
1548
1549
1550
  while( z[0] && !fossil_isspace(z[0]) ){ z++; }
  if( z[0]==0 ) return 0;
  z[0] = 0;
  z++;
  if( z[0]==0 || fossil_isspace(z[0]) ) return 0;
  return z;
}

/*
** Write formatted output back to the pop3 client, and also to the
** log file, if there is a log file.
*/
static void pop3_print(FILE *pLog, const char *zFormat, ...){
  va_list ap;
  char zLine[500];
  va_start(ap, zFormat);
  sqlite3_vsnprintf(sizeof(zLine),zLine,zFormat,ap);
  va_end(ap);
  printf("%s\r\n", zLine);
  fflush(stdout);
  if( pLog ) fprintf(pLog, "S: %s\n", zLine);
}

/*
** COMMAND: pop3d
**
** Usage: %fossil pop3d [OPTIONS] REPOSITORY
**
** Begin a POP3 conversation with a client using stdin/stdout using
** the mailboxes stored in REPOSITORY.
**
** If launched as root, the process first enters a chroot jail using
** the directory of REPOSITORY as root, then drops all privileges and
** assumes the user and group of REPOSITORY before reading any content
** off of the wire.
**
**   --logdir  DIR        Each pop3d session creates a new logfile
**                        in the directory DIR and records a transcript
**                        of the session there.  The logfile is opened
**                        before entering the chroot jail.
*/
void pop3d_command(void){
  char *zDbName;
  char *zA1, *zA2, *zCmd, *z;
  int inAuth = 1;
  int i;
  FILE *pLog;
  const char *zDir;
  Stmt q;
  char zIn[1000];
  char zUser[100];
  zDir = find_option("logdir",0,1);
  if( zDir ){
    char *zFile = file_time_tempname(zDir, ".txt");
    pLog = fossil_fopen(zFile, "w");
    fossil_free(zFile);
  }
  verify_all_options();
  if( g.argc!=3 ) usage("DBNAME");
  zDbName = g.argv[2];
  zDbName = enter_chroot_jail(zDbName, 0);
  db_open_repository(zDbName);
  add_content_sql_commands(g.db);
  pop3_print(pLog, "+OK POP3 server ready");
  while( fgets(zIn, sizeof(zIn), stdin) ){
    if( pLog ) fprintf(pLog, "C: %s", zIn);
    zCmd = zIn;
    zA1 = pop3d_arg(zCmd);
    zA2 = zA1 ? pop3d_arg(zA1) : 0;
    for(i=0; zCmd[i]; i++){ zCmd[i] = fossil_tolower(zCmd[i]); }
    if( inAuth ){
      if( strcmp(zCmd,"user")==0 ){
        if( zA1==0 || zA2!=0 ) goto cmd_error;
................................................................................
          " WHERE estate<2 AND ebid IN (SELECT ebid FROM pop3 WHERE isDel);"
        );
        break;
      }
      if( strcmp(zCmd,"stat")==0 ){
        db_prepare(&q, "SELECT count(*), sum(esz) FROM pop3 WHERE NOT isDel");
        if( db_step(&q)==SQLITE_ROW ){
          pop3_print(pLog, "+OK %d %d",
                     db_column_int(&q,0), db_column_int(&q,1));
        }else{

          pop3_print(pLog,"-ERR");
        }
        db_finalize(&q);

        continue;
      }
      if( strcmp(zCmd,"list")==0 ){
        if( zA1 ){
          db_prepare(&q, "SELECT id, esz FROM pop3"
                         " WHERE id=%d AND NOT isDel", atoi(zA1));
          if( db_step(&q)==SQLITE_ROW ){
            pop3_print(pLog, "+OK %d %d",
                       db_column_int(&q,0), db_column_int(&q,1));
          }else{

            pop3_print(pLog, "-ERR");
          }
        }else{

          pop3_print(pLog, "+OK");
          db_prepare(&q, "SELECT id, esz FROM pop3 WHERE NOT isDel");
          while( db_step(&q)==SQLITE_ROW ){
            pop3_print(pLog, "%d %d",
                       db_column_int(&q,0), db_column_int(&q,1));
          }

          pop3_print(pLog, ".");
        }

        db_finalize(&q);
        continue;
      }
      if( strcmp(zCmd,"retr")==0 ){
        Blob all, line;
        int nLine = 0;
        if( zA1==0 ) goto cmd_error;
        z = db_text(0, "SELECT decompress(emailblob.etxt) "
                       "  FROM emailblob, pop3"
                       " WHERE emailblob.emailid=pop3.emailid"
                       "   AND pop3.id=%d AND NOT pop3.isDel",
                       atoi(zA1));
        if( z==0 ) goto cmd_error;
        pop3_print(pLog, "+OK");
        blob_init(&all, z, -1);
        while( blob_line(&all, &line) ){
          if( blob_buffer(&line)[0]=='.' ){
            fputc('.', stdout);
          }
          fwrite(blob_buffer(&line), 1, blob_size(&line), stdout);
          nLine++;
        }

        if( pLog ) fprintf(pLog, "S: # %d lines of content\n", nLine);
        pop3_print(pLog, ".");
        fossil_free(z);
        blob_reset(&all);
        blob_reset(&line);
        fflush(stdout);
        continue;
      }
      if( strcmp(zCmd,"dele")==0 ){
        if( zA1==0 ) goto cmd_error;
        db_multi_exec("UPDATE pop3 SET isDel=1 WHERE id=%d",atoi(zA1));
        goto cmd_ok;
      }
      if( strcmp(zCmd,"rset")==0 ){
................................................................................
        goto cmd_error;
      }
      if( strcmp(zCmd,"uidl")==0 ){
        if( zA1 ){
          db_prepare(&q, "SELECT id, emailid FROM pop3"
                         " WHERE id=%d AND NOT isDel", atoi(zA1));
          if( db_step(&q)==SQLITE_ROW ){
            pop3_print(pLog, "+OK %d %d",
                       db_column_int(&q,0), db_column_int(&q,1));
          }else{

            pop3_print(pLog,"-ERR");
          }
        }else{

          pop3_print(pLog, "+OK");
          db_prepare(&q, "SELECT id, emailid FROM pop3 WHERE NOT isDel");
          while( db_step(&q)==SQLITE_ROW ){
            pop3_print(pLog, "%d %d",
                       db_column_int(&q,0), db_column_int(&q,1));
          }

          pop3_print(pLog, ".");
        }

        db_finalize(&q);
        continue;
      }
      if( strcmp(zCmd,"noop")==0 ){
        goto cmd_ok;
      }
      /* Else, fall through into cmd_error */
    }
  cmd_error:
    pop3_print(pLog, "-ERR");

    continue;
  cmd_ok:


    pop3_print(pLog, "+OK");
    continue;
  }
  if( pLog ) fclose(pLog);
}