/*
* IMC2 - an inter-mud communications protocol
*
* webserver.c: the server for a who-list-on-web-page thing
*
* Copyright (C) 1997 Oliver Jowett <oliver@jowett.manawatu.planet.co.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.
*
*/
/* Consider this alpha code. 'nuff said */
#include <stdlib.h>
#include <stdio.h>
#include <sys/time.h>
#include <sys/types.h>
#include <unistd.h>
#include <string.h>
#include <signal.h>
#include <sys/socket.h>
#include <fcntl.h>
#include <sys/un.h>
#include <ctype.h>
#include <sys/stat.h>
#include <errno.h>
#include "imc.h"
#ifndef CONFIG
#define CONFIG "cgi/"
#endif
int requests;
char *url;
/* MM stubs */
void *imc_malloc(int size)
{
return malloc(size);
}
void imc_free(void *block, int size)
{
free(block);
}
char *imc_strdup(const char *orig)
{
return strdup(orig);
}
void imc_strfree(char *str)
{
free(str);
}
int imc_readconfighook(const char *word, const char *value)
{
if (!strcasecmp(word, "URL"))
{
if (url)
imc_strfree(url);
url=imc_strdup(value);
return 1;
}
return 0;
}
void imc_saveconfighook(FILE *fp)
{
if (url)
fprintf(fp, "URL %s\n", url);
}
/* logging functions */
void imc_debug(const imc_connect *c, int out, const char *string)
{
#if 0
/* this is rarely useful */
char *dir;
dir=out ? "<" : ">";
fprintf(stdout, "%s %s %s\n", imc_getconnectname(c), dir, string);
#endif
}
void imc_log(const char *string)
{
char buf[IMC_DATA_LENGTH];
strcpy(buf, ctime(&imc_now));
buf[strlen(buf)-1]=' ';
fprintf(stderr, "%s%s\n", buf, string);
}
void imc_recv_who(const imc_char_data *from, const char *type)
{
char arg[IMC_DATA_LENGTH];
char buf[IMC_DATA_LENGTH];
type=imc_getarg(type, arg, IMC_DATA_LENGTH);
if (!strcasecmp(arg, "who") || !strcasecmp(arg, "info"))
{
if (!url)
imc_send_whoreply(from->name, "This is a CGI interface site.\n\r", -1);
else
{
sprintf(buf,
"This is a CGI interface site.\n\r"
"Its output can be accessed at %s.\n\r", url);
imc_send_whoreply(from->name, buf, -1);
}
}
else if (!strcasecmp(arg, "direct"))
imc_send_whoreply(from->name, imc_list(0), -1);
else if (!strcasecmp(arg, "list"))
imc_send_whoreply(from->name, imc_list(3), -1);
else if (!strcasecmp(arg, "config"))
imc_send_whoreply(from->name, imc_list(4), -1);
else if (!strcasecmp(arg, "istats"))
{
strcpy(buf, imc_getstats());
sprintf(buf+strlen(buf),
"\n\rProcessed requests: %d\n\r", requests);
imc_send_whoreply(from->name, buf, -1);
}
else if (!strcasecmp(arg, "help") || !strcasecmp(arg, "services") ||
!strcasecmp(arg, "help"))
imc_send_whoreply(from->name,
"Available rquery types:\n\r"
"help - this list\n\r"
"info - server information\n\r"
"list - active IMC connections\n\r"
"istats - IMC statistics\n\r", -1);
else
imc_send_whoreply(from->name,
"Sorry, no information of that type is available", -1);
}
void imc_recv_whois(const imc_char_data *from, const char *to)
{
}
void imc_recv_whoisreply(const char *to, const char *text)
{
}
void imc_recv_tell(const imc_char_data *from, const char *to, const char *text,
int isreply)
{
}
void imc_recv_chat(const imc_char_data *from, int channel, const char *text)
{
}
void imc_recv_emote(const imc_char_data *from, int channel, const char *text)
{
}
void imc_recv_beep(const imc_char_data *from, const char *to)
{
}
void imc_traceroute(int ping, const char *pathto, const char *pathfrom)
{
}
char *imc_mail_arrived(const char *from, const char *to, const char *date,
const char *subject, const char *text)
{
return "This is a webserver interface only, and does not accept mail.";
}
/* Core of the code! */
/* now handles rwho sequencing for those muds that support it */
typedef struct _replydata {
char *text;
int sequence;
struct _replydata *next;
} replydata;
typedef struct {
int fd;
int id;
int length;
int received;
replydata *reply;
} requestdata;
requestdata clients[20];
int id;
int freeclients;
void sendlist(int fd)
{
char buf[10000];
imc_reminfo *r;
buf[0]=0;
for (r=imc_reminfo_list; r; r=r->next)
{
sprintf(buf+strlen(buf),
"%s %s %d\n", r->name, r->version, r->ping);
}
write(fd, buf, strlen(buf));
}
void complete_request(int index)
{
replydata *r, *next;
int expected=0;
for (r=clients[index].reply; r; r=next)
{
next=r->next;
if (r->sequence != expected)
{
char buf[100];
while (expected<r->sequence)
{
sprintf(buf, "\n\r[missing data for sequence %d]\n\r", expected);
write(clients[index].fd, buf, strlen(buf));
expected++;
}
}
write(clients[index].fd, r->text, strlen(r->text));
expected++;
imc_strfree(r->text);
imc_free(r, sizeof(*r));
}
close (clients[index].fd);
clients[index].fd=-1;
requests++;
}
void add_request(int index, const char *text, int sequence)
{
replydata *r, *search;
if (clients[index].length < sequence)
{
imc_logerror("add_request: sequence higher than max?");
return;
}
r=imc_malloc(sizeof(*r));
r->sequence=sequence;
r->text=imc_strdup(text);
r->next=NULL;
if (!clients[index].reply)
clients[index].reply=r;
else
{
for (search=clients[index].reply;
search->next && search->next->sequence < sequence;
search=search->next)
;
if (search->next && search->next->sequence == sequence)
{
imc_logerror("add_request: duplicate rwho sequence?");
imc_strfree(r->text);
imc_free(r, sizeof(*r));
}
else
{
r->next=search->next;
search->next=r;
}
}
clients[index].received++;
if (clients[index].length &&
clients[index].received == clients[index].length)
complete_request(index);
}
void free_reply(replydata *r)
{
replydata *next;
for (; r; r=next)
{
next=r->next;
imc_strfree(r->text);
imc_free(r, sizeof(*r));
}
}
void imc_recv_whoreply(const char *to, const char *text, int sequence)
{
int i, j;
i=atoi(to);
if (!i)
return;
for (j=0; j<20; j++)
if (clients[j].fd!=-1 && clients[j].id==i)
{
if (sequence<0)
{
clients[j].length=-sequence;
add_request(j, text, -sequence-1);
}
else
add_request(j, text, sequence);
return;
}
}
void runclient(int clientfd)
{
char buf[1000];
char arg[100];
char name[IMC_NAME_LENGTH];
const char *p;
int r;
imc_char_data ch;
int i;
/* set up */
for (i=0; i<20; i++)
if (clients[i].fd==-1)
break;
if (i==20)
{
imc_log("runclient: no free clients?!");
close(clientfd);
return;
}
/* read the request */
alarm(5);
/* we're fucked if we don't have EINTR, but too bad */
r=read(clientfd, buf, 1000);
if (r<0)
{
alarm(0);
#ifndef NO_EINTR
if (errno!=EINTR)
#endif
imc_logerror("read");
return;
}
alarm(0);
buf[r--]=0;
while (r>=0 && !isalnum(buf[r]))
buf[r--]=0;
p=imc_getarg(buf, arg, 100);
imc_getarg(p, name, IMC_NAME_LENGTH);
if (!strcasecmp(arg, "who") || !strcasecmp(arg, "info"))
{
clients[i].fd=clientfd;
clients[i].id=id;
clients[i].length=0;
clients[i].received=0;
clients[i].reply=NULL;
freeclients--;
ch.wizi=ch.invis=ch.level=0;
sprintf(ch.name, "%d", id);
id++;
imc_send_who(&ch, name, buf);
}
else if (!strcasecmp(arg, "list"))
{
sendlist(clientfd);
close(clientfd);
}
else
{
close(clientfd);
}
}
void main(int argc, char *argv[])
{
int fd;
struct sockaddr_un sa;
int r;
int maxfd;
if (argc<2)
{
fprintf(stderr, "No socket path specified!\n");
exit(1);
}
signal(SIGPIPE, SIG_IGN);
imc_startup(CONFIG);
if ((imc_active < IA_UP) || (imc_lock_file < 0))
{
imc_logstring("giving up..");
/* imc failed to start up, or there's another process on this config */
imc_shutdown();
exit(0);
}
/* *whap self* why the hell did I have these reversed before? */
fd=socket(AF_UNIX, SOCK_STREAM, 0);
if (fd<0)
{
imc_lerror("CGI socket creation");
exit(1);
}
sa.sun_family=AF_UNIX;
strcpy(sa.sun_path, argv[1]);
unlink(argv[1]); /* toast any old sockets */
if (bind(fd, (struct sockaddr *)&sa, sizeof(sa))<0)
{
imc_lerror("CGI socket bind");
exit(1);
}
if (listen(fd, 5)<0)
{
imc_lerror("CGI socket listen");
exit(1);
}
for (r=0; r<20; r++)
clients[r].fd=-1;
id=1;
freeclients=20;
/* make the socket world-read/write/exec-able */
chmod(argv[1], 0777);
while(1)
{
int size;
fd_set in_set, out_set, exc_set;
struct timeval tv;
int i;
/* listen for requests */
FD_ZERO(&in_set);
FD_ZERO(&out_set);
FD_ZERO(&exc_set);
maxfd=-1;
if (freeclients)
{
FD_SET(fd, &in_set);
maxfd=fd;
}
for (r=0; r<20; r++)
if (clients[r].fd!=-1)
{
FD_SET(clients[r].fd, &in_set);
if (clients[r].fd > maxfd)
maxfd=clients[r].fd;
}
tv.tv_sec=imc_get_max_timeout();
tv.tv_usec=0;
maxfd=imc_fill_fdsets(maxfd, &in_set, &out_set, &exc_set);
#if NO_EINTR
i=select(maxfd+1, &in_set, &out_set, &exc_set, &tv);
#else
while ((i=select(maxfd+1, &in_set, &out_set, &exc_set, &tv))<0 &&
errno==EINTR)
;
#endif
if (i<0)
{
imc_lerror("select");
exit(0);
}
imc_idle_select(&in_set, &out_set, &exc_set, time(NULL));
if (FD_ISSET(fd, &in_set))
{
size=sizeof(sa);
r=accept(fd, (struct sockaddr *)&sa, &size);
if (r<0)
imc_lerror("CGI socket accept");
else
runclient(r);
}
for (r=0; r<20; r++)
if (clients[r].fd!=-1 && FD_ISSET(clients[r].fd, &in_set))
{
char dummy[100];
if (read(clients[r].fd, dummy, 100)<=0)
{
close(clients[r].fd);
clients[r].fd=-1;
free_reply(clients[r].reply);
freeclients++;
}
}
}
/* never reached */
}