25 #include "compression.h"
77 #define MAIN_ERR_PREFIX "NNTP: "
80 #define NNTP_HANDLEMAX 1U
90 #define NNTP_LINELENGTHMAX (size_t) 512
93 #define NNTP_ARGLENGTHMAX (size_t) 497
100 #define NNTP_CAPA_MODE_READER 0x0001U
101 #define NNTP_CAPA_READER 0x0002U
102 #define NNTP_CAPA_LIST 0x0004U
103 #define NNTP_CAPA_LIST_MOTD 0x0008U
104 #define NNTP_CAPA_LIST_DISTRIB_PATS 0x0010U
105 #define NNTP_CAPA_LIST_SUBSCRIPTIONS 0x0020U
106 #define NNTP_CAPA_OVER 0x0040U
107 #define NNTP_CAPA_POST 0x0080U
108 #define NNTP_CAPA_AUTHINFO_USER 0x0100U
109 #define NNTP_CAPA_COMPRESS 0x0200U
110 #define NNTP_CAPA_MAXARTNUM 0x0400U
118 #define NNTP_COMPRESS_DEFLATE 0x0001U
130 NNTP_CMD_CAPABILITIES,
131 NNTP_CMD_MODE_READER,
134 NNTP_CMD_AUTHINFO_USER,
135 NNTP_CMD_AUTHINFO_PASS,
137 NNTP_CMD_LIST_NEWSGROUPS,
139 NNTP_CMD_LIST_DISTRIB_PATS,
140 NNTP_CMD_LIST_SUBSCRIPTIONS,
141 NNTP_CMD_LIST_OVERVIEW_FMT,
144 NNTP_CMD_ARTICLE_BY_MID,
154 unsigned int connected;
157 unsigned int version;
158 unsigned int capabilities;
159 size_t over_newsgroups;
160 unsigned int compress_algs;
162 void* compress_stream;
166 size_t distrib_pats_len;
167 #if CFG_USE_TLS || CFG_NNTP_AUTH_UNENCRYPTED
173 # if CFG_USE_OPENSSL_API_1_1 && !CFG_USE_OPENSSL_API_3
204 static int exec_auth(
int);
205 static int refresh_capabilities(
int);
206 static int refresh_overview_format(
int);
218 static int parse_number(
int c,
char wm[16],
int* wm_len,
nntp_anum_t* n)
222 if(0x20 == c && *wm_len)
234 if(!(0x30 <= c && 0x39 >= c))
236 PRINT_ERROR(
"Invalid character in water mark field");
240 else if(16 <= *wm_len)
245 else { wm[(*wm_len)++] = (char) c; }
255 static int get_handle(
int* handle)
263 if(server[i].connected)
continue;
267 server[i].lfs = NULL;
268 server[i].version = 0;
269 server[i].capabilities = 0;
270 server[i].over_newsgroups = 0;
271 server[i].compress_algs = 0;
272 server[i].compress_active = 0;
273 server[i].compress_stream = NULL;
275 server[i].maxartnum = 0;
276 server[i].distrib_pats = NULL;
277 server[i].distrib_pats_len = 0;
278 #if CFG_USE_TLS || CFG_NNTP_AUTH_UNENCRYPTED
279 server[i].user = NULL;
280 server[i].passwd = NULL;
283 server[i].eco = NULL;
284 # if CFG_USE_OPENSSL_API_1_1 && !CFG_USE_OPENSSL_API_3
285 server[i].peekbuf = NULL;
286 server[i].peekbuf_len = 0;
302 static int transport_flush(
int handle,
int terminate)
306 #if !CFG_CMPR_DISABLE
307 if(server[handle].compress_active)
309 if(!terminate) { res =
cmpr_flush(server[handle].compress_stream); }
325 static api_posix_ssize_t transport_send(
int handle,
const void* buf,
326 size_t len,
int flags)
331 PRINT_ERROR(
"transport_send(): Called with invalid flags");
335 if(NULL != server[handle].eco)
337 return(
tls_send(server[handle].eco, buf, len));
340 return(api_posix_send(server[handle].sd, buf, len, flags));
347 static api_posix_ssize_t srv_send(
int handle,
const void* buf,
size_t len)
349 #if !CFG_CMPR_DISABLE
350 if(server[handle].compress_active)
352 return(
cmpr_send(server[handle].compress_stream, buf, len));
357 return(transport_send(handle, buf, len, 0));
369 static api_posix_ssize_t transport_recv(
int handle,
void* buf,
size_t len,
372 api_posix_ssize_t res = -1;
375 # if CFG_USE_OPENSSL_API_1_1 && !CFG_USE_OPENSSL_API_3
381 if(API_POSIX_SSIZE_MAX < len)
383 PRINT_ERROR(
"transport_recv(): Called with invalid data length");
388 if(~(API_POSIX_MSG_PEEK) & flags)
390 PRINT_ERROR(
"transport_recv(): Called with invalid flags");
395 if(NULL != server[handle].eco)
397 if(API_POSIX_MSG_PEEK & flags) { peek = 1; }
398 # if CFG_USE_OPENSSL_API_1_1 && !CFG_USE_OPENSSL_API_3
410 if(!peek && !server[handle].peekbuf_len)
412 res =
tls_recv(server[handle].eco, buf, len, 0);
417 if(peek && !server[handle].peekbuf_len)
420 p = (
char*) api_posix_realloc(server[handle].peekbuf, len);
423 server[handle].peekbuf = p;
425 server[handle].peekbuf, len, 0);
428 server[handle].peekbuf_len = (size_t) res;
434 if(server[handle].peekbuf_len)
436 if(server[handle].peekbuf_len < len)
439 len = server[handle].peekbuf_len;
441 memcpy(buf, server[handle].peekbuf, len);
442 res = (api_posix_ssize_t) len;
446 p = (
char*) server[handle].peekbuf;
447 memmove(server[handle].peekbuf, (
void*) &p[len],
448 server[handle].peekbuf_len - len);
449 server[handle].peekbuf_len -= len;
455 res =
tls_recv(server[handle].eco, buf, len, peek);
461 res = api_posix_recv(server[handle].sd, buf, len, flags);
471 static api_posix_ssize_t srv_recv(
int handle,
void* buf,
size_t len,
474 #if !CFG_CMPR_DISABLE
475 if(server[handle].compress_active)
477 return(
cmpr_recv(server[handle].compress_stream, buf, len, flags));
482 return(transport_recv(handle, buf, len, flags));
490 static int create_response(
struct nntp_response** r)
494 *r = (
struct nntp_response*) api_posix_malloc(
sizeof(
struct nntp_response));
497 PRINT_ERROR(
"Out of memory while allocating response");
504 (*r)->content = NULL;
516 static void destroy_response(
struct nntp_response** r)
520 api_posix_free((
void*) (*r)->msg);
521 api_posix_free((
void*) (*r)->content);
522 api_posix_free((
void*) *r);
536 static int multiline(
unsigned int status,
enum nntp_cmd command)
539 static unsigned int code[12] =
541 100U, 101U, 211U, 215U, 220U, 221U, 222U, 224U, 225U, 230U, 231U,
548 while(code[i]) {
if(status == code[i++]) { res = 1; } }
551 if(211U == status && NNTP_CMD_GROUP == command) { res = 0; }
560 static int recv_multiline_data_block(
int handle,
561 struct nntp_response* response)
564 api_posix_ssize_t rv;
580 resp = (
char*) api_posix_malloc(len);
583 PRINT_ERROR(
"Out of memory while receiving response");
590 while(!res && !eor && len > ri)
594 do { rv = srv_recv(handle, &resp[ri], len - ri, API_POSIX_MSG_PEEK); }
595 while((api_posix_ssize_t) -1 == rv
596 && API_POSIX_EINTR == api_posix_errno);
597 if((api_posix_ssize_t) -1 == rv)
615 if(
'\r' == resp[i - 1] &&
'.' == resp[i - 2])
624 if(
'\r' == resp[i - 1] &&
'.' == resp[i - 2]
625 &&
'\n' == resp[i - 3] &&
'\r' == resp[i - 4])
643 do { rv = srv_recv(handle, &resp[ri], i - ri, 0); }
644 while((api_posix_ssize_t) -1 == rv
645 && API_POSIX_EINTR == api_posix_errno);
646 if((api_posix_ssize_t) -1 == rv)
660 p = (
char*) api_posix_realloc(resp, len);
663 PRINT_ERROR(
"Out of memory while receiving response");
669 if(!res && !eor) { res = -1; }
670 if(res) { api_posix_free((
void*) resp); }
679 if(
'.' == resp[0] &&
'.' == resp[1])
681 memmove(&resp[0], &resp[1], --len + (
size_t) 1);
683 for(i = 2; i < len; ++i)
685 if(
'.' == resp[i] &&
'.' == resp[i - 1] && (
char) 10 == resp[i - 2])
687 memmove(&resp[i - 1], &resp[i], len-- - i + (
size_t) 1);
691 if(NULL != response->content)
693 api_posix_free((
void*) response->content);
695 response->content = resp;
696 response->lines = lines;
697 response->bufsize = len + 1;
698 log_add(server[handle].lfs, response->content);
708 static int recv_reply(
int handle,
enum nntp_cmd command,
709 struct nntp_response* response)
712 api_posix_ssize_t rv;
721 while(!res && !eol && len > li)
725 do { rv = srv_recv(handle, &resp[li], len - li, API_POSIX_MSG_PEEK); }
726 while((api_posix_ssize_t) -1 == rv && API_POSIX_EINTR == api_posix_errno);
728 if((api_posix_ssize_t) 0 >= rv)
739 if(
'\n' == resp[i++])
748 do { rv = srv_recv(handle, &resp[li], i - li, 0); }
749 while((api_posix_ssize_t) -1 == rv
750 && API_POSIX_EINTR == api_posix_errno);
751 if((api_posix_ssize_t) -1 == rv)
760 if(!res && !eol) { res = -1; }
766 for(i = 0; i < 3; ++i)
768 if((
unsigned char) 9 >= (
unsigned char) resp[i])
770 PRINT_ERROR(
"Protocol error: Invalid response code");
771 log_add(server[handle].lfs,
"[<= Invalid response]\n");
777 response->code1 = (
unsigned char) (resp[0] - (
char) 48);
778 response->code2 = (
unsigned char) (resp[1] - (
char) 48);
779 response->code3 = (
unsigned char) (resp[2] - (
char) 48);
780 if(NULL != response->msg) { api_posix_free((
void*) response->msg); }
781 response->msg = (
char*) api_posix_malloc(strlen(resp) + (size_t) 1);
782 if(NULL == response->msg)
784 PRINT_ERROR(
"Out of memory while receiving response");
787 else { strcpy(response->msg, resp); }
788 response->content = NULL;
790 log_add(server[handle].lfs,
"[<=] ");
791 log_add(server[handle].lfs, response->msg);
798 response->status = (
unsigned int) response->code3;
799 response->status += (
unsigned int) response->code2 * 10U;
800 response->status += (
unsigned int) response->code1 * 100U;
802 if(multiline(response->status, command))
804 log_add(server[handle].lfs,
"[<= Expect multiline data block]\n");
805 res = recv_multiline_data_block(handle, response);
816 static int send_multiline_data_block(
int handle,
const char*
data)
818 static const char eob[] =
".\r\n";
822 api_posix_ssize_t rv;
827 if(NULL ==
data) { res = -1; }
830 log_add(server[handle].lfs,
"[=> Send multiline data block]\n");
838 buf = (
char*) api_posix_malloc((strlen(
data) + (
size_t) 3) * (size_t) 2);
841 PRINT_ERROR(
"Memory allocation failed for dot stuffing");
848 while((c =
data[i++]))
850 if( (13 == (
int) c && 10 != (
int)
data[i])
851 || (10 == (
int) c && 13 != (
int)
data[i - (
size_t) 2] && i) )
855 if(
'.' == c && (
size_t) 3 <= i)
857 if( (10 == (
int)
data[i - (
size_t) 2])
858 && (13 == (
int)
data[i - (
size_t) 3]) )
870 || 10 != (
int) buf[bi - (
size_t) 1]
871 || 13 != (
int) buf[bi - (
size_t) 2] )
874 buf[bi++] = (char) 13;
875 buf[bi++] = (char) 10;
888 do { rv = srv_send(handle, &
data[i], len - i); }
889 while((api_posix_ssize_t) -1 == rv
890 && API_POSIX_EINTR == api_posix_errno);
891 if((api_posix_ssize_t) -1 == rv)
899 api_posix_free((
void*) buf);
901 log_add(server[handle].lfs, eob);
906 do { rv = srv_send(handle, &eob[i], len - i); }
907 while((api_posix_ssize_t)-1 == rv
908 && API_POSIX_EINTR == api_posix_errno);
909 if((api_posix_ssize_t) -1 == rv)
920 res = transport_flush(handle, 0);
921 if(0 > res) { res = -2; }
941 static int exec_cmd(
int handle,
enum nntp_cmd command,
942 struct nntp_response* response, ...)
947 api_posix_ssize_t rv;
952 char article_asc[17];
956 const char*
data = NULL;
969 va_start(ap, response);
975 strcpy(cmd,
"QUIT\r\n");
978 case NNTP_CMD_CAPABILITIES:
981 strcpy(cmd,
"CAPABILITIES\r\n");
984 case NNTP_CMD_MODE_READER:
987 strcpy(cmd,
"MODE READER\r\n");
990 case NNTP_CMD_MAXARTNUM:
992 strcpy(cmd,
"MAXARTNUM ");
993 s = va_arg(ap,
const char*);
995 strncat(cmd, s, len);
999 case NNTP_CMD_COMPRESS:
1001 strcpy(cmd,
"COMPRESS ");
1002 s = va_arg(ap,
const char*);
1004 strncat(cmd, s, len);
1005 strcat(cmd,
"\r\n");
1011 if(!(server[handle].capabilities & NNTP_CAPA_LIST))
1019 if(1U < server[handle].version)
1021 strcpy(cmd,
"LIST ACTIVE\r\n");
1023 else { strcpy(cmd,
"LIST\r\n"); }
1027 case NNTP_CMD_LIST_NEWSGROUPS:
1030 if(!(server[handle].capabilities & NNTP_CAPA_LIST))
1035 else { strcpy(cmd,
"LIST NEWSGROUPS\r\n"); }
1038 case NNTP_CMD_LIST_MOTD:
1041 if(!(server[handle].capabilities & NNTP_CAPA_LIST_MOTD))
1043 PRINT_ERROR(
"Server has no LIST MOTD capability");
1046 else { strcpy(cmd,
"LIST MOTD\r\n"); }
1049 case NNTP_CMD_LIST_DISTRIB_PATS:
1052 if(!(server[handle].capabilities & NNTP_CAPA_LIST_DISTRIB_PATS))
1054 PRINT_ERROR(
"Server has no LIST DISTRIB.PATS capability");
1057 else { strcpy(cmd,
"LIST DISTRIB.PATS\r\n"); }
1060 case NNTP_CMD_LIST_SUBSCRIPTIONS:
1064 if(!(server[handle].capabilities & NNTP_CAPA_LIST_SUBSCRIPTIONS))
1066 PRINT_ERROR(
"Server has no LIST SUBSCRIPTIONS capability");
1071 { strcpy(cmd,
"LIST SUBSCRIPTIONS\r\n"); }
1074 case NNTP_CMD_LIST_OVERVIEW_FMT:
1077 if(!(server[handle].capabilities & NNTP_CAPA_OVER))
1082 else { strcpy(cmd,
"LIST OVERVIEW.FMT\r\n"); }
1085 case NNTP_CMD_GROUP:
1087 strcpy(cmd,
"GROUP ");
1088 s = va_arg(ap,
const char*);
1092 PRINT_ERROR(
"Value of NNTP_LINELENGTHMAX too small");
1096 strncat(cmd, s, len);
1097 strcat(cmd,
"\r\n");
1103 if(!(server[handle].capabilities & NNTP_CAPA_OVER))
1110 strcpy(cmd,
"OVER ");
1121 PRINT_ERROR(
"Value of NNTP_LINELENGTHMAX too small");
1125 strncat(cmd, article_asc, len);
1136 PRINT_ERROR(
"Value of NNTP_LINELENGTHMAX too small");
1140 strncat(cmd, article_asc, len);
1141 strcat(cmd,
"\r\n");
1145 case NNTP_CMD_ARTICLE_BY_MID:
1147 strcpy(cmd,
"ARTICLE ");
1148 s = va_arg(ap,
const char*);
1152 PRINT_ERROR(
"Value of NNTP_LINELENGTHMAX too small");
1156 strncat(cmd, s, len);
1157 strcat(cmd,
"\r\n");
1160 case NNTP_CMD_ARTICLE:
1171 if(NNTP_CMD_HEAD == command) { strcpy(cmd,
"HEAD "); }
1172 else if(NNTP_CMD_BODY == command) { strcpy(cmd,
"BODY "); }
1173 else { strcpy(cmd,
"ARTICLE "); }
1176 PRINT_ERROR(
"Value of NNTP_LINELENGTHMAX too small");
1180 strncat(cmd, article_asc, len);
1181 strcat(cmd,
"\r\n");
1187 if(!(server[handle].capabilities & NNTP_CAPA_POST))
1194 strcpy(cmd,
"POST\r\n");
1195 data = va_arg(ap,
const char*);
1200 #if CFG_USE_TLS || CFG_NNTP_AUTH_UNENCRYPTED
1201 case NNTP_CMD_AUTHINFO_USER:
1204 strcpy(cmd,
"AUTHINFO USER ");
1205 len = strlen(server[handle].user);
1208 PRINT_ERROR(
"Value of NNTP_LINELENGTHMAX too small");
1212 strncat(cmd, server[handle].user, len);
1213 strcat(cmd,
"\r\n");
1216 case NNTP_CMD_AUTHINFO_PASS:
1219 strcpy(cmd,
"AUTHINFO PASS ");
1220 len = strlen(server[handle].passwd);
1223 PRINT_ERROR(
"Value of NNTP_LINELENGTHMAX too small");
1227 strncat(cmd, server[handle].passwd, len);
1228 strcat(cmd,
"\r\n");
1234 PRINT_ERROR(
"Request to execute unknown command");
1244 if(NNTP_CMD_AUTHINFO_PASS == command)
1246 log_add(server[handle].lfs,
"[=>] AUTHINFO PASS ********\n");
1251 log_add(server[handle].lfs,
"[=>] ");
1252 log_add(server[handle].lfs, cmd);
1259 do { rv = srv_send(handle, &cmd[i], len - i); }
1260 while((api_posix_ssize_t) -1 == rv
1261 && API_POSIX_EINTR == api_posix_errno);
1262 if((api_posix_ssize_t) -1 == rv)
1273 if(NNTP_CMD_QUIT == command)
1276 res = transport_flush(handle, 1);
1280 res = transport_flush(handle, 0);
1292 if(!res) { res = recv_reply(handle, command, response); }
1298 if(res || auth) {
break; }
1299 else if(480U != response->status) { retry = 0; }
1303 if(exec_auth(handle))
1306 log_add(server[handle].lfs,
"[Authentication failed]\n");
1309 else { res = refresh_capabilities(handle); }
1312 while(!res && retry--);
1315 if(!res && post && 3U == response->code1 && 4U == response->code2)
1318 res = send_multiline_data_block(handle,
data);
1321 if(!res) { res = recv_reply(handle, command, response); }
1331 static int exec_auth(
int handle)
1335 struct nntp_response* response = NULL;
1338 rv = create_response(&response);
1339 if(rv) {
PRINT_ERROR(
"Cannot allocate memory for response"); }
1343 switch(server[handle].auth)
1347 PRINT_ERROR(
"Error: Authentication requested more than once");
1352 PRINT_ERROR(
"Authentication required but disabled");
1358 if(!(server[handle].capabilities & NNTP_CAPA_AUTHINFO_USER))
1360 PRINT_ERROR(
"Server has no AUTHINFO USER capability");
1364 #if CFG_USE_TLS || CFG_NNTP_AUTH_UNENCRYPTED
1366 rv = exec_cmd(handle, NNTP_CMD_AUTHINFO_USER, response);
1367 if(!rv && 381U == response->status)
1369 rv = exec_cmd(handle, NNTP_CMD_AUTHINFO_PASS, response);
1370 if(!rv && 281U == response->status)
1373 server[handle].auth = -1;
1382 PRINT_ERROR(
"No TLS support (required for authentication)");
1389 PRINT_ERROR(
"Authentication algorithm not supported");
1395 destroy_response(&response);
1410 #if !CFG_CMPR_DISABLE
1411 static int exec_compress(
int handle)
1415 struct nntp_response* response = NULL;
1416 const char* alg = NULL;
1419 rv = create_response(&response);
1420 if(rv) {
PRINT_ERROR(
"Cannot allocate memory for response"); }
1424 if(!(server[handle].capabilities & NNTP_CAPA_COMPRESS))
1426 PRINT_ERROR(
"Server has no COMPRESS capability (bug)");
1431 if(server[handle].compress_algs & NNTP_COMPRESS_DEFLATE)
1440 PRINT_ERROR(
"No matching compression algorithm available");
1446 rv = exec_cmd(handle, NNTP_CMD_COMPRESS, response, alg);
1447 if(!rv && 206U == response->status)
1451 "[Using COMPRESS (RFC 8054) extension]\n");
1458 destroy_response(&response);
1467 if(NULL == server[handle].compress_stream) { res = -1; }
1468 else { server[handle].compress_active = 1; }
1472 if(res) {
PRINT_ERROR(
"Enabling compression failed"); }
1482 static int refresh_capabilities(
int handle)
1485 struct nntp_response* response = NULL;
1492 unsigned int version_old = server[handle].version;
1495 res = create_response(&response);
1503 if(1U == version_old) { res = 0; }
1506 res = exec_cmd(handle, NNTP_CMD_CAPABILITIES, response);
1510 if(101U != response->status)
1514 "[Switch to NNTP V1 (RFC 977) mode]\n");
1515 server[handle].version = 1;
1517 server[handle].capabilities = NNTP_CAPA_READER | NNTP_CAPA_LIST
1519 | NNTP_CAPA_AUTHINFO_USER;
1524 len = strlen(response->content);
1525 for(i = 0; i < len; ++i)
1527 response->content[i] = (char)
1528 toupper((
int) response->content[i]);
1534 rv = sscanf(response->content,
"VERSION %u %u", &v[0], &v[1]);
1537 PRINT_ERROR(
"Protocol error: No version information");
1543 for(i = 0; i < (size_t) rv; ++i)
1545 if(v[i] > ver) { ver = v[i]; }
1546 if(2U == ver) {
break; }
1550 if(2U != version_old)
1553 "[Switch to NNTP V2 (RFC 3977) mode]\n");
1555 server[handle].version = 2U;
1562 "[Advertised protocol not supported]\n");
1564 if(1U != version_old)
1567 "[Switch to NNTP V1 (RFC 977) mode]\n");
1569 server[handle].version = 1U;
1571 server[handle].capabilities = NNTP_CAPA_READER
1574 | NNTP_CAPA_AUTHINFO_USER;
1580 server[handle].capabilities = 0;
1581 if(NULL != strstr(response->content,
"\nMODE-READER"))
1583 server[handle].capabilities |= NNTP_CAPA_MODE_READER;
1585 if(NULL != strstr(response->content,
"\nREADER"))
1587 server[handle].capabilities |= NNTP_CAPA_READER;
1589 p = strstr(response->content,
"\nMAXARTNUM");
1592 i = 0;
while(p[++i])
1596 for (j = i, len = 0; i + (size_t) 20 > j; ++j)
1601 &server[handle].maxartnum, &p[i], len);
1610 >= server[handle].maxartnum)
1613 server[handle].maxartnum = 0;
1618 server[handle].capabilities
1619 |= NNTP_CAPA_MAXARTNUM;
1625 p = strstr(response->content,
"\nLIST");
1628 server[handle].capabilities |= NNTP_CAPA_LIST;
1629 i = 0;
while(p[++i])
1631 if(
'\n' == p[i]) { p[i] = 0;
break; }
1633 if(NULL != strstr(p,
"MOTD"))
1635 server[handle].capabilities |= NNTP_CAPA_LIST_MOTD;
1637 if(NULL != strstr(p,
"DISTRIB.PATS"))
1641 server[handle].capabilities
1642 |= NNTP_CAPA_LIST_DISTRIB_PATS;
1645 if(NULL != strstr(p,
"SUBSCRIPTIONS"))
1647 server[handle].capabilities
1648 |= NNTP_CAPA_LIST_SUBSCRIPTIONS;
1652 if(NULL != strstr(response->content,
"\nOVER"))
1656 server[handle].capabilities |= NNTP_CAPA_OVER;
1659 if(NULL != strstr(response->content,
"\nPOST"))
1661 server[handle].capabilities |= NNTP_CAPA_POST;
1663 p = strstr(response->content,
"\nAUTHINFO");
1666 i = 0;
while(p[++i])
1668 if(
'\n' == p[i]) { p[i] = 0;
break; }
1670 if(NULL != strstr(p,
"USER"))
1672 server[handle].capabilities
1673 |= NNTP_CAPA_AUTHINFO_USER;
1677 p = strstr(response->content,
"\nCOMPRESS");
1680 i = 0;
while(p[++i])
1682 if(
'\n' == p[i]) { p[i] = 0;
break; }
1684 if(NULL != strstr(p,
"DEFLATE"))
1686 server[handle].capabilities |= NNTP_CAPA_COMPRESS;
1687 server[handle].compress_algs
1688 |= NNTP_COMPRESS_DEFLATE;
1691 if(!(server[handle].capabilities & NNTP_CAPA_COMPRESS))
1694 "algorithms not supported");
1705 destroy_response(&response);
1714 static int refresh_overview_format(
int handle)
1717 struct nntp_response* response = NULL;
1724 size_t newsgroups_available = 0;
1727 if(!(NNTP_CAPA_OVER & server[handle].capabilities)) { res = 0; }
1731 res = create_response(&response);
1734 res = exec_cmd(handle, NNTP_CMD_LIST_OVERVIEW_FMT, response);
1737 if(215U != response->status)
1740 "[Reading overview format failed]\n");
1746 len = strlen(response->content);
1747 for(i = 0; i < len; ++i)
1749 response->content[i] = (char)
1750 toupper((
int) response->content[i]);
1753 bol = response->content;
1766 string =
"SUBJECT:";
1767 if(strncmp(bol,
string, strlen(
string))) { error = 1; }
1773 if(strncmp(bol,
string, strlen(
string))) { error = 1; }
1779 if(strncmp(bol,
string, strlen(
string))) { error = 1; }
1784 string =
"MESSAGE-ID:";
1785 if(strncmp(bol,
string, strlen(
string))) { error = 1; }
1790 string =
"REFERENCES:";
1791 if(strncmp(bol,
string, strlen(
string))) { error = 1; }
1797 if(strncmp(bol,
string, strlen(
string)))
1800 if(strncmp(bol,
string, strlen(
string)))
1810 if(strncmp(bol,
string, strlen(
string)))
1813 if(strncmp(bol,
string, strlen(
string)))
1822 string =
"NEWSGROUPS:FULL";
1823 if(!strncmp(bol,
string, strlen(
string)))
1827 "[Newsgroups header field "
1828 "available from overview]\n");
1829 newsgroups_available = field;
1841 bol = strchr(bol, (
int)
'\n');
1845 if(7 > field) { res = -1; }
1858 destroy_response(&response);
1864 server[handle].over_newsgroups = newsgroups_available;
1874 static int switch_mode_reader(
int handle)
1877 struct nntp_response* response = NULL;
1879 if(NNTP_CAPA_MODE_READER & server[handle].capabilities)
1882 res = create_response(&response);
1885 res = exec_cmd(handle, NNTP_CMD_MODE_READER, response);
1889 res = refresh_capabilities(handle);
1890 if(!(NNTP_CAPA_READER & server[handle].capabilities))
1893 PRINT_ERROR(
"Protocol error: No READER capability "
1894 "after (advertised) mode switch");
1900 destroy_response(&response);
1910 static int negotiate_maxartnum(
int handle)
1913 struct nntp_response* response = NULL;
1918 if(NNTP_CAPA_MAXARTNUM & server[handle].capabilities)
1921 #if ULONG_MAX < NNTP_ANUM_T_MAX
1922 PRINT_ERROR(
"Maximum article number does not fit in largest data type");
1925 if (maxartnum > server[handle].maxartnum)
1927 maxartnum = server[handle].maxartnum;
1930 "%lu", (
unsigned long int) maxartnum);
1933 PRINT_ERROR(
"Maximum article number string generation failed");
1940 res = create_response(&response);
1943 res = exec_cmd(handle, NNTP_CMD_MAXARTNUM, response, argument);
1946 if(2U != response->code1)
1949 PRINT_ERROR(
"Maximum article number negotiation failed");
1955 res = refresh_capabilities(handle);
1956 if(NNTP_CAPA_MAXARTNUM & server[handle].capabilities)
1959 PRINT_ERROR(
"Protocol error: MAXARTNUM capability still "
1960 "advertised after negotiation");
1967 destroy_response(&response);
1978 static int get_distrib_pats(
int handle)
1981 struct nntp_response* response = NULL;
1984 server[handle].distrib_pats_len = 0;
1985 if(NULL != server[handle].distrib_pats)
1987 api_posix_free((
void*) server[handle].distrib_pats);
1989 server[handle].distrib_pats = NULL;
1992 if(NNTP_CAPA_LIST_DISTRIB_PATS & server[handle].capabilities)
1995 res = create_response(&response);
2000 res = exec_cmd(handle, NNTP_CMD_LIST_DISTRIB_PATS, response);
2004 if(2U != response->code1)
2008 if(503U == response->status)
2010 PRINT_ERROR(
"Server reported that no distribution patterns"
2013 server[handle].distrib_pats_len = 1;
2020 server[handle].distrib_pats_len = response->bufsize;
2021 server[handle].distrib_pats = response->content;
2023 response->content = NULL;
2028 destroy_response(&response);
2084 int nntp_open(
int* handle,
const char* servername,
const char* service,
2085 const char* logfile,
int enc,
int auth, ...)
2089 int af = API_POSIX_AF_UNSPEC;
2090 struct nntp_response* response = NULL;
2093 #if CFG_USE_TLS || !CFG_CMPR_DISABLE
2098 const char* sni = NULL;
2105 #if CFG_USE_TLS || CFG_NNTP_AUTH_UNENCRYPTED
2116 if(0 > enc || 2 < enc)
2118 PRINT_ERROR(
"Requested encryption algorithm not supported");
2121 else if(0 > auth || 1 < auth)
2123 PRINT_ERROR(
"Requested authentication algorithm not supported");
2126 #if !CFG_NNTP_AUTH_UNENCRYPTED
2127 else if(1 == auth && !enc)
2129 PRINT_ERROR(
"Encryption required for selected authentication algorithm");
2135 if(!res) { res = create_response(&response); }
2140 res = get_handle(handle);
2143 server[*handle].auth = auth;
2144 #if CFG_USE_TLS || CFG_NNTP_AUTH_UNENCRYPTED
2148 immed = va_arg(ap,
int);
2150 s1 = va_arg(ap,
const char*);
2152 s2 = (
char*) api_posix_malloc(++len);
2153 if(NULL == s2) { res = -1; }
2156 memcpy(s2, s1, len);
2157 server[*handle].user = s2;
2158 s1 = va_arg(ap,
const char*);
2160 s2 = (
char*) api_posix_malloc(++len);
2161 if(NULL == s2) { res = -1; }
2164 memcpy(s2, s1, len);
2165 server[*handle].passwd = s2;
2177 if(!res) { server[*handle].lfs = fs; }
2183 log_add(server[*handle].lfs,
"[Using NNTP protocol driver]\n");
2184 log_add(server[*handle].lfs,
"[=> Connect to ");
2185 log_add(server[*handle].lfs, servername);
2186 api_posix_snprintf(sbuf, 7,
":%s", service);
2187 log_add(server[*handle].lfs, sbuf);
2188 log_add(server[*handle].lfs,
"]\n");
2189 res =
inet_connect(&server[*handle].sd, &af, servername, service);
2199 if(2 == enc) { weak = 1; }
2201 res =
tls_open(server[*handle].sd, &server[*handle].eco, weak, sni);
2204 PRINT_ERROR(
"Failed to establish encryption layer");
2208 log_add(server[*handle].lfs,
"[Established encrypted connection");
2212 log_add(server[*handle].lfs,
" using ");
2213 log_add(server[*handle].lfs, pv);
2214 log_add(server[*handle].lfs,
" protocol with cipher suite ");
2215 log_add(server[*handle].lfs, cs);
2217 log_add(server[*handle].lfs,
"]\n");
2225 log_add(server[*handle].lfs,
"[<= Expect X509 certificate]\n");
2228 log_add(server[*handle].lfs, certs);
2235 "[Server certificate verification failed]\n");
2236 if(-2 == rv) { res = rv; }
else { res = -1; }
2241 "[Server certificate verification successful]\n");
2251 if(!res) { res = recv_reply(*handle, NNTP_CMD_INIT, response); }
2254 destroy_response(&response);
2257 if(!res) { res = refresh_capabilities(*handle); }
2260 if(!res) { res = switch_mode_reader(*handle); }
2268 PRINT_ERROR(
"TLS module has requested to close the connection");
2275 server[*handle].connected = 1;
2277 #if CFG_USE_TLS || CFG_NNTP_AUTH_UNENCRYPTED
2279 if(1 == auth && immed)
2281 if(exec_auth(*handle))
2284 log_add(server[*handle].lfs,
"[Authentication failed]\n");
2287 else { res = refresh_capabilities(*handle); }
2292 if(!res) { (void) negotiate_maxartnum(*handle); }
2295 if(!res && 0 >= server[*handle].auth)
2298 if(server[*handle].capabilities & NNTP_CAPA_COMPRESS)
2300 #if !CFG_CMPR_DISABLE
2304 rv = exec_compress(*handle);
2309 "[No matching compression algorithm available]\n");
2314 log_add(server[*handle].lfs,
"[Enabling compression failed]\n");
2316 else { res = refresh_capabilities(*handle); }
2322 "[Compression negotiation disabled by user]\n");
2326 "[Compression disabled by configuration]\n");
2335 res = refresh_overview_format(*handle);
2338 PRINT_ERROR(
"Reading overview format failed of format invalid");
2345 if(get_distrib_pats(*handle))
2347 PRINT_ERROR(
"Reading distribution patterns failed");
2367 struct nntp_response* response = NULL;
2375 rv = create_response(&response);
2376 if(!rv) { exec_cmd(*handle, NNTP_CMD_QUIT, response); }
2377 destroy_response(&response);
2379 #if !CFG_CMPR_DISABLE
2381 if(server[*handle].compress_active)
2385 log_add(server[*handle].lfs,
"[Compression layer terminated]\n");
2390 if(NULL != server[*handle].eco)
2393 log_add(server[*handle].lfs,
"[Encryption layer terminated]\n");
2395 # if CFG_USE_OPENSSL_API_1_1 && !CFG_USE_OPENSSL_API_3
2396 api_posix_free((
void*) server[*handle].peekbuf);
2399 #if CFG_USE_TLS || CFG_NNTP_AUTH_UNENCRYPTED
2400 api_posix_free((
void*) server[*handle].user);
2401 api_posix_free((
void*) server[*handle].passwd);
2405 api_posix_free((
void*) server[*handle].distrib_pats);
2407 if(NULL != server[*handle].lfs)
2409 log_add(server[*handle].lfs,
"[Close connection]\n\n");
2413 if(-1 != server[*handle].sd) {
inet_close(&server[*handle].sd); }
2414 server[*handle].connected = 0;
2441 if(server[handle].capabilities & NNTP_CAPA_LIST_MOTD) { res = 1; }
2470 struct nntp_response* response = NULL;
2473 res = create_response(&response);
2476 if(!res) { res = exec_cmd(handle, NNTP_CMD_LIST_MOTD, response); }
2479 if(2U != response->code1)
2481 if(503U == response->status)
2483 PRINT_ERROR(
"Server reported that no MOTD is maintained");
2490 *len = response->bufsize;
2491 *
data = response->content;
2493 response->content = NULL;
2498 destroy_response(&response);
2535 if(NULL != len) { *len = 0; }
2536 if(server[handle].capabilities & NNTP_CAPA_LIST_DISTRIB_PATS)
2539 if(server[handle].distrib_pats_len)
2542 if(NULL == server[handle].distrib_pats) { res = 1; }
2545 *
data = server[handle].distrib_pats;
2546 if(NULL != len) { *len = server[handle].distrib_pats_len; }
2575 if(server[handle].capabilities & NNTP_CAPA_LIST_SUBSCRIPTIONS)
2607 struct nntp_response* response = NULL;
2610 res = create_response(&response);
2613 if(!res) { res = exec_cmd(handle, NNTP_CMD_LIST_SUBSCRIPTIONS, response); }
2616 if(2U != response->code1)
2618 if(503U == response->status)
2620 PRINT_ERROR(
"Server reported that no SUBSCRIPTIONS are maintained");
2627 *len = response->bufsize;
2628 *
data = response->content;
2630 response->content = NULL;
2635 destroy_response(&response);
2665 ls = strlen(
name) + (size_t) 1;
2670 strcpy(&((
char*) gd)[l],
name);
2671 gd->name = &((
char*) gd)[l];
2706 struct nntp_response* response = NULL;
2707 char* content = NULL;
2729 res = create_response(&response);
2734 res = exec_cmd(handle, NNTP_CMD_LIST, response);
2736 if (!res && NULL == response->content) { res = -1; }
2745 + response->bufsize);
2748 PRINT_ERROR(
"Memory allocation for group list failed");
2754 content = (
void*) &(*p)[response->lines];
2755 memcpy((
void*) content, (
void*) response->content, response->bufsize);
2760 destroy_response(&response);
2795 c = (int) content[i];
2830 if(0x20 == c) {
break; }
2837 rv = parse_number(c, wm, &wm_len, &n);
2838 if(-1 == rv) { field = -1;
break; }
2858 if(0x20 == c) {
break; }
2868 if(
'n' == c) pa = 0;
2882 while((
char) 0x0A != content[i++]);
2887 if (!gi && !content[i])
2895 PRINT_ERROR(
"Unsupported entry in group list ignored");
2902 printf(
"%lu: %s %lu %lu %d\n", (
unsigned long int) gi,
2906 (*p)[gi].name = &content[
name];
2962 struct nntp_response* response = NULL;
2963 char* content = NULL;
2977 res = create_response(&response);
2980 if(!res) { res = exec_cmd(handle, NNTP_CMD_LIST_NEWSGROUPS, response); }
2985 if(2U != response->code1)
2987 if(503U == response->status)
2989 PRINT_ERROR(
"Server reported that descriptions are not maintained");
2995 if (!res && NULL == response->content) { res = -1; }
3003 + response->bufsize);
3006 PRINT_ERROR(
"Memory allocation for group information failed");
3012 content = (
void*) &(*p)[response->lines];
3013 memcpy((
void*) content, (
void*) response->content, response->bufsize);
3018 destroy_response(&response);
3042 c = (int) content[i];
3050 if(0x20 == c || 0x09 == c)
3060 if(0x20 == c || 0x09 == c) {
break; };
3086 while((
char) 0x0A != content[i++]);
3092 PRINT_ERROR(
"Unsupported entry in group label list ignored");
3098 printf(
"%lu: %s %s\n", (
unsigned long int) gi,
3102 (*p)[gi].name = &content[
name];
3104 (*p)[gi].label = &content[
label];
3149 struct nntp_response* response = NULL;
3160 unsigned int flags = 0;
3162 if(NULL != gd) { *gd = NULL; }
3165 res = create_response(&response);
3183 if(!res) { res = exec_cmd(handle, NNTP_CMD_GROUP, response, name); }
3188 if(2U != response->code1)
3197 if (NULL == *gd) { res = -1; }
3201 (*gd)->flags = flags;
3203 s = &response->msg[4];
3204 while(s[i] && !abort)
3212 if(0x20 == c) {
break; }
3219 rv = parse_number(c, wm, &wm_len, &n);
3220 if(-1 == rv) { field = -1;
break; }
3231 if(0x20 == c) {
break; }
3238 rv = parse_number(c, wm, &wm_len, &n);
3239 if(-1 == rv) { field = -1;
break; }
3252 if((*gd)->lwm > (*gd)->hwm)
3281 destroy_response(&response);
3284 if(res && NULL != gd) { api_posix_free(*gd); *gd = NULL; }
3309 if(server[handle].capabilities & NNTP_CAPA_OVER) { res = 1; }
3340 *index = server[handle].over_newsgroups;
3369 char**
data,
size_t* len)
3372 struct nntp_response* response = NULL;
3375 res = create_response(&response);
3378 if(!res) { res = exec_cmd(handle, NNTP_CMD_OVER, response, &first, &last); }
3381 if(2U != response->code1) { res = -1; }
3385 *len = response->bufsize;
3386 *
data = response->content;
3388 response->content = NULL;
3393 destroy_response(&response);
3419 char** article,
size_t* len)
3422 struct nntp_response* response = NULL;
3425 res = create_response(&response);
3428 if(!res) { res = exec_cmd(handle, NNTP_CMD_ARTICLE_BY_MID, response, mid); }
3431 if(2U != response->code1) { res = -1; }
3435 *len = response->bufsize;
3436 *article = response->content;
3438 response->content = NULL;
3443 destroy_response(&response);
3472 struct nntp_response* response = NULL;
3475 res = create_response(&response);
3478 if(!res) { res = exec_cmd(handle, NNTP_CMD_ARTICLE, response,
id); }
3481 if(2U != response->code1) { res = -1; }
3485 *len = response->bufsize;
3486 *article = response->content;
3488 response->content = NULL;
3493 destroy_response(&response);
3519 char** header,
size_t* len)
3522 struct nntp_response* response = NULL;
3525 res = create_response(&response);
3528 if(!res) { res = exec_cmd(handle, NNTP_CMD_HEAD, response,
id); }
3531 if(2U != response->code1) { res = -1; }
3535 *len = response->bufsize;
3536 *header = response->content;
3538 response->content = NULL;
3543 destroy_response(&response);
3572 struct nntp_response* response = NULL;
3575 res = create_response(&response);
3578 if(!res) { res = exec_cmd(handle, NNTP_CMD_BODY, response,
id); }
3581 if(2U != response->code1) { res = -1; }
3585 *len = response->bufsize;
3586 *body = response->content;
3588 response->content = NULL;
3593 destroy_response(&response);
3618 struct nntp_response* response = NULL;
3621 res = create_response(&response);
3624 if(!res) { res = exec_cmd(handle, NNTP_CMD_POST, response, article); }
3627 if(2U != response->code1) { res = -1; }
3631 destroy_response(&response);