INN commit: trunk/nnrpd (article.c list.c nnrpd.c)
INN Commit
Russ_Allbery at isc.org
Sat Sep 6 11:16:28 UTC 2008
Date: Saturday, September 6, 2008 @ 04:16:27
Author: iulius
Revision: 8006
* Add support for HDR.
* Correctly parse the arguments before anything else.
* Reject unimplemented metadata requests with 503.
* LIST HEADERS is implemented with a different output between RANGE and MSGID.
* Homogenize the HELP result with "wildmat" everywhere (instead of "group_pattern"
or "pat").
* More user-friendly answers.
* XHDR and XPAT were not checking the permissions the user has to read
articles when using a message-ID. Now fixed.
* Fix calls to ARTclose().
Modified:
trunk/nnrpd/article.c
trunk/nnrpd/list.c
trunk/nnrpd/nnrpd.c
-----------+
article.c | 199 ++++++++++++++++++++++++++++++++++++++++++++++--------------
list.c | 52 ++++++++++++---
nnrpd.c | 15 ++--
3 files changed, 206 insertions(+), 60 deletions(-)
Modified: article.c
===================================================================
--- article.c 2008-09-06 08:58:33 UTC (rev 8005)
+++ article.c 2008-09-06 11:16:27 UTC (rev 8006)
@@ -292,7 +292,7 @@
/*
-** Open the article for a given Message-ID.
+** Open the article for a given message-ID.
*/
static bool
ARTopenbyid(char *msg_id, ARTNUM *ap, bool final)
@@ -625,10 +625,10 @@
return;
}
- /* Requesting by Message-ID? */
+ /* Requesting by message-ID? */
if (ac == 2 && av[1][0] == '<') {
if (!IsValidMessageID(av[1])) {
- Reply("%d Syntax error in Message-ID\r\n", NNTP_ERR_SYNTAX);
+ Reply("%d Syntax error in message-ID\r\n", NNTP_ERR_SYNTAX);
return;
}
if (!ARTopenbyid(av[1], &art, final)) {
@@ -684,6 +684,7 @@
if (ac > 1)
ARTnumber = tart;
if ((msgid = GetHeader("Message-ID")) == NULL) {
+ ARTclose();
Reply("%s\r\n", ARTnoartingroup);
return;
}
@@ -743,9 +744,9 @@
if (!ARTopen(ARTnumber))
continue;
msgid = GetHeader("Message-ID");
+ ARTclose();
} while (msgid == NULL);
- ARTclose();
Reply("%d %d %s Article retrieved; request text separately\r\n",
NNTP_OK_STAT, ARTnumber, msgid);
}
@@ -781,7 +782,7 @@
*DidReply = false;
if (ac == 1) {
- /* No argument, do only current article. */
+ /* No arguments, do only current article. */
if (ARTnumber < ARTlow || ARTnumber > ARThigh) {
Reply("%s\r\n", ARTnocurrart);
*DidReply = true;
@@ -878,7 +879,7 @@
/*
** Dump parts of the overview database with the OVER command.
-** The legacy XOVER is also kept.
+** The legacy XOVER is also kept, with its specific behaviour.
*/
void
CMDover(int ac, char *av[])
@@ -927,7 +928,7 @@
}
/* Parse range. CMDgetrange() correctly sets the range when
- * there is no argument. */
+ * there is no arguments. */
if (!CMDgetrange(ac, av, &range, &DidReply))
if (DidReply)
return;
@@ -1060,19 +1061,18 @@
}
/*
-** XHDR and XPAT extensions. Note that HDR as specified in the new NNTP
-** draft works differently than XHDR has historically, so don't just use this
-** function to implement it without reviewing the differences.
+** Access specific fields from an article with HDR.
+** The legacy XHDR and XPAT are also kept, with their specific behaviours.
*/
-/* ARGSUSED */
void
CMDpat(int ac, char *av[])
{
char *p;
unsigned long i;
ARTRANGE range;
- bool IsLines;
- bool DidReply;
+ bool IsBytes, IsLines;
+ bool IsMetaBytes, IsMetaLines;
+ bool DidReply, HasNotReplied;
char *header;
char *pattern;
char *text;
@@ -1084,102 +1084,190 @@
int len;
TOKEN token;
struct cvector *vector = NULL;
+ bool hdr, mid;
+ hdr = (strcasecmp(av[0], "HDR") == 0);
+ mid = (ac > 2 && IsValidMessageID(av[2]));
+
+ /* Check the syntax of the arguments first. */
+ if (ac > 2 && !mid && !CMDisrange(av[2])) {
+ Reply("%d Syntax error in arguments\r\n", NNTP_ERR_SYNTAX);
+ return;
+ }
+
+ /* Check authorizations. */
if (!PERMcanread) {
Reply("%d Read access denied\r\n", NNTP_ERR_ACCESS);
return;
}
header = av[1];
- IsLines = (strcasecmp(header, "lines") == 0);
- if (ac > 3) /* XPAT */
+ /* If metadata is asked for, convert it to headers that
+ * the overview database knows. */
+ IsBytes = (strcasecmp(header, "Bytes") == 0);
+ IsLines = (strcasecmp(header, "Lines") == 0);
+ IsMetaBytes = (strcasecmp(header, ":bytes") == 0);
+ IsMetaLines = (strcasecmp(header, ":lines") == 0);
+ /* Make these changes because our overview database does
+ * not currently know metadata names. */
+ if (IsMetaBytes)
+ header = xstrdup("Bytes");
+ if (IsMetaLines)
+ header = xstrdup("Lines");
+
+ /* We only allow :bytes and :lines for metadata. */
+ if ((strncasecmp(header, ":", 1) == 0) && !IsMetaLines && !IsMetaBytes) {
+ Reply("%d %s metadata request unsupported\r\n",
+ NNTP_ERR_UNAVAILABLE, header);
+ return;
+ }
+
+ if (ac > 3) /* Necessarily XPAT. */
pattern = Glom(&av[3]);
else
pattern = NULL;
+ /* We will only do the loop once. It is just in order to easily break. */
do {
/* Message-ID specified? */
- if (ac > 2 && av[2][0] == '<') {
- if (!IsValidMessageID(av[2])) {
- Reply("%d Syntax error in Message-ID\r\n", NNTP_ERR_SYNTAX);
- break;
- }
+ if (mid) {
p = av[2];
if (!ARTopenbyid(p, &artnum, false)) {
Printf("%d No such article\r\n", NNTP_FAIL_NOTFOUND);
break;
}
- Printf("%d %s matches follow (ID)\r\n", NNTP_OK_HEAD,
- header);
- if ((text = GetHeader(header)) != NULL
+
+ /* FIXME: We do not handle metadata requests by message-ID. */
+ if (hdr && (IsMetaBytes || IsMetaLines)) {
+ Reply("%d Metadata requests by message-ID unsupported\r\n",
+ NNTP_ERR_UNAVAILABLE);
+ break;
+ }
+
+ if (!PERMartok()) {
+ ARTclose();
+ Reply("%d Read access denied for this article\r\n", NNTP_ERR_ACCESS);
+ break;
+ }
+
+ Printf("%d Header information for %s follows (message-ID)\r\n",
+ hdr ? NNTP_OK_HDR : NNTP_OK_HEAD, av[1]);
+
+ if ((text = GetHeader(av[1])) != NULL
&& (!pattern || uwildmat_simple(text, pattern)))
- Printf("%s %s\r\n", p, text);
+ Printf("%s %s\r\n", hdr ? "0" : p, text);
+ else if (hdr) {
+ /* We always have to answer something with HDR. */
+ Printf("0 \r\n");
+ }
ARTclose();
Printf(".\r\n");
break;
}
+ /* Trying to read. */
if (GRPcount == 0) {
Reply("%s\r\n", ARTnotingroup);
break;
}
- /* Range specified. */
- if (!CMDgetrange(ac - 1, av + 1, &range, &DidReply)) {
- if (DidReply) {
- break;
- }
- }
+ /* Parse range. CMDgetrange() correctly sets the range when
+ * there is no arguments. */
+ if (!CMDgetrange(ac - 1, av + 1, &range, &DidReply))
+ if (DidReply)
+ break;
/* In overview? */
Overview = overview_index(header, OVextra);
+ HasNotReplied = true;
+
/* Not in overview, we have to fish headers out from the articles. */
- if (Overview < 0) {
- Reply("%d %s matches follow (art)\r\n", NNTP_OK_HEAD,
- header);
+ if (Overview < 0 || IsBytes || IsLines) {
for (i = range.Low; i <= range.High && range.High > 0; i++) {
if (!ARTopen(i))
continue;
+ if (HasNotReplied) {
+ Reply("%d Header information for %s follows (art)\r\n",
+ hdr ? NNTP_OK_HDR : NNTP_OK_HEAD, av[1]);
+ HasNotReplied = false;
+ }
p = GetHeader(header);
if (p && (!pattern || uwildmat_simple(p, pattern))) {
snprintf(buff, sizeof(buff), "%lu ", i);
SendIOb(buff, strlen(buff));
SendIOb(p, strlen(p));
SendIOb("\r\n", 2);
- ARTclose();
- }
+ } else if (hdr) {
+ /* We always have to answer something with HDR. */
+ snprintf(buff, sizeof(buff), "%lu \r\n", i);
+ SendIOb(buff, strlen(buff));
+ }
+ ARTclose();
}
- SendIOb(".\r\n", 3);
+ if (HasNotReplied) {
+ if (hdr) {
+ if (ac > 2)
+ Reply("%d No articles in %s\r\n",
+ NNTP_FAIL_BAD_ARTICLE, av[2]);
+ else
+ Reply("%d Current article number %d is invalid\r\n",
+ NNTP_FAIL_NO_ARTICLE, ARTnumber);
+ } else {
+ Reply("%d No header information for %s follows (art)\r\n",
+ NNTP_OK_HEAD, av[1]);
+ Reply(".\r\n");
+ }
+ break;
+ } else {
+ SendIOb(".\r\n", 3);
+ }
PushIOb();
break;
}
- /* Okay then, we can grab values from overview. */
+ /* Okay then, we can grab values from the overview database. */
handle = (void *)OVopensearch(GRPcur, range.Low, range.High);
if (handle == NULL) {
- Reply("%d %s no matches follow (NOV)\r\n",
- NNTP_OK_HEAD, header);
- Printf(".\r\n");
+ if (hdr) {
+ if (ac > 2)
+ Reply("%d No articles in %s\r\n",
+ NNTP_FAIL_BAD_ARTICLE, av[2]);
+ else
+ Reply("%d Current article number %d is invalid\r\n",
+ NNTP_FAIL_NO_ARTICLE, ARTnumber);
+ } else {
+ Reply("%d No header information for %s follows (NOV)\r\n",
+ NNTP_OK_HEAD, av[1]);
+ Printf(".\r\n");
+ }
break;
}
- Printf("%d %s matches follow (NOV)\r\n", NNTP_OK_HEAD,
- header);
while (OVsearch(handle, &artnum, &data, &len, &token, NULL)) {
if (len == 0 || (PERMaccessconf->nnrpdcheckart
&& !ARTinstorebytoken(token)))
continue;
+ if (HasNotReplied) {
+ Reply("%d Header or metadata information for %s follows (NOV)\r\n",
+ hdr ? NNTP_OK_HDR : NNTP_OK_HEAD, av[1]);
+ HasNotReplied = false;
+ }
vector = overview_split(data, len, NULL, vector);
p = overview_getheader(vector, Overview, OVextra);
if (p != NULL) {
if (PERMaccessconf->virtualhost &&
Overview == overhdr_xref) {
p = vhost_xref(p);
- if (p == NULL)
+ if (p == NULL) {
+ if (hdr) {
+ snprintf(buff, sizeof(buff), "%lu \r\n", artnum);
+ SendIOb(buff, strlen(buff));
+ }
continue;
+ }
}
if (!pattern || uwildmat_simple(p, pattern)) {
snprintf(buff, sizeof(buff), "%lu ", artnum);
@@ -1187,10 +1275,31 @@
SendIOb(p, strlen(p));
SendIOb("\r\n", 2);
}
+ /* No need to have another condition for HDR because
+ * pattern is NULL for it, and p is not NULL here. */
free(p);
- }
+ } else if (hdr) {
+ snprintf(buff, sizeof(buff), "%lu \r\n", artnum);
+ SendIOb(buff, strlen(buff));
+ }
}
- SendIOb(".\r\n", 3);
+ if (HasNotReplied) {
+ if (hdr) {
+ if (ac > 2)
+ Reply("%d No articles in %s\r\n",
+ NNTP_FAIL_BAD_ARTICLE, av[2]);
+ else
+ Reply("%d Current article number %d is invalid\r\n",
+ NNTP_FAIL_NO_ARTICLE, ARTnumber);
+ } else {
+ Reply("%d No header or metadata information for %s follows (NOV)\r\n",
+ NNTP_OK_HEAD, av[1]);
+ Reply(".\r\n");
+ }
+ break;
+ } else {
+ SendIOb(".\r\n", 3);
+ }
PushIOb();
OVclosesearch(handle);
} while (0);
Modified: list.c
===================================================================
--- list.c 2008-09-06 08:58:33 UTC (rev 8005)
+++ list.c 2008-09-06 11:16:27 UTC (rev 8006)
@@ -18,14 +18,15 @@
typedef struct _LISTINFO {
const char *method;
const char * File;
- void (*impl)(struct _LISTINFO *);
+ void (*impl)(struct _LISTINFO *, int ac, char *av[]);
bool Required;
const char * Items;
const char * Format;
} LISTINFO;
-static void cmd_list_schema(LISTINFO *lp);
-static void cmd_list_extensions(LISTINFO *lp);
+static void cmd_list_schema(LISTINFO *lp, int ac, char *av[]);
+static void cmd_list_extensions(LISTINFO *lp, int ac, char *av[]);
+static void cmd_list_headers(LISTINFO *lp, int ac, char *av[]);
static LISTINFO INFOactive = {
"ACTIVE", INN_PATH_ACTIVE, NULL, true, "active newsgroups",
@@ -39,6 +40,10 @@
"DISTRIBUTIONS", INN_PATH_NNRPDIST, NULL, false, "newsgroup distributions",
"Distributions in form \"area description\""
};
+static LISTINFO INFOheaders = {
+ "HEADERS", NULL, cmd_list_headers, false, "supported headers and metadata",
+ "Headers and metadata items supported"
+};
static LISTINFO INFOsubs = {
"SUBSCRIPTIONS", INN_PATH_NNRPSUBS, NULL, false,
"automatic group subscriptions", "Subscriptions in form \"group\""
@@ -72,6 +77,7 @@
&INFOactive,
&INFOactivetimes,
&INFOdistribs,
+ &INFOheaders,
&INFOsubs,
&INFOdistribpats,
&INFOextensions,
@@ -86,7 +92,7 @@
** List the overview schema (standard and extra fields).
*/
static void
-cmd_list_schema(LISTINFO *lp)
+cmd_list_schema(LISTINFO *lp, int ac UNUSED, char *av[] UNUSED)
{
const struct cvector *standard;
unsigned int i;
@@ -107,7 +113,7 @@
** List supported extensions.
*/
static void
-cmd_list_extensions(LISTINFO *lp)
+cmd_list_extensions(LISTINFO *lp, int ac UNUSED, char *av[] UNUSED)
{
const char *mechlist = NULL;
@@ -134,6 +140,33 @@
/*
+** List supported headers and metadata information.
+*/
+static void
+cmd_list_headers(LISTINFO *lp, int ac, char *av[])
+{
+ bool range;
+
+ range = (ac > 2 && strcasecmp(av[2], "RANGE") == 0);
+
+ if (ac > 2 && (strcasecmp(av[2], "MSGID") != 0)
+ && !range) {
+ Reply("%d Syntax error in arguments\r\n", NNTP_ERR_SYNTAX);
+ return;
+ }
+ Reply("%d %s\r\n", NNTP_OK_LIST, lp->Format);
+ Reply(":\r\n");
+ if (range) {
+ /* These information are only known by the overview system,
+ * and are only accessible with a range. */
+ Reply(":bytes\r\n");
+ Reply(":lines\r\n");
+ }
+ Reply(".\r\n");
+}
+
+
+/*
** List a single newsgroup. Called by LIST ACTIVE with a single argument.
** This is quicker than parsing the whole active file, but only works with
** single groups. It also doesn't work for aliased groups, since overview
@@ -204,12 +237,13 @@
if (CMD_list_single(wildarg))
return;
}
- } else if (lp == &INFOgroups || lp == &INFOactivetimes) {
+ } else if (lp == &INFOgroups || lp == &INFOactivetimes
+ || lp == &INFOheaders) {
if (ac == 3)
wildarg = av[2];
}
- /* Three arguments can be passed only when ACTIVE, ACTIVE.TIMES
- * or NEWSGROUPS keywords are used. */
+ /* Three arguments can be passed only when ACTIVE, ACTIVE.TIMES,
+ * HEADERS or NEWSGROUPS keywords are used. */
if (ac > 2 && !wildarg) {
Reply("%s\r\n", NNTP_SYNTAX_USE);
return;
@@ -217,7 +251,7 @@
/* If a function is provided for the given keyword, we call it. */
if (lp->impl != NULL) {
- lp->impl(lp);
+ lp->impl(lp, ac, av);
return;
}
Modified: nnrpd.c
===================================================================
--- nnrpd.c 2008-09-06 08:58:33 UTC (rev 8005)
+++ nnrpd.c 2008-09-06 11:16:27 UTC (rev 8006)
@@ -98,7 +98,7 @@
bool PY_use_dynamic = false;
#endif
-static char CMDfetchhelp[] = "[messageID|number]";
+static char CMDfetchhelp[] = "[message-ID|number]";
/* { command base name, function to call, need authentication,
min args, max args, help string } */
@@ -117,17 +117,20 @@
NULL },
{ "GROUP", CMDgroup, true, 2, 2,
"newsgroup" },
+ { "HDR", CMDpat , true, 2, 3,
+ "header [range|message-ID]" },
{ "HEAD", CMDfetch, true, 1, 2,
CMDfetchhelp },
{ "HELP", CMDhelp, false, 1, 1,
NULL },
{ "IHAVE", CMDpost, true, 2, 2,
- "messageID" },
+ "message-ID" },
{ "LAST", CMDnextlast, true, 1, 1,
NULL },
{ "LIST", CMDlist, true, 1, 3,
"[ACTIVE [wildmat]|ACTIVE.TIMES [wildmat]|DISTRIB.PATS|DISTRIBUTIONS"
- "|EXTENSIONS|MODERATORS|MOTD|NEWSGROUPS [wildmat]|OVERVIEW.FMT|SUBSCRIPTIONS]" },
+ "|EXTENSIONS|HEADERS [MSGID|RANGE]|MODERATORS|MOTD|NEWSGROUPS [wildmat]"
+ "|OVERVIEW.FMT|SUBSCRIPTIONS]" },
{ "LISTGROUP", CMDgroup, true, 1, 3,
"[newsgroup [range]]" },
{ "MODE", CMDmode, false, 2, 2,
@@ -155,13 +158,13 @@
{ "STAT", CMDfetch, true, 1, 2,
CMDfetchhelp },
{ "XGTITLE", CMDxgtitle, true, 1, 2,
- "[group_pattern]" },
+ "[wildmat]" },
{ "XHDR", CMDpat, true, 2, 3,
- "header [range|messageID]" },
+ "header [range|message-ID]" },
{ "XOVER", CMDover, true, 1, 2,
"[range]" },
{ "XPAT", CMDpat, true, 4, CMDany,
- "header range|messageID pat [morepat...]" },
+ "header range|message-ID wildmat [wildmat ...]" },
{ NULL, CMD_unimp, false, 0, 0,
NULL }
};
More information about the inn-committers
mailing list