diff options
Diffstat (limited to 'replay.c')
| -rw-r--r-- | replay.c | 94 | 
1 files changed, 80 insertions, 14 deletions
| @@ -45,9 +45,11 @@ int gettimestamp(char *str) {  }  // Set 'str' to a string with the leading unixtime timestamp removed. +// Record the stripped timestamp in 'origtimestamp'.  // Returns 1 on success, 0 on failure -int striptimestamp(char *str) { +int striptimestamp(char *str, int *origtimestamp) {    char line[MAXCHAR]; +  char origtimestr[MAXCHAR];    int count = 0;    // Make sure we're starting with a digit @@ -58,10 +60,15 @@ int striptimestamp(char *str) {    // Skip over each digit until we encounter a non-digit, record the position    for (int i = 0; i < TIMELEN; i++) {      if (isdigit(str[i])) { +      // Copy timestamp into 'origtimestr' +      origtimestr[i] = str[i];        count++;      }    } +  origtimestr[count] = '\0'; +  *origtimestamp = strtol(origtimestr, NULL, 10); +    // Skip over the space    count++; @@ -85,7 +92,8 @@ int striptimestamp(char *str) {  // :foo!bar@baz PRIVMSG foo :[17:41:41] hello world  // Or the same but e.g. [DD/MM/YY HH:MM:SS] if replaydates == 1.  // Only inserts the formatted time for PRIVMSGs at the moment (and maybe only needs to!). -void formattime(char *str, int replaydates) { +// Saves the original timestamp in 'origtimestamp' either way. +void formattime(char *str, int replaydates, int *origtimestamp) {    // Extract the timestamp for conversion into [HH:MM:SS]    char timestr[TIMELEN];    sprintf(timestr, "%d", gettimestamp(str)); // Convert int time to string time @@ -99,8 +107,8 @@ void formattime(char *str, int replaydates) {      strftime(timestampf, DATETIMELEN, "[%H:%M:%S]", &tm);    } -  // Strip the original unixtimestamp -  striptimestamp(str); +  // Strip the original unixtimestamp, recording the original timestamp in case it's needed +  striptimestamp(str, origtimestamp);    // Take note of the length    int len = strlen(str); @@ -179,10 +187,12 @@ void formattime(char *str, int replaydates) {  // 1. TOPIC/JOIN/PART but we're not in the channel any more  // 2. TOPIC/JOIN/PART and it was from us  // 3. NICK from us but not our current nick +// 4. QUIT from a nick that is not currently in any channel (in which case +//    'sourcefd' is used to fake the QUIT using a NOTICE)  // Returns 1 if the replay line should go through as is  // Returns 0 if the replay line should be skipped  // Returns -1 on error -int sanitisereplay (char *str, struct ircdstate *ircdstate, struct channel *channels) { +int sanitisereplay (char *str, int sourcefd, int *origtimestamp, struct client *clients, struct settings *settings,struct ircdstate *ircdstate, struct channel *channels) {    // Check if the replay line is a TOPIC, a JOIN, or a PART so we don't    // replay those if we are not currently in the channel they are from    // otherwise clients and state go a bit mad. @@ -208,6 +218,9 @@ int sanitisereplay (char *str, struct ircdstate *ircdstate, struct channel *chan      strncpy(tokens[i], token, strlen(token) + 1);    } +  // Done with strcopy +  free(strcopyPtr); +    if (strncmp(tokens[1], "TOPIC", strlen("TOPIC")) == 0 ||        strncmp(tokens[1], "JOIN", strlen("JOIN")) == 0 ||        strncmp(tokens[1], "PART", strlen("PART")) == 0) { @@ -224,7 +237,6 @@ int sanitisereplay (char *str, struct ircdstate *ircdstate, struct channel *chan      if (!inchannel(channels, ircdstate->maxchannelcount, tokens[2] + offset) ||          ((strlen(tokens[0]) == strlen(ircdstate->ircnick)) && (strncmp(tokens[0], ircdstate->ircnick, strlen(tokens[0])) == 0))) {        debugprint(DEBUG_FULL, "sanitisereplay(): Not sending '%s' replay line '%s'.\n", tokens[1], str); -      free(strcopyPtr);        return 0;      }    } @@ -235,12 +247,63 @@ int sanitisereplay (char *str, struct ircdstate *ircdstate, struct channel *chan      if ((strlen(tokens[0]) == strlen(ircdstate->ircnick)) && (strncmp(tokens[0], ircdstate->ircnick, strlen(tokens[0])) == 0)) {        debugprint(DEBUG_FULL, "sanitisereplay(): Not sending '%s' replay line '%s'.\n", tokens[1], str); -      free(strcopyPtr);        return 0;      }    } -  free(strcopyPtr); +  // Fake QUIT handling.  Since user QUIT messages don't contain channel names, clients that +  // didn't know which channels a nick was in when receiving a quit message for that nick +  // can't show a per-channel QUIT notification.  Some clients just display the QUIT message +  // in the server status window, others silently ignore it.  Cater for those clients by +  // sending a NOTICE about the QUIT rather than replaying it - assuming the nick isn't +  // still in any of our channels. +  if (strncmp(tokens[1], "QUIT", strlen("QUIT")) == 0) { +    extractnickfromprefix(tokens[0], 1); + +    // Only do any of this if the nick isn't in any channels +    if (isnickinanychannel(channels, ircdstate->maxchannelcount, tokens[0])) { +      return 1; +    } + +    debugprint(DEBUG_FULL, "sanitisereplay(): Faking QUIT message as NOTICE for '%s'.\n", tokens[0]); + +    time_t rawtime = *origtimestamp; +    struct tm tm; +    char timestampf[DATETIMELEN]; // Formatted timestamp + +    // Format time, "ddd yyyy-mm-dd hh:mm:ss zzz" +    tm = *localtime(&rawtime); +    if (settings->replaydates) { +      strftime(timestampf, DATETIMELEN, "[%x %H:%M:%S]", &tm); +    } else { +      strftime(timestampf, DATETIMELEN, "[%H:%M:%S]", &tm); +    } + +    // Prepare a new temporary string for the original QUIT message +    char *strcopyquit = strdup(str); +    // Keep track of initial pointer for free()ing later +    char *strcopyquitPtr = strcopyquit; +    extractfinalparameter(strcopyquit); +    // Remove trailing CRLF (if any) +    if (strlen(strcopyquit) > 2) { +      for (int i = 0; i < 2; i++) { +        if (strcopyquit[strlen(strcopyquit) - 1] == '\n' || strcopyquit[strlen(strcopyquit) - 1] == '\r') { +          strcopyquit[strlen(strcopyquit) - 1] = '\0'; +        } +      } +    } + +    char outgoingmsg[MAXDATASIZE]; +    if (!snprintf(outgoingmsg, MAXDATASIZE, "NOTICE %s :%s %s has quit (%s).", ircdstate->ircnick, timestampf, tokens[0], strcopyquit)) { +      debugprint(DEBUG_CRIT, "sanitisereplay(): error: couldn't prepare fake QUIT message as NOTICE!\n"); +    } else { +      sendtoclient(sourcefd, outgoingmsg, clients, settings, 0); +    } + +    free(strcopyquitPtr); +    return 1; +  } +    return 1;  } @@ -292,7 +355,7 @@ int replaylinestime(int seconds, char *basedir) {  // if settings.replaydates == 1.  // Returns 1 on success, 0 on failure, or -1 if the line should be ignored.  // TODO - This is horribly inefficient since it re-reads the entire file each call, rewrite this! -int readreplayline(int seconds, int linenum, char *str, struct settings *settings, struct ircdstate *ircdstate) { +int readreplayline(int seconds, int linenum, char *str, int *origtimestamp, struct settings *settings, struct ircdstate *ircdstate) {    FILE *fp;    char line[MAXCHAR];    char filename[PATH_MAX]; @@ -339,7 +402,7 @@ int readreplayline(int seconds, int linenum, char *str, struct settings *setting          }          // ...then return it          // Insert our formatted [HH:MM:SS] timestamp into the message -        formattime(line, settings->replaydates); +        formattime(line, settings->replaydates, origtimestamp);          strncpy(str, line, strlen(line));          str[strlen(line)] = '\0'; @@ -465,7 +528,9 @@ int doreplaytime(int sourcefd, int replayseconds, struct client *clients, struct    // Replay those lines!    for (int i = 0; i < numlines; i++) { -    int ret = readreplayline(replayseconds, i, outgoingmsg, settings, ircdstate); +    int origtimestamp; +//    origtimestamp[0] = '\0'; +    int ret = readreplayline(replayseconds, i, outgoingmsg, &origtimestamp, settings, ircdstate);      if (ret == 0) {        debugprint(DEBUG_CRIT, "doreplaytime(): Error requesting replay line.\n");        return 0; @@ -474,7 +539,7 @@ int doreplaytime(int sourcefd, int replayseconds, struct client *clients, struct        continue;      } -    if (sanitisereplay(outgoingmsg, ircdstate, channels) < 1) { +    if (sanitisereplay(outgoingmsg, sourcefd, &origtimestamp, clients, settings, ircdstate, channels) < 1) {        continue;      } @@ -539,12 +604,13 @@ int doreplaylastspoke(int sourcefd, long linenumber, struct client *clients, str      }      // Insert our formatted [HH:MM:SS] timestamp into the message -    formattime(line, settings->replaydates); +    int origtimestamp; +    formattime(line, settings->replaydates, &origtimestamp);      strncpy(outgoingmsg, line, strlen(line));      outgoingmsg[strlen(line)] = '\0'; -    if (sanitisereplay(outgoingmsg, ircdstate, channels) < 1) { +    if (sanitisereplay(outgoingmsg, sourcefd, &origtimestamp, clients, settings, ircdstate, channels) < 1) {        continue;      } | 
