+Thu Aug 6 19:25:01 EDT 1998 Art Cancro <ajc@uncnsrd.mt-kisco.ny.us>
+ * Got the CitadelAPI library to the point where the server can start
+ up an extension, and the extension will connect to the server, do
+ some initialization, call a user-supplied CtdlMain(), and exit. Also
+ hacked together a _temporary_ form of the new EXTN server command.
+
Wed Aug 5 23:02:22 EDT 1998 Art Cancro <ajc@uncnsrd.mt-kisco.ny.us>
* Second attempt at getting the server API started. Now it runs
outside of the server and builds a connection.
client: citadel whobbs
-server: citserver setup
+server: citserver setup citadelapi.a
utils: aidepost netmailer netproc netsetup msgform \
readlog rcit stats sysoputil citmail netpoll mailinglist userlist
#
citadel: ipc_c_tcp.o citadel.o rooms.o routines.o routines2.o messages.o \
- commands.o client_chat.o
+ commands.o client_chat.o serv_info.o
$(CC) $(CFLAGS) ipc_c_tcp.o citadel.o rooms.o routines.o routines2.o \
- messages.o commands.o client_chat.o $(LFLAGS) -o citadel
+ messages.o commands.o client_chat.o serv_info.o $(LFLAGS) -o citadel
netpoll: netpoll.c config.o ipc_c_tcp.o
$(CC) $(CFLAGS) netpoll.c config.o ipc_c_tcp.o $(LFLAGS) -o netpoll
client_chat.o: client_chat.c citadel.h
$(CC) -O $(CFLAGS) -c client_chat.c
+serv_info.o: serv_info.c citadel.h
+ $(CC) -O $(CFLAGS) -c serv_info.c
+
#
#
sysdep.o: sysdep.c citadel.h
$(CC) -O $(CFLAGS) -D_REENTRANT -c sysdep.c
+citadelapi.a: citadelapi.o ipc_c_tcp.o serv_info.o
+ $(AR) r citadelapi.a citadelapi.o ipc_c_tcp.o serv_info.o
+ $(RANLIB) citadelapi.a
+
+citadelapi.o: citadelapi.c citadel.h
+ $(CC) -O $(CFLAGS) -D_REENTRANT -c citadelapi.c
+
aidepost: aidepost.c config.o citadel.h
$(CC) -O $(CFLAGS) aidepost.c config.o $(LFLAGS) -o aidepost
int editor_pid = (-1);
char fullname[32];
jmp_buf nextbuf;
-struct serv_info serv_info; /* Info on the server connected */
+struct CtdlServInfo serv_info; /* Info on the server connected */
int screenwidth;
int screenheight;
unsigned room_flags;
}
-/*
- * get info about the server we've connected to
- */
-void get_serv_info() {
- char buf[256];
- int a;
-
- /* fetch info */
- serv_puts("INFO");
- serv_gets(buf);
- if (buf[0]!='1') return;
-
- a = 0;
- while(serv_gets(buf), strcmp(buf,"000")) {
- switch(a) {
- case 0: serv_info.serv_pid = atoi(buf);
- break;
- case 1: strcpy(serv_info.serv_nodename,buf);
- break;
- case 2: strcpy(serv_info.serv_humannode,buf);
- break;
- case 3: strcpy(serv_info.serv_fqdn,buf);
- break;
- case 4: strcpy(serv_info.serv_software,buf);
- break;
- case 5: serv_info.serv_rev_level = atoi(buf);
- break;
- case 6: strcpy(serv_info.serv_bbs_city,buf);
- break;
- case 7: strcpy(serv_info.serv_sysadm,buf);
- break;
- case 9: strcpy(serv_info.serv_moreprompt,buf);
- break;
- case 10: serv_info.serv_ok_floors = atoi(buf);
- break;
- }
- ++a;
- }
-
- /* be nice and identify ourself to the server */
- sprintf(buf,"IDEN %d|%d|%d|%s|",
- SERVER_TYPE,0,REV_LEVEL,
- (server_is_local ? "(local)" : CITADEL));
- locate_host(&buf[strlen(buf)]); /* append to the end */
- serv_puts(buf);
- serv_gets(buf); /* we don't care about the result code */
-
- }
-
/*
* grab assorted info about the user...
*/
}
+
+/*
+ * get info about the server we've connected to
+ */
+void get_serv_info() {
+ char buf[256];
+
+ CtdlInternalGetServInfo(&serv_info);
+
+ /* be nice and identify ourself to the server */
+ sprintf(buf,"IDEN %d|%d|%d|%s|",
+ SERVER_TYPE,0,REV_LEVEL,
+ (server_is_local ? "local" : CITADEL));
+ locate_host(&buf[strlen(buf)]); /* append to the end */
+ serv_puts(buf);
+ serv_gets(buf); /* we don't care about the result code */
+ }
+
+
+
+
+
/*
* Display list of users currently logged on to the server
*/
#include "sysdep.h"
#include "sysconfig.h"
#include "ipcdef.h"
-#define CITADEL "Citadel/UX DR5.03"
+#define CITADEL "Citadel/UX DR19980806"
#define REV_LEVEL 503
#define SERVER_TYPE 0 /* zero for stock Citadel/UX; other developers please
obtain SERVER_TYPE codes for your implementations */
--- /dev/null
+#include <stdlib.h>
+#include <unistd.h>
+#include <stdio.h>
+#include <time.h>
+#include <ctype.h>
+#include <string.h>
+#include <errno.h>
+#include "citadel.h"
+
+struct CtdlServerHandle CtdlAppHandle;
+struct CtdlServInfo CtdlAppServInfo;
+void CtdlMain();
+
+void logoff(exitcode) {
+ exit(exitcode);
+ }
+
+/*
+ * Programs linked against the Citadel server extension library need to
+ * be called with the following arguments:
+ * 0 - program name (as always)
+ * 1 - server address (usually 127.0.0.1)
+ * 2 - server port number
+ * 3 - internal program secret
+ * 4 - user name
+ * 5 - user password
+ * 6 - initial room
+ * 7 - associated client session
+ *
+ */
+
+main(argc, argv)
+int argc;
+char *argv[]; {
+ int a;
+ char buf[256];
+
+ /* We're really not interested in stdio */
+ close(0);
+ close(1);
+ close(2);
+
+ /* Bail out if someone tries to run this thing manually */
+ if (argc < 3) exit(1);
+
+ /* Zeroing out the server handle neatly sets the values of
+ * CtdlAppHandle to sane default values
+ */
+ bzero(&CtdlAppHandle, sizeof(struct CtdlServerHandle));
+
+ /* Now parse the command-line arguments fed to us by the server */
+ for (a=0; a<argc; ++a) switch(a) {
+ case 1: strcpy(CtdlAppHandle.ServerAddress, argv[a]);
+ break;
+ case 2: CtdlAppHandle.ServerPort = atoi(argv[a]);
+ break;
+ case 3: strcpy(CtdlAppHandle.ipgmSecret, argv[a]);
+ break;
+ case 4: strcpy(CtdlAppHandle.UserName, argv[a]);
+ break;
+ case 5: strcpy(CtdlAppHandle.Password, argv[a]);
+ break;
+ case 6: strcpy(CtdlAppHandle.InitialRoom, argv[a]);
+ break;
+ case 7: CtdlAppHandle.AssocClientSession = atoi(argv[a]);
+ break;
+ }
+
+ /* Connect to the server */
+ argc = 3;
+ attach_to_server(argc, argv);
+ serv_gets(buf);
+ if (buf[0] != '2') exit(1);
+
+ /* Set up the server environment to our liking */
+
+ CtdlInternalGetServInfo(&CtdlAppServInfo, 0);
+
+ sprintf(buf, "IDEN 0|5|006|CitadelAPI Client");
+ serv_puts(buf);
+ serv_gets(buf);
+
+ if (strlen(CtdlAppHandle.ipgmSecret) > 0) {
+ sprintf(buf, "IPGM %s", CtdlAppHandle.ipgmSecret);
+ serv_puts(buf);
+ serv_gets(buf);
+ }
+
+ if (strlen(CtdlAppHandle.UserName) > 0) {
+ sprintf(buf, "USER %s", CtdlAppHandle.UserName);
+ serv_puts(buf);
+ serv_gets(buf);
+ sprintf(buf, "PASS %s", CtdlAppHandle.Password);
+ serv_puts(buf);
+ serv_gets(buf);
+ }
+
+ sprintf(buf, "GOTO %s", CtdlAppHandle.InitialRoom);
+ serv_puts(buf);
+ serv_gets(buf);
+ if (buf[0] != '2') {
+ serv_puts("GOTO _BASEROOM_");
+ serv_gets(buf);
+ }
+
+ /* Now do the loop. */
+ CtdlMain();
+
+ /* Clean up gracefully and exit. */
+ serv_puts("QUIT");
+ exit(0);
+ }
}
/*
- * Shut down the server
+ * Schedule or cancel a server shutdown
*/
void cmd_scdn(char *argbuf)
{
cprintf("%d %d\n", OK, ScheduledShutdown);
}
+
+/*
+ * Run a server extension (FIX FIX FIX initial hack; polish this up)
+ */
+void cmd_extn(char *argbuf) {
+ char ExtensionName[256];
+ int is_ipgm;
+ int pid;
+ char portstr[16];
+ char ipgm[32];
+ char sess[16];
+
+
+ extract(ExtensionName, argbuf, 0);
+ is_ipgm = extract_int(argbuf, 1);
+
+ pid = fork();
+ lprintf(9, "fork() returned %d\n", pid);
+ if (pid < 0) {
+ cprintf("%d fork failed: %s\n",
+ ERROR + INTERNAL_ERROR,
+ strerror(errno));
+ return;
+ }
+ else if (pid == 0) {
+
+ sprintf(portstr, "%d", config.c_port_number);
+
+ if (is_ipgm)
+ sprintf(ipgm, "%d", config.c_ipgm_secret);
+ else
+ strcpy(ipgm, "");
+
+ sprintf(sess, "%d", CC->cs_pid);
+
+ execlp(ExtensionName, ExtensionName,
+ "localhost", /* server address */
+ portstr, /* port number */
+ ipgm, /* ipgm secret */
+ CC->usersupp.fullname, /* user name */
+ CC->usersupp.password, /* password */
+ CC->quickroom.QRname, /* current room */
+ sess, /* assoc session id */
+ NULL);
+
+ lprintf(9, "exec() failed: %s\n", strerror(errno));
+ exit(1);
+ }
+ else {
+ cprintf("%d Ok\n", OK);
+ }
+ }
+
+
+
+
/*
* main context loop
*/
else if (!strncasecmp(cmdbuf, "RCHG", 4)) {
cmd_rchg(&cmdbuf[5]);
}
+ else if (!strncasecmp(cmdbuf, "EXTN", 4)) {
+ cmd_extn(&cmdbuf[5]);
+ }
else {
cprintf("%d Unrecognized or unsupported command.\n",
ERROR);
extern unsigned room_flags;
extern char room_name[];
-extern struct serv_info serv_info;
+extern struct CtdlServInfo serv_info;
extern char axlevel;
extern char is_room_aide;
extern unsigned userflags;
u_long inet_addr();
-void timeout() {
+void CtdlInternalTCPtimeout() {
printf("\rConnection timed out.\n");
logoff(3);
}
-int connectsock(host,service,protocol)
+int CtdlInternalTCPConnectSock(host,service,protocol)
char *host;
char *service;
char *protocol; {
}
- signal(SIGALRM,timeout);
+ signal(SIGALRM,CtdlInternalTCPtimeout);
alarm(30);
if (connect(s,(struct sockaddr *)&sin,sizeof(sin))<0) {
* convert service and host entries into a six-byte numeric in the format
* expected by a SOCKS v4 server
*/
-void numericize(buf,host,service,protocol)
+void CtdlInternalTCPnumericize(buf,host,service,protocol)
unsigned char buf[];
char *host;
char *service;
/* if not using a SOCKS proxy server, make the connection directly */
if (strlen(socks4)==0) {
- serv_sock = connectsock(cithost,citport,"tcp");
+ serv_sock = CtdlInternalTCPConnectSock(cithost,citport,"tcp");
return;
}
/* if using SOCKS, connect first to the proxy... */
- serv_sock = connectsock(socks4,"1080","tcp");
+ serv_sock = CtdlInternalTCPConnectSock(socks4,"1080","tcp");
printf("Connected to SOCKS proxy at %s.\n",socks4);
printf("Attaching to server...\r");
fflush(stdout);
1); /* method = connect */
serv_write(buf,2);
- numericize(buf,cithost,citport,"tcp");
+ CtdlInternalTCPnumericize(buf,cithost,citport,"tcp");
serv_write(buf,6); /* port and address */
p = (struct passwd *) getpwuid(getuid());
#define NO_SUCH_SYSTEM 73
#define ALREADY_EXISTS 74
-struct serv_info {
+struct CtdlServInfo {
int serv_pid;
char serv_nodename[32];
char serv_humannode[64];
void serv_puts();
void serv_gets();
+
+struct CtdlServerHandle {
+ char ServerAddress[64];
+ int ServerPort;
+ char ipgmSecret[32];
+ char UserName[32];
+ char Password[32];
+ char InitialRoom[32];
+ int AssocClientSession;
+ };
+
extern char room_name[];
extern unsigned room_flags;
extern long highest_msg_read;
-extern struct serv_info serv_info;
+extern struct CtdlServInfo serv_info;
extern char temp[];
extern char temp2[];
extern int screenwidth;
extern unsigned userflags;
extern char *axdefs[7];
extern char sigcaught;
-extern struct serv_info serv_info;
+extern struct CtdlServInfo serv_info;
extern char rc_floor_mode;
int struncmp(lstr,rstr,len)
--- /dev/null
+/*
+ * serv_info.c
+ *
+ * The CtdlGetServerInfo() function is useful for
+ */
+
+#include <stdlib.h>
+#include <unistd.h>
+#include <stdio.h>
+#include <time.h>
+#include <ctype.h>
+#include <string.h>
+#include <errno.h>
+#include "citadel.h"
+
+void CtdlInternalGetServInfo(struct CtdlServInfo *infobuf) {
+ char buf[256];
+ int a;
+
+ /* fetch info */
+ serv_puts("INFO");
+ serv_gets(buf);
+ if (buf[0]!='1') return;
+
+ a = 0;
+ while(serv_gets(buf), strcmp(buf,"000")) {
+ switch(a) {
+ case 0: infobuf->serv_pid = atoi(buf);
+ break;
+ case 1: strcpy(infobuf->serv_nodename,buf);
+ break;
+ case 2: strcpy(infobuf->serv_humannode,buf);
+ break;
+ case 3: strcpy(infobuf->serv_fqdn,buf);
+ break;
+ case 4: strcpy(infobuf->serv_software,buf);
+ break;
+ case 5: infobuf->serv_rev_level = atoi(buf);
+ break;
+ case 6: strcpy(infobuf->serv_bbs_city,buf);
+ break;
+ case 7: strcpy(infobuf->serv_sysadm,buf);
+ break;
+ case 9: strcpy(infobuf->serv_moreprompt,buf);
+ break;
+ case 10: infobuf->serv_ok_floors = atoi(buf);
+ break;
+ }
+ ++a;
+ }
+
+ }
+
+++ /dev/null
-#include <stdlib.h>
-#include <unistd.h>
-#include <stdio.h>
-#include <time.h>
-#include <ctype.h>
-#include <string.h>
-#include <errno.h>
-#include "citadel.h"
-
-struct CtdlServerHandle {
- char ServerAddress[64];
- int ServerPort;
- char ipgmSecret[32];
- char UserName[32];
- char Password[32];
- char InitialRoom[32];
- int AssocClientSession;
- };
-
-struct CtdlServerHandle CtdlMyHandle;
-
-
-void logoff(exitcode) {
- exit(exitcode);
- }
-
-/*
- * Programs linked against the Citadel server extension library need to
- * be called with the following arguments:
- * 0 - program name (as always)
- * 1 - server address (usually 127.0.0.1)
- * 2 - server port number
- * 3 - internal program secret
- * 4 - user name
- * 5 - user password
- * 6 - initial room
- * 7 - associated client session
- *
- */
-
-main(argc, argv)
-int argc;
-char *argv[]; {
- int a;
- char buf[256];
-
- /* We're really not interested in stdio */
- close(0);
- close(1);
- close(2);
-
- /* Bail out if someone tries to run this thing manually */
- if (argc < 3) exit(1);
-
- /* Zeroing out the server handle neatly sets the values of
- * CtdlMyHandle to sane default values
- */
- bzero(&CtdlMyHandle, sizeof(struct CtdlServerHandle));
-
- /* Now parse the command-line arguments fed to us by the server */
- for (a=0; a<argc; ++a) switch(a) {
- case 1: strcpy(CtdlMyHandle.ServerAddress, argv[a]);
- break;
- case 2: CtdlMyHandle.ServerPort = atoi(argv[a]);
- break;
- case 3: strcpy(CtdlMyHandle.ipgmSecret, argv[a]);
- break;
- case 4: strcpy(CtdlMyHandle.UserName, argv[a]);
- break;
- case 5: strcpy(CtdlMyHandle.Password, argv[a]);
- break;
- case 6: strcpy(CtdlMyHandle.InitialRoom, argv[a]);
- break;
- case 7: CtdlMyHandle.AssocClientSession = atoi(argv[a]);
- break;
- }
-
- /* Connect to the server */
- argc = 3;
- attach_to_server(argc, argv);
- serv_gets(buf);
- if (buf[0] != '2') exit(1);
-
- /* Set up the server environment to our liking */
-
- sprintf(buf, "IDEN 0|5|006|CitadelAPI Client");
- serv_puts(buf);
- serv_gets(buf);
-
- if (strlen(CtdlMyHandle.ipgmSecret) > 0) {
- sprintf(buf, "IPGM %s", CtdlMyHandle.ipgmSecret);
- serv_puts(buf);
- serv_gets(buf);
- }
-
- if (strlen(CtdlMyHandle.UserName) > 0) {
- sprintf(buf, "USER %s", CtdlMyHandle.UserName);
- serv_puts(buf);
- serv_gets(buf);
- sprintf(buf, "PASS %s", CtdlMyHandle.Password);
- serv_puts(buf);
- serv_gets(buf);
- }
-
- sprintf(buf, "GOTO %s", CtdlMyHandle.InitialRoom);
- serv_puts(buf);
- serv_gets(buf);
- if (buf[0] != '2') {
- serv_puts("GOTO _BASEROOM_");
- serv_gets(buf);
- }
-
- /* Now do the loop. */
- sleep(10);
-
- /* Clean up gracefully and exit. */
- serv_puts("QUIT");
- exit(0);
- }
+ WRITING A SERVER-SIDE APPLICATION
+ ---------------------------------
+
+ Server-side applications written to the CitadelAPI may be written in any
+language, as long as the "citadelapi" library is linked in. Unlike normal
+user-mode programs, you must not declare a main() function, and there will be
+no stdin/stdout/stderr available. Instead, you must declare this:
+
+ void CtdlMain() {
+ /* your program's main loop goes here */
+ }
+
+ When a server-side application is started, the main() loop inside the
+CitadelAPI is invoked. This will perform all of the necessary initialization
+(attach to the server, authenticate, identify itself, etc.) and then call
+your program's CtdlMain() loop.
+
+ By the time CtdlMain() receives control, there will be two public symbols
+you can access, which point to data structures containing various information
+that may be useful to the program:
+
+ extern struct CtdlServerHandle CtdlAppHandle;
+ extern struct CtdlServInfo CtdlAppServInfo;
+
+ CtdlAppHandle contains various information about how and why this program is
+connected to the server. CtdlAppSrvInfo contains various static information
+about the server the program is connected to. Both CtdlServerHandle and
+CtdlServInfo are defined in the standard header files.
+
+
+
ERROR HANDLING
--------------