#include "replay.h" // Return the unixtime timestamp at the start of a line, // or -1 if a timestamp couldn't be extracted int gettimestamp(char *str) { int timestamp; char timestr[TIMELEN]; int count = 0; // Make sure we're starting with a digit if (!isdigit(str[0])) { return -1; } // Extract each digit until we encounter a non-digit for (int i = 0; i < TIMELEN; i++) { if (isdigit(str[i])) { timestr[count] = str[i]; count++; } } timestr[count] = '\0'; // Null terminate timestamp = strtol(timestr, NULL, 10); // Convert resulting string to an integer, base 10 return timestamp; } // Set 'str' to a string with the leading unixtime timestamp removed. // Returns 1 on success, 0 on failure int striptimestamp(char *str) { char line[MAXCHAR]; int count = 0; // Make sure we're starting with a digit if (!isdigit(str[0])) { return 0; } // Skip over each digit until we encounter a non-digit, record the position for (int i = 0; i < TIMELEN; i++) { if (isdigit(str[i])) { count++; } } // Skip over the space count++; int count2 = 0; // Copy from the end of the digits (and the space) until the end of the string for (size_t i = count; i < strlen(str); i++) { line[count2] = str[i]; count2++; } strncpy(str, line, count2); str[count2] = '\0'; return 1; } // Take a string like: // 1557592901 :foo!bar@baz PRIVMSG foo :hello world // And convert it to: // :foo!bar@baz PRIVMSG foo :[17:41:41] hello world // Only inserts the formatted time for PRIVMSGs at the moment (and maybe only needs to!). void formattime(char *str) { // Extract the timestamp for conversion into [HH:MM:SS] char timestr[TIMELEN]; sprintf(timestr, "%d", gettimestamp(str)); // Convert int time to string time struct tm tm; strptime(timestr, "%s", &tm); char timestampf[TIMELEN]; // Formatted timestamp // Convert into [HH:MM:SS] strftime(timestampf, TIMELEN, "[%H:%M:%S]", &tm); // Strip the original unixtimestamp striptimestamp(str); // Take note of the length int len = strlen(str); // Find the start of the message if it's a PRIVMSG char *ret; int pos1; if ((ret = strstr(str, "PRIVMSG")) != NULL) { // Position within str of "PRIVMSG" pos1 = ret - str; } else { // If it's not a PRIVMSG, stop here return; } char *ret2; int pos2; // Find the start of the actual message within the PRIVMSG // TODO - What if it's some other message with a colon in it? Find PRIVMSG or not properly. if ((ret2 = strstr(ret, ":")) != NULL) { // Position within ret of ":" pos2 = ret2 - ret; } else { // Didn't find the real message, weird. return; } // Position of start of PRIVMSG colon in original string int realpos = pos1 + pos2 + 1; // Build the new formatted string char newline[MAXCHAR]; // First bit (:foo!bar@baz PRIVMSG foo :) for (int i = 0; i < realpos; i++) { newline[i] = str[i]; } // Second bit (:foo!bar@baz PRIVMSG foo :[HH:MM:SS]) int j = 0; for (int i = realpos; i < TIMELEN + realpos - 1; i++) { // -1 to avoid the null char from timestampf newline[i] = timestampf[j]; j++; } // Insert a space after the formatted timestamp newline[TIMELEN + realpos - 1] = ' '; // Final bit (the original PRIVMSG contents) for (int i = TIMELEN + realpos; i < len + TIMELEN; i++) { newline[i] = str[i - TIMELEN]; } // Copy the whole thing back to str and null terminate strncpy(str, newline, len + TIMELEN); str[len + TIMELEN] = '\0'; } // Return the number of lines in the replay log since 'seconds' seconds ago, or -1 if there a problem. // 'basedir' is the directory in which to find 'replay.log'. int replaylines(int seconds, char *basedir) { FILE *fp; char str[MAXCHAR]; char filename[PATH_MAX]; // Build path snprintf(filename, PATH_MAX, "%s/replay.log", basedir); int numlines = 0; fp = fopen(filename, "r"); if (fp == NULL) { printf("error: could not open replay log '%s'.\n", filename); // Assume the file just doesn't exist yet - TODO - Interpret error codes to see what happened. fclose(fp); return 0; } // Get the current time for comparison later int timenow = (int)time(NULL); while (fgets(str, MAXCHAR, fp) != NULL) { // Read the timestamp from each line int timestamp = gettimestamp(str); if (timestamp < 1) { printf("Error reading timestamp from replay log file.\n"); fclose(fp); return -1; } // If the line is within range of the requested time, count it if (timestamp > timenow - seconds) { numlines++; } } fclose(fp); return numlines; } // Set 'str' to the line in the log with a timestamp of greater than 'seconds' // seconds ago, plus however many lines 'linenum' is set to. 'basedir' is the // directory in which to find 'replay.log'. // Also modify the line to include a timestamp in the form "[HH:MM:SS]". // Returns 1 on success, or 0 on failure. // TODO - This is horribly inefficient since it re-reads the entire file each call, rewrite this! int readreplayline(int seconds, int linenum, char *str, char *basedir) { FILE *fp; char line[MAXCHAR]; char filename[PATH_MAX]; // Build path snprintf(filename, PATH_MAX, "%s/replay.log", basedir); int count = 0; fp = fopen(filename, "r"); if (fp == NULL) { debugprint(DEBUG_CRIT, "error: could not open replay log '%s'.\n", filename); fclose(fp); return 0; } // Get the current time for comparison later int timenow = (int)time(NULL); while (fgets(line, MAXCHAR, fp) != NULL) { // Read the timestamp from each line int timestamp = gettimestamp(line); if (timestamp < 1) { debugprint(DEBUG_CRIT, "Error reading timestamp from replay log file.\n"); fclose(fp); return 0; } // If the line is within range of the requested time... if (timestamp > timenow - seconds) { // ...and it is the current requested line then return it if (count == linenum) { // Insert our formatted [HH:MM:SS] timestamp into the message formattime(line); strncpy(str, line, strlen(line)); str[strlen(line)] = '\0'; fclose(fp); return 1; } count++; } } // If we got here something went wrong fclose(fp); return 0; } // Write the line 'str' to the replay log file after prepending it with // the current unixtime timestamp. 'basedir' is the directory in which // to write to 'replay.log'. // Expects a string in the format: // :from!bar@baz PRIVMSG to :hello world // With the ":foo!bar@baz "prefix being important. // Returns 1 on success or 0 on failure. int writereplayline(char *str, char *basedir) { FILE *fp; char line[MAXCHAR]; char filename[PATH_MAX]; // Build path snprintf(filename, PATH_MAX, "%s/replay.log", basedir); int bytes = 0; fp = fopen(filename, "a"); if (fp == NULL) { debugprint(DEBUG_CRIT, "error: could not open replay log '%s' for writing.\n", filename); fclose(fp); return 0; } // Get the current time and manipulate it into a C string time_t timenow = time(NULL); int timenowlen = snprintf(NULL, 0, "%ld", timenow); char timenowstr[timenowlen + 1]; // TODO - Make this Year 2038 proof. snprintf(timenowstr, timenowlen + 1, "%ld", timenow); // Prepend the unixtime timestamp snprintf(line, MAXCHAR, "%s %s", timenowstr, str); // Ensure the line finishes with CRLF appendcrlf(line); debugprint(DEBUG_FULL, "Complete replay log string to write: '%s', length '%ld'.\n", line, strlen(line)); // Write complete line to file if ((bytes = fprintf(fp, "%s", line)) < 0) { debugprint(DEBUG_CRIT, "error: could not write to replay log file.\n"); fclose(fp); return 0; } fclose(fp); return bytes; }