#include <stdarg.h>
#include <stdio.h>
#include <string.h>
+#include <assert.h>
+#include "../lib/array.h"
#include "../lib/irc_string.h"
#include "../irc/irc.h"
#include "../control/control.h"
glinebuf *glinebuflog[MAXGLINELOG] = {};
int glinebuflogoffset = 0;
-void glinebufinit(glinebuf *gbuf, int merge) {
- gbuf->id = 0;
+void glinebufinit(glinebuf *gbuf, int id) {
+ gbuf->id = id;
gbuf->comment = NULL;
+ gbuf->commit = 0;
+ gbuf->amend = 0;
gbuf->glines = NULL;
- gbuf->merge = merge;
+ gbuf->hitsvalid = 1;
+ gbuf->userhits = 0;
+ gbuf->channelhits = 0;
+ array_init(&gbuf->hits, sizeof(sstring *));
}
gline *glinebufadd(glinebuf *gbuf, const char *mask, const char *creator, const char *reason, time_t expire, time_t lastmod, time_t lifetime) {
- gline *gl, *sgl, **pnext;
+ gline *gl;
gl = makegline(mask); /* sets up nick,user,host,node and flags */
return 0;
}
- if (gbuf->merge) {
- /* Check if an existing gline supercedes this mask */
- for (sgl = gbuf->glines; sgl; sgl = sgl->next) {
- if (gline_match_mask(sgl, gl)) {
- freegline(gl);
- return sgl;
- }
- }
-
- /* Remove older glines this gline matches */
- for (pnext = &gbuf->glines; *pnext; pnext = &((*pnext)->next)) {
- sgl = *pnext;
-
- if (gline_match_mask(gl, sgl)) {
- *pnext = sgl->next;
- freegline(sgl);
-
- if (!*pnext)
- break;
- }
- }
- }
-
gl->creator = getsstring(creator, 255);
/* it's not unreasonable to assume gline is active, if we're adding a deactivated gline, we can remove this later */
gl->next = gbuf->glines;
gbuf->glines = gl;
+ gbuf->hitsvalid = 0;
+
return gl;
}
-void glinebufaddbyip(glinebuf *gbuf, const char *user, struct irc_in_addr *ip, unsigned char bits, int flags, const char *creator, const char *reason, time_t expire, time_t lastmod, time_t lifetime) {
+char *glinebufaddbyip(glinebuf *gbuf, const char *user, struct irc_in_addr *ip, unsigned char bits, int flags, const char *creator, const char *reason, time_t expire, time_t lastmod, time_t lifetime) {
trusthost *th, *oth;
- char mask[512];
+ static char mask[512];
unsigned char nodebits;
nodebits = getnodebits(ip);
for (oth = th->group->hosts; oth; oth = oth->next)
glinebufaddbyip(gbuf, user, &oth->ip, oth->bits, flags | GLINE_ALWAYS_USER | GLINE_IGNORE_TRUST, creator, reason, expire, lastmod, lifetime);
- return;
+ snprintf(mask, sizeof(mask), "%s@%s/tg%u", user, (bits == 128) ? IPtostr(*ip) : CIDRtostr(*ip, bits), th->group->id);
+ return mask;
}
}
if (nodebits < bits)
bits = nodebits;
- snprintf(mask, sizeof(mask), "%s@%s", user, trusts_cidr2str(ip, bits));
+ snprintf(mask, sizeof(mask), "%s@%s", user, (bits == 128) ? IPtostr(*ip) : CIDRtostr(*ip, bits));
glinebufadd(gbuf, mask, creator, reason, expire, lastmod, lifetime);
+ return mask;
+}
+
+char *glinebufaddbynick(glinebuf *gbuf, nick *np, int flags, const char *creator, const char *reason, time_t expire, time_t lastmod, time_t lifetime) {
+ if (flags & GLINE_ALWAYS_NICK) {
+ static char mask[512];
+ snprintf(mask, sizeof(mask), "%s!*@*", np->nick);
+ glinebufadd(gbuf, mask, creator, reason, expire, lastmod, lifetime);
+ return mask;
+ } else {
+ return glinebufaddbyip(gbuf, np->ident, &np->ipaddress, 128, flags, creator, reason, expire, lastmod, lifetime);
+ }
}
-void glinebufaddbynick(glinebuf *gbuf, nick *np, int flags, const char *creator, const char *reason, time_t expire, time_t lastmod, time_t lifetime) {
+void glinebufaddbywhowas(glinebuf *gbuf, whowas *ww, int flags, const char *creator, const char *reason, time_t expire, time_t lastmod, time_t lifetime) {
+ nick *np = &ww->nick;
+
if (flags & GLINE_ALWAYS_NICK) {
char mask[512];
snprintf(mask, sizeof(mask), "%s!*@*", np->nick);
glinebufadd(gbuf, mask, creator, reason, expire, lastmod, lifetime);
} else {
- glinebufaddbyip(gbuf, np->ident, &np->p_ipaddr, 128, flags, creator, reason, expire, lastmod, lifetime);
+ glinebufaddbyip(gbuf, np->ident, &np->ipaddress, 128, flags, creator, reason, expire, lastmod, lifetime);
}
}
-void glinebufcounthits(glinebuf *gbuf, int *users, int *channels, nick *spewto) {
+void glinebufcounthits(glinebuf *gbuf, int *users, int *channels) {
gline *gl;
- int i, hit;
+ int i, hit, slot;
chanindex *cip;
channel *cp;
nick *np;
+ char uhmask[512];
- if (users)
- *users = 0;
+#if 0 /* Let's just do a new hit check anyway. */
+ if (gbuf->hitsvalid)
+ return;
+#endif
- if (channels)
- *channels = 0;
+ gbuf->userhits = 0;
+ gbuf->channelhits = 0;
- if (channels) {
- for (i = 0; i<CHANNELHASHSIZE; i++) {
- for (cip = chantable[i]; cip; cip = cip->next) {
- cp = cip->channel;
+ for (i = 0; i < gbuf->hits.cursi; i++)
+ freesstring(((sstring **)gbuf->hits.content)[i]);
- if (!cp)
- continue;
+ array_free(&gbuf->hits);
+ array_init(&gbuf->hits, sizeof(sstring *));
- hit = 0;
+ for (i = 0; i<CHANNELHASHSIZE; i++) {
+ for (cip = chantable[i]; cip; cip = cip->next) {
+ cp = cip->channel;
- for (gl = gbuf->glines; gl; gl = gl->next) {
- if (gline_match_channel(gl, cp)) {
- hit = 1;
- break;
- }
- }
+ if (!cp)
+ continue;
- if (hit) {
- if (spewto)
- controlreply(spewto, "channel: %s", cip->name->content);
+ hit = 0;
- (*channels)++;
+ for (gl = gbuf->glines; gl; gl = gl->next) {
+ if (gline_match_channel(gl, cp)) {
+ hit = 1;
+ break;
}
}
+
+ if (hit) {
+ snprintf(uhmask, sizeof(uhmask), "channel: %s", cip->name->content);
+
+ gbuf->channelhits++;
+
+ slot = array_getfreeslot(&gbuf->hits);
+ ((sstring **)gbuf->hits.content)[slot] = getsstring(uhmask, 512);
+ }
}
}
- if (users) {
- for (i = 0; i < NICKHASHSIZE; i++) {
- for (np = nicktable[i]; np; np = np->next) {
- hit = 0;
+ for (i = 0; i < NICKHASHSIZE; i++) {
+ for (np = nicktable[i]; np; np = np->next) {
+ hit = 0;
- for (gl = gbuf->glines; gl; gl = gl->next) {
- if (gline_match_nick(gl, np)) {
- hit = 1;
- break;
- }
+ for (gl = gbuf->glines; gl; gl = gl->next) {
+ if (gline_match_nick(gl, np)) {
+ hit = 1;
+ break;
}
+ }
- if (hit) {
- if (spewto)
- controlreply(spewto, "user: %s!%s@%s r(%s)", np->nick, np->ident, np->host->name->content, np->realname->name->content);
+ if (hit) {
+ snprintf(uhmask, sizeof(uhmask), "user: %s!%s@%s%s%s r(%s)", np->nick, np->ident, np->host->name->content,
+ (np->auth) ? "/" : "", (np->auth) ? np->authname : "", np->realname->name->content);
- (*users)++;
- }
+ gbuf->userhits++;
+
+ slot = array_getfreeslot(&gbuf->hits);
+ ((sstring **)gbuf->hits.content)[slot] = getsstring(uhmask, 512);
}
}
}
+
+ gbuf->hitsvalid = 1;
+
+ if (users)
+ *users = gbuf->userhits;
+
+ if (channels)
+ *channels = gbuf->channelhits;
}
-int glinebufchecksane(glinebuf *gbuf, nick *spewto, int overridesanity, int overridelimit, int spewhits) {
+int glinebufchecksane(glinebuf *gbuf, nick *spewto, int overridesanity, int overridelimit) {
gline *gl;
int users, channels, good;
const char *hint;
- glinebufcounthits(gbuf, &users, &channels, spewhits ? spewto : NULL);
+ glinebufcounthits(gbuf, &users, &channels);
if (!overridelimit) {
if (channels > MAXUSERGLINECHANNELHITS) {
void glinebufspew(glinebuf *gbuf, nick *spewto) {
gline *gl;
- time_t ref;
-
- ref = (gbuf->flush) ? gbuf->flush : getnettime();
+ int i;
+ char timebuf[30], lastmod[30];
+
+ if (!gbuf->hitsvalid)
+ glinebufcounthits(gbuf, NULL, NULL);
+
+ if (gbuf->id == 0)
+ controlreply(spewto, "Uncommitted G-Line transaction.");
+ else
+ controlreply(spewto, "G-Line transaction ID %d", gbuf->id);
+
+ controlreply(spewto, "Comment: %s", (gbuf->comment) ? gbuf->comment->content : "(no comment)");
+
+ if (gbuf->commit) {
+ strftime(timebuf, sizeof(timebuf), "%d/%m/%y %H:%M:%S", localtime(&gbuf->commit));
+ controlreply(spewto, "Committed at: %s", timebuf);
+ }
- controlreply(spewto, "Mask Duration Creator Reason");
+ if (gbuf->amend) {
+ strftime(timebuf, sizeof(timebuf), "%d/%m/%y %H:%M:%S", localtime(&gbuf->amend));
+ controlreply(spewto, "Amended at: %s", timebuf);
+ }
+
+ controlreply(spewto, "Mask Expiry Last modified Creator Reason");
- for (gl = gbuf->glines; gl; gl = gl->next)
- controlreply(spewto, "%-40s %-20s %-20s %s", glinetostring(gl), longtoduration(gl->expire - ref, 0), gl->creator->content, gl->reason->content);
+ for (gl = gbuf->glines; gl; gl = gl->next) {
+ strftime(timebuf, sizeof(timebuf), "%d/%m/%y %H:%M:%S", localtime(&gl->expire));
+
+ if (gl->lastmod == 0)
+ strncpy(lastmod, "<ulined>", sizeof(lastmod));
+ else
+ strftime(lastmod, sizeof(lastmod), "%d/%m/%y %H:%M:%S", localtime(&gl->lastmod));
+
+ controlreply(spewto, "%-40s %-20s %-20s %-25s %s", glinetostring(gl), timebuf, lastmod, gl->creator->content, gl->reason ? gl->reason->content : "");
+ }
+
+ controlreply(spewto, "Hits");
+
+ for (i = 0; i < gbuf->hits.cursi; i++) {
+ controlreply(spewto, "%s", ((sstring **)gbuf->hits.content)[i]->content);
+
+ if (i >= 500) {
+ controlreply(spewto, "More than 500 hits, list truncated.");
+ break;
+ }
+ }
+
+ if (i == 0)
+ controlreply(spewto, "(no hits)");
}
-void glinebufcommit(glinebuf *gbuf, int propagate) {
+void glinebufmerge(glinebuf *gbuf) {
+ /* TODO: reimplement */
+
+/*
+ if (gbuf->merge) {
+ /-* Check if an existing gline supercedes this mask *-/
+ for (sgl = gbuf->glines; sgl; sgl = sgl->next) {
+ if (gline_match_mask(sgl, gl)) {
+ freegline(gl);
+ return sgl;
+ }
+ }
+
+ /-* Remove older glines this gline matches *-/
+ for (pnext = &gbuf->glines; *pnext; pnext = &((*pnext)->next)) {
+ sgl = *pnext;
+
+ if (gline_match_mask(gl, sgl)) {
+ *pnext = sgl->next;
+ freegline(sgl);
+
+ if (!*pnext)
+ break;
+ }
+ }
+ }
+*/
+}
+
+int glinebufcommit(glinebuf *gbuf, int propagate) {
gline *gl, *next, *sgl;
- glinebuf *gbl;
- int users, channels;
+ int users, channels, id;
/* Sanity check */
- glinebufcounthits(gbuf, &users, &channels, NULL);
+ glinebufcounthits(gbuf, &users, &channels);
if (propagate && (users > MAXGLINEUSERHITS || channels > MAXGLINECHANNELHITS)) {
- controlwall(NO_OPER, NL_GLINES, "G-Line buffer would hit %d users/%d channels. Not setting G-Lines.");
+ controlwall(NO_OPER, NL_GLINES_AUTO, "G-Line buffer would hit %d users/%d channels. Not setting G-Lines.");
glinebufabort(gbuf);
- return;
+ return 0;
}
- time(&gbuf->flush);
+ /* Record the commit time */
+ time(&gbuf->commit);
- if (propagate) {
- /* Make a copy of the glinebuf for the log */
- gbl = malloc(sizeof(glinebuf));
- gbl->id = nextglinebufid++;
- gbl->comment = (gbuf->comment) ? getsstring(gbuf->comment->content, 512) : NULL;
- gbl->flush = gbuf->flush;
- gbl->glines = NULL; /* going to set this later */
- gbl->merge = gbuf->merge;
- }
+ id = glinebufwritelog(gbuf, propagate);
+ /* Move glines to the global gline list */
for (gl = gbuf->glines; gl; gl = next) {
next = gl->next;
glinelist = gl;
}
- if (propagate) {
- gline_propagate(gl);
+ gl->glinebufid = id;
- sgl = glinedup(gl);
- sgl->next = gbl->glines;
- gbl->glines = sgl;
- }
+ if (propagate)
+ gline_propagate(gl);
}
- if (propagate && gbl->glines) {
- glinebuflogoffset++;
-
- if (glinebuflogoffset >= MAXGLINELOG)
- glinebuflogoffset = 0;
-
- if (glinebuflog[glinebuflogoffset])
- glinebufabort(glinebuflog[glinebuflogoffset]);
+ /* We've moved all glines to the global gline list. Clear glines link in the glinebuf. */
+ gbuf->glines = NULL;
- glinebuflog[glinebuflogoffset]= gbl;
- }
-
glinebufabort(gbuf);
+
+ return id;
}
void glinebufabort(glinebuf *gbuf) {
gline *gl, *next;
+ int i;
for (gl = gbuf->glines; gl; gl = next) {
next = gl->next;
}
freesstring(gbuf->comment);
+
+ for (i = 0; i < gbuf->hits.cursi; i++)
+ freesstring(((sstring **)gbuf->hits.content)[i]);
+
+ array_free(&gbuf->hits);
}
int glinebufundo(int id) {
if (!sgl)
continue;
-
+
+ sgl->glinebufid = 0;
+
gline_deactivate(sgl, 0, 1);
}
gbuf->comment = getsstring(comment, 512);
}
+
+int glinebufwritelog(glinebuf *gbuf, int propagating) {
+ int i, slot;
+ gline *gl, *sgl;
+ glinebuf *gbl;
+
+ if (!gbuf->glines)
+ return 0; /* Don't waste log IDs on empty gline buffers */
+
+ gbl = NULL;
+
+ /* Find an existing log buffer with the same id */
+ if (gbuf->id != 0) {
+ for (i = 0; i < MAXGLINELOG; i++) {
+ if (!glinebuflog[i])
+ continue;
+
+ if (glinebuflog[i]->id == gbuf->id) {
+ gbl = glinebuflog[i];
+ gbl->amend = gbuf->commit;
+
+ /* We're going to re-insert this glinebuf, so remove it for now */
+ glinebuflog[i] = NULL;
+
+ break;
+ }
+ }
+ }
+
+ /* Find a recent glinebuf that's a close match */
+ if (!gbl && !propagating) {
+ for (i = 0; i < MAXGLINELOG; i++) {
+ if (!glinebuflog[i])
+ continue;
+
+ if (glinebuflog[i]->commit < getnettime() - 5 && glinebuflog[i]->amend < getnettime() - 5)
+ continue;
+
+ assert(glinebuflog[i]->glines);
+
+ if (strcmp(glinebuflog[i]->glines->creator->content, gbuf->glines->creator->content) != 0)
+ continue;
+
+ gbl = glinebuflog[i];
+ gbl->amend = gbuf->commit;
+
+ /* We're going to re-insert this glinebuf, so remove it for now */
+ glinebuflog[i] = NULL;
+
+ break;
+ }
+ }
+
+ /* Make a new glinebuf for the log */
+ if (!gbl) {
+ gbl = malloc(sizeof(glinebuf));
+ glinebufinit(gbl, (gbuf->id == 0) ? nextglinebufid++ : gbuf->id);
+
+ assert(gbl->hitsvalid);
+
+ if (gbuf->comment)
+ glinebufcommentf(gbl, "%s", gbuf->comment->content);
+ else if (!propagating)
+ glinebufcommentf(gbl, "G-Lines set by %s", gbuf->glines->creator->content);
+
+ gbl->commit = gbuf->commit;
+ }
+
+ /* Save a duplicate of the glines in the log buffer */
+ for (gl = gbuf->glines; gl; gl = gl->next) {
+ sgl = glinedup(gl);
+ sgl->next = gbl->glines;
+ gbl->glines = sgl;
+ }
+
+ gbl->userhits += gbuf->userhits;
+ gbl->channelhits += gbuf->channelhits;
+
+ assert(gbuf->userhits + gbuf->channelhits == gbuf->hits.cursi);
+
+ for (i = 0; i < gbuf->hits.cursi; i++) {
+ slot = array_getfreeslot(&gbl->hits);
+ ((sstring **)gbl->hits.content)[slot] = getsstring(((sstring **)gbuf->hits.content)[i]->content, 512);
+ }
+
+ /* Log the transaction */
+ glinebuflogoffset++;
+
+ if (glinebuflogoffset >= MAXGLINELOG)
+ glinebuflogoffset = 0;
+
+ if (glinebuflog[glinebuflogoffset])
+ glinebufabort(glinebuflog[glinebuflogoffset]);
+
+ glinebuflog[glinebuflogoffset] = gbl;
+
+ return gbl->id;
+}