/* * SPOOL.C - Spool server state machine * * (c)Copyright 1998, Matthew Dillon, All Rights Reserved. Refer to * the COPYRIGHT file in the base directory of this distribution * for specific rights granted. */ #include "defs.h" Prototype void NNSpoolCommand1(Connection *conn); void NNSpoolResponse1(Connection *conn); void NNSpoolResponse2(Connection *conn); void NNSpoolResponse3(Connection *conn); void NNSpoolResponseScrap(Connection *conn); void NNGetLocal1(Connection *conn); void NNSpoolCommand1(Connection *conn) { ServReq *sreq = conn->co_SReq; if (DebugOpt) printf("NNSpoolCommand1: article %s\n", sreq->sr_MsgId); if (conn->co_Desc->d_LocalSpool) { if (strcmp(conn->co_Desc->d_LocalSpool, "/") == 0) MBPrintf(&conn->co_TMBuf, "whereis %s\r\n", sreq->sr_MsgId); else MBPrintf(&conn->co_TMBuf, "whereis %s REL\r\n", sreq->sr_MsgId); NNGetLocal1(conn) ; } else { MBPrintf(&conn->co_TMBuf, "article %s\r\n", sreq->sr_MsgId); conn->co_ServerArticleRequestedCount++; NNSpoolResponse1(conn); } } /* * Retrieve location */ void NNGetLocal1(Connection *conn) { ServReq *sreq = conn->co_SReq; char *buf; char *ptr; char *filename; int len; int offset; int size; conn->co_Func = NNGetLocal1; conn->co_State = "getlcl1"; if ((len = MBReadLine(&conn->co_RMBuf, &buf)) > 0) { conn->co_ServerByteCount += len; if (strtol(buf, NULL, 10) == 223) { /* * sr_CConn may be NULL if client was terminated while * server operation was still in progress. */ if (conn->co_SReq->sr_CConn) { MBFree(&conn->co_SReq->sr_CConn->co_ArtBuf); switch(sreq->sr_CConn->co_ArtMode) { case COM_BODYNOSTAT: break; case COM_STAT: case COM_HEAD: case COM_BODY: case COM_ARTICLE: MBLogPrintf(sreq->sr_CConn, &sreq->sr_CConn->co_ArtBuf, "%03d 0 %s %s\r\n", GoodRC(sreq->sr_CConn), sreq->sr_MsgId, GoodResId(sreq->sr_CConn) ); break; case COM_BODYWVF: MBLogPrintf(sreq->sr_CConn, &sreq->sr_CConn->co_ArtBuf, "%03d %d %s %s\r\n", GoodRC(sreq->sr_CConn), sreq->sr_CConn->co_ArtNo, sreq->sr_MsgId, GoodResId(sreq->sr_CConn) ); break; case COM_ARTICLEWVF: { const char *ovdata; const char *msgid; int ovlen; if ((ovdata = NNRetrieveHead(sreq->sr_CConn, &ovlen, &msgid, NULL, NULL, NULL)) != NULL) { MBLogPrintf(sreq->sr_CConn, &sreq->sr_CConn->co_TMBuf, "%03d %d %s %s\r\n", GoodRC(sreq->sr_CConn), sreq->sr_CConn->co_ArtNo, sreq->sr_MsgId, GoodResId(sreq->sr_CConn) ); DumpOVHeaders(sreq->sr_CConn, ovdata, ovlen); MBPrintf(&sreq->sr_CConn->co_TMBuf, "\r\n"); sreq->sr_CConn->co_ArtMode = COM_BODYNOSTAT; } else { NNSpoolResponseScrap(conn); return; /* bleh */ } } break; } } /* * We have to get additionnal information from the buffer */ strtok(buf, " ") ; offset = -1; size = -1; filename = NULL; while ((ptr = strtok(NULL, " ")) != NULL) { if (strcasecmp(ptr, "in") == 0) { filename = strtok(NULL," "); } else if (strcasecmp("offset", ptr) == 0) { ptr = strtok(NULL," "); if (ptr) { offset = strtol(ptr, NULL, 10); if (offset == LONG_MIN || offset == LONG_MAX) offset = -1; } else { offset = -1; } } else if (strcasecmp("length", ptr) == 0) { ptr = strtok(NULL," "); if (ptr) { size = strtol(ptr, NULL, 10); if (size == LONG_MIN || size == LONG_MAX) size = -1; } else { size = -1; } } } if (filename != NULL && offset >= 0 && size >= 0) { int lf; static char filepath[PATH_MAX]; if (DebugOpt) printf("NNGetLocal1 : whereis returned file %s offset %i size %i\n", filename, offset, size); if (strcmp(conn->co_Desc->d_LocalSpool, "/") == 0) snprintf(filepath, sizeof(filepath), "%s", filename); else snprintf(filepath, sizeof(filepath), "%s/%s", conn->co_Desc->d_LocalSpool, filename); lf = open(filepath, O_RDONLY); if (lf >= 0 && NewDFA(conn, lf, offset, size)) { NNSendLocalArticle(conn); } else { if (lf == -1) { logit(LOG_ERR, "NNGetLocal1 : problem while openning file %s", filepath); } else { logit(LOG_ERR, "NNGetLocal1 : problem while seeking file %s offset %i", filepath, offset); } close(lf); MBPrintf(&conn->co_TMBuf, "article %s\r\n", sreq->sr_MsgId); NNSpoolResponse1(conn); } } else { logit(LOG_ERR, "NNGetLocal1 : error while getting whereis informations"); MBPrintf(&conn->co_TMBuf, "article %s\r\n", sreq->sr_MsgId); NNSpoolResponse1(conn); } } else { /* * whereis request failed, try the normal way */ MBPrintf(&conn->co_TMBuf, "article %s\r\n", sreq->sr_MsgId); conn->co_ServerArticleRequestedCount++; NNSpoolResponse1(conn); } } else if (len < 0) { NNServerTerminate(conn); } /* else we haven't got the response yet */ } /* * Retrieve return code */ void NNSpoolResponse1(Connection *conn) { ServReq *sreq = conn->co_SReq; char *buf; int len; conn->co_Func = NNSpoolResponse1; conn->co_State = "spres1"; if ((len = MBReadLine(&conn->co_RMBuf, &buf)) > 0) { conn->co_ServerByteCount += len; if (strtol(buf, NULL, 10) == 220) { /* We have a positive answer, we may cache article */ if (conn->co_Desc->d_Cache) { CreateCache(conn); } /* * sr_CConn may be NULL if client was terminated while * server operation was still in progress. */ if (conn->co_SReq->sr_CConn) { MBFree(&conn->co_SReq->sr_CConn->co_ArtBuf); switch(sreq->sr_CConn->co_ArtMode) { case COM_BODYNOSTAT: break; case COM_STAT: case COM_HEAD: case COM_BODY: case COM_ARTICLE: MBLogPrintf(sreq->sr_CConn, &sreq->sr_CConn->co_ArtBuf, "%03d 0 %s %s\r\n", GoodRC(sreq->sr_CConn), sreq->sr_MsgId, GoodResId(sreq->sr_CConn) ); break; case COM_BODYWVF: MBLogPrintf(sreq->sr_CConn, &sreq->sr_CConn->co_ArtBuf, "%03d %d %s %s\r\n", GoodRC(sreq->sr_CConn), sreq->sr_CConn->co_ArtNo, sreq->sr_MsgId, GoodResId(sreq->sr_CConn) ); break; case COM_ARTICLEWVF: { const char *ovdata; const char *msgid; int ovlen; if ((ovdata = NNRetrieveHead(sreq->sr_CConn, &ovlen, &msgid, NULL, NULL, NULL)) != NULL) { MBLogPrintf(sreq->sr_CConn, &sreq->sr_CConn->co_TMBuf, "%03d %d %s %s\r\n", GoodRC(sreq->sr_CConn), sreq->sr_CConn->co_ArtNo, sreq->sr_MsgId, GoodResId(sreq->sr_CConn) ); DumpOVHeaders(sreq->sr_CConn, ovdata, ovlen); MBPrintf(&sreq->sr_CConn->co_TMBuf, "\r\n"); sreq->sr_CConn->co_ArtMode = COM_BODYNOSTAT; } else { NNSpoolResponseScrap(conn); return; /* bleh */ } } break; } } NNSpoolResponse2(conn); return; } else if (strtol(buf, NULL, 10) == 430) { conn->co_ServerArticleNotFoundErrorCount++; } else { conn->co_ServerArticleMiscErrorCount++; } if (sreq->sr_CConn == NULL) NNFinishSReq(conn, NULL, 0); else if (sreq->sr_CConn->co_ArtMode == COM_BODYNOSTAT) NNFinishSReq(conn, "(article not available)\r\n.\r\n", 1); else if (sreq->sr_CConn->co_RequestFlags == ARTFETCH_ARTNO) NNFinishSReq(conn, "423 No such article number in this group\r\n", 1); else NNFinishSReq(conn, "430 No such article\r\n", 1); } else if (len < 0) { NNServerTerminate(conn); } /* else we haven't got the response yet */ } /* * Retrieve headers */ void NNSpoolResponse2(Connection *conn) { char *buf; int len; ServReq *sreq = conn->co_SReq; char *vserver; int doneconn; char ch; conn->co_Func = NNSpoolResponse2; conn->co_State = "spres2"; /* * We need to check that co_Auth.dr_VServerDef is defined * because it probably won't be for a connection to a backend * spool */ if (sreq->sr_CConn && sreq->sr_CConn->co_Auth.dr_VServerDef) vserver = sreq->sr_CConn->co_Auth.dr_VServerDef->vs_ClusterName; else vserver = ""; while ((len = MBReadLine(&conn->co_RMBuf, &buf)) > 0) { conn->co_ServerByteCount += len; if (len == 2 && strcmp(buf, "\r") == 0) { if (sreq->sr_Cache) fwrite("\r\n", 1, 2, sreq->sr_Cache); if (sreq->sr_CConn) { switch(sreq->sr_CConn->co_ArtMode) { case COM_ARTICLE: MBPrintf(&sreq->sr_CConn->co_ArtBuf, "\r\n"); break; } } NNSpoolResponse3(conn); return; } if (len == 3 && strcmp(buf, ".\r") == 0) { if (sreq->sr_CConn == NULL) NNFinishSReq(conn, NULL, 0); else if (sreq->sr_CConn->co_ArtMode == COM_BODYNOSTAT) NNFinishSReq(conn, "(article not available)\r\n.\r\n", 1); else if (sreq->sr_CConn->co_RequestFlags == ARTFETCH_ARTNO) NNFinishSReq(conn, "423 No such article number in this group\r\n", 1); else NNFinishSReq(conn, "430 No such article\r\n", 1); return; } /* * Only munge Xref: header if we asked for it, the wrong Xref: * header can blow up news readers, so we need to be able to * change it when viewing to match the Path: * * sr_CConn may be NULL if the client terminated or if an * autonomous lookahead request was issued. * */ doneconn = 0; ch = tolower(*buf); if (*vserver && sreq->sr_CConn && !sreq->sr_CConn->co_Auth.dr_VServerDef->vs_NoXrefHostUpdate && ch == 'x' && strncasecmp(buf, "Xref:", 5) == 0) { char *ptr; int l; /* * The len includes the trailing \n converted to a \0 */ l = len - 2; while (l > 5 && (buf[l] == '\r' || buf[l] == '\n')) l--; ptr = buf + 5; while (isspace((int)*ptr)) ptr++; while (!isspace((int)*ptr)) ptr++; while (isspace((int)*ptr)) ptr++; if (*ptr) { char line[8192]; int e; l = (buf + l) - ptr + 1; sprintf(line, "Xref: %s ", vserver); e = strlen(line); memcpy(&line[e], ptr, l); line[e + l] = '\0'; strcat(line, "\r\n"); switch(sreq->sr_CConn->co_ArtMode) { case COM_HEAD: case COM_ARTICLE: MBWrite(&sreq->sr_CConn->co_ArtBuf, line, strlen(line)); break; } doneconn = 1; } } if (*vserver && sreq->sr_CConn && !sreq->sr_CConn->co_Auth.dr_VServerDef->vs_NoReadPath && ch == 'p' && strncasecmp(buf, "Path:", 5) == 0) { char *ptr; int l; int vsl = strlen(vserver); /* * The len includes the trailing \n converted to a \0 */ l = len - 2; while (l > 5 && (buf[l] == '\r' || buf[l] == '\n')) l--; ptr = buf + 5; while (isspace((int)*ptr)) ptr++; if (*ptr && (strncmp(vserver, ptr, vsl) || ((ptr[vsl] != '\0') && (ptr[vsl] != '!')))) { char line[8192]; int e; l = (buf + l) - ptr + 1; sprintf(line, "Path: %s!", vserver); e = strlen(line); memcpy(&line[e], ptr, l); line[e + l] = '\0'; strcat(line, "\r\n"); switch(sreq->sr_CConn->co_ArtMode) { case COM_HEAD: case COM_ARTICLE: MBWrite(&sreq->sr_CConn->co_ArtBuf, line, strlen(line)); break; } doneconn = 1; } } if (len) { buf[len-1] = '\n'; if (sreq->sr_Cache) fwrite(buf, 1, len, sreq->sr_Cache); /* * sr_CConn may be NULL if the client terminated or if an * autonomous lookahead request was issued. */ if (!doneconn && sreq->sr_CConn) { switch(sreq->sr_CConn->co_ArtMode) { case COM_HEAD: case COM_ARTICLE: MBWrite(&sreq->sr_CConn->co_ArtBuf, buf, len); break; } } } } if (len < 0) { NNServerTerminate(conn); } /* else we are still waiting for input */ } /* * Retrieve the body, place in client's ArtBuf */ void NNSpoolResponse3(Connection *conn) { char *buf; ServReq *sreq = conn->co_SReq; conn->co_Func = NNSpoolResponse3; conn->co_State = "spres3"; for (;;) { int len = MBReadLine(&conn->co_RMBuf, &buf); int error = 0; /* * If an error occurs and we are not in FastCopyOpt mode, * we can simply terminate the server and the request will be * retried. If we are an FastCopyOpt mode we have already * started writing the article back to the client and cannot * simply cut things off. We simulate an ending, make sure * we do not commit the article to the cache, and *then* * terminate the server. */ if (len < 0) { if (FastCopyOpt == 0) { NNServerTerminate(conn); return; } len = 3; buf = ".\r"; error = 1; } /* * If len is 0, we have nothing to do */ if (len == 0) break; conn->co_ServerByteCount += len + 1; if (len == 3 && strcmp(buf, ".\r") == 0) { conn->co_ServerArticleCount++; if (sreq->sr_CConn) { MBCopy( &sreq->sr_CConn->co_ArtBuf, &sreq->sr_CConn->co_TMBuf ); } if (sreq->sr_Cache) { fflush(sreq->sr_Cache); if (ferror(sreq->sr_Cache) || error) AbortCache(fileno(sreq->sr_Cache), sreq->sr_MsgId, 0); else CommitCache(conn, 0); fclose(sreq->sr_Cache); sreq->sr_Cache = NULL; } if (sreq->sr_CConn == NULL) { NNFinishSReq(conn, NULL, 0); } else { switch(sreq->sr_CConn->co_ArtMode) { case COM_ARTICLE: case COM_BODY: case COM_BODYWVF: case COM_BODYNOSTAT: case COM_HEAD: if (error) MBPrintf(&sreq->sr_CConn->co_TMBuf, "(spool server died prior to completion of the article dump)\r\n"); NNFinishSReq(conn, ".\r\n", 0); break; case COM_STAT: NNFinishSReq(conn, "", 0); break; default: NNFinishSReq(conn, "(something blew up in the spool code)\r\n.\r\n", 0); break; } } /* * If an error occured ( can only happen if we were in FastCopyOpt * mode ), we have to terminate the server now, after we've * finished processing the client request. */ if (error) { NNServerTerminate(conn); } return; } if (len > 0) buf[len-1] = '\n'; if (sreq->sr_CConn) { switch(sreq->sr_CConn->co_ArtMode) { case COM_ARTICLE: case COM_BODY: case COM_BODYWVF: case COM_BODYNOSTAT: MBWrite(&sreq->sr_CConn->co_ArtBuf, buf, len); break; } } if (sreq->sr_Cache) fwrite(buf, 1, len, sreq->sr_Cache); } if (FastCopyOpt && sreq->sr_CConn) { MBCopy( &sreq->sr_CConn->co_ArtBuf, &sreq->sr_CConn->co_TMBuf ); } /* still waiting for input */ } void NNSpoolResponseScrap(Connection *conn) { char *buf; int len; ServReq *sreq = conn->co_SReq; conn->co_Func = NNSpoolResponse3; conn->co_State = "spres3"; while ((len = MBReadLine(&conn->co_RMBuf, &buf)) > 0) { conn->co_ServerByteCount += len + 1; if (len == 3 && strcmp(buf, ".\r") == 0) { conn->co_ServerArticleCount++; if (sreq->sr_Cache) { fflush(sreq->sr_Cache); if (ferror(sreq->sr_Cache)) AbortCache(fileno(sreq->sr_Cache), sreq->sr_MsgId, 0); else CommitCache(conn, 0); fclose(sreq->sr_Cache); sreq->sr_Cache = NULL; } if (sreq->sr_CConn == NULL) NNFinishSReq(conn, NULL, 0); else if (sreq->sr_CConn->co_RequestFlags == ARTFETCH_ARTNO) NNFinishSReq(conn, "423 No such article number in this group\r\n", 1); else NNFinishSReq(conn, "430 No such article\r\n", 1); return; } } if (len < 0) { NNServerTerminate(conn); } /* else we are still waiting for input */ }