/*
* IMC2 - an inter-mud communications protocol
*
* imc-comm.c: command interface code
*
* Copyright (C) 1996 Oliver Jowett <oliver@sa-search.massey.ac.nz>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program (see the file COPYING); if not, write to the
* Free Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*
*/
#include <stdlib.h>
#include <sys/types.h>
#include <sys/time.h>
#include <ctype.h>
#include <errno.h>
#include <stdio.h>
#include <unistd.h>
#include <string.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <sys/ioctl.h>
/* USEIOCTL #defined if TIOCINQ or TIOCOUTQ are - we assume that the ioctls
* work in that case.
*/
#if defined(TIOCINQ) && defined(TIOCOUTQ)
#define USEIOCTL
static int outqsize;
#endif
#include "imc.h"
#include "imc-comm.h"
#include "imc-mail.h"
/* rignore'd people/muds - should be a list, really */
char *imc_rignore[IMC_RIGNORE_MAX];
/* prefixes for all data files */
char *imc_prefix;
/* called when a keepalive has been received */
void imc_recv_keepalive(const char *from, const char *version)
{
imc_reminfo *p;
if (!strcasecmp(from, imc_name))
return;
p=imc_find_reminfo(from); /* this should never fail, imc.c should create an
* entry if one doesn't exist (in the path update
* code)
*/
if (!p) /* boggle */
return;
/* lower-level code has already updated p->alive */
if (strcasecmp(version, p->version)) /* remote version has changed? */
{
imc_strfree(p->version); /* if so, update it */
p->version=imc_strdup(version);
}
}
/* called when a ping request is received */
void imc_recv_ping(const char *from, int time_s, int time_u)
{
/* ping 'em back */
imc_send_pingreply(from, time_s, time_u);
}
/* called when a ping reply is received */
void imc_recv_pingreply(const char *from, int time_s, int time_u)
{
imc_reminfo *p;
struct timeval tv;
p = imc_find_reminfo(from); /* should always exist */
if (!p) /* boggle */
return;
gettimeofday(&tv, NULL); /* grab the exact time now and calc RTT */
p->ping = (tv.tv_sec - time_s) * 1000 + (tv.tv_usec - time_u) / 1000;
}
/* check if a packet from a given source should be ignored */
int imc_isignored(const char *who)
{
int i;
for (i=0; i<IMC_RIGNORE_MAX; i++)
if (imc_rignore[i] &&
(!strcasecmp(imc_rignore[i], who) ||
!strcasecmp(imc_rignore[i], imc_mudof(who))))
return 1;
return 0;
}
/* send a standard 'you are being ignored' rtell */
static void sendignore(const char *to)
{
char buf[IMC_DATA_LENGTH];
sprintf(buf, "%s is ignoring you", imc_name);
imc_send_tell(NULL, to, buf, 1);
}
/** imc_char_data representation:
*
* All levels are "MAX_LEVEL-independent". 0 and up are mortal levels as
* usual. -1 is MAX_LEVEL. -2 is MAX_LEVEL-1, and so on. Conversion between
* imc_char_data and the mud's internal levels etc. is done in the interface
* file (eg. imc-rom.c or imc-envy.c)
*
* Invis/hidden state, and detecting them, use the IMC_INVIS and IMC_HIDDEN
* #defines.
*
* d->wizi is the wizi or incog level of the character (whichever is higher)
* d->invis is a bitvector representing the invis/hidden state of a character
* d->see is a bitvector representing detect invis/hidden for a character
* d->level is the level of the character (for the purposes of seeing
* wizi/incog)
* d->sex is currently unused, but will eventually be used for socials
*/
/* convert from the char data in 'p' to an internal representation in 'd' */
static void getdata(const imc_packet *p, imc_char_data *d)
{
strcpy(d->name, p->from);
d->invis = imc_getkeyi(&p->data, "invis", 0);
d->wizi = imc_getkeyi(&p->data, "wizi", 0);
d->see = imc_getkeyi(&p->data, "see", 0);
d->level = imc_getkeyi(&p->data, "level", 0);
d->sex = imc_getkeyi(&p->data, "sex", 0);
}
/* convert back from 'd' to 'p' */
static void setdata(imc_packet *p, const imc_char_data *d)
{
imc_initdata(&p->data);
if (!d)
{
strcpy(p->from, "*");
imc_addkeyi(&p->data, "level", -1);
return;
}
strcpy(p->from, d->name);
if (d->invis)
imc_addkeyi(&p->data, "invis", d->invis);
if (d->wizi)
imc_addkeyi(&p->data, "wizi", d->wizi);
if (d->see)
imc_addkeyi(&p->data, "see", d->see);
if (d->level)
imc_addkeyi(&p->data, "level", d->level);
if (d->sex)
imc_addkeyi(&p->data, "sex", d->sex);
}
/* handle a packet destined for us, or a broadcast */
void imc_recv(const imc_packet *p)
{
imc_char_data d;
getdata(p, &d);
/* chat: message to a channel (broadcast) */
if (!strcasecmp(p->type, "chat") && !imc_isignored(p->from))
imc_recv_chat(&d, imc_getkeyi(&p->data, "channel", 0),
imc_getkey(&p->data, "text", ""));
/* emote: emote to a channel (broadcast) */
else if (!strcasecmp(p->type, "emote") && !imc_isignored(p->from))
imc_recv_emote(&d, imc_getkeyi(&p->data, "channel", 0),
imc_getkey(&p->data, "text", ""));
/* tell: tell a player here something */
else if (!strcasecmp(p->type, "tell"))
if (imc_isignored(p->from))
sendignore(p->from);
else
imc_recv_tell(&d, p->to, imc_getkey(&p->data, "text", ""),
imc_getkeyi(&p->data, "isreply", 0));
/* who-reply: receive a who response */
else if (!strcasecmp(p->type, "who-reply"))
imc_recv_whoreply(p->to, imc_getkey(&p->data, "text", ""));
/* who: receive a who request */
else if (!strcasecmp(p->type, "who"))
if (imc_isignored(p->from))
sendignore(p->from);
else
imc_recv_who(&d, imc_getkey(&p->data, "type", "who"));
/* beep: beep a player */
else if (!strcasecmp(p->type, "beep"))
if (imc_isignored(p->from))
sendignore(p->from);
else
imc_recv_beep(&d, p->to);
/* is-alive: receive a keepalive (broadcast) */
else if (!strcasecmp(p->type, "is-alive"))
imc_recv_keepalive(imc_mudof(p->from),
imc_getkey(&p->data, "versionid", "unknown"));
/* ping: receive a ping request */
else if (!strcasecmp(p->type, "ping"))
imc_recv_ping(imc_mudof(p->from), imc_getkeyi(&p->data, "time-s", 0),
imc_getkeyi(&p->data, "time-us", 0));
/* ping-reply: receive a ping reply */
else if (!strcasecmp(p->type, "ping-reply"))
imc_recv_pingreply(imc_mudof(p->from), imc_getkeyi(&p->data, "time-s", 0),
imc_getkeyi(&p->data, "time-us", 0));
/* mail: mail something to a local player */
else if (!strcasecmp(p->type, "mail"))
imc_recv_mail(imc_getkey(&p->data, "from", "error@hell"),
imc_getkey(&p->data, "to", "error@hell"),
imc_getkey(&p->data, "date", "(IMC error: bad date)"),
imc_getkey(&p->data, "subject", "no subject"),
imc_getkey(&p->data, "id", "bad_id"),
imc_getkey(&p->data, "text", ""));
/* mail-ok: remote confirmed that they got the mail ok */
else if (!strcasecmp(p->type, "mail-ok"))
imc_recv_mailok(p->from, imc_getkey(&p->data, "id", "bad_id"));
/* mail-reject: remote rejected our mail, bounce it */
else if (!strcasecmp(p->type, "mail-reject"))
imc_recv_mailrej(p->from, imc_getkey(&p->data, "id", "bad_id"),
imc_getkey(&p->data, "reason",
"(IMC error: no reason supplied"));
}
/* Commands called by the interface layer */
/* send a message out on a channel */
void imc_send_chat(const imc_char_data *from, int channel,
const char *argument, const char *to)
{
imc_packet out;
char tobuf[IMC_MNAME_LENGTH];
setdata(&out, from);
strcpy(out.type, "chat");
strcpy(out.to, "*@*");
imc_addkey(&out.data, "text", argument);
imc_addkeyi(&out.data, "channel", channel);
to=imc_getarg(to, tobuf, IMC_MNAME_LENGTH);
while (tobuf[0])
{
if (!strcmp(tobuf, "*")
|| !strcasecmp(tobuf, imc_name)
|| imc_find_reminfo(tobuf)!=NULL)
{
strcpy(out.to, "*@");
strcat(out.to, tobuf);
imc_send(&out);
}
to=imc_getarg(to, tobuf, IMC_MNAME_LENGTH);
}
imc_freedata(&out.data);
}
/* send an emote out on a channel */
void imc_send_emote(const imc_char_data *from, int channel,
const char *argument, const char *to)
{
imc_packet out;
char tobuf[IMC_MNAME_LENGTH];
setdata(&out, from);
strcpy(out.type, "emote");
imc_addkeyi(&out.data, "channel", channel);
imc_addkey(&out.data, "text", argument);
to=imc_getarg(to, tobuf, IMC_MNAME_LENGTH);
while (tobuf[0])
{
if (!strcmp(tobuf, "*")
|| !strcasecmp(tobuf, imc_name)
|| imc_find_reminfo(tobuf)!=NULL)
{
strcpy(out.to, "*@");
strcat(out.to, tobuf);
imc_send(&out);
}
to=imc_getarg(to, tobuf, IMC_MNAME_LENGTH);
}
imc_freedata(&out.data);
}
/* send a tell to a remote player */
void imc_send_tell(const imc_char_data *from, const char *to,
const char *argument, int isreply)
{
imc_packet out;
setdata(&out, from);
imc_sncpy(out.to, to, IMC_NAME_LENGTH);
strcpy(out.type, "tell");
imc_addkey(&out.data, "text", argument);
if (isreply)
imc_addkeyi(&out.data, "isreply", isreply);
imc_send(&out);
imc_freedata(&out.data);
}
/* send a who-request to a remote mud */
void imc_send_who(const imc_char_data *from, const char *to, const char *type)
{
imc_packet out;
setdata(&out, from);
sprintf(out.to, "*@%s", to);
strcpy(out.type, "who");
imc_addkey(&out.data, "type", type);
imc_send(&out);
imc_freedata(&out.data);
}
/* respond to a who request with the given data */
void imc_send_whoreply(const char *to, const char *data)
{
imc_packet out;
imc_initdata(&out.data);
imc_sncpy(out.to, to, IMC_NAME_LENGTH);
strcpy(out.type, "who-reply");
strcpy(out.from, "*");
imc_addkey(&out.data, "text", data);
imc_send(&out);
imc_freedata(&out.data);
}
/* beep a remote player */
void imc_send_beep(const imc_char_data *from, const char *to)
{
imc_packet out;
setdata(&out, from);
strcpy(out.type, "beep");
imc_sncpy(out.to, to, IMC_NAME_LENGTH);
imc_send(&out);
imc_freedata(&out.data);
}
/* send a keepalive to everyone */
void imc_send_keepalive(void)
{
imc_packet out;
imc_initdata(&out.data);
strcpy(out.type, "is-alive");
strcpy(out.from, "*");
strcpy(out.to, "*@*");
imc_addkey(&out.data, "versionid", IMC_VERSIONID);
imc_send(&out);
imc_freedata(&out.data);
}
/* send a ping with a given timestamp */
void imc_send_ping(const char *to, int time_s, int time_u)
{
imc_packet out;
imc_initdata(&out.data);
strcpy(out.type, "ping");
strcpy(out.from, "*");
strcpy(out.to, "*@");
imc_sncpy(out.to+2, to, IMC_MNAME_LENGTH-2);
imc_addkeyi(&out.data, "time-s", time_s);
imc_addkeyi(&out.data, "time-us", time_u);
imc_send(&out);
imc_freedata(&out.data);
}
/* send a pingreply with the given timestamp */
void imc_send_pingreply(const char *to, int time_s, int time_u)
{
imc_packet out;
imc_initdata(&out.data);
strcpy(out.type, "ping-reply");
strcpy(out.from, "*");
strcpy(out.to, "*@");
imc_sncpy(out.to+2, to, IMC_MNAME_LENGTH-2);
imc_addkeyi(&out.data, "time-s", time_s);
imc_addkeyi(&out.data, "time-us", time_u);
imc_send(&out);
imc_freedata(&out.data);
}
/* admin commands */
/* add/remove/list rignores */
const char *imc_ignore(const char *what)
{
static char buf[IMC_DATA_LENGTH];
int i, count;
if (!what || !what[0])
{
strcpy(buf, "Current ignores:\n\r");
for (i=0, count=0; i<IMC_RIGNORE_MAX; i++)
if (imc_rignore[i])
{
sprintf(buf + strlen(buf), " %s\n\r", imc_rignore[i]);
count++;
}
if (!count)
strcat(buf, " none");
else
sprintf(buf + strlen(buf), "[total %d]", count);
return buf;
}
for (i=0; i<IMC_RIGNORE_MAX; i++)
if (imc_rignore[i] && !strcasecmp(what, imc_rignore[i]))
{
imc_strfree(imc_rignore[i]);
imc_rignore[i] = NULL;
imc_saveignores();
return "Ignore removed";
}
for (i=0; i<IMC_RIGNORE_MAX; i++)
if (!imc_rignore[i])
{
imc_rignore[i] = imc_strdup(what);
imc_saveignores();
return "Ignore added";
}
return "No ignore slots free";
}
/* show current IMC socket states */
const char *imc_sockets(void)
{
int i;
static char buf[IMC_DATA_LENGTH];
char *state;
int r, s;
sprintf(buf, "%2s %4s %-9s %-15s %-6s %-6s %-6s %-6s",
"# ", "Desc", "Mud", "State", "Inbuf", "Outbuf", "Spam1", "Spam2");
for (i=0; i<IMC_MAX; i++)
{
if (imc[i].inuse)
{
switch (imc[i].state)
{
case IMC_CONNECTING:
state = "connecting";
break;
case IMC_WAIT1:
state = "wait1";
break;
case IMC_WAIT2:
state = "wait2";
break;
case IMC_CONNECTED:
state = "connected";
break;
default:
state = "unknown";
break;
}
#ifdef USEIOCTL
/* try to work out the system buffer sizes */
r=0;
ioctl(imc[i].desc, TIOCINQ, &r);
r += strlen(imc[i].inbuf);
s=outqsize;
if (s)
{
ioctl(imc[i].desc, TIOCOUTQ, &s);
s=outqsize-s;
}
s += strlen(imc[i].outbuf);
#else
r=strlen(imc[i].inbuf);
s=strlen(imc[i].outbuf);
#endif
sprintf(buf + strlen(buf), "\n\r%2d %4d %-9s %-15s %6d %6d %6d %6d",
i,
imc[i].desc,
imc[i].info != -1 ? imc_info[imc[i].info].name : "unknown",
state,
r,
s,
imc[i].spamcounter1,
imc[i].spamcounter2);
}
}
return buf;
}
/* list current connections/known muds
* level=0 is mortal-level access (mudnames and connection states)
* level=1 is imm-level access (names, hosts, ports, states)
* level=2 is full access (names, hosts, ports, passwords, flags, states)
*/
const char *imc_list(int level)
{
int i;
static char buf[IMC_DATA_LENGTH];
char *state;
imc_reminfo *p;
strcpy(buf, "Direct connections:\n\r");
switch (level)
{
case 0:
sprintf(buf + strlen(buf), "%-10s %-15s", "Name", "State");
break;
case 1:
sprintf(buf + strlen(buf), "%-10s %-30s %5s %-13s", "Name", "Host", "Port",
"State");
break;
case 2:
sprintf(buf + strlen(buf),
"%-8s %-25s %5s %-13s %-10s %-10s\n"
" %-8s %-9s %s",
"Name", "Host", "Port", "State", "ClientPW", "ServerPW",
"RcvStamp", "NoForward", "Flags");
break;
}
for (i=0; i<IMC_MAX; i++)
{
if (!imc_info[i].inuse)
continue;
state = imc_info[i].connected ? "connected" : "not connected";
switch (level)
{
case 0:
sprintf(buf + strlen(buf), "\n\r%-10s %-15s", imc_info[i].name, state);
break;
case 1:
sprintf(buf + strlen(buf), "\n\r%-10s %-30s %5hu %-13s",
imc_info[i].name,
imc_info[i].host,
imc_info[i].port,
state);
break;
case 2:
sprintf(buf + strlen(buf),
"\n\r%-8s %-25s %5hu %-13s %-10s %-10s"
"\n\r %-8d %-9d %s",
imc_info[i].name,
imc_info[i].host,
imc_info[i].port,
state,
imc_info[i].clientpw,
imc_info[i].serverpw,
imc_info[i].rcvstamp,
imc_info[i].noforward,
imc_flagname(imc_info[i].flags));
break;
}
}
strcpy(buf + strlen(buf), "\n\r\n\rActive muds on IMC:\n\r");
sprintf(buf + strlen(buf), "%-10s %-10s %-9s %-20s %-10s", "Name",
"Last alive", "Ping time", "IMC Version", "Route");
for (p=imc_remoteinfo; p; p=p->next)
if (p->ping)
sprintf(buf + strlen(buf), "\n\r%-10s %9ds %7dms %-20s %-10s",
p->name, (int) (imc_now - p->alive), p->ping, p->version,
p->route ? p->route : "broadcast");
else
sprintf(buf + strlen(buf), "\n\r%-10s %9ds %9s %-20s %-10s",
p->name, (int) (imc_now - p->alive), "unknown", p->version,
p->route ? p->route : "broadcast");
return buf;
}
/* runtime changing of imc.conf
* returns >0 success
* <0 error
* ==0 unknown command
*
* commands:
* add <mudname>
* delete <mudname>
* rename <oldname> <newname>
* set <mudname> <host|port|clientpw|serverpw|flags> <newvalue>
* set <mudname> all <host> <port> <clientpw> <serverpw> <flags>
*/
int imc_command(const char *argument)
{
char arg1[IMC_DATA_LENGTH];
char arg2[IMC_DATA_LENGTH];
char arg3[IMC_DATA_LENGTH];
int i;
argument=imc_getarg(argument, arg1, IMC_DATA_LENGTH);
argument=imc_getarg(argument, arg2, IMC_DATA_LENGTH);
if (!arg1[0] || !arg2[0])
return 0;
if (!strcasecmp(arg1, "add"))
{
for (i=0; i<IMC_MAX; i++)
if (!imc_info[i].inuse)
break;
if (i == IMC_MAX)
{
imc_qerror("No more entries are available");
return -1;
}
imc_info[i].name = imc_strdup(arg2);
imc_info[i].host = imc_strdup("");
imc_info[i].port = 0;
imc_info[i].connected = 0;
imc_info[i].index = -1;
imc_info[i].clientpw = imc_strdup("");
imc_info[i].serverpw = imc_strdup("");
imc_info[i].timer = 0;
imc_info[i].inuse = 1;
imc_info[i].rcvstamp = 0;
imc_info[i].noforward = 0;
return 1;
}
else if (!strcasecmp(arg1, "delete"))
{
i=imc_getindex(arg2);
if (i == -1)
{
imc_qerror("Entry not found");
return -1;
}
imc_disconnect(arg2);
imc_strfree(imc_info[i].name);
imc_strfree(imc_info[i].host);
imc_strfree(imc_info[i].clientpw);
imc_strfree(imc_info[i].serverpw);
imc_info[i].inuse = 0;
imc_saveconfig();
return 1;
}
else if (!strcasecmp(arg1, "rename"))
{
i=imc_getindex(arg2);
if (i == -1)
{
imc_qerror("Entry not found");
return -1;
}
argument=imc_getarg(argument, arg3, IMC_DATA_LENGTH);
if (!arg3[0])
return 0;
imc_strfree(imc_info[i].name);
imc_info[i].name = imc_strdup(arg3);
imc_saveconfig();
return 1;
}
else if (!strcasecmp(arg1, "set"))
{
for (i=0; i<IMC_MAX; i++)
if (imc_info[i].inuse && !strcasecmp(arg2, imc_info[i].name))
break;
if (i == IMC_MAX)
{
imc_qerror("Entry not found");
return -1;
}
argument=imc_getarg(argument, arg3, IMC_DATA_LENGTH);
if (!arg3[0] || !argument[0])
return 0;
else if (!strcasecmp(arg3, "all"))
{
imc_strfree(imc_info[i].host);
imc_strfree(imc_info[i].clientpw);
imc_strfree(imc_info[i].serverpw);
argument=imc_getarg(argument, arg3, IMC_DATA_LENGTH);
imc_info[i].host=imc_strdup(arg3);
argument=imc_getarg(argument, arg3, IMC_DATA_LENGTH);
imc_info[i].port=strtoul(arg3, NULL, 10);
argument=imc_getarg(argument, arg3, IMC_PW_LENGTH);
imc_info[i].clientpw=imc_strdup(arg3);
argument=imc_getarg(argument, arg3, IMC_PW_LENGTH);
imc_info[i].serverpw=imc_strdup(arg3);
argument=imc_getarg(argument, arg3, IMC_DATA_LENGTH);
imc_info[i].rcvstamp=strtoul(arg3, NULL, 10);
argument=imc_getarg(argument, arg3, IMC_DATA_LENGTH);
imc_info[i].noforward=strtoul(arg3, NULL, 10);
argument=imc_getarg(argument, arg3, IMC_DATA_LENGTH);
imc_info[i].flags=imc_flagvalue(arg3);
imc_saveconfig();
return 1;
}
else if (!strcasecmp(arg3, "host"))
{
imc_strfree(imc_info[i].host);
imc_info[i].host=imc_strdup(argument);
imc_saveconfig();
return 1;
}
else if (!strcasecmp(arg3, "port"))
{
imc_info[i].port=strtoul(argument, NULL, 10);
imc_saveconfig();
return 1;
}
else if (!strcasecmp(arg3, "clientpw"))
{
imc_strfree(imc_info[i].clientpw);
imc_info[i].clientpw=imc_strdup(argument);
imc_saveconfig();
return 1;
}
else if (!strcasecmp(arg3, "serverpw"))
{
imc_strfree(imc_info[i].serverpw);
imc_info[i].serverpw=imc_strdup(argument);
imc_saveconfig();
return 1;
}
else if (!strcasecmp(arg3, "rcvstamp"))
{
imc_info[i].rcvstamp=strtoul(argument, NULL, 10);
imc_saveconfig();
return 1;
}
else if (!strcasecmp(arg3, "noforward"))
{
imc_info[i].noforward=strtoul(argument, NULL, 10);
imc_saveconfig();
return 1;
}
else if (!strcasecmp(arg3, "flags"))
{
imc_info[i].flags=imc_flagvalue(argument);
imc_saveconfig();
return 1;
}
return 0;
}
return 0;
}
/* get some IMC stats, return a string describing them */
const char *imc_getstats(void)
{
static char buf[300];
sprintf(buf,
"IMC statistics\n\r"
"\n\r"
"Received packets: %ld\n\r"
"Received bytes: %ld (%ld/second)\n\r"
"Transmitted packets: %ld\n\r"
"Transmitted bytes: %ld (%ld/second)\n\r",
imc_stats.rx_pkts,
imc_stats.rx_bytes,
imc_stats.rx_bytes /
((imc_now - imc_stats.start) ?
(imc_now - imc_stats.start) : 1),
imc_stats.tx_pkts,
imc_stats.tx_bytes,
imc_stats.tx_bytes /
((imc_now - imc_stats.start) ?
(imc_now - imc_stats.start) : 1));
return buf;
}
/* read an IMC config file */
int imc_readconfig(void)
{
FILE *cf;
int i;
char name[IMC_NAME_LENGTH], host[200];
char pw1[IMC_PW_LENGTH], pw2[IMC_PW_LENGTH];
unsigned short port;
int count, count1;
char buf[1000];
char configfile[200];
imc_sncpy(configfile, imc_prefix, 193);
strcat(configfile, "config");
for (i=0; i<IMC_MAX; i++)
{
imc[i].inuse = 0;
imc_info[i].inuse = 0;
}
cf=fopen(configfile, "r");
if (!cf)
{
imc_logerror("imc_readconfig: couldn't open %s", configfile);
return 0;
}
for (i=0; i<IMC_MAX;)
{
if (fgets(buf, 1000, cf) == NULL)
break;
if (buf[0] == '#' || buf[0] == '\n')
continue;
if (sscanf(buf, "%s %s %hu %s %s %n",
name, host, &port, pw1, pw2, &count) < 5)
{
imc_logerror("Bad config file line: %s", buf);
i--;
continue;
}
imc_info[i].name = imc_strdup(name);
imc_info[i].host = imc_strdup(host);
imc_info[i].clientpw = imc_strdup(pw1);
imc_info[i].serverpw = imc_strdup(pw2);
imc_info[i].port = port;
imc_info[i].connected = 0;
imc_info[i].index = -1;
imc_info[i].inuse = 1;
if (sscanf(buf+count, "%d %d %n",
&imc_info[i].rcvstamp,
&imc_info[i].noforward,
&count1)<2)
{ /* old version config file */
imc_info[i].rcvstamp = 0;
imc_info[i].noforward = 0;
imc_info[i].flags = imc_flagvalue(buf + count);
}
else
imc_info[i].flags = imc_flagvalue(buf + count + count1);
i++;
}
if (ferror(cf))
{
imc_lerror("imc_readconfig");
fclose(cf);
return 0;
}
fclose(cf);
return 1;
}
/* save the IMC config file (under whatever name it was loaded from) */
int imc_saveconfig(void)
{
FILE *out;
int i;
char configfile[200];
imc_sncpy(configfile, imc_prefix, 193);
strcat(configfile, "config");
out = fopen(configfile, "w");
if (!out)
{
imc_lerror("imc_saveconfig: error opening %s", configfile);
return 0;
}
fprintf(out, "%-10s %-30s %5s %-10s %-10s %-5s %-5s %s\n",
"# Name", "Host", "Port", "ClientPW", "ServerPW",
"RcvSt", "NoFwd", "Flags");
for (i=0; i<IMC_MAX; i++)
if (imc_info[i].inuse)
fprintf(out, "%-10s %-30s %5hu %-10s %-10s %5d %5d %s\n",
imc_info[i].name,
imc_info[i].host,
imc_info[i].port,
imc_info[i].clientpw,
imc_info[i].serverpw,
imc_info[i].rcvstamp,
imc_info[i].noforward,
imc_flagname(imc_info[i].flags));
if (ferror(out))
{
imc_lerror("imc_saveconfig: error saving %s", configfile);
fclose(out);
return 0;
}
fclose(out);
return 1;
}
/* read an IMC rignores file */
int imc_readignores(void)
{
FILE *inf;
int i;
char buf[1000];
char buf1[IMC_NAME_LENGTH];
char name[200];
imc_sncpy(name, imc_prefix, 191);
strcat(name, "rignores");
for (i=0; i<IMC_RIGNORE_MAX; i++)
imc_rignore[i]=NULL;
inf=fopen(name, "r");
if (!inf)
{
imc_logerror("imc_readignores: couldn't open %s", name);
return 0;
}
for (i=0; i<IMC_RIGNORE_MAX;)
{
if (fgets(buf, 1000, inf) == NULL)
break;
if (buf[0] == '#' || buf[0] == '\n')
continue;
sscanf(buf, "%[^\n]", buf1);
imc_rignore[i]=imc_strdup(buf1);
i++;
}
if (ferror(inf))
{
imc_lerror("imc_readignores");
fclose(inf);
return 0;
}
fclose(inf);
return 1;
}
/* save the current rignore list */
int imc_saveignores(void)
{
FILE *out;
int i;
char name[200];
imc_sncpy(name, imc_prefix, 191);
strcat(name, "rignores");
out = fopen(name, "w");
if (!out)
{
imc_lerror("imc_saveignores: error opening %s", name);
return 0;
}
fprintf(out,
"# IMC rignores file, one name per line, no leading spaces\n\r"
"# lines starting with '#' are discarded\n\r");
for (i=0; i<IMC_RIGNORE_MAX; i++)
if (imc_rignore[i])
fprintf(out, "%s\n", imc_rignore[i]);
if (ferror(out))
{
imc_lerror("imc_saveignores: error saving %s", name);
fclose(out);
return 0;
}
fclose(out);
return 1;
}
#ifdef USEIOCTL
/* this is an ugly hack to generate the send-queue size for an empty queue.
* SO_SNDBUF is only supported in some places, and seems to cause problems
* under SunOS
*/
/* connect to the local discard server, and look at the queue size for an
* empty socket.
*/
static int getsndbuf(void)
{
struct sockaddr_in sa;
int s, queue;
if ((s=socket(AF_INET, SOCK_STREAM, 0))<0)
return 0;
sa.sin_family = AF_INET;
sa.sin_addr.s_addr = inet_addr("127.0.0.1"); /* connect to localhost */
sa.sin_port = htons(9); /* 'discard' service */
if (connect(s, (struct sockaddr *)&sa, sizeof(sa))<0)
{
close(s);
return 0;
}
if (ioctl(s, TIOCOUTQ, &queue)<0)
{
close(s);
return 0;
}
close(s);
return queue;
}
#endif
/* start everything up
* 'name' is the mudname of this mud (can't contain spaces, and should be
* short).
* 'port' is the port to listen on for incoming connections.
* 'prefix' is the prefix to add to all IMC file references. For example,
* you could pass "imc/" to get imc/config, imc/mail-queue, etc.
*/
int imc_startup(const char *name, int port, const char *prefix)
{
#ifdef USEIOCTL
outqsize = getsndbuf();
imc_logstring("Found TIOCOUTQ=%d", outqsize);
#endif
imc_now=time(NULL); /* start our clock */
imc_prefix=imc_strdup(prefix);
imc_readconfig(); /* ignore errors */
imc_readignores();
if (!imc_ll_startup(name, port))
return 0;
imc_remoteinfo=NULL;
imc_mail_startup(); /* start up the mailer */
return 1;
}
void imc_idle(void)
{
static time_t nextalive;
static time_t nextping;
static int whichping;
imc_reminfo *p, *pnext;
imc_now=time(NULL);
/* keepalives and time out old reminfo entries */
if (!nextalive) /* on startup */
nextalive=imc_now + 60;
if (nextalive<imc_now)
{
imc_send_keepalive();
nextalive=imc_now+IMC_KEEPALIVE_TIME;
for (p=imc_remoteinfo; p; p=pnext)
{
pnext=p->next;
if (p->alive+IMC_KEEPALIVE_TIMEOUT < imc_now)
imc_delete_reminfo(p);
}
}
/* send pings */
if (nextping<imc_now)
{
int i;
struct timeval tv;
nextping=imc_now+IMC_PING_TIME;
p=imc_remoteinfo;
for (i=0; i<whichping; i++)
{
if (!p)
break;
p=p->next;
if (!p)
{
p=imc_remoteinfo;
whichping=0;
break;
}
}
if (p)
{
whichping++;
gettimeofday(&tv, NULL);
imc_send_ping(p->name, tv.tv_sec, tv.tv_usec);
}
}
/* run low-level idle */
imc_ll_idle();
/* run mail idle */
imc_mail_idle();
}
/* shut down all of IMC */
void imc_shutdown(void)
{
int i;
imc_reminfo *p, *pnext;
imc_ll_shutdown();
for (i=0; i<IMC_RIGNORE_MAX; i++)
if (imc_rignore[i])
imc_strfree(imc_rignore[i]);
for (i=0; i<IMC_MAX; i++)
{
if (imc_info[i].inuse)
{
imc_strfree(imc_info[i].name);
imc_strfree(imc_info[i].host);
imc_strfree(imc_info[i].clientpw);
imc_strfree(imc_info[i].serverpw);
}
}
for (p=imc_remoteinfo; p; p=pnext)
{
pnext=p->next;
imc_strfree(p->version);
imc_strfree(p->name);
imc_free(p, sizeof(imc_reminfo));
}
imc_mail_shutdown();
imc_strfree(imc_prefix);
}