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;
171 # if CFG_USE_OPENSSL_API_1_1 && !CFG_USE_OPENSSL_API_3
202 static int exec_auth(
int);
203 static int refresh_capabilities(
int);
204 static int refresh_overview_format(
int);
216 static int parse_number(
int c,
char wm[16],
int* wm_len,
nntp_anum_t* n)
220 if(0x20 == c && *wm_len)
232 if(!(0x30 <= c && 0x39 >= c))
234 PRINT_ERROR(
"Invalid character in water mark field");
243 wm[(*wm_len)++] = (char) c;
253 static int get_handle(
int* handle)
261 if(server[i].connected)
continue;
265 server[i].lfs = NULL;
266 server[i].version = 0;
267 server[i].capabilities = 0;
268 server[i].over_newsgroups = 0;
269 server[i].compress_algs = 0;
270 server[i].compress_active = 0;
271 server[i].compress_stream = NULL;
273 server[i].maxartnum = 0;
274 server[i].distrib_pats = NULL;
275 server[i].distrib_pats_len = 0;
277 server[i].eco = NULL;
278 server[i].user = NULL;
279 server[i].passwd = NULL;
280 # if CFG_USE_OPENSSL_API_1_1 && !CFG_USE_OPENSSL_API_3
281 server[i].peekbuf = NULL;
282 server[i].peekbuf_len = 0;
298 static int transport_flush(
int handle,
int terminate)
302 #if !CFG_CMPR_DISABLE
303 if(server[handle].compress_active)
305 if(!terminate) { res =
cmpr_flush(server[handle].compress_stream); }
321 static posix_ssize_t transport_send(
int handle,
const void* buf,
size_t len,
327 PRINT_ERROR(
"transport_send(): Called with invalid flags");
331 if(NULL != server[handle].eco)
333 return(
tls_send(server[handle].eco, buf, len));
336 return(posix_send(server[handle].sd, buf, len, flags));
343 static posix_ssize_t srv_send(
int handle,
const void* buf,
size_t len)
345 #if !CFG_CMPR_DISABLE
346 if(server[handle].compress_active)
348 return(
cmpr_send(server[handle].compress_stream, buf, len));
353 return(transport_send(handle, buf, len, 0));
365 static posix_ssize_t transport_recv(
int handle,
void* buf,
size_t len,
368 posix_ssize_t res = -1;
371 # if CFG_USE_OPENSSL_API_1_1 && !CFG_USE_OPENSSL_API_3
377 if(POSIX_SSIZE_MAX < len)
379 PRINT_ERROR(
"transport_recv(): Called with invalid data length");
384 if(~(POSIX_MSG_PEEK) & flags)
386 PRINT_ERROR(
"transport_recv(): Called with invalid flags");
391 if(NULL != server[handle].eco)
393 if(POSIX_MSG_PEEK & flags) { peek = 1; }
394 # if CFG_USE_OPENSSL_API_1_1 && !CFG_USE_OPENSSL_API_3
406 if(!peek && !server[handle].peekbuf_len)
408 res =
tls_recv(server[handle].eco, buf, len, 0);
413 if(peek && !server[handle].peekbuf_len)
416 p = (
char*) posix_realloc(server[handle].peekbuf, len);
419 server[handle].peekbuf = p;
421 server[handle].peekbuf, len, 0);
424 server[handle].peekbuf_len = (size_t) res;
430 if(server[handle].peekbuf_len)
432 if(server[handle].peekbuf_len < len)
435 len = server[handle].peekbuf_len;
437 memcpy(buf, server[handle].peekbuf, len);
438 res = (posix_ssize_t) len;
442 p = (
char*) server[handle].peekbuf;
443 memmove(server[handle].peekbuf, (
void*) &p[len],
444 server[handle].peekbuf_len - len);
445 server[handle].peekbuf_len -= len;
451 res =
tls_recv(server[handle].eco, buf, len, peek);
457 res = posix_recv(server[handle].sd, buf, len, flags);
467 static posix_ssize_t srv_recv(
int handle,
void* buf,
size_t len,
int flags)
469 #if !CFG_CMPR_DISABLE
470 if(server[handle].compress_active)
472 return(
cmpr_recv(server[handle].compress_stream, buf, len, flags));
477 return(transport_recv(handle, buf, len, flags));
485 static int create_response(
struct nntp_response** r)
489 *r = (
struct nntp_response*) posix_malloc(
sizeof(
struct nntp_response));
492 PRINT_ERROR(
"Out of memory while allocating response");
499 (*r)->content = NULL;
511 static void destroy_response(
struct nntp_response** r)
515 posix_free((
void*) (*r)->msg);
516 posix_free((
void*) (*r)->content);
517 posix_free((
void*) *r);
531 static int multiline(
unsigned int status,
enum nntp_cmd command)
534 static unsigned int code[12] =
536 100U, 101U, 211U, 215U, 220U, 221U, 222U, 224U, 225U, 230U, 231U,
543 while(code[i]) {
if(status == code[i++]) { res = 1; } }
546 if(211U == status && NNTP_CMD_GROUP == command) { res = 0; }
555 static int recv_multiline_data_block(
int handle,
556 struct nntp_response* response)
575 resp = (
char*) posix_malloc(len);
578 PRINT_ERROR(
"Out of memory while receiving response");
585 while(!res && !eor && len > ri)
589 do { rv = srv_recv(handle, &resp[ri], len - ri, POSIX_MSG_PEEK); }
590 while((posix_ssize_t) -1 == rv && POSIX_EINTR == posix_errno);
591 if((posix_ssize_t) -1 == rv)
609 if(
'\r' == resp[i - 1] &&
'.' == resp[i - 2])
618 if(
'\r' == resp[i - 1] &&
'.' == resp[i - 2]
619 &&
'\n' == resp[i - 3] &&
'\r' == resp[i - 4])
637 do { rv = srv_recv(handle, &resp[ri], i - ri, 0); }
638 while((posix_ssize_t) -1 == rv && POSIX_EINTR == posix_errno);
639 if((posix_ssize_t) -1 == rv)
653 p = (
char*) posix_realloc(resp, len);
656 PRINT_ERROR(
"Out of memory while receiving response");
662 if(!res && !eor) { res = -1; }
663 if(res) { posix_free((
void*) resp); }
672 if(
'.' == resp[0] &&
'.' == resp[1])
674 memmove(&resp[0], &resp[1], --len + (
size_t) 1);
676 for(i = 2; i < len; ++i)
678 if(
'.' == resp[i] &&
'.' == resp[i - 1] && (
char) 10 == resp[i - 2])
680 memmove(&resp[i - 1], &resp[i], len-- - i + (
size_t) 1);
684 if(NULL != response->content) { posix_free((
void*) response->content); }
685 response->content = resp;
686 response->lines = lines;
687 response->bufsize = len + 1;
688 log_add(server[handle].lfs, response->content);
698 static int recv_reply(
int handle,
enum nntp_cmd command,
699 struct nntp_response* response)
711 while(!res && !eol && len > li)
715 do { rv = srv_recv(handle, &resp[li], len - li, POSIX_MSG_PEEK); }
716 while((posix_ssize_t) -1 == rv && POSIX_EINTR == posix_errno);
718 if((posix_ssize_t) 0 >= rv)
729 if(
'\n' == resp[i++])
738 do { rv = srv_recv(handle, &resp[li], i - li, 0); }
739 while((posix_ssize_t) -1 == rv && POSIX_EINTR == posix_errno);
740 if((posix_ssize_t) -1 == rv)
749 if(!res && !eol) { res = -1; }
755 for(i = 0; i < 3; ++i)
757 if((
unsigned char) 9 >= (
unsigned char) resp[i])
759 PRINT_ERROR(
"Protocol error: Invalid response code");
760 log_add(server[handle].lfs,
"[<= Invalid response]\n");
766 response->code1 = (
unsigned char) (resp[0] - (
char) 48);
767 response->code2 = (
unsigned char) (resp[1] - (
char) 48);
768 response->code3 = (
unsigned char) (resp[2] - (
char) 48);
769 if(NULL != response->msg) { posix_free((
void*) response->msg); }
770 response->msg = (
char*) posix_malloc(strlen(resp) + (size_t) 1);
771 if(NULL == response->msg)
773 PRINT_ERROR(
"Out of memory while receiving response");
776 else { strcpy(response->msg, resp); }
777 response->content = NULL;
779 log_add(server[handle].lfs,
"[<=] ");
780 log_add(server[handle].lfs, response->msg);
787 response->status = (
unsigned int) response->code3;
788 response->status += (
unsigned int) response->code2 * 10U;
789 response->status += (
unsigned int) response->code1 * 100U;
791 if(multiline(response->status, command))
793 log_add(server[handle].lfs,
"[<= Expect multiline data block]\n");
794 res = recv_multiline_data_block(handle, response);
805 static int send_multiline_data_block(
int handle,
const char*
data)
807 static const char eob[] =
".\r\n";
816 if(NULL ==
data) { res = -1; }
819 log_add(server[handle].lfs,
"[=> Send multiline data block]\n");
827 buf = (
char*) posix_malloc((strlen(
data) + (
size_t) 3) * (size_t) 2);
830 PRINT_ERROR(
"Memory allocation failed for dot stuffing");
837 while((c =
data[i++]))
839 if( (13 == (
int) c && 10 != (
int)
data[i])
840 || (10 == (
int) c && 13 != (
int)
data[i - (
size_t) 2] && i) )
844 if(
'.' == c && (
size_t) 3 <= i)
846 if( (10 == (
int)
data[i - (
size_t) 2])
847 && (13 == (
int)
data[i - (
size_t) 3]) )
859 || 10 != (
int) buf[bi - (
size_t) 1]
860 || 13 != (
int) buf[bi - (
size_t) 2] )
863 buf[bi++] = (char) 13;
864 buf[bi++] = (char) 10;
877 do { rv = srv_send(handle, &
data[i], len - i); }
878 while((posix_ssize_t) -1 == rv && POSIX_EINTR == posix_errno);
879 if((posix_ssize_t) -1 == rv)
887 posix_free((
void*) buf);
889 log_add(server[handle].lfs, eob);
894 do { rv = srv_send(handle, &eob[i], len - i); }
895 while((posix_ssize_t)-1 == rv && POSIX_EINTR == posix_errno);
896 if((posix_ssize_t) -1 == rv)
907 res = transport_flush(handle, 0);
908 if(0 > res) { res = -2; }
928 static int exec_cmd(
int handle,
enum nntp_cmd command,
929 struct nntp_response* response, ...)
939 char article_asc[17];
943 const char*
data = NULL;
956 va_start(ap, response);
962 strcpy(cmd,
"QUIT\r\n");
965 case NNTP_CMD_CAPABILITIES:
968 strcpy(cmd,
"CAPABILITIES\r\n");
971 case NNTP_CMD_MODE_READER:
974 strcpy(cmd,
"MODE READER\r\n");
977 case NNTP_CMD_MAXARTNUM:
979 strcpy(cmd,
"MAXARTNUM ");
980 s = va_arg(ap,
const char*);
982 strncat(cmd, s, len);
983 strncat(cmd,
"\r\n", 2);
986 case NNTP_CMD_COMPRESS:
988 strcpy(cmd,
"COMPRESS ");
989 s = va_arg(ap,
const char*);
991 strncat(cmd, s, len);
992 strncat(cmd,
"\r\n", 2);
998 if(!(server[handle].capabilities & NNTP_CAPA_LIST))
1006 if(1U < server[handle].version)
1008 strcpy(cmd,
"LIST ACTIVE\r\n");
1010 else { strcpy(cmd,
"LIST\r\n"); }
1014 case NNTP_CMD_LIST_NEWSGROUPS:
1017 if(!(server[handle].capabilities & NNTP_CAPA_LIST))
1022 else { strcpy(cmd,
"LIST NEWSGROUPS\r\n"); }
1025 case NNTP_CMD_LIST_MOTD:
1028 if(!(server[handle].capabilities & NNTP_CAPA_LIST_MOTD))
1030 PRINT_ERROR(
"Server has no LIST MOTD capability");
1033 else { strcpy(cmd,
"LIST MOTD\r\n"); }
1036 case NNTP_CMD_LIST_DISTRIB_PATS:
1039 if(!(server[handle].capabilities & NNTP_CAPA_LIST_DISTRIB_PATS))
1041 PRINT_ERROR(
"Server has no LIST DISTRIB.PATS capability");
1044 else { strcpy(cmd,
"LIST DISTRIB.PATS\r\n"); }
1047 case NNTP_CMD_LIST_SUBSCRIPTIONS:
1051 if(!(server[handle].capabilities & NNTP_CAPA_LIST_SUBSCRIPTIONS))
1053 PRINT_ERROR(
"Server has no LIST SUBSCRIPTIONS capability");
1058 { strcpy(cmd,
"LIST SUBSCRIPTIONS\r\n"); }
1061 case NNTP_CMD_LIST_OVERVIEW_FMT:
1064 if(!(server[handle].capabilities & NNTP_CAPA_OVER))
1069 else { strcpy(cmd,
"LIST OVERVIEW.FMT\r\n"); }
1072 case NNTP_CMD_GROUP:
1074 strcpy(cmd,
"GROUP ");
1075 s = va_arg(ap,
const char*);
1079 PRINT_ERROR(
"Value of NNTP_LINELENGTHMAX too small");
1083 strncat(cmd, s, len);
1084 strncat(cmd,
"\r\n", 2);
1090 if(!(server[handle].capabilities & NNTP_CAPA_OVER))
1097 strcpy(cmd,
"OVER ");
1108 PRINT_ERROR(
"Value of NNTP_LINELENGTHMAX too small");
1112 strncat(cmd, article_asc, len);
1113 strncat(cmd,
"-", 1);
1123 PRINT_ERROR(
"Value of NNTP_LINELENGTHMAX too small");
1127 strncat(cmd, article_asc, len);
1128 strncat(cmd,
"\r\n", 2);
1132 case NNTP_CMD_ARTICLE_BY_MID:
1134 strcpy(cmd,
"ARTICLE ");
1135 s = va_arg(ap,
const char*);
1139 PRINT_ERROR(
"Value of NNTP_LINELENGTHMAX too small");
1143 strncat(cmd, s, len);
1144 strncat(cmd,
"\r\n", 2);
1147 case NNTP_CMD_ARTICLE:
1158 if(NNTP_CMD_HEAD == command) { strcpy(cmd,
"HEAD "); }
1159 else if(NNTP_CMD_BODY == command) { strcpy(cmd,
"BODY "); }
1160 else { strcpy(cmd,
"ARTICLE "); }
1163 PRINT_ERROR(
"Value of NNTP_LINELENGTHMAX too small");
1167 strncat(cmd, article_asc, len);
1168 strncat(cmd,
"\r\n", 2);
1174 if(!(server[handle].capabilities & NNTP_CAPA_POST))
1181 strcpy(cmd,
"POST\r\n");
1182 data = va_arg(ap,
const char*);
1188 case NNTP_CMD_AUTHINFO_USER:
1191 strcpy(cmd,
"AUTHINFO USER ");
1192 len = strlen(server[handle].user);
1195 PRINT_ERROR(
"Value of NNTP_LINELENGTHMAX too small");
1199 strncat(cmd, server[handle].user, len);
1200 strncat(cmd,
"\r\n", 2);
1203 case NNTP_CMD_AUTHINFO_PASS:
1206 strcpy(cmd,
"AUTHINFO PASS ");
1207 len = strlen(server[handle].passwd);
1210 PRINT_ERROR(
"Value of NNTP_LINELENGTHMAX too small");
1214 strncat(cmd, server[handle].passwd, len);
1215 strncat(cmd,
"\r\n", 2);
1221 PRINT_ERROR(
"Request to execute unknown command");
1231 if(NNTP_CMD_AUTHINFO_PASS == command)
1233 log_add(server[handle].lfs,
"[=>] AUTHINFO PASS ********\n");
1238 log_add(server[handle].lfs,
"[=>] ");
1239 log_add(server[handle].lfs, cmd);
1246 do { rv = srv_send(handle, &cmd[i], len - i); }
1247 while((posix_ssize_t) -1 == rv && POSIX_EINTR == posix_errno);
1248 if((posix_ssize_t) -1 == rv)
1259 if(NNTP_CMD_QUIT == command)
1262 res = transport_flush(handle, 1);
1266 res = transport_flush(handle, 0);
1278 if(!res) { res = recv_reply(handle, command, response); }
1284 if(res || auth) {
break; }
1285 else if(480U != response->status) { retry = 0; }
1289 if(exec_auth(handle))
1292 log_add(server[handle].lfs,
"[Authentication failed]\n");
1295 else { res = refresh_capabilities(handle); }
1298 while(!res && retry--);
1301 if(!res && post && 3U == response->code1 && 4U == response->code2)
1304 res = send_multiline_data_block(handle,
data);
1307 if(!res) { res = recv_reply(handle, command, response); }
1317 static int exec_auth(
int handle)
1321 struct nntp_response* response = NULL;
1324 rv = create_response(&response);
1325 if(rv) {
PRINT_ERROR(
"Cannot allocate memory for response"); }
1329 switch(server[handle].auth)
1333 PRINT_ERROR(
"Error: Authentication requested more than once");
1338 PRINT_ERROR(
"Authentication required but disabled");
1344 if(!(server[handle].capabilities & NNTP_CAPA_AUTHINFO_USER))
1346 PRINT_ERROR(
"Server has no AUTHINFO USER capability");
1352 rv = exec_cmd(handle, NNTP_CMD_AUTHINFO_USER, response);
1353 if(!rv && 381U == response->status)
1355 rv = exec_cmd(handle, NNTP_CMD_AUTHINFO_PASS, response);
1356 if(!rv && 281U == response->status)
1359 server[handle].auth = -1;
1368 PRINT_ERROR(
"No TLS support (required for authentication)");
1375 PRINT_ERROR(
"Authentication algorithm not supported");
1381 destroy_response(&response);
1396 #if !CFG_CMPR_DISABLE
1397 static int exec_compress(
int handle)
1401 struct nntp_response* response = NULL;
1402 const char* alg = NULL;
1405 rv = create_response(&response);
1406 if(rv) {
PRINT_ERROR(
"Cannot allocate memory for response"); }
1410 if(!(server[handle].capabilities & NNTP_CAPA_COMPRESS))
1412 PRINT_ERROR(
"Server has no COMPRESS capability (bug)");
1417 if(server[handle].compress_algs & NNTP_COMPRESS_DEFLATE)
1426 PRINT_ERROR(
"No matching compression algorithm available");
1432 rv = exec_cmd(handle, NNTP_CMD_COMPRESS, response, alg);
1433 if(!rv && 206U == response->status)
1437 "[Using COMPRESS (RFC 8054) extension]\n");
1444 destroy_response(&response);
1453 if(NULL == server[handle].compress_stream) { res = -1; }
1454 else { server[handle].compress_active = 1; }
1458 if(res) {
PRINT_ERROR(
"Enabling compression failed"); }
1468 static int refresh_capabilities(
int handle)
1471 struct nntp_response* response = NULL;
1478 unsigned int version_old = server[handle].version;
1481 res = create_response(&response);
1489 if(1U == version_old) { res = 0; }
1492 res = exec_cmd(handle, NNTP_CMD_CAPABILITIES, response);
1496 if(101U != response->status)
1500 "[Switch to NNTP V1 (RFC 977) mode]\n");
1501 server[handle].version = 1;
1503 server[handle].capabilities = NNTP_CAPA_READER | NNTP_CAPA_LIST
1505 | NNTP_CAPA_AUTHINFO_USER;
1510 len = strlen(response->content);
1511 for(i = 0; i < len; ++i)
1513 response->content[i] = (char)
1514 toupper((
int) response->content[i]);
1520 rv = sscanf(response->content,
"VERSION %u %u", &v[0], &v[1]);
1523 PRINT_ERROR(
"Protocol error: No version information");
1529 for(i = 0; i < (size_t) rv; ++i)
1531 if(v[i] > ver) { ver = v[i]; }
1532 if(2U == ver) {
break; }
1536 if(2U != version_old)
1539 "[Switch to NNTP V2 (RFC 3977) mode]\n");
1541 server[handle].version = 2U;
1548 "[Advertised protocol not supported]\n");
1550 if(1U != version_old)
1553 "[Switch to NNTP V1 (RFC 977) mode]\n");
1555 server[handle].version = 1U;
1557 server[handle].capabilities = NNTP_CAPA_READER
1560 | NNTP_CAPA_AUTHINFO_USER;
1566 server[handle].capabilities = 0;
1567 if(NULL != strstr(response->content,
"\nMODE-READER"))
1569 server[handle].capabilities |= NNTP_CAPA_MODE_READER;
1571 if(NULL != strstr(response->content,
"\nREADER"))
1573 server[handle].capabilities |= NNTP_CAPA_READER;
1575 p = strstr(response->content,
"\nMAXARTNUM");
1578 i = 0;
while(p[++i])
1582 for (j = i, len = 0; i + (size_t) 20 > j; ++j)
1587 &server[handle].maxartnum, &p[i], len);
1596 >= server[handle].maxartnum)
1599 server[handle].maxartnum = 0;
1604 server[handle].capabilities
1605 |= NNTP_CAPA_MAXARTNUM;
1611 p = strstr(response->content,
"\nLIST");
1614 server[handle].capabilities |= NNTP_CAPA_LIST;
1615 i = 0;
while(p[++i])
1617 if(
'\n' == p[i]) { p[i] = 0;
break; }
1619 if(NULL != strstr(p,
"MOTD"))
1621 server[handle].capabilities |= NNTP_CAPA_LIST_MOTD;
1623 if(NULL != strstr(p,
"DISTRIB.PATS"))
1627 server[handle].capabilities
1628 |= NNTP_CAPA_LIST_DISTRIB_PATS;
1631 if(NULL != strstr(p,
"SUBSCRIPTIONS"))
1633 server[handle].capabilities
1634 |= NNTP_CAPA_LIST_SUBSCRIPTIONS;
1638 if(NULL != strstr(response->content,
"\nOVER"))
1642 server[handle].capabilities |= NNTP_CAPA_OVER;
1645 if(NULL != strstr(response->content,
"\nPOST"))
1647 server[handle].capabilities |= NNTP_CAPA_POST;
1649 p = strstr(response->content,
"\nAUTHINFO");
1652 i = 0;
while(p[++i])
1654 if(
'\n' == p[i]) { p[i] = 0;
break; }
1656 if(NULL != strstr(p,
"USER"))
1658 server[handle].capabilities
1659 |= NNTP_CAPA_AUTHINFO_USER;
1663 p = strstr(response->content,
"\nCOMPRESS");
1666 i = 0;
while(p[++i])
1668 if(
'\n' == p[i]) { p[i] = 0;
break; }
1670 if(NULL != strstr(p,
"DEFLATE"))
1672 server[handle].capabilities |= NNTP_CAPA_COMPRESS;
1673 server[handle].compress_algs
1674 |= NNTP_COMPRESS_DEFLATE;
1677 if(!(server[handle].capabilities & NNTP_CAPA_COMPRESS))
1680 "algorithms not supported");
1691 destroy_response(&response);
1700 static int refresh_overview_format(
int handle)
1703 struct nntp_response* response = NULL;
1710 size_t newsgroups_available = 0;
1713 if(!(NNTP_CAPA_OVER & server[handle].capabilities)) { res = 0; }
1717 res = create_response(&response);
1720 res = exec_cmd(handle, NNTP_CMD_LIST_OVERVIEW_FMT, response);
1723 if(215U != response->status)
1726 "[Reading overview format failed]\n");
1732 len = strlen(response->content);
1733 for(i = 0; i < len; ++i)
1735 response->content[i] = (char)
1736 toupper((
int) response->content[i]);
1739 bol = response->content;
1752 string =
"SUBJECT:";
1753 if(strncmp(bol,
string, strlen(
string))) { error = 1; }
1759 if(strncmp(bol,
string, strlen(
string))) { error = 1; }
1765 if(strncmp(bol,
string, strlen(
string))) { error = 1; }
1770 string =
"MESSAGE-ID:";
1771 if(strncmp(bol,
string, strlen(
string))) { error = 1; }
1776 string =
"REFERENCES:";
1777 if(strncmp(bol,
string, strlen(
string))) { error = 1; }
1783 if(strncmp(bol,
string, strlen(
string)))
1786 if(strncmp(bol,
string, strlen(
string)))
1796 if(strncmp(bol,
string, strlen(
string)))
1799 if(strncmp(bol,
string, strlen(
string)))
1808 string =
"NEWSGROUPS:FULL";
1809 if(!strncmp(bol,
string, strlen(
string)))
1813 "[Newsgroups header field "
1814 "available from overview]\n");
1815 newsgroups_available = field;
1827 bol = strchr(bol, (
int)
'\n');
1831 if(7 > field) { res = -1; }
1844 destroy_response(&response);
1850 server[handle].over_newsgroups = newsgroups_available;
1860 static int switch_mode_reader(
int handle)
1863 struct nntp_response* response = NULL;
1865 if(NNTP_CAPA_MODE_READER & server[handle].capabilities)
1868 res = create_response(&response);
1871 res = exec_cmd(handle, NNTP_CMD_MODE_READER, response);
1875 res = refresh_capabilities(handle);
1876 if(!(NNTP_CAPA_READER & server[handle].capabilities))
1879 PRINT_ERROR(
"Protocol error: No READER capability "
1880 "after (advertised) mode switch");
1886 destroy_response(&response);
1896 static int negotiate_maxartnum(
int handle)
1899 struct nntp_response* response = NULL;
1904 if(NNTP_CAPA_MAXARTNUM & server[handle].capabilities)
1907 #if ULONG_MAX < NNTP_ANUM_T_MAX
1908 PRINT_ERROR(
"Maximum article number does not fit in largest data type");
1911 if (maxartnum > server[handle].maxartnum)
1913 maxartnum = server[handle].maxartnum;
1916 "%lu", (
unsigned long int) maxartnum);
1919 PRINT_ERROR(
"Maximum article number string generation failed");
1926 res = create_response(&response);
1929 res = exec_cmd(handle, NNTP_CMD_MAXARTNUM, response, argument);
1932 if(2U != response->code1)
1935 PRINT_ERROR(
"Maximum article number negotiation failed");
1941 res = refresh_capabilities(handle);
1942 if(NNTP_CAPA_MAXARTNUM & server[handle].capabilities)
1945 PRINT_ERROR(
"Protocol error: MAXARTNUM capability still "
1946 "advertised after negotiation");
1953 destroy_response(&response);
1964 static int get_distrib_pats(
int handle)
1967 struct nntp_response* response = NULL;
1970 server[handle].distrib_pats_len = 0;
1971 if(NULL != server[handle].distrib_pats)
1973 posix_free((
void*) server[handle].distrib_pats);
1975 server[handle].distrib_pats = NULL;
1978 if(NNTP_CAPA_LIST_DISTRIB_PATS & server[handle].capabilities)
1981 res = create_response(&response);
1986 res = exec_cmd(handle, NNTP_CMD_LIST_DISTRIB_PATS, response);
1990 if(2U != response->code1)
1994 if(503U == response->status)
1996 PRINT_ERROR(
"Server reported that no distribution patterns"
1999 server[handle].distrib_pats_len = 1;
2006 server[handle].distrib_pats_len = response->bufsize;
2007 server[handle].distrib_pats = response->content;
2009 response->content = NULL;
2014 destroy_response(&response);
2070 int nntp_open(
int* handle,
const char* servername,
const char* service,
2071 const char* logfile,
int enc,
int auth, ...)
2075 int af = POSIX_AF_UNSPEC;
2076 struct nntp_response* response = NULL;
2079 #if CFG_USE_TLS || !CFG_CMPR_DISABLE
2084 const char* sni = NULL;
2100 if(0 > enc || 2 < enc)
2102 PRINT_ERROR(
"Requested encryption algorithm not supported");
2105 else if(0 > auth || 1 < auth)
2107 PRINT_ERROR(
"Requested authentication algorithm not supported");
2110 #if !CFG_NNTP_AUTH_UNENCRYPTED
2111 else if(1 == auth && !enc)
2113 PRINT_ERROR(
"Encryption required for selected authentication algorithm");
2119 if(!res) { res = create_response(&response); }
2124 res = get_handle(handle);
2127 server[*handle].auth = auth;
2132 immed = va_arg(ap,
int);
2134 s1 = va_arg(ap,
const char*);
2136 s2 = (
char*) posix_malloc(++len);
2137 if(NULL == s2) { res = -1; }
2140 strncpy(s2, s1, len);
2141 server[*handle].user = s2;
2142 s1 = va_arg(ap,
const char*);
2144 s2 = (
char*) posix_malloc(++len);
2145 if(NULL == s2) { res = -1; }
2148 strncpy(s2, s1, len);
2149 server[*handle].passwd = s2;
2161 if(!res) { server[*handle].lfs = fs; }
2167 log_add(server[*handle].lfs,
"[Using NNTP protocol driver]\n");
2168 log_add(server[*handle].lfs,
"[=> Connect to ");
2169 log_add(server[*handle].lfs, servername);
2170 posix_snprintf(sbuf, 7,
":%s", service);
2171 log_add(server[*handle].lfs, sbuf);
2172 log_add(server[*handle].lfs,
"]\n");
2173 res =
inet_connect(&server[*handle].sd, &af, servername, service);
2183 if(2 == enc) { weak = 1; }
2185 res =
tls_open(server[*handle].sd, &server[*handle].eco, weak, sni);
2188 PRINT_ERROR(
"Failed to establish encryption layer");
2192 log_add(server[*handle].lfs,
"[Established encrypted connection");
2196 log_add(server[*handle].lfs,
" using ");
2197 log_add(server[*handle].lfs, pv);
2198 log_add(server[*handle].lfs,
" protocol with cipher suite ");
2199 log_add(server[*handle].lfs, cs);
2201 log_add(server[*handle].lfs,
"]\n");
2209 log_add(server[*handle].lfs,
"[<= Expect X509 certificate]\n");
2212 log_add(server[*handle].lfs, certs);
2219 "[Server certificate verification failed]\n");
2220 if(-2 == rv) { res = rv; }
else { res = -1; }
2225 "[Server certificate verification successful]\n");
2235 if(!res) { res = recv_reply(*handle, NNTP_CMD_INIT, response); }
2238 destroy_response(&response);
2241 if(!res) { res = refresh_capabilities(*handle); }
2244 if(!res) { res = switch_mode_reader(*handle); }
2252 PRINT_ERROR(
"TLS module has requested to close the connection");
2259 server[*handle].connected = 1;
2263 if(1 == auth && immed)
2265 if(exec_auth(*handle))
2268 log_add(server[*handle].lfs,
"[Authentication failed]\n");
2271 else { res = refresh_capabilities(*handle); }
2276 if(!res) { (void) negotiate_maxartnum(*handle); }
2279 if(!res && 0 >= server[*handle].auth)
2282 if(server[*handle].capabilities & NNTP_CAPA_COMPRESS)
2284 #if !CFG_CMPR_DISABLE
2288 rv = exec_compress(*handle);
2293 "[No matching compression algorithm available]\n");
2298 log_add(server[*handle].lfs,
"[Enabling compression failed]\n");
2300 else { res = refresh_capabilities(*handle); }
2306 "[Compression negotiation disabled by user]\n");
2310 "[Compression disabled by configuration]\n");
2319 res = refresh_overview_format(*handle);
2322 PRINT_ERROR(
"Reading overview format failed of format invalid");
2329 if(get_distrib_pats(*handle))
2331 PRINT_ERROR(
"Reading distribution patterns failed");
2351 struct nntp_response* response = NULL;
2359 rv = create_response(&response);
2360 if(!rv) { exec_cmd(*handle, NNTP_CMD_QUIT, response); }
2361 destroy_response(&response);
2363 #if !CFG_CMPR_DISABLE
2365 if(server[*handle].compress_active)
2369 log_add(server[*handle].lfs,
"[Compression layer terminated]\n");
2374 if(NULL != server[*handle].eco)
2377 log_add(server[*handle].lfs,
"[Encryption layer terminated]\n");
2379 posix_free((
void*) server[*handle].user);
2380 posix_free((
void*) server[*handle].passwd);
2381 # if CFG_USE_OPENSSL_API_1_1 && !CFG_USE_OPENSSL_API_3
2382 posix_free((
void*) server[*handle].peekbuf);
2386 posix_free((
void*) server[*handle].distrib_pats);
2388 if(NULL != server[*handle].lfs)
2390 log_add(server[*handle].lfs,
"[Close connection]\n\n");
2394 if(-1 != server[*handle].sd) {
inet_close(&server[*handle].sd); }
2395 server[*handle].connected = 0;
2422 if(server[handle].capabilities & NNTP_CAPA_LIST_MOTD) { res = 1; }
2451 struct nntp_response* response = NULL;
2454 res = create_response(&response);
2457 if(!res) { res = exec_cmd(handle, NNTP_CMD_LIST_MOTD, response); }
2460 if(2U != response->code1)
2462 if(503U == response->status)
2464 PRINT_ERROR(
"Server reported that no MOTD is maintained");
2471 *len = response->bufsize;
2472 *
data = response->content;
2474 response->content = NULL;
2479 destroy_response(&response);
2516 if(NULL != len) { *len = 0; }
2517 if(server[handle].capabilities & NNTP_CAPA_LIST_DISTRIB_PATS)
2520 if(server[handle].distrib_pats_len)
2523 if(NULL == server[handle].distrib_pats) { res = 1; }
2526 *
data = server[handle].distrib_pats;
2527 if(NULL != len) { *len = server[handle].distrib_pats_len; }
2556 if(server[handle].capabilities & NNTP_CAPA_LIST_SUBSCRIPTIONS)
2588 struct nntp_response* response = NULL;
2591 res = create_response(&response);
2594 if(!res) { res = exec_cmd(handle, NNTP_CMD_LIST_SUBSCRIPTIONS, response); }
2597 if(2U != response->code1)
2599 if(503U == response->status)
2601 PRINT_ERROR(
"Server reported that no SUBSCRIPTIONS are maintained");
2608 *len = response->bufsize;
2609 *
data = response->content;
2611 response->content = NULL;
2616 destroy_response(&response);
2646 ls = strlen(
name) + (size_t) 1;
2651 strcpy(&((
char*) gd)[l],
name);
2652 gd->name = &((
char*) gd)[l];
2687 struct nntp_response* response = NULL;
2688 char* content = NULL;
2710 res = create_response(&response);
2715 res = exec_cmd(handle, NNTP_CMD_LIST, response);
2717 if (!res && NULL == response->content) { res = -1; }
2726 + response->bufsize);
2729 PRINT_ERROR(
"Memory allocation for group list failed");
2735 content = (
void*) &(*p)[response->lines];
2736 memcpy((
void*) content, (
void*) response->content, response->bufsize);
2741 destroy_response(&response);
2776 c = (int) content[i];
2811 if(0x20 == c) {
break; }
2817 rv = parse_number(c, wm, &wm_len, &n);
2818 if(-1 == rv) { field = -1;
break; }
2838 if(0x20 == c) {
break; }
2847 if(
'n' == c) pa = 0;
2861 while((
char) 0x0A != content[i++]);
2866 if (!gi && !content[i])
2874 PRINT_ERROR(
"Unsupported entry in group list ignored");
2881 printf(
"%lu: %s %lu %lu %d\n", (
unsigned long int) gi,
2885 (*p)[gi].name = &content[
name];
2941 struct nntp_response* response = NULL;
2942 char* content = NULL;
2956 res = create_response(&response);
2959 if(!res) { res = exec_cmd(handle, NNTP_CMD_LIST_NEWSGROUPS, response); }
2964 if(2U != response->code1)
2966 if(503U == response->status)
2968 PRINT_ERROR(
"Server reported that descriptions are not maintained");
2974 if (!res && NULL == response->content) { res = -1; }
2982 + response->bufsize);
2985 PRINT_ERROR(
"Memory allocation for group information failed");
2991 content = (
void*) &(*p)[response->lines];
2992 memcpy((
void*) content, (
void*) response->content, response->bufsize);
2997 destroy_response(&response);
3021 c = (int) content[i];
3029 if(0x20 == c || 0x09 == c)
3039 if(0x20 == c || 0x09 == c) {
break; };
3064 while((
char) 0x0A != content[i++]);
3070 PRINT_ERROR(
"Unsupported entry in group label list ignored");
3076 printf(
"%lu: %s %s\n", (
unsigned long int) gi,
3080 (*p)[gi].name = &content[
name];
3082 (*p)[gi].label = &content[
label];
3127 struct nntp_response* response = NULL;
3138 unsigned int flags = 0;
3140 if(NULL != gd) { *gd = NULL; }
3143 res = create_response(&response);
3161 if(!res) { res = exec_cmd(handle, NNTP_CMD_GROUP, response, name); }
3166 if(2U != response->code1)
3175 if (NULL == *gd) { res = -1; }
3179 (*gd)->flags = flags;
3181 s = &response->msg[4];
3182 while(s[i] && !abort)
3190 if(0x20 == c) {
break; }
3196 rv = parse_number(c, wm, &wm_len, &n);
3197 if(-1 == rv) { field = -1;
break; }
3208 if(0x20 == c) {
break; }
3214 rv = parse_number(c, wm, &wm_len, &n);
3215 if(-1 == rv) { field = -1;
break; }
3228 if((*gd)->lwm > (*gd)->hwm)
3257 destroy_response(&response);
3260 if(res && NULL != gd) { posix_free(*gd); *gd = NULL; }
3285 if(server[handle].capabilities & NNTP_CAPA_OVER) { res = 1; }
3316 *index = server[handle].over_newsgroups;
3345 char**
data,
size_t* len)
3348 struct nntp_response* response = NULL;
3351 res = create_response(&response);
3354 if(!res) { res = exec_cmd(handle, NNTP_CMD_OVER, response, &first, &last); }
3357 if(2U != response->code1) { res = -1; }
3361 *len = response->bufsize;
3362 *
data = response->content;
3364 response->content = NULL;
3369 destroy_response(&response);
3395 char** article,
size_t* len)
3398 struct nntp_response* response = NULL;
3401 res = create_response(&response);
3404 if(!res) { res = exec_cmd(handle, NNTP_CMD_ARTICLE_BY_MID, response, mid); }
3407 if(2U != response->code1) { res = -1; }
3411 *len = response->bufsize;
3412 *article = response->content;
3414 response->content = NULL;
3419 destroy_response(&response);
3448 struct nntp_response* response = NULL;
3451 res = create_response(&response);
3454 if(!res) { res = exec_cmd(handle, NNTP_CMD_ARTICLE, response,
id); }
3457 if(2U != response->code1) { res = -1; }
3461 *len = response->bufsize;
3462 *article = response->content;
3464 response->content = NULL;
3469 destroy_response(&response);
3495 char** header,
size_t* len)
3498 struct nntp_response* response = NULL;
3501 res = create_response(&response);
3504 if(!res) { res = exec_cmd(handle, NNTP_CMD_HEAD, response,
id); }
3507 if(2U != response->code1) { res = -1; }
3511 *len = response->bufsize;
3512 *header = response->content;
3514 response->content = NULL;
3519 destroy_response(&response);
3548 struct nntp_response* response = NULL;
3551 res = create_response(&response);
3554 if(!res) { res = exec_cmd(handle, NNTP_CMD_BODY, response,
id); }
3557 if(2U != response->code1) { res = -1; }
3561 *len = response->bufsize;
3562 *body = response->content;
3564 response->content = NULL;
3569 destroy_response(&response);
3594 struct nntp_response* response = NULL;
3597 res = create_response(&response);
3600 if(!res) { res = exec_cmd(handle, NNTP_CMD_POST, response, article); }
3603 if(2U != response->code1) { res = -1; }
3607 destroy_response(&response);