#ifdef RCS
static char rcsid[]="$Id: seen.c,v 1.1.1.1 2000/11/13 02:42:47 holsta Exp $";
#endif
/******************************************************************************
* Internetting Cooperating Programmers
* ----------------------------------------------------------------------------
*
* ____ PROJECT
* | _ \ __ _ _ __ ___ ___ _ __
* | | | |/ _` | '_ \ / __/ _ \ '__|
* | |_| | (_| | | | | (_| __/ |
* |____/ \__,_|_| |_|\___\___|_| the IRC bot
*
* All files in this archive are subject to the GNU General Public License.
*
* $Source: /cvsroot/dancer/dancer/src/seen.c,v $
* $Revision: 1.1.1.1 $
* $Date: 2000/11/13 02:42:47 $
* $Author: holsta $
* $State: Exp $
* $Locker: $
*
* ---------------------------------------------------------------------------
*****************************************************************************/
#include "dancer.h"
#include "trio.h"
#include "strio.h"
#include "function.h"
#include "user.h"
#include "transfer.h"
#include "seen.h"
/* --- Global ----------------------------------------------------- */
extern time_t now;
extern char seenfile[];
extern int seenmonths;
extern itemident *current;
int numSeenNicks = 0; /* This is not actually very truthful */
int numSeenHosts = 0;
struct Seenstruct SeenHead = {
NULL, NULL, NULL,
NULL, NULL,
0, 0,
NULL, NULL,
0
};
itemseen *seenHead = &SeenHead;
itemaux **hashnick = NULL;
itemseen **hashsite = NULL;
static long changedseenitem = 0;
/* --- SeenReport ------------------------------------------------- */
static void SeenReport(char *from, char *nick, time_t departure, itemseen *ps)
{
snapshot;
switch (ps->type) {
case SEENLEAVE:
Sendf(from, GetText(msg_seenleave),
nick, ps->host, TimeAgo(departure),
ps->msg ? " (" : "", ps->msg ? ps->msg : "", ps->msg ? ")" : "");
break;
case SEENQUIT:
Sendf(from, GetText(msg_seenquit),
nick, ps->host, TimeAgo(departure), ps->msg ? ps->msg : "");
break;
case SEENKICK:
Sendf(from, GetText(msg_seenkick), nick, ps->host,
TimeAgo(departure), ps->by ? ps->by : "", ps->msg ? ps->msg : "");
break;
case SEENLEFT:
Sendf(from, GetText(msg_seenleft), nick, ps->host, TimeAgo(departure));
break;
case SEENQUITED:
case SEENERROR:
Sendf(from, GetText(msg_seenquited), nick, ps->host, TimeAgo(departure));
break;
case SEENKICKED:
Sendf(from, GetText(msg_seenkicked),
nick, ps->host, TimeAgo(departure), ps->by ? ps->by : "");
break;
case SEENCHANGE:
Sendf(from, GetText(msg_seenchange), nick, ps->host, TimeAgo(departure));
default:
break;
}
}
/* --- SeenMostNick ----------------------------------------------- */
itemaux *SeenMostNick(char *nick)
{
long max = 0;
itemaux *p, *pp = NULL;
snapshot;
for (p = hashnick[HashU(nick)]; p; p = p->next) {
if (IRCEqual(p->nick, nick)) {
if (p->count >= max) {
max = p->count;
pp = p;
}
}
}
return pp;
}
/* --- SeenRecentNick --------------------------------------------- */
itemaux *SeenRecentNick(char *nick)
{
time_t recent = 0;
itemaux *p, *pp = NULL;
snapshot;
for (p = hashnick[HashU(nick)]; p; p = p->next) {
if (IRCEqual(p->nick, nick)) {
if (p->departure > recent) {
recent = p->departure;
pp = p;
}
}
}
return pp;
}
/* --- SeenRecentRootNick ----------------------------------------- */
itemaux *SeenRecentRootNick(char *nick)
{
time_t recent = 0;
itemaux *p, *pp = NULL;
snapshot;
for (p = hashnick[HashU(nick)]; p; p = p->next) {
if (IRCEqual(p->nick, nick)) {
if (p->root->departure > recent) {
recent = p->root->departure;
pp = p;
}
}
}
return pp;
}
/* --- SeenNick --------------------------------------------------- */
void SeenNick(char *from, char *nick, char opt)
{
extern char nickname[];
bool hit = FALSE;
itemaux *pa = NULL;
snapshot;
if (0 == opt) {
if (FindNick(nick)) {
if (IRCEqual(nickname, nick)) {
Send(from, GetText(msg_yes_i_am_here));
return;
}
else if (IRCEqual(current->nick, nick)) {
Send(from, GetText(msg_yes_i_see_you));
return;
}
else {
Send(from, GetText(msg_seen_already_here));
return;
}
}
else if (FindSplitNick(nick)) {
Sendf(from, GetText(msg_seensplit), nick);
return;
}
}
switch (opt) {
case 'A': /* All */
for (pa = hashnick[HashU(nick)]; pa; pa = pa->next) {
if (IRCEqual(pa->nick, nick)) {
hit = TRUE;
SeenReport(from, nick, pa->root->departure, pa->root);
}
}
break;
case 'M': /* Most often used */
pa = SeenMostNick(nick);
break;
default: /* Most recent */
/*
* gr8ron January 24th, 1999:
* After a little discussion with Bagder we came to the conclusion
* that it was time to drop checks for X->root->departure here unless
* explicitly requested. SeenRecentNick() uses X->departure now.
* SeenRecentRootNick() uses X->root->departure instead.
* Also, SeenAddNick() sets the departure time for nick to root's
* departure time when there is none supplied or when it's 0.
*/
pa = SeenRecentNick(nick);
break;
}
if (pa)
SeenReport(from, nick, pa->root->departure, pa->root);
else if (!hit)
Sendf(from, GetText(msg_never_seen), nick);
}
/* --- SeenMatch -------------------------------------------------- */
#define SEEN_MAX_DISPLAYED 15
void SeenMatch(char *from, char *pattern)
{
char nickpattern[NICKLEN+1] = "";
char *hostpattern;
int count = 0;
ulong nicksig, hostsig, max;
itemseen *ps;
itemaux *pa, *pam;
snapshot;
hostpattern = StrIndex(pattern, '!');
if (hostpattern) {
StrScan(pattern, "%"NICKLENTXT"[^!]", nickpattern);
nicksig = HashSignatureU(nickpattern);
hostpattern++;
}
else {
hostpattern = pattern;
}
hostsig = HashSignatureU(hostpattern);
for (ps = seenHead->link; ps; ps = ps->link) {
if (((ps->signature & hostsig) == hostsig) && Match(ps->host, hostpattern)) {
if (nickpattern[0]) {
for (pa = ps->first; pa; pa = pa->link) {
if (((pa->signature & nicksig) == nicksig) && Match(pa->nick, nickpattern)) {
SeenReport(from, pa->nick, pa->departure, ps);
if (SEEN_MAX_DISPLAYED <= ++count)
break;
}
}
}
else {
max = 0;
for (pa = pam = ps->first; pa; pa = pa->link) {
if (pa->count > max) {
pam = pa;
max = pa->count;
}
}
SeenReport(from, pam->nick, pam->root->departure, ps);
count++;
}
if (SEEN_MAX_DISPLAYED <= count) {
Send(from, GetText(msg_seen_bad_pattern));
break;
}
}
}
if (0 == count)
Sendf(from, GetText(msg_never_seen_match), pattern);
}
/* --- Seen ------------------------------------------------------- */
void Seen(char *from, char *line)
{
extern char *errfrom;
extern bool public;
extern long levels[];
extern itemopt option;
char *who;
char opt = (char)0;
bool illegalpub = FALSE;
snapshot;
if (GetOption(line)) {
switch (toupper(option.copt)) {
case 'A': /* All */
illegalpub = TRUE;
break;
case 'M': /* Most used */
case 'R': /* Recent */
break;
default:
break;
}
opt = toupper(option.copt);
line = option.newpos;
}
who = StrTokenize(line, " ");
if (who && *who) {
if (public && illegalpub) {
Send(errfrom, GetText(msg_no_public_options));
}
else if (public && StrIndex(line, '*')) {
Send(errfrom, GetText(msg_no_public_wildcards));
}
else {
if (strpbrk(who, "@*")) {
if (current->level >= LEVELPUB) {
SeenMatch(from, who);
}
else {
/* Pretend to be stupid */
Sendf(errfrom, GetText(msg_never_seen), who);
}
}
else
SeenNick(from, who, opt);
}
}
else
CmdSyntax(errfrom, "SEEN");
}
/* --- FreeSeen --------------------------------------------------- */
void FreeSeen(itemseen *ps)
{
snapshot;
if (ps) {
if (ps->usite)
StrFree(ps->usite);
if (ps->host)
StrFree(ps->host);
if (ps->by)
StrFree(ps->by);
if (ps->msg)
StrFree(ps->msg);
free(ps);
}
}
/* --- SeenNewSeen ------------------------------------------------ */
itemseen *SeenNewSeen(char *usite, char *host)
{
itemseen *ps;
snapshot;
/* Allocate a structure for the new site */
ps = NewEntry(itemseen);
if (ps) {
ps->usite = StrDuplicate(usite);
if (ps->usite) {
ps->host = StrDuplicate(host);
if (ps->host) {
ps->signature = HashSignatureU(ps->host);
return ps;
}
StrFree(ps->usite);
}
free(ps);
}
return NULL;
}
/* --- SeenLinkSeen ----------------------------------------------- */
static void SeenLinkSeen(itemseen *ps, unsigned int x)
{
snapshot;
ps->link = seenHead->link;
seenHead->link = ps;
ps->next = hashsite[x];
hashsite[x] = ps;
numSeenHosts++;
}
/* --- SeenNewAux ------------------------------------------------- */
itemaux *SeenNewAux(char *nick)
{
itemaux *pa;
snapshot;
/* Allocate a structure for the new aux */
pa = NewEntry(itemaux);
if (pa) {
pa->nick = StrDuplicate(nick);
if (pa->nick) {
pa->signature = HashSignatureU(pa->nick);
return pa;
}
free(pa);
}
return NULL;
}
/* --- SeenLinkAux ------------------------------------------------ */
static void SeenLinkAux(itemseen *ps, itemaux *pa, unsigned int x)
{
snapshot;
pa->link = ps->first;
ps->first = pa;
pa->next = hashnick[x];
hashnick[x] = pa;
pa->root = ps;
numSeenNicks++;
}
/* --- SeenInsert ------------------------------------------------- */
void SeenInsert(itemguest *g, int type, char *by, char *msg)
{
unsigned int xsite;
itemseen *ps;
itemaux *pa = NULL;
snapshot;
/* Check whether usite already exists */
xsite = Hash(g->ident->userdomain);
for (ps = hashsite[xsite]; ps; ps = ps->next) {
if (StrEqual(ps->usite, g->ident->userdomain))
break;
}
/* If the usite exists */
if (ps) {
/* Check whether nick has been used from this usite before */
for (pa = ps->first; pa; pa = pa->link) {
if (IRCEqual(pa->nick, g->ident->nick))
break;
}
/* Keep the last seen host in ps->host */
if (!StrEqualCase(ps->host, g->ident->host)) {
char *pointer;
pointer = StrDuplicate(g->ident->host);
if (pointer) {
StrFree(ps->host);
ps->host = pointer;
ps->signature = HashSignatureU(ps->host);
}
}
}
if (NULL == pa) {
/* We need a new aux structure */
pa = SeenNewAux(g->ident->nick);
if (pa) {
if (NULL == ps) {
/* We need a new usite */
ps = SeenNewSeen(g->ident->userdomain, g->ident->host);
if (ps)
SeenLinkSeen(ps, xsite);
}
if (ps) {
SeenLinkAux(ps, pa, HashU(g->ident->nick));
}
else {
StrFree(pa->nick);
free(pa);
pa = NULL;
}
}
}
/* Finally update the new seen information */
if (ps) {
if (pa) {
pa->count++;
pa->departure = now;
}
ps->departure = now;
ps->type = type;
if (ps->by)
StrFree(ps->by);
ps->by = (by && by[0]) ? StrDuplicate(by) : NULL;
if (ps->msg)
StrFree(ps->msg);
ps->msg = (msg && msg[0]) ? StrDuplicate(msg) : NULL;
changedseenitem++;
}
}
/* --- SeenInsertAll ---------------------------------------------- */
void SeenInsertAll(int type, char *by, char *msg)
{
extern itemguest *guestHead;
itemguest *g;
snapshot;
for (g = First(guestHead); g; g = Next(g)) {
SeenInsert(g, type, by, msg);
}
}
/* --- SeenAdd ---------------------------------------------------- */
itemseen *SeenAdd(char *line)
{
char host[MIDBUFFER];
char by[MIDBUFFER] = "";
char msg[BIGBUFFER] = "";
char *usite;
ulong departure;
int type;
itemseen *ps = NULL;
snapshot;
if (3 <= StrScan(line, "%"MIDBUFFERTXT"s %lu %d %"MIDBUFFERTXT"s :%"BIGBUFFERTXT"[^\n]",
host, &departure, &type, by, msg)) {
/* Simple sanity check, a host name must include the @-letter */
if (NULL == StrIndex(host, '@'))
return NULL;
usite = Userdomain(host);
if (usite) {
ps = SeenNewSeen(usite, host);
if (ps) {
ps->departure = departure;
ps->type = type;
ps->by = (by[0] && ('.' != by[0])) ? StrDuplicate(by) : NULL;
ps->msg = msg[0] ? StrDuplicate(msg) : NULL;
}
StrFree(usite);
}
}
return ps;
}
/* --- SeenAddNick ------------------------------------------------ */
void SeenAddNick(itemseen *ps, char *line)
{
char nick[NICKLEN+1], tmp[NICKLEN+1];
time_t departure = 0;
ulong count = 1;
itemaux *pa;
snapshot;
if (1 <= StrScan(line, "%"NICKLENTXT"s %d %d", nick, &count, &departure)) {
/*
* Only add nicknames that use legal characters, as other names are
* likely to be a proof of a broken seen file or similar
*/
if (StrScan(nick, "%[0-9A-~-]", tmp) && StrEqualCase(nick, tmp)) {
pa = SeenNewAux(nick);
if (pa) {
/*
* We can now link the allocated seen structure to seenlist
* if it has not been linked before.
*/
if (NULL == ps->first)
SeenLinkSeen(ps, Hash(ps->usite));
SeenLinkAux(ps, pa, HashU(pa->nick));
pa->count = count; /* Might be 1 if field wasn't set */
/*
* pa->departure is now set to departure time of the root if the
* corresponding field isn't set or when its set to 0.
*/
pa->departure = departure ? departure : ps->departure;
}
}
}
}
/* --- SeenInit --------------------------------------------------- */
bool SeenInit(void)
{
char line[MAXLINE];
itemseen *ps = NULL;
FILE *f;
snapshot;
hashnick = (itemaux **)calloc(HASHSIZE, sizeof(itemaux *));
hashsite = (itemseen **)calloc(HASHSIZE, sizeof(itemseen *));
if (hashnick && hashsite && seenfile[0]) {
f = fopen(seenfile, "r");
if (f) {
while (fgets(line, sizeof(line), f)) {
switch (line[0]) {
case ' ':
if (ps)
SeenAddNick(ps, &line[1]);
break;
default:
/* We have to free sites that have no nicks connected */
if (ps && (NULL == ps->first))
FreeSeen(ps);
ps = SeenAdd(line);
break;
}
}
fclose(f);
if (ps && (NULL == ps->first))
FreeSeen(ps);
return TRUE;
}
}
return FALSE;
}
/* --- SeenSave --------------------------------------------------- */
void SeenSave(void)
{
char tempfile[MIDBUFFER];
bool ok = TRUE;
itemseen *ps;
itemaux *pa;
FILE *f;
snapshot;
if ((char)0 == seenfile[0])
return;
if (0 == changedseenitem) {
Logf(LOG, "No seen item changed, no save performed.");
return;
}
Logf(LOG, "Saving seen data \"%s\"", seenfile);
StrFormatMax(tempfile, sizeof(tempfile), "%s~", seenfile);
f = fopen(tempfile, "w");
if (f) {
for (ps = seenHead->link; ps && ok; ps = ps->link) {
if ((ps->departure + seenmonths*SECINMONTH) >= now) {
/*
* Only save if the user departed within 'seenmonths' months ago!
*/
if (0 > fprintf(f, "%s %ld %d %s :%s\n",
ps->host, ps->departure, ps->type,
ps->by ? ps->by : ".",
ps->msg ? ps->msg : "")) {
ok = FALSE;
break;
}
for (pa = ps->first; pa; pa = pa->link) {
if ((pa->departure + seenmonths*SECINMONTH) >= now) {
/*
* Here too. We only save the nicks this person has used within
* the 'seenmonths' period. We shouldn't let old sins live on...
*/
if (0 > fprintf(f, " %s %ld %ld\n",
pa->nick, pa->count, pa->departure)) {
ok = FALSE;
break;
}
}
}
}
}
fclose(f);
if (ok) {
rename(tempfile, seenfile);
changedseenitem = 0;
}
}
}
/* --- SeenCleanup ------------------------------------------------ */
void SeenCleanup(void)
{
itemseen *ps, *tps;
itemaux *pa, *tpa;
snapshot;
SeenSave();
/* Remember to free all allocated memory in structures */
for (ps = seenHead->link; ps; ps = tps) {
tps = ps->link;
for (pa = ps->first; pa; pa = tpa) {
tpa = pa->link;
if (pa->nick)
StrFree(pa->nick);
free(pa);
}
FreeSeen(ps);
}
if (hashnick)
free(hashnick);
if (hashsite)
free(hashsite);
}
syntax highlighted by Code2HTML, v. 0.9.1