diff options
| author | Luke Bratch <luke@bratch.co.uk> | 2019-05-16 23:40:43 +0100 | 
|---|---|---|
| committer | Luke Bratch <luke@bratch.co.uk> | 2019-05-16 23:40:43 +0100 | 
| commit | 93e530320e024e9119fb8717459ef618086c5839 (patch) | |
| tree | 779e015cba061c55362f650638e5b92d8f53dc63 | |
| parent | 57faf310db4c7d7d07a5695fb305c481e040e4fb (diff) | |
Correctly handle nicks changing and actually track users PARTing channels.
Also change nickuserhost to store the leading colon (:) since it's always needed (so far).
| -rw-r--r-- | blabouncer.c | 102 | ||||
| -rw-r--r-- | functions.c | 32 | ||||
| -rw-r--r-- | functions.h | 5 | 
3 files changed, 128 insertions, 11 deletions
| diff --git a/blabouncer.c b/blabouncer.c index 7d2ea31..2a3bf7e 100644 --- a/blabouncer.c +++ b/blabouncer.c @@ -6,7 +6,7 @@  // - Do we actually need to store the modes in the channel struct?  // - Get CAP from server and relay to client  // - "01:53:47 -!- ServerMode/#test [b] by irc.tghost.co.uk" on existing clients when new client connects -// - Keep track of changing user nicks/modes +// - Keep track of changing user modes  // - Should replay log do more than PRIVMSGs?  // - Consider moving the three fd-related arrays into one struct  // - Check authentication before even getting to the send functions to save unnecessary processing @@ -84,8 +84,8 @@ struct ircdstrings {    char greeting003[MAXDATASIZE];    char greeting004[MAXDATASIZE];    char ircdname[MAXDATASIZE]; -  char nickuserhost[MAXDATASIZE]; // TODO - Make sure this changes when nick changes, if that's meant to happen. -  char ircnick[MAXNICKLENGTH]; // TODO - Make sure this changes when nick changes +  char nickuserhost[MAXDATASIZE]; +  char ircnick[MAXNICKLENGTH];    char ircusername[MAXUSERNAMELEN];    char currentmsg[MAXDATASIZE]; // Holding area for the current server-received IRC message being processed in case it needs building across multiple reads (i.e. a truncated/split message)  }; @@ -328,6 +328,51 @@ int addusertochannel(struct channel *channels, char *channelname, char *nick) {    return 0;  } +int removeuserfromchannel(struct channel *channels, char *channelname, char *nick) { +  printf("removeuserfromchannel(): given \"%s\" and \"%s\".\n", channelname, nick); + +  // Go through all the channels +  for (int i = 0; i < MAXCHANNELS; i++) { +    // Until we find the channel +    if (strncmp(channels[i].name, channelname, strlen(channelname)) == 0) { +      // Go through all the users in the channel +      for (int j = 0; j < MAXCHANUSERS; j++) { +        // And remove the user if found +        if (strncmp(channels[i].nicks[j], nick, strlen(nick)) == 0) { +          channels[i].nicks[j][0] = '\0'; +          return 1; +        } +      } +      // We failed +      return 0; +    } +  } + +  // TODO - Make a failed return do something to callers +  return 0; +} + +// Update any instance of oldnick with newnick in all channels, +// for instance of a new changed their nick. +int updatechannelnicks(struct channel *channels, char *oldnick, char *newnick) { +  printf("updatechannelnicks(): given '%s' and '%s'.\n", oldnick, newnick); + +  // Go through all the channels +  for (int i = 0; i < MAXCHANNELS; i++) { +    // Go through all the users in the channel +    for (int j = 0; j < MAXCHANUSERS; j++) { +      // And update the nick if found +      if (strncmp(channels[i].nicks[j], oldnick, strlen(oldnick)) == 0) { +        printf("Updating '%s' to '%s' in '%s'.\n", channels[i].nicks[j], newnick, channels[i].name); +        strcpy(channels[i].nicks[j], newnick); +      } +    } +  } + +  // TODO - Detect failing to update anything and tell the caller +  return 1; +} +  int setchanneltopicwhotime(struct channel *channels, char *channelname, char *who, char *when) {    printf("setchanneltopicwhotime(): given \"%s\", \"%s\", and \"%s\".\n", channelname, who, when); @@ -536,10 +581,15 @@ int processircmessage(SSL *server_ssl, int *clientsockfd, char *str, int source,            strncpy(ircdstrings->greeting001, str, strlen(str));            // Null the end of the string            ircdstrings->greeting001[strlen(str)] = '\0'; -          printf("Storing our nick!user@host (%s) from greeting 001 in ircdstrings struct.\n", tokens[counter - 1]); -          strncpy(ircdstrings->nickuserhost, tokens[counter - 1], strlen(tokens[counter - 1])); -          // Null the end of the string -          ircdstrings->nickuserhost[strlen(tokens[counter - 1])] = '\0'; +          printf("Storing our nick!user@host (:%s) from greeting 001 in ircdstrings struct.\n", tokens[counter - 1]); +          // Prepend a colon (:) first since everything (so far) needs one +          if (!snprintf(ircdstrings->nickuserhost, MAXDATASIZE, ":%s", tokens[counter - 1])) { +            fprintf(stderr, "Error while preparing nickuserhost for storage!\n"); +            exit(1); +          } +          // Null the end of the new string +          ircdstrings->nickuserhost[strlen(tokens[counter - 1]) + 1] = '\0'; // +1 for the inserted colon +          printf("nickuserhost '%s' stored.\n", ircdstrings->nickuserhost);            return 1;          } else if (strncmp(tokens[1], "002", strlen(tokens[1])) == 0) {            printf("Found greeting 002 (%s), storing in ircdstrings struct.\n", str); @@ -576,7 +626,6 @@ int processircmessage(SSL *server_ssl, int *clientsockfd, char *str, int source,            // If the user JOINing is us, then we must have joined a channel, so add to our local channel array.            // Copy to a temporary string so we still have the original in case we need it            char *prefixcopy = strdup(tokens[0]); -          stripprefix(prefixcopy);            if (strncmp(prefixcopy, ircdstrings->nickuserhost, strlen(tokens[0])) == 0) {              printf("Server JOIN: nickuserhost is ours ('%s' vs '%s').\n", prefixcopy, ircdstrings->nickuserhost);              // TODO - Saner way to initialise this since we don't have the variables yet? @@ -603,12 +652,13 @@ int processircmessage(SSL *server_ssl, int *clientsockfd, char *str, int source,            // (If it's not us, then it's another user PARTing a channel, so just pass straight through to letting all our clients know.)            // Copy to a temporary string so we still have the original in case we need it            char *prefixcopy = strdup(tokens[0]); -          stripprefix(prefixcopy);            if (strncmp(prefixcopy, ircdstrings->nickuserhost, strlen(tokens[0])) == 0) {              printf("Server PART: nickuserhost is ours ('%s' vs '%s').\n", prefixcopy, ircdstrings->nickuserhost);              removechannel(channels, tokens[2]);            } else {              printf("Server PART: nickuserhost is NOT ours ('%s' vs '%s').\n", prefixcopy, ircdstrings->nickuserhost); +            extractnickfromprefix(tokens[0]); +            removeuserfromchannel(channels, tokens[2], tokens[0]);            }            // And then send to all clients @@ -701,6 +751,36 @@ int processircmessage(SSL *server_ssl, int *clientsockfd, char *str, int source,            return 1;          } + +        // Server NICK received? +        // 1. Find out if it was us and change ircnick and nickuserhost if so +        // 2. Either way, update our records of nicks in channels +        // 3. Either way, relay to all clients +        if (strncmp(tokens[1], "NICK", strlen(tokens[1])) == 0) { +          printf("Server NICK found and it is: %s with length %zd!  Next token is '%s'.  Updating records and relaying to all clients.\n", tokens[0], strlen(tokens[0]), tokens[2]); + +          // Was it us? +          if (strncmp(ircdstrings->nickuserhost, tokens[0], strlen(ircdstrings->nickuserhost)) == 0) { +            // Update nickuserhost with the new :nick!user@host +            updatenickuserhost(ircdstrings->nickuserhost, tokens[2]); +            printf("Updated nickuserhost to '%s'.\n", ircdstrings->nickuserhost); +            // Update ircnick +            char *prefixcopy = strdup(ircdstrings->nickuserhost); +            extractnickfromprefix(prefixcopy); +            strcpy(ircdstrings->ircnick, prefixcopy); +            printf("Updated ircnick to '%s'.\n", ircdstrings->ircnick); +          } + +          // Update all nicks in channels +          extractnickfromprefix(tokens[0]); +          stripprefix(tokens[2]); +          updatechannelnicks(channels, tokens[0], tokens[2]); + +          // Relay to all clients +          sendtoallclients(clientsockfd, fdmax, arr_clients, str, sourcefd, arr_authed, arr_ssl, settings); + +          return 1; +        }        }        // Don't return if we got here because this means we didn't process something above @@ -767,7 +847,7 @@ int processircmessage(SSL *server_ssl, int *clientsockfd, char *str, int source,            }            // Get client to join channels -          if (!snprintf(outgoingmsg, MAXDATASIZE, ":%s JOIN :%s", ircdstrings->nickuserhost, channels[i].name)) { +          if (!snprintf(outgoingmsg, MAXDATASIZE, "%s JOIN :%s", ircdstrings->nickuserhost, channels[i].name)) {              fprintf(stderr, "Error while preparing USER just connected, channel JOIN responses!\n");              exit(1);            } @@ -873,7 +953,7 @@ int processircmessage(SSL *server_ssl, int *clientsockfd, char *str, int source,          // Rebuild to full PRIVMSG string and relay to all other clients          char outgoingmsg[MAXDATASIZE]; // String to send to client -        if (!snprintf(outgoingmsg, MAXDATASIZE, ":%s %s", ircdstrings->nickuserhost, str)) { +        if (!snprintf(outgoingmsg, MAXDATASIZE, "%s %s", ircdstrings->nickuserhost, str)) {            fprintf(stderr, "Error while preparing PRIVMSG relay from another bouncer client.\n");            exit(1);          } diff --git a/functions.c b/functions.c index e43f31c..6034250 100644 --- a/functions.c +++ b/functions.c @@ -150,3 +150,35 @@ void extractnickfromprefix(char *string) {    printf("extractnickfromprefix(): finishing with '%s', strlen: %zd.\n", string, strlen(string));  } + +// Update an existing nickuserhost string with a new nick +void updatenickuserhost(char *nickuserhost, char *nick) { +  printf("updatenickuserhost(): updating '%s' with '%s'.\n", nickuserhost, nick); + +  // Position of bang +  int bangpos = -1; + +  // Find the bang +  for (size_t i = 0; i < strlen(nickuserhost); i++) { +    if (nickuserhost[i] == '!') { +      printf("Found bang at position %ld!\n", i); +      bangpos = i; +      break; +    } +  } + +  if (bangpos == -1) { +    printf("No bang found in existing nickuserhost, quitting!\n"); +    return; +  } + +  // Make a new string combining the nick nick and the old nickuserhost + the offset of the bang +  char newstr[MAXDATASIZE]; +  snprintf(newstr, MAXDATASIZE, "%s%s", nick, nickuserhost + bangpos); +  newstr[strlen(nickuserhost) - bangpos + strlen(nick)] = '\0'; + +  // Copy back to source string +  strcpy(nickuserhost, newstr); + +  printf("updatenickuserhost(): new nickuserhost '%s', length '%ld'.\n", nickuserhost, strlen(nickuserhost)); +} diff --git a/functions.h b/functions.h index 4e525eb..de631e9 100644 --- a/functions.h +++ b/functions.h @@ -13,6 +13,8 @@  #include <arpa/inet.h>  #include <sys/select.h> +#define MAXDATASIZE 513 // max number of bytes we can get at once (RFC2812 says 512, plus one for null terminator) +  // getstdin() return codes  #define OK       0  #define NO_INPUT 1 @@ -33,4 +35,7 @@ void extractfinalparameter(char *string);  // Extract the IRC nick from a prefix  void extractnickfromprefix(char *string); +// Update an existing nickuserhost string with a new nick +void updatenickuserhost(char *nickuserhost, char *nick); +  #endif | 
