| | @@ -1334,36 +1334,69 @@ |
| 1334 | 1334 | z[0] = 0; |
| 1335 | 1335 | z++; |
| 1336 | 1336 | if( z[0]==0 || fossil_isspace(z[0]) ) return 0; |
| 1337 | 1337 | return z; |
| 1338 | 1338 | } |
| 1339 | + |
| 1340 | +/* |
| 1341 | +** Write formatted output back to the pop3 client, and also to the |
| 1342 | +** log file, if there is a log file. |
| 1343 | +*/ |
| 1344 | +static void pop3_print(FILE *pLog, const char *zFormat, ...){ |
| 1345 | + va_list ap; |
| 1346 | + char zLine[500]; |
| 1347 | + va_start(ap, zFormat); |
| 1348 | + sqlite3_vsnprintf(sizeof(zLine),zLine,zFormat,ap); |
| 1349 | + va_end(ap); |
| 1350 | + printf("%s\r\n", zLine); |
| 1351 | + fflush(stdout); |
| 1352 | + if( pLog ) fprintf(pLog, "S: %s\n", zLine); |
| 1353 | +} |
| 1339 | 1354 | |
| 1340 | 1355 | /* |
| 1341 | 1356 | ** COMMAND: pop3d |
| 1342 | 1357 | ** |
| 1343 | 1358 | ** Usage: %fossil pop3d [OPTIONS] REPOSITORY |
| 1344 | 1359 | ** |
| 1345 | 1360 | ** Begin a POP3 conversation with a client using stdin/stdout using |
| 1346 | 1361 | ** the mailboxes stored in REPOSITORY. |
| 1362 | +** |
| 1363 | +** If launched as root, the process first enters a chroot jail using |
| 1364 | +** the directory of REPOSITORY as root, then drops all privileges and |
| 1365 | +** assumes the user and group of REPOSITORY before reading any content |
| 1366 | +** off of the wire. |
| 1367 | +** |
| 1368 | +** --logdir DIR Each pop3d session creates a new logfile |
| 1369 | +** in the directory DIR and records a transcript |
| 1370 | +** of the session there. The logfile is opened |
| 1371 | +** before entering the chroot jail. |
| 1347 | 1372 | */ |
| 1348 | 1373 | void pop3d_command(void){ |
| 1349 | 1374 | char *zDbName; |
| 1350 | 1375 | char *zA1, *zA2, *zCmd, *z; |
| 1351 | 1376 | int inAuth = 1; |
| 1352 | 1377 | int i; |
| 1378 | + FILE *pLog; |
| 1379 | + const char *zDir; |
| 1353 | 1380 | Stmt q; |
| 1354 | 1381 | char zIn[1000]; |
| 1355 | 1382 | char zUser[100]; |
| 1383 | + zDir = find_option("logdir",0,1); |
| 1384 | + if( zDir ){ |
| 1385 | + char *zFile = file_time_tempname(zDir, ".txt"); |
| 1386 | + pLog = fossil_fopen(zFile, "w"); |
| 1387 | + fossil_free(zFile); |
| 1388 | + } |
| 1356 | 1389 | verify_all_options(); |
| 1357 | 1390 | if( g.argc!=3 ) usage("DBNAME"); |
| 1358 | 1391 | zDbName = g.argv[2]; |
| 1359 | 1392 | zDbName = enter_chroot_jail(zDbName, 0); |
| 1360 | 1393 | db_open_repository(zDbName); |
| 1361 | 1394 | add_content_sql_commands(g.db); |
| 1362 | | - printf("+OK POP3 server ready\r\n"); |
| 1363 | | - fflush(stdout); |
| 1395 | + pop3_print(pLog, "+OK POP3 server ready"); |
| 1364 | 1396 | while( fgets(zIn, sizeof(zIn), stdin) ){ |
| 1397 | + if( pLog ) fprintf(pLog, "C: %s", zIn); |
| 1365 | 1398 | zCmd = zIn; |
| 1366 | 1399 | zA1 = pop3d_arg(zCmd); |
| 1367 | 1400 | zA2 = zA1 ? pop3d_arg(zA1) : 0; |
| 1368 | 1401 | for(i=0; zCmd[i]; i++){ zCmd[i] = fossil_tolower(zCmd[i]); } |
| 1369 | 1402 | if( inAuth ){ |
| | @@ -1408,61 +1441,66 @@ |
| 1408 | 1441 | break; |
| 1409 | 1442 | } |
| 1410 | 1443 | if( strcmp(zCmd,"stat")==0 ){ |
| 1411 | 1444 | db_prepare(&q, "SELECT count(*), sum(esz) FROM pop3 WHERE NOT isDel"); |
| 1412 | 1445 | if( db_step(&q)==SQLITE_ROW ){ |
| 1413 | | - printf("+OK %d %d\r\n", db_column_int(&q,0), db_column_int(&q,1)); |
| 1446 | + pop3_print(pLog, "+OK %d %d", |
| 1447 | + db_column_int(&q,0), db_column_int(&q,1)); |
| 1414 | 1448 | }else{ |
| 1415 | | - printf("-ERR\r\n"); |
| 1449 | + pop3_print(pLog,"-ERR"); |
| 1416 | 1450 | } |
| 1417 | 1451 | db_finalize(&q); |
| 1418 | | - fflush(stdout); |
| 1419 | 1452 | continue; |
| 1420 | 1453 | } |
| 1421 | 1454 | if( strcmp(zCmd,"list")==0 ){ |
| 1422 | 1455 | if( zA1 ){ |
| 1423 | 1456 | db_prepare(&q, "SELECT id, esz FROM pop3" |
| 1424 | 1457 | " WHERE id=%d AND NOT isDel", atoi(zA1)); |
| 1425 | 1458 | if( db_step(&q)==SQLITE_ROW ){ |
| 1426 | | - printf("+OK %d %d\r\n", db_column_int(&q,0), db_column_int(&q,1)); |
| 1459 | + pop3_print(pLog, "+OK %d %d", |
| 1460 | + db_column_int(&q,0), db_column_int(&q,1)); |
| 1427 | 1461 | }else{ |
| 1428 | | - printf("-ERR\r\n"); |
| 1462 | + pop3_print(pLog, "-ERR"); |
| 1429 | 1463 | } |
| 1430 | 1464 | }else{ |
| 1431 | | - printf("+OK\r\n"); |
| 1465 | + pop3_print(pLog, "+OK"); |
| 1432 | 1466 | db_prepare(&q, "SELECT id, esz FROM pop3 WHERE NOT isDel"); |
| 1433 | 1467 | while( db_step(&q)==SQLITE_ROW ){ |
| 1434 | | - printf("%d %d\r\n", db_column_int(&q,0), db_column_int(&q,1)); |
| 1468 | + pop3_print(pLog, "%d %d", |
| 1469 | + db_column_int(&q,0), db_column_int(&q,1)); |
| 1435 | 1470 | } |
| 1436 | | - printf(".\r\n"); |
| 1471 | + pop3_print(pLog, "."); |
| 1437 | 1472 | } |
| 1438 | | - fflush(stdout); |
| 1439 | 1473 | db_finalize(&q); |
| 1440 | 1474 | continue; |
| 1441 | 1475 | } |
| 1442 | 1476 | if( strcmp(zCmd,"retr")==0 ){ |
| 1443 | 1477 | Blob all, line; |
| 1478 | + int nLine = 0; |
| 1444 | 1479 | if( zA1==0 ) goto cmd_error; |
| 1445 | 1480 | z = db_text(0, "SELECT decompress(emailblob.etxt) " |
| 1446 | 1481 | " FROM emailblob, pop3" |
| 1447 | 1482 | " WHERE emailblob.emailid=pop3.emailid" |
| 1448 | 1483 | " AND pop3.id=%d AND NOT pop3.isDel", |
| 1449 | 1484 | atoi(zA1)); |
| 1450 | 1485 | if( z==0 ) goto cmd_error; |
| 1486 | + pop3_print(pLog, "+OK"); |
| 1451 | 1487 | blob_init(&all, z, -1); |
| 1452 | 1488 | while( blob_line(&all, &line) ){ |
| 1453 | 1489 | if( blob_buffer(&line)[0]=='.' ){ |
| 1454 | 1490 | fputc('.', stdout); |
| 1455 | 1491 | } |
| 1456 | 1492 | fwrite(blob_buffer(&line), 1, blob_size(&line), stdout); |
| 1493 | + nLine++; |
| 1457 | 1494 | } |
| 1458 | | - printf(".\r\n"); |
| 1495 | + if( pLog ) fprintf(pLog, "S: # %d lines of content\n", nLine); |
| 1496 | + pop3_print(pLog, "."); |
| 1459 | 1497 | fossil_free(z); |
| 1460 | 1498 | blob_reset(&all); |
| 1461 | 1499 | blob_reset(&line); |
| 1462 | 1500 | fflush(stdout); |
| 1463 | | - goto cmd_ok; |
| 1501 | + continue; |
| 1464 | 1502 | } |
| 1465 | 1503 | if( strcmp(zCmd,"dele")==0 ){ |
| 1466 | 1504 | if( zA1==0 ) goto cmd_error; |
| 1467 | 1505 | db_multi_exec("UPDATE pop3 SET isDel=1 WHERE id=%d",atoi(zA1)); |
| 1468 | 1506 | goto cmd_ok; |
| | @@ -1477,36 +1515,36 @@ |
| 1477 | 1515 | if( strcmp(zCmd,"uidl")==0 ){ |
| 1478 | 1516 | if( zA1 ){ |
| 1479 | 1517 | db_prepare(&q, "SELECT id, emailid FROM pop3" |
| 1480 | 1518 | " WHERE id=%d AND NOT isDel", atoi(zA1)); |
| 1481 | 1519 | if( db_step(&q)==SQLITE_ROW ){ |
| 1482 | | - printf("+OK %d %d\r\n", db_column_int(&q,0), db_column_int(&q,1)); |
| 1520 | + pop3_print(pLog, "+OK %d %d", |
| 1521 | + db_column_int(&q,0), db_column_int(&q,1)); |
| 1483 | 1522 | }else{ |
| 1484 | | - printf("-ERR\r\n"); |
| 1523 | + pop3_print(pLog,"-ERR"); |
| 1485 | 1524 | } |
| 1486 | 1525 | }else{ |
| 1487 | | - printf("+OK\r\n"); |
| 1526 | + pop3_print(pLog, "+OK"); |
| 1488 | 1527 | db_prepare(&q, "SELECT id, emailid FROM pop3 WHERE NOT isDel"); |
| 1489 | 1528 | while( db_step(&q)==SQLITE_ROW ){ |
| 1490 | | - printf("%d %d\r\n", db_column_int(&q,0), db_column_int(&q,1)); |
| 1529 | + pop3_print(pLog, "%d %d", |
| 1530 | + db_column_int(&q,0), db_column_int(&q,1)); |
| 1491 | 1531 | } |
| 1492 | | - printf(".\r\n"); |
| 1532 | + pop3_print(pLog, "."); |
| 1493 | 1533 | } |
| 1494 | | - fflush(stdout); |
| 1495 | 1534 | db_finalize(&q); |
| 1496 | 1535 | continue; |
| 1497 | 1536 | } |
| 1498 | 1537 | if( strcmp(zCmd,"noop")==0 ){ |
| 1499 | 1538 | goto cmd_ok; |
| 1500 | 1539 | } |
| 1501 | 1540 | /* Else, fall through into cmd_error */ |
| 1502 | 1541 | } |
| 1503 | 1542 | cmd_error: |
| 1504 | | - printf("-ERR\r\n"); |
| 1505 | | - fflush(stdout); |
| 1543 | + pop3_print(pLog, "-ERR"); |
| 1506 | 1544 | continue; |
| 1507 | 1545 | cmd_ok: |
| 1508 | | - printf("+OK\r\n"); |
| 1509 | | - fflush(stdout); |
| 1546 | + pop3_print(pLog, "+OK"); |
| 1510 | 1547 | continue; |
| 1511 | 1548 | } |
| 1549 | + if( pLog ) fclose(pLog); |
| 1512 | 1550 | } |
| 1513 | 1551 | |