/*
* LIST.C
*
* NNTP LIST commands.
*
* (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 NNTPList(Connection *conn, char **pptr);
Prototype void NNTPListActive(Connection *conn, char **pptr);
Prototype void NNTPListActiveTimes(Connection *conn, char **pptr);
Prototype void NNTPListNewsgroups(Connection *conn, char **pptr);
Prototype void NNTPListExtensions(Connection *conn, char **pptr);
Prototype void NNTPListDistributions(Connection *conn, char **pptr);
Prototype void NNTPListDistribPats(Connection *conn, char **pptr);
Prototype void NNTPListOverviewFmt(Connection *conn, char **pptr);
Prototype void NNTPListSubscriptions(Connection *conn, char **pptr);
Prototype void NNTPListModerators(Connection *conn, char **pptr);
Prototype void NNTPListGroup(Connection *conn, char **pptr);
Prototype void NNTPNewgroups(Connection *conn, char **pptr);
Prototype void NNTPXGTitle(Connection *conn, char **pptr);
void NNStartListActiveScan(Connection *conn, const char *wc, TimeRestrict *tr, int mode);
void NNListActiveScan(Connection *conn);
void NNListGroup(Connection *conn);
/*
* Non-blocking calls which attempt to obtain a read or write lock on the
* active file cache. Return 0 in case of success, -1 in case of failure
* due to other locks not permitting the request.
*
* Unlock calls always succeed.
*/
Prototype int ActiveCacheReadLock(void);
Prototype void ActiveCacheReadUnlock(void);
Prototype int ActiveCacheWriteLock(void);
Prototype void ActiveCacheWriteUnlock(void);
/* Cache management functions */
Prototype void ActiveCacheFreeMain(void);
Prototype GroupList *ListActiveGroups(Connection *conn, char *pat);
int ActiveCacheValid(KPDB *kpdb);
void ActiveCacheMarkValid(KPDB *kpdb);
activeCacheEnt *ActiveCacheFind(activeCacheEnt *ac, int cts);
activeCacheEnt *ActiveCacheGetNext(activeCacheEnt *ac);
void ActiveCacheInsertHelper(activeCacheEnt **acp, activeCacheEnt *parent,
char *newGroup, int newCts);
void ActiveCacheInsert(activeCacheEnt **acp, char *newGroup, int newCts);
/*
* Active file cache -- locks, last-updated, and actual cache
*
* The read lock is 0 iff no request is accessing the database at this
* time. It is probably not necessary, but it's there as a safeguard
* against bad code which doesn't copy cache contents into local space
* before letting other processes run. When there are multiple requests
* in progress accessing the cache, the read lock should be equal to
* the number of such requests.
*
* The write lock is 0 iff it is safe to use the cache. The write lock
* and the read lock should never be non-zero at the same time.
*
* activeCache_AppendSeq is the active file append sequence. The cache
* is valid iff activeCache_AppendSeq is the same as the append sequence
* in the active file KPDB and activeCache is not NULL.
*
* Note that CTS might change without an append happening. However,
* this cache should ONLY be used for "list newgroups", and we ignore
* groups which spuriously change their CTS field without really
* being a new group.
*/
int activeCache_ReadLock = 0;
int activeCache_WriteLock = 0;
int activeCache_AppendSeq = 0;
struct activeCacheEnt *activeCache = NULL;
MemPool *activeCache_MemPool;
void
NNTPList(Connection *conn, char **pptr)
{
/*
* list active [groupwild]
* list active.times
* list newsgroups [groupwild]
* list extensions
* list distributions
* list distrib.pats
* list overview.fmt
* list subscriptions
* list moderators
*/
char *ltype = parseword(pptr, " \t");
if (ltype) {
int i;
for (i = 0; ltype[i]; ++i)
ltype[i] = tolower((int)(unsigned char)ltype[i]);
}
if (ltype == NULL) {
NNTPListActive(conn, NULL);
} else if (strcmp(ltype, "active") == 0) {
NNTPListActive(conn, pptr);
} else if (strcmp(ltype, "active.times") == 0) {
NNTPListActiveTimes(conn, pptr);
} else if (strcmp(ltype, "newsgroups") == 0) {
NNTPListNewsgroups(conn, pptr);
} else if (strcmp(ltype, "extensions") == 0) {
NNTPListExtensions(conn, pptr);
} else if (strcmp(ltype, "distributions") == 0) {
NNTPListDistributions(conn, pptr);
} else if (strcmp(ltype, "distrib.pats") == 0) {
NNTPListDistribPats(conn, pptr);
} else if (strcmp(ltype, "overview.fmt") == 0) {
NNTPListOverviewFmt(conn, pptr);
} else if (strcmp(ltype, "subscriptions") == 0) {
NNTPListSubscriptions(conn, pptr);
} else if (strcmp(ltype, "moderators") == 0) {
NNTPListModerators(conn, pptr);
} else {
NNBadCommandUse(conn);
}
}
void
NNTPListExtensions(Connection *conn, char **pptr)
{
MBLogPrintf(conn, &conn->co_TMBuf, "202 Extensions supported:\r\n");
MBLogPrintf(conn, &conn->co_TMBuf, "HDR\r\n");
MBLogPrintf(conn, &conn->co_TMBuf, "OVER\r\n");
MBLogPrintf(conn, &conn->co_TMBuf, ".\r\n");
}
void
NNTPListActive(Connection *conn, char **pptr)
{
const char *wc = NULL;
if (pptr)
wc = parseword(pptr, " \t"); /* may also be NULL */
else
wc = "*";
MBLogPrintf(conn, &conn->co_TMBuf, "215 Newsgroups in form \"group high low flags\".\r\n");
NNStartListActiveScan(conn, wc, NULL, COM_ACTIVE);
}
void
NNStartListActiveScan(Connection *conn, const char *wc, TimeRestrict *tr, int mode)
{
/*
* Setup list pattern
*/
zfreeStr(&conn->co_MemPool, &conn->co_ListPat);
if (wc)
conn->co_ListPat = zallocStr(&conn->co_MemPool, wc);
if (tr)
conn->co_TimeRestrict = *tr;
/*
* Optimize NEWGROUPS requests via active file cache
*/
if ((mode == COM_NEWGROUPS) && tr) {
if (ActiveCacheValid(KDBActive) && (ActiveCacheReadLock() == 0)) {
conn->co_ListCacheMode = ACMODE_READ;
conn->co_ListCachePtr = ActiveCacheFind(activeCache, tr->tr_Time);
conn->co_ListCacheGroups = conn->co_ListCachePtr ?
conn->co_ListCachePtr->nglist : NULL;
} else {
if (ActiveCacheWriteLock() == 0) {
ActiveCacheFreeMain();
conn->co_ListCacheMode = ACMODE_WRITE;
}
}
}
/*
* Initiate active file scan, but optimize if specific group requested
*/
if (wc == NULL || strchr(wc, '*') || strchr(wc, '?')) {
conn->co_ListRec = (conn->co_ListCacheMode == ACMODE_READ) ? 0 :
KPDBScanFirst(KDBActive, 0, &conn->co_ListRecLen);
conn->co_Flags |= COF_PATISWILD;
} else {
conn->co_ListRec = (conn->co_ListCacheMode == ACMODE_READ) ? 0 :
KPDBReadRecordOff(KDBActive, wc, 0, &conn->co_ListRecLen);
conn->co_Flags &= ~COF_PATISWILD;
}
conn->co_ArtMode = mode;
NNListActiveScan(conn);
}
void
NNListActiveScan(Connection *conn)
{
struct GroupList *groups = conn->co_Auth.dr_ListGroupDef->gr_Groups;
conn->co_Func = NNListActiveScan;
conn->co_State = "listac";
while ((conn->co_TMBuf.mh_Bytes < MBUF_HIWAT) &&
(((conn->co_ListCacheMode != ACMODE_READ) &&
conn->co_ListRec) ||
((conn->co_ListCacheMode == ACMODE_READ) &&
conn->co_ListCacheGroups))) {
int glen;
const char *group;
char grpbuf[MAXGNAME];
const char *rec;
if (conn->co_ListCacheMode == ACMODE_READ) {
char *grpname = conn->co_ListCacheGroups->group;
rec = KPDBReadRecord(KDBActive, grpname, 0, &conn->co_ListRecLen);
} else {
rec = KPDBReadRecordAt(KDBActive, conn->co_ListRec, 0, NULL);
}
group = rec ?
KPDBGetField(rec, conn->co_ListRecLen, NULL, &glen, NULL) :
"";
if (*group && (glen < MAXGNAME)) {
bcopy(group, grpbuf, glen);
grpbuf[glen] = 0;
if ((conn->co_ListPat == NULL ||
WildCmp(conn->co_ListPat, grpbuf) == 0) &&
(groups == NULL || GroupFindWild(grpbuf, groups))
) {
/*
* note: because we are locking a previously retrieved but unlocked
* record, the record may be marked as deleted. We cannot do
* KPDBWrite()'s with KP_LOCK_CONTINUE in this case because it
* may not properly locate the record to continue the lock at.
*/
KPDBLock(KDBActive, rec);
if (conn->co_ArtMode == COM_GROUPDESC) {
int glen;
const char *desc = KPDBGetField(rec, conn->co_ListRecLen, "GD", &glen, "?");
MBPrintf(&conn->co_TMBuf, "%s\t", grpbuf);
MBWriteDecode(&conn->co_TMBuf, desc, glen);
MBWrite(&conn->co_TMBuf, "\r\n", 2);
} else if (conn->co_ArtMode == COM_ACTIVE) {
int flen;
const char *flags = KPDBGetField(rec, conn->co_ListRecLen, "S", &flen, "n");
int ne = strtol(KPDBGetField(rec, conn->co_ListRecLen, "NE", NULL, "0"), NULL, 10);
int nb = strtol(KPDBGetField(rec, conn->co_ListRecLen, "NB", NULL, "0"), NULL, 10);
/*
* NOTE: we cannot use *.*s because it's broken on most
* platforms... it will strlen() the string, and the string
* in this case is the entire size of the active file!
*/
MBPrintf(&conn->co_TMBuf, "%s %010d %010d ",
grpbuf, ne, nb
);
MBWrite(&conn->co_TMBuf, flags, flen);
MBWrite(&conn->co_TMBuf, "\r\n", 2);
} else if (conn->co_ArtMode == COM_NEWGROUPS) {
const char *cts = KPDBGetField(rec, conn->co_ListRecLen, "CTS", NULL, NULL);
if (cts) {
int dt, cts_int = (int)strtoul(cts, NULL, 16);
if (conn->co_ListCacheMode == ACMODE_WRITE)
ActiveCacheInsert(&activeCache, grpbuf, cts_int);
dt = cts_int - (int)conn->co_TimeRestrict.tr_Time;
if (dt > 0) {
int flen;
const char *flags = KPDBGetField(rec, conn->co_ListRecLen, "S", &flen, "n");
int ne = strtol(KPDBGetField(rec, conn->co_ListRecLen, "NE", NULL, "0"), NULL, 10);
int nb = strtol(KPDBGetField(rec, conn->co_ListRecLen, "NB", NULL, "0"), NULL, 10);
MBPrintf(&conn->co_TMBuf, "%s %d %d ",
grpbuf, ne, nb
);
MBWrite(&conn->co_TMBuf, flags, flen);
MBWrite(&conn->co_TMBuf, "\r\n", 2);
}
}
}
KPDBUnlock(KDBActive, rec);
}
}
/*
* If we are looking for a wildcard, continue the scan. Otherwise
* we are done.
*/
if (conn->co_Flags & COF_PATISWILD) {
if (conn->co_ListCacheMode == ACMODE_READ) {
conn->co_ListCacheGroups = conn->co_ListCacheGroups->next;
if (conn->co_ListCacheGroups == NULL) {
conn->co_ListCachePtr =
ActiveCacheGetNext(conn->co_ListCachePtr);
conn->co_ListCacheGroups = conn->co_ListCachePtr ?
conn->co_ListCachePtr->nglist : NULL;
}
} else {
conn->co_ListRec = KPDBScanNext(
KDBActive,
conn->co_ListRec,
0,
&conn->co_ListRecLen
);
}
} else {
conn->co_ListRec = 0;
conn->co_ListCachePtr = NULL;
}
}
if ((conn->co_ListCacheMode == ACMODE_NONE) && (conn->co_ListRec == 0)) {
MBPrintf(&conn->co_TMBuf, ".\r\n");
NNCommand(conn);
zfreeStr(&conn->co_MemPool, &conn->co_ListPat);
if (DebugOpt)
printf("done\n");
}
if ((conn->co_ListCacheMode == ACMODE_READ) &&
(conn->co_ListCacheGroups == NULL)) {
ActiveCacheReadUnlock();
conn->co_ListCacheMode = ACMODE_NONE;
MBPrintf(&conn->co_TMBuf, ".\r\n");
NNCommand(conn);
zfreeStr(&conn->co_MemPool, &conn->co_ListPat);
if (DebugOpt)
printf("done\n");
}
if ((conn->co_ListCacheMode == ACMODE_WRITE) && (conn->co_ListRec == 0)) {
ActiveCacheMarkValid(KDBActive);
ActiveCacheWriteUnlock();
conn->co_ListCacheMode = ACMODE_NONE;
MBPrintf(&conn->co_TMBuf, ".\r\n");
NNCommand(conn);
zfreeStr(&conn->co_MemPool, &conn->co_ListPat);
if (DebugOpt)
printf("done\n");
}
}
void
NNTPListActiveTimes(Connection *conn, char **pptr)
{
MBLogPrintf(conn, &conn->co_TMBuf, "500 list active.times not supported yet\r\n");
NNCommand(conn);
}
/*
* list newsgroups grouppat
*/
void
NNTPListNewsgroups(Connection *conn, char **pptr)
{
const char *wc = NULL;
if (pptr)
wc = parseword(pptr, " \t"); /* may also be NULL */
MBLogPrintf(conn, &conn->co_TMBuf, "215 descriptions in form \"group description\"\r\n");
NNStartListActiveScan(conn, wc, NULL, COM_GROUPDESC);
}
void
NNTPXGTitle(Connection *conn, char **pptr)
{
const char *wc = NULL;
if (pptr)
wc = parseword(pptr, " \t"); /* may also be NULL */
MBLogPrintf(conn, &conn->co_TMBuf, "282 list follows\r\n");
NNStartListActiveScan(conn, wc, NULL, COM_GROUPDESC);
}
void
NNTPListDistributions(Connection *conn, char **pptr)
{
FILE *fi;
MBLogPrintf(conn, &conn->co_TMBuf, "215 Distributions in form \"area description\".\r\n");
if ((fi = fopen(PatLibExpand(DistributionsPat), "r")) != NULL) {
char buf[256];
while (fgets(buf, sizeof(buf), fi) != NULL) {
int l = strlen(buf);
if (l > 0 && buf[l-1] == '\n') {
buf[--l] = 0;
if (l > 0 && buf[l-1] == '\r')
buf[--l] = 0;
}
if (buf[0] == '.')
MBWrite(&conn->co_TMBuf, ".", 1);
MBWrite(&conn->co_TMBuf, buf, l);
MBWrite(&conn->co_TMBuf, "\r\n", 2);
}
fclose(fi);
}
MBPrintf(&conn->co_TMBuf, ".\r\n");
NNCommand(conn);
}
void
NNTPListDistribPats(Connection *conn, char **pptr)
{
FILE *fi;
MBLogPrintf(conn, &conn->co_TMBuf, "215 Default distributions in form \"weight:pattern:value\".\r\n");
if ((fi = fopen(PatLibExpand(DistribDotPatsPat), "r")) != NULL) {
char buf[256];
while (fgets(buf, sizeof(buf), fi) != NULL) {
int l = strlen(buf);
if (l > 0 && buf[l-1] == '\n') {
buf[--l] = 0;
if (l > 0 && buf[l-1] == '\r')
buf[--l] = 0;
}
if (buf[0] == '.')
MBWrite(&conn->co_TMBuf, ".", 1);
MBWrite(&conn->co_TMBuf, buf, l);
MBWrite(&conn->co_TMBuf, "\r\n", 2);
}
fclose(fi);
}
MBPrintf(&conn->co_TMBuf, ".\r\n");
NNCommand(conn);
}
void
NNTPListOverviewFmt(Connection *conn, char **pptr)
{
MBLogPrintf(conn, &conn->co_TMBuf, "215 Order of fields in overview database.\r\n");
MBPrintf(&conn->co_TMBuf, "%s.\r\n", OverViewFmt);
NNCommand(conn);
}
void
NNTPListSubscriptions(Connection *conn, char **pptr)
{
MBLogPrintf(conn, &conn->co_TMBuf, "215 Subscriptions in form \"group\"\r\n");
MBPrintf(&conn->co_TMBuf, ".\r\n");
NNCommand(conn);
}
void
NNTPListModerators(Connection *conn, char **pptr)
{
FILE *fi;
MBLogPrintf(conn, &conn->co_TMBuf, "215 Newsgroup moderators in form \"group-pattern:mail-address-pattern\".\r\n");
if ((fi = fopen(PatLibExpand(ModeratorsPat), "r")) != NULL) {
char buf[256];
while (fgets(buf, sizeof(buf), fi) != NULL) {
int l = strlen(buf);
if (l == 0 || buf[0] == '#' || buf[0] == '\n')
continue;
if (buf[l-1] == '\n')
buf[--l] = 0;
if (buf[0] == '.')
MBWrite(&conn->co_TMBuf, ".", 1);
MBWrite(&conn->co_TMBuf, buf, l);
MBWrite(&conn->co_TMBuf, "\r\n", 2);
}
fclose(fi);
}
MBPrintf(&conn->co_TMBuf, ".\r\n");
NNCommand(conn);
}
void
NNTPListGroup(Connection *conn, char **pptr)
{
char *group;
const char *rec;
int recLen;
OverInfo *ov;
if ((group = parseword(pptr, " \t")) != NULL && strlen(group) < MAXGNAME)
SetCurrentGroup(conn, group);
if (conn->co_GroupName && (ov = GetOverInfo(conn->co_GroupName)) != NULL) {
if ((rec = KPDBReadRecord(KDBActive, conn->co_GroupName, KP_LOCK, &recLen)) != NULL) {
conn->co_ListBegNo = strtol(KPDBGetField(rec, recLen, "NB", NULL, "0"), NULL, 10);
conn->co_ListEndNo = strtol(KPDBGetField(rec, recLen, "NE", NULL, "0"), NULL, 10) + 1;
KPDBUnlock(KDBActive, rec);
if (conn->co_ListEndNo - conn->co_ListBegNo > ov->ov_Head->oh_MaxArts)
conn->co_ListBegNo = conn->co_ListEndNo - ov->ov_Head->oh_MaxArts;
MBLogPrintf(conn, &conn->co_TMBuf, "211 Article list follows\r\n");
NNListGroup(conn);
} else {
MBLogPrintf(conn, &conn->co_TMBuf, "411 No such group %s\r\n", conn->co_GroupName);
NNCommand(conn);
}
PutOverInfo(ov);
} else {
MBLogPrintf(conn, &conn->co_TMBuf, "481 No group specified\r\n");
NNCommand(conn);
}
}
void
NNListGroup(Connection *conn)
{
OverInfo *ov = (conn->co_GroupName) ? GetOverInfo(conn->co_GroupName) : NULL;
conn->co_Func = NNListGroup;
conn->co_State = "listgr";
while (
conn->co_TMBuf.mh_Bytes < MBUF_HIWAT &&
conn->co_ListBegNo < conn->co_ListEndNo
) {
int resLen;
int artSize;
if (GetOverRecord(ov, conn->co_ListBegNo, &resLen, &artSize, NULL, NULL) != NULL) {
MBPrintf(&conn->co_TMBuf, "%d\r\n", conn->co_ListBegNo);
}
++conn->co_ListBegNo;
}
PutOverInfo(ov);
if (conn->co_ListBegNo < conn->co_ListEndNo) {
;
} else {
MBPrintf(&conn->co_TMBuf, ".\r\n");
NNCommand(conn);
}
}
void
NNTPNewgroups(Connection *conn, char **pptr)
{
const char *yymmdd = NULL;
const char *hhmmss = NULL;
const char *gmt = NULL;
TimeRestrict tr;
if (pptr) {
yymmdd = parseword(pptr, " \t");
hhmmss = parseword(pptr, " \t");
gmt = parseword(pptr, " \t");
}
if (SetTimeRestrict(&tr, yymmdd, hhmmss, gmt) < 0) {
MBLogPrintf(conn, &conn->co_TMBuf, "501 yymmdd hhmmss [\"GMT\"] [<distributions>]\r\n");
NNCommand(conn);
} else {
MBLogPrintf(conn, &conn->co_TMBuf, "231 New newsgroups follow.\r\n");
NNStartListActiveScan(conn, "*", &tr, COM_NEWGROUPS);
}
}
int
ActiveCacheValid(KPDB *kpdb)
{
int cur_aval;
cur_aval = KPDBAppendCount(kpdb);
return ((cur_aval == activeCache_AppendSeq) &&
(activeCache != NULL));
}
void
ActiveCacheMarkValid(KPDB *kpdb)
{
activeCache_AppendSeq = KPDBAppendCount(kpdb);
}
int
ActiveCacheReadLock(void)
{
if (activeCache_WriteLock != 0)
return -1;
activeCache_ReadLock++;
return 0;
}
void
ActiveCacheReadUnlock(void)
{
activeCache_ReadLock--;
}
int
ActiveCacheWriteLock(void)
{
if (activeCache_ReadLock != 0)
return -1;
if (activeCache_WriteLock != 0)
return -1;
activeCache_WriteLock = 1;
return 0;
}
void
ActiveCacheWriteUnlock(void)
{
activeCache_WriteLock = 0;
}
void
ActiveCacheFreeMain(void)
{
freePool(&activeCache_MemPool);
activeCache_MemPool = NULL;
activeCache = NULL;
}
activeCacheEnt *
ActiveCacheFind(activeCacheEnt *ac, int cts)
{
activeCacheEnt *tryLeft;
if (ac == NULL)
return NULL;
if (cts > ac->cts)
return ActiveCacheFind(ac->right, cts);
tryLeft = ActiveCacheFind(ac->left, cts);
if (tryLeft)
return tryLeft;
else
return ac;
}
activeCacheEnt *
ActiveCacheGetNext(activeCacheEnt *ac)
{
return ac->next;
}
void
ActiveCacheInsertHelper(activeCacheEnt **acp, activeCacheEnt *parent,
char *newGroup, int newCts)
{
if (*acp == NULL) {
struct activeCacheEnt *newEnt =
(struct activeCacheEnt *)zalloc(&activeCache_MemPool,
sizeof(struct activeCacheEnt));
*acp = newEnt;
newEnt->nglist =
(struct GroupList *)zalloc(&activeCache_MemPool,
sizeof(struct GroupList));
newEnt->nglist->group = zallocStr(&activeCache_MemPool, newGroup);
newEnt->nglist->next = NULL;
newEnt->cts = newCts;
newEnt->left = NULL;
newEnt->right = NULL;
newEnt->parent = parent;
if (newEnt->parent->left == newEnt) {
activeCacheEnt *temp;
temp = newEnt->parent->prev;
newEnt->parent->prev = newEnt;
newEnt->next = newEnt->parent;
newEnt->prev = temp;
if (newEnt->prev)
newEnt->prev->next = newEnt;
} else {
/* Assume newEnt->parent->right == newEnt */
activeCacheEnt *temp;
temp = newEnt->parent->next;
newEnt->parent->next = newEnt;
newEnt->prev = newEnt->parent;
newEnt->next = temp;
if (newEnt->next)
newEnt->next->prev = newEnt;
}
} else {
if (newCts == (*acp)->cts) {
struct GroupList *newList =
(struct GroupList *)zalloc(&activeCache_MemPool,
sizeof(struct GroupList));
newList->next = (*acp)->nglist;
newList->group = zallocStr(&activeCache_MemPool, newGroup);
(*acp)->nglist = newList;
} else {
if (newCts > (*acp)->cts)
ActiveCacheInsertHelper(&((*acp)->right), *acp,
newGroup, newCts);
else
ActiveCacheInsertHelper(&((*acp)->left), *acp,
newGroup, newCts);
}
}
}
void
ActiveCacheInsert(activeCacheEnt **acp, char *newGroup, int newCts)
{
if (*acp == NULL) {
*acp = (struct activeCacheEnt *)zalloc(&activeCache_MemPool,
sizeof(activeCacheEnt));
(*acp)->nglist = (struct GroupList *)zalloc(&activeCache_MemPool,
sizeof(struct GroupList));
(*acp)->nglist->group = zallocStr(&activeCache_MemPool, newGroup);
(*acp)->nglist->next = NULL;
(*acp)->cts = newCts;
(*acp)->left = NULL;
(*acp)->right = NULL;
(*acp)->next = NULL;
(*acp)->prev = NULL;
(*acp)->parent = NULL;
} else {
ActiveCacheInsertHelper(acp, NULL, newGroup, newCts);
}
}
/*
* Return a list of all newsgroups matching a pattern
*
* NOTE that the returned list is not in the same order as the
* comma-separated pattern
*/
GroupList *
ListActiveGroups(Connection *conn, char *pat)
{
int recLen;
int recOff;
GroupList *patlist = NULL;
GroupList *gl = NULL;
GroupList *allowedgroups = conn->co_Auth.dr_GroupDef->gr_Groups;
char *ng;
/*
* Create a list of the patterns
*/
for (ng = strtok(pat, ","); ng; ng = strtok(NULL, ",")) {
GroupList *tgl;
int wild = (strchr(ng, '*') || strchr(ng, '?'));
if (allowedgroups != NULL && !wild &&
GroupFindWild(ng, allowedgroups) == 0)
continue;
tgl = zalloc(&conn->co_MemPool, sizeof(GroupList));
tgl->group = zallocStr(&conn->co_MemPool, ng);
tgl->next = NULL;
if (wild) {
tgl->next = patlist;
patlist = tgl;
} else {
tgl->next = conn->co_ListCacheGroups;
conn->co_ListCacheGroups = tgl;
}
}
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 && groupLen < MAXGNAME) {
bcopy(group, grpbuf, groupLen);
grpbuf[groupLen] = 0;
for (gl = patlist; gl; gl = gl->next) {
if (WildCmp(gl->group, grpbuf) == 0) {
GroupList *tgl;
if (allowedgroups != NULL &&
GroupFindWild(grpbuf, allowedgroups) == 0) {
continue;
}
tgl = zalloc(&conn->co_MemPool, sizeof(GroupList));
tgl->group = zallocStr(&conn->co_MemPool, grpbuf);
tgl->next = NULL;
tgl->next = conn->co_ListCacheGroups;
conn->co_ListCacheGroups = tgl;
}
}
}
}
while (patlist) {
gl = patlist;
patlist = patlist->next;
zfreeStr(&conn->co_MemPool, &gl->group);
zfree(&conn->co_MemPool, gl, sizeof(GroupList));
}
return(conn->co_ListCacheGroups);
}
syntax highlighted by Code2HTML, v. 0.9.1