📄 article.c
字号:
/* $Revision: 1.13 $**** Article-related routines.*/#include "nnrpd.h"/*** Data structures for use in ARTICLE/HEAD/BODY/STAT common code.*/typedef enum _SENDTYPE { STarticle, SThead, STbody, STstat} SENDTYPE;typedef struct _SENDDATA { SENDTYPE Type; int ReplyCode; STRING Item;} SENDDATA;/*** Information about the schema of the news overview files.*/typedef struct _ARTOVERFIELD { char *Header; int Length; BOOL HasHeader;} ARTOVERFIELD;STATIC char ARTnotingroup[] = NNTP_NOTINGROUP;STATIC char ARTnoartingroup[] = NNTP_NOARTINGRP;STATIC char ARTnocurrart[] = NNTP_NOCURRART;STATIC QIOSTATE *ARTqp;STATIC ARTOVERFIELD *ARTfields;STATIC int ARTfieldsize;STATIC SENDDATA SENDbody = { STbody, NNTP_BODY_FOLLOWS_VAL, "body"};STATIC SENDDATA SENDarticle = { STarticle, NNTP_ARTICLE_FOLLOWS_VAL, "article"};STATIC SENDDATA SENDstat = { STstat, NNTP_NOTHING_FOLLOWS_VAL, "status"};STATIC SENDDATA SENDhead = { SThead, NNTP_HEAD_FOLLOWS_VAL, "head"};/*** Overview state information.*/STATIC QIOSTATE *OVERqp; /* Open overview file */STATIC char *OVERline; /* Current line */STATIC ARTNUM OVERarticle; /* Current article */STATIC int OVERopens; /* Number of opens done *//*** Read the overview schema.*/voidARTreadschema(){ static char SCHEMA[] = _PATH_SCHEMA; register FILE *F; register char *p; register ARTOVERFIELD *fp; register int i; char buff[SMBUF]; /* Open file, count lines. */ if ((F = fopen(SCHEMA, "r")) == NULL) return; for (i = 0; fgets(buff, sizeof buff, F) != NULL; i++) continue; (void)fseek(F, (OFFSET_T)0, SEEK_SET); ARTfields = NEW(ARTOVERFIELD, i + 1); /* Parse each field. */ for (fp = ARTfields; fgets(buff, sizeof buff, F) != NULL; ) { /* Ignore blank and comment lines. */ if ((p = strchr(buff, '\n')) != NULL) *p = '\0'; if ((p = strchr(buff, COMMENT_CHAR)) != NULL) *p = '\0'; if (buff[0] == '\0') continue; if ((p = strchr(buff, ':')) != NULL) { *p++ = '\0'; fp->HasHeader = EQ(p, "full"); } else fp->HasHeader = FALSE; fp->Header = COPY(buff); fp->Length = strlen(buff); fp++; } ARTfieldsize = fp - ARTfields; (void)fclose(F);}/*** If we have an article open, close it.*/voidARTclose(){ if (ARTqp) { QIOclose(ARTqp); ARTqp = NULL; }}/*** Get the Message-ID from a file.*/STATIC voidARTgetmsgid(qp, id) register QIOSTATE *qp; char *id;{ register char *p; register char *q; for (*id = '\0'; (p = QIOread(qp)) != NULL && *p != '\0'; ) { if (*p != 'M' && *p != 'm') continue; if ((q = strchr(p, ' ')) == NULL) continue; *q++ = '\0'; if (caseEQ(p, "Message-ID:")) { (void)strcpy(id, q); break; } } (void)QIOrewind(qp);}/*** If the article name is valid, open it and stuff in the ID.*/STATIC BOOLARTopen(name, id) char *name; char *id;{ static ARTNUM save_artnum; static char save_artid[BIG_BUFFER]; struct stat Sb; /* Re-use article if it's the same one. */ if (ARTqp != NULL) { if (save_artnum == atol(name) && QIOrewind(ARTqp) != -1) { if (id) (void)strcpy(id, save_artid); return TRUE; } QIOclose(ARTqp); } /* Open it, make sure it's a regular file. */ if ((ARTqp = QIOopen(name, QIO_BUFFER)) == NULL) return FALSE; if (fstat(QIOfileno(ARTqp), &Sb) < 0 || !S_ISREG(Sb.st_mode)) { QIOclose(ARTqp); ARTqp = NULL; return FALSE; } CloseOnExec(QIOfileno(ARTqp), TRUE); save_artnum = atol(name); ARTgetmsgid(ARTqp, save_artid); (void)strcpy(id, save_artid); return TRUE;}/*** Open the article for a given Message-ID.*/STATIC QIOSTATE *ARTopenbyid(msg_id, ap) char *msg_id; ARTNUM *ap;{ QIOSTATE *qp; char *p; char *q; *ap = 0; if ((p = HISgetent(msg_id, FALSE)) == NULL) return NULL; if ((qp = QIOopen(p, QIO_BUFFER)) == NULL) return NULL; CloseOnExec(QIOfileno(qp), TRUE); if ((q = strrchr(p, '/')) != NULL) *q++ = '\0'; if (GRPlast[0] && EQ(p, GRPlast)) *ap = atol(q); return qp;}/*** Send a (part of) a file to stdout, doing newline and dot conversion.*/STATIC voidARTsend(qp, what) register QIOSTATE *qp; SENDTYPE what;{ register char *p; ARTcount++; GRParticles++; /* Get the headers. */ for ( ; ; ) { p = QIOread(qp); if (p == NULL) { if (QIOtoolong(qp)) continue; break; } if (*p == '\0') break; if (what == STbody) continue; Printf("%s%s\r\n", *p == '.' ? "." : "", p); } if (what == SThead) { Printf(".\r\n"); return; } if (what == STarticle) Printf("\r\n"); for ( ; ; ) { p = QIOread(qp); if (p == NULL) { if (QIOtoolong(qp)) continue; break; } Printf("%s%s\r\n", *p == '.' ? "." : "", p); } Printf(".\r\n");}/*** Find an article number in the article array via a binary search;** return -1 if not found. Cache last hit to make linear lookups** faster.*/STATIC intARTfind(i) register ARTNUM i;{ register ARTNUM *bottom; register ARTNUM *middle; register ARTNUM *top; if (ARTsize == 0) return -1; top = &ARTnumbers[ARTsize - 1]; if (ARTcache && ++ARTcache <= top && *ARTcache <= i) { if (*ARTcache == i) return ARTcache - ARTnumbers; bottom = ARTcache; } else { ARTcache = NULL; bottom = ARTnumbers; } for ( ; ; ) { if (i < *bottom || i > *top) break; middle = bottom + (top - bottom) / 2; if (i == *middle) { /* Found it; update cache. */ ARTcache = middle; return middle - ARTnumbers; } if (i > *middle) bottom = middle + 1; else top = middle; } return -1;}/*** Ask the innd server for the article. Only called from CMDfetch,** and only if history file is buffered. Common case: "oops, cancel** that article I just posted."*/STATIC QIOSTATE *ARTfromboss(what, id) SENDDATA *what; char *id;{ FILE *FromServer; FILE *ToServer; QIOSTATE *qp; char buff[NNTP_STRLEN + 2]; char *name; char *p; BOOL more; /* If we can, open the connection. */ if (NNTPlocalopen(&FromServer, &ToServer, (char *)NULL) < 0) return NULL; /* Send the query to the server. */ qp = NULL; (void)fprintf(ToServer, "XPATH %s\r\n", id); (void)fflush(ToServer); if (ferror(ToServer)) goto QuitClose; /* Get the reply; article exist? */ if (fgets(buff, sizeof buff, FromServer) == NULL || atoi(buff) == NNTP_DONTHAVEIT_VAL) goto QuitClose; /* Yes. Be quick if just doing a stat. */ if (what == &SENDstat) { qp = QIOopen("/dev/null", 0); goto QuitClose; } /* Clean up response. */ if ((p = strchr(buff, '\r')) != NULL) *p = '\0'; if ((p = strchr(buff, '\n')) != NULL) *p = '\0'; /* Loop over all filenames until we can open one. */ for (name = buff; *name; name = p + 1) { /* Snip off next name, turn dots to slashes. */ for (p = name; ISWHITE(*p); p++) continue; for (name = p; *p && *p != ' '; p++) if (*p == '.') *p = '/'; more = *p == ' '; if (more) *p = '\0'; if ((qp = QIOopen(name, QIO_BUFFER)) != NULL || !more) break; } /* Send quit, read server's reply, close up and return. */ QuitClose: (void)fprintf(ToServer, "quit\r\n"); (void)fclose(ToServer); (void)fgets(buff, sizeof buff, FromServer); (void)fclose(FromServer); return qp;}/*** Fetch part or all of an article and send it to the client.*/FUNCTYPECMDfetch(ac, av) int ac; char *av[];{ char buff[SMBUF]; char idbuff[BIG_BUFFER]; SENDDATA *what; register QIOSTATE *qp; register BOOL ok; ARTNUM art; /* Find what to send; get permissions. */ ok = PERMcanread; switch (*av[0]) { default: what = &SENDbody; break; case 'a': case 'A': what = &SENDarticle; break; case 's': case 'S': what = &SENDstat; break; case 'h': case 'H': what = &SENDhead; /* Poster might do a "head" command to verify the article. */ ok = PERMcanread || PERMcanpost; break; } if (!ok) { Reply("%s\r\n", NOACCESS); return; } /* Requesting by Message-ID? */ if (ac == 2 && av[1][0] == '<') { if ((qp = ARTopenbyid(av[1], &art)) == NULL && (qp = ARTfromboss(what, av[1])) == NULL) { Reply("%d No such article\r\n", NNTP_DONTHAVEIT_VAL); return; } if (!PERMartok(qp)) { QIOclose(qp); Reply("%s\r\n", NOACCESS); return; } Reply("%d %ld %s %s\r\n", what->ReplyCode, art, what->Item, av[1]); if (what->Type != STstat) ARTsend(qp, what->Type); QIOclose(qp); return; } /* Trying to read. */ if (GRPcount == 0) { Reply("%s\r\n", ARTnotingroup); return; } /* Default is to get current article, or specified article. */ if (ac == 1) { if (ARTindex < 0 || ARTindex >= ARTsize) { Reply("%s\r\n", ARTnocurrart); return; } (void)sprintf(buff, "%ld", ARTnumbers[ARTindex]); } else { if (strspn(av[1], "0123456789") != strlen(av[1])) { Reply("%s\r\n", ARTnoartingroup); return; } (void)strcpy(buff, av[1]); } /* Move forward until we can find one. */ while (!ARTopen(buff, idbuff)) { if (ac > 1 || ++ARTindex >= ARTsize) { Reply("%s\r\n", ARTnoartingroup); return; } (void)sprintf(buff, "%ld", ARTnumbers[ARTindex]); } Reply("%d %s %s %s\r\n", what->ReplyCode, buff, idbuff, what->Item); if (what->Type != STstat) ARTsend(ARTqp, what->Type); if (ac > 1) ARTindex = ARTfind((ARTNUM)atol(buff));}/*** Go to the next or last (really previous) article in the group.*/FUNCTYPECMDnextlast(ac, av) int ac; char *av[];{ char buff[SPOOLNAMEBUFF]; char idbuff[SMBUF]; int save; BOOL next; int delta; int errcode; STRING message; if (!PERMcanread) { Reply("%s\r\n", NOACCESS); return; } if (GRPcount == 0) { Reply("%s\r\n", ARTnotingroup); return; } if (ARTindex < 0 || ARTindex >= ARTsize) { Reply("%s\r\n", ARTnocurrart); return; } next = (av[0][0] == 'n' || av[0][0] == 'N'); if (next) { delta = 1; errcode = NNTP_NONEXT_VAL; message = "next"; } else { delta = -1; errcode = NNTP_NOPREV_VAL; message = "previous"; } save = ARTindex; ARTindex += delta; if (ARTindex < 0 || ARTindex >= ARTsize) { Reply("%d No %s to retrieve.\r\n", errcode, message); ARTindex = save; return; } (void)sprintf(buff, "%ld", ARTnumbers[ARTindex]); while (!ARTopen(buff, idbuff)) { ARTindex += delta; if (ARTindex < 0 || ARTindex >= ARTsize) { Reply("%d No %s article to retrieve.\r\n", errcode, message); ARTindex = save; return; } (void)sprintf(buff, "%ld", ARTnumbers[ARTindex]); } Reply("%d %s %s Article retrieved; request text separately.\r\n", NNTP_NOTHING_FOLLOWS_VAL, buff, idbuff); if (ac > 1) ARTindex = ARTfind((ARTNUM)atol(buff));}/*** Return the header from the specified file, or NULL if not found.** We can estimate the Lines header, if that's what's wanted.*/STATIC char *GetHeader(qp, header, IsLines) register QIOSTATE *qp; register char *header; BOOL IsLines;{ static char buff[40]; register char *p;
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -