/*
* 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