/* * GROUPCTL.C * * Create/Modify/Delete newsgroups in the active file * */ #include "dreaderd/defs.h" #define MAXCMDARGS 8 const char *GrpCtlCancel(char *article); const char *GrpCtlGroupInfo(char *group); const char *GrpCtlListActive(char *match); const char *GrpCtlListGroupHash(char *match, char *hash, int fname); const char *GrpCtlListNewsgroups(char *match); const char *GrpCtlNewGroup(int ac, char **av, int pos); const char *GrpCtlRmGroup(char *group); const char *GrpCtlAdjust(int ac, char **av, int pos); const char *GrpCtlCheckGroups(int ac, char **av, int pos); KPDB *KDBActive; typedef struct GrpList { char *group; struct GrpList *next; } GrpList; void Usage(void) { printf("Usage:\n"); printf(" dgrpctl -s to use server active file (defaults to reader)\n"); printf(" dgrpctl listactive [PATTERN]\n"); printf(" - list the contents of active file\n"); printf(" dgrpctl listnewsgroups [PATTERN]\n"); printf(" - list the newsgroups and their descriptions\n"); printf(" dgrpctl listgrouphash [PATTERN [HASH]]\n"); printf(" - list the hashes of newsgroup names\n"); printf(" dgrpctl listgrouphashfile [PATTERN [HASH]]\n"); printf(" - list the index path/filename of a newsgroup\n"); printf(" dgrpctl groupinfo NEWSGROUP\n"); printf(" - display detailed information about a newsgroup\n"); printf(" dgrpctl rmgroup NEWSGROUP\n"); printf(" - remove the specified newsgroup\n"); printf(" dgrpctl newgroup NEWSGROUP [FLAGS] [MODERATOR] [DESCRIPTION]\n"); printf(" - add/modify the specified newsgroup\n"); printf(" flags = 'y' or 'm' (default is 'y')\n"); printf(" moderator = moderator email address or \"\"\n"); printf(" description = group description\n"); printf(" dgrpctl adjust NEWSGROUP NB|NE|NX value\n"); printf(" - adjust one of the specified numbers for group\n"); printf(" dgrpctl checkgroups [exec] filename|-\n"); printf(" - parse the checkgroups list and print changes required\n"); printf(" exec = actually make all the necessary changes\n"); printf(" - = read list on stdin\n"); printf("\n"); } int main(int ac, char **av) { int i = 1; const char *activefile; LoadDiabloConfig(ac, av); activefile = PatDbExpand(ReaderDActivePat); while (av[i] && av[i][0] == '-') { switch (av[i][1]) { case 'V': PrintVersion(); break; case 's': activefile = PatDbExpand(ServerDActivePat); break; default: Usage(); } i++; } KDBActive = KPDBOpen(activefile, O_RDWR); if (!KDBActive) { printf("Unable to open active file: %s\n", activefile); exit(1); } if (av[i] && av[i+1] && strcmp(av[i], "newgroup") == 0) GrpCtlNewGroup(ac, av, i+1); else if (av[i] && av[i+1] && strcmp(av[i], "modgroup") == 0) GrpCtlNewGroup(ac, av, i+1); else if (av[i] && av[i+1] && strcmp(av[i], "rmgroup") == 0) GrpCtlRmGroup(av[i+1]); else if (av[i] && strcmp(av[i], "listactive") == 0) GrpCtlListActive(av[i+1]); else if (av[i] && strcmp(av[i], "listgrouphash") == 0) GrpCtlListGroupHash(av[i+1], av[i+1] ? av[i+2] : NULL, 0); else if (av[i] && strcmp(av[i], "listgrouphashfile") == 0) GrpCtlListGroupHash(av[i+1], av[i+1] ? av[i+2] : NULL, 1); else if (av[i] && strcmp(av[i], "listnewsgroups") == 0) GrpCtlListNewsgroups(av[i+1]); else if (av[i] && strcmp(av[i], "cancel") == 0) GrpCtlCancel(av[i+1]); else if (av[i] && strcmp(av[i], "groupinfo") == 0) GrpCtlGroupInfo(av[i+1]); else if (av[i] && strcmp(av[i], "adjust") == 0) GrpCtlAdjust(ac, av, i+1); else if (av[i] && strcmp(av[i], "checkgroups") == 0) GrpCtlCheckGroups(ac, av, i+1); else { Usage(); } KPDBClose(KDBActive); return(0); } /* * CANCEL - */ const char * GrpCtlCancel(char *article) { #if 0 char *group; int artNo; OverInfo *ov; MyGroupHome = strdup(PatExpand(GroupHomePat)); if ((group = strchr(article, ':')) == NULL) { printf(">> cancel: Invalid group:artno (%s)\n", article); return(NULL); } *group++ = '\0'; artNo = atoi(group); group = article; if (ValidGroupName(group) < 0) { printf(">> cancel: Illegal newsgroup name\n"); return(NULL); } if (artNo <= 0) { printf(">> cancel: Illegal article number\n"); return(NULL); } ov = GetOverInfo(group); CancelOverArt(ov, artNo); #endif return(NULL); } /* * GROUPINFO - */ const char * GrpCtlGroupInfo(char *group) { int recLen; const char *rec; int nb; int iter = 0; if (ValidGroupName(group) < 0) { printf(">> groupinfo: Illegal newsgroup name\n"); return(NULL); } if ((rec = KPDBReadRecord(KDBActive, group, 0, &recLen)) == NULL) { printf(">> groupinfo: Unable to access group active data\n"); return(NULL); } else { const char *flags; int n; int flen; char buf[MAXGNAME]; printf("%s\n", group); n = strtol(KPDBGetField(rec, recLen, "NE", NULL, "0"), NULL, 10); printf(" NE=%010d ", n); nb = strtol(KPDBGetField(rec, recLen, "NB", NULL, "0"), NULL, 10); printf("NB=%010d ", nb); n = strtol(KPDBGetField(rec, recLen, "NX", NULL, "0"), NULL, 10); printf("NX=%010d ", n); flags = KPDBGetField(rec, recLen, "S", &flen, "n"); bcopy(flags, buf, flen); buf[flen] = 0; printf("flag=%s ", buf); iter = strtol(KPDBGetField(rec, recLen, "ITER", NULL, "0"), NULL, 10); printf("ITER=%d\n", iter); n = strtol(KPDBGetField(rec, recLen, "CTS", NULL, "0"), NULL, 16); printf(" CTS=%u=%s", n, ctime((const time_t *)&n)); n = strtol(KPDBGetField(rec, recLen, "LMTS", NULL, "0"), NULL, 16); printf(" LMTS=%u=%s", n, ctime((const time_t *)&n)); flags = KPDBGetField(rec, recLen, "GD", &flen, "?"); bcopy(flags, buf, flen); buf[flen] = 0; printf(" GD=%s\n", buf); } { int fd; int maxarts; int pos; char path[PATH_MAX]; OverHead oh; OverArt oa; struct stat st; const char *gname = GFName(group, GRPFTYPE_OVER, 0, 1, iter, &DOpts.ReaderGroupHashMethod); printf(" grouphash=%s\n", GFHash(group, &DOpts.ReaderGroupHashMethod)); snprintf(path, sizeof(path), "%s/%s", PatExpand(GroupHomePat), gname); printf(" overfile=%s\n", path); if ((fd = open(path, O_RDONLY)) < 0) { printf(" Unable to open %s\n", path); return(NULL); } if (read(fd, &oh, sizeof(oh)) != sizeof(oh)) { printf(" Error reading over header data\n"); close(fd); return(NULL); } if (oh.oh_Version != OH_VERSION || oh.oh_ByteOrder != OH_BYTEORDER) { printf(" Wrong version or byte order\n"); close(fd); return(NULL); } fstat(fd, &st); maxarts = (st.st_size - oh.oh_HeadSize) / sizeof(OverArt); printf(" overfilesize=%u maxarts=%u\n", (unsigned)st.st_size, maxarts); pos = oh.oh_HeadSize + ((nb & 0x7FFFFFFF) % maxarts) * sizeof(OverArt); lseek(fd, pos, 0); if (read(fd, &oa, sizeof(oa)) != sizeof(oa)) { printf(" Unable to read OverArt\n"); close(fd); return(NULL); } if (oa.oa_ArtNo > 0) { printf(" StartNo=%u\n", oa.oa_ArtNo); } else if (oa.oa_ArtNo == -1) { printf(" Article cancelled\n"); } else if (oa.oa_ArtNo == -2) { printf(" Article expired\n"); } else { printf(" Article not found\n"); } if (oa.oa_ArtNo != nb) { printf(" artNoMismatch (got=%d wanted=%d)\n", oa.oa_ArtNo, nb); } printf(" Time received: %d = %s", oa.oa_TimeRcvd, ctime((time_t *)&oa.oa_TimeRcvd)); } return(NULL); } /* * LISTACTIVE - */ const char * GrpCtlListActive(char *match) { int recLen; int recOff; for (recOff = KPDBScanFirst(KDBActive, 0, &recLen); recOff; recOff = KPDBScanNext(KDBActive, recOff, 0, &recLen) ) { int groupLen; char grpbuf[MAXGNAME]; const char *rec = KPDBReadRecordAt(KDBActive, recOff, 0, NULL); const char *group = KPDBGetField(rec, recLen, NULL, &groupLen, NULL); if (group) { if (groupLen >= MAXGNAME) groupLen = MAXGNAME - 1; bcopy(group, grpbuf, groupLen); grpbuf[groupLen] = 0; if (match == NULL || WildCmp(match, grpbuf) == 0) { const char *flags; int ne; int nb; int flen; flags = KPDBGetField(rec, recLen, "S", &flen, "n"); ne = strtol(KPDBGetField(rec, recLen, "NE", NULL, "0"), NULL, 10); nb = strtol(KPDBGetField(rec, recLen, "NB", NULL, "0"), NULL, 10); printf("%s %010d %010d ", grpbuf, ne, nb); bcopy(flags, grpbuf, flen); grpbuf[flen] = 0; printf("%s\n", grpbuf); } } } return(NULL); } /* * LISTGROUPHASH - */ const char * GrpCtlListGroupHash(char *match, char *hash, int dir) { int recLen; int recOff; int wild = 1; if (hash != NULL) SetGroupHashMethod(hash, &DOpts.ReaderGroupHashMethod); if (match != NULL && index(match, '?') == NULL && index(match, '*') == NULL) wild = 0; for (recOff = KPDBScanFirst(KDBActive, 0, &recLen); wild && recOff; recOff = KPDBScanNext(KDBActive, recOff, 0, &recLen) ) { int groupLen; char grpbuf[MAXGNAME]; const char *rec = KPDBReadRecordAt(KDBActive, recOff, 0, NULL); const char *group = KPDBGetField(rec, recLen, NULL, &groupLen, NULL); if (group) { if (groupLen >= MAXGNAME) groupLen = MAXGNAME - 1; bcopy(group, grpbuf, groupLen); grpbuf[groupLen] = 0; if (match == NULL || WildCmp(match, grpbuf) == 0) { if (dir) printf("%s %s\n", GFName(grpbuf, GRPFTYPE_OVER, 0, 1, 0, &DOpts.ReaderGroupHashMethod), grpbuf ); else printf("%s %s\n", GFHash(grpbuf, &DOpts.ReaderGroupHashMethod), grpbuf); } } } if (!wild) { if (dir) printf("%s %s\n", GFName(match, GRPFTYPE_OVER, 0, 1, 0, &DOpts.ReaderGroupHashMethod), match ); else printf("%s %s\n", GFHash(match, &DOpts.ReaderGroupHashMethod), match); } return(NULL); } /* * LISTNEWSGROUPS - */ const char * GrpCtlListNewsgroups(char *match) { int recLen; int recOff; for (recOff = KPDBScanFirst(KDBActive, 0, &recLen); recOff; recOff = KPDBScanNext(KDBActive, recOff, 0, &recLen) ) { int groupLen; char grpbuf[MAXGNAME]; const char *rec = KPDBReadRecordAt(KDBActive, recOff, 0, NULL); const char *group = KPDBGetField(rec, recLen, NULL, &groupLen, NULL); if (group) { if (groupLen >= MAXGNAME) groupLen = MAXGNAME - 1; bcopy(group, grpbuf, groupLen); grpbuf[groupLen] = 0; if (match == NULL || WildCmp(match, grpbuf) == 0) { const char *desc; desc = KPDBGetFieldDecode(rec, recLen, "GD", NULL, NULL); printf("%s\t%s\n", grpbuf, (desc != NULL) ? desc : "?"); } } } return(NULL); } /* * NEWGROUP - */ const char * GrpCtlNewGroup(int ac, char **av, int pos) { char *flags; char *moderator = NULL; char description[256]; char *group = NULL; int isNew = 0; int hasCts = 0; const char *rec; int recLen; description[0] = 0; group = av[pos++]; if ((flags = av[pos++]) == NULL) flags = "y"; else { if (av[pos]) { moderator = av[pos++]; if (strcmp(moderator, "\"\"") == 0) moderator = NULL; } if (av[pos]) strcpy(description, av[pos]); } if (ValidGroupName(group) < 0) { printf(">> Newgroup: Illegal newsgroup name\n"); return(NULL); } if (flags && strlen(flags) != 1) { printf(">> Newgroup: Illegal flag: %s \n", flags); return(NULL); } /* * Read and lock the record. If the record does not exist, create a new * record (and lock that). */ if ((rec = KPDBReadRecord(KDBActive, group, KP_LOCK, &recLen)) == NULL) { KPDBWrite(KDBActive, group, "NB", "0000000001", KP_LOCK); KPDBWrite(KDBActive, group, "NE", "0000000000", KP_LOCK_CONTINUE); isNew = 1; } else { if (KPDBGetField(rec, recLen, "CTS", NULL, NULL) != NULL) hasCts = 1; } if (moderator) { SanitizeString(moderator); KPDBWriteEncode(KDBActive, group, "M", moderator, KP_LOCK_CONTINUE); } if (description) { SanitizeDescString(description); KPDBWriteEncode(KDBActive, group, "GD", description, KP_LOCK_CONTINUE); } { /* * add creation-time-stamp and last-modified-time-stamp * for group. */ char tsBuf[64]; sprintf(tsBuf, "%08x", (int)time(NULL)); if (hasCts == 0) KPDBWrite(KDBActive, group, "CTS", tsBuf, KP_LOCK_CONTINUE); KPDBWrite(KDBActive, group, "LMTS", tsBuf, KP_LOCK_CONTINUE); } KPDBWrite(KDBActive, group, "S", flags, KP_UNLOCK); if (isNew) { printf(">> Newgroup: Created new group %s flags=%s\n", group, flags ); } else printf(">> Newgroup: updated group %s flags=%s moderator=%s description=%s\n", group, flags, (moderator ? moderator : "no chg"), (description ? description : "no chg") ); return(NULL); } const char * GrpCtlRmGroup(char *options) { const char *rec; int recLen; char *group; group = options; if (ValidGroupName(group) < 0) { printf(">> Rmgroup: Illegal newsgroup name\n"); return(NULL); } /* * Note that the record isn't locked in this case. */ if ((rec = KPDBReadRecord(KDBActive, group, 0, &recLen)) != NULL) { KPDBDelete(KDBActive, group); printf(">> RmGroup: Deleted group %s\n", group); } else { printf(">> RmGroup: No group %s\n", group); } return(NULL); } /* * ADJUST - */ const char * GrpCtlAdjust(int ac, char **av, int pos) { char *field; char *group = NULL; int adjust = 0; int oldval = 0; const char *rec; int recLen; if ((group = av[pos++]) == NULL) { printf(">> adjust: missing newsgroup\n"); return(NULL); } if ((field = av[pos++]) == NULL) { printf(">> adjust: missing field to adjust\n"); return(NULL); } if ((av[pos]) == NULL) { printf(">> adjust: missing adjust value\n"); return(NULL); } adjust = strtol(av[pos++], NULL, 0); if (ValidGroupName(group) < 0) { printf(">> adjust: Illegal newsgroup name\n"); return(NULL); } if (strcmp(field, "NB") != 0 && strcmp(field, "NE") != 0 && strcmp(field, "NX") != 0) { printf(">> adjust: Illegal field: %s \n", field); return(NULL); } /* * Read and lock the record. If the record does not exist, create a new * record (and lock that). */ if ((rec = KPDBReadRecord(KDBActive, group, KP_LOCK, &recLen)) != NULL) { char buf[32]; oldval = strtol(KPDBGetField(rec, recLen, field, NULL, "0"), NULL, 10); sprintf(buf, "%010d", oldval + adjust); KPDBWrite(KDBActive, group, field, buf, KP_LOCK); } else { printf(">> adjust: group '%s' not found\n", group); return(NULL); } printf(">> adjust: %s for %s adjusted from %d to %d\n", field, group, oldval, oldval + adjust); return(NULL); } /* * CHECKGROUPS - */ const char * GrpCtlCheckGroups(int ac, char **av, int pos) { char *filename; int exec = 0; FILE *f; char buf[128]; const char *rec; int recLen; GrpList *grpList = NULL; char *hierarchy = NULL; if (av[pos] != NULL && strcmp(av[pos], "exec") == 0) { pos++; exec = 1; } if ((filename = av[pos++]) == NULL) { printf(">> checkgroups: missing filename\n"); return(NULL); } if (strcmp(filename, "-") == 0) { f = stdin; } else { f = fopen(filename, "r"); if (f == NULL) { printf(">> checkgroups: cannot open %s\n", filename); return(NULL); } } while (fgets(buf, sizeof(buf), f) != NULL) { char *newsgroup; char *description = NULL; char *flags = "y"; if ((newsgroup = strtok(buf, " \t\r\n")) == NULL) continue; if (ValidGroupName(newsgroup) < 0) /* valid group name */ continue; if (strchr(newsgroup, '.') == NULL) /* sanity check */ continue; if ((description = strtok(NULL, "\r\n")) == NULL) continue; if (hierarchy == NULL) { char *p; hierarchy = (char *)malloc(strlen(newsgroup) + 2); strcpy(hierarchy, newsgroup); p = strchr(hierarchy, '.'); if (p != NULL) *p = 0; strcat(hierarchy, ".*"); } /* * clean up the description */ SanitizeDescString(description); { int l = strlen(description); while (*description == ' ' || *description == '\t') { ++description; --l; } while (l > 0 && (description[l-1] == ' ' || description[l-1] == '\t')) { description[--l] = 0; } } if (strstr(description, "(Moderated)") != 0) flags = "m"; else flags = "y"; /* * Keep a list of all the newsgroups to check for deleted groups */ { GrpList *gl = (GrpList *)malloc(sizeof(GrpList)); gl->next = grpList; gl->group = strdup(newsgroup); grpList = gl; } if ((rec = KPDBReadRecord(KDBActive, newsgroup, KP_LOCK, &recLen)) == NULL) { if (exec) { KPDBWrite(KDBActive, newsgroup, "NB", "0000000001", KP_LOCK); KPDBWrite(KDBActive, newsgroup, "NE", "0000000000", KP_LOCK_CONTINUE); printf(">> Checkgroups: Added group %s\n", newsgroup); } else { printf("dgrpctl newgroup %s %s \"\" \"%s\"\n", newsgroup, flags, description); } } else { const char *p; char buf[MAXGNAME]; int plen; int needflags = 0; int needdesc = 0; p = KPDBGetField(rec, recLen, "S", &plen, "n"); bcopy(p, buf, plen); buf[plen] = 0; if (strcmp(buf, flags) != 0) needflags = 1; p = KPDBGetFieldDecode(rec, recLen, "GD", NULL, NULL); if (p != NULL) strcpy(buf, p); else buf[0] = 0; if (p == NULL || strcmp(buf, description) != 0) needdesc = 1; if (needflags || needdesc) { if (exec) { KPDBWrite(KDBActive, newsgroup, "S", flags, KP_UNLOCK); KPDBWriteEncode(KDBActive, newsgroup, "GD", description, KP_LOCK_CONTINUE); printf(">> Checkgroups: Modified group %s\n", newsgroup); } else { printf("dgrpctl modgroup %s %s \"\" \"%s\"\n", newsgroup, flags, description); } } } } { int recLen; int recOff; for (recOff = KPDBScanFirst(KDBActive, 0, &recLen); recOff; recOff = KPDBScanNext(KDBActive, recOff, 0, &recLen) ) { int groupLen; char grpbuf[MAXGNAME]; const char *rec = KPDBReadRecordAt(KDBActive, recOff, 0, NULL); const char *group = KPDBGetField(rec, recLen, NULL, &groupLen, NULL); if (group) { if (groupLen >= MAXGNAME) groupLen = MAXGNAME - 1; bcopy(group, grpbuf, groupLen); grpbuf[groupLen] = 0; if (WildCmp(hierarchy, grpbuf) == 0) { GrpList *g; int found = 0; for (g = grpList; g != NULL; g = g->next) { if (strcmp(grpbuf, g->group) == 0) { found = 1; break; } } if (!found) { if (exec) { const char *rec; if ((rec = KPDBReadRecord(KDBActive, grpbuf, 0, &recLen)) != NULL) { KPDBDelete(KDBActive, grpbuf); printf(">> Checkgroups: Deleted group %s\n", grpbuf); } } else { printf("dgrpctl rmgroup %s\n", grpbuf); } } } } } } return(NULL); }