anntp

a nntp implementation in pure C99
Log | Files | Refs | README | LICENSE

commit 15fab62468e9586e98d89e940cfdf8bc0974a97d
parent 468c760ce2502ccc994cc4d5415fdcf93cfbcbc5
Author: Mario Rosell R. Martinez <mario@mariorosell.es>
Date:   Sat, 28 Mar 2026 21:19:45 +0100

article: Add anntp_article

Diffstat:
Manntp.h | 91+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
1 file changed, 91 insertions(+), 0 deletions(-)

diff --git a/anntp.h b/anntp.h @@ -193,6 +193,7 @@ ANNTP_API void anntp_freeconn(AnntpConnection* conn); ANNTP_API ssize_t anntp_read(AnntpConnection* conn, uchar_t* buf, size_t bufsize); ANNTP_API ssize_t anntp_write(AnntpConnection* conn, const uchar_t* buf, size_t bufsize); ANNTP_API int anntp_group(AnntpConnection* conn, const char* group, AnntpGroup* out_group); +ANNTP_API ssize_t anntp_article(AnntpConnection*, size_t num, AnntpArticle* out_art); ANNTP_API ssize_t anntp_write_all(AnntpConnection* conn, const uchar_t* buf, size_t bufsize); ANNTP_API ssize_t anntp_writeline(AnntpConnection* conn, const char* buf); ANNTP_API ssize_t anntp_readline(AnntpConnection* conn, char* buf, size_t maxlen); @@ -301,6 +302,94 @@ cleanup: return NULL; } +static char* +anntp__trim_left(char* s) +{ + while (*s == ' ' || *s == '\t') + s++; + return s; +} + +ssize_t +anntp_article(AnntpConnection* cv, size_t n, AnntpArticle* art) +{ + if (!cv || !art) + return ANNTPE(ANE_PARAMS); + + char cmd[ANNTP_BUFSIZE]; + char line[ANNTP_BUFSIZE]; + + snprintf(cmd, sizeof(cmd), "ARTICLE %zu", n); + + if (anntp_writeline(cv, cmd) <= 0) + return ANNTPE(ANE_IO); + + ssize_t r = anntp_readline(cv, line, sizeof(line)); + if (r <= 0) + return ANNTPE(ANE_IO); + + if (strncmp(line, "220", 3) != 0) + return ANNTPE(ANE_PROTO); + + size_t cap = ANNTP_BUFSIZE * 8; + char* buf = (char*)ANNTP_MALLOC(cap); + if (!buf) + return ANNTPE(ANE_IO); + + ssize_t len = anntp_readdot(cv, buf, cap); + if (len < 0) { + ANNTP_FREE(buf); + return len; + } + + memset(art, 0, sizeof(*art)); + art->n = (AnntpArticleNumber)n; + + char* p = buf; + char* body_start = NULL; + + while (*p) { + char* line_start = p; + + char* nl = strchr(p, '\n'); + if (!nl) + break; + + *nl = '\0'; + p = nl + 1; + + /* end of headers */ + if (line_start[0] == '\r' || line_start[0] == '\0') { + body_start = p; + break; + } + + if (strncmp(line_start, "Message-ID:", 11) == 0) { + char* v = anntp__trim_left(line_start + 11); + art->id = ANNTP_STRDUP(v); + } + else if (strncmp(line_start, "From:", 5) == 0) { + char* v = anntp__trim_left(line_start + 5); + art->from = ANNTP_STRDUP(v); + } + else if (strncmp(line_start, "Date:", 5) == 0) { + char* v = anntp__trim_left(line_start + 5); + art->date = ANNTP_STRDUP(v); + } + } + + if (body_start) { + size_t blen = strlen(body_start); + art->body = (char*)ANNTP_MALLOC(blen + 1); + if (art->body) { + memcpy(art->body, body_start, blen + 1); + } + } + + ANNTP_FREE(buf); + return (ssize_t)len; +} + void anntp_freeconn(AnntpConnection* cv) { @@ -485,6 +574,8 @@ anntp_readdot(AnntpConnection* cv, char* buf, size_t maxlen) return (ssize_t)pos; } + + int anntp_readdot_cb(AnntpConnection* cv, AnntpLineCb cb, void* extra) {