/* * OpenCache() * * return +1 if a valid cache file was found, set *pcfd, *psize * * return 0 if a valid cache file was not found, set *pcfd to >= 0 * if we were able to open a temporary file for a new * cache object, < 0 if we could not. * * return -1 If we found a negatively cached entry. * * (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 int OpenCache(const char *msgid, int *pcfd, int *psize); Prototype void AbortCache(int fd, const char *msgid, int closefd); Prototype void CommitCache(int fd, const char *msgid, int closefd); Prototype void DumpArticleFromCache(Connection *conn, const char *map, int size); unsigned int cacheHashNum(char *st) { hash_t h = hhash(st); return(abs(h.h1 + h.h2)); } void cacheFile(hash_t hv, char *path, int makedir) { int blen = 0; char fstr[32]; int i; sprintf(fstr, "%08x%08x", (int)hv.h1, (int)hv.h2); strcpy(path, PatExpand(CacheHomePat)); for (i = 0; i < DOpts.ReaderCacheDirs.dt_dirlvl; i++) { int c = DOpts.ReaderCacheDirs.dt_dirinfo[i]; int formsize = 0; char format[32]; while (c > 0) { formsize++; c = (c - 1) / 16; } sprintf(format, "/%%0%dx", formsize); blen = strlen(path); sprintf(path + blen, format, cacheHashNum(&fstr[i]) % DOpts.ReaderCacheDirs.dt_dirinfo[i]); if (makedir) { struct stat st; if (stat(path, &st) < 0) { mkdir(path, 0755); } } } blen = strlen(path); sprintf(path + blen, "/%08x.%08x", (int)hv.h1, (int)hv.h2); } int OpenCache(const char *msgid, int *pcfd, int *psize) { int fd; hash_t hv = hhash(msgid); char path[PATH_MAX]; *pcfd = -1; *psize = 0; /* * open cache file */ cacheFile(hv, path, 1); if ((fd = open(path, O_RDWR)) >= 0) { struct stat st; if (fstat(fd, &st) == 0) { if (st.st_size == 0) { /* negatively cached */ close(fd); return(-1); } *pcfd = fd; /* positively cached */ *psize = st.st_size; return(1); } close(fd); /* error */ return(0); } strcat(path, ".tmp"); if ((fd = open(path, O_RDWR|O_CREAT, 0644)) < 0) { return(0); /* error */ } if (hflock(fd, 0, XLOCK_EX|XLOCK_NB) < 0) { close(fd); /* someone else owns it */ return(0); } { struct stat st; struct stat st2; if (fstat(fd, &st) < 0 || st.st_nlink == 0) { close(fd); /* delete race */ return(0); } if (stat(path, &st2) < 0 || st.st_ino != st2.st_ino) { close(fd); /* rename race */ return(0); } if (st.st_size != 0) /* truncate if partial left over */ ftruncate(fd, 0); } /* * created new cache file. */ *pcfd = fd; return(0); } /* * AbortCache() - cache not successfully written, destroy */ void AbortCache(int fd, const char *msgid, int closefd) { char path[PATH_MAX]; hash_t hv = hhash(msgid); cacheFile(hv, path, 0); strcat(path, ".tmp"); remove(path); ftruncate(fd, 0); hflock(fd, 0, XLOCK_UN); if (closefd) close(fd); } /* * CommitCache() - cache successfully written, commit to disk */ void CommitCache(int fd, const char *msgid, int closefd) { char path1[PATH_MAX]; char path2[PATH_MAX]; hash_t hv = hhash(msgid); cacheFile(hv, path2, 0); strcpy(path1, path2); strcat(path1, ".tmp"); if (rename(path1, path2) < 0) remove(path1); hflock(fd, 0, XLOCK_UN); if (closefd) close(fd); } /* * DUMPARTICLEFROMCACHE() - article buffer is passed as an argument. The * buffer is already '.' escaped (but has no * terminating '.\r\n'), and \r\n terminated. * * if (conn->co_ArtMode == COM_BODYNOSTAT), just * do the body. Otherwise do the whole thing. */ void DumpArticleFromCache(Connection *conn, const char *map, int size) { int b = 0; int inHeader = 1; int nonl = 0; char line[8192]; char *vserver; char *buf; char ch; if (conn->co_Auth.dr_VServerDef) vserver = conn->co_Auth.dr_VServerDef->vs_ClusterName; else vserver = ""; while (b < size) { int i; int yes = 0; for (i = b; i < size && map[i] != '\n'; ++i) ; if (i < size) ++i; else nonl = 1; switch(conn->co_ArtMode) { case COM_STAT: break; case COM_HEAD: if (inHeader == 1) { yes = 1; if (i == b + 2 && map[b] == '\r' && map[b+1] == '\n') yes = 0; } break; case COM_ARTICLEWVF: { const char *ovdata; int ovlen; if ((ovdata = NNRetrieveHead(conn, &ovlen, NULL)) != NULL) { DumpOVHeaders(conn, ovdata, ovlen); MBPrintf(&conn->co_TMBuf, "\r\n"); conn->co_ArtMode = COM_BODYNOSTAT; } else { yes = 1; } } break; case COM_ARTICLE: yes = 1; break; case COM_BODY: case COM_BODYWVF: case COM_BODYNOSTAT: if (inHeader == 0) yes = 1; break; } /* * Do some header rewriting for virtual servers */ *line = '\0'; buf = (char *)&map[b]; ch = tolower(*buf); if (inHeader && *vserver && !conn->co_Auth.dr_VServerDef->vs_NoXrefHostUpdate && ch == 'x' && strncasecmp(buf, "Xref:", 5) == 0) { char *ptr; ptr = (char *)buf + 5; while (isspace((int)*ptr)) ptr++; while (!isspace((int)*ptr)) ptr++; while (isspace((int)*ptr)) ptr++; /* ptr should point to first group name */ if (*ptr) { int len; int e = i - 1; while (map[e] == '\r' || map[e] == '\n') e--; len = (e - b + 1) - (ptr - buf); if (len > sizeof(line) - 100) len = sizeof(line) - 100; sprintf(line, "Xref: %s ", vserver); e = strlen(line); memcpy(&line[e], ptr, len); line[e + len] = '\0'; strcat(line, "\r\n"); } } if (inHeader && *vserver && ch == 'p' && !conn->co_Auth.dr_VServerDef->vs_NoReadPath && strncasecmp(buf, "Path:", 5) == 0) { char *ptr; int vsl = strlen(vserver); ptr = (char *)buf + 5; while (isspace((int)*ptr)) ptr++; if (ptr && *ptr && (strncmp(vserver, ptr, vsl) || ((ptr[vsl] != '\0') && (ptr[vsl] != '!')))) { int len; int e = i - 1; while (map[e] == '\r' || map[e] == '\n') e--; len = (e - b + 1) - (ptr - buf); if (len > sizeof(line) - 100) len = sizeof(line) - 100; sprintf(line, "Path: %s!", vserver); e = strlen(line); memcpy(&line[e], ptr, len); line[e + len] = '\0'; strcat(line, "\r\n"); } } if (yes) { if (*line) MBWrite(&conn->co_TMBuf, line, strlen(line)); else MBWrite(&conn->co_TMBuf, map + b, i - b); } if (inHeader && i == b + 2 && map[b] == '\r' && map[b+1] == '\n') inHeader = 0; b = i; } if (nonl && conn->co_ArtMode != COM_STAT) MBPrintf(&conn->co_TMBuf, "\r\n", 2); }