/* * Playground+ - intercom.c * The intercom talk server (by Grim) * --------------------------------------------------------------------------- * * Modifications made to vanilla 1.1.6 distribution from Grim: * - Changed to work with PG+'s soft config files * - send_hello function creates string in parts to prevent * bizzare bug where the talker incorrectly broadcasts its intercom id * as its talker name. * * Many thanks for Maverick who assisted in the conversion of 1.1.5 to * work under Playground Plus */ #include "include/config.h" #include <stdio.h> #include <stdlib.h> #ifdef INTERCOM #include <string.h> #include <sys/time.h> #include <sys/types.h> #include <sys/stat.h> #include <fcntl.h> #include <time.h> #include <unistd.h> #ifndef BSDISH #include <malloc.h> #endif #include <sys/socket.h> #include <sys/un.h> #include <sys/ioctl.h> #include <stdlib.h> #include <netinet/in.h> #include <arpa/inet.h> #include <arpa/telnet.h> #include <netdb.h> #include <signal.h> #include <ctype.h> #include <stdarg.h> #include <errno.h> #include "include/intercom.h" #ifdef ANSI #include "include/ansi.h" #endif /* lets give intercom soft msgs shall we? */ #include "xstring.c" char *dynamic_dns = 0; #ifndef MAX_NAME /* intercom.h relies on MAX_NAME to decide if player.h should be included, so shall we */ typedef struct file { char *where; int length; } file; #endif /* ! MAX_NAME */ file config_msg; file load_file (char *); char *get_config_msg (char *); int max_log_size; #ifndef S_DAY #define S_DAY (60*60*24) #endif /*Interns */ char *stack, *stack_start, current_name[MAX_NAME]; int closedown, ping_time; time_t system_time; int talker_fd, unix_fd, inet_fd, portnumber; talker_list *talker_list_anchor, *validation_anchor; unsigned long int intercom_status, job_id; job_list *job_anchor, *free_jobs_anchor; packet *free_packet_anchor; packet *talker_packet_anchor; net_usage server_net, total_net; int close_main_socket_now = 0; /*functions */ static void parse_user_action(char *); static void getDynamicHost (void); static void ping_all_down_talkers (void); static talker_list *match_talker_name (char *); static talker_list *match_talker_abbr_absolute (char *); static talker_list *match_talker_abbr_pattern (char *); static int add_new_talker (char *, int); static void tell_talker_su (char *); static int connect_new_talker (talker_list *); static char *set_name (char *); static void sync_talkers (void); static void make_unique_but (talker_list *, talker_list *); #ifdef __GNUC__ static int tell_remote_talker (talker_list *, const char *,...) __attribute__ ((format (printf, 2, 3))); static void tell_personal (const char *,...) __attribute__ ((format (printf, 1, 2))); static void tell_personal_inc_ref (job_list *, const char *,...) __attribute__ ((format (printf, 2, 3))); static void send_to_talker (const char *,...) __attribute__ ((format (printf, 1, 2))); #else static int tell_remote_talker (talker_list *, const char *,...); static void tell_personal (const char *,...); static void tell_personal_inc_ref (job_list *, const char *,...); static void send_to_talker (const char *,...); #endif static void parse_in_user_ichan_say_command(talker_list *, char *); static void parse_in_user_ichan_emote_command(talker_list *, char *); static void parse_in_user_ichan_action_command(talker_list *, char *); static void return_ichan_who(talker_list*, char *); static void do_ichan_who(talker_list *, char *); static void request_ichan_list_global(void); static void reply_ichan_list_global(char *); #define I_SHUTDOWN(x,y) if (shutdown(x,y)==-1) fail_shutdown(__LINE__,__FILE__); static int ATOI (char *str) { return strtol (str, (char **) NULL, 10); } /* modified these a bit to go better with the existant stuffs.... since soft messages wants status quo, and we need soft messages here... i undid these a bit.. no harm done at all... ~phy */ char *end_string (char *str) { return (strchr (str, 0) + 1); } void lower_case (char *str) { while (*str) { *str = tolower (*str); str++; } } /* return a string of the system time */ static char *sys_time (time_t t) { static char time_string[25]; strftime (time_string, 25, "%H:%M:%S - %d/%m/%Y", localtime (&t)); return time_string; } /* log errors and things to file */ static void log (const char *filename, const char *string) { int fd, length; sprintf (stack, "logs/%s.log", filename); #ifdef BSDISH fd = open (stack, O_CREAT | O_WRONLY | S_IRUSR | S_IWUSR); #else fd = open (stack, O_CREAT | O_WRONLY | O_SYNC, S_IRUSR | S_IWUSR); #endif length = lseek (fd, 0, SEEK_END); if (length > max_log_size * 1024) { close (fd); #ifdef BSDISH fd = open (stack, O_CREAT | O_WRONLY | O_TRUNC, S_IRUSR | S_IWUSR); #else fd = open (stack, O_CREAT | O_WRONLY | O_SYNC | O_TRUNC, S_IRUSR | S_IWUSR); #endif } sprintf (stack, "%s - %s\n", sys_time (time (NULL)), string); write (fd, stack, strlen (stack)); close (fd); return; } static void do_close (talker_list * target) { close (target->fd); target->fd = -1; target->validation = 0; return; } static void fail_shutdown (int line, const char *filename) { /* char *oldstack=stack; sprintf(stack,"Shutdown failed, %d %s ",line,filename); stack=strchr(stack,0); switch(errno) { case EBADF: sprintf(stack,"Not a valid fd"); break; case ENOTSOCK: sprintf(stack,"Socket is a file"); break; case ENOTCONN: sprintf(stack,"Socket not connected"); break; default: sprintf(stack,"Unknown error"); break; } stack=end_string(stack); log("intercom",oldstack); stack=oldstack; */ return; } static void close_all_sockets (void) { talker_list *scan; scan = talker_list_anchor; while (scan->next) { scan = scan->next; if (scan->fd > 0) { if (!(scan->flags & WAITING_CONNECT)) I_SHUTDOWN (scan->fd, 2); do_close (scan); } } return; } static void close_intercom_main_socket (void) { if (inet_fd > 0) { I_SHUTDOWN (inet_fd, 2); close (inet_fd); log ("intercom", "Closing main file descriptor"); inet_fd = -1; close_all_sockets (); } return; } static void fatal_error (const char *filename, const char *string) { stack = stack_start; log (filename, string); close_intercom_main_socket (); close_all_sockets (); abort (); } static void sigpipe (int dummy) { return; } static void sighup (int dummy) { log ("intercom", "Hangup received. Dying"); closedown = 1; return; } static void sigquit (int dummy) { fatal_error ("intercom", "Quit signal received."); } static void sigill (int dummy) { fatal_error ("intercom", "Illegal instruction."); } static void sigfpe (int dummy) { fatal_error ("intercom", "Floating Point Error."); } static void sigbus (int dummy) { fatal_error ("intercom", "Bus Error."); } static void sigsegv (int dummy) { fatal_error ("intercom", "Segmentation Violation."); } static void sigterm (int dummy) { fatal_error ("intercom", "Terminate signal received."); } static void sigxfsz (int dummy) { fatal_error ("intercom", "File descriptor limit exceeded."); } static void sigchld (int dummy) { fatal_error ("intercom", "Received SIGCHLD"); } static void sigusr1 (int dummy) { close_main_socket_now = 1; return; } /**********************JOBS HANDLING****************/ static job_list *return_job (char *str) { long unsigned int job_num; job_list *scan; if (!str || !*str) return 0; job_num = atoi (str); scan = job_anchor; while (scan->next != job_anchor && scan->next->job_id <= job_num) { scan = scan->next; if (scan->job_id == job_num) return scan; } return 0; } static void add_new_jobs (void) { int loopa; job_list *new_job; /*Add 10 new jobs in */ for (loopa = 0; loopa < 10; loopa++) { new_job = (job_list *) calloc (1, sizeof (job_list)); new_job->next = free_jobs_anchor->next; new_job->prev = free_jobs_anchor; free_jobs_anchor->next = new_job; new_job->next->prev = new_job; } return; } static job_list *make_job_entry (void) { job_list *target_job; if (free_jobs_anchor->next == NULL || free_jobs_anchor->next == free_jobs_anchor) add_new_jobs (); target_job = free_jobs_anchor->next; target_job->next->prev = target_job->prev; target_job->prev->next = target_job->next; target_job->next = job_anchor; target_job->prev = job_anchor->prev; job_anchor->prev = target_job; target_job->prev->next = target_job; target_job->job_id = ++job_id; target_job->timeout = system_time + 120; return target_job; } static void free_job (job_list * target_job) { target_job->next->prev = target_job->prev; target_job->prev->next = target_job->next; /*Wipe all info */ memset (target_job, 0, sizeof (job_list)); target_job->next = free_jobs_anchor->next; target_job->prev = free_jobs_anchor; target_job->next->prev = target_job; free_jobs_anchor->next = target_job; return; } static void setup_jobs_list (void) { job_anchor = (job_list *) calloc (1, sizeof (job_list)); free_jobs_anchor = (job_list *) calloc (1, sizeof (job_list)); job_anchor->next = job_anchor; job_anchor->prev = job_anchor; free_jobs_anchor->next = free_jobs_anchor; free_jobs_anchor->prev = free_jobs_anchor; add_new_jobs (); job_id = 0; return; } /*****************PACKET HANDLING********************/ static void make_new_packets (void) { packet *new_var; int loop; for (loop = 0; loop < 10; loop++) { new_var = (packet *) calloc (1, sizeof (packet)); new_var->next = free_packet_anchor->next; free_packet_anchor->next = new_var; } return; } static packet *get_new_packet (void) { packet *target; if (!(free_packet_anchor->next)) make_new_packets (); target = free_packet_anchor->next; free_packet_anchor->next = target->next; target->next = 0; return target; } static packet *add_packet_to_talker (talker_list * remote_talker) { packet *new_var, *scan; new_var = get_new_packet (); remote_talker->net_stats.packets_in++; total_net.packets_in++; if (!(remote_talker->packet_anchor)) { remote_talker->packet_anchor = new_var; return new_var; } scan = remote_talker->packet_anchor; while (scan->next) scan = scan->next; scan->next = new_var; return new_var; } static packet *add_packet_to_list (void) { packet *new_var, *scan; new_var = get_new_packet (); server_net.packets_in++; if (!talker_packet_anchor) { talker_packet_anchor = new_var; return new_var; } scan = talker_packet_anchor; while (scan->next) scan = scan->next; scan->next = new_var; return new_var; } static void free_packet (packet * target) { memset (target, 0, sizeof (packet)); target->next = free_packet_anchor->next; free_packet_anchor->next = target; return; } static void free_list_packets (void) { packet *target; target = talker_packet_anchor; while (target) { talker_packet_anchor = target->next; free_packet (target); target = talker_packet_anchor; } return; } static void free_talker_packets (talker_list * remote_talker) { packet *target; target = remote_talker->packet_anchor; while (target) { remote_talker->packet_anchor = target->next; free_packet (target); target = remote_talker->packet_anchor; } return; } static void setup_free_packets (void) { free_packet_anchor = (packet *) calloc (1, sizeof (packet)); return; } static talker_list *match_address (talker_list * remote_talker) { talker_list *scan; scan = talker_list_anchor; while (scan->next) { scan = scan->next; if (scan != remote_talker) { if (remote_talker->num[0] || remote_talker->num[1] || remote_talker->num[2] || remote_talker->num[3]) if (scan->num[0] == remote_talker->num[0] && scan->num[1] == remote_talker->num[1] && scan->num[2] == remote_talker->num[2] && scan->num[3] == remote_talker->num[3] && scan->port == remote_talker->port) return scan; if (remote_talker->addr[0]) if (scan->port == remote_talker->port && !strcasecmp (scan->addr, remote_talker->addr)) return scan; if (remote_talker->dynamic[0] && scan->dynamic[0]) if (scan->port == remote_talker->port && !strcasecmp (scan->dynamic, remote_talker->dynamic)) return scan; } } return 0; } static void actual_validate_send (talker_list * known_talker) { known_talker->validation = -(abs ((rand () % 100000) + 1)); tell_remote_talker (known_talker, "%c%d:%d", REQUEST_VALIDATION_AS, portnumber, abs (known_talker->validation)); /*We've sent the validation request, disconnect */ I_SHUTDOWN (known_talker->fd, 2); close (known_talker->fd); known_talker->fd = -1; return; } static void request_validation (talker_list * known_talker) { if (!connect_new_talker (known_talker)) { known_talker->flags |= VALIDATE_AFTER_CONNECT; return; } if (known_talker->flags & WAITING_CONNECT) { known_talker->flags |= VALIDATE_AFTER_CONNECT; return; } actual_validate_send (known_talker); return; } static void rerequest_validation (talker_list * remote_talker) { talker_list *known; known = match_address (remote_talker); if (known) request_validation (known); else { I_SHUTDOWN (remote_talker->fd, 2); do_close (remote_talker); } /*We have requested the validation, now we wait for its reply */ return; } static void parse_hello (talker_list * remote_talker, char *str) { char *oldstack; char *name, *abbr, *port_str; talker_list *known; int name_match; char dummy_name[MAX_TALKER_NAME + 1]; struct hostent *hp; struct in_addr inet_address; char *dynamic, *terminator, numeric_string[MAX_INET_ADDR]; char remote_ip[MAX_INET_ADDR]; oldstack = stack; if (!str || !*str) { /*Bad hello sent */ tell_remote_talker (remote_talker, "%c", BAD_HELLO); I_SHUTDOWN (remote_talker->fd, 2); do_close (remote_talker); return; } name = str; abbr = 0; port_str = 0; dynamic = 0; abbr = strchr (name, ':'); if (abbr) { *abbr++ = '\0'; if (*abbr) { port_str = strchr (abbr, ':'); if (port_str) { *port_str++ = '\0'; if (*port_str) { dynamic = strchr (port_str, ':'); if (dynamic) { *dynamic++ = 0; if (*dynamic) { terminator = strchr (dynamic, ':'); if (terminator) *terminator = 0; } } } } } } if (!port_str || !*port_str || !*abbr || !*name) { /*Bad hello sent */ tell_remote_talker (remote_talker, "%c", BAD_HELLO); I_SHUTDOWN (remote_talker->fd, 2); do_close (remote_talker); return; } if (strlen (name) > MAX_TALKER_NAME - 1) name[MAX_TALKER_NAME - 1] = '\0'; if (strlen (abbr) > MAX_TALKER_ABBR - 1) abbr[MAX_TALKER_ABBR - 1] = '\0'; /*We have a hello message */ /*Add the HELLO info into the struct we have */ strcpy (remote_talker->name, name); strcpy (remote_talker->abbr, abbr); lower_case (remote_talker->abbr); remote_talker->port = ATOI (port_str); if (dynamic && *dynamic) strcpy (remote_talker->dynamic, dynamic); else remote_talker->dynamic[0] = 0; /*Now we see if the dynamic matches the IP, if it doesnt, reject it */ if (dynamic && *dynamic) { hp = gethostbyname (dynamic); if (!hp) dynamic = 0; else { memcpy((char *)&inet_address,hp->h_addr,sizeof(struct in_addr)); strcpy(numeric_string,inet_ntoa(inet_address)); sprintf(remote_ip,"%d.%d.%d.%d", remote_talker->num[0], remote_talker->num[1], remote_talker->num[2], remote_talker->num[3]); if (strcmp(numeric_string,remote_ip)) dynamic=0; } if (!dynamic) { /*Bad hello sent */ tell_remote_talker (remote_talker, "%c", BAD_HELLO); I_SHUTDOWN (remote_talker->fd, 2); do_close (remote_talker); return; } } known = match_address (remote_talker); if (!known) { known = match_talker_name (remote_talker->name); /*it COULD be a talker on a dynamic IP address. We match the name to see if it is there and hasnt been seen for a while. If that is the case, we replace the old one. Damn dynamic IP addresses, I mean, what kind of people run a talker on dynamic IP*/ name_match = 1; if (known) { /*If it is connected */ if (known->fd < -1 || known->fd > 0 || (time (0) - known->last_seen) < 7 * ONE_DAY || known->port != remote_talker->port) known = 0; else name_match = 0; } else name_match = 0; while (!known && name_match) { strcpy (dummy_name, remote_talker->name); dummy_name[MAX_TALKER_NAME - 2] = 0; if (name_match > 9) dummy_name[MAX_TALKER_NAME - 3] = 0; if (name_match > 99) dummy_name[MAX_TALKER_NAME - 4] = 0; if (name_match > 999) dummy_name[MAX_TALKER_NAME - 5] = 0; sprintf (dummy_name, "%s%d", dummy_name, name_match); known = match_talker_name (dummy_name); if (known) { /*If it is connected */ if (known->fd < -1 || known->fd > 0 || (time (0) - known->last_seen) < 7 * ONE_DAY || known->port != remote_talker->port) { known = 0; name_match++; } } else name_match = 0; } /*We have found a match that we will use now */ if (known) /*Copy the address into the struct we have */ { strcpy (known->addr, remote_talker->addr); known->num[0] = remote_talker->num[0]; known->num[1] = remote_talker->num[1]; known->num[2] = remote_talker->num[2]; known->num[3] = remote_talker->num[3]; } } if (!known) { /*Its a new talker requesting connection */ if (match_talker_name (remote_talker->name) || match_talker_abbr_absolute (remote_talker->abbr)) make_unique_but (NULL, remote_talker); sprintf(oldstack,"%s:%s:%d.%d.%d.%d:%s:F:%ld:%s",name,abbr, remote_talker->num[0], remote_talker->num[1], remote_talker->num[2], remote_talker->num[3],port_str,system_time, dynamic&&*dynamic?dynamic:""); stack = end_string (oldstack); if (add_new_talker (oldstack, 0)) { sprintf (oldstack, " A new talker '%s' tried to connect from '%s %d'. " "Added it to the database as a banished talker.", remote_talker->name, remote_talker->addr, remote_talker->port); stack = end_string (oldstack); tell_talker_su (oldstack); } stack = oldstack; I_SHUTDOWN (remote_talker->fd, 2); do_close (remote_talker); sync_talkers (); return; } /*If we get here, known is pointing at the existing talker at that address that we know about. Thus, we work with 'known' and send a request to ask it to validate for us */ if (remote_talker->dynamic[0] && known->dynamic[0] && !strcasecmp (remote_talker->dynamic, known->dynamic)) strcpy (known->addr, remote_talker->addr); if (known->flags & FIRST_CONTACT) { if (strcmp (known->name, remote_talker->name) || strcmp (known->abbr, remote_talker->abbr)) { make_unique_but (known, remote_talker); strcpy (known->name, remote_talker->name); strcpy (known->abbr, remote_talker->abbr); } known->flags &= ~FIRST_CONTACT; } /*If it is a banished talker, tell it so, and close its connection */ if (known->fd == BARRED || known->fd == P_BARRED) { tell_remote_talker (remote_talker, "%c", BARRING_YOU); I_SHUTDOWN (remote_talker->fd, 2); do_close (remote_talker); return; } request_validation (known); /*We have requested the validation, now we wait for its reply */ return; } static void reply_validation (talker_list * known_talker) { if (known_talker->fd < 1) return; tell_remote_talker (known_talker, "%c%d:%d", VALIDATION_IS, portnumber, known_talker->validation); known_talker->last_seen = system_time; return; } static void parse_validation_request (talker_list * remote_talker, char *str) { char *port_str, *validation_str; talker_list *known; if (!str || !*str) { log ("intercom", "Empty validation string"); I_SHUTDOWN (remote_talker->fd, 2); do_close (remote_talker); return; } port_str = str; validation_str = strchr (port_str, ':'); if (validation_str) *validation_str++ = '\0'; if (!validation_str || !*validation_str || !*port_str) { log ("intercom", "Incorrect validation string"); I_SHUTDOWN (remote_talker->fd, 2); do_close (remote_talker); return; } remote_talker->port = ATOI (port_str); known = match_address (remote_talker); if (!known) { /*This was a validation request from an unknown server. Kill it */ log ("intercom", "Unknown talker sent validation. Killed."); I_SHUTDOWN (remote_talker->fd, 2); do_close (remote_talker); return; } known->validation = ATOI (validation_str); reply_validation (known); } static void check_validation (talker_list * remote_talker, char *str) { char *port_str, *validation_str; talker_list *known; if (!str || !*str) { log ("intercom", "Empty validation reply string"); I_SHUTDOWN (remote_talker->fd, 2); do_close (remote_talker); return; } port_str = str; validation_str = strchr (port_str, ':'); if (validation_str) *validation_str++ = '\0'; if (!validation_str || !*validation_str || !*port_str) { log ("intercom", "Incorrect validation reply string"); I_SHUTDOWN (remote_talker->fd, 2); do_close (remote_talker); return; } remote_talker->port = ATOI (port_str); known = match_address (remote_talker); if (!known) { /*This was a validation request from an unknown server. Kill it */ log ("intercom", "Unknown talker sent reply validation. Killed."); I_SHUTDOWN (remote_talker->fd, 2); do_close (remote_talker); return; } /*Check the validation is OK */ if (abs (ATOI (validation_str)) != abs (known->validation)) { /*The validation was bad, kill it */ tell_remote_talker (remote_talker, "%c", BAD_VALIDATION); I_SHUTDOWN (remote_talker->fd, 2); do_close (remote_talker); known->validation = 0; return; } /*The validation was SUCCESSFUL!! */ known->fd = remote_talker->fd; known->net_stats.established = system_time; remote_talker->fd = -1; known->validation = abs (known->validation); known->last_seen = system_time; return; } static void tell_intercom_room (char *msg) { send_to_talker ("%c%s", INTERCOM_ROOM_MESSAGE, msg); return; } static void parse_in_user_tell_command (talker_list * remote_talker, char *str) { char *job_str, *from_name, *msg = 0, *oldstack; job_list *this_job; oldstack = stack; if (!str || !*str) { log ("intercom", "Bad user message sent"); return; } job_str = str; str = strchr (job_str, ':'); if (str) *str++ = '\0'; else { log ("intercom", "Bad user message sent"); return; } from_name = set_name (str); if (from_name && *from_name) { msg = strchr (from_name, ':'); if (msg) *msg++ = '\0'; } if (!from_name || !*from_name || !job_str || !*job_str || !msg || !*msg) { log ("intercom", "Bad user message sent"); return; } this_job = make_job_entry (); this_job->job_ref = ATOI (job_str); strcpy (this_job->originator, remote_talker->abbr); intercom_status |= INTERCOM_HIGHLIGHT | INTERCOM_PERSONAL_MSG; sprintf (oldstack, "%s@%s ", from_name, remote_talker->abbr); stack = strchr (oldstack, '\0'); switch (msg[strlen (msg) - 1]) { case '!': sprintf (stack, "exclaims to you, '%s'", msg); break; case '?': sprintf (stack, "asks of you, '%s'", msg); break; default: sprintf (stack, "tells you, '%s'", msg); break; } stack = end_string (oldstack); tell_personal_inc_ref (this_job, "%s", oldstack); stack = oldstack; intercom_status &= ~(INTERCOM_HIGHLIGHT | INTERCOM_PERSONAL_MSG); return; } static void parse_in_user_remote_command (talker_list * remote_talker, char *str) { char *job_str, *from_name, *msg = 0; job_list *this_job; if (!str || !*str) { log ("intercom", "Bad user message sent"); return; } job_str = str; str = strchr (job_str, ':'); if (str) *str++ = '\0'; else { log ("intercom", "Bad user message sent"); return; } from_name = set_name (str); if (from_name && *from_name) { msg = strchr (from_name, ':'); if (msg) *msg++ = '\0'; } if (!from_name || !*from_name || !job_str || !*job_str || !msg || !*msg) { log ("intercom", "Bad user message sent"); return; } this_job = make_job_entry (); this_job->job_ref = ATOI (job_str); strcpy (this_job->originator, remote_talker->abbr); intercom_status |= INTERCOM_HIGHLIGHT | INTERCOM_PERSONAL_MSG; tell_personal_inc_ref (this_job, "%s@%s %s", from_name, remote_talker->abbr, msg); intercom_status &= ~(INTERCOM_HIGHLIGHT | INTERCOM_PERSONAL_MSG); return; } static void parse_in_user_examine_command (talker_list * remote_talker, char *str) { char *job_str, *name, *ptr; job_list *this_job; job_str = str; name = strchr (job_str, ':'); if (name) { *name++ = '\0'; ptr = strchr (name, ':'); if (ptr) *ptr = '\0'; } if (!job_str || !*job_str || !name || !*name) return; this_job = make_job_entry (); this_job->job_ref = ATOI (job_str); strcpy (this_job->originator, remote_talker->abbr); send_to_talker ("%c%c%ld:%s", USER_COMMAND, COMMAND_EXAMINE, this_job->job_id, name); return; } static void parse_in_user_finger_command (talker_list * remote_talker, char *str) { char *job_str, *name, *ptr; job_list *this_job; job_str = str; name = strchr (job_str, ':'); if (name) { *name++ = '\0'; ptr = strchr (name, ':'); if (ptr) *ptr = '\0'; } if (!job_str || !*job_str || !name || !*name) return; this_job = make_job_entry (); this_job->job_ref = ATOI (job_str); strcpy (this_job->originator, remote_talker->abbr); send_to_talker ("%c%c%ld:%s", USER_COMMAND, COMMAND_FINGER, this_job->job_id, name); return; } static void parse_in_user_who_command (talker_list * remote_talker, char *str) { char *job_str, *ptr; job_list *this_job; job_str = str; ptr = strchr (job_str, ':'); if (ptr) *ptr++ = '\0'; if (!*job_str) return; this_job = make_job_entry (); this_job->job_ref = ATOI (job_str); strcpy (this_job->originator, remote_talker->abbr); send_to_talker ("%c%c%ld", USER_COMMAND, COMMAND_WHO, this_job->job_id); return; } static void parse_in_user_lsu_command (talker_list * remote_talker, char *str) { char *job_str, *ptr; job_list *this_job; job_str = str; ptr = strchr (job_str, ':'); if (ptr) *ptr++ = '\0'; if (!*job_str) return; this_job = make_job_entry (); this_job->job_ref = ATOI (job_str); strcpy (this_job->originator, remote_talker->abbr); send_to_talker ("%c%c%ld", USER_COMMAND, COMMAND_LSU, this_job->job_id); return; } static void parse_in_user_locate_command (talker_list * remote_talker, char *str) { char *job_str, *name; job_list *this_job; job_str = str; name = strchr (job_str, ':'); if (name) *name++ = '\0'; if (!*job_str || !name || !*name) return; this_job = make_job_entry (); this_job->job_ref = ATOI (job_str); strcpy (this_job->originator, remote_talker->abbr); send_to_talker ("%c%c%ld:%s", USER_COMMAND, COMMAND_LOCATE, this_job->job_id, name); return; } static void parse_in_user_idle_command (talker_list * remote_talker, char *str) { char *job_str, *name, *ptr; job_list *this_job; job_str = str; name = strchr (job_str, ':'); if (name) { *name++ = '\0'; ptr = strchr (name, ':'); if (ptr) *ptr = '\0'; } if (!job_str || !*job_str || !name || !*name) return; this_job = make_job_entry (); this_job->job_ref = ATOI (job_str); strcpy (this_job->originator, remote_talker->abbr); send_to_talker ("%c%c%ld:%s", USER_COMMAND, COMMAND_IDLE, this_job->job_id, name); return; } static void parse_in_user_say_command (talker_list * remote_talker, char *str) { char *name, *msg = 0, *ptr, *oldstack; const char *method; name = str; if (name && *name) { msg = strchr (name, ':'); if (msg) *msg++ = 0; } if (!name || !*name || !msg || !*msg) { log ("intercom", "Invalid string in parse_in_user_say_command"); return; } /*Build a reply string */ oldstack = stack; ptr = (strchr (msg, 0)) - 1; switch (*ptr) { case '!': method = "exclaims"; break; case '?': method = "asks"; break; default: method = "says"; break; } sprintf (oldstack, " %s@%s %s '%s'", name, remote_talker->abbr, method, msg); stack = end_string (oldstack); tell_intercom_room (oldstack); stack = oldstack; return; } static void parse_in_user_emote_command (talker_list * remote_talker, char *str) { char *name, *msg = 0, *oldstack; name = str; if (name && *name) { msg = strchr (name, ':'); if (msg) *msg++ = 0; } if (!name || !*name || !msg || !*msg) { log ("intercom", "Invalid string in parse_in_user_emote_command"); return; } /*Build a reply string */ oldstack = stack; if (*msg == '\'') sprintf (oldstack, "%s@%s%s", name, remote_talker->abbr, msg); else sprintf (oldstack, "%s@%s %s", name, remote_talker->abbr, msg); stack = end_string (oldstack); tell_intercom_room (oldstack); stack = oldstack; return; } static void parse_in_user_command (talker_list * remote_talker, char *str) { if (!str || !*str) return; switch (*str) { case COMMAND_WHO: parse_in_user_who_command (remote_talker, str + 1); break; case COMMAND_EXAMINE: parse_in_user_examine_command (remote_talker, str + 1); break; case COMMAND_FINGER: parse_in_user_finger_command (remote_talker, str + 1); break; case COMMAND_TELL: parse_in_user_tell_command (remote_talker, str + 1); break; case COMMAND_REMOTE: parse_in_user_remote_command (remote_talker, str + 1); break; case COMMAND_LSU: parse_in_user_lsu_command (remote_talker, str + 1); break; case COMMAND_LOCATE: parse_in_user_locate_command (remote_talker, str + 1); break; case COMMAND_IDLE: parse_in_user_idle_command (remote_talker, str + 1); break; case COMMAND_SAY: parse_in_user_say_command (remote_talker, str + 1); break; case COMMAND_EMOTE: parse_in_user_emote_command (remote_talker, str + 1); break; case ICHAN_SAY: parse_in_user_ichan_say_command(remote_talker, str + 1); break; case ICHAN_EMOTE: parse_in_user_ichan_emote_command(remote_talker, str + 1); break; case ICHAN_ACTION: parse_in_user_ichan_action_command(remote_talker, str + 1); break; } return; } static void return_good_tell_command (talker_list * remote_talker, job_list * this_job) { switch (this_job->msg[strlen (this_job->msg) - 1]) { case '?': tell_personal (" You ask of %s@%s, '%s'", this_job->target, remote_talker->abbr, this_job->msg); break; case '!': tell_personal (" You exclaim to %s@%s, '%s'", this_job->target, remote_talker->abbr, this_job->msg); break; default: tell_personal (" You tell %s@%s, '%s'", this_job->target, remote_talker->abbr, this_job->msg); break; } return; } static void return_good_remote_command (talker_list * remote_talker, job_list * this_job) { if (*(this_job->msg) == 39) tell_personal (" You emote: '%s%s' to %s@%s", current_name, this_job->msg, this_job->target, remote_talker->abbr); else tell_personal (" You emote: '%s %s' to %s@%s", current_name, this_job->msg, this_job->target, remote_talker->abbr); return; } static void return_good_command (talker_list * remote_talker, job_list * this_job) { switch (this_job->command_type) { case COMMAND_TELL: return_good_tell_command (remote_talker, this_job); break; case COMMAND_REMOTE: return_good_remote_command (remote_talker, this_job); break; } return; } static void send_who_reply_to_server (talker_list * remote_talker, job_list * this_job, char *str) { intercom_status |= INTERCOM_FORMAT_MSG; tell_personal ("%s%s", str, remote_talker->name); intercom_status &= ~INTERCOM_FORMAT_MSG; return; } static void send_string_reply_to_server (talker_list * remote_talker, job_list * this_job, char *str) { tell_personal ("%s", str); return; } static void parse_reply_from_remote (talker_list * remote_talker, char *str) { job_list *this_job; char *ptr = 0, *name; char blank_field[2]; blank_field[0] = 'x'; blank_field[1] = '\0'; if (!str || !*str || !*(str + 1)) { log ("intercom", "Bad reply received for remote command result."); return; } name = strchr (str + 1, ':'); if (name) { *name++ = '\0'; if (*name) { ptr = strchr (name, ':'); if (ptr) *ptr++ = 0; } } if (!name || !*name) { log ("error", "Invalid format in parse_reply_from_remote"); return; } if (!ptr || !*ptr) ptr = blank_field; this_job = return_job (str + 1); if (!this_job) /*The job must have expired */ return; if (this_job->command_type != COMMAND_LOCATE) strcpy (this_job->target, name); strcpy (current_name, this_job->sender); /*Here we have the job detailing what was sent by who. Build a reply and finish */ switch (*str) { case COMMAND_WHO: send_who_reply_to_server (remote_talker, this_job, ptr); break; case COMMAND_EXAMINE: case COMMAND_FINGER: case COMMAND_LSU: case COMMAND_IDLE: send_string_reply_to_server (remote_talker, this_job, ptr); break; case COMMAND_LOCATE: strcpy (current_name, this_job->sender); if (*name == COMMAND_SUCCESSFUL) tell_personal (" Talker %s (%s) reports %s is logged in there.", remote_talker->name, remote_talker->abbr, this_job->target); current_name[0] = 0; /*Now return so it doenst free the job here cos we have more replies possibly stuill to come */ return; break; case NO_SUCH_PLAYER: strcpy (current_name, this_job->sender); if (this_job->command_type == COMMAND_FINGER) tell_personal (" No such person in saved files."); else tell_personal (" No-one of the name '%s' on %s at the moment.", this_job->target, remote_talker->name); break; case NAME_IGNORED: strcpy (current_name, this_job->sender); tell_personal (" %s is ignoring you.", this_job->target); break; case NAME_BANISHED: strcpy (current_name, this_job->sender); tell_personal (" Your name is banished on %s.", remote_talker->name); break; case NAME_BLOCKED: strcpy (current_name, this_job->sender); tell_personal (" %s is blocking you.", this_job->target); break; case TALKER_IGNORED: strcpy (current_name, this_job->sender); tell_personal (" %s is ignoring %s.", this_job->target, get_config_msg ("talker_name")); break; case TALKER_BLOCKED: strcpy (current_name, this_job->sender); tell_personal (" %s is blocking %s.", this_job->target, get_config_msg ("talker_name")); break; case COMMAND_SUCCESSFUL: return_good_command (remote_talker, this_job); break; default: strcpy (current_name, this_job->sender); tell_personal (" Unknown reply from remote intercom."); break; } current_name[0] = '\0'; free_job (this_job); return; } static void get_remote_list (talker_list * remote_talker, char *str) { char *name, *addr, *abbr, *port_str, *ptr, *oldstack; int port, all_num, dots, num[4]; struct hostent *hp; struct in_addr inet_address; char numeric_string[MAX_INET_ADDR]; talker_list *scan; char *dynamic, *terminator; name = str; abbr = 0; addr = 0; port_str = 0; dynamic = 0; if (name && *name) { abbr = strchr (name, ':'); if (abbr) { *abbr++ = '\0'; if (*abbr) { addr = strchr (abbr, ':'); if (addr) { *addr++ = '\0'; if (*addr) { port_str = strchr (addr, ':'); if (port_str) { *port_str++ = '\0'; if (*port_str) { dynamic = strchr (port_str, ':'); if (dynamic) { *dynamic++ = 0; if (*dynamic) { terminator = strchr (dynamic, ':'); if (terminator) *terminator = 0; } } } } } } } } } if (!port_str || !*port_str || !*addr || !*abbr || !*name || !name) return; if (match_talker_name (name) || match_talker_abbr_absolute (name) || match_talker_name (abbr) || match_talker_abbr_absolute (abbr)) return; if (!strcasecmp (name, get_config_msg ("talker_name")) || !strcasecmp (abbr, get_config_msg ("intercom_abbr"))) return; /*We need to check its not a duplicate name */ /*Test whether the address we have is all in numeric */ dots = 0; all_num = 1; ptr = addr; while (all_num && *ptr) { if (!isdigit (*ptr) && *ptr != '.') all_num = 0; if (*ptr++ == '.') dots++; } if (all_num) { if (dots != 3) return; strncpy (numeric_string, addr, MAX_INET_ADDR - 1); numeric_string[MAX_INET_ADDR - 1] = 0; } else { /*Its an alpha address, we need to convert */ hp = gethostbyname (addr); if (!hp) return; memcpy ((char *) &inet_address, hp->h_addr, sizeof (struct in_addr)); strcpy (numeric_string, inet_ntoa (inet_address)); } /*We have a dot notation address in numeric_string, so lets paste this into the struct */ sscanf (numeric_string, "%d.%d.%d.%d", &(num[0]), &(num[1]), &(num[2]), &(num[3])); port = ATOI (port_str); /*We have an address we can use now. */ /*Check it against all other addresses.... */ scan = talker_list_anchor; while (scan->next) { scan = scan->next; if (((scan->num[0] == num[0] && scan->num[1] == num[1] && scan->num[2] == num[2] && scan->num[3] == num[3]) || !strcasecmp (scan->addr, addr)) && scan->port == port) /*Its the same site, so we dont want it */ return; if (dynamic && *dynamic && !strcasecmp (scan->dynamic, dynamic) && scan->port == port) return; } /*This is a new talker we DO want, so add it */ oldstack = stack; sprintf(oldstack,"%s:%s:%d.%d.%d.%d:%s:F:%ld:%s",name,abbr,num[0], num[1],num[2],num[3],port_str,system_time, dynamic&&*dynamic?dynamic:""); stack = end_string (oldstack); if (add_new_talker (oldstack, 0)) { sprintf (oldstack, "Talker '%s' just informed us of talker '%s'. Added to the" " database as a banished talker.", remote_talker->name, name); stack = end_string (oldstack); tell_talker_su (oldstack); } stack = oldstack; sync_talkers (); return; } static void send_server_list (talker_list * remote_server) { talker_list *scan; char address[MAX_INET_ADDR]; scan = talker_list_anchor; while (scan->next) { scan = scan->next; if (scan->fd > 0 && scan->validation > 0 && !(scan->flags & WAITING_CONNECT)) { if (scan->num[0] == 0 && scan->num[1] == 0 && scan->num[2] == 0 && scan->num[3] == 0) strcpy (address, scan->addr); else sprintf (address, "%d.%d.%d.%d", scan->num[0], scan->num[1], scan->num[2], scan->num[3]); tell_remote_talker (remote_server, "%c%s:%s:%s:%d:%s", I_KNOW_OF, scan->name, scan->abbr, address, scan->port, scan->dynamic); } } return; } static void remote_has_barred (talker_list * remote_talker) { I_SHUTDOWN (remote_talker->fd, 2); do_close (remote_talker); remote_talker->fd = BARRED_REMOTE; } static void do_room_look (talker_list * remote_talker, char *str) { job_list *this_job; this_job = make_job_entry (); this_job->job_ref = ATOI (str); strcpy (this_job->originator, remote_talker->abbr); send_to_talker ("%c%ld", INTERCOM_ROOM_LOOK, this_job->job_id); return; } static void return_room_look (talker_list * remote_talker, char *str) { char *user_list = 0, *count_str = 0, *job_str; job_list *this_job; /*Format of str is job_id:count:list */ job_str = str; if (job_str && *job_str) { count_str = strchr (job_str, ':'); if (count_str) { *count_str++ = 0; if (*count_str) { user_list = strchr (count_str, ':'); if (user_list) *user_list++ = 0; } } } if (!user_list || !*user_list || !*count_str || !job_str || !*job_str) { log ("intercom", "Bad string in return_room_look"); return; } this_job = return_job (job_str); strcpy (current_name, this_job->sender); tell_personal (" Talker '%s' has %s user%s here: %s", remote_talker->name, count_str, ATOI (count_str) == 1 ? "" : "s", user_list); return; } static void informed_move (talker_list * remote_talker, char *str) { int dots, all_num; char *site, *port = 0, *ptr, numeric_string[MAX_INET_ADDR], *oldstack; struct hostent *hp; struct in_addr inet_address; struct sockaddr_in sa; /*Split this into site and port */ site = str; if (site && *site) { port = strchr (site, ':'); if (port) *port++ = 0; } if (!port || !*port || !site || !*site) { log ("intercom", "Bad string from informed_move"); return; } I_SHUTDOWN (remote_talker->fd, 2); do_close (remote_talker); /*Test whether the address we have is all in numeric */ dots = 0; all_num = 1; ptr = site; while (all_num && *ptr) { if (!isdigit (*ptr) && *ptr != '.') all_num = 0; if (*ptr++ == '.') dots++; } if (all_num) { if (dots != 3 || !strcmp (site, "0.0.0.0")) return; strncpy (numeric_string, site, MAX_INET_ADDR - 1); numeric_string[MAX_INET_ADDR - 1] = 0; /*Now see if we can lookup the name address */ hp = 0; inet_address.s_addr = inet_addr (numeric_string); if (inet_address.s_addr != -1) { sa.sin_addr = inet_address; sa.sin_family = AF_INET; /*Do the name lookup */ hp = gethostbyaddr ((char *) &(sa.sin_addr.s_addr), sizeof (sa.sin_addr.s_addr), AF_INET); } /*If we found it, use it */ if (hp) strcpy (remote_talker->addr, (const char *) hp->h_name); else strcpy (remote_talker->addr, inet_ntoa (sa.sin_addr)); } else { /*Its an alpha address, we need to convert */ hp = gethostbyname (site); if (!hp) return; else { memcpy ((char *) &inet_address, hp->h_addr, sizeof (struct in_addr)); strcpy (numeric_string, inet_ntoa (inet_address)); } } /*We have a dot notation address in numeric_string, so lets paste this into the struct */ sscanf (numeric_string, "%d.%d.%d.%d", &(remote_talker->num[0]), &(remote_talker->num[1]), &(remote_talker->num[2]), &(remote_talker->num[3])); remote_talker->port = ATOI (port); sync_talkers (); /*Announce the change */ oldstack = stack; sprintf (oldstack, "Talker '%s' just changed its address to %s %d", remote_talker->name, remote_talker->addr, remote_talker->port); stack = end_string (oldstack); tell_talker_su (oldstack); stack = oldstack; return; } static void do_user_action(talker_list *remote_talker,char *str) { switch(*str) { case ENTER_ROOM: case LEAVE_ROOM: send_to_talker("%c%s:%s",USER_ACTION,str,remote_talker->abbr); break; } return; } static void parse_remote_talker_input (talker_list * remote_talker, int validated) { char c; int chars_left; char *oldstack, *ptr; int parse_ok = 0; packet *this_packet; /*Firstly, if we were waiting for a connect, this means it failed if we get here */ if (remote_talker->flags & WAITING_CONNECT) { do_close (remote_talker); remote_talker->flags &= ~WAITING_CONNECT; remote_talker->flags &= ~HELLO_AFTER_CONNECT; return; } if (ioctl (remote_talker->fd, FIONREAD, &chars_left) == -1) { I_SHUTDOWN (remote_talker->fd, 2); do_close (remote_talker); log ("intercom", "PANIC on FIONREAD on remote_talker->fd."); return; } if (!chars_left) { I_SHUTDOWN (remote_talker->fd, 2); do_close (remote_talker); return; } this_packet = add_packet_to_talker (remote_talker); oldstack = stack; c = (char) (END_MESSAGE - 1); while (chars_left && c != (char) END_MESSAGE) { chars_left--; remote_talker->net_stats.chars_in++; total_net.chars_in++; if (read (remote_talker->fd, &c, 1) != 1) { I_SHUTDOWN (remote_talker->fd, 2); do_close (remote_talker); return; } if (c != (char) END_MESSAGE) { this_packet->data[this_packet->length] = c; this_packet->length++; if (this_packet->length >= MAX_PACKET) this_packet = add_packet_to_talker (remote_talker); } else parse_ok = 1; } if (intercom_status & BAR_ALL_CONNECTIONS) { stack = oldstack; if (chars_left > 0) parse_remote_talker_input (remote_talker, validated); return; } if (!parse_ok) return; this_packet = remote_talker->packet_anchor; strcpy (oldstack, this_packet->data); stack = strchr (oldstack, 0); while (this_packet->next) { this_packet = this_packet->next; strcpy (stack, this_packet->data); stack = strchr (stack, 0); } stack++; ptr = oldstack; /*HERE note we HAVE seen this talker recently */ remote_talker->last_seen = system_time; if (*ptr) { if (validated) { switch (*ptr) { case USER_COMMAND: parse_in_user_command (remote_talker, ptr + 1); break; case REPLY_IS: parse_reply_from_remote (remote_talker, ptr + 1); break; case REQUEST_SERVER_LIST: send_server_list (remote_talker); break; case I_KNOW_OF: get_remote_list (remote_talker, ptr + 1); break; case BARRING_YOU: remote_has_barred (remote_talker); break; case INTERCOM_ROOM_LOOK: do_room_look (remote_talker, ptr + 1); break; case INTERCOM_ROOM_LIST: return_room_look (remote_talker, ptr + 1); break; case WE_ARE_MOVING: informed_move (remote_talker, ptr + 1); break; case USER_ACTION: do_user_action(remote_talker,ptr+1); break; case INTERCOM_ICHAN_WHO: do_ichan_who(remote_talker, ptr + 1); break; case INTERCOM_ICHAN_LIST: return_ichan_who(remote_talker, ptr+1); break; } } else { switch (*ptr) { case HELLO_I_AM: parse_hello (remote_talker, ptr + 1); break; case REQUEST_VALIDATION_AS: parse_validation_request (remote_talker, ptr + 1); break; case VALIDATION_IS: check_validation (remote_talker, ptr + 1); break; default: rerequest_validation (remote_talker); } } } stack = oldstack; free_talker_packets (remote_talker); if (chars_left > 0) parse_remote_talker_input (remote_talker, validated); return; } static int get_new_talker_connection (void) { #ifdef HAVE_SOCKLEN_T socklen_t incoming_length; #else int incoming_length; #endif /* HAVE_SOCKLEN_T */ struct sockaddr_in sa; int new_fd, dummy = 0; talker_list *new_var, *scan; struct hostent *hp; struct linger lingerval; incoming_length = sizeof (sa); new_fd = accept (inet_fd, (struct sockaddr *) &sa, &incoming_length); if (new_fd < 0) { log ("intercom", "Failed to accept on inet_fd"); return -1; } if (ioctl (new_fd, FIONBIO, &dummy) < 0) { log ("intercom", "Failed to set non-blocking on incoming" " remote talker."); I_SHUTDOWN (new_fd, 2); close (new_fd); return 0; } dummy = 1; setsockopt (new_fd, SOL_SOCKET, SO_REUSEADDR, (char *) &dummy, sizeof (dummy)); lingerval.l_onoff = 1; lingerval.l_linger = 0; setsockopt (new_fd, SOL_SOCKET, SO_LINGER, (struct linger *) &lingerval, sizeof (struct linger)); /*if the intercom is closed, bar all connection attempts */ if (intercom_status & BAR_ALL_CONNECTIONS) { I_SHUTDOWN (new_fd, 2); close (new_fd); return 0; } /*We have a new connection, so make a new struct for it */ scan = validation_anchor; while (scan->next) scan = scan->next; new_var = (talker_list *) calloc (1, sizeof (talker_list)); scan->next = new_var; new_var->fd = new_fd; new_var->timeout = system_time + 240; /*Extract their address from the socket info */ sscanf (inet_ntoa (sa.sin_addr), "%d.%d.%d.%d", &(new_var->num[0]), &(new_var->num[1]), &(new_var->num[2]), &(new_var->num[3])); /*If possible, get the name address too */ hp = gethostbyaddr ((char *) &(sa.sin_addr.s_addr), sizeof (sa.sin_addr.s_addr), AF_INET); if (hp) strcpy (new_var->addr, (const char *) hp->h_name); else strcpy (new_var->addr, inet_ntoa (sa.sin_addr)); /*We have as much as we can get now. Lets wait for the HELLO */ return talker_fd; } static int create_main_intercom_socket (const char *path) { struct sockaddr_un sa; /*Unlink the path in case its still there */ unlink (path); unix_fd = socket (PF_UNIX, SOCK_STREAM, 0); if (unix_fd < 0) { log ("intercom", "Failed to create unix socket."); return -1; } sa.sun_family = AF_UNIX; strcpy (sa.sun_path, path); if (bind (unix_fd, (struct sockaddr *) &sa, sizeof (sa)) < 0) { log ("intercom", "Failed to bind to unix socket."); return -1; } if (listen (unix_fd, 1) < 0) { log ("intercom", "Failed to listen at unix socket"); I_SHUTDOWN (unix_fd, 2); close (unix_fd); unix_fd = -1; unlink (path); return -1; } log ("intercom", "Unix socket bound and listening."); return unix_fd; } static int get_unix_fd_input (void) { #ifdef HAVE_SOCKLEN_T socklen_t incoming_length; #else int incoming_length; #endif /* HAVE_SOCKLEN_T */ struct sockaddr_un sa; incoming_length = sizeof (sa); talker_fd = accept (unix_fd, (struct sockaddr *) &sa, &incoming_length); if (talker_fd < 0) { log ("intercom", "Failed to accept on unix_fd"); return -1; } log ("intercom", "Connected intercom to talker"); server_net.established = system_time; return talker_fd; } void send_to_talker (const char *fmt,...) { va_list varlist; char *oldstack; oldstack = stack; if (!fmt || !*fmt) { log ("intercom", "Empty string in send_to_talker"); return; } va_start (varlist, fmt); vsprintf (stack, fmt, varlist); va_end (varlist); if (!*oldstack) { log ("intercom", "Tried to send null string to server."); return; } stack = strchr (oldstack, '\0'); *stack++ = (char) END_MESSAGE; *stack++ = '\0'; server_net.chars_out += strlen (oldstack); server_net.packets_out++; if (write (talker_fd, oldstack, strlen (oldstack)) == -1) /*Write to the talker failed. Die controlled */ closedown = 1; stack = oldstack; return; } static void request_port_from_talker (void) { /*Send message to the talker requesting info on what port to run on */ log ("intercom", "Requesting port number from talker"); send_to_talker ("%c", REQUEST_PORTNUMBER); return; } int tell_remote_talker (talker_list * remote_talker, const char *fmt,...) { va_list varlist; char *oldstack, *ptr; int fail = 0; if (!fmt || !*fmt) { log ("intercom", "Tried to write NULL to remote talker"); return -1; } if (remote_talker->fd < 1) return -1; oldstack = stack; va_start (varlist, fmt); vsprintf (oldstack, fmt, varlist); va_end (varlist); stack = strchr (oldstack, 0); *stack++ = (char) END_MESSAGE; *stack++ = '\0'; ptr = oldstack; while (*ptr && (strchr (ptr, 0) - ptr) >= MAX_PACKET) { remote_talker->net_stats.chars_out += MAX_PACKET; remote_talker->net_stats.packets_out++; total_net.chars_out += MAX_PACKET; total_net.packets_out++; if (write (remote_talker->fd, ptr, MAX_PACKET) == -1) fail = 1; ptr += MAX_PACKET; } if (*ptr) { remote_talker->net_stats.chars_out += (strchr (ptr, 0) - ptr); remote_talker->net_stats.packets_out++; total_net.chars_out += (strchr (ptr, 0) - ptr); total_net.packets_out++; if (write (remote_talker->fd, ptr, (strchr (ptr, 0) - ptr)) == -1) fail = 1; } /*If any writes failed, dump the connection */ if (fail) { I_SHUTDOWN (remote_talker->fd, 2); do_close (remote_talker); } stack = oldstack; return 1; } static void send_hello (talker_list * remote_talker) { char *oldstack = stack; /* A cludge I'm afriad - using two get_config_msg's in 'tell_remote_talker' doesn't seem to work so we'll break down the string --Silver */ sprintf(stack, "%c%s:", HELLO_I_AM, get_config_msg("talker_name")); stack = strchr(stack, 0); sprintf(stack, "%s:%d", get_config_msg("intercom_abbr"), portnumber); stack = strchr(stack, 0); if (dynamic_dns && *dynamic_dns) { sprintf(stack, ":%s", dynamic_dns); stack = strchr(stack, 0); } *stack++ = 0; tell_remote_talker(remote_talker, oldstack); stack = oldstack; } int connect_new_talker (talker_list * new_link) { char numeric_string[MAX_INET_ADDR]; struct in_addr inet_address; struct sockaddr_in sa; int flags, dummy = 1; struct linger lingerval; if (new_link->flags & WAITING_CONNECT) new_link->flags &= ~WAITING_CONNECT; if (new_link->flags & HELLO_AFTER_CONNECT) new_link->flags &= ~HELLO_AFTER_CONNECT; if (new_link->flags & VALIDATE_AFTER_CONNECT) new_link->flags &= ~VALIDATE_AFTER_CONNECT; if (new_link->fd == BARRED || new_link->fd == P_BARRED || new_link->flags & INVIS) return 0; if (inet_fd < 1) { new_link->fd = -1; return 0; } if (new_link->fd > 0) { I_SHUTDOWN (new_link->fd, 2); do_close (new_link); } if (!(new_link->num[0] || new_link->num[1] || new_link->num[2] || new_link->num[3])) { new_link->fd = ERROR_FD; return 0; } if (intercom_status & INTERCOM_BOOTING) { new_link->fd = NO_CONNECT_TRIED; return 0; } sprintf (numeric_string, "%d.%d.%d.%d", new_link->num[0], new_link->num[1], new_link->num[2], new_link->num[3]); inet_address.s_addr = inet_addr (numeric_string); if (inet_address.s_addr == -1) { new_link->fd = -1; return 0; } new_link->fd = socket (AF_INET, SOCK_STREAM, 0); if (new_link->fd < 0) { log ("intercom", "Failed to create socket for remote link"); return 0; } dummy = 1; setsockopt (new_link->fd, SOL_SOCKET, SO_REUSEADDR, (char *) &dummy, sizeof (dummy)); lingerval.l_onoff = 1; lingerval.l_linger = 0; setsockopt (new_link->fd, SOL_SOCKET, SO_LINGER, (struct linger *) &lingerval, sizeof (struct linger)); sa.sin_family = AF_INET; sa.sin_port = htons ((new_link->port) - 1); sa.sin_addr = inet_address; ioctl (new_link->fd, FIONBIO, &dummy); flags = 0; send_to_talker ("%c", STARTING_CONNECT); /*Now connect to the remote server */ if (connect (new_link->fd, (struct sockaddr *) &sa, sizeof (sa)) == 0) { /*Connect was successful */ /*Let the server know we have finished */ send_to_talker ("%c", PING); /*Here, we have connected */ new_link->net_stats.established = system_time; return 1; } else if (errno == EINPROGRESS) { /*The connection needs more time.... */ new_link->flags |= WAITING_CONNECT; return 0; } /*It failed. */ do_close (new_link); send_to_talker ("%c", PING); return 0; } static void create_inet_socket (char *str) { struct sockaddr_in main_socket; int dummy = 0; char hostname[101], *oldstack; struct hostent *hp; talker_list *scan; int local_errno; struct linger lingerval; oldstack = stack; if (!*str) { log ("intercom", "Talker sent zero length portnumber. Re-requesting."); request_port_from_talker (); return; } portnumber = ATOI (str); if (portnumber < 1) { sprintf (oldstack, "Talker sent port under 1 (%d), Re-requesting.", portnumber); stack = end_string (oldstack); log ("intercom", oldstack); stack = oldstack; request_port_from_talker (); return; } /*We have a portnumber and its over 0 */ bzero ((char *) &main_socket, sizeof (struct sockaddr_in)); gethostname (hostname, 100); hp = gethostbyname (hostname); if (hp == NULL) { log ("intercom", "Host machine does not exist"); return; } main_socket.sin_family = hp->h_addrtype; main_socket.sin_port = htons (portnumber); /*Add 1 to portnumber, as this will now refer to the main talkers port */ portnumber++; inet_fd = socket (AF_INET, SOCK_STREAM, 0); if (inet_fd < 0) { log ("intercom", "Couldnt grab inet_fd socket"); inet_fd = -1; return; } dummy = 1; if (setsockopt (inet_fd, SOL_SOCKET, SO_REUSEADDR, (char *) &dummy, sizeof (dummy)) < 0) log ("intercom", "Couldnt set reuse address on inet_fd, ignoring."); lingerval.l_onoff = 1; lingerval.l_linger = 0; setsockopt (inet_fd, SOL_SOCKET, SO_LINGER, (struct linger *) &lingerval, sizeof (struct linger)); if (ioctl (inet_fd, FIONBIO, &dummy) < 0) { log ("intercom", "Couldnt set non-blocking on inet_fd."); close (inet_fd); inet_fd = -1; return; } if (bind (inet_fd, (struct sockaddr *) &main_socket, sizeof (main_socket)) < 0) { local_errno = errno; log ("intercom", "Couldnt bind inet_fd to port"); switch (local_errno) { case EBADF: log ("intercom", "Bad file descriptor"); break; case EINVAL: log ("intercom", "Already bound."); break; case EACCES: log ("intercom", "Privillaged port"); break; case EADDRINUSE: log ("intercom", "Address in use."); break; default: log ("intercom", "errno failed."); break; } I_SHUTDOWN (inet_fd, 2); close (inet_fd); inet_fd = -1; close_all_sockets (); return; } if (listen (inet_fd, 5) < 0) { log ("intercom", "Failed on listen. Closing inet_fd."); I_SHUTDOWN (inet_fd, 2); close (inet_fd); inet_fd = -1; close_all_sockets (); return; } sprintf (oldstack, "inet_fd bound and listening to port %d", portnumber - 1); stack = end_string (oldstack); log ("intercom", oldstack); stack = oldstack; total_net.established = system_time; scan = talker_list_anchor; intercom_status |= INTERCOM_BOOTING; while (scan->next) { scan = scan->next; /*Connect the talker */ if (connect_new_talker (scan)) /*Send a HELLO */ send_hello (scan); else scan->flags |= HELLO_AFTER_CONNECT; } intercom_status &= ~INTERCOM_BOOTING; return; } void tell_personal_inc_ref (job_list * this_job, const char *fmt,...) { char *oldstack, *str; va_list varlist; if (!fmt || !*fmt) return; if (current_name[0] == '\0') { log ("intercom", "No name to send personal message to."); return; } oldstack = stack; va_start (varlist, fmt); vsprintf (stack, fmt, varlist); va_end (varlist); stack = end_string (oldstack); str = stack; if (intercom_status & INTERCOM_HIGHLIGHT) *stack++ = HIGHLIGHT_RETURN; if (intercom_status & INTERCOM_PERSONAL_MSG) *stack++ = PERSONAL_MESSAGE_TAG; sprintf (stack, "%c%ld:%s:%s", PERSONAL_MESSAGE_AND_RETURN, this_job->job_id, current_name, oldstack); stack = end_string (str); send_to_talker ("%s", str); stack = oldstack; return; } void tell_personal (const char *fmt,...) { char *oldstack, *str, *ptr; va_list varlist; if (!fmt || !*fmt) return; if (current_name[0] == '\0') { log ("intercom", "No name to send personal message to."); return; } oldstack = stack; va_start (varlist, fmt); vsprintf (stack, fmt, varlist); va_end (varlist); stack = end_string (oldstack); str = stack; if (intercom_status & INTERCOM_HIGHLIGHT) *stack++ = HIGHLIGHT_RETURN; if (intercom_status & INTERCOM_PERSONAL_MSG) *stack++ = PERSONAL_MESSAGE_TAG; if (intercom_status & INTERCOM_FORMAT_MSG) *stack++ = FORMAT_MESSAGE_TAG; sprintf (stack, "%c%s:", PERSONAL_MESSAGE, current_name); stack = strchr (stack, '\0'); ptr = oldstack; while (*ptr) { if (*ptr != '\r') *stack++ = *ptr++; else ptr++; } *stack++ = '\0'; send_to_talker ("%s", str); stack = oldstack; return; } void tell_talker_su (char *str) { if (!str || !*str) return; send_to_talker ("%c%s", SU_MESSAGE, str); return; } int add_new_talker (char *str, int verbose) { talker_list *scan, *new_talker, *prev; char *name, *abbr, *addr, *port, *status, *oldstack, *ptr, *last_seen; char numeric_string[MAX_INET_ADDR]; int all_num, dots; struct hostent *hp; struct in_addr inet_address; struct sockaddr_in sa; char *dynamic; ptr = str; while (*ptr) { if (iscntrl (*ptr)) *ptr = '.'; ptr++; } oldstack = stack; name = str; abbr = 0; dynamic = 0; addr = 0; port = 0; status = 0; last_seen = 0; if (*name) { abbr = strchr (name, ':'); if (abbr) { *abbr++ = '\0'; if (*abbr) { addr = strchr (abbr, ':'); if (addr) { *addr++ = '\0'; if (*addr) { port = strchr (addr, ':'); if (port) { *port++ = '\0'; if (*port) { status = strchr (port, ':'); if (status) { *status++ = '\0'; if (*status) { last_seen = strchr (status, ':'); if (last_seen) { *last_seen++ = '\0'; dynamic = strchr (last_seen, ':'); if (dynamic) *dynamic++ = 0; } } } } } } } } } } if (!status || !*status || !*port || !*addr || !*abbr || !name || !*name) { return 0; } new_talker = (talker_list *) calloc (1, sizeof (talker_list)); if (strlen (name) > MAX_TALKER_NAME - 1) name[MAX_TALKER_NAME - 1] = '\0'; if (strlen (abbr) > MAX_TALKER_ABBR - 1) abbr[MAX_TALKER_ABBR - 1] = '\0'; if (strlen (addr) > (MAX_INET_ADDR - 1)) addr[MAX_INET_ADDR - 1] = '\0'; if (dynamic && *dynamic && strlen (dynamic) > (MAX_INET_ADDR - 1)) dynamic[MAX_INET_ADDR - 1] = '\0'; strcpy (new_talker->name, name); strcpy (new_talker->abbr, abbr); lower_case (new_talker->abbr); strcpy (new_talker->addr, addr); new_talker->port = ATOI (port); if (dynamic && *dynamic) strcpy (new_talker->dynamic, dynamic); if (last_seen && *last_seen) new_talker->last_seen = ATOI (last_seen); else new_talker->last_seen = system_time; if (*status == 'B') new_talker->fd = P_BARRED; else if (*status == 'F') { new_talker->fd = P_BARRED; new_talker->flags |= FIRST_CONTACT; } else if (*status == 'I') { new_talker->fd = P_BARRED; new_talker->flags |= INVIS; } /*TIMEOUTS */ /*If invisible and not seen for 6 months */ if (new_talker->fd == P_BARRED) { if (new_talker->flags & INVIS && system_time > new_talker->last_seen + (14515200)) { free(new_talker); return 0; } } else { /*If visible and not seen for 3 months */ if (system_time > new_talker->last_seen + (7257600)) { free(new_talker); return 0; } } make_unique_but (NULL, new_talker); /*Add it to the list */ scan = talker_list_anchor; while (scan->next && strcmp (scan->next->abbr, new_talker->abbr) < 0) scan = scan->next; new_talker->next = scan->next; scan->next = new_talker; prev = scan; /*Test whether the address we have is all in numeric */ dots = 0; all_num = 1; ptr = new_talker->addr; while (all_num && *ptr) { if (!isdigit (*ptr) && *ptr != '.') all_num = 0; if (*ptr++ == '.') dots++; } if (all_num) { if (dots != 3 || !strcmp (new_talker->addr, "0.0.0.0")) { prev->next = new_talker->next; free(new_talker); if (verbose > 0) tell_personal (" Invalid address, '%s' supplied.", addr); return 0; } strncpy (numeric_string, new_talker->addr, MAX_INET_ADDR - 1); numeric_string[MAX_INET_ADDR - 1] = 0; /*Now see if we can lookup the name address */ hp = 0; inet_address.s_addr = inet_addr (numeric_string); if (inet_address.s_addr != -1) { sa.sin_addr = inet_address; sa.sin_family = AF_INET; /*Do the name lookup */ hp = gethostbyaddr ((char *) &(sa.sin_addr.s_addr), sizeof (sa.sin_addr.s_addr), AF_INET); } /*If we found it, use it */ if (hp) strcpy (new_talker->addr, (const char *) hp->h_name); else strcpy (new_talker->addr, inet_ntoa (sa.sin_addr)); } else { /*Its an alpha address, we need to convert */ hp = gethostbyname (new_talker->addr); if (!hp) { new_talker->fd = -1; if (verbose > 0) tell_personal (" Cannot resolve hostname to connect '%s %d'" ,new_talker->addr, new_talker->port); return 0; } else { memcpy ((char *) &inet_address, hp->h_addr, sizeof (struct in_addr)); strcpy (numeric_string, inet_ntoa (inet_address)); } } if (!strcmp(numeric_string,"127.0.0.1")) { tell_personal(" Cannot use localhost as an address"); prev->next=new_talker->next; free(new_talker); return 0; } /*We have a dot notation address in numeric_string, so lets paste this into the struct */ sscanf (numeric_string, "%d.%d.%d.%d", &(new_talker->num[0]), &(new_talker->num[1]), &(new_talker->num[2]), &(new_talker->num[3])); /*We have an address we can use now. */ if (match_address (new_talker)) /*We have an address clash */ { if (verbose > 0) { tell_personal ("Already have that address in the database."); stack = oldstack; prev->next = new_talker->next; FREE (new_talker); return 0; } } if (verbose > 0) { sprintf (oldstack, "%s just added %s to the intercom server list.", current_name, new_talker->name); stack = end_string (oldstack); tell_talker_su (oldstack); stack = oldstack; } if (verbose != -1 && new_talker->fd != P_BARRED) { if (connect_new_talker (new_talker)) send_hello (new_talker); else new_talker->flags |= HELLO_AFTER_CONNECT; } return 1; } talker_list *match_talker_name (char *str) { talker_list *scan; if (!str || !*str) return 0; scan = talker_list_anchor; while (scan->next) { scan = scan->next; if (!strcasecmp (str, scan->name)) return scan; } return 0; } talker_list *match_talker_abbr_absolute (char *str) { talker_list *scan; if (!str || !*str) return 0; scan = talker_list_anchor; while (scan->next) { scan = scan->next; if (!strcasecmp (str, scan->abbr)) return scan; } return 0; } talker_list *match_talker_abbr_pattern (char *str) { talker_list *match = 0; talker_list *scan; char *oldstack, *name, *match_string, *ptr; int matches = 0; if (!str || !*str) return 0; oldstack = stack; name = stack; strcpy (name, str); ptr = name; while (*ptr) { *ptr = tolower (*ptr); ptr++; } stack = end_string (oldstack); match_string = stack; scan = talker_list_anchor; while (scan->next) { scan = scan->next; if (!(scan->flags & INVIS)) { strcpy (stack, scan->abbr); ptr = stack; while (*ptr) { *ptr = tolower (*ptr); ptr++; } if (!strcmp (stack, name)) { stack = oldstack; return scan; } if (strstr (stack, name) == stack) { matches++; stack = strchr (stack, 0); *stack++ = ','; *stack++ = ' '; match = scan; } } } if (matches == 0) { if (current_name[0]) tell_personal ("Cannot find server aliased to '%s'.", str); stack = oldstack; return 0; } if (matches > 1) { if (stack > match_string + 2) { stack -= 2; *stack++ = 0; } if (current_name[0]) tell_personal ("Multiple matches: %s.", match_string); stack = oldstack; return 0; } stack = oldstack; return match; } static void add_new_server_to_file (char *str) { /*The talker has requested we add a new talker to the database */ FILE *fp; char *name, *abbr, *addr, *oldstack; name = str; abbr = 0; addr = 0; if (*name) { abbr = strchr (name, ':'); if (abbr) { *abbr++ = '\0'; if (*abbr) { addr = strchr (abbr, ':'); if (addr) *addr++ = '\0'; } } } oldstack = stack; if (match_talker_name (name)) { tell_personal (" Intercom error: Talker '%s' already has an " "entry.", name); return; } if (match_talker_abbr_absolute (abbr)) { tell_personal (" Intercom error: Abbreviation '%s' already " "has an entry.", name); return; } addr--; *addr = ':'; abbr--; *abbr = ':'; if (!add_new_talker (name, 1)) return; fp = fopen ("files/intercom.dbase", "a"); if (fp) { fprintf (fp, "%s\n", name); fclose (fp); } else log ("intercom", "Failed to open database list for addition"); return; } static void read_talker_database (void) { FILE *fp; char buffer[255]; char *ptr; talker_list_anchor = (talker_list *) calloc (1, sizeof (talker_list)); validation_anchor = (talker_list *) calloc (1, sizeof (talker_list)); fp = fopen ("files/intercom.dbase", "r"); if (fp) { while (!feof (fp)) { buffer[0] = '\0'; fgets (buffer, 254, fp); ptr = strchr (buffer, '\n'); if (ptr) *ptr = '\0'; if (buffer[0]) { /*Add the entry we are given */ add_new_talker (buffer, -1); } } fclose (fp); } else log ("intercom", "Tried to open list of talkers but failed"); return; } char *set_name (char *str) { char *ptr; if (!str || !*str) return 0; ptr = strchr (str, ':'); if (ptr) { *ptr++ = '\0'; strcpy (current_name, str); return ptr; } return str; } static void show_all_links (int hidden, int up_only) { char *buffer, *oldstack; talker_list *scan; int count, ucount; int show = 0; if (!(talker_list_anchor->next)) { tell_personal (" There are no links in the intercom database."); return; } oldstack = stack; sprintf (stack, "Main server connection status : %s----------------------" "------------------\n", (inet_fd < 1 || intercom_status & BAR_ALL_CONNECTIONS) ? "DOWN " : "UP --"); stack = strchr (stack, 0); scan = talker_list_anchor->next; ucount = 0; if (!hidden && inet_fd > 1) { while (scan && (stack + 200 < stack_start + INTERCOM_STACK)) { if (!(scan->flags & INVIS) && scan->fd > 0 && scan->validation > 0 && !(scan->flags & WAITING_CONNECT)) { ucount++; sprintf (stack, "%-10s : UP : %s running on %s %d\n", scan->abbr, scan->name, scan->dynamic[0] ? scan->dynamic : scan->addr, scan->port); stack = strchr (stack, 0); } scan = scan->next; } if (ucount > 0) { sprintf (stack, "---------------------------- %2d talker%s up %s------" "----------------------------\n", ucount, ucount == 1 ? "" : "s", ucount == 1 ? "-" : ""); stack = strchr (stack, 0); } } scan = talker_list_anchor->next; count = 0; if (up_only) scan = 0; else while (scan && (stack + 200 < stack_start + INTERCOM_STACK)) { show = 1; /*If 'hidden', only show invisible or old */ if (hidden) { if (!(scan->flags & INVIS) && system_time < scan->last_seen + 2419200) show = 0; } else { if (scan->flags & INVIS) show = 0; else if (!( scan->fd < 1 || scan->flags & WAITING_CONNECT || scan->validation < 1)) show = 0; else if (system_time > scan->last_seen + 2419200) show = 0; } if (show) { count++; sprintf (stack, "%-10s :", scan->abbr); stack = strchr (stack, 0); if (scan->fd == BARRED) strcpy (stack, " BAR"); else if (scan->fd == P_BARRED) strcpy (stack, " BAN"); else if (scan->fd == ERROR_FD) strcpy (stack, " ERR"); else if (scan->fd == BARRED_REMOTE) strcpy (stack, "RBLK"); else if (scan->flags & INVIS) strcpy (stack, "INVS"); else if (scan->fd < 1) strcpy (stack, "DOWN"); else if (scan->flags & WAITING_CONNECT) strcpy (stack, "WAIT"); else if (scan->validation < 1) strcpy (stack, "UNCF"); else strcpy (stack, " UP "); stack += 4; sprintf (stack, ": %s running on %s %d\n", scan->name, scan->dynamic[0] ? scan->dynamic : scan->addr, scan->port); stack = strchr (stack, 0); } scan = scan->next; } if (ucount == 0 || count > 0) { if (scan) sprintf (stack, "MORE ROOM NEEDED IN INTERCOM_STACK\n"); else { sprintf (stack, "---------------------------- %2d talker%s connected " "%s%s-----------------", count + ucount, count + ucount == 1 ? "" : "s", hidden ? "invisibly " : "----------", count + ucount == 1 ? "-" : ""); } } stack = end_string (oldstack); buffer = (char *) calloc (1, strlen (oldstack) + 2); strcpy (buffer, oldstack); stack = oldstack; tell_personal ("%s", buffer); FREE (buffer); return; } static void show_all_links_short (void) { char *oldstack; talker_list *scan; int count = 0; oldstack = stack; scan = talker_list_anchor->next; while (scan) { count++; sprintf (stack, "%s:", scan->abbr); if (scan->fd > 0 && !(scan->flags & INVIS) && scan->validation > 0 && !(scan->flags & WAITING_CONNECT)) { while (*stack) { *stack = toupper (*stack); stack++; } } else if (scan->fd < -1) count--; else { while (*stack) { *stack = tolower (*stack); stack++; } } scan = scan->next; } if (count == 0) { tell_personal (" There are no active links in the intercom database."); stack = oldstack; return; } /*There are some links, the stack must reflect this... */ stack--; *stack++ = 0; intercom_status |= INTERCOM_FORMAT_MSG; tell_personal ("%s\n\nThere are %d servers active.", oldstack, count); intercom_status &= ~INTERCOM_FORMAT_MSG; stack = oldstack; return; } static void open_talker_link (char *str) { talker_list *open_site; char *oldstack; if (!str || !*str) return; open_site = match_talker_abbr_absolute (str); if (!open_site) open_site = match_talker_name (str); oldstack = stack; if (!open_site) { tell_personal (" Cannot match talker name or abbreviation '%s'.", str); return; } if (open_site->fd > 0) { I_SHUTDOWN (open_site->fd, 2); do_close (open_site); } if (connect_new_talker (open_site)) send_hello (open_site); else open_site->flags |= HELLO_AFTER_CONNECT; tell_personal ("Attempting to establish link to '%s'.", open_site->name); return; } static void unbar_talker (char *str) { talker_list *open_site; char *oldstack; if (!str || !*str) return; open_site = match_talker_abbr_absolute (str); if (!open_site) open_site = match_talker_name (str); oldstack = stack; if (!open_site) { tell_personal (" Cannot match talker name or abbreviation '%s'.", str); return; } if (open_site->flags & INVIS) { tell_personal (" %s is not visible to unbar.", open_site->name); return; } if (open_site->fd == BARRED || open_site->fd == P_BARRED) { sprintf (oldstack, "%s unbars talker '%s'.", current_name, open_site->name); stack = end_string (oldstack); tell_talker_su (oldstack); stack = oldstack; } open_site->fd = -1; if (connect_new_talker (open_site)) send_hello (open_site); else open_site->flags |= HELLO_AFTER_CONNECT; sync_talkers (); return; } static void close_talker_link (char *str) { talker_list *close_site; char *oldstack; if (!str || !*str) return; close_site = match_talker_name (str); if (!close_site) close_site = match_talker_abbr_absolute (str); oldstack = stack; if (!close_site) { tell_personal (" Cannot match talker name or abbreviation '%s'.", str); return; } if (close_site->fd == BARRED || close_site->fd == P_BARRED) { tell_personal (" Talker '%s' is already barred.", str); return; } if (close_site->fd > 0) { tell_remote_talker (close_site, "%c", BARRING_YOU); I_SHUTDOWN (close_site->fd, 2); do_close (close_site); } close_site->fd = BARRED; sprintf (oldstack, "Talker '%s' barred by %s.", close_site->name, current_name); stack = end_string (oldstack); tell_talker_su (oldstack); stack = oldstack; return; } static void do_out_user_command_locate (char *target_name) { job_list *this_job; talker_list *scan; int count = 0; this_job = make_job_entry (); strcpy (this_job->sender, current_name); this_job->command_type = COMMAND_LOCATE; if (strlen (target_name) > MAX_NAME - 1) target_name[MAX_NAME - 1] = '\0'; strcpy (this_job->target, target_name); scan = talker_list_anchor->next; while (scan) { if (scan->fd > 0 && !(scan->flags & INVIS) && scan->validation > 0 && !(scan->flags & WAITING_CONNECT)) { count++; tell_remote_talker (scan, "%c%c%ld:%s:x:x", USER_COMMAND, COMMAND_LOCATE, this_job->job_id, target_name); } scan = scan->next; } if (count > 0) tell_personal (" Searching for %s on %d site%s.", target_name, count, count == 1 ? "" : "s"); else tell_personal (" No sites connected to search."); return; } static void do_out_user_command_room (char command_style, char *msg) { talker_list *scan; scan = talker_list_anchor->next; while (scan) { if (scan->fd > 0 && !(scan->flags & INVIS) && scan->validation > 0 && !(scan->flags & WAITING_CONNECT)) { tell_remote_talker (scan, "%c%c%s:%s", USER_COMMAND, command_style, current_name, msg); } scan = scan->next; } return; } static void do_out_user_command (char *str) { char *target_talker, *target_name, *msg = 0, *oldstack; talker_list *destination_talker; job_list *this_job; char *command_style; if (!str || !*str || !*(str + 1)) { log ("intercom", "User command with no body"); return; } command_style = str; str++; target_talker = set_name (str); target_name = strchr (target_talker, ':'); if (target_name) { *target_name++ = '\0'; if (*target_name) { msg = strchr (target_name, ':'); if (msg) *msg++ = '\0'; } } while (*msg && isspace (*msg)) msg++; if (!msg || !*msg || !*target_name || !*target_talker) { tell_personal (" Badly formed string for intercom message."); return; } if (*command_style == COMMAND_LOCATE) { do_out_user_command_locate (target_name); return; } if (*command_style == COMMAND_SAY || *command_style == COMMAND_EMOTE || *command_style == ICHAN_SAY || *command_style == ICHAN_EMOTE || *command_style == ICHAN_ACTION) { do_out_user_command_room (*command_style, msg); return; } oldstack = stack; destination_talker = match_talker_abbr_pattern (target_talker); if (!destination_talker) return; if (destination_talker->fd < 1 || destination_talker->flags & WAITING_CONNECT) { tell_personal (" '%s' currently has no link.", destination_talker->name); return; } if (*command_style == COMMAND_UPDATE) { tell_remote_talker (destination_talker, "%c", REQUEST_SERVER_LIST); return; } this_job = make_job_entry (); strcpy (this_job->sender, current_name); if (strlen (msg) > 255) msg[255] = '\0'; strcpy (this_job->msg, msg); this_job->command_type = *command_style; if (strlen (target_name) > MAX_NAME - 1) target_name[MAX_NAME - 1] = '\0'; strcpy (this_job->target, target_name); strcpy (this_job->destination, destination_talker->abbr); tell_remote_talker (destination_talker, "%c%c%ld:%s:%s:%s", USER_COMMAND, *command_style, this_job->job_id, target_name, current_name, msg); return; } static void return_command_with_string (talker_list * remote_server, job_list * this_job, char *name, char *str, char c_type) { if (!str || !*str) { log ("intercom", "Returned empty who list from server"); return; } tell_remote_talker (remote_server, "%c%c%ld:%s:%s", REPLY_IS, c_type, this_job->job_ref, name, str); return; } static void parse_server_reply (char *str) { job_list *this_job; talker_list *remote_server; char *name, *ptr; char blank_field[2]; blank_field[0] = 'x'; blank_field[1] = '\0'; if (!str || !*str) { log ("intercom", "Talker sent empty reply to request"); return; } ptr = 0; name = strchr (str + 1, ':'); if (name) { *name++ = '\0'; if (*name) { ptr = strchr (name, ':'); if (ptr) *ptr++ = '\0'; } } if (!name || !*name) { log ("intercom", " Invalid line to parse in parse_server_reply"); return; } if (!ptr || !*ptr) ptr = blank_field; this_job = return_job (str + 1); if (!this_job) return; strcpy (this_job->target, name); /*We now know which job was related to this */ /*Send a message to the remote server */ if (!this_job->originator[0]) { log ("intercom", "No return talker address in reply request."); return; } remote_server = match_talker_abbr_absolute (this_job->originator); if (!remote_server) return; switch (*str) { case COMMAND_WHO: case COMMAND_EXAMINE: case COMMAND_FINGER: case COMMAND_LSU: case COMMAND_IDLE: return_command_with_string (remote_server, this_job, name, ptr, *str); break; default: tell_remote_talker (remote_server, "%c%c%ld:%s", REPLY_IS, *str, this_job->job_ref, this_job->target); break; } free_job (this_job); return; } static void close_all_links (void) { char *oldstack; talker_list *current; oldstack = stack; if (!(intercom_status & BAR_ALL_CONNECTIONS)) { tell_personal (" You close %s to external messages.", get_config_msg ("talker_name")); sprintf (oldstack, "%s closes the intercom to external messages.", current_name); stack = end_string (oldstack); tell_talker_su (oldstack); stack = oldstack; } intercom_status |= BAR_ALL_CONNECTIONS; current = talker_list_anchor; while (current->next) { current = current->next; if (current->fd > 0) { I_SHUTDOWN (current->fd, 2); do_close (current); } } return; } static void open_all_links (void) { char *oldstack; talker_list *current; oldstack = stack; if (intercom_status & BAR_ALL_CONNECTIONS) { tell_personal (" You open %s to external messages.", get_config_msg ("talker_name")); sprintf (oldstack, "%s opens the intercom to external messages.", current_name); stack = end_string (oldstack); tell_talker_su (oldstack); stack = oldstack; } intercom_status &= ~BAR_ALL_CONNECTIONS; intercom_status |= INTERCOM_BOOTING; current = talker_list_anchor; while (current->next) { current = current->next; if (current->fd != BARRED && current->fd != P_BARRED && current->fd < 1) { if (connect_new_talker (current)) /*Send a HELLO */ send_hello (current); else current->flags |= HELLO_AFTER_CONNECT; } } intercom_status &= ~INTERCOM_BOOTING; return; } void sync_talkers () { FILE *fp; talker_list *scan, *prev; char address[MAX_INET_ADDR]; fp = fopen ("files/intercom.dbak", "w"); if (!fp) { log ("intercom", "ERROR: Couldnt open intercom database file."); return; } scan = talker_list_anchor; while (scan->next) { prev = scan; scan = scan->next; if (scan->fd != NO_SYNC_TALKER) { if (scan->num[0] == 0 && scan->num[1] == 0 && scan->num[2] == 0 && scan->num[3] == 0) strcpy (address, scan->addr); else sprintf (address, "%d.%d.%d.%d", scan->num[0], scan->num[1], scan->num[2], scan->num[3]); if (scan->flags & INVIS) fprintf (fp, "%s:%s:%s:%d:I:%ld:%s\n", scan->name, scan->abbr, address, scan->port, (long int) scan->last_seen, scan->dynamic); else if (scan->fd != P_BARRED) fprintf (fp, "%s:%s:%s:%d:O:%ld:%s\n", scan->name, scan->abbr, address, scan->port, (long int) scan->last_seen, scan->dynamic); else if (scan->flags & FIRST_CONTACT) fprintf (fp, "%s:%s:%s:%d:F:%ld:%s\n", scan->name, scan->abbr, address, scan->port, (long int) scan->last_seen, scan->dynamic); else fprintf (fp, "%s:%s:%s:%d:B:%ld:%s\n", scan->name, scan->abbr, address, scan->port, (long int) scan->last_seen, scan->dynamic); } else { prev->next = scan->next; FREE (scan); scan = prev; } } fclose (fp); rename ("files/intercom.dbak", "files/intercom.dbase"); return; } static void delete_link (char *str) { char *oldstack; talker_list *target; if (!str || !*str) { log ("intercom", "No talker name specified to delete"); return; } target = match_talker_abbr_absolute (str); if (!target) { target = match_talker_name (str); if (!target) { tell_personal (" Cannot find talker '%s' to delete.", str); return; } } if (target->fd > 0) { I_SHUTDOWN (target->fd, 2); do_close (target); } target->fd = NO_SYNC_TALKER; tell_personal (" Talker deleted."); oldstack = stack; sprintf (oldstack, "%s removed '%s' from the intercom server list.", current_name, target->name); stack = end_string (oldstack); tell_talker_su (oldstack); stack = oldstack; sync_talkers (); return; } static void change_name (char *str) { char *new_var = 0; talker_list *scan, *target; if (str && *str) { new_var = strchr (str, ':'); if (new_var) *new_var++ = '\0'; } if (!new_var || !*new_var || !str || !*str) { log ("intercom", "Bad message passed to change_name"); return; } if (strlen (new_var) > MAX_TALKER_NAME - 1) new_var[MAX_TALKER_NAME - 1] = '\0'; scan = match_talker_name (str); if (!scan) { tell_personal (" Cannot find a talker with the name '%s'.", str); return; } target = match_talker_name (new_var); if (target && target != scan) { tell_personal (" There is already a talker with the name '%s'.", new_var); return; } target = match_talker_abbr_absolute (new_var); if (target && target != scan) { tell_personal (" There is already a talker with the alias '%s'.", new_var); return; } tell_personal (" You change the name of '%s' to '%s'.", scan->name, new_var); strcpy (scan->name, new_var); if (scan->flags & FIRST_CONTACT) scan->flags &= ~FIRST_CONTACT; sync_talkers (); return; } static void change_abbr (char *str) { char *new_var = 0; talker_list *scan, *target; if (str && *str) { new_var = strchr (str, ':'); if (new_var) *new_var++ = '\0'; } if (!new_var || !*new_var || !str || !*str) { log ("intercom", "Bad message passed to change_abbr"); return; } if (strlen (new_var) > MAX_TALKER_ABBR - 1) new_var[MAX_TALKER_ABBR - 1] = '\0'; scan = match_talker_abbr_absolute (str); if (!scan) { tell_personal (" Cannot find a talker with the alias '%s'.", str); return; } target = match_talker_name (new_var); if (target && target != scan) { tell_personal (" There is already a talker with the name '%s'.", new_var); return; } target = match_talker_abbr_absolute (new_var); if (target && target != scan) { tell_personal (" There is already a talker with the alias '%s'.", new_var); return; } tell_personal (" You change the alias of '%s' to '%s'.", scan->abbr, new_var); strcpy (scan->abbr, new_var); lower_case (scan->abbr); if (scan->flags & FIRST_CONTACT) scan->flags &= ~FIRST_CONTACT; sync_talkers (); return; } static void change_addr (char *str) { char *new_var = 0, *ptr; talker_list *target, *scan; char new_ip[MAX_INET_ADDR]; char existing_ip[MAX_INET_ADDR]; struct hostent *hp; struct in_addr inet_address; int dots, all_num; if (str && *str) { new_var = strchr (str, ':'); if (new_var) *new_var++ = '\0'; } if (!new_var || !*new_var || !str || !*str) { log ("intercom", "Bad message passed to change_address"); return; } target = match_talker_name (str); if (!target) target = match_talker_abbr_absolute (str); if (!target) { tell_personal (" Cannot find talker '%s'.", str); return; } /*Test whether the address we have is all in numeric */ dots = 0; all_num = 1; ptr = new_var; while (all_num && *ptr) { if (!isdigit (*ptr) && *ptr != '.') all_num = 0; if (*ptr++ == '.') dots++; } if (all_num) { if (dots != 3) { tell_personal (" Invalid address, '%s' supplied.", new_var); return; } strncpy (new_ip, new_var, MAX_INET_ADDR - 1); new_ip[MAX_INET_ADDR - 1] = 0; } else { /*Its an alpha address, we need to convert */ hp = gethostbyname (new_var); if (!hp) { tell_personal (" Cannot resolve hostname '%s'.", new_var); return; } else { memcpy ((char *) &inet_address, hp->h_addr, sizeof (struct in_addr)); strcpy (new_ip, inet_ntoa (inet_address)); } } /*We now have an IP address in dot notation */ scan = talker_list_anchor; while (scan->next) { scan = scan->next; if (scan != target && scan->port == target->port) { sprintf (existing_ip, "%d.%d.%d.%d", scan->num[0], scan->num[1], scan->num[2], scan->num[3]); if (!strcmp (existing_ip, new_ip)) { tell_personal (" But %s already has that address.", scan->name); return; } } } /*Its a unique address, so let them have it */ tell_personal (" You change the address of '%s' from %s to %s.", target->name, target->addr, new_var); strcpy (target->addr, new_var); if (target->fd > 0) { I_SHUTDOWN (target->fd, 2); do_close (target); } sscanf (new_ip, "%d.%d.%d.%d", &(target->num[0]), &(target->num[1]), &(target->num[2]), &(target->num[3])); if (connect_new_talker (target)) send_hello (target); else target->flags |= HELLO_AFTER_CONNECT; sync_talkers (); return; } static void change_port (char *str) { char *new_var = 0; talker_list *target, *scan; int port; if (str && *str) { new_var = strchr (str, ':'); if (new_var) *new_var++ = '\0'; } if (!new_var || !*new_var || !str || !*str) { log ("intercom", "Bad message passed to change_port"); return; } port = ATOI (new_var); if (port < 1) { tell_personal (" Bad portnumber (%d) given.", port); return; } target = match_talker_name (str); if (!target) target = match_talker_abbr_absolute (str); if (!target) { tell_personal (" Cannot find talker '%s'.", str); return; } scan = talker_list_anchor; while (scan->next) { scan = scan->next; if (scan != target) if (scan->num[0] == target->num[0] && scan->num[1] == target->num[1] && scan->num[2] == target->num[2] && scan->num[3] == target->num[3] && scan->port == port) { tell_personal (" But %s already has that address.", scan->name); return; } } /*Its a unique address, so let them have it */ tell_personal (" You change the port of '%s' from %d to %d.", target->name, target->port, port); target->port = port; sync_talkers (); if (target->fd > 0) { I_SHUTDOWN (target->fd, 2); do_close (target); } if (connect_new_talker (target)) send_hello (target); else target->flags |= HELLO_AFTER_CONNECT; return; } static void banish_site (char *str) { talker_list *target; char *oldstack; if (!str || !*str) { log ("intercom", "Sent bad string to banish_site()"); return; } target = match_talker_name (str); if (!target) target = match_talker_abbr_absolute (str); if (!target) { tell_personal (" Cannot locate talker '%s'.", str); return; } if (target->fd == P_BARRED) { tell_personal (" '%s' is already banished.", target->name); return; } if (target->fd > 0) { tell_remote_talker (target, "%c", BARRING_YOU); I_SHUTDOWN (target->fd, 2); do_close (target); } target->fd = P_BARRED; sync_talkers (); oldstack = stack; sprintf (oldstack, "%s banishes talker '%s'.", current_name, target->name); stack = end_string (oldstack); tell_talker_su (oldstack); stack = oldstack; return; } static void unhide_entry (char *str) { talker_list *target; if (!str || !*str) { log ("intercom", "Sent bad string to unhide_entry()"); return; } target = match_talker_name (str); if (!target) target = match_talker_abbr_absolute (str); if (!target) { tell_personal (" Cannot locate talker '%s'.", str); return; } if (!(target->flags & INVIS)) { tell_personal (" %s doesnt appear to be invisible.", target->name); return; } target->flags &= ~INVIS; tell_personal (" %s is now visible (but still banished).", target->name); return; } static void hide_entry (char *str) { talker_list *target; char *oldstack; if (!str || !*str) { log ("intercom", "Sent bad string to hide_entry()"); return; } target = match_talker_name (str); if (!target) target = match_talker_abbr_absolute (str); if (!target) { tell_personal (" Cannot locate talker '%s'.", str); return; } if (target->flags & INVIS) { tell_personal (" %s is already invisible.", target->name); return; } if (target->fd > 0) { tell_remote_talker (target, "%c", BARRING_YOU); I_SHUTDOWN (target->fd, 2); do_close (target); } target->fd = P_BARRED; target->flags |= INVIS; sync_talkers (); oldstack = stack; sprintf (oldstack, "%s banishes and removes talker '%s' from the visible " "database.", current_name, target->name); stack = end_string (oldstack); tell_talker_su (oldstack); stack = oldstack; return; } static void request_server_list (void) { talker_list *scan; scan = talker_list_anchor; while (scan->next) { scan = scan->next; if (scan->fd > 0) tell_remote_talker (scan, "%c", REQUEST_SERVER_LIST); } return; } static void expire_jobs (void) { job_list *scan; scan = job_anchor; while (scan->next != job_anchor && scan->next->timeout < system_time) { scan = scan->next; if (scan->timeout < system_time) { if (*(scan->sender)) { sprintf (current_name, scan->sender); switch (scan->command_type) { case COMMAND_TELL: tell_personal (" Your tell command to %s@%s expired.", scan->target, scan->destination); break; case COMMAND_REMOTE: tell_personal (" Your remote command to %s@%s expired.", scan->target, scan->destination); break; case COMMAND_WHO: tell_personal (" Your who command at %s expired.", scan->destination); break; case COMMAND_EXAMINE: tell_personal (" Your examine command on %s@%s expired.", scan->target, scan->destination); break; case COMMAND_FINGER: tell_personal (" Your finger command on %s@%s expired.", scan->target, scan->destination); break; case COMMAND_IDLE: tell_personal (" Your idle command on %s@%s expired.", scan->target, scan->destination); break; } } scan = scan->prev; free_job (scan->next); } } return; } static void request_stats (char *str) { talker_list *target = 0; net_usage *stats; char *oldstack; oldstack = stack; if (!str || !*str) { stats = &total_net; sprintf (oldstack, "Total intercom network statistics:\n"); } else if (!strcasecmp (str, get_config_msg ("talker_name")) || !strcasecmp (str, get_config_msg ("intercom_abbr"))) { stats = &server_net; sprintf (oldstack, "Intercom from %s link statistics:\n", get_config_msg ("talker_name")); } else { target = match_talker_abbr_absolute (str); if (!target) target = match_talker_name (str); if (!target) { tell_personal (" Cannot find talker '%s' in the database.", str); return; } stats = &(target->net_stats); sprintf (oldstack, "Intercom statistics for talker '%s'.\n", target->name); } stack = strchr (oldstack, 0); if (target) { sprintf (stack, "\nLink status is "); stack = strchr (stack, 0); if (target->fd == BARRED) strcpy (stack, "Barred link"); else if (target->fd == P_BARRED) strcpy (stack, "Banished link"); else if (target->fd == BARRED_REMOTE) strcpy (stack, "Refused Link"); else if (target->fd == ERROR_FD) strcpy (stack, "Unknown error"); else if (target->fd < 1) strcpy (stack, "Down"); else if (target->flags & WAITING_CONNECT) strcpy (stack, "Waiting for response to connection request"); else if (target->validation < 0) strcpy (stack, "Connected but not securely validated"); else strcpy (stack, "Up and running."); stack = strchr (stack, 0); } sprintf (stack, "\n\n" " IN OUT\n" "Bytes/Sec %7.3f %7.3f\n" "Total Bytes %6d %6d\n" "Packets/Sec %7.3f %7.3f\n" "Total Packets %6d %6d\n" "\nLink has been alive for a total of %d:%02d\n" "This link was established: %s", stats->chars_in == 0 ? 0 : (float) (stats->chars_in) / (float) (stats->up_clicks), stats->chars_out == 0 ? 0 : (float) (stats->chars_out) / (float) (stats->up_clicks), stats->chars_in, stats->chars_out, stats->packets_in == 0 ? 0 : (float) (stats->packets_in) / (float) (stats->up_clicks), stats->packets_out == 0 ? 0 : (float) (stats->packets_out) / (float) (stats->up_clicks), stats->packets_in, stats->packets_out, stats->up_clicks == 0 ? 0 : (stats->up_clicks) / 60, stats->up_clicks == 0 ? 0 : (stats->up_clicks) % 60, sys_time (stats->established)); stack = end_string (oldstack); tell_personal ("%s", oldstack); stack = oldstack; return; } static void request_room_look_global (void) { talker_list *scan; job_list *this_job; this_job = make_job_entry (); strcpy (this_job->sender, current_name); scan = talker_list_anchor; while (scan->next) { scan = scan->next; if (scan->fd > 0) tell_remote_talker (scan, "%c%ld", INTERCOM_ROOM_LOOK, this_job->job_id); } return; } static void parse_show_links_request (char *str) { set_name (str + 1); switch (*str) { case LIST_ALL: show_all_links (0, 0); break; case LIST_HIDDEN: show_all_links (1, 0); break; case LIST_UP: show_all_links (0, 1); break; } return; } static void reply_room_look_global (char *str) { job_list *this_job; talker_list *remote_talker = 0; char *job_str, *msg = 0; job_str = str; if (job_str && *job_str) { msg = strchr (job_str, ':'); if (msg) *msg++ = 0; } if (!job_str || !*job_str || !msg || !*msg) { log ("intercom", "Invalid message sent to reply_room_look_global"); return; } this_job = return_job (job_str); if (!this_job) return; if (this_job->originator[0]) remote_talker = match_talker_abbr_absolute (this_job->originator); if (!remote_talker) return; tell_remote_talker (remote_talker, "%c%ld:%s", INTERCOM_ROOM_LIST, this_job->job_ref, msg); return; } static void inform_connected_move (char *str) { talker_list *scan; scan = talker_list_anchor; while (scan->next) { scan = scan->next; if (scan->fd > 0) tell_remote_talker (scan, "%c%s", WE_ARE_MOVING, str); } return; } static void send_room_move_notify (char *str) { talker_list *scan; scan = talker_list_anchor; while (scan->next) { scan = scan->next; if (scan->fd > 0) tell_remote_talker (scan, "%c%s", USER_ACTION, str); } return; } static void parse_user_action (char *str) { switch (*str) { case ENTER_ROOM: case LEAVE_ROOM: send_room_move_notify (str); break; } return; } static void use_dynamic (void) { talker_list *scan; scan = talker_list_anchor; while (scan->next) { scan = scan->next; if (scan->fd > 0) { I_SHUTDOWN (scan->fd, 2); do_close (scan); } } getDynamicHost (); ping_all_down_talkers (); } static void parse_talker_input (void) { char c; int chars_left; char *oldstack, *ptr; packet *this_packet; int parse_ok = 0; if (ioctl (talker_fd, FIONREAD, &chars_left) == -1) { I_SHUTDOWN (talker_fd, 2); close (talker_fd); talker_fd = -1; closedown = 1; log ("intercom", "PANIC on FIONREAD on talker_fd."); return; } if (!chars_left) { I_SHUTDOWN (talker_fd, 2); close (talker_fd); talker_fd = -1; closedown = 1; log ("intercom", "Link died on talker_fd"); return; } this_packet = add_packet_to_list (); oldstack = stack; ptr = oldstack; ping_time = system_time + 600; c = (char) (END_MESSAGE - 1); while (chars_left && c != (char) END_MESSAGE) { chars_left--; server_net.chars_in++; if (read (talker_fd, &c, 1) != 1) { log ("intercom", "Read error on talker_fd socket"); I_SHUTDOWN (talker_fd, 2); close (talker_fd); talker_fd = -1; closedown = 1; return; } if (c != (char) END_MESSAGE) { if (c != (char) INCOMPLETE_MESSAGE) { this_packet->data[this_packet->length] = c; this_packet->length++; if (this_packet->length >= MAX_PACKET) this_packet = add_packet_to_list (); } } else parse_ok = 1; } if (!parse_ok) return; this_packet = talker_packet_anchor; strcpy (oldstack, this_packet->data); stack = strchr (oldstack, 0); while (this_packet->next) { this_packet = this_packet->next; strcpy (stack, this_packet->data); stack = strchr (stack, 0); } stack++; ptr = oldstack; if (*ptr) { switch (*ptr) { case BANISH_SITE: ptr = set_name (ptr + 1); banish_site (ptr); break; case OPEN_ALL_LINKS: ptr = set_name (ptr + 1); open_all_links (); break; case CLOSE_ALL_LINKS: ptr = set_name (ptr + 1); close_all_links (); break; case PORTNUMBER_FOLLOWS: create_inet_socket (ptr + 1); break; case UNBAR_LINK: ptr = set_name (ptr + 1); unbar_talker (ptr); break; case CLOSE_LINK: ptr = set_name (ptr + 1); close_talker_link (ptr); break; case ADD_NEW_LINK: ptr = set_name (ptr + 1); add_new_server_to_file (ptr); break; case USER_COMMAND: do_out_user_command (ptr + 1); break; case SHOW_LINKS: parse_show_links_request (ptr + 1); break; case DELETE_LINK: ptr = set_name (ptr + 1); delete_link (ptr); break; case REPLY_IS: parse_server_reply (ptr + 1); break; case OPEN_LINK: ptr = set_name (ptr + 1); open_talker_link (ptr); break; case CHANGE_NAME: ptr = set_name (ptr + 1); change_name (ptr); break; case CHANGE_ABBR: ptr = set_name (ptr + 1); change_abbr (ptr); break; case CHANGE_ADDRESS: ptr = set_name (ptr + 1); change_addr (ptr); break; case CHANGE_PORT: ptr = set_name (ptr + 1); change_port (ptr); break; case REQUEST_SERVER_LIST: request_server_list (); break; case INTERCOM_DIE: closedown = 1; break; case REQUEST_STATS: ptr = set_name (ptr + 1); request_stats (ptr); break; case SHOW_ALL_LINKS_SHORT: ptr = set_name (ptr + 1); show_all_links_short (); break; case HIDE_ENTRY: ptr = set_name (ptr + 1); hide_entry (ptr); break; case UNHIDE_ENTRY: ptr = set_name (ptr + 1); unhide_entry (ptr); break; case INTERCOM_ROOM_LOOK: ptr = set_name (ptr + 1); request_room_look_global (); break; case INTERCOM_ROOM_LIST: reply_room_look_global (ptr + 1); break; case WE_ARE_MOVING: inform_connected_move (ptr + 1); break; case INTERCOM_ICHAN_WHO: ptr = set_name(ptr + 1); request_ichan_list_global(); break; case INTERCOM_ICHAN_LIST: reply_ichan_list_global(ptr + 1); break; case USER_ACTION: parse_user_action (ptr + 1); break; case USE_DYNAMIC: use_dynamic (); break; } } stack = oldstack; current_name[0] = '\0'; free_list_packets (); if (chars_left > 0) parse_talker_input (); return; } static void ping_all_down_talkers (void) { talker_list *scan; scan = talker_list_anchor; while (scan->next) { scan = scan->next; if (scan->fd < 1) { /*Connect the talker */ if (connect_new_talker (scan)) /*Send a HELLO */ send_hello (scan); else scan->flags |= HELLO_AFTER_CONNECT; } } return; } void get_config (void) { if (config_msg.where) FREE (config_msg.where); config_msg = load_file ("soft/config.msg"); log ("intercom", "Intercom booting"); if (strchr (get_config_msg ("talker_name"), ':')) { log ("intercom", "In soft/config.msg, talker_name may NOT have a : in it. Not booted"); exit (-1); } if (strchr (get_config_msg ("intercom_abbr"), ':')) { log ("intercom", "In soft/config.msg, intercom_abbr may NOT have a : in it. Not booted"); exit (-1); } /* ones for colours --silver */ if (strchr (get_config_msg ("talker_name"), '^')) { log ("intercom", "Colour in your talker_name causes problems on other talkers. Intercom not booted"); exit (-1); } if (strchr (get_config_msg ("intercom_abbr"), ':')) { log ("intercom", "Colour in your intercom_abbr causes problems on other talkers. Intercom not booted"); exit (-1); } /* ones for default values --silver */ if (!strcasecmp(get_config_msg ("talker_name"), "playground plus")) { log ("intercom", "Your talker name needs to be changed from 'Playground Plus'. Intercom not booted."); exit (-1); } if (!strcasecmp (get_config_msg ("intercom_abbr"), "pgp")) { log ("intercom", "Your intercom alias needs to be changed from 'pgp'. Intercom not booted"); exit (-1); } max_log_size = atoi (get_config_msg ("max_log_size")); } static void getDynamicHost (void) { char hostname[255], *ptr; FILE *fp; fp = fopen ("files/intercom.dynamic", "r"); if (fp) { hostname[0] = 0; fgets (hostname, 254, fp); if (hostname[0]) { ptr = strchr (hostname, 0) - 1; while (ptr >= hostname && !isalnum (*ptr)) *ptr-- = 0; } dynamic_dns=(char *)MALLOC(strlen(hostname)+1); strcpy(dynamic_dns,hostname); fclose (fp); } return; } extern int main (int argc, char **argv) { int dummy; fd_set fd_list, connect_list; int failed, connects_this_loop; struct timeval timeout; struct sigaction siga; talker_list *current_talker, *prev; time_t last_click, now = 0; intercom_status = INTERCOM_BOOTING; closedown = 0; srand (time (NULL)); stack_start = (char *) calloc (1, INTERCOM_STACK); stack = stack_start; get_config (); /**********SIGNALS***********/ siga.sa_handler = sigpipe; siga.sa_flags = 0; sigaction (SIGPIPE, &siga, 0); siga.sa_handler = sighup; sigaction (SIGHUP, &siga, 0); siga.sa_handler = sigquit; sigaction (SIGQUIT, &siga, 0); siga.sa_handler = sigill; sigaction (SIGILL, &siga, 0); siga.sa_handler = sigfpe; sigaction (SIGFPE, &siga, 0); siga.sa_handler = sigbus; sigaction (SIGBUS, &siga, 0); siga.sa_handler = sigsegv; sigaction (SIGSEGV, &siga, 0); siga.sa_handler = sigterm; sigaction (SIGTERM, &siga, 0); siga.sa_handler = sigxfsz; sigaction (SIGXFSZ, &siga, 0); siga.sa_handler = sigchld; sigaction (SIGCHLD, &siga, 0); siga.sa_handler = sigusr1; sigaction (SIGUSR1, &siga, 0); /****END SIGNALS*********/ system_time = time (NULL); ping_time = system_time + 600; if (chdir (ROOT)) { log ("intercom", " Cannot change to root directory."); exit (1); } /*get the dynamic hostname */ getDynamicHost (); /*Bind main unix socket */ failed = 0; unix_fd = -1; talker_fd = -1; inet_fd = -1; while (unix_fd < 0 && failed < 5) { unix_fd = create_main_intercom_socket (INTERCOM_SOCKET); if (unix_fd < 0) failed++; } if (failed == 5) { log ("intercom", "Repeated fails to create unix socket, aborting."); exit (1); } setup_jobs_list (); setup_free_packets (); memset (&server_net, 0, sizeof (net_usage)); memset (&total_net, 0, sizeof (net_usage)); while (!closedown) { if (getppid () == 1) break; last_click = now; now = time (NULL); if (now != last_click) system_time++; connects_this_loop = 0; intercom_status &= ~INTERCOM_HIGHLIGHT; intercom_status &= ~INTERCOM_PERSONAL_MSG; current_name[0] = '\0'; if (stack != stack_start) { stack = stack_start; log ("intercom", "Bad stack in main loop. Recovered."); } if ((now != last_click) && system_time % 10 == 0) { if (inet_fd < 1 && talker_fd > 0) request_port_from_talker (); if (ping_time - system_time < 300) send_to_talker ("%c", PING); if (system_time % 300 == 0) /*Every 5 minutes, ping all down talkers */ ping_all_down_talkers (); } if (ping_time < system_time) closedown = 1; /*Clear out the old jobs stuff */ expire_jobs (); FD_ZERO (&fd_list); FD_ZERO (&connect_list); FD_SET (unix_fd, &fd_list); if (talker_fd > -1) { FD_SET (talker_fd, &fd_list); if (now != last_click) server_net.up_clicks += (now - last_click); } if (inet_fd > -1) { FD_SET (inet_fd, &fd_list); if (now != last_click) total_net.up_clicks += (now - last_click); current_talker = talker_list_anchor; while (current_talker->next) { current_talker = current_talker->next; if (current_talker->fd > 0) { /*If waiting connect() set to both lists */ if (current_talker->flags & WAITING_CONNECT) FD_SET (current_talker->fd, &connect_list); FD_SET (current_talker->fd, &fd_list); if (now != last_click) current_talker->net_stats.up_clicks += (now - last_click); } else if (current_talker->fd == NO_CONNECT_TRIED && connects_this_loop == 0) { connects_this_loop = 1; if (connect_new_talker (current_talker)) send_hello (current_talker); else current_talker->flags |= HELLO_AFTER_CONNECT; } } current_talker = validation_anchor; while (current_talker->next) { current_talker = current_talker->next; if (current_talker->fd > 0) FD_SET (current_talker->fd, &fd_list); } } timeout.tv_sec = 0; timeout.tv_usec = (1000000 / TIMER_CLICK); if (select (FD_SETSIZE, &fd_list, &connect_list, 0, &timeout) > 0) { if (FD_ISSET (unix_fd, &fd_list)) { dummy = get_unix_fd_input (); if (dummy > -1) { talker_fd = dummy; read_talker_database (); request_port_from_talker (); } } if (talker_fd > -1) if (FD_ISSET (talker_fd, &fd_list)) parse_talker_input (); if (inet_fd > -1) if (FD_ISSET (inet_fd, &fd_list)) get_new_talker_connection (); current_talker = talker_list_anchor; while (current_talker->next) { current_talker = current_talker->next; if (current_talker->fd > 0) { if (current_talker->flags & WAITING_CONNECT && FD_ISSET (current_talker->fd, &connect_list)) { /*A connect just succeded */ current_talker->flags &= ~WAITING_CONNECT; /*If we need a hello sent, send it */ if (current_talker->flags & HELLO_AFTER_CONNECT) { current_talker->flags &= ~HELLO_AFTER_CONNECT; send_hello (current_talker); } if (current_talker->flags & VALIDATE_AFTER_CONNECT) { current_talker->flags &= ~VALIDATE_AFTER_CONNECT; actual_validate_send (current_talker); } } if (current_talker->fd > 0 && FD_ISSET (current_talker->fd, &fd_list)) parse_remote_talker_input (current_talker, 1); } } current_talker = validation_anchor; while (current_talker->next) { current_talker = current_talker->next; if (current_talker->fd > 0) { if (FD_ISSET (current_talker->fd, &fd_list)) parse_remote_talker_input (current_talker, 0); } } current_talker = validation_anchor; while (current_talker->next) { prev = current_talker; current_talker = current_talker->next; if (current_talker->fd < 1 || current_talker->timeout < system_time) { prev->next = current_talker->next; FREE (current_talker); current_talker = prev; } } } } close_all_sockets (); close_intercom_main_socket (); sync_talkers (); log ("intercom", "Shutdown complete."); return 0; } file load_file (char *filename) { file f; int d; char *oldstack; oldstack = stack; d = open (filename, O_RDONLY); if (d < 0) { sprintf (oldstack, "Intercom can't find file:%s", filename); stack = end_string (oldstack); log ("error", oldstack); f.where = (char *) MALLOC (1); *(char *) f.where = 0; f.length = 0; stack = oldstack; return f; } f.length = lseek (d, 0, SEEK_END); lseek (d, 0, SEEK_SET); f.where = (char *) MALLOC (f.length + 1); memset (f.where, 0, f.length + 1); if (read (d, f.where, f.length) < 0) { sprintf (oldstack, "Intercom error reading file:%s", filename); stack = end_string (oldstack); log ("error", oldstack); f.where = (char *) MALLOC (1); *(char *) f.where = 0; f.length = 0; stack = oldstack; return f; } close (d); stack = oldstack; *(f.where + f.length) = 0; return f; } char *get_config_msg (char *type) { char *got; static char smbuf[1024]; memset (smbuf, 0, 1024); if (!config_msg.where || !*config_msg.where) { log ("error", "Intercom : Softmsg file for config_msg aint loaded!"); exit (-1); } got = lineval (config_msg.where, type); if (!got || !*got) { log ("error", "Intercom : Softmsg in config_msg isnt there!"); exit (-1); } strncpy (smbuf, got, 1023); return smbuf; } /* thanks to grim, this fixes problems with 1.0.9 versions and intercoms with the name and id "error" */ static void make_unique_but (talker_list * safe, talker_list * remote_talker) { char dummy_name[MAX_TALKER_NAME]; char dummy_abbr[MAX_TALKER_ABBR]; int loopa; int length; talker_list *match; loopa = 0; strcpy (dummy_name, remote_talker->name); match = match_talker_name (dummy_name); while (match && match != safe) { loopa++; strcpy (dummy_name, remote_talker->name); length = strlen (dummy_name); dummy_name[MAX_TALKER_NAME - 2] = 0; if (loopa > 9) dummy_name[MAX_TALKER_NAME - 3] = 0; if (loopa > 99) dummy_name[MAX_TALKER_NAME - 4] = 0; if (loopa > 999) dummy_name[MAX_TALKER_NAME - 5] = 0; sprintf (dummy_name, "%s%d", dummy_name, loopa); match = match_talker_name (dummy_name); } loopa = 0; strcpy (dummy_abbr, remote_talker->abbr); match = match_talker_abbr_absolute (dummy_abbr); while (match && match != safe) { loopa++; strcpy (dummy_abbr, remote_talker->abbr); length = strlen (dummy_abbr); dummy_abbr[MAX_TALKER_ABBR - 2] = 0; if (loopa > 9) dummy_abbr[MAX_TALKER_ABBR - 3] = 0; if (loopa > 99) dummy_abbr[MAX_TALKER_ABBR - 4] = 0; if (loopa > 999) dummy_abbr[MAX_TALKER_ABBR - 5] = 0; sprintf (dummy_abbr, "%s%d", dummy_abbr, loopa); match = match_talker_abbr_absolute (dummy_abbr); } strcpy (remote_talker->abbr, dummy_abbr); lower_case (remote_talker->abbr); return; } #include "intercom2.c" #else /* another case where we must sacrifice a tad of convience in one place to get loads of it else where... */ #define TALKER_NAME "Playground+" int main (void) { printf ("\nPlayground+ Intercom Server\n" LINE "This code is currently disabled. If you wish to have the intercom " "included\nwithin %s you need to re-run 'make config' and select it.\n\n" "Once selected, the intercom is run as part of the talker and should " "not be\nrun on its own.\n\n", TALKER_NAME); exit (0); } #endif