Track user names to properly show joins and parts - irc - A minimalistic IRC client, forked from https://c9x.me/irc/
(HTM) git clone git://vernunftzentrum.de/irc.git
(DIR) Log
(DIR) Files
(DIR) Refs
(DIR) README
---
(DIR) commit e5ae717f4ac681933c42f9bb82ffd01386545522
(DIR) parent a68ac08f9543bbb869b49f1329fdf9281d412127
(HTM) Author: Christian Kellermann <ckeen@pestilenz.org>
Date: Fri, 6 Apr 2018 23:03:04 +0200
Track user names to properly show joins and parts
Diffstat:
irc.c | 145 +++++++++++++++++++++++++++++--
1 file changed, 136 insertions(+), 9 deletions(-)
---
(DIR) diff --git a/irc.c b/irc.c
@@ -36,7 +36,8 @@
enum {
ChanLen = 64,
LineLen = 512,
- MaxChans = 16,
+ MaxChans = 32,
+ MaxKnownUsers = 2048,
BufSz = 2048,
LogSz = 4096,
MaxRecons = 10, /* -1 for infinitely many */
@@ -62,6 +63,12 @@ static struct Chan {
char join; /* Channel was 'j'-oined. */
} chl[MaxChans];
+static struct User {
+ char nick[64];
+ uint32_t channels; /* Needs to match MaxChans */
+ char inuse;
+} usrs[MaxKnownUsers];
+
static int ssl;
static struct {
int fd;
@@ -84,6 +91,8 @@ static void tdrawbar(void);
static void tredraw(void);
static void treset(void);
+static void usrchandrop(int);
+
static void
panic(const char *m)
{
@@ -326,6 +335,7 @@ chdel(char *name)
if (!(n = chfind(name)))
return 0;
+ usrchandrop(n);
nch--;
free(chl[n].buf);
memmove(&chl[n], &chl[n + 1], (nch - n) * sizeof(struct Chan));
@@ -334,6 +344,81 @@ chdel(char *name)
return 1;
}
+static void
+usrchandrop(int chan)
+{
+ for (size_t i = 0; i < MaxKnownUsers; i++)
+ if (usrs[i].channels == (1 << chan)) {
+ usrs[i].channels = 0;
+ usrs[i].inuse = 0;
+ bzero(usrs[i].nick, 64);
+ }
+}
+
+static size_t
+usrfind(char *nick)
+{
+ size_t i = 0;
+ for (i = MaxKnownUsers - 1; i > 0; i--){
+ if (usrs[i].inuse && !strcmp(nick, usrs[i].nick))
+ break;
+ }
+ return i;
+}
+
+static void
+usradd(char* nick, int chan)
+{
+ size_t i;
+ i = usrfind(nick);
+ if (!i) {
+ for (i = MaxKnownUsers - 1; i > 0; i--)
+ if (!usrs[i].inuse)
+ break;
+ }
+ if (!i)
+ panic("Too many users!");
+ strlcpy(usrs[i].nick, nick, 64);
+ usrs[i].channels |= 1 << chan;
+ usrs[i].inuse = 1;
+}
+
+static void
+usrdel(char* nick, int chan)
+{
+ size_t h;
+
+ h = usrfind(nick);
+ if (!h)
+ return;
+ if (!chan)
+ usrs[h].channels = 0;
+
+ usrs[h].channels &= ~(1 << chan);
+
+ if (!usrs[h].channels) {
+ usrs[h].inuse = 0;
+ bzero(usrs[h].nick, 64);
+ }
+}
+
+static void
+usrchange(char* old, char* new)
+{
+ size_t h;
+
+ h = usrfind(old);
+ if(!h)
+ panic("Missed a user!");
+ strlcpy(usrs[h].nick, new, 64);
+}
+
+static uint32_t
+usrchans(char* nick)
+{
+ return usrs[usrfind(nick)].channels;
+}
+
static char *
pushl(char *p, char *e)
{
@@ -461,7 +546,7 @@ scmd(char *usr, char *cmd, char *par, char *data)
tdrawbar();
}
} else if (!strcmp(cmd, "NICK")) {
- if (!data || !pm)
+ if (!data)
return;
if (!strcmp(usr, nick)){
for (int c=0; c < nch; c++){
@@ -469,21 +554,33 @@ scmd(char *usr, char *cmd, char *par, char *data)
}
strlcpy(nick, data, sizeof(nick));
} else {
- pushf(chfind(pm), "%s - is now known as %s", usr, data);
+ pushf(chfind(data), "%s - is now known as %s", usr, data);
+ usrchange(usr, data);
}
tredraw();
return;
} else if (!strcmp(cmd, "PING")) {
sndf("PONG :%s", data ? data : "(null)");
} else if (!strcmp(cmd, "PART")) {
+ int ch = 0;
if (!pm)
return;
- pushf(chfind(pm), "-!- %s has left %s", usr, pm);
+ ch = chfind(pm);
+ pushf(ch, "-!- %s has left %s", usr, pm);
+ usrdel(usr, ch);
} else if (!strcmp(cmd, "JOIN")) { /* some servers pass the channel as data :#channel */
- if (pm)
- pushf(chfind(pm), "-!- %s has joined %s", usr, pm);
- else if (data)
- pushf(chfind(data), "-!- %s has joined %s", usr, data);
+ int ch;
+ char *chan;
+ if (pm) {
+ ch = chfind(pm);
+ chan = pm;
+ } else if (data) {
+ ch = chfind(data);
+ chan = data;
+ }
+
+ pushf(ch, "-!- %s has joined %s", usr, chan);
+ usradd(usr, ch);
return;
} else if (!strcmp(cmd, "470")) { /* Channel forwarding. */
char *ch = strtok(0, " "), *fch = strtok(0, " ");
@@ -500,6 +597,23 @@ scmd(char *usr, char *cmd, char *par, char *data)
return;
pushf(chfind(chan), "Topic for %s: %s", chan, data);
tredraw();
+ } else if (!strcmp(cmd, "353")) { /* RPL_NAMREPLY */
+ pushf(0, "Names %s", data ? data : "");
+ if ((pm = strtok(0, " ")) && (!strcmp(pm, "=") || !strcmp(pm, "*") || !strcmp(pm, "@"))) {
+ char *n;
+ char *chan = strtok(0, " ");
+ int c = chfind(chan);
+ if (!chan || !data || !c)
+ return;
+ n = strtok(data, " ");
+ if (!n)
+ return;
+ do {
+ if (n[0] == '@' || n[0] == '+')
+ n+=1;
+ usradd(n, c);
+ } while ((n = strtok(0, " ")));
+ }
} else if (!strcmp(cmd, "471") || !strcmp(cmd, "473")
|| !strcmp(cmd, "474") || !strcmp(cmd, "475")) { /* Join error. */
if ((pm = strtok(0, " "))) {
@@ -513,7 +627,19 @@ scmd(char *usr, char *cmd, char *par, char *data)
return;
pushf(0, "-!- Cannot change to nick %s: %s", pm, data);
tredraw();
- } else if (!strcmp(cmd, "QUIT")) { /* Commands we don't care about. */
+ } else if (!strcmp(cmd, "QUIT")) {
+ char *msg = "";
+ if (!usr)
+ return;
+ uint64_t chans = usrchans(usr);
+ usrdel(usr, 0);
+ if (data)
+ msg = data;
+ for (int c = 0; c < MaxChans; c++) {
+ if (1<<c & chans)
+ pushf(c, "-!- %s has left ('%s').", usr, msg);
+ }
+ tredraw();
return;
} else if (!strcmp(cmd, "NOTICE") || !strcmp(cmd, "375")
|| !strcmp(cmd, "372") || !strcmp(cmd, "376")) {
@@ -894,6 +1020,7 @@ main(int argc, char *argv[])
strcpy(nick, user);
if (!nick[0])
goto usage;
+ bzero(usrs, MaxKnownUsers * sizeof(*usrs));
tinit();
err = dial(server, port);
if (err)