]> code.citadel.org Git - citadel.git/commitdiff
Moved some files around into sub dirs to clean up the base dir a litle.
authorDave West <davew@uncensored.citadel.org>
Sat, 24 Oct 2009 15:35:16 +0000 (15:35 +0000)
committerDave West <davew@uncensored.citadel.org>
Sat, 24 Oct 2009 15:35:16 +0000 (15:35 +0000)
75 files changed:
citadel/Makefile.in
citadel/aidepost.c [deleted file]
citadel/base64.c [deleted file]
citadel/chkpw.c [deleted file]
citadel/chkpwd.c [deleted file]
citadel/citadel.c [deleted file]
citadel/citadel_dirs.c [deleted file]
citadel/citadel_dirs.h [deleted file]
citadel/citadel_ipc.c [deleted file]
citadel/citadel_ipc.h [deleted file]
citadel/citmail.c [deleted file]
citadel/client_chat.c [deleted file]
citadel/client_chat.h [deleted file]
citadel/client_passwords.c [deleted file]
citadel/client_passwords.h [deleted file]
citadel/commands.c [deleted file]
citadel/commands.h [deleted file]
citadel/ctdlmigrate.c [deleted file]
citadel/getmail.c [deleted file]
citadel/include/citadel_dirs.h [new file with mode: 0644]
citadel/include/citadel_ipc.h [new file with mode: 0644]
citadel/include/commands.h [new file with mode: 0644]
citadel/ipc_c_tcp.c [deleted file]
citadel/messages.c [deleted file]
citadel/messages.h [deleted file]
citadel/msgform.c [deleted file]
citadel/rooms.c [deleted file]
citadel/rooms.h [deleted file]
citadel/routines.c [deleted file]
citadel/routines.h [deleted file]
citadel/routines2.c [deleted file]
citadel/routines2.h [deleted file]
citadel/screen.c [deleted file]
citadel/screen.h [deleted file]
citadel/sendcommand.c [deleted file]
citadel/server_main.c
citadel/setup.c [deleted file]
citadel/stress.c [deleted file]
citadel/textclient/citadel.c [new file with mode: 0644]
citadel/textclient/client_chat.c [new file with mode: 0644]
citadel/textclient/client_chat.h [new file with mode: 0644]
citadel/textclient/client_passwords.c [new file with mode: 0644]
citadel/textclient/client_passwords.h [new file with mode: 0644]
citadel/textclient/commands.c [new file with mode: 0644]
citadel/textclient/messages.c [new file with mode: 0644]
citadel/textclient/messages.h [new file with mode: 0644]
citadel/textclient/rooms.c [new file with mode: 0644]
citadel/textclient/rooms.h [new file with mode: 0644]
citadel/textclient/routines.c [new file with mode: 0644]
citadel/textclient/routines.h [new file with mode: 0644]
citadel/textclient/routines2.c [new file with mode: 0644]
citadel/textclient/routines2.h [new file with mode: 0644]
citadel/textclient/screen.c [new file with mode: 0644]
citadel/textclient/screen.h [new file with mode: 0644]
citadel/textclient/tuiconfig.c [new file with mode: 0644]
citadel/textclient/tuiconfig.h [new file with mode: 0644]
citadel/tuiconfig.c [deleted file]
citadel/tuiconfig.h [deleted file]
citadel/userlist.c [deleted file]
citadel/utillib/citadel_dirs.c [new file with mode: 0644]
citadel/utillib/citadel_ipc.c [new file with mode: 0644]
citadel/utils/aidepost.c [new file with mode: 0644]
citadel/utils/base64.c [new file with mode: 0644]
citadel/utils/chkpw.c [new file with mode: 0644]
citadel/utils/chkpwd.c [new file with mode: 0644]
citadel/utils/citmail.c [new file with mode: 0644]
citadel/utils/ctdlmigrate.c [new file with mode: 0644]
citadel/utils/getmail.c [new file with mode: 0644]
citadel/utils/msgform.c [new file with mode: 0644]
citadel/utils/sendcommand.c [new file with mode: 0644]
citadel/utils/setup.c [new file with mode: 0644]
citadel/utils/stress.c [new file with mode: 0644]
citadel/utils/userlist.c [new file with mode: 0644]
citadel/utils/whobbs.c [new file with mode: 0644]
citadel/whobbs.c [deleted file]

index 0d7c57fb845da9c61426d1414fc171fea628a592..4c9d7e6a7d0bcea7a029bdc3744bbfbfb93e6a34 100644 (file)
@@ -72,18 +72,21 @@ YACC=@YACC@
 # End configuration section
 
 
-SOURCES=aidepost.c auth.c base64.c chkpwd.c chkpw.c citadel.c citadel_ipc.c \
-       citmail.c citserver.c client_chat.c client_passwords.c \
-       clientsocket.c commands.c config.c control.c $(DATABASE) \
+SOURCES=utils/aidepost.c utils/stress.c utils/whobbs.c utils/citmail.c \
+       utils/setup.c utils/msgform.c utils/chkpw.c \
+       utils/sendcommand.c utils/getmail.c utils/userlist.c \
+       utils/ctdlmigrate.c utils/base64.c utils/chkpwd.c \
+       utillib/citadel_ipc.c utillib/citadel_dirs.c utillib/ipc_c_tcp.c \
+       textclient/client_chat.c textclient/client_passwords.c \
+       textclient/commands.c textclient/messages.c textclient/rooms.c \
+       textclient/routines.c textclient/routines2.c textclient/tuiconfig.c \
+       textclient/citadel.c textclient/screen.c \
+       citserver.c clientsocket.c config.c control.c $(DATABASE) \
        domain.c serv_extensions.c file_ops.c genstamp.c getutline.c \
        housekeeping.c ical_dezonify.c internet_addressing.c ecrash.c \
-       ipc_c_tcp.c locate_host.c md5.c messages.c  \
-       msgbase.c msgform.c parsedate.c policy.c \
-       room_ops.c rooms.c routines.c routines2.c tuiconfig.c euidindex.c \
-       screen.c sendcommand.c getmail.c \
-       server_main.c setup.c snprintf.c ldap.c \
-       stress.c support.c sysdep.c user_ops.c userlist.c \
-       whobbs.c journaling.c citadel_dirs.c threads.c ctdlmigrate.c
+       locate_host.c md5.c auth.c msgbase.c parsedate.c policy.c \
+       room_ops.c euidindex.c server_main.c snprintf.c ldap.c \
+       support.c sysdep.c user_ops.c journaling.c threads.c 
 
 
 include Make_sources
@@ -108,13 +111,19 @@ server: $(SERVER_TARGETS) $(SERV_MODULES)
 
 utils: $(UTIL_TARGETS) $(UTILBIN_TARGETS)
 
-citadel$(EXEEXT): citadel.o citadel_ipc.o client_chat.o client_passwords.o \
-       commands.o ipc_c_tcp.o md5.o messages.o rooms.o routines.o \
-       routines2.o tuiconfig.o screen.o citadel_dirs.o ecrash.o $(LIBOBJS)
-       $(CC) citadel.o citadel_ipc.o client_chat.o client_passwords.o \
-       commands.o ipc_c_tcp.o md5.o messages.o rooms.o routines.o \
-       routines2.o tuiconfig.o screen.o citadel_dirs.o ecrash.o $(LIBOBJS) \
-       $(LDFLAGS) -o citadel $(LIBS)
+citadel$(EXEEXT): textclient/citadel.o utillib/citadel_ipc.o \
+               textclient/client_chat.o textclient/client_passwords.o \
+               textclient/commands.o utillib/ipc_c_tcp.o md5.o \
+               textclient/messages.o textclient/rooms.o textclient/routines.o \
+               textclient/routines2.o textclient/tuiconfig.o \
+               textclient/screen.o utillib/citadel_dirs.o ecrash.o $(LIBOBJS)
+       $(CC) textclient/citadel.o utillib/citadel_ipc.o \
+               textclient/client_chat.o textclient/client_passwords.o \
+               textclient/commands.o utillib/ipc_c_tcp.o md5.o \
+               textclient/messages.o textclient/rooms.o textclient/routines.o \
+               textclient/routines2.o textclient/tuiconfig.o \
+               textclient/screen.o utillib/citadel_dirs.o ecrash.o $(LIBOBJS) \
+               $(LDFLAGS) -o citadel $(LIBS)
 
 .y.c:
        $(YACC) $(YFLAGS) $<
@@ -131,7 +140,7 @@ Make_modules: modules_init.c
 
 modules_upgrade.c: modules_init.c
 
-SERV_OBJS = server_main.o \
+SERV_OBJS = server_main.o utillib/citadel_dirs.o\
        user_ops.o citserver.o sysdep.o serv_extensions.o \
        $(DATABASE:.c=.o) domain.o \
        control.o policy.o config.o support.o room_ops.o \
@@ -148,48 +157,65 @@ citserver$(EXEEXT): $(SERV_OBJS)
 .c.o:
        $(CC) $(CFLAGS) $(CPPFLAGS) $(DEFS) -c $< -o $@
 
-aidepost$(EXEEXT): aidepost.o config.o
-       $(CC) aidepost.o config.o citadel_dirs.o $(LDFLAGS) -o aidepost$(EXEEXT) $(LIBS)
+aidepost$(EXEEXT): utils/aidepost.o config.o
+       $(CC) utils/aidepost.o config.o utillib/citadel_dirs.o \
+               $(LDFLAGS) -o aidepost$(EXEEXT) $(LIBS)
 
-citmail$(EXEEXT): citmail.o citadel_dirs.o
-       $(CC) citmail.o citadel_dirs.o $(LDFLAGS) -o citmail$(EXEEXT) $(LIBS)
+citmail$(EXEEXT): utils/citmail.o utillib/citadel_dirs.o
+       $(CC) utils/citmail.o utillib/citadel_dirs.o \
+               $(LDFLAGS) -o citmail$(EXEEXT) $(LIBS)
 
 # setup does need LIBS defined, because it uses network functions which are in -lsocket -lnsl on Solaris.
-setup$(EXEEXT): setup.o citadel_dirs.o
-       $(CC) setup.o citadel_dirs.o $(LDFLAGS) -o setup$(EXEEXT) $(LIBS) $(SETUP_LIBS)
-
-ctdlmigrate$(EXEEXT): ctdlmigrate.o citadel_dirs.o
-       $(CC) ctdlmigrate.o citadel_dirs.o $(LDFLAGS) -o ctdlmigrate$(EXEEXT) $(LIBS)
-
-chkpwd$(EXEEXT): chkpwd.o auth.o
-       $(CC) chkpwd.o auth.o $(LDFLAGS) -o chkpwd$(EXEEXT) $(chkpwd_LIBS)
-
-chkpw$(EXEEXT): chkpw.o auth.o citadel_dirs.o
-       $(CC) chkpw.o auth.o citadel_dirs.o $(LDFLAGS) -o chkpw$(EXEEXT) $(chkpwd_LIBS)
-
-whobbs$(EXEEXT): whobbs.o ipc_c_tcp.o citadel_ipc.o citadel_dirs.o $(LIBOBJS)
-       $(CC) whobbs.o ipc_c_tcp.o citadel_ipc.o  citadel_dirs.o $(LIBOBJS) $(LDFLAGS) -o whobbs$(EXEEXT) $(LIBS)
-
-stress$(EXEEXT): stress.o ipc_c_tcp.o citadel_ipc.o citadel_dirs.o $(LIBOBJS)
-       $(CC) stress.o ipc_c_tcp.o citadel_ipc.o citadel_dirs.o $(LIBOBJS) $(LDFLAGS) -o stress$(EXEEXT) $(LIBS)
-
-sendcommand$(EXEEXT): sendcommand.o ipc_c_tcp.o citadel_ipc.o config.o  $(LIBOBJS)
-       $(CC) sendcommand.o ipc_c_tcp.o citadel_ipc.o config.o  \
-        citadel_dirs.o $(LIBOBJS) $(LDFLAGS) -o sendcommand$(EXEEXT) $(LIBS)
-
-getmail$(EXEEXT): getmail.o ipc_c_tcp.o citadel_ipc.o config.o  $(LIBOBJS)
-       $(CC) getmail.o ipc_c_tcp.o citadel_ipc.o config.o  \
-        citadel_dirs.o $(LIBOBJS) $(LDFLAGS) -o getmail$(EXEEXT) $(LIBS)
-
-base64$(EXEEXT): base64.o
-       $(CC) base64.o $(LDFLAGS) -o base64$(EXEEXT)
-
-userlist$(EXEEXT): userlist.o ipc_c_tcp.o citadel_ipc.o citadel_dirs.o $(LIBOBJS)
-       $(CC) userlist.o ipc_c_tcp.o citadel_ipc.o citadel_dirs.o \
-       $(LIBOBJS) $(LDFLAGS) -o userlist$(EXEEXT) $(LIBS)
-
-msgform$(EXEEXT): msgform.o
-       $(CC) msgform.o $(LDFLAGS) -o msgform$(EXEEXT)
+setup$(EXEEXT): utils/setup.o utillib/citadel_dirs.o
+       $(CC) utils/setup.o utillib/citadel_dirs.o \
+               $(LDFLAGS) -o setup$(EXEEXT) $(LIBS) $(SETUP_LIBS)
+
+ctdlmigrate$(EXEEXT): utils/ctdlmigrate.o utillib/citadel_dirs.o
+       $(CC) utils/ctdlmigrate.o utillib/citadel_dirs.o \
+               $(LDFLAGS) -o ctdlmigrate$(EXEEXT) $(LIBS)
+
+chkpwd$(EXEEXT): utils/chkpwd.o auth.o
+       $(CC) utils/chkpwd.o auth.o $(LDFLAGS) -o chkpwd$(EXEEXT) $(chkpwd_LIBS)
+
+chkpw$(EXEEXT): utils/chkpw.o auth.o utillib/citadel_dirs.o
+       $(CC) utils/chkpw.o auth.o utillib/citadel_dirs.o \
+               $(LDFLAGS) -o chkpw$(EXEEXT) $(chkpwd_LIBS)
+
+whobbs$(EXEEXT): utils/whobbs.o utillib/ipc_c_tcp.o utillib/citadel_ipc.o \
+               utillib/citadel_dirs.o $(LIBOBJS)
+       $(CC) utils/whobbs.o utillib/ipc_c_tcp.o utillib/citadel_ipc.o \
+               utillib/citadel_dirs.o $(LIBOBJS) \
+               $(LDFLAGS) -o whobbs$(EXEEXT) $(LIBS)
+
+stress$(EXEEXT): utils/stress.o utillib/ipc_c_tcp.o utillib/citadel_ipc.o \
+               utillib/citadel_dirs.o $(LIBOBJS)
+       $(CC) utils/stress.o utillib/ipc_c_tcp.o utillib/citadel_ipc.o \
+               utillib/citadel_dirs.o $(LIBOBJS) \
+               $(LDFLAGS) -o stress$(EXEEXT) $(LIBS)
+
+sendcommand$(EXEEXT): utils/sendcommand.o utillib/ipc_c_tcp.o \
+               utillib/citadel_ipc.o config.o  $(LIBOBJS)
+       $(CC) utils/sendcommand.o utillib/ipc_c_tcp.o \
+               utillib/citadel_ipc.o config.o utillib/citadel_dirs.o \
+               $(LIBOBJS) $(LDFLAGS) -o sendcommand$(EXEEXT) $(LIBS)
+
+getmail$(EXEEXT): utils/getmail.o utillib/ipc_c_tcp.o utillib/citadel_ipc.o \
+               config.o  $(LIBOBJS)
+       $(CC) utils/getmail.o utillib/ipc_c_tcp.o utillib/citadel_ipc.o \
+               config.o utillib/citadel_dirs.o \
+               $(LIBOBJS) $(LDFLAGS) -o getmail$(EXEEXT) $(LIBS)
+
+base64$(EXEEXT): utils/base64.o
+       $(CC) utils/base64.o $(LDFLAGS) -o base64$(EXEEXT)
+
+userlist$(EXEEXT): utils/userlist.o utillib/ipc_c_tcp.o utillib/citadel_ipc.o \
+               utillib/citadel_dirs.o $(LIBOBJS)
+       $(CC) utils/userlist.o utillib/ipc_c_tcp.o utillib/citadel_ipc.o \
+               utillib/citadel_dirs.o \
+               $(LIBOBJS) $(LDFLAGS) -o userlist$(EXEEXT) $(LIBS)
+
+msgform$(EXEEXT): utils/msgform.o
+       $(CC) utils/msgform.o $(LDFLAGS) -o msgform$(EXEEXT)
 
 .PHONY: install-data install-doc install-exec clean cleaner distclean
 
@@ -351,6 +377,9 @@ install-exec-new: all
 
 clean:
        rm -f *.o 
+       rm -f utils/*.o ;\
+       rm -f utillib/*.o ;\
+       rm -f textclient/*.o ;\
        for i in $(srcdir)/modules/* ; do \
                rm -f $$i/*.o ;\
        done
@@ -369,6 +398,9 @@ cleaner: clean
 distclean: cleaner
        find . -name '*~' -o -name '.#*' | xargs rm -f
        rm -f Makefile sysdep.h config.cache config.log config.status *.d 
+       rm -f utils/*.d ;
+       rm -f utillib/*.d ;
+       rm -f textclient/*.d ;
        for i in $(srcdir)/modules/* ; do \
                rm -f $$i/*.d ;\
        done
diff --git a/citadel/aidepost.c b/citadel/aidepost.c
deleted file mode 100644 (file)
index 7b637f5..0000000
+++ /dev/null
@@ -1,168 +0,0 @@
-/*
- * $Id$
- *
- * This is just a little hack to copy standard input to a message in Aide>
- *
- * Copyright (c) 1987-2009 by the citadel.org team
- *
- *  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 3 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; if not, write to the Free Software
- *  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
- */
-
-#include <stdlib.h>
-#include <unistd.h>
-#include <stdio.h>
-
-#if TIME_WITH_SYS_TIME
-# include <sys/time.h>
-# include <time.h>
-#else
-# if HAVE_SYS_TIME_H
-#  include <sys/time.h>
-# else
-#  include <time.h>
-# endif
-#endif
-
-#include <limits.h>
-#include <errno.h>
-#include <string.h>
-#include "citadel.h"
-#include "citadel_dirs.h"
-#include "config.h"
-
-#ifndef HAVE_SNPRINTF
-#include "snprintf.h"
-#endif
-
-
-/*
- * Simplified function to generate a message in our format
- */
-static void ap_make_message(FILE *fp, char *target_room, char *author, char *subject)
-{
-       int a;
-       long bb, cc;
-       time_t now;
-       time(&now);
-       putc(255, fp);
-       putc(MES_NORMAL, fp);
-       putc(1, fp);
-       fprintf(fp, "Proom_aide");
-       putc(0, fp);
-       fprintf(fp, "T%ld", (long)now);
-       putc(0, fp);
-       fprintf(fp, "A%s", author);
-       putc(0, fp);
-       fprintf(fp, "O%s", target_room);
-       putc(0, fp);
-       if (strlen(subject) > 0) {
-               fprintf(fp, "U%s%c", subject, 0);
-       }
-       fprintf(fp, "N%s", NODENAME);
-       putc(0, fp);
-       putc('M', fp);
-       bb = ftell(fp);
-       while (a = getc(stdin), a > 0) {
-               if (a != 8)
-                       putc(a, fp);
-               else {
-                       cc = ftell(fp);
-                       if (cc != bb)
-                               fseek(fp, (-1L), 1);
-               }
-       }
-       putc(0, fp);
-       putc(0, fp);
-       putc(0, fp);
-}
-
-int main(int argc, char **argv)
-{
-       char tempspool[64];
-       char target_room[ROOMNAMELEN];
-       char author[64];
-       char subject[256];
-       FILE *tempfp, *spoolfp;
-       int ch;
-       int i;
-
-       int relh=0;
-       int home=0;
-       char relhome[PATH_MAX]="";
-       char ctdldir[PATH_MAX]=CTDLDIR;
-
-       /* TODO: should we be able to calculate relative dirs? */
-       calc_dirs_n_files(relh, home, relhome, ctdldir, 0);
-
-
-       get_config();
-
-       strcpy(target_room, "Aide");
-       strcpy(author, "Citadel");
-       strcpy(subject, "");
-       for (i=1; i<argc; ++i) {
-               if (!strncasecmp(argv[i], "-r", 2)) {
-                       strncpy(target_room, &argv[i][2], sizeof(target_room));
-                       target_room[sizeof(target_room)-1] = 0;
-               }
-               else if (!strncasecmp(argv[i], "-a", 2)) {
-                       strncpy(author, &argv[i][2], sizeof(author));
-                       author[sizeof(author)-1] = 0;
-               }
-               else if (!strncasecmp(argv[i], "-s", 2)) {
-                       strncpy(subject, &argv[i][2], sizeof(subject));
-                       subject[sizeof(subject)-1] = 0;
-               } else {
-                       fprintf(stderr, "%s: usage: %s "
-                                       "[-rTargetRoom] [-aAuthor] [-sSubject]\n",
-                               argv[0], argv[0]);
-                       exit(1);
-               }
-       }
-
-       snprintf(tempspool, sizeof tempspool,
-                        "%s/ap.%04lx",
-                        ctdl_netin_dir,
-               (long)getpid());
-
-       unlink(tempspool);
-
-       tempfp = fopen(tempspool, "w+b");
-       unlink(tempspool);
-       if (tempfp == NULL) {
-               perror("cannot open temp file");
-               exit(errno);
-       }
-
-       /* Generate a message from stdin */
-       ap_make_message(tempfp, target_room, author, subject);
-
-       /* Copy it to a new temp file in the spool directory */
-       rewind(tempfp);
-
-       spoolfp = fopen(tempspool, "wb");
-       if (spoolfp == NULL) {
-               perror("cannot open spool file");
-               exit(errno);
-       }
-       while (ch = getc(tempfp), (ch >= 0)) {
-               putc(ch, spoolfp);
-       }
-
-       fclose(tempfp);
-       fclose(spoolfp);
-
-       exit(0);
-}
diff --git a/citadel/base64.c b/citadel/base64.c
deleted file mode 100644 (file)
index 388606c..0000000
+++ /dev/null
@@ -1,336 +0,0 @@
-/*
- * $Id$
- *
- * Encode or decode file as MIME base64 (RFC 1341)
- * Public domain by John Walker, August 11 1997
- * Modified slightly for the Citadel system, June 1999
- *
- *  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 3 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; if not, write to the Free Software
- *  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
- */
-
-
-#include <stdio.h>
-#include <stdlib.h>
-#include <ctype.h>
-#include <string.h>
-
-#define TRUE  1
-#define FALSE 0
-
-#define LINELEN 72                   /* Encoded line length (max 76) */
-
-typedef unsigned char byte;          /* Byte type */
-
-FILE *fi;                            /* Input file */
-FILE *fo;                            /* Output file */
-static byte iobuf[256];              /* I/O buffer */
-static int iolen = 0;                /* Bytes left in I/O buffer */
-static int iocp = 256;               /* Character removal pointer */
-static int ateof = FALSE;            /* EOF encountered */
-static byte dtable[256];             /* Encode / decode table */
-static int linelength = 0;           /* Length of encoded output line */
-static char eol[] = "\r\n";           /* End of line sequence */
-static int errcheck = TRUE;          /* Check decode input for errors ? */
-
-/*  INBUF  --  Fill input buffer with data  */
-
-static int inbuf(void)
-{
-    int l;
-
-    if (ateof) {
-       return FALSE;
-    }
-    l = fread(iobuf, 1, sizeof iobuf, fi);     /* Read input buffer */
-    if (l <= 0) {
-       if (ferror(fi)) {
-           exit(1);
-       }
-       ateof = TRUE;
-       return FALSE;
-    }
-    iolen = l;
-    iocp = 0;
-    return TRUE;
-}
-
-/*  INCHAR  -- Return next character from input  */
-
-static int inchar(void)
-{
-    if (iocp >= iolen) {
-       if (!inbuf()) {
-         return EOF;
-       }
-    }
-
-    return iobuf[iocp++];
-}
-
-/*  OCHAR  --  Output an encoded character, inserting line breaks
-              where required.  */
-
-static void ochar(int c)
-{
-    if (linelength >= LINELEN) {
-       if (fputs(eol, fo) == EOF) {
-           exit(1);
-       }
-       linelength = 0;
-    }
-    if (putc(((byte) c), fo) == EOF) {
-       exit(1);
-    }
-    linelength++;
-}
-
-/*  ENCODE  -- Encode binary file into base64.  */
-
-static void encode(void)
-{
-    int i, hiteof = FALSE;
-
-    /* Fill dtable with character encodings.  */
-
-    for (i = 0; i < 26; i++) {
-        dtable[i] = 'A' + i;
-        dtable[26 + i] = 'a' + i;
-    }
-    for (i = 0; i < 10; i++) {
-        dtable[52 + i] = '0' + i;
-    }
-    dtable[62] = '+';
-    dtable[63] = '/';
-
-    while (!hiteof) {
-       byte igroup[3], ogroup[4];
-       int c, n;
-
-       igroup[0] = igroup[1] = igroup[2] = 0;
-       for (n = 0; n < 3; n++) {
-           c = inchar();
-           if (c == EOF) {
-               hiteof = TRUE;
-               break;
-           }
-           igroup[n] = (byte) c;
-       }
-       if (n > 0) {
-           ogroup[0] = dtable[igroup[0] >> 2];
-           ogroup[1] = dtable[((igroup[0] & 3) << 4) | (igroup[1] >> 4)];
-           ogroup[2] = dtable[((igroup[1] & 0xF) << 2) | (igroup[2] >> 6)];
-           ogroup[3] = dtable[igroup[2] & 0x3F];
-
-            /* Replace characters in output stream with "=" pad
-              characters if fewer than three characters were
-              read from the end of the input stream. */
-
-           if (n < 3) {
-                ogroup[3] = '=';
-               if (n < 2) {
-                    ogroup[2] = '=';
-               }
-           }
-           for (i = 0; i < 4; i++) {
-               ochar(ogroup[i]);
-           }
-       }
-    }
-    if (fputs(eol, fo) == EOF) {
-       exit(1);
-    }
-}
-
-/*  INSIG  --  Return next significant input  */
-
-static int insig(void)
-{
-    int c;
-
-    /*CONSTANTCONDITION*/
-    while (TRUE) {
-       c = inchar();
-        if (c == EOF || (c > ' ')) {
-           return c;
-       }
-    }
-    /*NOTREACHED*/
-}
-
-/*  DECODE  -- Decode base64.  */
-
-static void decode(void)
-{
-    int i;
-
-    for (i = 0; i < 255; i++) {
-       dtable[i] = 0x80;
-    }
-    for (i = 'A'; i <= 'Z'; i++) {
-        dtable[i] = 0 + (i - 'A');
-    }
-    for (i = 'a'; i <= 'z'; i++) {
-        dtable[i] = 26 + (i - 'a');
-    }
-    for (i = '0'; i <= '9'; i++) {
-        dtable[i] = 52 + (i - '0');
-    }
-    dtable['+'] = 62;
-    dtable['/'] = 63;
-    dtable['='] = 0;
-
-    /*CONSTANTCONDITION*/
-    while (TRUE) {
-       byte a[4], b[4], o[3];
-
-       for (i = 0; i < 4; i++) {
-           int c = insig();
-
-           if (c == EOF) {
-               if (errcheck && (i > 0)) {
-                    fprintf(stderr, "Input file incomplete.\n");
-                   exit(1);
-               }
-               return;
-           }
-           if (dtable[c] & 0x80) {
-               if (errcheck) {
-                    fprintf(stderr, "Illegal character '%c' in input file.\n", c);
-                   exit(1);
-               }
-               /* Ignoring errors: discard invalid character. */
-               i--;
-               continue;
-           }
-           a[i] = (byte) c;
-           b[i] = (byte) dtable[c];
-       }
-       o[0] = (b[0] << 2) | (b[1] >> 4);
-       o[1] = (b[1] << 4) | (b[2] >> 2);
-       o[2] = (b[2] << 6) | b[3];
-        i = a[2] == '=' ? 1 : (a[3] == '=' ? 2 : 3);
-       if (fwrite(o, i, 1, fo) == EOF) {
-           exit(1);
-       }
-       if (i < 3) {
-           return;
-       }
-    }
-}
-
-/*  USAGE  --  Print how-to-call information.  */
-
-static void usage(char *pname)
-{
-    fprintf(stderr, "%s  --  Encode/decode file as base64.  Call:\n", pname);
-    fprintf(stderr,
-    "            %s [-e[ncode] / -d[ecode]] [-n] [infile] [outfile]\n", pname);
-    fprintf(stderr, "\n");
-    fprintf(stderr, "Options:\n");
-    fprintf(stderr, "           -D         Decode base64 encoded file\n");
-    fprintf(stderr, "           -E         Encode file into base64\n");
-    fprintf(stderr, "           -N         Ignore errors when decoding\n");
-    fprintf(stderr, "           -U         Print this message\n");
-    fprintf(stderr, "\n");
-    fprintf(stderr, "by John Walker\n");
-    fprintf(stderr, "   WWW:    http://www.fourmilab.ch/\n");
-}
-
-/*  Main program  */
-
-int main(int argc, char *argv[])
-{
-    int i, f = 0, decoding = FALSE;
-    char *cp, opt;
-
-    fi = stdin;
-    fo = stdout;
-
-    for (i = 1; i < argc; i++) {
-       cp = argv[i];
-        if (*cp == '-') {
-           opt = *(++cp);
-           if (islower(opt)) {
-               opt = toupper(opt);
-           }
-           switch (opt) {
-
-                case 'D':             /* -D  Decode */
-                   decoding = TRUE;
-                   break;
-
-                case 'E':             /* -E  Encode */
-                   decoding = FALSE;
-                   break;
-
-                case 'N':             /* -N  Suppress error checking */
-                   errcheck = FALSE;
-                   break;
-
-                case 'U':             /* -U  Print how-to-call information */
-                case '?':
-                   usage(argv[0]);
-                   return 0;
-          }
-       } else {
-           switch (f) {
-
-               /** Warning!  On systems which distinguish text mode and
-                   binary I/O (MS-DOS, Macintosh, etc.) the modes in these
-                   open statements will have to be made conditional based
-                   upon whether an encode or decode is being done, which
-                    will have to be specified earlier.  But it's worse: if
-                   input or output is from standard input or output, the 
-                   mode will have to be changed on the fly, which is
-                    generally system and compiler dependent.  'Twasn't me
-                    who couldn't conform to Unix CR/LF convention, so 
-                    don't ask me to write the code to work around
-                    Apple and Microsoft's incompatible standards. **/
-
-               case 0:
-                    if (strcmp(cp, "-") != 0) {
-                        if ((fi = fopen(cp, "r")) == NULL) {
-                            fprintf(stderr, "Cannot open input file %s\n", cp);
-                           return 2;
-                       }
-                   }
-                   f++;
-                   break;
-
-               case 1:
-                    if (strcmp(cp, "-") != 0) {
-                        if ((fo = fopen(cp, "w")) == NULL) {
-                            fprintf(stderr, "Cannot open output file %s\n", cp);
-                           return 2;
-                       }
-                   }
-                   f++;
-                   break;
-
-               default:
-                    fprintf(stderr, "Too many file names specified.\n");
-                   usage(argv[0]);
-                   return 2;
-           }
-       }
-    }
-
-    if (decoding) {
-       decode();
-    } else {
-       encode();
-    }
-    return 0;
-}
diff --git a/citadel/chkpw.c b/citadel/chkpw.c
deleted file mode 100644 (file)
index ce5faaf..0000000
+++ /dev/null
@@ -1,141 +0,0 @@
-/* 
- *
- *
- * Copyright (c) 1987-2009 by the citadel.org team
- *
- *  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 3 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; if not, write to the Free Software
- *  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
- */
-
-#include <errno.h>
-#include <stdlib.h>
-#include <unistd.h>
-#include <stdio.h>
-#include <fcntl.h>
-#include <signal.h>
-#include <pwd.h>
-#include <ctype.h>
-#include <sys/types.h>
-#include <sys/wait.h>
-#include <sys/stat.h>
-#include <unistd.h>
-#include <string.h>
-#include <limits.h>
-#include <dirent.h>
-
-
-#include "citadel.h"
-#include "sysdep.h"
-#include "citadel_dirs.h"
-/* These pipes are used to talk to the chkpwd daemon, which is forked during startup */
-int chkpwd_write_pipe[2];
-int chkpwd_read_pipe[2];
-
-/*
- * Validate a password on the host unix system by talking to the chkpwd daemon
- */
-static int validpw(uid_t uid, const char *pass)
-{
-       char buf[256];
-
-       write(chkpwd_write_pipe[1], &uid, sizeof(uid_t));
-       write(chkpwd_write_pipe[1], pass, 256);
-       read(chkpwd_read_pipe[0], buf, 4);
-
-       if (!strncmp(buf, "PASS", 4)) {
-               printf("pass\n");
-               return(1);
-       }
-
-       printf("fail\n");
-       return 0;
-}
-
-/* 
- * Start up the chkpwd daemon so validpw() has something to talk to
- */
-void start_chkpwd_daemon(void) {
-       pid_t chkpwd_pid;
-       struct stat filestats;
-       int i;
-
-       printf("Starting chkpwd daemon for host authentication mode\n");
-
-       if ((stat(file_chkpwd, &filestats)==-1) ||
-           (filestats.st_size==0)){
-               printf("didn't find chkpwd daemon in %s: %s\n", file_chkpwd, strerror(errno));
-               abort();
-       }
-       if (pipe(chkpwd_write_pipe) != 0) {
-               printf("Unable to create pipe for chkpwd daemon: %s\n", strerror(errno));
-               abort();
-       }
-       if (pipe(chkpwd_read_pipe) != 0) {
-               printf("Unable to create pipe for chkpwd daemon: %s\n", strerror(errno));
-               abort();
-       }
-
-       chkpwd_pid = fork();
-       if (chkpwd_pid < 0) {
-               printf("Unable to fork chkpwd daemon: %s\n", strerror(errno));
-               abort();
-       }
-       if (chkpwd_pid == 0) {
-               dup2(chkpwd_write_pipe[0], 0);
-               dup2(chkpwd_read_pipe[1], 1);
-               for (i=2; i<256; ++i) close(i);
-               execl(file_chkpwd, file_chkpwd, NULL);
-               printf("Unable to exec chkpwd daemon: %s\n", strerror(errno));
-               abort();
-               exit(errno);
-       }
-}
-
-
-
-int main(int argc, char **argv) {
-       char buf[256];
-       struct passwd *p;
-       int uid;
-       char ctdldir[PATH_MAX]=CTDLDIR;
-       
-       calc_dirs_n_files(0,0,"", ctdldir, 0);
-       
-       printf("\n\n ** host auth mode test utility **\n\n");
-       start_chkpwd_daemon();
-
-       if (getuid() != 0){
-               printf("\n\nERROR: you need to be root to run this!\n\n");
-               return(1);
-       }
-       while(1) {
-               printf("\n\nUsername: ");
-               fgets(buf, sizeof buf, stdin);
-               buf[strlen(buf)-1] = 0;
-               p = getpwnam(buf);
-               if (p == NULL) {
-                       printf("Not found\n");
-               }
-               else {
-                       uid = p->pw_uid;
-                       printf("     uid: %d\n", uid);
-                       printf("Password: ");
-                       fgets(buf, sizeof buf, stdin);
-                       buf[strlen(buf)-1] = 0;
-                       validpw(uid, buf);
-               }
-       }
-
-       return(0);
-}
diff --git a/citadel/chkpwd.c b/citadel/chkpwd.c
deleted file mode 100644 (file)
index 604ff9b..0000000
+++ /dev/null
@@ -1,60 +0,0 @@
-/*
- * $Id$
- *
- * a setuid helper program for machines which use shadow passwords
- * by Nathan Bryant, March 1999
- *
- *
- * Copyright (c) 1987-2009 by the citadel.org team
- *
- *  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 3 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; if not, write to the Free Software
- *  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
- */
-
-#include <pwd.h>
-#include <stdio.h>
-#include <limits.h>
-#include <stdlib.h>
-#include <string.h>
-#include <syslog.h>
-#include <unistd.h>
-#include <sys/types.h>
-
-#include "auth.h"
-#include "config.h"
-#include "citadel_dirs.h"
-#include "citadel.h"
-
-int main(void)
-{
-       uid_t uid;
-       char buf[SIZ];
-
-       while (1) {
-               buf[0] = '\0';
-               read(0, &uid, sizeof(uid_t));   /* uid */
-               read(0, buf, 256);      /* password */
-
-               if (buf[0] == '\0') 
-                       return (0);
-               if (validate_password(uid, buf)) {
-                       write(1, "PASS", 4);
-               }
-               else {
-                       write(1, "FAIL", 4);
-               }
-       }
-
-       return(0);
-}
diff --git a/citadel/citadel.c b/citadel/citadel.c
deleted file mode 100644 (file)
index 7e5ef87..0000000
+++ /dev/null
@@ -1,2348 +0,0 @@
-/*
- * $Id$
- *
- * Main source module for the client program.
- *
- * Copyright (c) 1987-2009 by the citadel.org team
- *
- *  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 3 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; if not, write to the Free Software
- *  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
- */
-
-#include "sysdep.h"
-#include <stdlib.h>
-#include <unistd.h>
-#include <fcntl.h>
-#include <stdio.h>
-#include <ctype.h>
-#include <string.h>
-
-#if TIME_WITH_SYS_TIME
-# include <sys/time.h>
-# include <time.h>
-#else
-# if HAVE_SYS_TIME_H
-#  include <sys/time.h>
-# else
-#  include <time.h>
-# endif
-#endif
-
-#include <limits.h>
-#include <sys/types.h>
-#include <sys/wait.h>
-#include <sys/stat.h>
-#include <sys/ioctl.h>
-#include <signal.h>
-#include <pwd.h>
-#include <stdarg.h>
-#include <errno.h>
-#include <libcitadel.h>
-#include "citadel.h"
-#include "citadel_ipc.h"
-#include "axdefs.h"
-#include "routines.h"
-#include "routines2.h"
-#include "tuiconfig.h"
-#include "rooms.h"
-#include "messages.h"
-#include "commands.h"
-#include "client_chat.h"
-#include "client_passwords.h"
-#include "citadel_decls.h"
-#include "sysdep.h"
-#ifndef HAVE_SNPRINTF
-#include "snprintf.h"
-#endif
-#include "screen.h"
-#include "citadel_dirs.h"
-
-#include "ecrash.h"
-#include "md5.h"
-
-#define IFEXPERT if (userflags&US_EXPERT)
-#define IFNEXPERT if ((userflags&US_EXPERT)==0)
-#define IFAIDE if (axlevel>=6)
-#define IFNAIDE if (axlevel<6)
-
-int rordercmp(struct ctdlroomlisting *r1, struct ctdlroomlisting *r2);
-
-march *marchptr = NULL;
-
-/* globals associated with the client program */
-char temp[PATH_MAX];           /* Name of general-purpose temp file */
-char temp2[PATH_MAX];          /* Name of general-purpose temp file */
-char tempdir[PATH_MAX];                /* Name of general-purpose temp directory */
-char editor_paths[MAX_EDITORS][SIZ];   /* paths to external editors */
-char printcmd[SIZ];            /* print command */
-int editor_pid = (-1);
-char fullname[USERNAME_SIZE];
-int screenwidth;
-int screenheight;
-unsigned room_flags;
-unsigned room_flags2;
-char room_name[ROOMNAMELEN];
-char *uglist[UGLISTLEN]; /* size of the ungoto list */
-long uglistlsn[UGLISTLEN]; /* current read position for all the ungoto's. Not going to make any friends with this one. */
-int uglistsize = 0;
-char is_mail = 0;              /* nonzero when we're in a mail room */
-char axlevel = 0;              /* access level */
-char is_room_aide = 0;         /* boolean flag, 1 if room aide */
-int timescalled;
-int posted;
-unsigned userflags;
-long usernum = 0L;             /* user number */
-time_t lastcall = 0L;          /* Date/time of previous login */
-char newnow;
-long highest_msg_read;         /* used for <A>bandon room cmd */
-long maxmsgnum;                        /* used for <G>oto */
-char sigcaught = 0;
-char have_xterm = 0;           /* are we running on an xterm? */
-char rc_username[USERNAME_SIZE];
-char rc_password[32];
-char hostbuf[SIZ];
-char portbuf[SIZ];
-char rc_floor_mode;
-char floor_mode;
-char curr_floor = 0;           /* number of current floor */
-char floorlist[128][SIZ];      /* names of floors */
-int termn8 = 0;                        /* Set to nonzero to cause a logoff */
-int secure;                    /* Set to nonzero when wire is encrypted */
-
-extern char instant_msgs;      /* instant messages waiting! */
-extern int rc_ansi_color;      /* ansi color value from citadel.rc */
-extern int next_lazy_cmd;
-
-CtdlIPC *ipc_for_signal_handlers;      /* KLUDGE cover your eyes */
-int enable_syslog = 0;
-
-
-/*
- * CtdlLogPrintf()  ...   Write logging information; 
- *                  simple here to have the same 
- *                  symbols in the client.
- */
-
-void CtdlLogPrintf(enum LogLevel loglevel, const char *format, ...) {   
-       va_list arg_ptr;
-
-       va_start(arg_ptr, format);
-       vfprintf(stderr, format, arg_ptr);   
-       va_end(arg_ptr);   
-       fflush(stderr);
-}   
-
-/*
- * here is our 'clean up gracefully and exit' routine
- */
-void ctdl_logoff(char *file, int line, CtdlIPC *ipc, int code)
-{
-       int lp;
-
-       if (editor_pid > 0) {   /* kill the editor if it's running */
-               kill(editor_pid, SIGHUP);
-       }
-
-       /* Free the ungoto list */
-       for (lp = 0; lp < uglistsize; lp++) {
-               free(uglist[lp]);
-       }
-
-/* Shut down the server connection ... but not if the logoff code is 3,
- * because that means we're exiting because we already lost the server.
- */
-       if (code != 3) {
-               CtdlIPCQuit(ipc);
-       }
-
-/*
- * now clean up various things
- */
-       screen_delete();
-
-       unlink(temp);
-       unlink(temp2);
-       nukedir(tempdir);
-
-       /* Violently kill off any child processes if Citadel is
-        * the login shell. 
-        */
-       if (getppid() == 1) {
-               kill(0 - getpgrp(), SIGTERM);
-               sleep(1);
-               kill(0 - getpgrp(), SIGKILL);
-       }
-       color(ORIGINAL_PAIR);   /* Restore the old color settings */
-       stty_ctdl(SB_RESTORE);  /* return the old terminal settings */
-       /* 
-        * uncomment the following if you need to know why Citadel exited
-       printf("*** Exit code %d at %s:%d\n", code, file, line);
-       sleep(2);
-        */
-       exit(code);             /* exit with the proper exit code */
-}
-
-
-
-/*
- * signal catching function for hangups...
- */
-void dropcarr(int signum)
-{
-       logoff(NULL, 3);        /* No IPC when server's already gone! */
-}
-
-
-
-/*
- * catch SIGCONT to reset terminal modes when were are put back into the
- * foreground.
- */
-void catch_sigcont(int signum)
-{
-       stty_ctdl(SB_LAST);
-       signal(SIGCONT, catch_sigcont);
-}
-
-
-/* general purpose routines */
-
-/* display a file */
-void formout(CtdlIPC *ipc, char *name)
-{
-       int r;                  /* IPC return code */
-       char buf[SIZ];
-       char *text = NULL;
-
-       r = CtdlIPCSystemMessage(ipc, name, &text, buf);
-       if (r / 100 != 1) {
-               scr_printf("%s\n", buf);
-               return;
-       }
-       if (text) {
-               fmout(screenwidth, NULL, text, NULL,
-                     ((userflags & US_PAGINATOR) ? 1 : 0),
-                     screenheight, 1, 1);
-               free(text);
-       }
-}
-
-
-void userlist(CtdlIPC *ipc, char *patn)
-{
-       char buf[SIZ];
-       char fl[SIZ];
-       struct tm tmbuf;
-       time_t lc;
-       int r;                          /* IPC response code */
-       char *listing = NULL;
-
-       r = CtdlIPCUserListing(ipc, patn, &listing, buf);
-       if (r / 100 != 1) {
-               pprintf("%s\n", buf);
-               return;
-       }
-
-       pprintf("       User Name           Num  L Last Visit Logins Messages\n");
-       pprintf("------------------------- ----- - ---------- ------ --------\n");
-       if (listing != NULL) while (!IsEmptyStr(listing)) {
-               extract_token(buf, listing, 0, '\n', sizeof buf);
-               remove_token(listing, 0, '\n');
-
-               if (sigcaught == 0) {
-                   extract_token(fl, buf, 0, '|', sizeof fl);
-                   if (pattern(fl, patn) >= 0) {
-                       pprintf("%-25s ", fl);
-                       pprintf("%5ld %d ", extract_long(buf, 2),
-                              extract_int(buf, 1));
-                       lc = extract_long(buf, 3);
-                       localtime_r(&lc, &tmbuf);
-                       pprintf("%02d/%02d/%04d ",
-                              (tmbuf.tm_mon + 1),
-                              tmbuf.tm_mday,
-                              (tmbuf.tm_year + 1900));
-                       pprintf("%6ld %8ld\n", extract_long(buf, 4), extract_long(buf, 5));
-                   }
-
-               }
-       }
-       free(listing);
-       pprintf("\n");
-}
-
-
-/*
- * grab assorted info about the user...
- */
-void load_user_info(char *params)
-{
-       extract_token(fullname, params, 0, '|', sizeof fullname);
-       axlevel = extract_int(params, 1);
-       timescalled = extract_int(params, 2);
-       posted = extract_int(params, 3);
-       userflags = extract_int(params, 4);
-       usernum = extract_long(params, 5);
-       lastcall = extract_long(params, 6);
-}
-
-
-/*
- * Remove a room from the march list.  'floornum' is ignored unless
- * 'roomname' is set to _FLOOR_, in which case all rooms on the requested
- * floor will be removed from the march list.
- */
-void remove_march(char *roomname, int floornum)
-{
-       struct march *mptr, *mptr2;
-
-       if (marchptr == NULL)
-               return;
-
-       if ((!strcasecmp(marchptr->march_name, roomname))
-           || ((!strcasecmp(roomname, "_FLOOR_")) && (marchptr->march_floor == floornum))) {
-               mptr = marchptr->next;
-               free(marchptr);
-               marchptr = mptr;
-               return;
-       }
-       mptr2 = marchptr;
-       for (mptr = marchptr; mptr != NULL; mptr = mptr->next) {
-
-               if ((!strcasecmp(mptr->march_name, roomname))
-                   || ((!strcasecmp(roomname, "_FLOOR_"))
-                       && (mptr->march_floor == floornum))) {
-
-                       mptr2->next = mptr->next;
-                       free(mptr);
-                       mptr = mptr2;
-               } else {
-                       mptr2 = mptr;
-               }
-       }
-}
-
-
-/*
- * Locate the room on the march list which we most want to go to.  Each room
- * is measured given a "weight" of preference based on various factors.
- */
-char *pop_march(int desired_floor, struct march *_march)
-{
-       static char TheRoom[ROOMNAMELEN];
-       int TheFloor = 0;
-       int TheOrder = 32767;
-       int TheWeight = 0;
-       int weight;
-       struct march *mptr = NULL;
-
-       strcpy(TheRoom, "_BASEROOM_");
-       if (_march == NULL)
-               return (TheRoom);
-
-       for (mptr = _march; mptr != NULL; mptr = mptr->next) {
-               weight = 0;
-               if ((strcasecmp(mptr->march_name, "_BASEROOM_")))
-                       weight = weight + 10000;
-               if (mptr->march_floor == desired_floor)
-                       weight = weight + 5000;
-
-               weight = weight + ((128 - (mptr->march_floor)) * 128);
-               weight = weight + (128 - (mptr->march_order));
-
-               if (weight > TheWeight) {
-                       TheWeight = weight;
-                       strcpy(TheRoom, mptr->march_name);
-                       TheFloor = mptr->march_floor;
-                       TheOrder = mptr->march_order;
-               }
-       }
-       return (TheRoom);
-}
-
-
-/*
- * jump directly to a room
- */
-void dotgoto(CtdlIPC *ipc, char *towhere, int display_name, int fromungoto)
-{
-       char aaa[SIZ], bbb[SIZ];
-       static long ls = 0L;
-       int newmailcount = 0;
-       int partial_match, best_match;
-       char from_floor;
-       int ugpos = uglistsize;
-       int r;                          /* IPC result code */
-       struct ctdlipcroom *room = NULL;
-       int rv = 0;
-
-       /* store ungoto information */
-       if (fromungoto == 0) {
-               /* sloppy slide them all down, hey it's the client, who cares. :-) */
-               if (uglistsize >= (UGLISTLEN-1)) {
-                       int lp;
-                       free (uglist[0]);
-                       for (lp = 0; lp < (UGLISTLEN-1); lp++) {
-                               uglist[lp] = uglist[lp+1];
-                               uglistlsn[lp] = uglistlsn[lp+1];
-                       }
-                       ugpos--;
-               } else {
-                       uglistsize++;
-               }
-        
-               uglist[ugpos] = malloc(strlen(room_name)+1);
-               strcpy(uglist[ugpos], room_name);
-               uglistlsn[ugpos] = ls;
-       }
-      
-       /* first try an exact match */
-       r = CtdlIPCGotoRoom(ipc, towhere, "", &room, aaa);
-       if (r / 10 == 54) {
-               newprompt("Enter room password: ", bbb, 9);
-               r = CtdlIPCGotoRoom(ipc, towhere, bbb, &room, aaa);
-               if (r / 10 == 54) {
-                       scr_printf("Wrong password.\n");
-                       return;
-               }
-       }       
-
-       /*
-        * If a match is not found, try a partial match.
-        * Partial matches anywhere in the string carry a weight of 1,
-        * left-aligned matches carry a weight of 2.  Pick the room that
-        * has the highest-weighted match.  Do not match on forgotten
-        * rooms.
-        */
-       if (r / 100 != 2) {
-               struct march *march = NULL;
-
-               best_match = 0;
-               strcpy(bbb, "");
-
-               r = CtdlIPCKnownRooms(ipc, SubscribedRooms, AllFloors, &march, aaa);
-               if (r / 100 == 1) {
-                       /* Run the roomlist; free the data as we go */
-                       struct march *mp = march;       /* Current */
-
-                       while (mp) {
-                               partial_match = 0;
-                               if (pattern(mp->march_name, towhere) >= 0) {
-                                       partial_match = 1;
-                               }
-                               if (!strncasecmp(mp->march_name, towhere, strlen(towhere))) {
-                                       partial_match = 2;
-                               }
-                               if (partial_match > best_match) {
-                                       strcpy(bbb, mp->march_name);
-                                       best_match = partial_match;
-                               }
-                               /* Both pointers are NULL at end of list */
-                               march = mp->next;
-                               free(mp);
-                               mp = march;
-                       }
-               }
-
-               if (IsEmptyStr(bbb)) {
-                       scr_printf("No room '%s'.\n", towhere);
-                       return;
-               }
-               r = CtdlIPCGotoRoom(ipc, bbb, "", &room, aaa);
-       }
-       if (r / 100 != 1 && r / 100 != 2) {
-               scr_printf("%s\n", aaa);
-               return;
-       }
-       safestrncpy(room_name, room->RRname, ROOMNAMELEN);
-       room_flags = room->RRflags;
-       room_flags2 = room->RRflags2;
-       from_floor = curr_floor;
-       curr_floor = room->RRfloor;
-
-       remove_march(room_name, 0);
-       if (!strcasecmp(towhere, "_BASEROOM_"))
-               remove_march(towhere, 0);
-       if (!room->RRunread)
-               next_lazy_cmd = 5;      /* Don't read new if no new msgs */
-       if ((from_floor != curr_floor) && (display_name > 0) && (floor_mode == 1)) {
-               if (floorlist[(int) curr_floor][0] == 0)
-                       load_floorlist(ipc);
-               scr_printf("(Entering floor: %s)\n", &floorlist[(int) curr_floor][0]);
-       }
-       if (display_name == 1) {
-               color(BRIGHT_WHITE);
-               scr_printf("%s ", room_name);
-               color(DIM_WHITE);
-               scr_printf("- ");
-       }
-       if (display_name != 2) {
-               color(BRIGHT_YELLOW);
-               scr_printf("%d ", room->RRunread);
-               color(DIM_WHITE);
-               scr_printf("new of ");
-               color(BRIGHT_YELLOW);
-               scr_printf("%d ", room->RRtotal);
-               color(DIM_WHITE);
-               scr_printf("messages.\n");
-       }
-       highest_msg_read = room->RRlastread;
-       maxmsgnum = room->RRhighest;
-       is_mail = room->RRismailbox;
-       is_room_aide = room->RRaide;
-       ls = room->RRlastread;
-
-       /* read info file if necessary */
-       if (room->RRinfoupdated > 0)
-               readinfo(ipc);
-
-       /* check for newly arrived mail if we can */
-       newmailcount = room->RRnewmail;
-       if (newmailcount > 0) {
-               color(BRIGHT_RED);
-               if (newmailcount == 1) {
-                       scr_printf("*** A new mail message has arrived.\n");
-               }
-               else {
-                       scr_printf("*** %d new mail messages have arrived.\n",
-                                       newmailcount);
-               }
-               color(DIM_WHITE);
-               if (!IsEmptyStr(rc_gotmail_cmd)) {
-                       rv = system(rc_gotmail_cmd);
-               }
-       }
-       status_line(ipc->ServInfo.humannode, ipc->ServInfo.site_location,
-                       room_name, secure, newmailcount);
-       free(room);
-}
-
-/* Goto next room having unread messages.
- * We want to skip over rooms that the user has already been to, and take the
- * user back to the lobby when done.  The room we end up in is placed in
- * newroom - which is set to 0 (the lobby) initially.
- */
-void gotonext(CtdlIPC *ipc)
-{
-       char buf[SIZ];
-       struct march *mptr, *mptr2;
-       char next_room[ROOMNAMELEN];
-       int r;                          /* IPC response code */
-
-       /* Check to see if the march-mode list is already allocated.
-        * If it is, pop the first room off the list and go there.
-        */
-       if (marchptr == NULL) {
-               r = CtdlIPCKnownRooms(ipc, SubscribedRoomsWithNewMessages,
-                                       AllFloors, &marchptr, buf);
-
-/* add _BASEROOM_ to the end of the march list, so the user will end up
- * in the system base room (usually the Lobby>) at the end of the loop
- */
-               mptr = (struct march *) malloc(sizeof(struct march));
-               mptr->next = NULL;
-               mptr->march_order = 0;
-               mptr->march_floor = 0;
-               strcpy(mptr->march_name, "_BASEROOM_");
-               if (marchptr == NULL) {
-                       marchptr = mptr;
-               } else {
-                       mptr2 = marchptr;
-                       while (mptr2->next != NULL)
-                               mptr2 = mptr2->next;
-                       mptr2->next = mptr;
-               }
-/*
- * ...and remove the room we're currently in, so a <G>oto doesn't make us
- * walk around in circles
- */
-               remove_march(room_name, 0);
-       }
-       if (marchptr != NULL) {
-               strcpy(next_room, pop_march(curr_floor, marchptr));
-       } else {
-               strcpy(next_room, "_BASEROOM_");
-       }
-       remove_march(next_room, 0);
-       dotgoto(ipc, next_room, 1, 0);
-}
-
-/*
- * forget all rooms on a given floor
- */
-void forget_all_rooms_on(CtdlIPC *ipc, int ffloor)
-{
-       char buf[SIZ];
-       struct march *flist = NULL;
-       struct march *fptr = NULL;
-       struct ctdlipcroom *room = NULL;
-       int r;                          /* IPC response code */
-
-       scr_printf("Forgetting all rooms on %s...\n", &floorlist[ffloor][0]);
-       scr_flush();
-       remove_march("_FLOOR_", ffloor);
-       r = CtdlIPCKnownRooms(ipc, AllAccessibleRooms, ffloor, &flist, buf);
-       if (r / 100 != 1) {
-               scr_printf("Error %d: %s\n", r, buf);
-               return;
-       }
-       while (flist) {
-               r = CtdlIPCGotoRoom(ipc, flist->march_name, "", &room, buf);
-               if (r / 100 == 2) {
-                       r = CtdlIPCForgetRoom(ipc, buf);
-                       if (r / 100 != 2) {
-                               scr_printf("Error %d: %s\n", r, buf);
-                       }
-
-               }
-               fptr = flist;
-               flist = flist->next;
-               free(fptr);
-       }
-       if (room) free(room);
-}
-
-
-/*
- * routine called by gotofloor() to move to a new room on a new floor
- */
-void gf_toroom(CtdlIPC *ipc, char *towhere, int mode)
-{
-       int floor_being_left;
-
-       floor_being_left = curr_floor;
-
-       if (mode == GF_GOTO) {          /* <;G>oto mode */
-               updatels(ipc);
-               dotgoto(ipc, towhere, 1, 0);
-       }
-       else if (mode == GF_SKIP) {     /* <;S>kip mode */
-               dotgoto(ipc, towhere, 1, 0);
-               remove_march("_FLOOR_", floor_being_left);
-       }
-       else if (mode == GF_ZAP) {      /* <;Z>ap mode */
-               dotgoto(ipc, towhere, 1, 0);
-               remove_march("_FLOOR_", floor_being_left);
-               forget_all_rooms_on(ipc, floor_being_left);
-       }
-}
-
-
-/*
- * go to a new floor
- */
-void gotofloor(CtdlIPC *ipc, char *towhere, int mode)
-{
-       int a, tofloor;
-       int r;          /* IPC response code */
-       struct march *mptr;
-       char buf[SIZ], targ[SIZ];
-
-       if (floorlist[0][0] == 0)
-               load_floorlist(ipc);
-       tofloor = (-1);
-       for (a = 0; a < 128; ++a)
-               if (!strcasecmp(&floorlist[a][0], towhere))
-                       tofloor = a;
-
-       if (tofloor < 0) {
-               for (a = 0; a < 128; ++a) {
-                       if (!strncasecmp(&floorlist[a][0], towhere, strlen(towhere))) {
-                               tofloor = a;
-                       }
-               }
-       }
-       if (tofloor < 0) {
-               for (a = 0; a < 128; ++a)
-                       if (pattern(towhere, &floorlist[a][0]) > 0)
-                               tofloor = a;
-       }
-       if (tofloor < 0) {
-               scr_printf("No floor '%s'.\n", towhere);
-               return;
-       }
-       for (mptr = marchptr; mptr != NULL; mptr = mptr->next) {
-               if ((mptr->march_floor) == tofloor) {
-                       gf_toroom(ipc, mptr->march_name, mode);
-                       return;
-               }
-       }
-
-       /* Find first known room on the floor */
-
-       strcpy(targ, "");
-       mptr = NULL;
-       r = CtdlIPCKnownRooms(ipc, SubscribedRooms, tofloor, &mptr, buf);
-       if (r / 100 == 1) {
-               struct march *tmp = mptr;
-
-               /*. . . according to room order */
-               if (mptr)
-           strcpy(targ, pop_march(tofloor, mptr));
-               while (mptr) {
-                       tmp = mptr->next;
-                       free(mptr);
-                       mptr = tmp;
-               }
-       }
-       if (!IsEmptyStr(targ)) {
-               gf_toroom(ipc, targ, mode);
-               return;
-       }
-
-       /* No known rooms on the floor; unzap the first one then */
-
-       strcpy(targ, "");
-       mptr = NULL;
-       r = CtdlIPCKnownRooms(ipc, AllAccessibleRooms, tofloor, &mptr, buf);
-       if (r / 100 == 1) {
-               struct march *tmp = mptr;
-               
-        /*. . . according to room order */
-               if (mptr)
-                       strcpy(targ, pop_march(tofloor, mptr));
-               while (mptr) {
-                       tmp = mptr->next;
-                       free(mptr);
-                       mptr = tmp;
-               }
-       }
-       if (!IsEmptyStr(targ)) {
-               gf_toroom(ipc, targ, mode);
-       } else {
-               scr_printf("There are no rooms on '%s'.\n", &floorlist[tofloor][0]);
-       }
-}
-
-/*
- * Indexing mechanism for a room list, called by gotoroomstep()
- */
-void room_tree_list_query(struct ctdlroomlisting *rp, char *findrmname, int findrmslot, char *rmname, int *rmslot, int *rmtotal)
-{
-       char roomname[ROOMNAMELEN];
-       static int cur_rmslot = 0;
-
-       if (rp == NULL) {
-               cur_rmslot = 0;
-               return;
-       }
-
-       if (rp->lnext != NULL) {
-               room_tree_list_query(rp->lnext, findrmname, findrmslot, rmname, rmslot, rmtotal);
-       }
-
-       if (sigcaught == 0) {
-               strcpy(roomname, rp->rlname);
-
-               if (rmname != NULL) {
-                       if (cur_rmslot == findrmslot) {
-                               strcpy(rmname, roomname);
-                       }
-               }
-               if (rmslot != NULL) {
-                       if (!strcmp(roomname, findrmname)) {
-                               *rmslot = cur_rmslot;
-                       }
-               }
-               cur_rmslot++;
-       }
-
-       if (rp->rnext != NULL) {
-               room_tree_list_query(rp->rnext, findrmname, findrmslot, rmname, rmslot, rmtotal);
-       }
-
-       if ((rmname == NULL) && (rmslot == NULL))
-               free(rp);
-
-       if (rmtotal != NULL) {
-               *rmtotal = cur_rmslot;
-       }
-}
-
-/*
- * step through rooms on current floor
- */
-void  gotoroomstep(CtdlIPC *ipc, int direction, int mode)
-{
-       struct march *listing = NULL;
-       struct march *mptr;
-       int r;          /* IPC response code */
-       char buf[SIZ];
-       struct ctdlroomlisting *rl = NULL;
-       struct ctdlroomlisting *rp;
-       struct ctdlroomlisting *rs;
-       int list_it;
-       char rmname[ROOMNAMELEN];
-       int rmslot = 0;
-       int rmtotal;
-
-       /* Ask the server for a room list */
-       r = CtdlIPCKnownRooms(ipc, SubscribedRooms, (-1), &listing, buf);
-       if (r / 100 != 1) {
-               listing = NULL;
-       }
-
-       load_floorlist(ipc);
-
-       for (mptr = listing; mptr != NULL; mptr = mptr->next) {
-               list_it = 1;
-
-               if ( floor_mode 
-                        && (mptr->march_floor != curr_floor))
-                       list_it = 0;
-
-               if (list_it) {
-                       rp = malloc(sizeof(struct ctdlroomlisting));
-                       strncpy(rp->rlname, mptr->march_name, ROOMNAMELEN);
-                       rp->rlflags = mptr->march_flags;
-                       rp->rlfloor = mptr->march_floor;
-                       rp->rlorder = mptr->march_order;
-                       rp->lnext = NULL;
-                       rp->rnext = NULL;
-
-                       rs = rl;
-                       if (rl == NULL) {
-                               rl = rp;
-                       } else {
-                               while (rp != NULL) {
-                                       if (rordercmp(rp, rs) < 0) {
-                                               if (rs->lnext == NULL) {
-                                                       rs->lnext = rp;
-                                                       rp = NULL;
-                                               } else {
-                                                       rs = rs->lnext;
-                                               }
-                                       } else {
-                                               if (rs->rnext == NULL) {
-                                                       rs->rnext = rp;
-                                                       rp = NULL;
-                                               } else {
-                                                       rs = rs->rnext;
-                                               }
-                                       }
-                               }
-                       }
-               }
-       }
-
-       /* Find position of current room */
-       room_tree_list_query(NULL, NULL, 0, NULL, NULL, NULL);
-       room_tree_list_query(rl,  room_name, 0, NULL, &rmslot, &rmtotal);
-
-       if (direction == 0) { /* Previous room */
-               /* If we're at the first room, wrap to the last room */
-               if (rmslot == 0) {
-                       rmslot = rmtotal - 1;
-               } else {
-                       rmslot--;
-               }
-       } else {                 /* Next room */
-               /* If we're at the last room, wrap to the first room */
-               if (rmslot == rmtotal - 1) {
-                       rmslot = 0; 
-               } else {
-                       rmslot++;
-               }
-       }
-
-       /* Get name of next/previous room */
-       room_tree_list_query(NULL, NULL, 0, NULL, NULL, NULL);
-       room_tree_list_query(rl,  NULL, rmslot, rmname, NULL, NULL);
-
-       /* Free the tree */
-       room_tree_list_query(rl, NULL, 0, NULL, NULL, NULL);
-
-       if (mode == 0) { /* not skipping */
-           updatels(ipc);
-       } else {
-               if (rc_alt_semantics) {
-               updatelsa(ipc);
-               }
-       }
-
-       /* Free the room list */
-       while (listing) {
-               mptr = listing->next;
-               free(listing);
-               listing = mptr;
-       };
-
-       dotgoto(ipc, rmname, 1, 0);
-}
-
-
-/*
- * step through floors on system
- */
-void  gotofloorstep(CtdlIPC *ipc, int direction, int mode)
-{
-       int  tofloor;
-
-       if (floorlist[0][0] == 0)
-               load_floorlist(ipc);
-
-       empty_keep_going:
-
-       if (direction == 0) { /* Previous floor */
-               if (curr_floor) tofloor = curr_floor - 1;
-               else tofloor = 127;
-
-               while (!floorlist[tofloor][0]) tofloor--;
-       } else {                   /* Next floor */
-               if (curr_floor < 127) tofloor = curr_floor + 1;
-               else tofloor = 0;
-
-               while (!floorlist[tofloor][0] && tofloor < 127) tofloor++;
-               if (!floorlist[tofloor][0])     tofloor = 0;
-       }
-       /* ;g works when not in floor mode so . . . */
-       if (!floor_mode) {
-               scr_printf("(%s)\n", floorlist[tofloor] );
-       }
-
-       gotofloor(ipc, floorlist[tofloor], mode);
-       if (curr_floor != tofloor) { /* gotofloor failed */
-            curr_floor = tofloor;
-            goto empty_keep_going;
-       }            
-}
-
-/* 
- * Display user 'preferences'.
- */
-extern int rc_prompt_control;
-void read_config(CtdlIPC *ipc)
-{
-       char buf[SIZ];
-       char *resp = NULL;
-       int r;                  /* IPC response code */
-    char _fullname[USERNAME_SIZE];
-       long _usernum;
-       int _axlevel, _timescalled, _posted;
-       time_t _lastcall;
-       struct ctdluser *user = NULL;
-
-       /* get misc user info */   
-       r = CtdlIPCGetBio(ipc, fullname, &resp, buf);
-       if (r / 100 != 1) {
-               pprintf("%s\n", buf);
-               return;
-       }
-       extract_token(_fullname, buf, 1, '|', sizeof fullname);
-       _usernum = extract_long(buf, 2);
-       _axlevel = extract_int(buf, 3);
-       _lastcall = extract_long(buf, 4);
-    _timescalled = extract_int(buf, 5);
-       _posted = extract_int(buf, 6);
-       free(resp);
-       resp = NULL;
-   
-       /* get preferences */
-       r = CtdlIPCGetConfig(ipc, &user, buf);
-       if (r / 100 != 2) {
-               scr_printf("%s\n", buf);
-               free(user);
-               return;
-       }
-
-       /* show misc user info */
-       scr_printf("%s\nAccess level: %d (%s)\n"
-                  "User #%ld / %d Calls / %d Posts",
-                  _fullname, _axlevel, axdefs[(int) _axlevel],
-                  _usernum, _timescalled, _posted);
-       if (_lastcall > 0L) {
-               scr_printf(" / Curr login: %s",
-                          asctime(localtime(&_lastcall)));
-       }
-       scr_printf("\n");
-
-       /* show preferences */
-       scr_printf("Your screen width: ");                                     color(BRIGHT_CYAN); scr_printf("%d",   /*user->USscreenwidth*/ screenwidth);          color(DIM_WHITE); 
-       scr_printf(", height: ");                                              color(BRIGHT_CYAN); scr_printf("%d\n", /*user->USscreenheight*/ screenheight);        color(DIM_WHITE);  
-       scr_printf("Are you an experienced Citadel user: ");                   color(BRIGHT_CYAN); scr_printf("%s\n", (user->flags & US_EXPERT) ? "Yes" : "No");     color(DIM_WHITE);
-       scr_printf("Print last old message on New message request: ");         color(BRIGHT_CYAN); scr_printf("%s\n", (user->flags & US_LASTOLD)? "Yes" : "No");     color(DIM_WHITE);
-       scr_printf("Prompt after each message: ");                             color(BRIGHT_CYAN); scr_printf("%s\n", (!(user->flags & US_NOPROMPT))? "Yes" : "No"); color(DIM_WHITE);
-       if ((user->flags & US_NOPROMPT) == 0) {
-       scr_printf("Use 'disappearing' prompts: ");                        color(BRIGHT_CYAN); scr_printf("%s\n", (user->flags & US_DISAPPEAR)? "Yes" : "No");   color(DIM_WHITE);
-       }
-       scr_printf("Pause after each screenful of text: ");                    color(BRIGHT_CYAN); scr_printf("%s\n", (user->flags & US_PAGINATOR)? "Yes" : "No");   color(DIM_WHITE);
-    if (rc_prompt_control == 3 && (user->flags & US_PAGINATOR)) {
-       scr_printf("<N>ext and <S>top work at paginator prompt: ");        color(BRIGHT_CYAN); scr_printf("%s\n", (user->flags & US_PROMPTCTL)? "Yes" : "No");   color(DIM_WHITE);
-       }
-    if (rc_floor_mode == RC_DEFAULT) {
-       scr_printf("View rooms by floor: ");                               color(BRIGHT_CYAN); scr_printf("%s\n", (user->flags & US_FLOORS)? "Yes" : "No");          color(DIM_WHITE);
-       }
-       if (rc_ansi_color == 3) {
-           scr_printf("Enable color support: ");                              color(BRIGHT_CYAN); scr_printf("%s\n", (user->flags & US_COLOR)? "Yes" : "No");       color(DIM_WHITE);
-       }
-       scr_printf("Be unlisted in userlog: ");                                color(BRIGHT_CYAN); scr_printf("%s\n", (user->flags & US_UNLISTED)? "Yes" : "No");    color(DIM_WHITE);
-       if (!IsEmptyStr(editor_paths[0])) {
-       scr_printf("Always enter messages with the full-screen editor: "); color(BRIGHT_CYAN); scr_printf("%s\n", (user->flags & US_EXTEDIT)? "Yes" : "No");     color(DIM_WHITE);
-       }
-       free(user);
-}
-
-/*
- * Display system statistics.
- */
-void system_info(CtdlIPC *ipc)
-{
-       char buf[SIZ];
-       char *resp = NULL;
-       size_t bytes;
-       int mrtg_users, mrtg_active_users; 
-       char mrtg_server_uptime[40];
-       long mrtg_himessage;
-       int ret;                        /* IPC response code */
-
-       /* get #users, #active & server uptime */
-       ret = CtdlIPCGenericCommand(ipc, "MRTG|users", NULL, 0, &resp, &bytes, buf);
-       mrtg_users = extract_int(resp, 0);
-       remove_token(resp, 0, '\n');
-       mrtg_active_users = extract_int(resp, 0);
-       remove_token(resp, 0, '\n');
-       extract_token(mrtg_server_uptime, resp, 0, '\n', sizeof mrtg_server_uptime);
-    free(resp);
-       resp = NULL;
-
-       /* get high message# */
-       ret = CtdlIPCGenericCommand(ipc, "MRTG|messages", NULL, 0, &resp, &bytes, buf);
-       mrtg_himessage = extract_long(resp, 0);
-       free(resp);
-       resp = NULL;
-
-       /* refresh server info just in case */
-       CtdlIPCServerInfo(ipc, buf);
-
-       scr_printf("You are connected to %s (%s) @%s\n", ipc->ServInfo.nodename, ipc->ServInfo.humannode, ipc->ServInfo.fqdn);
-       scr_printf("running %s with text client v%.2f,\n", ipc->ServInfo.software, (float)REV_LEVEL/100);
-       scr_printf("server build %s,\n", ipc->ServInfo.svn_revision, (float)REV_LEVEL/100);
-    scr_printf("and located in %s.\n", ipc->ServInfo.site_location);
-    scr_printf("Connected users %d / Active users %d / Highest message #%ld\n", mrtg_users, mrtg_active_users, mrtg_himessage);
-    scr_printf("Server uptime: %s\n", mrtg_server_uptime);
-    scr_printf("Your system administrator is %s.\n", ipc->ServInfo.sysadm);
-    scr_printf("Copyright (C)1987-2009 by the Citadel development team\n");
-}
-
-/*
- * forget all rooms on current floor
- */
-void forget_this_floor(CtdlIPC *ipc)
-{
-       if (curr_floor == 0) {
-               scr_printf("Can't forget this floor.\n");
-               return;
-       }
-       if (floorlist[0][0] == 0) {
-               load_floorlist(ipc);
-       }
-       scr_printf("Are you sure you want to forget all rooms on %s? ",
-              &floorlist[(int) curr_floor][0]);
-       if (yesno() == 0) {
-               return;
-       }
-
-       gf_toroom(ipc, "_BASEROOM_", GF_ZAP);
-}
-
-
-/* 
- * Figure out the physical screen dimensions, if we can
- * WARNING:  this is now called from a signal handler!
- */
-void check_screen_dims(void)
-{
-#ifdef TIOCGWINSZ
-       struct {
-               unsigned short height;  /* rows */
-               unsigned short width;   /* columns */
-               unsigned short xpixels;
-               unsigned short ypixels;         /* pixels */
-       } xwinsz;
-
-       if (have_xterm) {       /* dynamically size screen if on an xterm */
-               if (ioctl(0, TIOCGWINSZ, &xwinsz) == 0) {
-                       if (xwinsz.height)
-                               screenheight = is_curses_enabled() ? (int)xwinsz.height - 1 : (int) xwinsz.height;
-                       if (xwinsz.width)
-                               screenwidth = (int) xwinsz.width;
-               }
-       }
-#endif
-}
-
-
-/*
- * set floor mode depending on client, server, and user settings
- */
-void set_floor_mode(CtdlIPC* ipc)
-{
-       if (ipc->ServInfo.ok_floors == 0) {
-               floor_mode = 0; /* Don't use floors if the server */
-       }
-       /* doesn't support them!          */
-       else {
-               if (rc_floor_mode == RC_NO) {   /* never use floors */
-                       floor_mode = 0;
-               }
-               if (rc_floor_mode == RC_YES) {  /* always use floors */
-                       floor_mode = 1;
-               }
-               if (rc_floor_mode == RC_DEFAULT) {      /* user choice */
-                       floor_mode = ((userflags & US_FLOORS) ? 1 : 0);
-               }
-       }
-}
-
-/*
- * Set or change the user's password
- */
-int set_password(CtdlIPC *ipc)
-{
-       char pass1[20];
-       char pass2[20];
-       char buf[SIZ];
-
-       if (!IsEmptyStr(rc_password)) {
-               strcpy(pass1, rc_password);
-               strcpy(pass2, rc_password);
-       } else {
-               IFNEXPERT formout(ipc, "changepw");
-               newprompt("Enter a new password: ", pass1, -19);
-               newprompt("Enter it again to confirm: ", pass2, -19);
-       }
-       strproc(pass1);
-       strproc(pass2);
-       if (!strcasecmp(pass1, pass2)) {
-               CtdlIPCChangePassword(ipc, pass1, buf);
-               scr_printf("%s\n", buf);
-               offer_to_remember_password(ipc, hostbuf, portbuf, fullname, pass1);
-               return (0);
-       } else {
-               scr_printf("*** They don't match... try again.\n");
-               return (1);
-       }
-}
-
-
-
-/*
- * get info about the server we've connected to
- */
-void get_serv_info(CtdlIPC *ipc, char *supplied_hostname)
-{
-       char buf[SIZ];
-
-       CtdlIPCServerInfo(ipc, buf);
-
-       /* be nice and identify ourself to the server */
-       CtdlIPCIdentifySoftware(ipc, SERVER_TYPE, 0, REV_LEVEL,
-                (ipc->isLocal ? "local" : CITADEL),
-                (supplied_hostname) ? supplied_hostname : 
-                /* Look up the , in the bible if you're confused */
-                (locate_host(ipc, buf), buf), buf);
-
-       /* Indicate to the server that we prefer to decode Base64 and
-        * quoted-printable on the client side.
-        */
-       if ((CtdlIPCSpecifyPreferredFormats(ipc, buf, "dont_decode") / 100 ) != 2) {
-               scr_printf("ERROR: Extremely old server; MSG4 framework not supported.\n");
-               logoff(ipc, 0);
-       }
-
-       /*
-        * Tell the server what our preferred content formats are.
-        *
-        * Originally we preferred HTML over plain text because we can format
-        * it to the reader's screen width, but since our HTML-to-text parser
-        * isn't really all that great, it's probably better to just go with
-        * the plain text when we have it available.
-        */
-       if ((CtdlIPCSpecifyPreferredFormats(ipc, buf, "text/plain|text/html") / 100 ) != 2) {
-               scr_printf("ERROR: Extremely old server; MSG4 framework not supported.\n");
-               logoff(ipc, 0);
-       }
-}
-
-
-
-/*
- * Record compare function for SortOnlineUsers()
- */
-int idlecmp(const void *rec1, const void *rec2) {
-       time_t i1, i2;
-
-       i1 = extract_long(rec1, 5);
-       i2 = extract_long(rec2, 5);
-
-       if (i1 < i2) return(1);
-       if (i1 > i2) return(-1);
-       return(0);
-}
-
-
-/*
- * Sort the list of online users by idle time.
- * This function frees the supplied buffer, and returns a new one
- * to the caller.  The caller is responsible for freeing the returned buffer.
- */
-char *SortOnlineUsers(char *listing) {
-       int rows;
-       char *sortbuf;
-       char *retbuf;
-       char buf[SIZ];
-       int i;
-
-       rows = num_tokens(listing, '\n');
-       sortbuf = malloc(rows * SIZ);
-       if (sortbuf == NULL) return(listing);
-       retbuf = malloc(rows * SIZ);
-       if (retbuf == NULL) {
-               free(sortbuf);
-               return(listing);
-       }
-
-       /* Copy the list into a fixed-record-size array for sorting */
-       for (i=0; i<rows; ++i) {
-               memset(buf, 0, SIZ);
-               extract_token(buf, listing, i, '\n', sizeof buf);
-               memcpy(&sortbuf[i*SIZ], buf, (size_t)SIZ);
-       }
-
-       /* Do the sort */
-       qsort(sortbuf, rows, SIZ, idlecmp);
-
-       /* Copy back to a \n delimited list */
-       strcpy(retbuf, "");
-       for (i=0; i<rows; ++i) {
-               strcat(retbuf, &sortbuf[i*SIZ]);
-               if (i<(rows-1)) strcat(retbuf, "\n");
-       }
-    free(listing);
-    free(sortbuf);
-       return(retbuf);
-}
-
-
-
-/*
- * Display list of users currently logged on to the server
- */
-void who_is_online(CtdlIPC *ipc, int longlist)
-{
-       char buf[SIZ], username[SIZ], roomname[SIZ], fromhost[SIZ];
-       char flags[SIZ];
-       char actual_user[SIZ], actual_room[SIZ], actual_host[SIZ];
-       char clientsoft[SIZ];
-       time_t timenow = 0;
-       time_t idletime, idlehours, idlemins, idlesecs;
-       int last_session = (-1);
-       int skipidle = 0;
-       char *listing = NULL;
-       int r;                          /* IPC response code */
-    
-       if (longlist == 2) {
-               longlist = 0;
-               skipidle = 1;
-       }
-
-       if (!longlist) {
-               color(BRIGHT_WHITE);
-               pprintf("           User Name               Room          ");
-               if (screenwidth >= 80) pprintf(" Idle        From host");
-               pprintf("\n");
-               color(DIM_WHITE);
-               pprintf("   ------------------------- --------------------");
-               if (screenwidth >= 80) pprintf(" ---- ------------------------");
-               pprintf("\n");
-       }
-       r = CtdlIPCOnlineUsers(ipc, &listing, &timenow, buf);
-       listing = SortOnlineUsers(listing);
-       if (r / 100 == 1) {
-               while (!IsEmptyStr(listing)) {
-                       int isidle = 0;
-                       
-                       /* Get another line */
-                       extract_token(buf, listing, 0, '\n', sizeof buf);
-                       remove_token(listing, 0, '\n');
-
-                       extract_token(username, buf, 1, '|', sizeof username);
-                       extract_token(roomname, buf, 2, '|', sizeof roomname);
-                       extract_token(fromhost, buf, 3, '|', sizeof fromhost);
-                       extract_token(clientsoft, buf, 4, '|', sizeof clientsoft);
-                       extract_token(flags, buf, 7, '|', sizeof flags);
-
-                       idletime = timenow - extract_long(buf, 5);
-                       idlehours = idletime / 3600;
-                       idlemins = (idletime - (idlehours * 3600)) / 60;
-                       idlesecs = (idletime - (idlehours * 3600) - (idlemins * 60));
-
-                       if (idletime > rc_idle_threshold) {
-                               if (skipidle) {
-                                       isidle = 1;
-                               }
-                       }
-
-                       if (longlist) {
-                               extract_token(actual_user, buf, 8, '|', sizeof actual_user);
-                               extract_token(actual_room, buf, 9, '|', sizeof actual_room);
-                               extract_token(actual_host, buf, 10, '|', sizeof actual_host);
-
-                               pprintf("  Flags: %s\n", flags);
-                               pprintf("Session: %d\n", extract_int(buf, 0));
-                               pprintf("   Name: %s\n", username);
-                               pprintf("In room: %s\n", roomname);
-                               pprintf("   Host: %s\n", fromhost);
-                               pprintf(" Client: %s\n", clientsoft);
-                               pprintf("   Idle: %ld:%02ld:%02ld\n",
-                                       (long) idlehours,
-                                       (long) idlemins,
-                                       (long) idlesecs);
-
-                               if ( (!IsEmptyStr(actual_user)&&
-                                     !IsEmptyStr(actual_room)&&
-                                     !IsEmptyStr(actual_host))) {
-                                       pprintf("(really ");
-                                       if (!IsEmptyStr(actual_user)) pprintf("<%s> ", actual_user);
-                                       if (!IsEmptyStr(actual_room)) pprintf("in <%s> ", actual_room);
-                                       if (!IsEmptyStr(actual_host)) pprintf("from <%s> ", actual_host);
-                                       pprintf(")\n");
-                               }
-                               pprintf("\n");
-
-                       } else {
-                               if (isidle == 0) {
-                                       if (extract_int(buf, 0) == last_session) {
-                                               pprintf("        ");
-                                       }
-                                       else {
-                                               color(BRIGHT_MAGENTA);
-                                               pprintf("%-3s", flags);
-                                       }
-                                       last_session = extract_int(buf, 0);
-                                       color(BRIGHT_CYAN);
-                                       pprintf("%-25s ", username);
-                                       color(BRIGHT_MAGENTA);
-                                       roomname[20] = 0;
-                                       pprintf("%-20s", roomname);
-
-                                       if (screenwidth >= 80) {
-                                               pprintf(" ");
-                                               if (idletime > rc_idle_threshold) {
-                                                       /* over 1000d, must be gone fishing */
-                                                       if (idlehours > 23999) {
-                                                               pprintf("fish");
-                                                       /* over 10 days */
-                                                       } else if (idlehours > 239) {
-                                                               pprintf("%3ldd", idlehours / 24);
-                                                       /* over 10 hours */
-                                                       } else if (idlehours > 9) {
-                                                               pprintf("%1ldd%02ld",
-                                                                       idlehours / 24,
-                                                                       idlehours % 24);
-                                                       /* less than 10 hours */
-                                                       }
-                                                       else {
-                                                               pprintf("%1ld:%02ld", idlehours, idlemins);
-                                                       }
-                                               }
-                                               else {
-                                                       pprintf("    ");
-                                               }
-                                               pprintf(" ");
-                                               color(BRIGHT_CYAN);
-                                               fromhost[24] = '\0';
-                                               pprintf("%-24s", fromhost);
-                                       }
-                                       pprintf("\n");
-                                       color(DIM_WHITE);
-                               }
-                       }
-               }
-       }
-       free(listing);
-}
-
-void enternew(CtdlIPC *ipc, char *desc, char *buf, int maxlen)
-{
-       char bbb[128];
-       snprintf(bbb, sizeof bbb, "Enter in your new %s: ", desc);
-       newprompt(bbb, buf, maxlen);
-}
-
-
-
-int shift(int argc, char **argv, int start, int count) {
-       int i;
-
-       for (i=start; i<(argc-count); ++i) {
-               argv[i] = argv[i+count];
-       }
-       argc = argc - count;
-       return argc;
-}
-
-static void statusHook(char *s) {
-       sln_printf(s);
-       sln_flush();
-}
-
-/*
- * main
- */
-int main(int argc, char **argv)
-{
-       int a, b, mcmd;
-       char aaa[100], bbb[100];/* general purpose variables */
-       char argbuf[64];        /* command line buf */
-       char nonce[NONCE_SIZE];
-       char *telnet_client_host = NULL;
-       char *sptr, *sptr2;     /* USed to extract the nonce */
-       char hexstring[MD5_HEXSTRING_SIZE];
-       int stored_password = 0;
-       char password[SIZ];
-       struct ctdlipcmisc chek;
-       struct ctdluser *myself = NULL;
-       CtdlIPC* ipc;                   /* Our server connection */
-       int r;                          /* IPC result code */
-       int rv = 0;                     /* fetch but ignore syscall return value to suppress warnings */
-
-       int relh=0;
-       int home=0;
-       char relhome[PATH_MAX]="";
-       char ctdldir[PATH_MAX]=CTDLDIR;
-    int lp; 
-#ifdef HAVE_BACKTRACE
-       eCrashParameters params;
-//     eCrashSymbolTable symbol_table;
-#endif
-       calc_dirs_n_files(relh, home, relhome, ctdldir, 0);
-
-#ifdef HAVE_BACKTRACE
-       bzero(&params, sizeof(params));
-       params.filename = file_pid_paniclog;
-//     panic_fd=open(file_pid_paniclog, O_APPEND|O_CREAT|O_DIRECT);
-       params.filep = fopen(file_pid_paniclog, "a+");
-       params.debugLevel = ECRASH_DEBUG_VERBOSE;
-       params.dumpAllThreads = TRUE;
-       params.useBacktraceSymbols = 1;
-///    BuildSymbolTable(&symbol_table);
-//     params.symbolTable = &symbol_table;
-       params.signals[0]=SIGSEGV;
-       params.signals[1]=SIGILL;
-       params.signals[2]=SIGBUS;
-       params.signals[3]=SIGABRT;
-
-       eCrash_Init(&params);
-#endif 
-       setIPCDeathHook(screen_delete);
-       setIPCErrorPrintf(err_printf);
-       setCryptoStatusHook(statusHook);
-       
-       /* Permissions sanity check - don't run citadel setuid/setgid */
-       if (getuid() != geteuid()) {
-               err_printf("Please do not run citadel setuid!\n");
-               logoff(NULL, 3);
-       } else if (getgid() != getegid()) {
-               err_printf("Please do not run citadel setgid!\n");
-               logoff(NULL, 3);
-       }
-
-       stty_ctdl(SB_SAVE);     /* Store the old terminal parameters */
-       load_command_set();     /* parse the citadel.rc file */
-       stty_ctdl(SB_NO_INTR);  /* Install the new ones */
-       /* signal(SIGHUP, dropcarr);FIXME */    /* Cleanup gracefully if carrier is dropped */
-       signal(SIGPIPE, dropcarr);      /* Cleanup gracefully if local conn. dropped */
-       signal(SIGTERM, dropcarr);      /* Cleanup gracefully if terminated */
-       signal(SIGCONT, catch_sigcont); /* Catch SIGCONT so we can reset terminal */
-#ifdef SIGWINCH
-       signal(SIGWINCH, scr_winch);    /* Window resize signal */
-#endif
-
-#ifdef HAVE_OPENSSL
-       arg_encrypt = RC_DEFAULT;
-#endif
-#if defined(HAVE_CURSES_H) && !defined(DISABLE_CURSES)
-       arg_screen = RC_DEFAULT;
-#endif
-
-       /* 
-        * Handle command line options as if we were called like /bin/login
-        * (i.e. from in.telnetd)
-        */
-       for (a=0; a<argc; ++a) {
-               if ((argc > a+1) && (!strcmp(argv[a], "-h")) ) {
-                       telnet_client_host = argv[a+1];
-                       argc = shift(argc, argv, a, 2);
-               }
-               if (!strcmp(argv[a], "-x")) {
-#ifdef HAVE_OPENSSL
-                       arg_encrypt = RC_NO;
-#endif
-                       argc = shift(argc, argv, a, 1);
-               }
-               if (!strcmp(argv[a], "-X")) {
-#ifdef HAVE_OPENSSL
-                       arg_encrypt = RC_YES;
-                       argc = shift(argc, argv, a, 1);
-#else
-                       fprintf(stderr, "Not compiled with encryption support");
-                       return 1;
-#endif
-               }
-               if (!strcmp(argv[a], "-s")) {
-#if defined(HAVE_CURSES_H) && !defined(DISABLE_CURSES)
-                       arg_screen = RC_NO;
-#endif
-                       argc = shift(argc, argv, a, 1);
-               }
-               if (!strcmp(argv[a], "-S")) {
-#if defined(HAVE_CURSES_H) && !defined(DISABLE_CURSES)
-                       arg_screen = RC_YES;
-#endif
-                       argc = shift(argc, argv, a, 1);
-               }
-               if (!strcmp(argv[a], "-p")) {
-                       struct stat st;
-               
-                       if (chdir(CTDLDIR) < 0) {
-                               perror("can't change to " CTDLDIR);
-                               logoff(NULL, 3);
-                       }
-
-                       /*
-                        * Drop privileges if necessary. We stat
-                        * citadel.config to get the uid/gid since it's
-                        * guaranteed to have the uid/gid we want.
-                        */
-                       if (!getuid() || !getgid()) {
-                               if (stat(file_citadel_config, &st) < 0) {
-                                       perror("couldn't stat citadel.config");
-                                       logoff(NULL, 3);
-                               }
-                               if (!getgid() && (setgid(st.st_gid) < 0)) {
-                                       perror("couldn't change gid");
-                                       logoff(NULL, 3);
-                               }
-                               if (!getuid() && (setuid(st.st_uid) < 0)) {
-                                       perror("couldn't change uid");
-                                       logoff(NULL, 3);
-                               }
-                               /*
-                                 scr_printf("Privileges changed to uid %d gid %d\n",
-                                 getuid(), getgid());
-                               */
-                       }
-                       argc = shift(argc, argv, a, 1);
-               }
-       }
-       
-
-       screen_new();
-
-#ifdef __CYGWIN__
-       newprompt("Connect to (return for local server): ", hostbuf, 64);
-#endif
-
-       sln_printf("Attaching to server... \r");
-       sln_flush();
-       ipc = CtdlIPC_new(argc, argv, hostbuf, portbuf);
-       if (!ipc) {
-               screen_delete();
-               error_printf("Can't connect: %s\n", strerror(errno));
-               logoff(NULL, 3);
-       }
-#if defined(HAVE_CURSES_H) && !defined(DISABLE_CURSES)
-       CtdlIPC_SetNetworkStatusCallback(ipc, wait_indicator);
-#endif
-       ipc_for_signal_handlers = ipc;  /* KLUDGE cover your eyes */
-
-       CtdlIPC_chat_recv(ipc, aaa);
-       if (aaa[0] != '2') {
-               scr_printf("%s\n", &aaa[4]);
-               logoff(ipc, atoi(aaa));
-       }
-
-       /* If there is a [nonce] at the end, put the nonce in <nonce>, else nonce
-        * is zeroized.
-        */
-       
-       if ((sptr = strchr(aaa, '<')) == NULL)
-               {
-                       nonce[0] = '\0';
-               }
-       else
-               {
-                       if ((sptr2 = strchr(sptr, '>')) == NULL)
-                               {
-                                       nonce[0] = '\0';
-                               }
-                       else
-                               {
-                                       sptr2++;
-                                       *sptr2 = '\0';
-                                       strncpy(nonce, sptr, (size_t)NONCE_SIZE);
-                               }
-               }
-
-#ifdef HAVE_OPENSSL
-       /* Evaluate encryption preferences */
-       if (arg_encrypt != RC_NO && rc_encrypt != RC_NO) {
-               if (!ipc->isLocal || arg_encrypt == RC_YES || rc_encrypt == RC_YES) {
-                       secure = (CtdlIPCStartEncryption(ipc, aaa) / 100 == 2) ? 1 : 0;
-                       if (!secure)
-                               error_printf("Can't encrypt: %s\n", aaa);
-               }
-       }
-#endif
-
-       get_serv_info(ipc, telnet_client_host);
-       scr_printf("%-24s\n%s\n%s\n", ipc->ServInfo.software, ipc->ServInfo.humannode,
-                  ipc->ServInfo.site_location);
-       scr_flush();
-
-       status_line(ipc->ServInfo.humannode, ipc->ServInfo.site_location, NULL,
-                   secure, -1);
-
-       screenwidth = 80;       /* default screen dimensions */
-       screenheight = 24;
-       
-       scr_printf(" pause    next    stop\n");
-       scr_printf(" ctrl-s  ctrl-o  ctrl-c\n\n");
-       formout(ipc, "hello");  /* print the opening greeting */
-       scr_printf("\n");
-
- GSTA: /* See if we have a username and password on disk */
-       if (rc_remember_passwords) {
-               get_stored_password(hostbuf, portbuf, fullname, password);
-               if (!IsEmptyStr(fullname)) {
-                       r = CtdlIPCTryLogin(ipc, fullname, aaa);
-                       if (r / 100 == 3) {
-                               if (*nonce) {
-                                       r = CtdlIPCTryApopPassword(ipc, make_apop_string(password, nonce, hexstring, sizeof hexstring), aaa);
-                               } else {
-                                       r = CtdlIPCTryPassword(ipc, password, aaa);
-                               }
-                       }
-
-                       if (r / 100 == 2) {
-                               load_user_info(aaa);
-                               stored_password = 1;
-                               goto PWOK;
-                       } else {
-                               set_stored_password(hostbuf, portbuf, "", "");
-                       }
-               }
-       }
-
-       termn8 = 0;
-       newnow = 0;
-       do {
-               if (!IsEmptyStr(rc_username)) {
-                       strcpy(fullname, rc_username);
-               } else {
-                       newprompt("Enter your name: ", fullname, 29);
-               }
-               strproc(fullname);
-               if (!strcasecmp(fullname, "new")) {     /* just in case */
-                       scr_printf("Please enter the name you wish to log in with.\n");
-               }
-       } while (
-                (!strcasecmp(fullname, "bbs"))
-                || (!strcasecmp(fullname, "new"))
-                || (IsEmptyStr(fullname)));
-
-       if (!strcasecmp(fullname, "off")) {
-               mcmd = 29;
-               goto TERMN8;
-       }
-       /* sign on to the server */
-       r = CtdlIPCTryLogin(ipc, fullname, aaa);
-       if (r / 100 != 3)
-               goto NEWUSR;
-
-       /* password authentication */
-       if (!IsEmptyStr(rc_password)) {
-               strcpy(password, rc_password);
-       } else {
-               newprompt("\rPlease enter your password: ", password, -19);
-       }
-       strproc(password);
-
-       if (*nonce) {
-               r = CtdlIPCTryApopPassword(ipc, make_apop_string(password, nonce, hexstring, sizeof hexstring), aaa);
-       } else {
-               r = CtdlIPCTryPassword(ipc, password, aaa);
-       }
-       
-       if (r / 100 == 2) {
-               load_user_info(aaa);
-               offer_to_remember_password(ipc, hostbuf, portbuf,
-                                          fullname, password);
-               goto PWOK;
-       }
-       scr_printf("<< wrong password >>\n");
-       if (!IsEmptyStr(rc_password))
-               logoff(ipc, 2);
-       goto GSTA;
-
-NEWUSR:        if (IsEmptyStr(rc_password)) {
-               scr_printf("'%s' not found.\n", fullname);
-               scr_printf("Type 'off' if you would like to exit.\n");
-               if (ipc->ServInfo.newuser_disabled == 1) {
-                       goto GSTA;
-               }
-               scr_printf("Do you want to create a new user account called '%s'? ",
-                       fullname);
-               if (yesno() == 0) {
-                       goto GSTA;
-               }
-       }
-
-       r = CtdlIPCCreateUser(ipc, fullname, 1, aaa);
-       if (r / 100 != 2) {
-               scr_printf("%s\n", aaa);
-               goto GSTA;
-       }
-       load_user_info(aaa);
-
-       while (set_password(ipc) != 0);
-       newnow = 1;
-
-       enter_config(ipc, 1);
-
- PWOK:
-       /* Switch color support on or off if we're in user mode */
-       if (rc_ansi_color == 3) {
-               if (userflags & US_COLOR)
-                       enable_color = 1;
-               else
-                       enable_color = 0;
-       }
-
-       color(BRIGHT_WHITE);
-       scr_printf("\n%s\nAccess level: %d (%s)\n"
-                  "User #%ld / Login #%d",
-                  fullname, axlevel, axdefs[(int) axlevel],
-                  usernum, timescalled);
-       if (lastcall > 0L) {
-               scr_printf(" / Last login: %s",
-                          asctime(localtime(&lastcall)));
-       }
-       scr_printf("\n");
-
-       r = CtdlIPCMiscCheck(ipc, &chek, aaa);
-       if (r / 100 == 2) {
-               b = chek.newmail;
-               if (b > 0) {
-                       color(BRIGHT_RED);
-                       if (b == 1)
-                               scr_printf("*** You have a new private message in Mail>\n");
-                       if (b > 1)
-                               scr_printf("*** You have %d new private messages in Mail>\n", b);
-                       color(DIM_WHITE);
-                       if (!IsEmptyStr(rc_gotmail_cmd)) {
-                               rv = system(rc_gotmail_cmd);
-                       }
-               }
-               if ((axlevel >= 6) && (chek.needvalid > 0)) {
-                       scr_printf("*** Users need validation\n");
-               }
-               if (chek.needregis > 0) {
-                       scr_printf("*** Please register.\n");
-                       formout(ipc, "register");
-                       entregis(ipc);
-               }
-       }
-       /* Make up some temporary filenames for use in various parts of the
-        * program.  Don't mess with these once they've been set, because we
-        * will be unlinking them later on in the program and we don't
-        * want to delete something that we didn't create. */
-       CtdlMakeTempFileName(temp, sizeof temp);
-       CtdlMakeTempFileName(temp2, sizeof temp2);
-       CtdlMakeTempFileName(tempdir, sizeof tempdir);
-
-       /* Get screen dimensions.  First we go to a default of 80x24.  Then
-        * we try to get the user's actual screen dimensions off the server.
-        * However, if we're running on an xterm, all this stuff is
-        * irrelevant because we're going to dynamically size the screen
-        * during the session.
-        */
-       screenwidth = 80;
-       screenheight = 24;
-       r = CtdlIPCGetConfig(ipc, &myself, aaa);
-       if (r == 2) {
-               screenwidth = myself->USscreenwidth;
-               screenheight = myself->USscreenheight;
-       }
-       if (getenv("TERM") != NULL)
-               if (!strcmp(getenv("TERM"), "xterm")) {
-                       have_xterm = 1;
-               }
-#ifdef TIOCGWINSZ
-       check_screen_dims();
-#endif
-
-       set_floor_mode(ipc);
-
-       /* Enter the lobby */
-       dotgoto(ipc, "_BASEROOM_", 1, 0);
-
-       /* Main loop for the system... user is logged in. */
-    free(uglist[0]);
-       uglistsize = 0;
-
-       if (newnow == 1)
-               readmsgs(ipc, LastMessages, ReadForward, 5);
-       else
-               readmsgs(ipc, NewMessages, ReadForward, 0);
-
-       /* MAIN COMMAND LOOP */
-       do {
-               mcmd = getcmd(ipc, argbuf);     /* Get keyboard command */
-
-#ifdef TIOCGWINSZ
-               check_screen_dims();            /* if xterm, get screen size */
-#endif
-
-               if (termn8 == 0)
-                       switch (mcmd) {
-                       case 1:
-                               formout(ipc, "help");
-                               break;
-                       case 4:
-                               entmsg(ipc, 0, ((userflags & US_EXTEDIT) ? 2 : 0), 0);
-                               break;
-                       case 36:
-                               entmsg(ipc, 0, 1, 0);
-                               break;
-                       case 46:
-                               entmsg(ipc, 0, 2, 0);
-                               break;
-                       case 78:
-                               entmsg(ipc, 0, ((userflags & US_EXTEDIT) ? 2 : 0), 1);
-                               break;
-                       case 5:                         /* <G>oto */
-                               updatels(ipc);
-                               gotonext(ipc);
-                               break;
-                       case 47:                        /* <A>bandon */
-                               if (!rc_alt_semantics) {
-                                       updatelsa(ipc);
-                               }
-                               gotonext(ipc);
-                               break;
-                       case 90:                        /* <.A>bandon */
-                               if (!rc_alt_semantics)
-                                       updatelsa(ipc);
-                               dotgoto(ipc, argbuf, 0, 0);
-                               break;
-                       case 58:                        /* <M>ail */
-                               updatelsa(ipc);
-                               dotgoto(ipc, "_MAIL_", 1, 0);
-                               break;
-                       case 20:
-                               if (!IsEmptyStr(argbuf)) {
-                                       updatels(ipc);
-                                       dotgoto(ipc, argbuf, 0, 0);
-                               }
-                               break;
-                       case 52:
-                               if (!IsEmptyStr(argbuf)) {
-                                       if (rc_alt_semantics) {
-                                               updatelsa(ipc);
-                                       }
-                                       dotgoto(ipc, argbuf, 0, 0);
-                               }
-                               break;
-                       case 95: /* what exactly is the numbering scheme supposed to be anyway? --Ford, there isn't one. -IO */
-                               dotungoto(ipc, argbuf);
-                               break;
-                       case 10:
-                               readmsgs(ipc, AllMessages, ReadForward, 0);
-                               break;
-                       case 9:
-                               readmsgs(ipc, LastMessages, ReadForward, 5);
-                               break;
-                       case 13:
-                               readmsgs(ipc, NewMessages, ReadForward, 0);
-                               break;
-                       case 11:
-                               readmsgs(ipc, AllMessages, ReadReverse, 0);
-                               break;
-                       case 12:
-                               readmsgs(ipc, OldMessages, ReadReverse, 0);
-                               break;
-                       case 71:
-                               readmsgs(ipc, LastMessages, ReadForward,
-                                               atoi(argbuf));
-                               break;
-                       case 7:
-                               forget(ipc);
-                               break;
-                       case 18:
-                               subshell();
-                               break;
-                       case 38:
-                               updatels(ipc);
-                               entroom(ipc);
-                               break;
-                       case 22:
-                               killroom(ipc);
-                               break;
-                       case 32:
-                               userlist(ipc, argbuf);
-                               break;
-                       case 27:
-                               invite(ipc);
-                               break;
-                       case 28:
-                               kickout(ipc);
-                               break;
-                       case 23:
-                               editthisroom(ipc);
-                               break;
-                       case 14:
-                               roomdir(ipc);
-                               break;
-                       case 33:
-                               download(ipc, 0);
-                               break;
-                       case 34:
-                               download(ipc, 1);
-                               break;
-                       case 31:
-                               download(ipc, 2);
-                               break;
-                       case 43:
-                               download(ipc, 3);
-                               break;
-                       case 45:
-                               download(ipc, 4);
-                               break;
-                       case 55:
-                               download(ipc, 5);
-                               break;
-                       case 39:
-                               upload(ipc, 0);
-                               break;
-                       case 40:
-                               upload(ipc, 1);
-                               break;
-                       case 42:
-                               upload(ipc, 2);
-                               break;
-                       case 44:
-                               upload(ipc, 3);
-                               break;
-                       case 57:
-                               cli_upload(ipc);
-                               break;
-                       case 16:
-                               ungoto(ipc);
-                               break;
-                       case 24:
-                               whoknows(ipc);
-                               break;
-                       case 26:
-                               validate(ipc);
-                               break;
-                       case 29:
-                       case 30:
-                               if (!rc_alt_semantics) {
-                                       updatels(ipc);
-                               }
-                               termn8 = 1;
-                               break;
-                       case 48:
-                               enterinfo(ipc);
-                               break;
-                       case 49:
-                               readinfo(ipc);
-                               break;
-                       case 72:
-                               cli_image_upload(ipc, "_userpic_");
-                               break;
-                       case 73:
-                               cli_image_upload(ipc, "_roompic_");
-                               break;
-
-                       case 74:
-                               snprintf(aaa, sizeof aaa, "_floorpic_|%d", curr_floor);
-                               cli_image_upload(ipc, aaa);
-                               break;
-
-                       case 75:
-                               enternew(ipc, "roomname", aaa, 20);
-                               r = CtdlIPCChangeRoomname(ipc, aaa, bbb);
-                               if (r / 100 != 2)
-                                       scr_printf("\n%s\n", bbb);
-                               break;
-                       case 76:
-                               enternew(ipc, "hostname", aaa, 25);
-                               r = CtdlIPCChangeHostname(ipc, aaa, bbb);
-                               if (r / 100 != 2)
-                                       scr_printf("\n%s\n", bbb);
-                               break;
-                       case 77:
-                               enternew(ipc, "username", aaa, 32);
-                               r = CtdlIPCChangeUsername(ipc, aaa, bbb);
-                               if (r / 100 != 2)
-                                       scr_printf("\n%s\n", bbb);
-                               break;
-
-                       case 35:
-                               set_password(ipc);
-                               break;
-
-                       case 21:
-                               if (argbuf[0] == 0)
-                                       strcpy(aaa, "?");
-                               display_help(ipc, argbuf);
-                               break;
-
-                       case 41:
-                               formout(ipc, "register");
-                               entregis(ipc);
-                               break;
-
-                       case 15:
-                               scr_printf("Are you sure (y/n)? ");
-                               if (yesno() == 1) {
-                                       if (!rc_alt_semantics)
-                                               updatels(ipc);
-                                       a = 0;
-                                       termn8 = 1;
-                               }
-                               break;
-
-                       case 85:
-                               scr_printf("All users will be disconnected!  "
-                                          "Really terminate the server? ");
-                               if (yesno() == 1) {
-                                       if (!rc_alt_semantics)
-                                               updatels(ipc);
-                                       r = CtdlIPCTerminateServerNow(ipc, aaa);
-                                       scr_printf("%s\n", aaa);
-                                       if (r / 100 == 2) {
-                                               a = 0;
-                                               termn8 = 1;
-                                       }
-                               }
-                               break;
-
-                       case 86:
-                               scr_printf("Do you really want to schedule a "
-                                          "server shutdown? ");
-                               if (yesno() == 1) {
-                                       r = CtdlIPCTerminateServerScheduled(ipc, 1, aaa);
-                                       if (r / 100 == 2) {
-                                               if (atoi(aaa)) {
-                                                       scr_printf(
-                                                                  "The Citadel server will terminate when all users are logged off.\n"
-                                                                  );
-                                               } else {
-                                                       scr_printf(
-                                                                  "The Citadel server will not terminate.\n"
-                                                                  );
-                                               }
-                                       }
-                               }
-                               break;
-
-                       case 87:
-                               network_config_management(ipc, "listrecp",
-                                                         "Message-by-message mailing list recipients");
-                               break;
-
-                       case 94:
-                               network_config_management(ipc, "digestrecp",
-                                                         "Digest mailing list recipients");
-                               break;
-
-                       case 89:
-                               network_config_management(ipc, "ignet_push_share",
-                                                         "Nodes with which we share this room");
-                               break;
-
-                       case 88:
-                               do_ignet_configuration(ipc);
-                               break;
-
-                       case 92:
-                               do_filterlist_configuration(ipc);
-                               break;
-
-                       case 6:
-                               if (rc_alt_semantics) {
-                                       updatelsa(ipc);
-                               }
-                               gotonext(ipc);
-                               break;
-
-                       case 3:
-                               chatmode(ipc);
-                               break;
-
-                       case 2:
-                               if (ipc->isLocal) {
-                                       screen_reset();
-                                       stty_ctdl(SB_RESTORE);
-                                       snprintf(aaa, sizeof aaa, "USERNAME=\042%s\042; export USERNAME;"
-                                                "exec ./subsystem %ld %d %d", fullname,
-                                                usernum, screenwidth, axlevel);
-                                       ka_system(aaa);
-                                       stty_ctdl(SB_NO_INTR);
-                                       screen_set();
-                               } else {
-                                       scr_printf("*** Can't run doors when server is not local.\n");
-                               }
-                               break;
-
-                       case 17:
-                               who_is_online(ipc, 0);
-                               break;
-
-                       case 79:
-                               who_is_online(ipc, 1);
-                               break;
-
-                       case 91:
-                               who_is_online(ipc, 2);
-                               break;
-                
-                       case 80:
-                               do_system_configuration(ipc);
-                               break;
-
-                       case 82:
-                               do_internet_configuration(ipc);
-                               break;
-
-                       case 83:
-                               check_message_base(ipc);
-                               break;
-
-                       case 84:
-                               quiet_mode(ipc);
-                               break;
-
-                       case 93:
-                               stealth_mode(ipc);
-                               break;
-
-                       case 50:
-                               enter_config(ipc, 2);
-                               break;
-
-                       case 37:
-                               enter_config(ipc, 0);
-                               set_floor_mode(ipc);
-                               break;
-
-                       case 59:
-                               enter_config(ipc, 3);
-                               set_floor_mode(ipc);
-                               break;
-
-                       case 60:
-                               gotofloor(ipc, argbuf, GF_GOTO);
-                               break;
-
-                       case 61:
-                               gotofloor(ipc, argbuf, GF_SKIP);
-                               break;
-
-                       case 62:
-                               forget_this_floor(ipc);
-                               break;
-
-                       case 63:
-                               create_floor(ipc);
-                               break;
-
-                       case 64:
-                               edit_floor(ipc);
-                               break;
-
-                       case 65:
-                               kill_floor(ipc);
-                               break;
-
-                       case 66:
-                               enter_bio(ipc);
-                               break;
-
-                       case 67:
-                               read_bio(ipc);
-                               break;
-
-                       case 25:
-                               edituser(ipc, 25);
-                               break;
-
-                       case 96:
-                               edituser(ipc, 96);
-                               break;
-
-                       case 8:
-                               knrooms(ipc, floor_mode);
-                               scr_printf("\n");
-                               break;
-
-                       case 68:
-                               knrooms(ipc, 2);
-                               scr_printf("\n");
-                               break;
-
-                       case 69:
-                               misc_server_cmd(ipc, argbuf);
-                               break;
-
-                       case 70:
-                               edit_system_message(ipc, argbuf);
-                               break;
-
-                       case 19:
-                               listzrooms(ipc);
-                               scr_printf("\n");
-                               break;
-
-                       case 51:
-                               deletefile(ipc);
-                               break;
-
-                       case 54:
-                               movefile(ipc);
-                               break;
-
-                       case 56:
-                               page_user(ipc);
-                               break;
-
-            case 110:           /* <+> Next room */
-                                gotoroomstep(ipc, 1, 0);
-                            break;
-
-            case 111:           /* <-> Previous room */
-                 gotoroomstep(ipc, 0, 0);
-                            break;
-
-                       case 112:           /* <>> Next floor */
-                 gotofloorstep(ipc, 1, GF_GOTO);
-                            break;
-
-                       case 113:           /* <<> Previous floor */
-                 gotofloorstep(ipc, 0, GF_GOTO);
-                                break;
-
-            case 116:           /* <.> skip to <+> Next room */
-                 gotoroomstep(ipc, 1, 1);
-                            break;
-
-            case 117:           /* <.> skip to <-> Previous room */
-                 gotoroomstep(ipc, 0, 1);
-                            break;
-
-                       case 118:           /* <.> skip to <>> Next floor */
-                 gotofloorstep(ipc, 1, GF_SKIP);
-                            break;
-
-                       case 119:           /* <.> skip to <<> Previous floor */
-                 gotofloorstep(ipc, 0, GF_SKIP);
-                                break;
-
-                       case 114:           
-                 read_config(ipc);
-                                break;
-
-                       case 115:           
-                 system_info(ipc);
-                                break;
-
-                       case 120:           /* .KAnonymous */
-                        dotknown(ipc, 0, NULL);
-                                break;
-
-                       case 121:           /* .KDirectory */
-                        dotknown(ipc, 1, NULL);
-                                break;
-
-                       case 122:           /* .KMatch */
-                        dotknown(ipc, 2, argbuf);
-                                break;
-
-                       case 123:           /* .KpreferredOnly */
-                        dotknown(ipc, 3, NULL);
-                                break;
-
-                       case 124:           /* .KPrivate */
-                        dotknown(ipc, 4, NULL);
-                                break;
-
-                       case 125:           /* .KRead only */
-                        dotknown(ipc, 5, NULL);
-                                break;
-
-                       case 126:           /* .KShared */
-                        dotknown(ipc, 6, NULL);
-                                break;
-
-                       case 127:           /* Configure POP3 aggregation */
-                               do_pop3client_configuration(ipc);
-                               break;
-
-                       case 128:           /* Configure XML/RSS feed retrieval */
-                               do_rssclient_configuration(ipc);
-                               break;
-
-                       default: /* allow some math on the command */
-                               /* commands 100... to 100+MAX_EDITORS-1 will
-                                  call the appropriate editor... in other
-                                  words, command numbers 100+ will match
-                                  the citadel.rc keys editor0, editor1, etc.*/
-                               if (mcmd >= 100 && mcmd < (100+MAX_EDITORS))
-                               {
-                                       /* entmsg mode >=2 select editor */
-                                       entmsg(ipc, 0, mcmd - 100 + 2, 0);
-                                       break;
-                               }
-                       }       /* end switch */
-       } while (termn8 == 0);
-
-TERMN8:        scr_printf("%s logged out.", fullname);
-       termn8 = 0;
-       color(ORIGINAL_PAIR);
-       scr_printf("\n");
-       while (marchptr != NULL) {
-               remove_march(marchptr->march_name, 0);
-       }
-       if (mcmd == 30) {
-               sln_printf("\n\nType 'off' to disconnect, or next user...\n");
-       }
-       CtdlIPCLogout(ipc);
-       if ((mcmd == 29) || (mcmd == 15)) {
-               screen_delete();
-               stty_ctdl(SB_RESTORE);
-               formout(ipc, "goodbye");
-               logoff(ipc, 0);
-       }
-       /* Free the ungoto list */
-       for (lp = 0; lp < uglistsize; lp++) {
-               free(uglist[lp]);
-       }
-    uglistsize = 0;
-       goto GSTA;
-
-}      /* end main() */
-
diff --git a/citadel/citadel_dirs.c b/citadel/citadel_dirs.c
deleted file mode 100644 (file)
index 9bc46c8..0000000
+++ /dev/null
@@ -1,377 +0,0 @@
-/*
- * citadel_dirs.c : calculate pathnames for various files used in the Citadel system
- *
- * Copyright (c) 1987-2009 by the citadel.org team
- *
- *  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 3 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; if not, write to the Free Software
- *  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
- */
-#include <unistd.h>
-#include <stdio.h>
-#include <sys/stat.h>
-#include <ctype.h>
-#include <string.h>
-#include <limits.h>
-
-
-#if TIME_WITH_SYS_TIME
-# include <sys/time.h>
-# include <time.h>
-#else
-# if HAVE_SYS_TIME_H
-#  include <sys/time.h>
-# else
-#  include <time.h>
-# endif
-#endif
-
-#include <errno.h>
-#include <libcitadel.h>
-
-
-#include "citadel.h"
-
-/* our directories... */
-char ctdl_home_directory[PATH_MAX] = "";
-char ctdl_bio_dir[PATH_MAX]="bio";
-char ctdl_bb_dir[PATH_MAX]="bitbucket";
-char ctdl_data_dir[PATH_MAX]="data";
-char ctdl_dspam_dir[PATH_MAX]="dspam";
-char ctdl_file_dir[PATH_MAX]="files";
-char ctdl_hlp_dir[PATH_MAX]="help";
-char ctdl_shared_dir[PATH_MAX]="";
-char ctdl_image_dir[PATH_MAX]="images";
-char ctdl_info_dir[PATH_MAX]="info";
-char ctdl_key_dir[PATH_MAX]=SSL_DIR;
-char ctdl_message_dir[PATH_MAX]="messages";
-char ctdl_usrpic_dir[PATH_MAX]="userpics";
-char ctdl_bbsbase_dir[PATH_MAX]="";
-char ctdl_etc_dir[PATH_MAX]="";
-char ctdl_autoetc_dir[PATH_MAX]="";
-/* attention! this may be non volatile on some oses */
-char ctdl_run_dir[PATH_MAX]="";
-char ctdl_spool_dir[PATH_MAX]="network";
-char ctdl_netout_dir[PATH_MAX]="network/spoolout";
-char ctdl_netin_dir[PATH_MAX]="network/spoolin";
-char ctdl_netcfg_dir[PATH_MAX]="netconfigs";
-char ctdl_utilbin_dir[PATH_MAX]="";
-char ctdl_sbin_dir[PATH_MAX]="";
-char ctdl_bin_dir[PATH_MAX]="";
-
-/* some of our files, that are needed in several places */
-char file_citadel_control[PATH_MAX]="";
-char file_citadel_rc[PATH_MAX]="";
-char file_citadel_config[PATH_MAX]="";
-char file_lmtp_socket[PATH_MAX]="";
-char file_lmtp_unfiltered_socket[PATH_MAX]="";
-char file_arcq[PATH_MAX]="";
-char file_citadel_socket[PATH_MAX]="";
-char file_mail_aliases[PATH_MAX]="";
-char file_pid_file[PATH_MAX]="";
-char file_pid_paniclog[PATH_MAX]="";
-char file_crpt_file_key[PATH_MAX]="";
-char file_crpt_file_csr[PATH_MAX]="";
-char file_crpt_file_cer[PATH_MAX]="";
-char file_chkpwd[PATH_MAX]="";
-char file_base64[PATH_MAX]="";
-char file_guesstimezone[PATH_MAX]="";
-char file_funambol_msg[PATH_MAX] = "";
-char file_dpsam_conf[PATH_MAX] = "";
-char file_dspam_log[PATH_MAX] = "";
-
-
-
-
-
-#define COMPUTE_DIRECTORY(SUBDIR) memcpy(dirbuffer,SUBDIR, sizeof dirbuffer);\
-       snprintf(SUBDIR,sizeof SUBDIR,  "%s%s%s%s%s%s%s", \
-                        (home&!relh)?ctdl_home_directory:basedir, \
-             ((basedir!=ctdldir)&(home&!relh))?basedir:"/", \
-             ((basedir!=ctdldir)&(home&!relh))?"/":"", \
-                        relhome, \
-             (relhome[0]!='\0')?"/":"",\
-                        dirbuffer,\
-                        (dirbuffer[0]!='\0')?"/":"");
-
-#define DBG_PRINT(A) if (dbg==1) fprintf (stderr,"%s : %s \n", #A, A)
-
-
-void calc_dirs_n_files(int relh, int home, const char *relhome, char  *ctdldir, int dbg)
-{
-       const char* basedir = "";
-       char dirbuffer[PATH_MAX] = "";
-
-       /*
-        * Ok, we keep our binaries either in the citadel base dir,
-        * or in /usr/sbin / /usr/bin
-        */
-       StripSlashes(ctdldir, 1);
-#ifdef HAVE_ETC_DIR
-       snprintf(ctdl_sbin_dir, sizeof ctdl_sbin_dir, "/usr/sbin/");
-       snprintf(ctdl_bin_dir, sizeof ctdl_bin_dir, "/usr/bin/");
-#else
-       snprintf(ctdl_sbin_dir, sizeof ctdl_sbin_dir, ctdldir);
-       snprintf(ctdl_bin_dir, sizeof ctdl_bin_dir, ctdldir);
-#endif
-       StripSlashes(ctdl_sbin_dir, 1);
-       StripSlashes(ctdl_bin_dir, 1);
-
-#ifndef HAVE_AUTO_ETC_DIR
-       basedir=ctdldir;
-#else
-       basedir=AUTO_ETC_DIR;
-#endif
-       COMPUTE_DIRECTORY(ctdl_autoetc_dir);
-       StripSlashes(ctdl_autoetc_dir, 1);
-
-#ifndef HAVE_ETC_DIR
-       basedir=ctdldir;
-#else
-       basedir=ETC_DIR;
-#endif
-       COMPUTE_DIRECTORY(ctdl_netcfg_dir);
-       COMPUTE_DIRECTORY(ctdl_etc_dir);
-       StripSlashes(ctdl_netcfg_dir, 1);
-       StripSlashes(ctdl_etc_dir, 1);
-
-#ifndef HAVE_UTILBIN_DIR
-       basedir=ctdldir;
-#else
-       basedir=UTILBIN_DIR;
-#endif
-       COMPUTE_DIRECTORY(ctdl_utilbin_dir);
-       StripSlashes(ctdl_utilbin_dir, 1);
-
-#ifndef HAVE_RUN_DIR
-       basedir=ctdldir;
-#else
-       basedir=RUN_DIR;
-#endif
-       COMPUTE_DIRECTORY(ctdl_run_dir);
-       StripSlashes(ctdl_run_dir, 1);
-
-#ifndef HAVE_STATICDATA_DIR
-       basedir=ctdldir;
-#else
-       basedir=STATICDATA_DIR;
-#endif
-       COMPUTE_DIRECTORY(ctdl_message_dir);
-       StripSlashes(ctdl_message_dir, 1);
-
-#ifndef HAVE_HELP_DIR
-       basedir=ctdldir;
-#else
-       basedir=HELP_DIR;
-#endif
-       COMPUTE_DIRECTORY(ctdl_hlp_dir);
-       StripSlashes(ctdl_hlp_dir, 1);
-       COMPUTE_DIRECTORY(ctdl_shared_dir);
-       StripSlashes(ctdl_shared_dir, 1);
-
-#ifndef HAVE_DATA_DIR
-       basedir=ctdldir;
-#else
-       basedir=DATA_DIR;
-#endif
-       COMPUTE_DIRECTORY(ctdl_bio_dir);
-       COMPUTE_DIRECTORY(ctdl_bb_dir);
-       COMPUTE_DIRECTORY(ctdl_data_dir);
-       COMPUTE_DIRECTORY(ctdl_dspam_dir);
-       COMPUTE_DIRECTORY(ctdl_file_dir);
-       COMPUTE_DIRECTORY(ctdl_image_dir);
-       COMPUTE_DIRECTORY(ctdl_info_dir);
-       COMPUTE_DIRECTORY(ctdl_usrpic_dir);
-       COMPUTE_DIRECTORY(ctdl_bbsbase_dir);
-
-       StripSlashes(ctdl_bio_dir, 1);
-       StripSlashes(ctdl_bb_dir, 1);
-       StripSlashes(ctdl_data_dir, 1);
-       StripSlashes(ctdl_dspam_dir, 1);
-       StripSlashes(ctdl_file_dir, 1);
-       StripSlashes(ctdl_image_dir, 1);
-       StripSlashes(ctdl_info_dir, 1);
-       StripSlashes(ctdl_usrpic_dir, 1);
-       StripSlashes(ctdl_bbsbase_dir, 1);
-
-#ifndef HAVE_SPOOL_DIR
-       basedir=ctdldir;
-#else
-       basedir=SPOOL_DIR;
-#endif
-       COMPUTE_DIRECTORY(ctdl_spool_dir);
-       COMPUTE_DIRECTORY(ctdl_netout_dir);
-       COMPUTE_DIRECTORY(ctdl_netin_dir);
-
-       StripSlashes(ctdl_spool_dir, 1);
-       StripSlashes(ctdl_netout_dir, 1);
-       StripSlashes(ctdl_netin_dir, 1);
-
-       /* ok, now we know the dirs, calc some commonly used files */
-
-       snprintf(file_arcq, 
-                        sizeof file_arcq,
-                        "%srefcount_adjustments.dat",
-                        ctdl_autoetc_dir);
-       StripSlashes(file_arcq, 0);
-       snprintf(file_citadel_control, 
-                        sizeof file_citadel_control,
-                        "%scitadel.control",
-                        ctdl_autoetc_dir
-                        );
-       StripSlashes(file_citadel_control, 0);
-       snprintf(file_citadel_config, 
-                        sizeof file_citadel_config,
-                        "%scitadel.config",
-                        ctdl_autoetc_dir);
-       StripSlashes(file_citadel_config, 0);
-       snprintf(file_citadel_rc, 
-                        sizeof file_citadel_rc,
-                        "%scitadel.rc",
-                        ctdl_etc_dir);
-       StripSlashes(file_citadel_rc, 0);
-       snprintf(file_lmtp_socket, 
-                        sizeof file_lmtp_socket,
-                        "%slmtp.socket",
-                        ctdl_run_dir);
-       StripSlashes(file_lmtp_socket, 0);
-       snprintf(file_lmtp_unfiltered_socket, 
-                        sizeof file_lmtp_socket,
-                        "%slmtp-unfiltered.socket",
-                        ctdl_run_dir);
-       StripSlashes(file_lmtp_unfiltered_socket, 0);
-       snprintf(file_citadel_socket, 
-                        sizeof file_citadel_socket,
-                               "%scitadel.socket",
-                        ctdl_run_dir);
-       StripSlashes(file_citadel_socket, 0);
-       snprintf(file_pid_file, 
-                sizeof file_pid_file,
-                "%scitadel.pid",
-                ctdl_run_dir);
-       StripSlashes(file_pid_file, 0);
-       snprintf(file_pid_paniclog, 
-                sizeof file_pid_paniclog, 
-                "%spanic.log",
-                ctdl_home_directory);
-       StripSlashes(file_pid_paniclog, 0);
-       snprintf(file_crpt_file_key,
-                sizeof file_crpt_file_key, 
-                "%s/citadel.key",
-                ctdl_key_dir);
-       StripSlashes(file_crpt_file_key, 0);
-       snprintf(file_crpt_file_csr,
-                sizeof file_crpt_file_csr, 
-                "%s/citadel.csr",
-                ctdl_key_dir);
-       StripSlashes(file_crpt_file_csr, 0);
-       snprintf(file_crpt_file_cer,
-                sizeof file_crpt_file_cer, 
-                "%s/citadel.cer",
-                ctdl_key_dir);
-       StripSlashes(file_crpt_file_cer, 0);
-       snprintf(file_chkpwd,
-                sizeof file_chkpwd, 
-                "%schkpwd",
-                ctdl_utilbin_dir);
-       StripSlashes(file_chkpwd, 0);
-       snprintf(file_base64,
-                sizeof file_base64,
-                "%sbase64",
-                ctdl_utilbin_dir);
-       StripSlashes(file_base64, 0);
-       snprintf(file_guesstimezone,
-                sizeof file_guesstimezone,
-                "%sguesstimezone.sh",
-                ctdl_utilbin_dir);
-
-       snprintf(file_dpsam_conf,
-                sizeof file_dpsam_conf,
-                "%sdspam.conf",
-                ctdl_etc_dir);
-       StripSlashes(file_dpsam_conf, 0);
-       snprintf(file_dspam_log, 
-                sizeof file_dspam_log, 
-                "%sdspam.log",
-                ctdl_home_directory);
-       StripSlashes(file_dspam_log, 0);
-       /* 
-        * DIRTY HACK FOLLOWS! due to configs in the network dir in the 
-        * legacy installations, we need to calculate ifdeffed here.
-        */
-       snprintf(file_mail_aliases, 
-                sizeof file_mail_aliases,
-                "%smail.aliases",
-#ifdef HAVE_ETC_DIR
-                ctdl_etc_dir
-#else
-                ctdl_spool_dir
-#endif
-               );
-       StripSlashes(file_mail_aliases, 0);
-        snprintf(file_funambol_msg,
-                sizeof file_funambol_msg,
-                "%sfunambol_newmail_soap.xml",
-                ctdl_shared_dir);
-       StripSlashes(file_funambol_msg, 0);
-
-       DBG_PRINT(ctdl_bio_dir);
-       DBG_PRINT(ctdl_bb_dir);
-       DBG_PRINT(ctdl_data_dir);
-       DBG_PRINT(ctdl_dspam_dir);
-       DBG_PRINT(ctdl_file_dir);
-       DBG_PRINT(ctdl_hlp_dir);
-       DBG_PRINT(ctdl_image_dir);
-       DBG_PRINT(ctdl_info_dir);
-       DBG_PRINT(ctdl_key_dir);
-       DBG_PRINT(ctdl_message_dir);
-       DBG_PRINT(ctdl_usrpic_dir);
-       DBG_PRINT(ctdl_etc_dir);
-       DBG_PRINT(ctdl_run_dir);
-       DBG_PRINT(ctdl_spool_dir);
-       DBG_PRINT(ctdl_netout_dir);
-       DBG_PRINT(ctdl_netin_dir);
-       DBG_PRINT(ctdl_netcfg_dir);
-       DBG_PRINT(ctdl_bbsbase_dir);
-       DBG_PRINT(ctdl_sbin_dir);
-       DBG_PRINT(ctdl_bin_dir);
-       DBG_PRINT(ctdl_utilbin_dir);
-       DBG_PRINT(file_citadel_control);
-       DBG_PRINT(file_citadel_rc);
-       DBG_PRINT(file_citadel_config);
-       DBG_PRINT(file_lmtp_socket);
-       DBG_PRINT(file_lmtp_unfiltered_socket);
-       DBG_PRINT(file_arcq);
-       DBG_PRINT(file_citadel_socket);
-       DBG_PRINT(file_mail_aliases);
-       DBG_PRINT(file_pid_file);
-       DBG_PRINT(file_pid_paniclog);
-       DBG_PRINT(file_crpt_file_key);
-       DBG_PRINT(file_crpt_file_csr);
-       DBG_PRINT(file_crpt_file_cer);
-       DBG_PRINT(file_chkpwd);
-       DBG_PRINT(file_base64);
-       DBG_PRINT(file_guesstimezone);
-       DBG_PRINT(file_funambol_msg);
-}
-
-
-/*
- * Generate an associated file name for a room
- */
-void assoc_file_name(char *buf, size_t n,
-                    struct ctdlroom *qrbuf, const char *prefix)
-{
-       snprintf(buf, n, "%s%ld", prefix, qrbuf->QRnumber);
-}
-
diff --git a/citadel/citadel_dirs.h b/citadel/citadel_dirs.h
deleted file mode 100644 (file)
index 1586ba6..0000000
+++ /dev/null
@@ -1,65 +0,0 @@
-#ifndef __CITADEL_DIRS_H
-#define __CITADEL_DIRS_H
-
-#include <limits.h>
-
-
-extern char ctdl_home_directory[PATH_MAX];
-
-
-/* all our directories */
-extern char ctdl_bio_dir[PATH_MAX];
-extern char ctdl_bb_dir[PATH_MAX];
-extern char ctdl_data_dir[PATH_MAX];
-extern char ctdl_dspam_dir[PATH_MAX];
-extern char ctdl_file_dir[PATH_MAX];
-extern char ctdl_hlp_dir[PATH_MAX];
-extern char ctdl_shared_dir[PATH_MAX];
-extern char ctdl_image_dir[PATH_MAX];
-extern char ctdl_info_dir[PATH_MAX];
-extern char ctdl_key_dir[PATH_MAX];
-extern char ctdl_message_dir[PATH_MAX];
-extern char ctdl_usrpic_dir[PATH_MAX];
-extern char ctdl_etc_dir[PATH_MAX];
-extern char ctdl_autoetc_dir[PATH_MAX];
-extern char ctdl_run_dir[PATH_MAX];
-extern char ctdl_spool_dir[PATH_MAX];
-extern char ctdl_netout_dir[PATH_MAX];
-extern char ctdl_netin_dir[PATH_MAX];
-extern char ctdl_netcfg_dir[PATH_MAX];
-extern char ctdl_bbsbase_dir[PATH_MAX];
-extern char ctdl_sbin_dir[PATH_MAX];
-extern char ctdl_bin_dir[PATH_MAX];
-extern char ctdl_utilbin_dir[PATH_MAX];
-
-
-
-/* some of the frequently used files */
-extern char file_citadel_control[PATH_MAX];
-extern char file_citadel_rc[PATH_MAX];
-extern char file_citadel_config[PATH_MAX];
-extern char file_lmtp_socket[PATH_MAX];
-extern char file_lmtp_unfiltered_socket[PATH_MAX];
-extern char file_arcq[PATH_MAX];
-extern char file_citadel_socket[PATH_MAX];
-extern char file_mail_aliases[PATH_MAX];
-extern char file_pid_file[PATH_MAX];
-extern char file_pid_paniclog[PATH_MAX];
-extern char file_crpt_file_key[PATH_MAX];
-extern char file_crpt_file_csr[PATH_MAX];
-extern char file_crpt_file_cer[PATH_MAX];
-extern char file_chkpwd[PATH_MAX];
-extern char file_base64[PATH_MAX];
-extern char file_guesstimezone[PATH_MAX];
-extern char file_dpsam_conf[PATH_MAX];
-extern char file_dspam_log[PATH_MAX];
-
-extern char file_funambol_msg[PATH_MAX];
-
-extern void calc_dirs_n_files(int relh, int home, const char *relhome, char  *ctdldir, int dbg);
-
-
-void assoc_file_name(char *buf, size_t n,
-                    struct ctdlroom *qrbuf, const char *prefix);
-
-#endif /* __CITADEL_DIRS_H */
diff --git a/citadel/citadel_ipc.c b/citadel/citadel_ipc.c
deleted file mode 100644 (file)
index ef90228..0000000
+++ /dev/null
@@ -1,3328 +0,0 @@
-/* $Id$ 
- *
- * Copyright (c) 1987-2009 by the citadel.org team
- *
- *  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 3 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; if not, write to the Free Software
- *  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
- */
-
-#include "sysdep.h"
-#if TIME_WITH_SYS_TIME
-# include <sys/time.h>
-# include <time.h>
-#else
-# if HAVE_SYS_TIME_H
-#  include <sys/time.h>
-# else
-#  include <time.h>
-# endif
-#endif
-#include <unistd.h>
-#include <stdio.h>
-#include <sys/types.h>
-#include <string.h>
-#ifdef HAVE_MALLOC_H
-#include <malloc.h>
-#endif
-#include <stdlib.h>
-#include <ctype.h>
-#include <sys/socket.h>
-#include <arpa/inet.h>
-#include <netinet/in.h>
-#include <netdb.h>
-#include <sys/un.h>
-#include <errno.h>
-#ifdef THREADED_CLIENT
-#include <pthread.h>
-#endif
-#include <libcitadel.h>
-#include "citadel.h"
-#include "citadel_ipc.h"
-#include "citadel_decls.h"
-#include "citadel_dirs.h"
-#ifdef THREADED_CLIENT
-pthread_mutex_t rwlock;
-#endif
-
-#ifdef HAVE_OPENSSL
-static SSL_CTX *ssl_ctx;
-char arg_encrypt;
-char rc_encrypt;
-#ifdef THREADED_CLIENT
-pthread_mutex_t **Critters;                    /* Things that need locking */
-#endif /* THREADED_CLIENT */
-
-#endif /* HAVE_OPENSSL */
-
-#ifndef INADDR_NONE
-#define INADDR_NONE 0xffffffff
-#endif
-
-static void (*status_hook)(char *s) = NULL;
-
-void setCryptoStatusHook(void (*hook)(char *s)) {
-       status_hook = hook;
-}
-
-void CtdlIPC_SetNetworkStatusCallback(CtdlIPC *ipc, void (*hook)(int state)) {
-       ipc->network_status_cb = hook;
-}
-
-
-char instant_msgs = 0;
-
-
-static void serv_read(CtdlIPC *ipc, char *buf, unsigned int bytes);
-static void serv_write(CtdlIPC *ipc, const char *buf, unsigned int nbytes);
-#ifdef HAVE_OPENSSL
-static void serv_read_ssl(CtdlIPC *ipc, char *buf, unsigned int bytes);
-static void serv_write_ssl(CtdlIPC *ipc, const char *buf, unsigned int nbytes);
-static void endtls(SSL *ssl);
-#ifdef THREADED_CLIENT
-static unsigned long id_callback(void);
-#endif /* THREADED_CLIENT */
-#endif /* HAVE_OPENSSL */
-static void CtdlIPC_getline(CtdlIPC* ipc, char *buf);
-static void CtdlIPC_putline(CtdlIPC *ipc, const char *buf);
-
-
-
-const char *svn_revision(void);
-
-/*
- * Does nothing.  The server should always return 200.
- */
-int CtdlIPCNoop(CtdlIPC *ipc)
-{
-       char aaa[128];
-
-       return CtdlIPCGenericCommand(ipc, "NOOP", NULL, 0, NULL, NULL, aaa);
-}
-
-
-/*
- * Does nothing interesting.  The server should always return 200
- * along with your string.
- */
-int CtdlIPCEcho(CtdlIPC *ipc, const char *arg, char *cret)
-{
-       register int ret;
-       char *aaa;
-       
-       if (!arg) return -2;
-       if (!cret) return -2;
-
-       aaa = (char *)malloc((size_t)(strlen(arg) + 6));
-       if (!aaa) return -1;
-
-       sprintf(aaa, "ECHO %s", arg);
-       ret = CtdlIPCGenericCommand(ipc, aaa, NULL, 0, NULL, NULL, cret);
-       free(aaa);
-       return ret;
-}
-
-
-/*
- * Asks the server to close the connecction.
- * Should always return 200.
- */
-int CtdlIPCQuit(CtdlIPC *ipc)
-{
-       register int ret = 221;         /* Default to successful quit */
-       char aaa[SIZ]; 
-
-       CtdlIPC_lock(ipc);
-       if (ipc->sock > -1) {
-               CtdlIPC_putline(ipc, "QUIT");
-               CtdlIPC_getline(ipc, aaa);
-               ret = atoi(aaa);
-       }
-#ifdef HAVE_OPENSSL
-       if (ipc->ssl)
-               SSL_shutdown(ipc->ssl);
-       ipc->ssl = NULL;
-#endif
-       if (ipc->sock)
-               shutdown(ipc->sock, 2); /* Close connection; we're dead */
-       ipc->sock = -1;
-       CtdlIPC_unlock(ipc);
-       return ret;
-}
-
-
-/*
- * Asks the server to log out.  Should always return 200, even if no user
- * was logged in.  The user will not be logged in after this!
- */
-int CtdlIPCLogout(CtdlIPC *ipc)
-{
-       register int ret;
-       char aaa[SIZ];
-
-       CtdlIPC_lock(ipc);
-       CtdlIPC_putline(ipc, "LOUT");
-       CtdlIPC_getline(ipc, aaa);
-       ret = atoi(aaa);
-       CtdlIPC_unlock(ipc);
-       return ret;
-}
-
-
-/*
- * First stage of authentication - pass the username.  Returns 300 if the
- * username is able to log in, with the username correctly spelled in cret.
- * Returns various 500 error codes if the user doesn't exist, etc.
- */
-int CtdlIPCTryLogin(CtdlIPC *ipc, const char *username, char *cret)
-{
-       register int ret;
-       char *aaa;
-
-       if (!username) return -2;
-       if (!cret) return -2;
-
-       aaa = (char *)malloc((size_t)(strlen(username) + 6));
-       if (!aaa) return -1;
-
-       sprintf(aaa, "USER %s", username);
-       ret = CtdlIPCGenericCommand(ipc, aaa, NULL, 0, NULL, NULL, cret);
-       free(aaa);
-       return ret;
-}
-
-
-/*
- * Second stage of authentication - provide password.  The server returns
- * 200 and several arguments in cret relating to the user's account.
- */
-int CtdlIPCTryPassword(CtdlIPC *ipc, const char *passwd, char *cret)
-{
-       register int ret;
-       char *aaa;
-
-       if (!passwd) return -2;
-       if (!cret) return -2;
-
-       aaa = (char *)malloc((size_t)(strlen(passwd) + 6));
-       if (!aaa) return -1;
-
-       sprintf(aaa, "PASS %s", passwd);
-       ret = CtdlIPCGenericCommand(ipc, aaa, NULL, 0, NULL, NULL, cret);
-       free(aaa);
-       return ret;
-}
-
-
-/*
- * Second stage of authentication - provide password.  The server returns
- * 200 and several arguments in cret relating to the user's account.
- */
-int CtdlIPCTryApopPassword(CtdlIPC *ipc, const char *response, char *cret)
-{
-       register int ret;
-       char *aaa;
-
-       if (!response) return -2;
-       if (!cret) return -2;
-
-       aaa = (char *)malloc((size_t)(strlen(response) + 6));
-       if (!aaa) return -1;
-
-       sprintf(aaa, "PAS2 %s", response);
-       ret = CtdlIPCGenericCommand(ipc, aaa, NULL, 0, NULL, NULL, cret);
-       free(aaa);
-       return ret;
-}
-
-
-/*
- * Create a new user.  This returns 200 plus the same arguments as TryPassword
- * if selfservice is nonzero, unless there was a problem creating the account.
- * If selfservice is zero, creates a new user but does not log out the existing
- * user - intended for use by system administrators to create accounts on
- * behalf of other users.
- */
-int CtdlIPCCreateUser(CtdlIPC *ipc, const char *username, int selfservice, char *cret)
-{
-       register int ret;
-       char *aaa;
-
-       if (!username) return -2;
-       if (!cret) return -2;
-
-       aaa = (char *)malloc((size_t)(strlen(username) + 6));
-       if (!aaa) return -1;
-
-       sprintf(aaa, "%s %s", selfservice ? "NEWU" : "CREU",  username);
-       ret = CtdlIPCGenericCommand(ipc, aaa, NULL, 0, NULL, NULL, cret);
-       free(aaa);
-       return ret;
-}
-
-
-/*
- * Changes the user's password.  Returns 200 if changed, errors otherwise.
- */
-int CtdlIPCChangePassword(CtdlIPC *ipc, const char *passwd, char *cret)
-{
-       register int ret;
-       char *aaa;
-
-       if (!passwd) return -2;
-       if (!cret) return -2;
-
-       aaa = (char *)malloc((size_t)(strlen(passwd) + 6));
-       if (!aaa) return -1;
-
-       sprintf(aaa, "SETP %s", passwd);
-       ret = CtdlIPCGenericCommand(ipc, aaa, NULL, 0, NULL, NULL, cret);
-       free(aaa);
-       return ret;
-}
-
-
-/* LKRN */
-/* Caller must free the march list */
-/* Room types are defined in enum RoomList; keep these in sync! */
-/* floor is -1 for all, or floornum */
-int CtdlIPCKnownRooms(CtdlIPC *ipc, enum RoomList which, int floor, struct march **listing, char *cret)
-{
-       register int ret;
-       struct march *march = NULL;
-       static char *proto[] =
-               {"LKRA", "LKRN", "LKRO", "LZRM", "LRMS", "LPRM" };
-       char aaa[SIZ];
-       char *bbb = NULL;
-       size_t bbb_len;
-
-       if (!listing) return -2;
-       if (*listing) return -2;        /* Free the listing first */
-       if (!cret) return -2;
-       /* if (which < 0 || which > 4) return -2; */
-       if (floor < -1) return -2;      /* Can't validate upper bound, sorry */
-
-       sprintf(aaa, "%s %d", proto[which], floor);
-       ret = CtdlIPCGenericCommand(ipc, aaa, NULL, 0, &bbb, &bbb_len, cret);
-       if (ret / 100 == 1) {
-               struct march *mptr;
-
-               while (bbb && strlen(bbb)) {
-                       int a;
-
-                       extract_token(aaa, bbb, 0, '\n', sizeof aaa);
-                       a = strlen(aaa);
-                       memmove(bbb, bbb + a + 1, strlen(bbb) - a);
-                       mptr = (struct march *) malloc(sizeof (struct march));
-                       if (mptr) {
-                               mptr->next = NULL;
-                               extract_token(mptr->march_name, aaa, 0, '|', sizeof mptr->march_name);
-                               mptr->march_flags = (unsigned int) extract_int(aaa, 1);
-                               mptr->march_floor = (char) extract_int(aaa, 2);
-                               mptr->march_order = (char) extract_int(aaa, 3);
-                               mptr->march_flags2 = (unsigned int) extract_int(aaa, 4);
-                               mptr->march_access = (char) extract_int(aaa, 5);
-                               if (march == NULL)
-                                       march = mptr;
-                               else {
-                                       struct march *mptr2;
-
-                                       mptr2 = march;
-                                       while (mptr2->next != NULL)
-                                               mptr2 = mptr2->next;
-                                       mptr2->next = mptr;
-                               }
-                       }
-               }
-       }
-       *listing = march;
-       if (bbb) free(bbb);
-       return ret;
-}
-
-
-/* GETU */
-/* Caller must free the struct ctdluser; caller may pass an existing one */
-int CtdlIPCGetConfig(CtdlIPC *ipc, struct ctdluser **uret, char *cret)
-{
-       register int ret;
-
-       if (!cret) return -2;
-       if (!uret) return -2;
-       if (!*uret) *uret = (struct ctdluser *)calloc(1, sizeof (struct ctdluser));
-       if (!*uret) return -1;
-
-       ret = CtdlIPCGenericCommand(ipc, "GETU", NULL, 0, NULL, NULL, cret);
-       if (ret / 100 == 2) {
-               uret[0]->USscreenwidth = extract_int(cret, 0);
-               uret[0]->USscreenheight = extract_int(cret, 1);
-               uret[0]->flags = extract_int(cret, 2);
-       }
-       return ret;
-}
-
-
-/* SETU */
-int CtdlIPCSetConfig(CtdlIPC *ipc, struct ctdluser *uret, char *cret)
-{
-       char aaa[48];
-
-       if (!uret) return -2;
-       if (!cret) return -2;
-
-       sprintf(aaa, "SETU %d|%d|%d",
-                       uret->USscreenwidth, uret->USscreenheight,
-                       uret->flags);
-       return CtdlIPCGenericCommand(ipc, aaa, NULL, 0, NULL, NULL, cret);
-}
-
-
-/* RENU */
-int CtdlIPCRenameUser(CtdlIPC *ipc, char *oldname, char *newname, char *cret)
-{
-       register int ret;
-       char cmd[256];
-
-       if (!oldname) return -2;
-       if (!newname) return -2;
-       if (!cret) return -2;
-
-       snprintf(cmd, sizeof cmd, "RENU %s|%s", oldname, newname);
-       ret = CtdlIPCGenericCommand(ipc, cmd, NULL, 0, NULL, NULL, cret);
-       return ret;
-}
-
-
-/* GOTO */
-int CtdlIPCGotoRoom(CtdlIPC *ipc, const char *room, const char *passwd,
-               struct ctdlipcroom **rret, char *cret)
-{
-       register int ret;
-       char *aaa;
-
-       if (!cret) return -2;
-       if (!rret) return -2;
-       if (!*rret) *rret = (struct ctdlipcroom *)calloc(1, sizeof (struct ctdlipcroom));
-       if (!*rret) return -1;
-
-       if (passwd) {
-               aaa = (char *)malloc(strlen(room) + strlen(passwd) + 7);
-               if (!aaa) {
-                       free(*rret);
-                       return -1;
-               }
-               sprintf(aaa, "GOTO %s|%s", room, passwd);
-       } else {
-               aaa = (char *)malloc(strlen(room) + 6);
-               if (!aaa) {
-                       free(*rret);
-                       return -1;
-               }
-               sprintf(aaa, "GOTO %s", room);
-       }
-       ret = CtdlIPCGenericCommand(ipc, aaa, NULL, 0, NULL, NULL, cret);
-       if (ret / 100 == 2) {
-               extract_token(rret[0]->RRname, cret, 0, '|', sizeof rret[0]->RRname);
-               rret[0]->RRunread = extract_long(cret, 1);
-               rret[0]->RRtotal = extract_long(cret, 2);
-               rret[0]->RRinfoupdated = extract_int(cret, 3);
-               rret[0]->RRflags = extract_int(cret, 4);
-               rret[0]->RRhighest = extract_long(cret, 5);
-               rret[0]->RRlastread = extract_long(cret, 6);
-               rret[0]->RRismailbox = extract_int(cret, 7);
-               rret[0]->RRaide = extract_int(cret, 8);
-               rret[0]->RRnewmail = extract_long(cret, 9);
-               rret[0]->RRfloor = extract_int(cret, 10);
-               rret[0]->RRflags2 = extract_int(cret, 14);
-       } else {
-               free(*rret);
-               *rret = NULL;
-       }
-       free(aaa);
-       return ret;
-}
-
-
-/* MSGS */
-/* which is 0 = all, 1 = old, 2 = new, 3 = last, 4 = first, 5 = gt, 6 = lt */
-/* whicharg is number of messages, applies to last, first, gt, lt */
-int CtdlIPCGetMessages(CtdlIPC *ipc, enum MessageList which, int whicharg,
-               const char *mtemplate, unsigned long **mret, char *cret)
-{
-       register int ret;
-       register unsigned long count = 0;
-       static char *proto[] =
-               { "ALL", "OLD", "NEW", "LAST", "FIRST", "GT", "LT" };
-       char aaa[33];
-       char *bbb = NULL;
-       size_t bbb_len;
-
-       if (!cret) return -2;
-       if (!mret) return -2;
-       if (*mret) return -2;
-       if (which < 0 || which > 6) return -2;
-
-       if (which <= 2)
-               sprintf(aaa, "MSGS %s||%d", proto[which],
-                               (mtemplate) ? 1 : 0);
-       else
-               sprintf(aaa, "MSGS %s|%d|%d", proto[which], whicharg,
-                               (mtemplate) ? 1 : 0);
-       if (mtemplate) count = strlen(mtemplate);
-       ret = CtdlIPCGenericCommand(ipc, aaa, mtemplate, count, &bbb, &bbb_len, cret);
-       if (ret / 100 != 1)
-               return ret;
-       count = 0;
-       *mret = (unsigned long *)calloc(1, sizeof(unsigned long));
-       if (!*mret)
-               return -1;
-       while (bbb && strlen(bbb)) {
-               extract_token(aaa, bbb, 0, '\n', sizeof aaa);
-               remove_token(bbb, 0, '\n');
-               *mret = (unsigned long *)realloc(*mret, (size_t)((count + 2) *
-                                       sizeof (unsigned long)));
-               if (*mret) {
-                       (*mret)[count++] = atol(aaa);
-                       (*mret)[count] = 0L;
-               } else {
-                       break;
-               }
-       }
-       if (bbb) free(bbb);
-       return ret;
-}
-
-
-/* MSG0, MSG2 */
-int CtdlIPCGetSingleMessage(CtdlIPC *ipc, long msgnum, int headers, int as_mime,
-               struct ctdlipcmessage **mret, char *cret)
-{
-       register int ret;
-       char aaa[SIZ];
-       char *bbb = NULL;
-       size_t bbb_len;
-       int multipart_hunting = 0;
-       char multipart_prefix[128];
-       char encoding[256];
-
-       if (!cret) return -1;
-       if (!mret) return -1;
-       if (!*mret) *mret = (struct ctdlipcmessage *)calloc(1, sizeof (struct ctdlipcmessage));
-       if (!*mret) return -1;
-       if (!msgnum) return -1;
-
-       strcpy(encoding, "");
-       strcpy(mret[0]->content_type, "");
-       sprintf(aaa, "MSG%d %ld|%d", as_mime, msgnum, headers);
-       ret = CtdlIPCGenericCommand(ipc, aaa, NULL, 0, &bbb, &bbb_len, cret);
-       if (ret / 100 == 1) {
-               if (as_mime != 2) {
-                       strcpy(mret[0]->mime_chosen, "1");      /* Default chosen-part is "1" */
-                       while (strlen(bbb) > 4 && bbb[4] == '=') {
-                               extract_token(aaa, bbb, 0, '\n', sizeof aaa);
-                               remove_token(bbb, 0, '\n');
-
-                               if (!strncasecmp(aaa, "nhdr=yes", 8))
-                                       mret[0]->nhdr = 1;
-                               else if (!strncasecmp(aaa, "from=", 5))
-                                       safestrncpy(mret[0]->author, &aaa[5], SIZ);
-                               else if (!strncasecmp(aaa, "type=", 5))
-                                       mret[0]->type = atoi(&aaa[5]);
-                               else if (!strncasecmp(aaa, "msgn=", 5))
-                                       safestrncpy(mret[0]->msgid, &aaa[5], SIZ);
-                               else if (!strncasecmp(aaa, "subj=", 5))
-                                       safestrncpy(mret[0]->subject, &aaa[5], SIZ);
-                               else if (!strncasecmp(aaa, "rfca=", 5))
-                                       safestrncpy(mret[0]->email, &aaa[5], SIZ);
-                               else if (!strncasecmp(aaa, "hnod=", 5))
-                                       safestrncpy(mret[0]->hnod, &aaa[5], SIZ);
-                               else if (!strncasecmp(aaa, "room=", 5))
-                                       safestrncpy(mret[0]->room, &aaa[5], SIZ);
-                               else if (!strncasecmp(aaa, "node=", 5))
-                                       safestrncpy(mret[0]->node, &aaa[5], SIZ);
-                               else if (!strncasecmp(aaa, "rcpt=", 5))
-                                       safestrncpy(mret[0]->recipient, &aaa[5], SIZ);
-                               else if (!strncasecmp(aaa, "wefw=", 5))
-                                       safestrncpy(mret[0]->references, &aaa[5], SIZ);
-                               else if (!strncasecmp(aaa, "time=", 5))
-                                       mret[0]->time = atol(&aaa[5]);
-
-                               /* Multipart/alternative prefix & suffix strings help
-                                * us to determine which part we want to download.
-                                */
-                               else if (!strncasecmp(aaa, "pref=", 5)) {
-                                       extract_token(multipart_prefix, &aaa[5], 1, '|', sizeof multipart_prefix);
-                                       if (!strcasecmp(multipart_prefix,
-                                          "multipart/alternative")) {
-                                               ++multipart_hunting;
-                                       }
-                               }
-                               else if (!strncasecmp(aaa, "suff=", 5)) {
-                                       extract_token(multipart_prefix, &aaa[5], 1, '|', sizeof multipart_prefix);
-                                       if (!strcasecmp(multipart_prefix,
-                                          "multipart/alternative")) {
-                                               ++multipart_hunting;
-                                       }
-                               }
-
-                               else if (!strncasecmp(aaa, "part=", 5)) {
-                                       struct parts *ptr, *chain;
-       
-                                       ptr = (struct parts *)calloc(1, sizeof (struct parts));
-                                       if (ptr) {
-
-                                               /* Fill the buffers for the caller */
-                                               extract_token(ptr->name, &aaa[5], 0, '|', sizeof ptr->name);
-                                               extract_token(ptr->filename, &aaa[5], 1, '|', sizeof ptr->filename);
-                                               extract_token(ptr->number, &aaa[5], 2, '|', sizeof ptr->number);
-                                               extract_token(ptr->disposition, &aaa[5], 3, '|', sizeof ptr->disposition);
-                                               extract_token(ptr->mimetype, &aaa[5], 4, '|', sizeof ptr->mimetype);
-                                               ptr->length = extract_long(&aaa[5], 5);
-                                               if (!mret[0]->attachments)
-                                                       mret[0]->attachments = ptr;
-                                               else {
-                                                       chain = mret[0]->attachments;
-                                                       while (chain->next)
-                                                               chain = chain->next;
-                                                       chain->next = ptr;
-                                               }
-
-                                               /* Now handle multipart/alternative */
-                                               if (multipart_hunting > 0) {
-                                                       if ( (!strcasecmp(ptr->mimetype,
-                                                            "text/plain"))
-                                                          || (!strcasecmp(ptr->mimetype,
-                                                             "text/html")) ) {
-                                                               strcpy(mret[0]->mime_chosen,
-                                                                       ptr->number);
-                                                       }
-                                               }
-
-                                       }
-                               }
-                       }
-                       /* Eliminate "text\n" */
-                       remove_token(bbb, 0, '\n');
-
-                       /* If doing a MIME thing, pull out the extra headers */
-                       if (as_mime == 4) {
-                               do {
-                                       if (!strncasecmp(bbb, "Content-type:", 13)) {
-                                               extract_token(mret[0]->content_type, bbb, 0, '\n', sizeof mret[0]->content_type);
-                                               strcpy(mret[0]->content_type, &mret[0]->content_type[13]);
-                                               striplt(mret[0]->content_type);
-
-                                               /* strip out ";charset=" portion.  FIXME do something with
-                                                * the charset (like... convert it) instead of just throwing
-                                                * it away
-                                                */
-                                               if (strstr(mret[0]->content_type, ";") != NULL) {
-                                                       strcpy(strstr(mret[0]->content_type, ";"), "");
-                                               }
-
-                                       }
-                                       if (!strncasecmp(bbb, "X-Citadel-MSG4-Partnum:", 23)) {
-                                               extract_token(mret[0]->mime_chosen, bbb, 0, '\n', sizeof mret[0]->mime_chosen);
-                                               strcpy(mret[0]->mime_chosen, &mret[0]->mime_chosen[23]);
-                                               striplt(mret[0]->mime_chosen);
-                                       }
-                                       if (!strncasecmp(bbb, "Content-transfer-encoding:", 26)) {
-                                               extract_token(encoding, bbb, 0, '\n', sizeof encoding);
-                                               strcpy(encoding, &encoding[26]);
-                                               striplt(encoding);
-                                       }
-                                       remove_token(bbb, 0, '\n');
-                               } while ((bbb[0] != 0) && (bbb[0] != '\n'));
-                               remove_token(bbb, 0, '\n');
-                       }
-
-
-               }
-               if (strlen(bbb)) {
-
-                       if ( (!strcasecmp(encoding, "base64")) || (!strcasecmp(encoding, "quoted-printable")) ) {
-                               char *ccc = NULL;
-                               int bytes_decoded = 0;
-                               ccc = malloc(strlen(bbb) + 32768);
-                               if (!strcasecmp(encoding, "base64")) {
-                                       bytes_decoded = CtdlDecodeBase64(ccc, bbb, strlen(bbb));
-                               }
-                               else if (!strcasecmp(encoding, "quoted-printable")) {
-                                       bytes_decoded = CtdlDecodeQuotedPrintable(ccc, bbb, strlen(bbb));
-                               }
-                               ccc[bytes_decoded] = 0;
-                               free(bbb);
-                               bbb = ccc;
-                       }
-
-                       /* FIXME: Strip trailing whitespace */
-                       bbb = (char *)realloc(bbb, (size_t)(strlen(bbb) + 1));
-
-               } else {
-                       bbb = (char *)realloc(bbb, 1);
-                       *bbb = '\0';
-               }
-               mret[0]->text = bbb;
-       }
-       return ret;
-}
-
-
-/* WHOK */
-int CtdlIPCWhoKnowsRoom(CtdlIPC *ipc, char **listing, char *cret)
-{
-       register int ret;
-       size_t bytes;
-
-       if (!cret) return -2;
-       if (!listing) return -2;
-       if (*listing) return -2;
-
-       ret = CtdlIPCGenericCommand(ipc, "WHOK", NULL, 0, listing, &bytes, cret);
-       return ret;
-}
-
-
-/* INFO */
-int CtdlIPCServerInfo(CtdlIPC *ipc, char *cret)
-{
-       register int ret;
-       size_t bytes;
-       char *listing = NULL;
-       char buf[SIZ];
-
-       if (!cret) return -2;
-
-       ret = CtdlIPCGenericCommand(ipc, "INFO", NULL, 0, &listing, &bytes, cret);
-       if (ret / 100 == 1) {
-               int line = 0;
-
-               while (*listing && strlen(listing)) {
-                       extract_token(buf, listing, 0, '\n', sizeof buf);
-                       remove_token(listing, 0, '\n');
-                       switch (line++) {
-                       case 0:         ipc->ServInfo.pid = atoi(buf);
-                                       break;
-                       case 1:         strcpy(ipc->ServInfo.nodename,buf);
-                                       break;
-                       case 2:         strcpy(ipc->ServInfo.humannode,buf);
-                                       break;
-                       case 3:         strcpy(ipc->ServInfo.fqdn,buf);
-                                       break;
-                       case 4:         strcpy(ipc->ServInfo.software,buf);
-                                       break;
-                       case 5:         ipc->ServInfo.rev_level = atoi(buf);
-                                       break;
-                       case 6:         strcpy(ipc->ServInfo.site_location,buf);
-                                       break;
-                       case 7:         strcpy(ipc->ServInfo.sysadm,buf);
-                                       break;
-                       case 9:         strcpy(ipc->ServInfo.moreprompt,buf);
-                                       break;
-                       case 10:        ipc->ServInfo.ok_floors = atoi(buf);
-                                       break;
-                       case 11:        ipc->ServInfo.paging_level = atoi(buf);
-                                       break;
-                       case 13:        ipc->ServInfo.supports_qnop = atoi(buf);
-                                       break;
-                       case 14:        ipc->ServInfo.supports_ldap = atoi(buf);
-                                       break;
-                       case 15:        ipc->ServInfo.newuser_disabled = atoi(buf);
-                                       break;
-                       case 16:        strcpy(ipc->ServInfo.default_cal_zone, buf);
-                                       break;
-                       case 17:        ipc->ServInfo.load_avg = atof(buf);
-                                       break;
-                       case 18:        ipc->ServInfo.worker_avg = atof(buf);
-                                       break;
-                       case 19:        ipc->ServInfo.thread_count = atoi(buf);
-                                       break;
-                       case 20:        ipc->ServInfo.has_sieve = atoi(buf);
-                                       break;
-                       case 21:        ipc->ServInfo.fulltext_enabled = atoi(buf);
-                                       break;
-                       case 22:        strcpy(ipc->ServInfo.svn_revision, buf);
-                                       break;
-                       }
-               }
-
-       }
-       if (listing) free(listing);
-       return ret;
-}
-
-
-/* RDIR */
-int CtdlIPCReadDirectory(CtdlIPC *ipc, char **listing, char *cret)
-{
-       register int ret;
-       size_t bytes;
-
-       if (!cret) return -2;
-       if (!listing) return -2;
-       if (*listing) return -2;
-
-       ret = CtdlIPCGenericCommand(ipc, "RDIR", NULL, 0, listing, &bytes, cret);
-       return ret;
-}
-
-
-/*
- * Set last-read pointer in this room to msgnum, or 0 for HIGHEST.
- */
-int CtdlIPCSetLastRead(CtdlIPC *ipc, long msgnum, char *cret)
-{
-       register int ret;
-       char aaa[16];
-
-       if (!cret) return -2;
-
-       if (msgnum) {
-               sprintf(aaa, "SLRP %ld", msgnum);
-       }
-       else {
-               sprintf(aaa, "SLRP HIGHEST");
-       }
-       ret = CtdlIPCGenericCommand(ipc, aaa, NULL, 0, NULL, NULL, cret);
-       return ret;
-}
-
-
-/* INVT */
-int CtdlIPCInviteUserToRoom(CtdlIPC *ipc, const char *username, char *cret)
-{
-       register int ret;
-       char *aaa;
-
-       if (!cret) return -2;
-       if (!username) return -2;
-
-       aaa = (char *)malloc(strlen(username) + 6);
-       if (!aaa) return -1;
-
-       sprintf(aaa, "INVT %s", username);
-       ret = CtdlIPCGenericCommand(ipc, aaa, NULL, 0, NULL, NULL, cret);
-       free(aaa);
-       return ret;
-}
-
-
-/* KICK */
-int CtdlIPCKickoutUserFromRoom(CtdlIPC *ipc, const char *username, char *cret)
-{
-       register int ret;
-       char *aaa;
-
-       if (!cret) return -1;
-       if (!username) return -1;
-
-       aaa = (char *)malloc(strlen(username) + 6);
-
-       sprintf(aaa, "KICK %s", username);
-       ret = CtdlIPCGenericCommand(ipc, aaa, NULL, 0, NULL, NULL, cret);
-       free(aaa);
-       return ret;
-}
-
-
-/* GETR */
-int CtdlIPCGetRoomAttributes(CtdlIPC *ipc, struct ctdlroom **qret, char *cret)
-{
-       register int ret;
-
-       if (!cret) return -2;
-       if (!qret) return -2;
-       if (!*qret) *qret = (struct ctdlroom *)calloc(1, sizeof (struct ctdlroom));
-       if (!*qret) return -1;
-
-       ret = CtdlIPCGenericCommand(ipc, "GETR", NULL, 0, NULL, NULL, cret);
-       if (ret / 100 == 2) {
-               extract_token(qret[0]->QRname, cret, 0, '|', sizeof qret[0]->QRname);
-               extract_token(qret[0]->QRpasswd, cret, 1, '|', sizeof qret[0]->QRpasswd);
-               extract_token(qret[0]->QRdirname, cret, 2, '|', sizeof qret[0]->QRdirname);
-               qret[0]->QRflags = extract_int(cret, 3);
-               qret[0]->QRfloor = extract_int(cret, 4);
-               qret[0]->QRorder = extract_int(cret, 5);
-               qret[0]->QRdefaultview = extract_int(cret, 6);
-               qret[0]->QRflags2 = extract_int(cret, 7);
-       }
-       return ret;
-}
-
-
-/* SETR */
-/* set forget to kick all users out of room */
-int CtdlIPCSetRoomAttributes(CtdlIPC *ipc, int forget, struct ctdlroom *qret, char *cret)
-{
-       register int ret;
-       char *aaa;
-
-       if (!cret) return -2;
-       if (!qret) return -2;
-
-       aaa = (char *)malloc(strlen(qret->QRname) + strlen(qret->QRpasswd) +
-                       strlen(qret->QRdirname) + 64);
-       if (!aaa) return -1;
-
-       sprintf(aaa, "SETR %s|%s|%s|%d|%d|%d|%d|%d|%d",
-                       qret->QRname, qret->QRpasswd, qret->QRdirname,
-                       qret->QRflags, forget, qret->QRfloor, qret->QRorder,
-                       qret->QRdefaultview, qret->QRflags2);
-       ret = CtdlIPCGenericCommand(ipc, aaa, NULL, 0, NULL, NULL, cret);
-       free(aaa);
-       return ret;
-}
-
-
-/* GETA */
-int CtdlIPCGetRoomAide(CtdlIPC *ipc, char *cret)
-{
-       if (!cret) return -1;
-
-       return CtdlIPCGenericCommand(ipc, "GETA", NULL, 0, NULL, NULL, cret);
-}
-
-
-/* SETA */
-int CtdlIPCSetRoomAide(CtdlIPC *ipc, const char *username, char *cret)
-{
-       register int ret;
-       char *aaa;
-
-       if (!cret) return -2;
-       if (!username) return -2;
-
-       aaa = (char *)malloc(strlen(username) + 6);
-       if (!aaa) return -1;
-
-       sprintf(aaa, "SETA %s", username);
-       ret = CtdlIPCGenericCommand(ipc, aaa, NULL, 0, NULL, NULL, cret);
-       free(aaa);
-       return ret;
-}
-
-
-/* ENT0 */
-int CtdlIPCPostMessage(CtdlIPC *ipc, int flag, int *subject_required,  struct ctdlipcmessage *mr, char *cret)
-{
-       register int ret;
-       char cmd[SIZ];
-       char *ptr;
-
-       if (!cret) return -2;
-       if (!mr) return -2;
-
-       if (mr->references) {
-               for (ptr=mr->references; *ptr != 0; ++ptr) {
-                       if (*ptr == '|') *ptr = '!';
-               }
-       }
-
-       snprintf(cmd, sizeof cmd,
-                       "ENT0 %d|%s|%d|%d|%s|%s||||||%s|", flag, mr->recipient,
-                       mr->anonymous, mr->type, mr->subject, mr->author, mr->references);
-       ret = CtdlIPCGenericCommand(ipc, cmd, mr->text, strlen(mr->text), NULL,
-                       NULL, cret);
-       if ((flag == 0) && (subject_required != NULL)) {
-               /* Is the server strongly recommending that the user enter a message subject? */
-               if ((cret[3] != '\0') && (cret[4] != '\0')) {
-                       *subject_required = extract_int(&cret[4], 1);
-               }
-
-               
-       }
-       return ret;
-}
-
-
-/* RINF */
-int CtdlIPCRoomInfo(CtdlIPC *ipc, char **iret, char *cret)
-{
-       size_t bytes;
-
-       if (!cret) return -2;
-       if (!iret) return -2;
-       if (*iret) return -2;
-
-       return CtdlIPCGenericCommand(ipc, "RINF", NULL, 0, iret, &bytes, cret);
-}
-
-
-/* DELE */
-int CtdlIPCDeleteMessage(CtdlIPC *ipc, long msgnum, char *cret)
-{
-       char aaa[16];
-
-       if (!cret) return -2;
-       if (!msgnum) return -2;
-
-       sprintf(aaa, "DELE %ld", msgnum);
-       return CtdlIPCGenericCommand(ipc, aaa, NULL, 0, NULL, NULL, cret);
-}
-
-
-/* MOVE */
-int CtdlIPCMoveMessage(CtdlIPC *ipc, int copy, long msgnum, const char *destroom, char *cret)
-{
-       register int ret;
-       char *aaa;
-
-       if (!cret) return -2;
-       if (!destroom) return -2;
-       if (!msgnum) return -2;
-
-       aaa = (char *)malloc(strlen(destroom) + 28);
-       if (!aaa) return -1;
-
-       sprintf(aaa, "MOVE %ld|%s|%d", msgnum, destroom, copy);
-       ret = CtdlIPCGenericCommand(ipc, aaa, NULL, 0, NULL, NULL, cret);
-       free(aaa);
-       return ret;
-}
-
-
-/* KILL */
-int CtdlIPCDeleteRoom(CtdlIPC *ipc, int for_real, char *cret)
-{
-       char aaa[16];
-
-       if (!cret) return -2;
-
-       sprintf(aaa, "KILL %d", for_real);
-       return CtdlIPCGenericCommand(ipc, aaa, NULL, 0, NULL, NULL, cret);
-}
-
-
-/* CRE8 */
-int CtdlIPCCreateRoom(CtdlIPC *ipc, int for_real, const char *roomname, int type,
-               const char *password, int floor, char *cret)
-{
-       register int ret;
-       char *aaa;
-
-       if (!cret) return -2;
-       if (!roomname) return -2;
-
-       if (password) {
-               aaa = (char *)malloc(strlen(roomname) + strlen(password) + 40);
-               if (!aaa) return -1;
-               sprintf(aaa, "CRE8 %d|%s|%d|%s|%d", for_real, roomname, type,
-                               password, floor);
-       } else {
-               aaa = (char *)malloc(strlen(roomname) + 40);
-               if (!aaa) return -1;
-               sprintf(aaa, "CRE8 %d|%s|%d||%d", for_real, roomname, type,
-                               floor);
-       }
-       ret = CtdlIPCGenericCommand(ipc, aaa, NULL, 0, NULL, NULL, cret);
-       free(aaa);
-       return ret;
-}
-
-
-/* FORG */
-int CtdlIPCForgetRoom(CtdlIPC *ipc, char *cret)
-{
-       if (!cret) return -2;
-
-       return CtdlIPCGenericCommand(ipc, "FORG", NULL, 0, NULL, NULL, cret);
-}
-
-
-/* MESG */
-int CtdlIPCSystemMessage(CtdlIPC *ipc, const char *message, char **mret, char *cret)
-{
-       register int ret;
-       char *aaa;
-       size_t bytes;
-
-       if (!cret) return -2;
-       if (!mret) return -2;
-       if (*mret) return -2;
-       if (!message) return -2;
-
-       aaa = (char *)malloc(strlen(message) + 6);
-       if (!aaa) return -1;
-
-       sprintf(aaa, "MESG %s", message);
-       ret = CtdlIPCGenericCommand(ipc, aaa, NULL, 0, mret, &bytes, cret);
-       free(aaa);
-       return ret;
-}
-
-
-/* GNUR */
-int CtdlIPCNextUnvalidatedUser(CtdlIPC *ipc, char *cret)
-{
-       if (!cret) return -2;
-
-       return CtdlIPCGenericCommand(ipc, "GNUR", NULL, 0, NULL, NULL, cret);
-}
-
-
-/* GREG */
-int CtdlIPCGetUserRegistration(CtdlIPC *ipc, const char *username, char **rret, char *cret)
-{
-       register int ret;
-       char *aaa;
-       size_t bytes;
-
-       if (!cret) return -2;
-       if (!rret) return -2;
-       if (*rret) return -2;
-
-       if (username)
-               aaa = (char *)malloc(strlen(username) + 6);
-       else
-               aaa = (char *)malloc(12);
-       if (!aaa) return -1;
-
-       if (username)
-               sprintf(aaa, "GREG %s", username);
-       else
-               sprintf(aaa, "GREG _SELF_");
-       ret = CtdlIPCGenericCommand(ipc, aaa, NULL, 0, rret, &bytes, cret);
-       free(aaa);
-       return ret;
-}
-
-
-/* VALI */
-int CtdlIPCValidateUser(CtdlIPC *ipc, const char *username, int axlevel, char *cret)
-{
-       register int ret;
-       char *aaa;
-
-       if (!cret) return -2;
-       if (!username) return -2;
-       if (axlevel < 0 || axlevel > 7) return -2;
-
-       aaa = (char *)malloc(strlen(username) + 17);
-       if (!aaa) return -1;
-
-       sprintf(aaa, "VALI %s|%d", username, axlevel);
-       ret = CtdlIPCGenericCommand(ipc, aaa, NULL, 0, NULL, NULL, cret);
-       free(aaa);
-       return ret;
-}
-
-
-/* EINF */
-int CtdlIPCSetRoomInfo(CtdlIPC *ipc, int for_real, const char *info, char *cret)
-{
-       char aaa[16];
-
-       if (!cret) return -1;
-       if (!info) return -1;
-
-       sprintf(aaa, "EINF %d", for_real);
-       return CtdlIPCGenericCommand(ipc, aaa, NULL, 0, NULL, NULL, cret);
-}
-
-
-/* LIST */
-int CtdlIPCUserListing(CtdlIPC *ipc, char *searchstring, char **listing, char *cret)
-{
-       size_t bytes;
-       char *cmd;
-       int ret;
-
-       if (!cret) return -1;
-       if (!listing) return -1;
-       if (*listing) return -1;
-       if (!searchstring) return -1;
-
-       cmd = malloc(strlen(searchstring) + 10);
-       sprintf(cmd, "LIST %s", searchstring);
-
-       ret = CtdlIPCGenericCommand(ipc, cmd, NULL, 0, listing, &bytes, cret);
-       free(cmd);
-       return(ret);
-}
-
-
-/* REGI */
-int CtdlIPCSetRegistration(CtdlIPC *ipc, const char *info, char *cret)
-{
-       if (!cret) return -1;
-       if (!info) return -1;
-
-       return CtdlIPCGenericCommand(ipc, "REGI", info, strlen(info),
-                       NULL, NULL, cret);
-}
-
-
-/* CHEK */
-int CtdlIPCMiscCheck(CtdlIPC *ipc, struct ctdlipcmisc *chek, char *cret)
-{
-       register int ret;
-
-       if (!cret) return -1;
-       if (!chek) return -1;
-
-       ret = CtdlIPCGenericCommand(ipc, "CHEK", NULL, 0, NULL, NULL, cret);
-       if (ret / 100 == 2) {
-               chek->newmail = extract_long(cret, 0);
-               chek->needregis = extract_int(cret, 1);
-               chek->needvalid = extract_int(cret, 2);
-       }
-       return ret;
-}
-
-
-/* DELF */
-int CtdlIPCDeleteFile(CtdlIPC *ipc, const char *filename, char *cret)
-{
-       register int ret;
-       char *aaa;
-
-       if (!cret) return -2;
-       if (!filename) return -2;
-       
-       aaa = (char *)malloc(strlen(filename) + 6);
-       if (!aaa) return -1;
-
-       sprintf(aaa, "DELF %s", filename);
-       ret = CtdlIPCGenericCommand(ipc, aaa, NULL, 0, NULL, NULL, cret);
-       free(aaa);
-       return ret;
-}
-
-
-/* MOVF */
-int CtdlIPCMoveFile(CtdlIPC *ipc, const char *filename, const char *destroom, char *cret)
-{
-       register int ret;
-       char *aaa;
-
-       if (!cret) return -2;
-       if (!filename) return -2;
-       if (!destroom) return -2;
-
-       aaa = (char *)malloc(strlen(filename) + strlen(destroom) + 7);
-       if (!aaa) return -1;
-
-       sprintf(aaa, "MOVF %s|%s", filename, destroom);
-       ret = CtdlIPCGenericCommand(ipc, aaa, NULL, 0, NULL, NULL, cret);
-       free(aaa);
-       return ret;
-}
-
-
-/* RWHO */
-int CtdlIPCOnlineUsers(CtdlIPC *ipc, char **listing, time_t *stamp, char *cret)
-{
-       register int ret;
-       size_t bytes;
-
-       if (!cret) return -1;
-       if (!listing) return -1;
-       if (*listing) return -1;
-
-       *stamp = CtdlIPCServerTime(ipc, cret);
-       if (!*stamp)
-               *stamp = time(NULL);
-       ret = CtdlIPCGenericCommand(ipc, "RWHO", NULL, 0, listing, &bytes, cret);
-       return ret;
-}
-
-
-/* OPEN */
-int CtdlIPCFileDownload(CtdlIPC *ipc, const char *filename, void **buf,
-               size_t resume,
-               void (*progress_gauge_callback)
-                       (CtdlIPC*, unsigned long, unsigned long),
-               char *cret)
-{
-       register int ret;
-       size_t bytes;
-       time_t last_mod;
-       char mimetype[SIZ];
-       char *aaa;
-
-       if (!cret) return -2;
-       if (!filename) return -2;
-       if (!buf) return -2;
-       if (*buf) return -2;
-       if (ipc->downloading) return -2;
-
-       aaa = (char *)malloc(strlen(filename) + 6);
-       if (!aaa) return -1;
-
-       sprintf(aaa, "OPEN %s", filename);
-       ret = CtdlIPCGenericCommand(ipc, aaa, NULL, 0, NULL, NULL, cret);
-       free(aaa);
-       if (ret / 100 == 2) {
-               ipc->downloading = 1;
-               bytes = extract_long(cret, 0);
-               last_mod = extract_int(cret, 1);
-               extract_token(mimetype, cret, 2, '|', sizeof mimetype);
-
-               ret = CtdlIPCReadDownload(ipc, buf, bytes, resume,
-                                       progress_gauge_callback, cret);
-               /*
-               ret = CtdlIPCHighSpeedReadDownload(ipc, buf, bytes, resume,
-                                       progress_gauge_callback, cret);
-               */
-
-               ret = CtdlIPCEndDownload(ipc, cret);
-               if (ret / 100 == 2)
-                       sprintf(cret, "%d|%ld|%s|%s", (int)bytes, last_mod,
-                                       filename, mimetype);
-       }
-       return ret;
-}
-
-
-/* OPNA */
-int CtdlIPCAttachmentDownload(CtdlIPC *ipc, long msgnum, const char *part,
-               void **buf,
-               void (*progress_gauge_callback)
-                       (CtdlIPC*, unsigned long, unsigned long),
-               char *cret)
-{
-       register int ret;
-       size_t bytes;
-       time_t last_mod;
-       char filename[SIZ];
-       char mimetype[SIZ];
-       char aaa[SIZ];
-
-       if (!cret) return -2;
-       if (!buf) return -2;
-       if (*buf) return -2;
-       if (!part) return -2;
-       if (!msgnum) return -2;
-       if (ipc->downloading) return -2;
-
-       sprintf(aaa, "OPNA %ld|%s", msgnum, part);
-       ret = CtdlIPCGenericCommand(ipc, aaa, NULL, 0, NULL, NULL, cret);
-       if (ret / 100 == 2) {
-               ipc->downloading = 1;
-               bytes = extract_long(cret, 0);
-               last_mod = extract_int(cret, 1);
-               extract_token(filename, cret, 2, '|', sizeof filename);
-               extract_token(mimetype, cret, 3, '|', sizeof mimetype);
-               /* ret = CtdlIPCReadDownload(ipc, buf, bytes, 0, progress_gauge_callback, cret); */
-               ret = CtdlIPCHighSpeedReadDownload(ipc, buf, bytes, 0, progress_gauge_callback, cret);
-               ret = CtdlIPCEndDownload(ipc, cret);
-               if (ret / 100 == 2)
-                       sprintf(cret, "%d|%ld|%s|%s", (int)bytes, last_mod,
-                                       filename, mimetype);
-       }
-       return ret;
-}
-
-
-/* OIMG */
-int CtdlIPCImageDownload(CtdlIPC *ipc, const char *filename, void **buf,
-               void (*progress_gauge_callback)
-                       (CtdlIPC*, unsigned long, unsigned long),
-               char *cret)
-{
-       register int ret;
-       size_t bytes;
-       time_t last_mod;
-       char mimetype[SIZ];
-       char *aaa;
-
-       if (!cret) return -1;
-       if (!buf) return -1;
-       if (*buf) return -1;
-       if (!filename) return -1;
-       if (ipc->downloading) return -1;
-
-       aaa = (char *)malloc(strlen(filename) + 6);
-       if (!aaa) return -1;
-
-       sprintf(aaa, "OIMG %s", filename);
-       ret = CtdlIPCGenericCommand(ipc, aaa, NULL, 0, NULL, NULL, cret);
-       free(aaa);
-       if (ret / 100 == 2) {
-               ipc->downloading = 1;
-               bytes = extract_long(cret, 0);
-               last_mod = extract_int(cret, 1);
-               extract_token(mimetype, cret, 2, '|', sizeof mimetype);
-/*             ret = CtdlIPCReadDownload(ipc, buf, bytes, 0, progress_gauge_callback, cret); */
-               ret = CtdlIPCHighSpeedReadDownload(ipc, buf, bytes, 0, progress_gauge_callback, cret);
-               ret = CtdlIPCEndDownload(ipc, cret);
-               if (ret / 100 == 2)
-                       sprintf(cret, "%d|%ld|%s|%s", (int)bytes, last_mod,
-                                       filename, mimetype);
-       }
-       return ret;
-}
-
-
-/* UOPN */
-int CtdlIPCFileUpload(CtdlIPC *ipc, const char *save_as, const char *comment, 
-               const char *path, 
-               void (*progress_gauge_callback)
-                       (CtdlIPC*, unsigned long, unsigned long),
-               char *cret)
-{
-       register int ret;
-       char *aaa;
-       FILE *uploadFP;
-       char MimeTestBuf[64];
-       const char *MimeType;
-       long len;
-
-       if (!cret) return -1;
-       if (!save_as) return -1;
-       if (!comment) return -1;
-       if (!path) return -1;
-       if (!*path) return -1;
-       if (ipc->uploading) return -1;
-
-       uploadFP = fopen(path, "r");
-       if (!uploadFP) return -2;
-
-       len = fread(&MimeTestBuf[0], 1, 64, uploadFP);
-       rewind (uploadFP);
-       if (len < 0) 
-               return -3;
-
-       MimeType = GuessMimeType(&MimeTestBuf[0], len);
-       aaa = (char *)malloc(strlen(save_as) + strlen(comment) + 7);
-       if (!aaa) return -1;
-
-       sprintf(aaa, "UOPN %s|%s|%s", save_as, MimeType,  comment);
-       ret = CtdlIPCGenericCommand(ipc, aaa, NULL, 0, NULL, NULL, cret);
-       free(aaa);
-       if (ret / 100 == 2) {
-               ipc->uploading = 1;
-               ret = CtdlIPCWriteUpload(ipc, uploadFP, progress_gauge_callback, cret);
-               ret = CtdlIPCEndUpload(ipc, (ret == -2 ? 1 : 0), cret);
-               ipc->uploading = 0;
-       }
-       return ret;
-}
-
-
-/* UIMG */
-int CtdlIPCImageUpload(CtdlIPC *ipc, int for_real, const char *path,
-               const char *save_as,
-               void (*progress_gauge_callback)
-                       (CtdlIPC*, unsigned long, unsigned long),
-               char *cret)
-{
-       register int ret;
-       FILE *uploadFP;
-       char *aaa;
-       char MimeTestBuf[64];
-       const char *MimeType;
-       long len;
-
-       if (!cret) return -1;
-       if (!save_as) return -1;
-       if (!path && for_real) return -1;
-       if (!*path && for_real) return -1;
-       if (ipc->uploading) return -1;
-
-       aaa = (char *)malloc(strlen(save_as) + 17);
-       if (!aaa) return -1;
-
-       uploadFP = fopen(path, "r");
-       if (!uploadFP) return -2;
-
-       len = fread(&MimeTestBuf[0], 1, 64, uploadFP);
-       rewind (uploadFP);
-       if (len < 0) 
-               return -3;
-       MimeType = GuessMimeType(&MimeTestBuf[0], 64);
-
-       sprintf(aaa, "UIMG %d|%s|%s", for_real, MimeType, save_as);
-       ret = CtdlIPCGenericCommand(ipc, aaa, NULL, 0, NULL, NULL, cret);
-       free(aaa);
-       if (ret / 100 == 2 && for_real) {
-               ipc->uploading = 1;
-               ret = CtdlIPCWriteUpload(ipc, uploadFP, progress_gauge_callback, cret);
-               ret = CtdlIPCEndUpload(ipc, (ret == -2 ? 1 : 0), cret);
-               ipc->uploading = 0;
-       }
-       return ret;
-}
-
-
-/* QUSR */
-int CtdlIPCQueryUsername(CtdlIPC *ipc, const char *username, char *cret)
-{
-       register int ret;
-       char *aaa;
-
-       if (!cret) return -2;
-       if (!username) return -2;
-
-       aaa = (char *)malloc(strlen(username) + 6);
-       if (!aaa) return -1;
-
-       sprintf(aaa, "QUSR %s", username);
-       ret = CtdlIPCGenericCommand(ipc, aaa, NULL, 0, NULL, NULL, cret);
-       free(aaa);
-       return ret;
-}
-
-
-/* LFLR */
-int CtdlIPCFloorListing(CtdlIPC *ipc, char **listing, char *cret)
-{
-       size_t bytes;
-
-       if (!cret) return -2;
-       if (!listing) return -2;
-       if (*listing) return -2;
-
-       return CtdlIPCGenericCommand(ipc, "LFLR", NULL, 0, listing, &bytes, cret);
-}
-
-
-/* CFLR */
-int CtdlIPCCreateFloor(CtdlIPC *ipc, int for_real, const char *name, char *cret)
-{
-       register int ret;
-       char aaa[SIZ];
-
-       if (!cret) return -2;
-       if (!name) return -2;
-
-       sprintf(aaa, "CFLR %s|%d", name, for_real);
-       ret = CtdlIPCGenericCommand(ipc, aaa, NULL, 0, NULL, NULL, cret);
-       return ret;
-}
-
-
-/* KFLR */
-int CtdlIPCDeleteFloor(CtdlIPC *ipc, int for_real, int floornum, char *cret)
-{
-       char aaa[SIZ];
-
-       if (!cret) return -1;
-       if (floornum < 0) return -1;
-
-       sprintf(aaa, "KFLR %d|%d", floornum, for_real);
-       return CtdlIPCGenericCommand(ipc, aaa, NULL, 0, NULL, NULL, cret);
-}
-
-
-/* EFLR */
-int CtdlIPCEditFloor(CtdlIPC *ipc, int floornum, const char *floorname, char *cret)
-{
-       register int ret;
-       char aaa[SIZ];
-
-       if (!cret) return -2;
-       if (!floorname) return -2;
-       if (floornum < 0) return -2;
-
-       sprintf(aaa, "EFLR %d|%s", floornum, floorname);
-       ret = CtdlIPCGenericCommand(ipc, aaa, NULL, 0, NULL, NULL, cret);
-       return ret;
-}
-
-
-/*
- * IDEN 
- *
- * You only need to fill out hostname, the defaults will be used if any of the
- * other fields are not set properly.
- */
-int CtdlIPCIdentifySoftware(CtdlIPC *ipc, int developerid, int clientid,
-               int revision, const char *software_name, const char *hostname,
-               char *cret)
-{
-       register int ret;
-       char *aaa;
-
-       if (developerid < 0 || clientid < 0 || revision < 0 ||
-           !software_name) {
-               developerid = 8;
-               clientid = 0;
-               revision = REV_LEVEL - 600;
-               software_name = "Citadel (libcitadel)";
-       }
-       if (!hostname) return -2;
-
-       aaa = (char *)malloc(strlen(software_name) + strlen(hostname) + 29);
-       if (!aaa) return -1;
-
-       sprintf(aaa, "IDEN %d|%d|%d|%s|%s", developerid, clientid,
-                       revision, software_name, hostname);
-       ret = CtdlIPCGenericCommand(ipc, aaa, NULL, 0, NULL, NULL, cret);
-       free(aaa);
-       return ret;
-}
-
-
-/* SEXP */
-int CtdlIPCSendInstantMessage(CtdlIPC *ipc, const char *username, const char *text,
-               char *cret)
-{
-       register int ret;
-       char *aaa;
-
-       if (!cret) return -2;
-       if (!username) return -2;
-
-       aaa = (char *)malloc(strlen(username) + 8);
-       if (!aaa) return -1;
-
-       if (text) {
-               sprintf(aaa, "SEXP %s|-", username);
-               ret = CtdlIPCGenericCommand(ipc, aaa, text, strlen(text),
-                               NULL, NULL, cret);
-       } else {
-               sprintf(aaa, "SEXP %s||", username);
-               ret = CtdlIPCGenericCommand(ipc, aaa, NULL, 0, NULL, NULL, cret);
-       }
-       free(aaa);
-       return ret;
-}
-
-
-/* GEXP */
-int CtdlIPCGetInstantMessage(CtdlIPC *ipc, char **listing, char *cret)
-{
-       size_t bytes;
-
-       if (!cret) return -2;
-       if (!listing) return -2;
-       if (*listing) return -2;
-
-       return CtdlIPCGenericCommand(ipc, "GEXP", NULL, 0, listing, &bytes, cret);
-}
-
-
-/* DEXP */
-/* mode is 0 = enable, 1 = disable, 2 = status */
-int CtdlIPCEnableInstantMessageReceipt(CtdlIPC *ipc, int mode, char *cret)
-{
-       char aaa[16];
-
-       if (!cret) return -2;
-
-       sprintf(aaa, "DEXP %d", mode);
-       return CtdlIPCGenericCommand(ipc, aaa, NULL, 0, NULL, NULL, cret);
-}
-
-
-/* EBIO */
-int CtdlIPCSetBio(CtdlIPC *ipc, char *bio, char *cret)
-{
-       if (!cret) return -2;
-       if (!bio) return -2;
-
-       return CtdlIPCGenericCommand(ipc, "EBIO", bio, strlen(bio),
-                       NULL, NULL, cret);
-}
-
-
-/* RBIO */
-int CtdlIPCGetBio(CtdlIPC *ipc, const char *username, char **listing, char *cret)
-{
-       register int ret;
-       size_t bytes;
-       char *aaa;
-
-       if (!cret) return -2;
-       if (!username) return -2;
-       if (!listing) return -2;
-       if (*listing) return -2;
-
-       aaa = (char *)malloc(strlen(username) + 6);
-       if (!aaa) return -1;
-
-       sprintf(aaa, "RBIO %s", username);
-       ret = CtdlIPCGenericCommand(ipc, aaa, NULL, 0, listing, &bytes, cret);
-       free(aaa);
-       return ret;
-}
-
-
-/* LBIO */
-int CtdlIPCListUsersWithBios(CtdlIPC *ipc, char **listing, char *cret)
-{
-       size_t bytes;
-
-       if (!cret) return -2;
-       if (!listing) return -2;
-       if (*listing) return -2;
-
-       return CtdlIPCGenericCommand(ipc, "LBIO", NULL, 0, listing, &bytes, cret);
-}
-
-
-/* STEL */
-int CtdlIPCStealthMode(CtdlIPC *ipc, int mode, char *cret)
-{
-       char aaa[16];
-
-       if (!cret) return -1;
-
-       sprintf(aaa, "STEL %d", mode ? 1 : 0);
-       return CtdlIPCGenericCommand(ipc, aaa, NULL, 0, NULL, NULL, cret);
-}
-
-
-/* TERM */
-int CtdlIPCTerminateSession(CtdlIPC *ipc, int sid, char *cret)
-{
-       char aaa[16];
-
-       if (!cret) return -1;
-
-       sprintf(aaa, "TERM %d", sid);
-       return CtdlIPCGenericCommand(ipc, aaa, NULL, 0, NULL, NULL, cret);
-}
-
-
-/* DOWN */
-int CtdlIPCTerminateServerNow(CtdlIPC *ipc, char *cret)
-{
-       if (!cret) return -1;
-
-       return CtdlIPCGenericCommand(ipc, "DOWN", NULL, 0, NULL, NULL, cret);
-}
-
-
-/* SCDN */
-int CtdlIPCTerminateServerScheduled(CtdlIPC *ipc, int mode, char *cret)
-{
-       char aaa[16];
-
-       if (!cret) return -1;
-
-       sprintf(aaa, "SCDN %d", mode ? 1 : 0);
-       return CtdlIPCGenericCommand(ipc, aaa, NULL, 0, NULL, NULL, cret);
-}
-
-
-/* EMSG */
-int CtdlIPCEnterSystemMessage(CtdlIPC *ipc, const char *filename, const char *text,
-               char *cret)
-{
-       register int ret;
-       char *aaa;
-
-       if (!cret) return -2;
-       if (!text) return -2;
-       if (!filename) return -2;
-
-       aaa = (char *)malloc(strlen(filename) + 6);
-       if (!aaa) return -1;
-
-       sprintf(aaa, "EMSG %s", filename);
-       ret = CtdlIPCGenericCommand(ipc, aaa, text, strlen(text), NULL, NULL, cret);
-       free(aaa);
-       return ret;
-}
-
-
-/* HCHG */
-int CtdlIPCChangeHostname(CtdlIPC *ipc, const char *hostname, char *cret)
-{
-       register int ret;
-       char *aaa;
-
-       if (!cret) return -2;
-       if (!hostname) return -2;
-
-       aaa = (char *)malloc(strlen(hostname) + 6);
-       if (!aaa) return -1;
-
-       sprintf(aaa, "HCHG %s", hostname);
-       ret = CtdlIPCGenericCommand(ipc, aaa, NULL, 0, NULL, NULL, cret);
-       free(aaa);
-       return ret;
-}
-
-
-/* RCHG */
-int CtdlIPCChangeRoomname(CtdlIPC *ipc, const char *roomname, char *cret)
-{
-       register int ret;
-       char *aaa;
-
-       if (!cret) return -2;
-       if (!roomname) return -2;
-
-       aaa = (char *)malloc(strlen(roomname) + 6);
-       if (!aaa) return -1;
-
-       sprintf(aaa, "RCHG %s", roomname);
-       ret = CtdlIPCGenericCommand(ipc, aaa, NULL, 0, NULL, NULL, cret);
-       free(aaa);
-       return ret;
-}
-
-
-/* UCHG */
-int CtdlIPCChangeUsername(CtdlIPC *ipc, const char *username, char *cret)
-{
-       register int ret;
-       char *aaa;
-
-       if (!cret) return -2;
-       if (!username) return -2;
-
-       aaa = (char *)malloc(strlen(username) + 6);
-       if (!aaa) return -1;
-
-       sprintf(aaa, "UCHG %s", username);
-       ret = CtdlIPCGenericCommand(ipc, aaa, NULL, 0, NULL, NULL, cret);
-       free(aaa);
-       return ret;
-}
-
-
-/* TIME */
-/* This function returns the actual server time reported, or 0 if error */
-time_t CtdlIPCServerTime(CtdlIPC *ipc, char *cret)
-{
-       register time_t tret;
-       register int ret;
-
-       ret = CtdlIPCGenericCommand(ipc, "TIME", NULL, 0, NULL, NULL, cret);
-       if (ret / 100 == 2) {
-               tret = extract_long(cret, 0);
-       } else {
-               tret = 0L;
-       }
-       return tret;
-}
-
-
-/* AGUP */
-int CtdlIPCAideGetUserParameters(CtdlIPC *ipc, const char *who,
-                                struct ctdluser **uret, char *cret)
-{
-       register int ret;
-       char aaa[SIZ];
-
-       if (!cret) return -2;
-       if (!uret) return -2;
-       if (!*uret) *uret = (struct ctdluser *)calloc(1, sizeof(struct ctdluser));
-       if (!*uret) return -1;
-
-       sprintf(aaa, "AGUP %s", who);
-       ret = CtdlIPCGenericCommand(ipc, aaa, NULL, 0, NULL, NULL, cret);
-
-       if (ret / 100 == 2) {
-               extract_token(uret[0]->fullname, cret, 0, '|', sizeof uret[0]->fullname);
-               extract_token(uret[0]->password, cret, 1, '|', sizeof uret[0]->password);
-               uret[0]->flags = extract_int(cret, 2);
-               uret[0]->timescalled = extract_long(cret, 3);
-               uret[0]->posted = extract_long(cret, 4);
-               uret[0]->axlevel = extract_int(cret, 5);
-               uret[0]->usernum = extract_long(cret, 6);
-               uret[0]->lastcall = extract_long(cret, 7);
-               uret[0]->USuserpurge = extract_int(cret, 8);
-       }
-       return ret;
-}
-
-
-/* ASUP */
-int CtdlIPCAideSetUserParameters(CtdlIPC *ipc, const struct ctdluser *uret, char *cret)
-{
-       register int ret;
-       char *aaa;
-
-       if (!cret) return -2;
-       if (!uret) return -2;
-
-       aaa = (char *)malloc(strlen(uret->fullname) + strlen(uret->password) + 84);
-       if (!aaa) return -1;
-
-       sprintf(aaa, "ASUP %s|%s|%d|%ld|%ld|%d|%ld|%ld|%d",
-                       uret->fullname, uret->password, uret->flags,
-                       uret->timescalled, uret->posted, uret->axlevel,
-                       uret->usernum, uret->lastcall, uret->USuserpurge);
-       ret = CtdlIPCGenericCommand(ipc, aaa, NULL, 0, NULL, NULL, cret);
-       free(aaa);
-       return ret;
-}
-
-
-/* GPEX */
-/* which is 0 = room, 1 = floor, 2 = site, 3 = default for mailboxes */
-/* caller must free the struct ExpirePolicy */
-int CtdlIPCGetMessageExpirationPolicy(CtdlIPC *ipc, int which,
-               struct ExpirePolicy **policy, char *cret)
-{
-       static char *proto[] = {"room", "floor", "site", "mailboxes" };
-       char cmd[256];
-       register int ret;
-
-       if (!cret) return -2;
-       if (!policy) return -2;
-       if (!*policy) *policy = (struct ExpirePolicy *)calloc(1, sizeof(struct ExpirePolicy));
-       if (!*policy) return -1;
-       if (which < 0 || which > 3) return -2;
-       
-       sprintf(cmd, "GPEX %s", proto[which]);
-       ret = CtdlIPCGenericCommand(ipc, cmd, NULL, 0, NULL, NULL, cret);
-       if (ret / 100 == 2) {
-               policy[0]->expire_mode = extract_int(cret, 0);
-               policy[0]->expire_value = extract_int(cret, 1);
-       }
-       return ret;
-}
-
-
-/* SPEX */
-/* which is 0 = room, 1 = floor, 2 = site, 3 = default for mailboxes */
-/* policy is 0 = inherit, 1 = no purge, 2 = by count, 3 = by age (days) */
-int CtdlIPCSetMessageExpirationPolicy(CtdlIPC *ipc, int which,
-               struct ExpirePolicy *policy, char *cret)
-{
-       char aaa[38];
-       char *whichvals[] = { "room", "floor", "site", "mailboxes" };
-
-       if (!cret) return -2;
-       if (which < 0 || which > 3) return -2;
-       if (!policy) return -2;
-       if (policy->expire_mode < 0 || policy->expire_mode > 3) return -2;
-       if (policy->expire_mode >= 2 && policy->expire_value < 1) return -2;
-
-       sprintf(aaa, "SPEX %s|%d|%d", whichvals[which],
-                       policy->expire_mode, policy->expire_value);
-       return CtdlIPCGenericCommand(ipc, aaa, NULL, 0, NULL, NULL, cret);
-}
-
-
-/* CONF GET */
-int CtdlIPCGetSystemConfig(CtdlIPC *ipc, char **listing, char *cret)
-{
-       size_t bytes;
-
-       if (!cret) return -2;
-       if (!listing) return -2;
-       if (*listing) return -2;
-
-       return CtdlIPCGenericCommand(ipc, "CONF GET", NULL, 0,
-                       listing, &bytes, cret);
-}
-
-
-/* CONF SET */
-int CtdlIPCSetSystemConfig(CtdlIPC *ipc, const char *listing, char *cret)
-{
-       if (!cret) return -2;
-       if (!listing) return -2;
-
-       return CtdlIPCGenericCommand(ipc, "CONF SET", listing, strlen(listing),
-                       NULL, NULL, cret);
-}
-
-
-/* CONF GETSYS */
-int CtdlIPCGetSystemConfigByType(CtdlIPC *ipc, const char *mimetype,
-               char **listing, char *cret)
-{
-       register int ret;
-       char *aaa;
-       size_t bytes;
-
-       if (!cret) return -2;
-       if (!mimetype) return -2;
-       if (!listing) return -2;
-       if (*listing) return -2;
-
-       aaa = malloc(strlen(mimetype) + 13);
-       if (!aaa) return -1;
-       sprintf(aaa, "CONF GETSYS|%s", mimetype);
-       ret = CtdlIPCGenericCommand(ipc, aaa, NULL, 0,
-                       listing, &bytes, cret);
-    free(aaa);
-    return ret;
-}
-
-
-/* CONF PUTSYS */
-int CtdlIPCSetSystemConfigByType(CtdlIPC *ipc, const char *mimetype,
-              const char *listing, char *cret)
-{
-    register int ret;
-       char *aaa;
-
-       if (!cret) return -2;
-       if (!mimetype) return -2;
-       if (!listing) return -2;
-
-       aaa = malloc(strlen(mimetype) + 13);
-       if (!aaa) return -1;
-       sprintf(aaa, "CONF PUTSYS|%s", mimetype);
-       ret = CtdlIPCGenericCommand(ipc, aaa, listing, strlen(listing),
-                       NULL, NULL, cret);
-    free(aaa);
-    return ret;
-}
-
-
-/* GNET */
-int CtdlIPCGetRoomNetworkConfig(CtdlIPC *ipc, char **listing, char *cret)
-{
-       size_t bytes;
-
-       if (!cret) return -2;
-       if (!listing) return -2;
-       if (*listing) return -2;
-
-       return CtdlIPCGenericCommand(ipc, "GNET", NULL, 0,
-                       listing, &bytes, cret);
-}
-
-
-/* SNET */
-int CtdlIPCSetRoomNetworkConfig(CtdlIPC *ipc, const char *listing, char *cret)
-{
-       if (!cret) return -2;
-       if (!listing) return -2;
-
-       return CtdlIPCGenericCommand(ipc, "SNET", listing, strlen(listing),
-                       NULL, NULL, cret);
-}
-
-
-/* REQT */
-int CtdlIPCRequestClientLogout(CtdlIPC *ipc, int session, char *cret)
-{
-       char aaa[16];
-
-       if (!cret) return -2;
-       if (session < 0) return -2;
-
-       sprintf(aaa, "REQT %d", session);
-       return CtdlIPCGenericCommand(ipc, aaa, NULL, 0, NULL, NULL, cret);
-}
-
-
-/* SEEN */
-int CtdlIPCSetMessageSeen(CtdlIPC *ipc, long msgnum, int seen, char *cret)
-{
-       char aaa[27];
-
-       if (!cret) return -2;
-       if (msgnum < 0) return -2;
-
-       sprintf(aaa, "SEEN %ld|%d", msgnum, seen ? 1 : 0);
-       return CtdlIPCGenericCommand(ipc, aaa, NULL, 0, NULL, NULL, cret);
-}
-
-
-/* STLS */
-int CtdlIPCStartEncryption(CtdlIPC *ipc, char *cret)
-{
-       int a;
-       int r;
-       char buf[SIZ];
-
-#ifdef HAVE_OPENSSL
-       SSL *temp_ssl;
-
-       /* New SSL object */
-       temp_ssl = SSL_new(ssl_ctx);
-       if (!temp_ssl) {
-               error_printf("SSL_new failed: %s\n",
-                               ERR_reason_error_string(ERR_get_error()));
-               return -2;
-       }
-       /* Pointless flag waving */
-#if SSLEAY_VERSION_NUMBER >= 0x0922
-       SSL_set_session_id_context(temp_ssl, "Citadel SID", 14);
-#endif
-
-       if (!access(EGD_POOL, F_OK))
-               RAND_egd(EGD_POOL);
-
-       if (!RAND_status()) {
-               error_printf("PRNG not properly seeded\n");
-               return -2;
-       }
-
-       /* Associate network connection with SSL object */
-       if (SSL_set_fd(temp_ssl, ipc->sock) < 1) {
-               error_printf("SSL_set_fd failed: %s\n",
-                               ERR_reason_error_string(ERR_get_error()));
-               return -2;
-       }
-
-       if (status_hook != NULL)
-               status_hook("Requesting encryption...\r");
-
-       /* Ready to start SSL/TLS */
-       /* Old code
-       CtdlIPC_putline(ipc, "STLS");
-       CtdlIPC_getline(ipc, buf);
-       if (buf[0] != '2') {
-               error_printf("Server can't start TLS: %s\n", buf);
-               return 0;
-       }
-       */
-       r = CtdlIPCGenericCommand(ipc,
-                                 "STLS", NULL, 0, NULL, NULL, cret);
-       if (r / 100 != 2) {
-               error_printf("Server can't start TLS: %s\n", buf);
-               endtls(temp_ssl);
-               return r;
-       }
-
-       /* Do SSL/TLS handshake */
-       if ((a = SSL_connect(temp_ssl)) < 1) {
-               error_printf("SSL_connect failed: %s\n",
-                               ERR_reason_error_string(ERR_get_error()));
-               endtls(temp_ssl);
-               return -2;
-       }
-       ipc->ssl = temp_ssl;
-
-       BIO_set_close(ipc->ssl->rbio, BIO_NOCLOSE);
-       {
-               int bits, alg_bits;
-
-               bits = SSL_CIPHER_get_bits(SSL_get_current_cipher(ipc->ssl), &alg_bits);
-               error_printf("Encrypting with %s cipher %s (%d of %d bits)\n",
-                               SSL_CIPHER_get_version(SSL_get_current_cipher(ipc->ssl)),
-                               SSL_CIPHER_get_name(SSL_get_current_cipher(ipc->ssl)),
-                               bits, alg_bits);
-       }
-       return r;
-#else
-       return 0;
-#endif /* HAVE_OPENSSL */
-}
-
-
-#ifdef HAVE_OPENSSL
-static void endtls(SSL *ssl)
-{
-       if (ssl) {
-               SSL_shutdown(ssl);
-               SSL_free(ssl);
-       }
-}
-#endif
-
-
-/* QDIR */
-int CtdlIPCDirectoryLookup(CtdlIPC *ipc, const char *address, char *cret)
-{
-    register int ret;
-       char *aaa;
-
-       if (!address) return -2;
-       if (!cret) return -2;
-
-       aaa = (char *)malloc(strlen(address) + 6);
-       if (!aaa) return -1;
-
-       sprintf(aaa, "QDIR %s", address);
-       ret = CtdlIPCGenericCommand(ipc, aaa, NULL, 0, NULL, NULL, cret);
-    free(aaa);
-    return ret;
-}
-
-
-/* IPGM */
-int CtdlIPCInternalProgram(CtdlIPC *ipc, int secret, char *cret)
-{
-       char aaa[30];
-
-       if (!cret) return -2;
-       sprintf(aaa, "IPGM %d", secret);
-       return CtdlIPCGenericCommand(ipc, aaa, NULL, 0, NULL, NULL, cret);
-}
-
-
-/* FSCK */
-int CtdlIPCMessageBaseCheck(CtdlIPC *ipc, char **mret, char *cret)
-{
-       size_t size = 0;
-
-       if (!cret) return -2;
-       if (!mret) return -2;
-       if (*mret) return -2;
-
-       return CtdlIPCGenericCommand(ipc, "FSCK", NULL, 0, mret, &size, cret);
-}
-
-
-/*
- * Not implemented:
- * 
- * CHAT
- * ETLS
- * EXPI
- * GTLS
- * IGAB
- * MSG3
- * MSG4
- * NDOP
- * NETP
- * NUOP
- * SMTP
- */
-
-
-/* ************************************************************************** */
-/*             Stuff below this line is not for public consumption            */
-/* ************************************************************************** */
-
-
-INLINE void CtdlIPC_lock(CtdlIPC *ipc)
-{
-       if (ipc->network_status_cb) ipc->network_status_cb(1);
-#ifdef THREADED_CLIENT
-       pthread_mutex_lock(&(ipc->mutex));
-#endif
-}
-
-
-INLINE void CtdlIPC_unlock(CtdlIPC *ipc)
-{
-#ifdef THREADED_CLIENT
-       pthread_mutex_unlock(&(ipc->mutex));
-#endif
-       if (ipc->network_status_cb) ipc->network_status_cb(0);
-}
-
-
-/* Read a listing from the server up to 000.  Append to dest if it exists */
-char *CtdlIPCReadListing(CtdlIPC *ipc, char *dest)
-{
-       size_t length = 0;
-       size_t linelength;
-       char *ret = NULL;
-       char aaa[SIZ];
-
-       ret = dest;
-       if (ret != NULL) {
-               length = strlen(ret);
-       } else {
-               length = 0;
-       }
-
-       while (CtdlIPC_getline(ipc, aaa), strcmp(aaa, "000")) {
-               linelength = strlen(aaa);
-               ret = (char *)realloc(ret, (size_t)(length + linelength + 2));
-               if (ret) {
-                       strcpy(&ret[length], aaa);
-                       length += linelength;
-                       strcpy(&ret[length++], "\n");
-               }
-       }
-
-       return(ret);
-}
-
-
-/* Send a listing to the server; generate the ending 000. */
-int CtdlIPCSendListing(CtdlIPC *ipc, const char *listing)
-{
-       char *text;
-
-       text = (char *)malloc(strlen(listing) + 6);
-       if (text) {
-               strcpy(text, listing);
-               while (text[strlen(text) - 1] == '\n')
-                       text[strlen(text) - 1] = '\0';
-               strcat(text, "\n000");
-               CtdlIPC_putline(ipc, text);
-               free(text);
-               text = NULL;
-       } else {
-               /* Malloc failed but we are committed to send */
-               /* This may result in extra blanks at the bottom */
-               CtdlIPC_putline(ipc, text);
-               CtdlIPC_putline(ipc, "000");
-       }
-       return 0;
-}
-
-
-/* Partial read of file from server */
-size_t CtdlIPCPartialRead(CtdlIPC *ipc, void **buf, size_t offset, size_t bytes, char *cret)
-{
-       register size_t len = 0;
-       char aaa[SIZ];
-
-       if (!buf) return 0;
-       if (!cret) return 0;
-       if (bytes < 1) return 0;
-
-       CtdlIPC_lock(ipc);
-       sprintf(aaa, "READ %d|%d", (int)offset, (int)bytes);
-       CtdlIPC_putline(ipc, aaa);
-       CtdlIPC_getline(ipc, aaa);
-       if (aaa[0] != '6')
-               strcpy(cret, &aaa[4]);
-       else {
-               len = extract_long(&aaa[4], 0);
-               *buf = (void *)realloc(*buf, (size_t)(offset + len));
-               if (*buf) {
-                       /* I know what I'm doing */
-                       serv_read(ipc, ((char *)(*buf) + offset), len);
-               } else {
-                       /* We have to read regardless */
-                       serv_read(ipc, aaa, len);
-                       len = 0;
-               }
-       }
-       CtdlIPC_unlock(ipc);
-       return len;
-}
-
-
-/* CLOS */
-int CtdlIPCEndDownload(CtdlIPC *ipc, char *cret)
-{
-       register int ret;
-
-       if (!cret) return -2;
-       if (!ipc->downloading) return -2;
-
-       ret = CtdlIPCGenericCommand(ipc, "CLOS", NULL, 0, NULL, NULL, cret);
-       if (ret / 100 == 2)
-               ipc->downloading = 0;
-       return ret;
-}
-
-
-/* MSGP */
-int CtdlIPCSpecifyPreferredFormats(CtdlIPC *ipc, char *cret, char *formats) {
-       register int ret;
-       char cmd[SIZ];
-       
-       snprintf(cmd, sizeof cmd, "MSGP %s", formats);
-       ret = CtdlIPCGenericCommand(ipc, cmd, NULL, 0, NULL, NULL, cret);
-       return ret;
-}
-
-
-
-/* READ */
-int CtdlIPCReadDownload(CtdlIPC *ipc, void **buf, size_t bytes, size_t resume,
-               void (*progress_gauge_callback)
-                       (CtdlIPC*, unsigned long, unsigned long),
-              char *cret)
-{
-       register size_t len;
-
-       if (!cret) return -1;
-       if (!buf) return -1;
-       if (*buf) return -1;
-       if (!ipc->downloading) return -1;
-
-       len = resume;
-       if (progress_gauge_callback)
-               progress_gauge_callback(ipc, len, bytes);
-       while (len < bytes) {
-               register size_t block;
-
-               block = CtdlIPCPartialRead(ipc, buf, len, 4096, cret);
-               if (block == 0) {
-                       free(*buf);
-                       return 0;
-               }
-               len += block;
-               if (progress_gauge_callback)
-                       progress_gauge_callback(ipc, len, bytes);
-       }
-       return len;
-}
-
-/* READ - pipelined */
-int CtdlIPCHighSpeedReadDownload(CtdlIPC *ipc, void **buf, size_t bytes,
-              size_t resume,
-               void (*progress_gauge_callback)
-                       (CtdlIPC*, unsigned long, unsigned long),
-              char *cret)
-{
-       register size_t len;
-       register int calls;     /* How many calls in the pipeline */
-       register int i;         /* iterator */
-       char aaa[4096];
-
-       if (!cret) return -1;
-       if (!buf) return -1;
-       if (*buf) return -1;
-       if (!ipc->downloading) return -1;
-
-       *buf = (void *)realloc(*buf, bytes - resume);
-       if (!*buf) return -1;
-
-       len = 0;
-       CtdlIPC_lock(ipc);
-       if (progress_gauge_callback)
-               progress_gauge_callback(ipc, len, bytes);
-
-       /* How many calls will be in the pipeline? */
-       calls = (bytes - resume) / 4096;
-       if ((bytes - resume) % 4096) calls++;
-
-       /* Send all requests at once */
-       for (i = 0; i < calls; i++) {
-               sprintf(aaa, "READ %d|4096", (int)(i * 4096 + resume) );
-               CtdlIPC_putline(ipc, aaa);
-       }
-
-       /* Receive all responses at once */
-       for (i = 0; i < calls; i++) {
-               CtdlIPC_getline(ipc, aaa);
-               if (aaa[0] != '6')
-                       strcpy(cret, &aaa[4]);
-               else {
-                       len = extract_long(&aaa[4], 0);
-                       /* I know what I'm doing */
-                       serv_read(ipc, ((char *)(*buf) + (i * 4096)), len);
-               }
-               if (progress_gauge_callback)
-                       progress_gauge_callback(ipc, i * 4096 + len, bytes);
-       }
-       CtdlIPC_unlock(ipc);
-       return len;
-}
-
-
-/* UCLS */
-int CtdlIPCEndUpload(CtdlIPC *ipc, int discard, char *cret)
-{
-       register int ret;
-       char cmd[8];
-
-       if (!cret) return -1;
-       if (!ipc->uploading) return -1;
-
-       sprintf(cmd, "UCLS %d", discard ? 0 : 1);
-       ret = CtdlIPCGenericCommand(ipc, cmd, NULL, 0, NULL, NULL, cret);
-       ipc->uploading = 0;
-       return ret;
-}
-
-
-/* WRIT */
-int CtdlIPCWriteUpload(CtdlIPC *ipc, FILE *uploadFP,
-               void (*progress_gauge_callback)
-                       (CtdlIPC*, unsigned long, unsigned long),
-               char *cret)
-{
-       register int ret = -1;
-       register size_t offset = 0;
-       size_t bytes;
-       char aaa[SIZ];
-       char buf[4096];
-       FILE *fd = uploadFP;
-       int ferr;
-
-       if (!cret) return -1;
-
-       fseek(fd, 0L, SEEK_END);
-       bytes = ftell(fd);
-       rewind(fd);
-
-       if (progress_gauge_callback)
-               progress_gauge_callback(ipc, 0, bytes);
-
-       while (offset < bytes) {
-               register size_t to_write;
-
-               /* Read some data in */
-               to_write = fread(buf, 1, 4096, fd);
-               if (!to_write) {
-                       if (feof(fd) || ferror(fd)) break;
-               }
-               sprintf(aaa, "WRIT %d", (int)to_write);
-               CtdlIPC_putline(ipc, aaa);
-               CtdlIPC_getline(ipc, aaa);
-               strcpy(cret, &aaa[4]);
-               ret = atoi(aaa);
-               if (aaa[0] == '7') {
-                       to_write = extract_long(&aaa[4], 0);
-                       
-                       serv_write(ipc, buf, to_write);
-                       offset += to_write;
-                       if (progress_gauge_callback)
-                               progress_gauge_callback(ipc, offset, bytes);
-                       /* Detect short reads and back up if needed */
-                       /* offset will never be negative anyway */
-                       fseek(fd, (signed)offset, SEEK_SET);
-               } else {
-                       break;
-               }
-       }
-       if (progress_gauge_callback)
-               progress_gauge_callback(ipc, 1, 1);
-       ferr = ferror(fd);
-       fclose(fd);
-       return (!ferr ? ret : -2);
-}
-
-
-/*
- * Generic command method.  This method should handle any server command
- * except for CHAT.  It takes the following arguments:
- *
- * ipc                 The server to speak with
- * command             Preformatted command to send to server
- * to_send             A text or binary file to send to server
- *                     (only sent if server requests it)
- * bytes_to_send       The number of bytes in to_send (required if
- *                     sending binary, optional if sending listing)
- * to_receive          Pointer to a NULL pointer, if the server
- *                     sends text or binary we will allocate memory
- *                     for the file and stuff it here
- * bytes_to_receive    If a file is received, we will store its
- *                     byte count here
- * proto_response      The protocol response.  Caller must provide
- *                     this buffer and ensure that it is at least
- *                     128 bytes in length.
- *
- * This function returns a number equal to the protocol response number,
- * -1 if an internal error occurred, -2 if caller provided bad values,
- * or 0 - the protocol response number if bad values were found during
- * the protocol exchange.
- * It stores the protocol response string (minus the number) in 
- * protocol_response as described above.  Some commands send additional
- * data in this string.
- */
-int CtdlIPCGenericCommand(CtdlIPC *ipc,
-               const char *command, const char *to_send,
-               size_t bytes_to_send, char **to_receive, 
-               size_t *bytes_to_receive, char *proto_response)
-{
-       char buf[SIZ];
-       register int ret;
-       int watch_ssl = 0;
-
-       if (!command) return -2;
-       if (!proto_response) return -2;
-
-#ifdef HAVE_OPENSSL
-       if (ipc->ssl) watch_ssl = 1;
-#endif
-
-       CtdlIPC_lock(ipc);
-       CtdlIPC_putline(ipc, command);
-       while (1) {
-               CtdlIPC_getline(ipc, proto_response);
-               if (proto_response[3] == '*')
-                       instant_msgs = 1;
-               ret = atoi(proto_response);
-               strcpy(proto_response, &proto_response[4]);
-               switch (ret / 100) {
-               default:                        /* Unknown, punt */
-               case 2:                         /* OK */
-               case 3:                         /* MORE_DATA */
-               case 5:                         /* ERROR */
-                       /* Don't need to do anything */
-                       break;
-               case 1:                         /* LISTING_FOLLOWS */
-                       if (to_receive && !*to_receive && bytes_to_receive) {
-                               *to_receive = CtdlIPCReadListing(ipc, NULL);
-                       } else { /* Drain */
-                               while (CtdlIPC_getline(ipc, buf), strcmp(buf, "000")) ;
-                               ret = -ret;
-                       }
-                       break;
-               case 4:                         /* SEND_LISTING */
-                       if (to_send) {
-                               CtdlIPCSendListing(ipc, to_send);
-                       } else {
-                               /* No listing given, fake it */
-                               CtdlIPC_putline(ipc, "000");
-                               ret = -ret;
-                       }
-                       break;
-               case 6:                         /* BINARY_FOLLOWS */
-                       if (to_receive && !*to_receive && bytes_to_receive) {
-                               *bytes_to_receive =
-                                       extract_long(proto_response, 0);
-                               *to_receive = (char *)
-                                       malloc((size_t)*bytes_to_receive);
-                               if (!*to_receive) {
-                                       ret = -1;
-                               } else {
-                                       serv_read(ipc, *to_receive,
-                                                       *bytes_to_receive);
-                               }
-                       } else {
-                               /* Drain */
-                               size_t drain;
-
-                               drain = extract_long(proto_response, 0);
-                               while (drain > SIZ) {
-                                       serv_read(ipc, buf, SIZ);
-                                       drain -= SIZ;
-                               }
-                               serv_read(ipc, buf, drain);
-                               ret = -ret;
-                       }
-                       break;
-               case 7:                         /* SEND_BINARY */
-                       if (to_send && bytes_to_send) {
-                               serv_write(ipc, to_send, bytes_to_send);
-                       } else if (bytes_to_send) {
-                               /* Fake it, send nulls */
-                               size_t fake;
-
-                               fake = bytes_to_send;
-                               memset(buf, '\0', SIZ);
-                               while (fake > SIZ) {
-                                       serv_write(ipc, buf, SIZ);
-                                       fake -= SIZ;
-                               }
-                               serv_write(ipc, buf, fake);
-                               ret = -ret;
-                       } /* else who knows?  DANGER WILL ROBINSON */
-                       break;
-               case 8:                         /* START_CHAT_MODE */
-                       if (!strncasecmp(command, "CHAT", 4)) {
-                               /* Don't call chatmode with generic! */
-                               CtdlIPC_putline(ipc, "/quit");
-                               ret = -ret;
-                       } else {
-                               /* In this mode we send then receive listing */
-                               if (to_send) {
-                                       CtdlIPCSendListing(ipc, to_send);
-                               } else {
-                                       /* No listing given, fake it */
-                                       CtdlIPC_putline(ipc, "000");
-                                       ret = -ret;
-                               }
-                               if (to_receive && !*to_receive
-                                               && bytes_to_receive) {
-                                       *to_receive = CtdlIPCReadListing(ipc, NULL);
-                               } else { /* Drain */
-                                       while (CtdlIPC_getline(ipc, buf),
-                                                       strcmp(buf, "000")) ;
-                                       ret = -ret;
-                               }
-                       }
-                       break;
-               case 9:                         /* ASYNC_MSG */
-                       /* CtdlIPCDoAsync(ret, proto_response); */
-                       free(CtdlIPCReadListing(ipc, NULL));    /* STUB FIXME */
-                       break;
-               }
-               if (ret / 100 != 9)
-                       break;
-       }
-       CtdlIPC_unlock(ipc);
-       return ret;
-}
-
-
-static int connectsock(char *host, char *service, char *protocol, int defaultPort)
-{
-       struct hostent *phe;
-       struct servent *pse;
-       struct protoent *ppe;
-       struct sockaddr_in sin;
-       int s, type;
-
-       memset(&sin, 0, sizeof(sin));
-       sin.sin_family = AF_INET;
-
-       pse = getservbyname(service, protocol);
-       if (pse != NULL) {
-               sin.sin_port = pse->s_port;
-       }
-       else if (atoi(service) > 0) {
-               sin.sin_port = htons(atoi(service));
-       }
-       else {
-               sin.sin_port = htons(defaultPort);
-       }
-       phe = gethostbyname(host);
-       if (phe) {
-               memcpy(&sin.sin_addr, phe->h_addr, phe->h_length);
-       } else if ((sin.sin_addr.s_addr = inet_addr(host)) == INADDR_NONE) {
-               return -1;
-       }
-       if ((ppe = getprotobyname(protocol)) == 0) {
-               return -1;
-       }
-       if (!strcmp(protocol, "udp")) {
-               type = SOCK_DGRAM;
-       } else {
-               type = SOCK_STREAM;
-       }
-
-       s = socket(PF_INET, type, ppe->p_proto);
-       if (s < 0) {
-               return -1;
-       }
-
-       if (connect(s, (struct sockaddr *) &sin, sizeof(sin)) < 0) {
-               close(s);
-               return -1;
-       }
-
-       return (s);
-}
-
-static int uds_connectsock(int *isLocal, char *sockpath)
-{
-       struct sockaddr_un addr;
-       int s;
-
-       memset(&addr, 0, sizeof(addr));
-       addr.sun_family = AF_UNIX;
-       safestrncpy(addr.sun_path, sockpath, sizeof addr.sun_path);
-
-       s = socket(AF_UNIX, SOCK_STREAM, 0);
-       if (s < 0) {
-               return -1;
-       }
-
-       if (connect(s, (struct sockaddr *) &addr, sizeof(addr)) < 0) {
-               close(s);
-               return -1;
-       }
-
-       *isLocal = 1;
-       return s;
-}
-
-
-/*
- * input binary data from socket
- */
-static void serv_read(CtdlIPC *ipc, char *buf, unsigned int bytes)
-{
-       unsigned int len, rlen;
-
-#if defined(HAVE_OPENSSL)
-       if (ipc->ssl) {
-               serv_read_ssl(ipc, buf, bytes);
-               return;
-       }
-#endif
-       len = 0;
-       while (len < bytes) {
-               rlen = read(ipc->sock, &buf[len], bytes - len);
-               if (rlen < 1) {
-                       connection_died(ipc, 0);
-                       return;
-               }
-               len += rlen;
-       }
-}
-
-
-/*
- * send binary to server
- */
-void serv_write(CtdlIPC *ipc, const char *buf, unsigned int nbytes)
-{
-       unsigned int bytes_written = 0;
-       int retval;
-
-#if defined(HAVE_OPENSSL)
-       if (ipc->ssl) {
-               serv_write_ssl(ipc, buf, nbytes);
-               return;
-       }
-#endif
-       while (bytes_written < nbytes) {
-               retval = write(ipc->sock, &buf[bytes_written],
-                              nbytes - bytes_written);
-               if (retval < 1) {
-                       connection_died(ipc, 0);
-                       return;
-               }
-               bytes_written += retval;
-       }
-}
-
-
-#ifdef HAVE_OPENSSL
-/*
- * input binary data from encrypted connection
- */
-static void serv_read_ssl(CtdlIPC* ipc, char *buf, unsigned int bytes)
-{
-       int len, rlen;
-       char junk[1];
-
-       len = 0;
-       while (len < bytes) {
-               if (SSL_want_read(ipc->ssl)) {
-                       if ((SSL_write(ipc->ssl, junk, 0)) < 1) {
-                               error_printf("SSL_write in serv_read:\n");
-                               ERR_print_errors_fp(stderr);
-                       }
-               }
-               rlen = SSL_read(ipc->ssl, &buf[len], bytes - len);
-               if (rlen < 1) {
-                       long errval;
-
-                       errval = SSL_get_error(ipc->ssl, rlen);
-                       if (errval == SSL_ERROR_WANT_READ ||
-                                       errval == SSL_ERROR_WANT_WRITE) {
-                               sleep(1);
-                               continue;
-                       }
-/***
- Not sure why we'd want to handle these error codes any differently,
- but this definitely isn't the way to handle them.  Someone must have
- naively assumed that we could fall back to unencrypted communications,
- but all it does is just recursively blow the stack.
-                       if (errval == SSL_ERROR_ZERO_RETURN ||
-                                       errval == SSL_ERROR_SSL) {
-                               serv_read(ipc, &buf[len], bytes - len);
-                               return;
-                       }
- ***/
-                       error_printf("SSL_read in serv_read: %s\n",
-                                       ERR_reason_error_string(ERR_peek_error()));
-                       connection_died(ipc, 1);
-                       return;
-               }
-               len += rlen;
-       }
-}
-
-
-/*
- * send binary to server encrypted
- */
-static void serv_write_ssl(CtdlIPC *ipc, const char *buf, unsigned int nbytes)
-{
-       unsigned int bytes_written = 0;
-       int retval;
-       char junk[1];
-
-       while (bytes_written < nbytes) {
-               if (SSL_want_write(ipc->ssl)) {
-                       if ((SSL_read(ipc->ssl, junk, 0)) < 1) {
-                               error_printf("SSL_read in serv_write:\n");
-                               ERR_print_errors_fp(stderr);
-                       }
-               }
-               retval = SSL_write(ipc->ssl, &buf[bytes_written],
-                               nbytes - bytes_written);
-               if (retval < 1) {
-                       long errval;
-
-                       errval = SSL_get_error(ipc->ssl, retval);
-                       if (errval == SSL_ERROR_WANT_READ ||
-                                       errval == SSL_ERROR_WANT_WRITE) {
-                               sleep(1);
-                               continue;
-                       }
-                       if (errval == SSL_ERROR_ZERO_RETURN ||
-                                       errval == SSL_ERROR_SSL) {
-                               serv_write(ipc, &buf[bytes_written],
-                                               nbytes - bytes_written);
-                               return;
-                       }
-                       error_printf("SSL_write in serv_write: %s\n",
-                                       ERR_reason_error_string(ERR_peek_error()));
-                       connection_died(ipc, 1);
-                       return;
-               }
-               bytes_written += retval;
-       }
-}
-
-
-#ifdef THREADED_CLIENT
-static void ssl_lock(int mode, int n, const char *file, int line)
-{
-       if (mode & CRYPTO_LOCK)
-               pthread_mutex_lock(Critters[n]);
-       else
-               pthread_mutex_unlock(Critters[n]);
-}
-#endif /* THREADED_CLIENT */
-
-
-static void CtdlIPC_init_OpenSSL(void)
-{
-       int a;
-       SSL_METHOD *ssl_method;
-       DH *dh;
-       
-       /* already done init */
-       if (ssl_ctx) {
-               return;
-       }
-
-       /* Get started */
-       a = 0;
-       ssl_ctx = NULL;
-       dh = NULL;
-       SSL_load_error_strings();
-       SSLeay_add_ssl_algorithms();
-
-       /* Set up the SSL context in which we will oeprate */
-       ssl_method = SSLv23_client_method();
-       ssl_ctx = SSL_CTX_new(ssl_method);
-       if (!ssl_ctx) {
-               error_printf("SSL_CTX_new failed: %s\n",
-                               ERR_reason_error_string(ERR_get_error()));
-               return;
-       }
-       /* Any reasonable cipher we can get */
-       if (!(SSL_CTX_set_cipher_list(ssl_ctx, CIT_CIPHERS))) {
-               error_printf("No ciphers available for encryption\n");
-               return;
-       }
-       SSL_CTX_set_session_cache_mode(ssl_ctx, SSL_SESS_CACHE_BOTH);
-       
-       /* Load DH parameters into the context */
-       dh = DH_new();
-       if (!dh) {
-               error_printf("Can't allocate a DH object: %s\n",
-                               ERR_reason_error_string(ERR_get_error()));
-               return;
-       }
-       if (!(BN_hex2bn(&(dh->p), DH_P))) {
-               error_printf("Can't assign DH_P: %s\n",
-                               ERR_reason_error_string(ERR_get_error()));
-               DH_free(dh);
-               return;
-       }
-       if (!(BN_hex2bn(&(dh->g), DH_G))) {
-               error_printf("Can't assign DH_G: %s\n",
-                               ERR_reason_error_string(ERR_get_error()));
-               DH_free(dh);
-               return;
-       }
-       dh->length = DH_L;
-       SSL_CTX_set_tmp_dh(ssl_ctx, dh);
-       DH_free(dh);
-
-#ifdef THREADED_CLIENT
-       /* OpenSSL requires callbacks for threaded clients */
-       CRYPTO_set_locking_callback(ssl_lock);
-       CRYPTO_set_id_callback(id_callback);
-
-       /* OpenSSL requires us to do semaphores for threaded clients */
-       Critters = malloc(CRYPTO_num_locks() * sizeof (pthread_mutex_t *));
-       if (!Critters) {
-               perror("malloc failed");
-               exit(1);
-       } else {
-               for (a = 0; a < CRYPTO_num_locks(); a++) {
-                       Critters[a] = malloc(sizeof (pthread_mutex_t));
-                       if (!Critters[a]) {
-                               perror("malloc failed");
-                               exit(1);
-                       }
-                       pthread_mutex_init(Critters[a], NULL);
-               }
-       }
-#endif /* THREADED_CLIENT */       
-}
-
-
-
-#ifdef THREADED_CLIENT
-static unsigned long id_callback(void) {
-       return (unsigned long)pthread_self();
-}
-#endif /* THREADED_CLIENT */
-#endif /* HAVE_OPENSSL */
-
-
-int
-ReadNetworkChunk(CtdlIPC* ipc)
-{
-       fd_set read_fd;
-       int tries;
-       int ret = 0;
-       int err = 0;
-       struct timeval tv;
-       size_t n;
-
-       tv.tv_sec = 1;
-       tv.tv_usec = 1000;
-       tries = 0;
-       n = 0;
-       while (1)
-       {
-               errno=0;
-               FD_ZERO(&read_fd);
-               FD_SET(ipc->sock, &read_fd);
-               ret = select(ipc->sock+1, &read_fd, NULL, NULL,  &tv);
-               
-//             fprintf(stderr, "\nselect failed: %d %d %s\n", ret,  err, strerror(err));
-               
-               if (ret > 0) {
-                       
-                       *(ipc->BufPtr) = '\0';
-//                     n = read(ipc->sock, ipc->BufPtr, ipc->BufSize  - (ipc->BufPtr - ipc->Buf) - 1);
-                       n = recv(ipc->sock, ipc->BufPtr, ipc->BufSize  - (ipc->BufPtr - ipc->Buf) - 1, 0);
-                       if (n > 0) {
-                               ipc->BufPtr[n]='\0';
-                               ipc->BufUsed += n;
-                               return n;
-                       }
-                       else 
-                               return n;
-               }
-               else if (ret < 0) {
-                       if (!(errno == EINTR || errno == EAGAIN))
-                               error_printf( "\nselect failed: %d %s\n", err, strerror(err));
-                       return -1;
-               }/*
-               else {
-                       tries ++;
-                       if (tries >= 10)
-                       n = read(ipc->sock, ipc->BufPtr, ipc->BufSize  - (ipc->BufPtr - ipc->Buf) - 1);
-                       if (n > 0) {
-                               ipc->BufPtr[n]='\0';
-                               ipc->BufUsed += n;
-                               return n;
-                       }
-                       else {
-                               connection_died(ipc, 0);
-                               return -1;
-                       }
-                       }*/
-       }
-}
-
-/*
- * input string from socket - implemented in terms of serv_read()
- */
-#ifdef CHUNKED_READ
-
-static void CtdlIPC_getline(CtdlIPC* ipc, char *buf)
-{
-       int i, ntries;
-       char *aptr, *bptr, *aeptr, *beptr;
-
-//     error_printf("---\n");
-
-       beptr = buf + SIZ;
-#if defined(HAVE_OPENSSL)
-       if (ipc->ssl) {
-               
-               /* Read one character at a time. */
-               for (i = 0;; i++) {
-                       serv_read(ipc, &buf[i], 1);
-                       if (buf[i] == '\n' || i == (SIZ-1))
-                               break;
-               }
-               
-               /* If we got a long line, discard characters until the newline. */
-               if (i == (SIZ-1))
-                       while (buf[i] != '\n')
-                               serv_read(ipc, &buf[i], 1);
-               
-               /* Strip the trailing newline (and carriage return, if present) */
-               if (i>=0 && buf[i] == 10) buf[i--] = 0;
-               if (i>=0 && buf[i] == 13) buf[i--] = 0;
-       }
-       else
-#endif
-       {
-               if (ipc->Buf == NULL)
-               {
-                       ipc->BufSize = SIZ;
-                       ipc->Buf = (char*) malloc(ipc->BufSize + 10);
-                       *(ipc->Buf) = '\0';
-                       ipc->BufPtr = ipc->Buf;
-               }
-
-               ntries = 0;
-//             while ((ipc->BufUsed == 0)||(ntries++ > 10))
-               if (ipc->BufUsed == 0)
-                       ReadNetworkChunk(ipc);
-
-////           if (ipc->BufUsed != 0) while (1)
-               bptr = buf;
-
-               while (1)
-               {
-                       aptr = ipc->BufPtr;
-                       aeptr = ipc->Buf + ipc->BufSize;
-                       while ((aptr < aeptr) && 
-                              (bptr < beptr) &&
-                              (*aptr != '\0') && 
-                              (*aptr != '\n'))
-                               *(bptr++) = *(aptr++);
-                       if ((*aptr == '\n') && (aptr < aeptr))
-                       {
-                               /* Terminate it right, remove the line breaks */
-                               while ((aptr < aeptr) && ((*aptr == '\n') || (*aptr == '\r')))
-                                       aptr ++;
-                               while ((aptr < aeptr ) && (*(aptr + 1) == '\0') )
-                                       aptr ++;
-                               *(bptr++) = '\0';
-//                             fprintf(stderr, "parsing %d %d %d - %d %d %d %s\n", ipc->BufPtr - ipc->Buf, aptr - ipc->BufPtr, ipc->BufUsed , *aptr, *(aptr-1), *(aptr+1), buf);
-                               if ((bptr > buf + 1) && (*(bptr-1) == '\r'))
-                                       *(--bptr) = '\0';
-                               
-                               /* is there more in the buffer we need to read later? */
-                               if (ipc->Buf + ipc->BufUsed > aptr)
-                               {
-                                       ipc->BufPtr = aptr;
-                               }
-                               else
-                               {
-                                       ipc->BufUsed = 0;
-                                       ipc->BufPtr = ipc->Buf;
-                               }
-//                             error_printf("----bla6\n");
-                               return;
-                               
-                       }/* should we move our read stuf to the bufferstart so we have more space at the end? */
-                       else if ((ipc->BufPtr != ipc->Buf) && 
-                                (ipc->BufUsed > (ipc->BufSize  - (ipc->BufSize / 4))))
-                       {
-                               size_t NewBufSize = ipc->BufSize * 2;
-                               int delta = (ipc->BufPtr - ipc->Buf);
-                               char *NewBuf;
-
-                               /* if the line would end after our buffer, we should use a bigger buffer. */
-                               NewBuf = (char *)malloc (NewBufSize + 10);
-                               memcpy (NewBuf, ipc->BufPtr, ipc->BufUsed - delta);
-                               free(ipc->Buf);
-                               ipc->Buf = ipc->BufPtr = NewBuf;
-                               ipc->BufUsed -= delta;
-                               ipc->BufSize = NewBufSize;
-                       }
-                       if (ReadNetworkChunk(ipc) <0)
-                       {
-//                             error_printf("----bla\n");
-                               return;
-                       }
-               }
-///            error_printf("----bl45761%s\nipc->BufUsed");
-       }
-//     error_printf("----bla1\n");
-}
-
-#else  /* CHUNKED_READ */
-
-static void CtdlIPC_getline(CtdlIPC* ipc, char *buf)
-{
-       int i;
-
-       /* Read one character at a time. */
-       for (i = 0;; i++) {
-               serv_read(ipc, &buf[i], 1);
-               if (buf[i] == '\n' || i == (SIZ-1))
-                       break;
-       }
-
-       /* If we got a long line, discard characters until the newline. */
-       if (i == (SIZ-1))
-               while (buf[i] != '\n')
-                       serv_read(ipc, &buf[i], 1);
-
-       /* Strip the trailing newline (and carriage return, if present) */
-       if (i>=0 && buf[i] == 10) buf[i--] = 0;
-       if (i>=0 && buf[i] == 13) buf[i--] = 0;
-}
-
-
-#endif /* CHUNKED_READ */
-
-
-void CtdlIPC_chat_recv(CtdlIPC* ipc, char* buf)
-{
-       CtdlIPC_getline(ipc, buf);
-}
-
-/*
- * send line to server - implemented in terms of serv_write()
- */
-static void CtdlIPC_putline(CtdlIPC *ipc, const char *buf)
-{
-       char *cmd = NULL;
-       int len;
-
-       len = strlen(buf);
-       cmd = malloc(len + 2);
-       if (!cmd) {
-               /* This requires no extra memory */
-               serv_write(ipc, buf, len);
-               serv_write(ipc, "\n", 1);
-       } else {
-               /* This is network-optimized */
-               strncpy(cmd, buf, len);
-               strcpy(cmd + len, "\n");
-               serv_write(ipc, cmd, len + 1);
-               free(cmd);
-       }
-
-       ipc->last_command_sent = time(NULL);
-}
-
-void CtdlIPC_chat_send(CtdlIPC* ipc, const char* buf)
-{
-       CtdlIPC_putline(ipc, buf);
-}
-
-
-/*
- * attach to server
- */
-CtdlIPC* CtdlIPC_new(int argc, char **argv, char *hostbuf, char *portbuf)
-{
-       int a;
-       char cithost[SIZ];
-       char citport[SIZ];
-       char sockpath[SIZ];
-       CtdlIPC* ipc;
-
-       ipc = ialloc(CtdlIPC);
-       if (!ipc) {
-               return 0;
-       }
-#if defined(HAVE_OPENSSL)
-       ipc->ssl = NULL;
-       CtdlIPC_init_OpenSSL();
-#endif
-#if defined(HAVE_PTHREAD_H)
-       pthread_mutex_init(&(ipc->mutex), NULL); /* Default fast mutex */
-#endif
-       ipc->sock = -1;                 /* Not connected */
-       ipc->isLocal = 0;               /* Not local, of course! */
-       ipc->downloading = 0;
-       ipc->uploading = 0;
-       ipc->last_command_sent = 0L;
-       ipc->network_status_cb = NULL;
-       ipc->Buf = NULL;
-       ipc->BufUsed = 0;
-       ipc->BufPtr = NULL;
-
-       strcpy(cithost, DEFAULT_HOST);  /* default host */
-       strcpy(citport, DEFAULT_PORT);  /* default port */
-
-       /* Allow caller to supply our values (Windows) */
-       if (hostbuf && strlen(hostbuf) > 0)
-               strcpy(cithost, hostbuf);
-       if (portbuf && strlen(portbuf) > 0)
-               strcpy(citport, portbuf);
-
-       /* Read host/port from command line if present */
-       for (a = 0; a < argc; ++a) {
-               if (a == 0) {
-                       /* do nothing */
-               } else if (a == 1) {
-                       strcpy(cithost, argv[a]);
-               } else if (a == 2) {
-                       strcpy(citport, argv[a]);
-               } else {
-                       error_printf("%s: usage: ",argv[0]);
-                       error_printf("%s [host] [port] ",argv[0]);
-                       ifree(ipc);
-                       errno = EINVAL;
-                       return 0;
-               }
-       }
-
-       if ((!strcmp(cithost, "localhost"))
-          || (!strcmp(cithost, "127.0.0.1"))) {
-               ipc->isLocal = 1;
-       }
-
-       /* If we're using a unix domain socket we can do a bunch of stuff */
-       if (!strcmp(cithost, UDS)) {
-               if (!strcasecmp(citport, DEFAULT_PORT)) {
-                       snprintf(sockpath, sizeof sockpath, file_citadel_socket);
-               }
-               else {
-                       snprintf(sockpath, sizeof sockpath, "%s/%s", citport, "citadel.socket");
-               }
-               ipc->sock = uds_connectsock(&(ipc->isLocal), sockpath);
-               if (ipc->sock == -1) {
-                       ifree(ipc);
-                       return 0;
-               }
-               if (hostbuf != NULL) strcpy(hostbuf, cithost);
-               if (portbuf != NULL) strcpy(portbuf, sockpath);
-               return ipc;
-       }
-
-       ipc->sock = connectsock(cithost, citport, "tcp", 504);
-       if (ipc->sock == -1) {
-               ifree(ipc);
-               return 0;
-       }
-       if (hostbuf != NULL) strcpy(hostbuf, cithost);
-       if (portbuf != NULL) strcpy(portbuf, citport);
-       return ipc;
-}
-
-
-/*
- * Disconnect and delete the IPC class (destructor)
- */
-void CtdlIPC_delete(CtdlIPC* ipc)
-{
-#ifdef HAVE_OPENSSL
-       if (ipc->ssl) {
-               SSL_shutdown(ipc->ssl);
-               SSL_free(ipc->ssl);
-               ipc->ssl = NULL;
-       }
-#endif
-       if (ipc->sock > -1) {
-               shutdown(ipc->sock, 2); /* Close it up */
-               ipc->sock = -1;
-       }
-       if (ipc->Buf != NULL)
-               free (ipc->Buf);
-       ipc->Buf = NULL;
-       ipc->BufPtr = NULL;
-       ifree(ipc);
-}
-
-
-/*
- * Disconnect and delete the IPC class (destructor)
- * Also NULLs out the pointer
- */
-void CtdlIPC_delete_ptr(CtdlIPC** pipc)
-{
-       CtdlIPC_delete(*pipc);
-       *pipc = NULL;
-}
-
-
-/*
- * return the file descriptor of the server socket so we can select() on it.
- *
- * FIXME: This is only used in chat mode; eliminate it when chat mode gets
- * rewritten...
- */
-int CtdlIPC_getsockfd(CtdlIPC* ipc)
-{
-       return ipc->sock;
-}
-
-
-/*
- * return one character
- *
- * FIXME: This is only used in chat mode; eliminate it when chat mode gets
- * rewritten...
- */
-char CtdlIPC_get(CtdlIPC* ipc)
-{
-       char buf[2];
-       char ch;
-
-       serv_read(ipc, buf, 1);
-       ch = (int) buf[0];
-
-       return (ch);
-}
diff --git a/citadel/citadel_ipc.h b/citadel/citadel_ipc.h
deleted file mode 100644 (file)
index 01b0636..0000000
+++ /dev/null
@@ -1,371 +0,0 @@
-/* $Id$ */
-
-#define        UDS                     "_UDS_"
-#ifdef __CYGWIN__
-#define DEFAULT_HOST           "localhost"
-#else
-#define DEFAULT_HOST           UDS
-#endif
-#define DEFAULT_PORT           "citadel"
-
-#include "sysdep.h"
-#ifdef HAVE_PTHREAD_H
-#include <pthread.h>
-#endif
-#ifdef HAVE_OPENSSL
-#include <openssl/ssl.h>
-#include <openssl/err.h>
-#include <openssl/rand.h>
-#endif
-
-#include "server.h"
-
-#ifdef __cplusplus
-extern "C" {
-#endif
-
-/* Quick and dirty hack; we don't want to use malloc() in C++ */
-#ifdef __cplusplus
-#define ialloc(t)      new t()
-#define ifree(o)       delete o
-#else
-#define ialloc(t)      malloc(sizeof(t))
-#define ifree(o)       free(o);
-#endif
-
-struct CtdlServInfo {
-       int pid;
-       char nodename[32];
-       char humannode[64];
-       char fqdn[64];
-       char software[64];
-       int rev_level;
-       char site_location[64];
-       char sysadm[64];
-       char moreprompt[256];
-       int ok_floors;
-       int paging_level;
-       int supports_qnop;
-       int supports_ldap;
-       int newuser_disabled;
-       char default_cal_zone[256];
-       double load_avg;
-       double worker_avg;
-       int thread_count;
-       int has_sieve;
-       int fulltext_enabled;
-       char svn_revision[256];
-};
-
-/* This class is responsible for the server connection */
-typedef struct _CtdlIPC {
-       /* The server info for this connection */
-       struct CtdlServInfo ServInfo;
-
-#if defined(HAVE_OPENSSL)
-       /* NULL if not encrypted, non-NULL otherwise */
-       SSL *ssl;
-#endif
-#if defined(HAVE_PTHREAD_H)
-       /* Fast mutex, call CtdlIPC_lock() or CtdlIPC_unlock() to use */
-       pthread_mutex_t mutex;
-#endif
-       /* -1 if not connected, >= 0 otherwise */
-       int sock;
-       /* 1 if server is local, 0 otherwise or if not connected */
-       int isLocal;
-       /* 1 if a download is open on the server, 0 otherwise */
-       int downloading;
-       /* 1 if an upload is open on the server, 0 otherwise */
-       int uploading;
-       /* Time the last command was sent to the server */
-       time_t last_command_sent;
-       /* Our buffer for linebuffered read. */
-       char *Buf;
-       size_t BufSize;
-       size_t BufUsed;
-       char *BufPtr;
-       /* Callback for update on whether the IPC is locked */
-       void (*network_status_cb)(int state);
-} CtdlIPC;
-
-/* C constructor */
-CtdlIPC* CtdlIPC_new(int argc, char **argv, char *hostbuf, char *portbuf);
-/* C destructor */
-void CtdlIPC_delete(CtdlIPC* ipc);
-/* Convenience destructor; also nulls out caller's pointer */
-void CtdlIPC_delete_ptr(CtdlIPC** pipc);
-/* Read a line from server, discarding newline, for chat, will go away */
-void CtdlIPC_chat_recv(CtdlIPC* ipc, char *buf);
-/* Write a line to server, adding newline, for chat, will go away */
-void CtdlIPC_chat_send(CtdlIPC* ipc, const char *buf);
-
-struct ctdlipcroom {
-       char RRname[ROOMNAMELEN];       /* Name of room */
-       long RRunread;                  /* Number of unread messages */
-       long RRtotal;                   /* Total number of messages in room */
-       char RRinfoupdated;             /* Nonzero if info was updated */
-       unsigned RRflags;               /* Various flags (see LKRN) */
-       unsigned RRflags2;              /* Various flags (see LKRN) */
-       long RRhighest;                 /* Highest message number in room */
-       long RRlastread;                /* Highest message user has read */
-       char RRismailbox;               /* Is this room a mailbox room? */
-       char RRaide;                    /* User can do aide commands in room */
-       long RRnewmail;                 /* Number of new mail messages */
-       char RRfloor;                   /* Which floor this room is on */
-};
-
-
-struct parts {
-       struct parts *next;
-       char number[16];                /* part number */
-       char name[PATH_MAX];            /* Name */
-       char filename[PATH_MAX];        /* Suggested filename */
-       char mimetype[SIZ];             /* MIME type */
-       char disposition[SIZ];          /* Content disposition */
-       unsigned long length;           /* Content length */
-};
-
-
-struct ctdlipcmessage {
-       char msgid[SIZ];                /* Original message ID */
-       char path[SIZ];                 /* Return path to sender */
-       char zaps[SIZ];                 /* Message ID that this supersedes */
-       char subject[SIZ];              /* Message subject */
-       char email[SIZ];                /* Email address of sender */
-       char author[SIZ];               /* Sender of message */
-       char recipient[SIZ];            /* Recipient of message */
-       char room[SIZ];                 /* Originating room */
-       char node[SIZ];                 /* Short nodename of origin system */
-       char hnod[SIZ];                 /* Humannode of origin system */
-       struct parts *attachments;      /* Available attachments */
-       char *text;                     /* Message text */
-       int type;                       /* Message type */
-       time_t time;                    /* Time message was posted */
-       char nhdr;                      /* Suppress message header? */
-       char anonymous;                 /* An anonymous message */
-       char mime_chosen[SIZ];          /* Chosen MIME part to output */
-       char content_type[SIZ];         /* How would you like that? */
-       char references[SIZ];           /* Thread references */
-};
-
-
-struct ctdlipcfile {
-       char remote_name[PATH_MAX];     /* Filename on server */
-       char local_name[PATH_MAX];      /* Filename on client */
-       char description[80];           /* Description on server */
-       FILE *local_fd;                 /* Open file on client */
-       size_t size;                    /* Size of file in octets */
-       unsigned int upload:1;          /* uploading? 0 if downloading */
-       unsigned int complete:1;        /* Transfer has finished? */
-};
-
-
-struct ctdlipcmisc {
-       long newmail;                   /* Number of new Mail messages */
-       char needregis;                 /* Nonzero if user needs to register */
-       char needvalid;                 /* Nonzero if users need validation */
-};
-
-enum RoomList {
-       SubscribedRooms,
-       SubscribedRoomsWithNewMessages,
-       SubscribedRoomsWithNoNewMessages,
-       UnsubscribedRooms,
-       AllAccessibleRooms,
-       AllPublicRooms
-};
-#define AllFloors -1
-enum MessageList {
-       AllMessages,
-       OldMessages,
-       NewMessages,
-       LastMessages,
-       FirstMessages,
-       MessagesGreaterThan,
-       MessagesLessThan
-};
-enum MessageDirection {
-       ReadReverse = -1,
-       ReadForward = 1
-};
-
-/* Shared Diffie-Hellman parameters */
-#define DH_P           "1A74527AEE4EE2568E85D4FB2E65E18C9394B9C80C42507D7A6A0DBE9A9A54B05A9A96800C34C7AA5297095B69C88901EEFD127F969DCA26A54C0E0B5C5473EBAEB00957D2633ECAE3835775425DE66C0DE6D024DBB17445E06E6B0C78415E589B8814F08531D02FD43778451E7685541079CFFB79EF0D26EFEEBBB69D1E80383"
-#define DH_G           "2"
-#define DH_L           1024
-#define CIT_CIPHERS    "ALL:RC4+RSA:+SSLv2:+TLSv1:!MD5:@STRENGTH"      /* see ciphers(1) */
-
-int CtdlIPCNoop(CtdlIPC *ipc);
-int CtdlIPCEcho(CtdlIPC *ipc, const char *arg, char *cret);
-int CtdlIPCQuit(CtdlIPC *ipc);
-int CtdlIPCLogout(CtdlIPC *ipc);
-int CtdlIPCTryLogin(CtdlIPC *ipc, const char *username, char *cret);
-int CtdlIPCTryPassword(CtdlIPC *ipc, const char *passwd, char *cret);
-int CtdlIPCTryApopPassword(CtdlIPC *ipc, const char *response, char *cret);
-int CtdlIPCCreateUser(CtdlIPC *ipc, const char *username, int selfservice,
-               char *cret);
-int CtdlIPCChangePassword(CtdlIPC *ipc, const char *passwd, char *cret);
-int CtdlIPCKnownRooms(CtdlIPC *ipc, enum RoomList which, int floor,
-               struct march **listing, char *cret);
-int CtdlIPCGetConfig(CtdlIPC *ipc, struct ctdluser **uret, char *cret);
-int CtdlIPCSetConfig(CtdlIPC *ipc, struct ctdluser *uret, char *cret);
-int CtdlIPCGotoRoom(CtdlIPC *ipc, const char *room, const char *passwd,
-               struct ctdlipcroom **rret, char *cret);
-int CtdlIPCGetMessages(CtdlIPC *ipc, enum MessageList which, int whicharg,
-               const char *mtemplate, unsigned long **mret, char *cret);
-int CtdlIPCGetSingleMessage(CtdlIPC *ipc, long msgnum, int headers, int as_mime,
-               struct ctdlipcmessage **mret, char *cret);
-int CtdlIPCWhoKnowsRoom(CtdlIPC *ipc, char **listing, char *cret);
-int CtdlIPCServerInfo(CtdlIPC *ipc, char *cret);
-/* int CtdlIPCReadDirectory(CtdlIPC *ipc, struct ctdlipcfile **files, char *cret); */
-int CtdlIPCReadDirectory(CtdlIPC *ipc, char **listing, char *cret);
-int CtdlIPCSetLastRead(CtdlIPC *ipc, long msgnum, char *cret);
-int CtdlIPCInviteUserToRoom(CtdlIPC *ipc, const char *username, char *cret);
-int CtdlIPCKickoutUserFromRoom(CtdlIPC *ipc, const char *username, char *cret);
-int CtdlIPCGetRoomAttributes(CtdlIPC *ipc, struct ctdlroom **qret, char *cret);
-int CtdlIPCSetRoomAttributes(CtdlIPC *ipc, int forget, struct ctdlroom *qret,
-               char *cret);
-int CtdlIPCGetRoomAide(CtdlIPC *ipc, char *cret);
-int CtdlIPCSetRoomAide(CtdlIPC *ipc, const char *username, char *cret);
-int CtdlIPCPostMessage(CtdlIPC *ipc, int flag, int *subject_required, 
-                                          struct ctdlipcmessage *mr,
-                                          char *cret);
-int CtdlIPCRoomInfo(CtdlIPC *ipc, char **iret, char *cret);
-int CtdlIPCDeleteMessage(CtdlIPC *ipc, long msgnum, char *cret);
-int CtdlIPCMoveMessage(CtdlIPC *ipc, int copy, long msgnum,
-               const char *destroom, char *cret);
-int CtdlIPCDeleteRoom(CtdlIPC *ipc, int for_real, char *cret);
-int CtdlIPCCreateRoom(CtdlIPC *ipc, int for_real, const char *roomname,
-               int type, const char *password, int floor, char *cret);
-int CtdlIPCForgetRoom(CtdlIPC *ipc, char *cret);
-int CtdlIPCSystemMessage(CtdlIPC *ipc, const char *message, char **mret,
-               char *cret);
-int CtdlIPCNextUnvalidatedUser(CtdlIPC *ipc, char *cret);
-int CtdlIPCGetUserRegistration(CtdlIPC *ipc, const char *username, char **rret,
-               char *cret);
-int CtdlIPCValidateUser(CtdlIPC *ipc, const char *username, int axlevel,
-               char *cret);
-int CtdlIPCSetRoomInfo(CtdlIPC *ipc, int for_real, const char *info,
-               char *cret);
-int CtdlIPCUserListing(CtdlIPC *ipc, char *searchstring, char **list, char *cret);
-int CtdlIPCSetRegistration(CtdlIPC *ipc, const char *info, char *cret);
-int CtdlIPCMiscCheck(CtdlIPC *ipc, struct ctdlipcmisc *chek, char *cret);
-int CtdlIPCDeleteFile(CtdlIPC *ipc, const char *filename, char *cret);
-int CtdlIPCMoveFile(CtdlIPC *ipc, const char *filename, const char *destroom,
-               char *cret);
-int CtdlIPCNetSendFile(CtdlIPC *ipc, const char *filename,
-               const char *destnode, char *cret);
-int CtdlIPCOnlineUsers(CtdlIPC *ipc, char **listing, time_t *stamp, char *cret);
-int CtdlIPCFileDownload(CtdlIPC *ipc, const char *filename, void **buf,
-               size_t resume,
-               void (*progress_gauge_callback)(CtdlIPC*, unsigned long, unsigned long),
-               char *cret);
-int CtdlIPCAttachmentDownload(CtdlIPC *ipc, long msgnum, const char *part,
-               void **buf,
-               void (*progress_gauge_callback)(CtdlIPC*, unsigned long, unsigned long),
-               char *cret);
-int CtdlIPCImageDownload(CtdlIPC *ipc, const char *filename, void **buf,
-               void (*progress_gauge_callback)(CtdlIPC*, unsigned long, unsigned long),
-               char *cret);
-int CtdlIPCFileUpload(CtdlIPC *ipc, const char *save_as, const char *comment,
-               const char *path, 
-               void (*progress_gauge_callback)(CtdlIPC*, unsigned long, unsigned long),
-               char *cret);
-int CtdlIPCImageUpload(CtdlIPC *ipc, int for_real, const char *path,
-               const char *save_as,
-               void (*progress_gauge_callback)(CtdlIPC*, unsigned long, unsigned long),
-               char *cret);
-int CtdlIPCQueryUsername(CtdlIPC *ipc, const char *username, char *cret);
-int CtdlIPCFloorListing(CtdlIPC *ipc, char **listing, char *cret);
-int CtdlIPCCreateFloor(CtdlIPC *ipc, int for_real, const char *name,
-               char *cret);
-int CtdlIPCDeleteFloor(CtdlIPC *ipc, int for_real, int floornum, char *cret);
-int CtdlIPCEditFloor(CtdlIPC *ipc, int floornum, const char *floorname,
-               char *cret);
-int CtdlIPCIdentifySoftware(CtdlIPC *ipc, int developerid, int clientid,
-               int revision, const char *software_name, const char *hostname,
-               char *cret);
-int CtdlIPCSendInstantMessage(CtdlIPC *ipc, const char *username,
-               const char *text, char *cret);
-int CtdlIPCGetInstantMessage(CtdlIPC *ipc, char **listing, char *cret);
-int CtdlIPCEnableInstantMessageReceipt(CtdlIPC *ipc, int mode, char *cret);
-int CtdlIPCSetBio(CtdlIPC *ipc, char *bio, char *cret);
-int CtdlIPCGetBio(CtdlIPC *ipc, const char *username, char **listing,
-               char *cret);
-int CtdlIPCListUsersWithBios(CtdlIPC *ipc, char **listing, char *cret);
-int CtdlIPCStealthMode(CtdlIPC *ipc, int mode, char *cret);
-int CtdlIPCTerminateSession(CtdlIPC *ipc, int sid, char *cret);
-int CtdlIPCTerminateServerNow(CtdlIPC *ipc, char *cret);
-int CtdlIPCTerminateServerScheduled(CtdlIPC *ipc, int mode, char *cret);
-int CtdlIPCEnterSystemMessage(CtdlIPC *ipc, const char *filename, const char *text,
-               char *cret);
-int CtdlIPCChangeHostname(CtdlIPC *ipc, const char *hostname, char *cret);
-int CtdlIPCChangeRoomname(CtdlIPC *ipc, const char *roomname, char *cret);
-int CtdlIPCChangeUsername(CtdlIPC *ipc, const char *username, char *cret);
-time_t CtdlIPCServerTime(CtdlIPC *ipc, char *crert);
-int CtdlIPCAideGetUserParameters(CtdlIPC *ipc, const char *who,
-                                struct ctdluser **uret, char *cret);
-int CtdlIPCAideSetUserParameters(CtdlIPC *ipc, const struct ctdluser *uret, char *cret);
-int CtdlIPCRenameUser(CtdlIPC *ipc, char *oldname, char *newname, char *cret);
-int CtdlIPCGetMessageExpirationPolicy(CtdlIPC *ipc, int which,
-               struct ExpirePolicy **policy, char *cret);
-int CtdlIPCSetMessageExpirationPolicy(CtdlIPC *ipc, int which,
-               struct ExpirePolicy *policy, char *cret);
-int CtdlIPCGetSystemConfig(CtdlIPC *ipc, char **listing, char *cret);
-int CtdlIPCSetSystemConfig(CtdlIPC *ipc, const char *listing, char *cret);
-int CtdlIPCGetSystemConfigByType(CtdlIPC *ipc, const char *mimetype,
-               char **listing, char *cret);
-int CtdlIPCSetSystemConfigByType(CtdlIPC *ipc, const char *mimetype,
-              const char *listing, char *cret);
-int CtdlIPCGetRoomNetworkConfig(CtdlIPC *ipc, char **listing, char *cret);
-int CtdlIPCSetRoomNetworkConfig(CtdlIPC *ipc, const char *listing, char *cret);
-int CtdlIPCRequestClientLogout(CtdlIPC *ipc, int session, char *cret);
-int CtdlIPCSetMessageSeen(CtdlIPC *ipc, long msgnum, int seen, char *cret);
-int CtdlIPCStartEncryption(CtdlIPC *ipc, char *cret);
-int CtdlIPCDirectoryLookup(CtdlIPC *ipc, const char *address, char *cret);
-int CtdlIPCSpecifyPreferredFormats(CtdlIPC *ipc, char *cret, char *formats);
-int CtdlIPCInternalProgram(CtdlIPC *ipc, int secret, char *cret);
-int CtdlIPCMessageBaseCheck(CtdlIPC *ipc, char **mret, char *cret);
-
-/* ************************************************************************** */
-/*             Stuff below this line is not for public consumption            */
-/* ************************************************************************** */
-
-INLINE void CtdlIPC_lock(CtdlIPC *ipc);
-INLINE void CtdlIPC_unlock(CtdlIPC *ipc);
-char *CtdlIPCReadListing(CtdlIPC *ipc, char *dest);
-int CtdlIPCSendListing(CtdlIPC *ipc, const char *listing);
-size_t CtdlIPCPartialRead(CtdlIPC *ipc, void **buf, size_t offset,
-               size_t bytes, char *cret);
-int CtdlIPCEndUpload(CtdlIPC *ipc, int discard, char *cret);
-int CtdlIPCWriteUpload(CtdlIPC *ipc, FILE *uploadFP,
-               void (*progress_gauge_callback)(CtdlIPC*, unsigned long, unsigned long),
-               char *cret);
-int CtdlIPCEndDownload(CtdlIPC *ipc, char *cret);
-int CtdlIPCReadDownload(CtdlIPC *ipc, void **buf, size_t bytes, size_t resume,
-               void (*progress_gauge_callback)(CtdlIPC*, unsigned long, unsigned long),
-               char *cret);
-int CtdlIPCHighSpeedReadDownload(CtdlIPC *ipc, void **buf, size_t bytes,
-               size_t resume,
-               void (*progress_gauge_callback)(CtdlIPC*, unsigned long, unsigned long),
-               char *cret);
-int CtdlIPCGenericCommand(CtdlIPC *ipc, const char *command,
-               const char *to_send, size_t bytes_to_send, char **to_receive,
-               size_t *bytes_to_receive, char *proto_response);
-
-/* Internals */
-int starttls(CtdlIPC *ipc);
-void setCryptoStatusHook(void (*hook)(char *s));
-void CtdlIPC_SetNetworkStatusCallback(CtdlIPC *ipc, void (*hook)(int state));
-/* This is all Ford's doing.  FIXME: figure out what it's doing */
-extern int (*error_printf)(char *s, ...);
-void setIPCDeathHook(void (*hook)(void));
-void setIPCErrorPrintf(int (*func)(char *s, ...));
-void connection_died(CtdlIPC* ipc, int using_ssl);
-int CtdlIPC_getsockfd(CtdlIPC* ipc);
-char CtdlIPC_get(CtdlIPC* ipc);
-
-#ifdef __cplusplus
-}
-#endif
diff --git a/citadel/citmail.c b/citadel/citmail.c
deleted file mode 100644 (file)
index 2f1bdc2..0000000
+++ /dev/null
@@ -1,340 +0,0 @@
-/*
- * $Id$
- *
- * This program attempts to act like a local MDA if you're using sendmail or
- * some other non-Citadel MTA.  It basically just contacts the Citadel LMTP
- * listener on a unix domain socket and transmits the message.
- *
- * Copyright (c) 1987-2009 by the citadel.org team
- *
- *  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 3 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; if not, write to the Free Software
- *  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
- */
-
-#include "sysdep.h"
-#include <stdlib.h>
-#include <unistd.h>
-#include <ctype.h>
-#include <stdio.h>
-#include <signal.h>
-#include <sys/types.h>
-#include <sys/socket.h>
-#include <sys/un.h>
-#include <netdb.h>
-#include <string.h>
-#include <pwd.h>
-#include <errno.h>
-#include <stdarg.h>
-#include <limits.h>
-#include <libcitadel.h>
-#include "citadel.h"
-#ifndef HAVE_SNPRINTF
-#include "snprintf.h"
-#endif
-#include "citadel_dirs.h"
-
-int serv_sock;
-int debug = 0;
-
-void strip_trailing_nonprint(char *buf)
-{
-        while ( (!IsEmptyStr(buf)) && (!isprint(buf[strlen(buf) - 1])) )
-                buf[strlen(buf) - 1] = 0;
-}
-
-
-void timeout(int signum)
-{
-       exit(signum);
-}
-
-
-int uds_connectsock(char *sockpath)
-{
-       int s;
-       struct sockaddr_un addr;
-
-       memset(&addr, 0, sizeof(addr));
-       addr.sun_family = AF_UNIX;
-       strncpy(addr.sun_path, sockpath, sizeof addr.sun_path);
-
-       s = socket(AF_UNIX, SOCK_STREAM, 0);
-       if (s < 0) {
-               fprintf(stderr, "citmail: Can't create socket: %s\n",
-                       strerror(errno));
-               exit(3);
-       }
-
-       if (connect(s, (struct sockaddr *) &addr, sizeof(addr)) < 0) {
-               fprintf(stderr, "citmail: can't connect: %s\n",
-                       strerror(errno));
-               close(s);
-               exit(3);
-       }
-
-       return s;
-}
-
-
-/*
- * input binary data from socket
- */
-void serv_read(char *buf, int bytes)
-{
-       int len, rlen;
-
-       len = 0;
-       while (len < bytes) {
-               rlen = read(serv_sock, &buf[len], bytes - len);
-               if (rlen < 1) {
-                       return;
-               }
-               len = len + rlen;
-       }
-}
-
-
-/*
- * send binary to server
- */
-void serv_write(char *buf, int nbytes)
-{
-       int bytes_written = 0;
-       int retval;
-       while (bytes_written < nbytes) {
-               retval = write(serv_sock, &buf[bytes_written],
-                              nbytes - bytes_written);
-               if (retval < 1) {
-                       return;
-               }
-               bytes_written = bytes_written + retval;
-       }
-}
-
-
-
-/*
- * input string from socket - implemented in terms of serv_read()
- */
-void serv_gets(char *buf)
-{
-       int i;
-
-       /* Read one character at a time.
-        */
-       for (i = 0;; i++) {
-               serv_read(&buf[i], 1);
-               if (buf[i] == '\n' || i == (SIZ-1))
-                       break;
-       }
-
-       /* If we got a long line, discard characters until the newline.
-        */
-       if (i == (SIZ-1))
-               while (buf[i] != '\n')
-                       serv_read(&buf[i], 1);
-
-       /* Strip all trailing nonprintables (crlf)
-        */
-       buf[i] = 0;
-       strip_trailing_nonprint(buf);
-       if (debug) fprintf(stderr, "> %s\n", buf);
-}
-
-
-/*
- * send line to server - implemented in terms of serv_write()
- */
-void serv_puts(char *buf)
-{
-       if (debug) fprintf(stderr, "< %s\n", buf);
-       serv_write(buf, strlen(buf));
-       serv_write("\n", 1);
-}
-
-
-
-void cleanup(int exitcode) {
-       char buf[1024];
-
-       if (exitcode != 0) {
-               fprintf(stderr, "citmail: error #%d occurred while sending mail.\n", exitcode);
-               fprintf(stderr, "Please check your Citadel configuration.\n");
-       }
-       serv_puts("QUIT");
-       serv_gets(buf);
-       exit(exitcode);
-}
-
-
-
-int main(int argc, char **argv) {
-       char buf[1024];
-       char fromline[1024];
-       FILE *fp;
-       int i;
-       struct passwd *pw;
-       int from_header = 0;
-       int in_body = 0;
-       int relh=0;
-       int home=0;
-       char relhome[PATH_MAX]="";
-       char ctdldir[PATH_MAX]=CTDLDIR;
-       char *sp, *ep;
-       char hostname[256];
-       char **recipients = NULL;
-       int num_recipients = 0;
-       int to_or_cc = 0;
-       int read_recipients_from_headers = 0;
-       char *add_these_recipients = NULL;
-
-       for (i=1; i<argc; ++i) {
-               if (!strcmp(argv[i], "-d")) {
-                       debug = 1;
-               }
-               else if (!strcmp(argv[i], "-t")) {
-                       read_recipients_from_headers = 1;
-               }
-               else if (argv[i][0] != '-') {
-                       ++num_recipients;
-                       recipients = realloc(recipients, (num_recipients * sizeof (char *)));
-                       recipients[num_recipients - 1] = strdup(argv[i]);
-               }
-       }
-              
-       /* TODO: should we be able to calculate relative dirs? */
-       calc_dirs_n_files(relh, home, relhome, ctdldir, 0);
-
-       pw = getpwuid(getuid());
-
-       fp = tmpfile();
-       if (fp == NULL) return(errno);
-       serv_sock = uds_connectsock(file_lmtp_socket);  /* FIXME: if called as 'sendmail' connect to file_lmtp_unfiltered_socket */
-       serv_gets(buf);
-       if (buf[0] != '2') {
-               fprintf(stderr, "%s\n", &buf[4]);
-               if (debug) fprintf(stderr, "citmail: could not connect to LMTP socket.\n");
-               cleanup(1);
-       }
-
-       sp = strchr (buf, ' ');
-       if (sp == NULL) {
-               if (debug) fprintf(stderr, "citmail: ould not calculate hostname.\n");
-               cleanup(2);
-       }
-       sp ++;
-       ep = strchr (sp, ' ');
-       if (ep == NULL) cleanup(3);
-       *ep = '\0';
-       strncpy(hostname, sp, sizeof hostname);
-
-       snprintf(fromline, sizeof fromline, "From: %s@%s", pw->pw_name, hostname);
-       while (fgets(buf, 1024, stdin) != NULL) {
-               if ( ( (buf[0] == 13) || (buf[0] == 10)) && (in_body == 0) ) {
-                       in_body = 1;
-                       if (from_header == 0) {
-                               fprintf(fp, "%s%s", fromline, buf);
-                       }
-               }
-               if (in_body == 0 && !strncasecmp(buf, "From:", 5)) {
-                       strcpy(fromline, buf);
-                       from_header = 1;
-               }
-
-               if (read_recipients_from_headers) {
-                       add_these_recipients = NULL;
-                       if ((isspace(buf[0])) && (to_or_cc)) {
-                               add_these_recipients = buf;
-                       }
-                       else {
-                               if ((!strncasecmp(buf, "To:", 3)) || (!strncasecmp(buf, "Cc:", 3))) {
-                                       to_or_cc = 1;
-                               }
-                               else {
-                                       to_or_cc = 0;
-                               }
-                               if (to_or_cc) {
-                                       add_these_recipients = &buf[3];
-                               }
-                       }
-
-                       if (add_these_recipients) {
-                               int num_recp_on_this_line;
-                               char this_recp[256];
-
-                               num_recp_on_this_line = num_tokens(add_these_recipients, ',');
-                               for (i=0; i<num_recp_on_this_line; ++i) {
-                                       extract_token(this_recp, add_these_recipients,
-                                               i, ',', sizeof this_recp);
-                                       striplt(this_recp);
-                                       if (!IsEmptyStr(this_recp)) {
-                                               ++num_recipients;
-                                               recipients = realloc(recipients,
-                                                       (num_recipients * sizeof (char *)));
-                                               recipients[num_recipients - 1] = strdup(this_recp);
-                                       }
-                               }
-                       }
-               }
-
-               fprintf(fp, "%s", buf);
-       }
-       strip_trailing_nonprint(fromline);
-
-       sprintf(buf, "LHLO %s", hostname);
-       serv_puts(buf);
-       do {
-               serv_gets(buf);
-               strcat(buf, "    ");
-       } while (buf[3] == '-');
-       if (buf[0] != '2') cleanup(4);
-
-       snprintf(buf, sizeof buf, "MAIL %s", fromline);
-       serv_puts(buf);
-       serv_gets(buf);
-       if (buf[0] != '2') cleanup(5);
-
-       for (i=0; i<num_recipients; ++i) {
-               snprintf(buf, sizeof buf, "RCPT To: %s", recipients[i]);
-               serv_puts(buf);
-               serv_gets(buf);
-               free(recipients[i]);
-       }
-       free(recipients);
-
-       serv_puts("DATA");
-       serv_gets(buf);
-       if (buf[0]!='3') cleanup(6);
-
-       rewind(fp);
-       while (fgets(buf, sizeof buf, fp) != NULL) {
-               strip_trailing_nonprint(buf);
-               serv_puts(buf);
-       }
-       serv_puts(".");
-       serv_gets(buf);
-       if (buf[0] != '2') {
-               fprintf(stderr, "%s\n", &buf[4]);
-               cleanup(7);
-       }
-       else {
-               cleanup(0);
-       }
-
-       /* We won't actually reach this statement but the compiler will
-        * display a spurious warning about an invalid return type if
-        * we don't return an int.
-        */
-       return(0);
-}
diff --git a/citadel/client_chat.c b/citadel/client_chat.c
deleted file mode 100644 (file)
index c86914f..0000000
+++ /dev/null
@@ -1,333 +0,0 @@
-/*
- * $Id$
- *
- * front end for chat mode
- * (the "single process" version - no more fork() anymore)
- *
- * Copyright (c) 1987-2009 by the citadel.org team
- *
- *  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 3 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; if not, write to the Free Software
- *  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
- */
-
-#include "sysdep.h"
-#include <stdlib.h>
-#include <unistd.h>
-#include <stdio.h>
-#include <fcntl.h>
-#include <ctype.h>
-#include <string.h>
-#include <signal.h>
-#include <errno.h>
-
-#if TIME_WITH_SYS_TIME
-# include <sys/time.h>
-# include <time.h>
-#else
-# if HAVE_SYS_TIME_H
-#  include <sys/time.h>
-# else
-#  include <time.h>
-# endif
-#endif
-
-#include <sys/types.h>
-#ifdef HAVE_SYS_SELECT_H
-#include <sys/select.h>
-#endif
-#include <stdarg.h>
-#include <libcitadel.h>
-#include "citadel.h"
-#include "citadel_ipc.h"
-#include "client_chat.h"
-#include "commands.h"
-#include "routines.h"
-#include "citadel_decls.h"
-#include "rooms.h"
-#include "messages.h"
-#ifndef HAVE_SNPRINTF
-#include "snprintf.h"
-#endif
-#include "screen.h"
-
-#define MIN(a, b) ((a) < (b) ? (a) : (b))
-
-extern char temp[];
-void ctdl_getline(char *, int);
-
-char last_paged[SIZ] = "";
-
-void chatmode(CtdlIPC *ipc)
-{
-       char wbuf[SIZ];
-       char buf[SIZ];
-       char c_user[SIZ];
-       char c_text[SIZ];
-       char c_room[SIZ];
-       char last_user[SIZ];
-       int send_complete_line;
-       int recv_complete_line;
-       char ch;
-       int a, pos;
-       time_t last_transmit;
-
-       fd_set rfds;
-       struct timeval tv;
-       int retval;
-
-       CtdlIPC_chat_send(ipc, "CHAT");
-       CtdlIPC_chat_recv(ipc, buf);
-       if (buf[0] != '8') {
-               scr_printf("%s\n", &buf[4]);
-               return;
-       }
-       scr_printf("Entering chat mode "
-               "(type /quit to exit, /help for other cmds)\n");
-       set_keepalives(KA_NO);
-       last_transmit = time(NULL);
-
-       strcpy(buf, "");
-       strcpy(wbuf, "");
-       strcpy(last_user, ""); 
-       color(BRIGHT_YELLOW);
-       sln_printf_if("\n");
-       sln_printf("> ");
-       send_complete_line = 0;
-       recv_complete_line = 0;
-
-       while (1) {
-               sln_flush();
-               FD_ZERO(&rfds);
-               FD_SET(0, &rfds);
-               FD_SET(CtdlIPC_getsockfd(ipc), &rfds);
-               tv.tv_sec = S_KEEPALIVE;
-               tv.tv_usec = 0;
-               retval = select(CtdlIPC_getsockfd(ipc) + 1, &rfds,
-                       NULL, NULL, &tv);
-
-               /* If there's data from the server... */
-               if (FD_ISSET(CtdlIPC_getsockfd(ipc), &rfds)) {
-                       CtdlIPC_chat_recv(ipc, buf);
-                       recv_complete_line = 1;
-                       goto RCL;       /* ugly, but we've gotta get out! */
-               }
-
-               /* If there's data from the keyboard... */
-               if (FD_ISSET(0, &rfds)) {
-                       ch = scr_getc(SCR_BLOCK);
-                       if ((ch == 10) || (ch == 13)) {
-                               send_complete_line = 1;
-                       } else if ((ch == 8) || (ch == 127)) {
-                               if (!IsEmptyStr(wbuf)) {
-                                       wbuf[strlen(wbuf) - 1] = 0;
-                                       sln_printf("%c %c", 8, 8);
-                               }
-                       } else {
-                               sln_putc(ch);
-                               wbuf[strlen(wbuf) + 1] = 0;
-                               wbuf[strlen(wbuf)] = ch;
-                       }
-               }
-
-               /* if the user hit return, send the line */
-RCL:           if (send_complete_line) {
-                       CtdlIPC_chat_send(ipc, wbuf);
-                       last_transmit = time(NULL);
-                       strcpy(wbuf, "");
-                       send_complete_line = 0;
-               }
-
-               /* if it's time to word wrap, send a partial line */
-               if (strlen(wbuf) >= (77 - strlen(fullname))) {
-                       pos = 0;
-                       for (a = 0; !IsEmptyStr(&wbuf[a]); ++a) {
-                               if (wbuf[a] == 32)
-                                       pos = a;
-                       }
-                       if (pos == 0) {
-                               CtdlIPC_chat_send(ipc, wbuf);
-                               last_transmit = time(NULL);
-                               strcpy(wbuf, "");
-                               send_complete_line = 0;
-                       } else {
-                               wbuf[pos] = 0;
-                               CtdlIPC_chat_send(ipc, wbuf);
-                               last_transmit = time(NULL);
-                               strcpy(wbuf, &wbuf[pos + 1]);
-                       }
-               }
-
-               if (recv_complete_line) {
-                       sln_printf("\r%79s\r", "");
-                       if (!strcmp(buf, "000")) {
-                               color(BRIGHT_WHITE);
-                               sln_printf("\rExiting chat mode\n");
-                               sln_flush();
-                               set_keepalives(KA_YES);
-
-                               /* Some users complained about the client and
-                                * server losing protocol synchronization when
-                                * exiting chat.  This little dialog forces
-                                * everything to be hunky-dory.
-                                */
-                               CtdlIPC_chat_send(ipc, "ECHO __ExitingChat__");
-                               do {
-                                       CtdlIPC_chat_recv(ipc, buf);
-                               } while (strcmp(buf, "200 __ExitingChat__"));
-                               return;
-                       }
-                       if (num_parms(buf) >= 2) {
-                               extract_token(c_user, buf, 0, '|', sizeof c_user);
-                               extract_token(c_text, buf, 1, '|', sizeof c_text);
-                               if (num_parms(buf) > 2) {
-                                       extract_token(c_room, buf, 2, '|', sizeof c_room);
-                                       scr_printf("Got room %s\n", c_room);
-                               }
-                               if (strcasecmp(c_text, "NOOP")) {
-                                       if (!strcmp(c_user, fullname)) {
-                                               color(BRIGHT_YELLOW);
-                                       } else if (!strcmp(c_user, ":")) {
-                                               color(BRIGHT_RED);
-                                       } else {
-                                               color(BRIGHT_GREEN);
-                                       }
-                                       if (strcmp(c_user, last_user)) {
-                                               snprintf(buf, sizeof buf, "%s: %s", c_user, c_text);
-                                       } else {
-                                               size_t i = MIN(sizeof buf - 1,
-                                                    strlen(c_user) + 2);
-
-                                               memset(buf, ' ', i);
-                                               safestrncpy(&buf[i], c_text,
-                                                        sizeof buf - i);
-                                       }
-                                       while (strlen(buf) < 79)
-                                               strcat(buf, " ");
-                                       if (strcmp(c_user, last_user)) {
-                                               sln_printf("\r%79s\n", "");
-                                               strcpy(last_user, c_user);
-                                       }
-                                       scr_printf("\r%s\n", buf);
-                                       scr_flush();
-                               }
-                       }
-                       color(BRIGHT_YELLOW);
-                       sln_printf("\r> %s", wbuf);
-                       sln_flush();
-                       recv_complete_line = 0;
-                       strcpy(buf, "");
-               }
-
-               /* If the user is sitting idle, send a half-keepalive to the
-                * server to prevent session timeout.
-                */
-               if ((time(NULL) - last_transmit) >= S_KEEPALIVE) {
-                       CtdlIPC_chat_send(ipc, "NOOP");
-                       last_transmit = time(NULL);
-               }
-
-       }
-}
-
-/*
- * send an instant message
- */
-void page_user(CtdlIPC *ipc)
-{
-       char buf[SIZ], touser[SIZ], msg[SIZ];
-       FILE *pagefp;
-
-       strcpy(touser, last_paged);
-       strprompt("Page who", touser, 30);
-
-       /* old server -- use inline paging */
-       if (ipc->ServInfo.paging_level == 0) {
-               newprompt("Message: ", msg, 69);
-               snprintf(buf, sizeof buf, "SEXP %s|%s", touser, msg);
-               CtdlIPC_chat_send(ipc, buf);
-               CtdlIPC_chat_recv(ipc, buf);
-               if (!strncmp(buf, "200", 3)) {
-                       strcpy(last_paged, touser);
-               }
-               scr_printf("%s\n", &buf[4]);
-               return;
-       }
-       /* new server -- use extended paging */
-       else if (ipc->ServInfo.paging_level >= 1) {
-               snprintf(buf, sizeof buf, "SEXP %s||", touser);
-               CtdlIPC_chat_send(ipc, buf);
-               CtdlIPC_chat_recv(ipc, buf);
-               if (buf[0] != '2') {
-                       scr_printf("%s\n", &buf[4]);
-                       return;
-               }
-               if (client_make_message(ipc, temp, touser, 0, 0, 0, NULL, 0) != 0) {
-                       scr_printf("No message sent.\n");
-                       return;
-               }
-               pagefp = fopen(temp, "r");
-               unlink(temp);
-               snprintf(buf, sizeof buf, "SEXP %s|-", touser);
-               CtdlIPC_chat_send(ipc, buf);
-               CtdlIPC_chat_recv(ipc, buf);
-               if (buf[0] == '4') {
-                       strcpy(last_paged, touser);
-                       while (fgets(buf, sizeof buf, pagefp) != NULL) {
-                               buf[strlen(buf) - 1] = 0;
-                               CtdlIPC_chat_send(ipc, buf);
-                       }
-                       fclose(pagefp);
-                       CtdlIPC_chat_send(ipc, "000");
-                       scr_printf("Message sent.\n");
-               } else {
-                       scr_printf("%s\n", &buf[4]);
-               }
-       }
-}
-
-
-void quiet_mode(CtdlIPC *ipc)
-{
-       static int quiet = 0;
-       char cret[SIZ];
-       int r;
-
-       r = CtdlIPCEnableInstantMessageReceipt(ipc, !quiet, cret);
-       if (r / 100 == 2) {
-               quiet = !quiet;
-               scr_printf("Quiet mode %sabled (%sother users may page you)\n",
-                               (quiet) ? "en" : "dis",
-                               (quiet) ? "no " : "");
-       } else {
-               scr_printf("Unable to change quiet mode: %s\n", cret);
-       }
-}
-
-
-void stealth_mode(CtdlIPC *ipc)
-{
-       static int stealth = 0;
-       char cret[SIZ];
-       int r;
-
-       r = CtdlIPCStealthMode(ipc, !stealth, cret);
-       if (r / 100 == 2) {
-               stealth = !stealth;
-               scr_printf("Stealth mode %sabled (you are %s)\n",
-                               (stealth) ? "en" : "dis",
-                               (stealth) ? "invisible" : "listed as online");
-       } else {
-               scr_printf("Unable to change stealth mode: %s\n", cret);
-       }
-}
diff --git a/citadel/client_chat.h b/citadel/client_chat.h
deleted file mode 100644 (file)
index 4515961..0000000
+++ /dev/null
@@ -1,25 +0,0 @@
-/* $Id$ 
- *
- * Copyright (c) 1987-2009 by the citadel.org team
- *
- *  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 3 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; if not, write to the Free Software
- *  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
- */
-
-void chatmode(CtdlIPC *ipc);
-void page_user(CtdlIPC *ipc);
-void quiet_mode(CtdlIPC *ipc);
-void stealth_mode(CtdlIPC *ipc);
-
-extern char last_paged[];
diff --git a/citadel/client_passwords.c b/citadel/client_passwords.c
deleted file mode 100644 (file)
index 4922a12..0000000
+++ /dev/null
@@ -1,160 +0,0 @@
-/*
- * $Id$
- *
- * Functions which allow the client to remember usernames and passwords for
- * various sites.
- *
- * Copyright (c) 1987-2009 by the citadel.org team
- *
- *  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 3 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; if not, write to the Free Software
- *  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
- */
-
-#include "sysdep.h"
-#include <stdlib.h>
-#include <unistd.h>
-#include <string.h>
-#include <ctype.h>
-#include <pwd.h>
-#include <sys/types.h>
-#include <sys/stat.h>
-#include <limits.h>
-#include <stdio.h>
-#include <libcitadel.h>
-#include "citadel.h"
-#include "citadel_ipc.h"
-#include "commands.h"
-#include "client_passwords.h"
-
-#define PWFILENAME "%s/.citadel.passwords"
-
-void determine_pwfilename(char *pwfile, size_t n) {
-       struct passwd *p;
-
-       p = getpwuid(getuid());
-       if (p == NULL) strcpy(pwfile, "");
-       snprintf(pwfile, n, PWFILENAME, p->pw_dir);
-}
-
-
-/*
- * Check the password file for a host/port match; if found, stuff the user
- * name and password into the user/pass buffers
- */
-void get_stored_password(
-               char *host,
-               char *port,
-               char *username,
-               char *password) {
-
-       char pwfile[PATH_MAX];
-       FILE *fp;
-       char buf[SIZ];
-       char buf64[SIZ];
-       char hostbuf[256], portbuf[256], ubuf[256], pbuf[256];
-
-       strcpy(username, "");
-       strcpy(password, "");
-
-       determine_pwfilename(pwfile, sizeof pwfile);
-       if (IsEmptyStr(pwfile)) return;
-
-       fp = fopen(pwfile, "r");
-       if (fp == NULL) return;
-       while (fgets(buf64, sizeof buf64, fp) != NULL) {
-               CtdlDecodeBase64(buf, buf64, sizeof(buf64));
-               extract_token(hostbuf, buf, 0, '|', sizeof hostbuf);
-               extract_token(portbuf, buf, 1, '|', sizeof portbuf);
-               extract_token(ubuf, buf, 2, '|', sizeof ubuf);
-               extract_token(pbuf, buf, 3, '|', sizeof pbuf);
-
-               if (!strcasecmp(hostbuf, host)) {
-                       if (!strcasecmp(portbuf, port)) {
-                               strcpy(username, ubuf);
-                               strcpy(password, pbuf);
-                       }
-               }
-       }
-       fclose(fp);
-}
-
-
-/*
- * Set (or clear) stored passwords.
- */
-void set_stored_password(
-               char *host,
-               char *port,
-               char *username,
-               char *password) {
-
-       char pwfile[PATH_MAX];
-       FILE *fp, *oldfp;
-       char buf[SIZ];
-       char buf64[SIZ];
-       char hostbuf[256], portbuf[256], ubuf[256], pbuf[256];
-
-       determine_pwfilename(pwfile, sizeof pwfile);
-       if (IsEmptyStr(pwfile)) return;
-
-       oldfp = fopen(pwfile, "r");
-       if (oldfp == NULL) oldfp = fopen("/dev/null", "r");
-       unlink(pwfile);
-       fp = fopen(pwfile, "w");
-       if (fp == NULL) fp = fopen("/dev/null", "w");
-       while (fgets(buf64, sizeof buf64, oldfp) != NULL) {
-               CtdlDecodeBase64(buf, buf64, sizeof(buf64));
-               extract_token(hostbuf, buf, 0, '|', sizeof hostbuf);
-               extract_token(portbuf, buf, 1, '|', sizeof portbuf);
-               extract_token(ubuf, buf, 2, '|', sizeof ubuf);
-               extract_token(pbuf, buf, 3, '|', sizeof pbuf);
-
-               if ( (strcasecmp(hostbuf, host)) 
-                  || (strcasecmp(portbuf, port)) ) {
-                       snprintf(buf, sizeof buf, "%s|%s|%s|%s|",
-                               hostbuf, portbuf, ubuf, pbuf);
-                       CtdlEncodeBase64(buf64, buf, strlen(buf), 0);
-                       fprintf(fp, "%s\n", buf64);
-               }
-       }
-       if (!IsEmptyStr(username)) {
-               snprintf(buf, sizeof buf, "%s|%s|%s|%s|",
-                       host, port, username, password);
-               CtdlEncodeBase64(buf64, buf, strlen(buf), 0);
-               fprintf(fp, "%s\n", buf64);
-       }
-       fclose(oldfp);
-       fclose(fp);
-       chmod(pwfile, 0600);
-}
-
-
-/*
- * Set the password if the user wants to, clear it otherwise 
- */
-void offer_to_remember_password(CtdlIPC *ipc,
-               char *host,
-               char *port,
-               char *username,
-               char *password) {
-
-       if (rc_remember_passwords) {
-               if (boolprompt("Remember username/password for this site", 0)) {
-                       set_stored_password(host, port, username, password);
-               }
-               else {
-                       set_stored_password(host, port, "", "");
-               }
-       }
-}
diff --git a/citadel/client_passwords.h b/citadel/client_passwords.h
deleted file mode 100644 (file)
index 6f6a454..0000000
+++ /dev/null
@@ -1,37 +0,0 @@
-/* 
- * $Id$
- *
- * Copyright (c) 1987-2009 by the citadel.org team
- *
- *  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 3 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; if not, write to the Free Software
- *  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
- */
-
-
-void determine_pwfilename(char *pwfile, size_t n);
-void get_stored_password(
-               char *host,
-               char *port,
-               char *username,
-               char *password);
-void set_stored_password(
-               char *host,
-               char *port,
-               char *username,
-               char *password);
-void offer_to_remember_password(CtdlIPC *ipc,
-               char *host,
-               char *port,
-               char *username,
-               char *password);
diff --git a/citadel/commands.c b/citadel/commands.c
deleted file mode 100644 (file)
index 427887c..0000000
+++ /dev/null
@@ -1,1712 +0,0 @@
-/*
- * $Id$
- *
- * This file contains functions which implement parts of the
- * text-mode user interface.
- *
- * Copyright (c) 1987-2009 by the citadel.org team
- *
- *  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 3 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; if not, write to the Free Software
- *  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
- */
-
-#include "sysdep.h"
-#include <stdlib.h>
-#include <unistd.h>
-#include <stdio.h>
-#include <fcntl.h>
-#include <ctype.h>
-#include <string.h>
-#include <sys/types.h>
-
-#if TIME_WITH_SYS_TIME
-# include <sys/time.h>
-# include <time.h>
-#else
-# if HAVE_SYS_TIME_H
-#  include <sys/time.h>
-# else
-#  include <time.h>
-# endif
-#endif
-
-#ifdef HAVE_TERMIOS_H
-#include <termios.h>
-#else
-#include <sgtty.h>
-#endif
-
-#ifdef HAVE_SYS_SELECT_H
-#include <sys/select.h>
-#endif
-
-#ifdef THREADED_CLIENT
-#include <pthread.h>
-#endif
-
-#include <signal.h>
-#include <errno.h>
-#include <stdarg.h>
-#include <libcitadel.h>
-#include "citadel.h"
-#include "citadel_ipc.h"
-#include "commands.h"
-#include "messages.h"
-#include "citadel_decls.h"
-#include "routines.h"
-#include "routines2.h"
-#include "rooms.h"
-#include "client_chat.h"
-#include "citadel_dirs.h"
-#ifndef HAVE_SNPRINTF
-#include "snprintf.h"
-#endif
-#include "screen.h"
-#include "ecrash.h"
-
-struct citcmd {
-       struct citcmd *next;
-       int c_cmdnum;
-       int c_axlevel;
-       char c_keys[5][64];
-};
-
-#define IFNEXPERT if ((userflags&US_EXPERT)==0)
-
-
-int rc_exp_beep;
-char rc_exp_cmd[1024];
-int rc_allow_attachments;
-int rc_display_message_numbers;
-int rc_force_mail_prompts;
-int rc_remember_passwords;
-int rc_ansi_color;
-int rc_color_use_bg;
-int rc_prompt_control = 0;
-time_t rc_idle_threshold = (time_t)900;
-char rc_url_cmd[SIZ];
-char rc_open_cmd[SIZ];
-char rc_gotmail_cmd[SIZ];
-
-char *gl_string;
-int next_lazy_cmd = 5;
-
-int lines_printed = 0;         /* line count for paginator */
-extern int screenwidth, screenheight;
-extern int termn8;
-extern CtdlIPC *ipc_for_signal_handlers;       /* KLUDGE cover your eyes */
-
-struct citcmd *cmdlist = NULL;
-
-
-/* these variables are local to this module */
-char keepalives_enabled = KA_YES;      /* send NOOPs to server when idle */
-int ok_to_interrupt = 0;               /* print instant msgs asynchronously */
-time_t AnsiDetect;                     /* when did we send the detect code? */
-int enable_color = 0;                  /* nonzero for ANSI color */
-
-
-
-
-/*
- * If an interesting key has been pressed, return its value, otherwise 0
- */
-char was_a_key_pressed(void) {
-       fd_set rfds;
-       struct timeval tv;
-       int the_character;
-       int retval;
-
-       FD_ZERO(&rfds);
-       FD_SET(0, &rfds);
-       tv.tv_sec = 0;
-       tv.tv_usec = 0;
-       retval = select(1, &rfds, NULL, NULL, &tv); 
-
-       /* Careful!  Disable keepalives during keyboard polling; we're probably
-        * in the middle of a data transfer from the server, in which case
-        * sending a NOOP would throw the client protocol out of sync.
-        */
-       if (FD_ISSET(0, &rfds)) {
-               set_keepalives(KA_NO);
-               the_character = inkey();
-               set_keepalives(KA_YES);
-       }
-       else {
-               the_character = 0;
-       }
-       return(the_character);
-}
-
-
-
-
-
-/*
- * Check to see if we need to pause at the end of a screen.
- * If we do, we have to switch to half keepalives during the pause because
- * we are probably in the middle of a server operation and the NOOP command
- * would confuse everything.
- */
-int checkpagin(int lp, unsigned int pagin, unsigned int height)
-{
-       int thekey;
-
-       if (sigcaught) return(lp);
-       thekey = was_a_key_pressed();
-       if (thekey == 'q' || thekey == 'Q' || thekey == 's' || thekey == 'S')
-               thekey = STOP_KEY;
-       if (thekey == 'n' || thekey == 'N')
-               thekey = NEXT_KEY;
-       if ( (thekey == NEXT_KEY) || (thekey == STOP_KEY)) sigcaught = thekey;
-       if (sigcaught) return(lp);
-
-       if (!pagin) return(0);
-       if (lp>=(height-1)) {
-               set_keepalives(KA_HALF);
-               hit_any_key(ipc_for_signal_handlers);   /* Cheating -IO */
-               set_keepalives(KA_YES);
-               return(0);
-       }
-       return(lp);
-}
-
-
-
-
-/*
- * pprintf()  ...   paginated version of printf()
- */
-void pprintf(const char *format, ...) {   
-        va_list arg_ptr;
-       static char buf[4096];  /* static for performance, change if needed */
-       int i;
-
-       /* If sigcaught is nonzero, a keypress has interrupted this and we
-        * should just drain output.
-        */
-       if (sigcaught) return;
-       /* Otherwise, start spewing... */ 
-        va_start(arg_ptr, format);   
-        vsnprintf(buf, sizeof(buf), format, arg_ptr);   
-        va_end(arg_ptr);   
-
-       for (i=0; !IsEmptyStr(&buf[i]); ++i) {
-               scr_putc(buf[i]);
-               if (buf[i]==10) {
-                       ++lines_printed;
-                       lines_printed = checkpagin(lines_printed,
-                               (userflags & US_PAGINATOR),
-                               screenheight);
-               }
-       }
-}   
-
-
-
-/*
- * print_instant()  -  print instant messages if there are any
- */
-void print_instant(void)
-{
-       char buf[1024];
-       FILE *outpipe;
-       time_t timestamp;
-       struct tm stamp;
-       int flags = 0;
-       char sender[64];
-       char node[64];
-       char *listing = NULL;
-       int r;                  /* IPC result code */
-
-       if (instant_msgs == 0)
-               return;
-
-       if (rc_exp_beep) {
-               ctdl_beep();
-       }
-       if (IsEmptyStr(rc_exp_cmd)) {
-               color(BRIGHT_RED);
-               scr_printf("\r---");
-       }
-       
-       while (instant_msgs != 0) {
-               r = CtdlIPCGetInstantMessage(ipc_for_signal_handlers, &listing, buf);
-               if (r / 100 != 1)
-                       return;
-       
-               instant_msgs = extract_int(buf, 0);
-               timestamp = extract_long(buf, 1);
-               flags = extract_int(buf, 2);
-               extract_token(sender, buf, 3, '|', sizeof sender);
-               extract_token(node, buf, 4, '|', sizeof node);
-               strcpy(last_paged, sender);
-       
-               localtime_r(&timestamp, &stamp);
-
-               /* If the page is a Logoff Request, honor it. */
-               if (flags & 2) {
-                       termn8 = 1;
-                       return;
-               }
-       
-               if (!IsEmptyStr(rc_exp_cmd)) {
-                       outpipe = popen(rc_exp_cmd, "w");
-                       if (outpipe != NULL) {
-                               /* Header derived from flags */
-                               if (flags & 2)
-                                       fprintf(outpipe,
-                                              "Please log off now, as requested ");
-                               else if (flags & 1)
-                                       fprintf(outpipe, "Broadcast message ");
-                               else if (flags & 4)
-                                       fprintf(outpipe, "Chat request ");
-                               else
-                                       fprintf(outpipe, "Message ");
-                               /* Timestamp.  Can this be improved? */
-                               if (stamp.tm_hour == 0 || stamp.tm_hour == 12)
-                                       fprintf(outpipe, "at 12:%02d%cm",
-                                               stamp.tm_min, 
-                                               stamp.tm_hour ? 'p' : 'a');
-                               else if (stamp.tm_hour > 12)            /* pm */
-                                       fprintf(outpipe, "at %d:%02dpm",
-                                               stamp.tm_hour - 12,
-                                               stamp.tm_min);
-                               else                                    /* am */
-                                       fprintf(outpipe, "at %d:%02dam",
-                                               stamp.tm_hour, stamp.tm_min);
-                               fprintf(outpipe, " from %s", sender);
-                               if (strncmp(ipc_for_signal_handlers->ServInfo.nodename, node, 32))
-                                       fprintf(outpipe, " @%s", node);
-                               fprintf(outpipe, ":\n%s\n", listing);
-                               pclose(outpipe);
-                               if (instant_msgs == 0)
-                                       return;
-                               continue;
-                       }
-               }
-               /* fall back to built-in instant message display */
-               scr_printf("\n");
-               lines_printed++;
-
-               /* Header derived from flags */
-               if (flags & 2)
-                       scr_printf("Please log off now, as requested ");
-               else if (flags & 1)
-                       scr_printf("Broadcast message ");
-               else if (flags & 4)
-                       scr_printf("Chat request ");
-               else
-                       scr_printf("Message ");
-       
-               /* Timestamp.  Can this be improved? */
-               if (stamp.tm_hour == 0 || stamp.tm_hour == 12)/* 12am/12pm */
-                       scr_printf("at 12:%02d%cm", stamp.tm_min, 
-                               stamp.tm_hour ? 'p' : 'a');
-               else if (stamp.tm_hour > 12)                    /* pm */
-                       scr_printf("at %d:%02dpm",
-                               stamp.tm_hour - 12, stamp.tm_min);
-               else                                            /* am */
-                       scr_printf("at %d:%02dam", stamp.tm_hour, stamp.tm_min);
-               
-               /* Sender */
-               scr_printf(" from %s", sender);
-       
-               /* Remote node, if any */
-               if (strncmp(ipc_for_signal_handlers->ServInfo.nodename, node, 32))
-                       scr_printf(" @%s", node);
-       
-               scr_printf(":\n");
-               lines_printed++;
-               fmout(screenwidth, NULL, listing, NULL, 1, screenheight, -1, 0);
-               free(listing);
-
-               /* when running in curses mode, the scroll bar in most
-                  xterm-style programs becomes useless, so it makes sense to
-                  pause after a screenful of pages if the user has been idle
-                  for a while. However, this is annoying to some of the users
-                  who aren't in curses mode and tend to leave their clients
-                  idle. keepalives become disabled, resulting in getting booted
-                  when coming back to the idle session. but they probably have
-                  a working scrollback in their terminal, so disable it in this
-                  case:
-                */
-               if (!is_curses_enabled())
-                       lines_printed = 0;
-       }
-       scr_printf("\n---\n");
-       color(BRIGHT_WHITE);
-
-
-}
-
-
-void set_keepalives(int s)
-{
-       keepalives_enabled = (char) s;
-}
-
-/* 
- * This loop handles the "keepalive" messages sent to the server when idling.
- */
-
-static time_t idlet = 0;
-static void really_do_keepalive(void) {
-       int r;                          /* IPC response code */
-
-       time(&idlet);
-
-       /* This may sometimes get called before we are actually connected
-        * to the server.  Don't do anything if we aren't connected. -IO
-        */
-       if (!ipc_for_signal_handlers)
-               return;
-
-       /* If full keepalives are enabled, send a NOOP to the server and
-        * wait for a response.
-        */
-       if (keepalives_enabled == KA_YES) {
-               r = CtdlIPCNoop(ipc_for_signal_handlers);
-               if (instant_msgs > 0) {
-                       if (ok_to_interrupt == 1) {
-                               scr_printf("\r%64s\r", "");
-                               print_instant();
-                               scr_printf("%s%c ", room_name,
-                                      room_prompt(room_flags));
-                               scr_flush();
-                       }
-               }
-       }
-
-       /* If half keepalives are enabled, send a QNOP to the server (if the
-        * server supports it) and then do nothing.
-        */
-       if ( (keepalives_enabled == KA_HALF)
-          && (ipc_for_signal_handlers->ServInfo.supports_qnop > 0) ) {
-               CtdlIPC_chat_send(ipc_for_signal_handlers, "QNOP");
-       }
-}
-
-/* threaded nonblocking keepalive stuff starts here. I'm going for a simple
-   encapsulated interface; in theory there should be no need to touch these
-   globals outside of the async_ka_* functions. */
-
-#ifdef THREADED_CLIENT
-static pthread_t ka_thr_handle;
-static int ka_thr_active = 0;
-static int async_ka_enabled = 0;
-
-static void *ka_thread(void *arg)
-{
-#ifdef HAVE_BACKTRACE
-       char threadName[256];
-
-       // Set up our name
-       sprintf(threadName, "ka_Thread n");
-
-       // Register for tracing
-       eCrash_RegisterThread(threadName, 0);
-#endif
-       really_do_keepalive();
-       pthread_detach(ka_thr_handle);
-       ka_thr_active = 0;
-       
-#ifdef HAVE_BACKTRACE
-       eCrash_UnregisterThread();
-#endif
-       return NULL;
-}
-
-/* start up a thread to handle a keepalive in the background */
-static void async_ka_exec(void)
-{
-       if (!ka_thr_active) {
-               ka_thr_active = 1;
-               if (pthread_create(&ka_thr_handle, NULL, ka_thread, NULL)) {
-                       perror("pthread_create");
-                       exit(1);
-               }
-       }
-}
-#endif /* THREADED_CLIENT */
-
-/* I changed this from static to not because I need to call it from
-   screen.c, either that or make something in screen.c not static.
-   Fix it how you like. Why all the staticness? stu */
-   
-void do_keepalive(void)
-{
-       time_t now;
-
-       time(&now);
-       if ((now - idlet) < ((long) S_KEEPALIVE))
-               return;
-
-       /* Do a space-backspace to keep telnet sessions from idling out */
-       scr_printf(" %c", 8);
-       scr_flush();
-
-#ifdef THREADED_CLIENT
-       if (async_ka_enabled)
-               async_ka_exec();
-       else
-#endif
-               really_do_keepalive();
-}
-
-
-/* Now the actual async-keepalve API that we expose to higher levels:
-   async_ka_start() and async_ka_end(). These do nothing when we don't have
-   threading enabled, so we avoid sprinkling ifdef's throughout the code. */
-
-/* wait for a background keepalive to complete. this must be done before
-   attempting any further server requests! */
-void async_ka_end(void)
-{
-#ifdef THREADED_CLIENT
-       if (ka_thr_active)
-               pthread_join(ka_thr_handle, NULL);
-
-       async_ka_enabled--;
-#endif
-}
-
-/* tell do_keepalive() that keepalives are asynchronous. */
-void async_ka_start(void)
-{
-#ifdef THREADED_CLIENT
-       async_ka_enabled++;
-#endif
-}
-
-
-int inkey(void)
-{                              /* get a character from the keyboard, with   */
-       int a;                  /* the watchdog timer in effect if necessary */
-       fd_set rfds;
-       struct timeval tv;
-       time_t start_time;
-
-       scr_flush();
-       lines_printed = 0;
-       time(&start_time);
-
-       do {
-               /* This loop waits for keyboard input.  If the keepalive
-                * timer expires, it sends a keepalive to the server if
-                * necessary and then waits again.
-                */
-               do {
-                       scr_set_windowsize(ipc_for_signal_handlers);
-                       do_keepalive();
-                       scr_set_windowsize(ipc_for_signal_handlers);
-
-                       FD_ZERO(&rfds);
-                       FD_SET(0, &rfds);
-                       tv.tv_sec = S_KEEPALIVE;
-                       tv.tv_usec = 0;
-
-                       select(1, &rfds, NULL, NULL, &tv);
-               } while (!FD_ISSET(0, &rfds));
-
-               /* At this point, there's input, so fetch it.
-                * (There's a hole in the bucket...)
-                */
-               a = scr_getc(SCR_BLOCK);
-               if (a == 127) {
-                       a = 8;
-               }
-               if (a == 13) {
-                       a = 10;
-               }
-/* not so fast there dude, we have to handle UTF-8 and ISO-8859-1...
-               if (a > 126) {
-                       a = 0;
-               }
-               if (((a != 23) && (a != 4) && (a != 10) && (a != 8) && (a != NEXT_KEY) && (a != STOP_KEY))
-                   && ((a < 32) || (a > 126))) {
-                       a = 0;
-               }
- */
-
-#ifndef DISABLE_CURSES
-#if defined(HAVE_CURSES_H) || defined(HAVE_NCURSES_H)
-               if (a == ERR) {
-                       logoff(NULL, 3);
-               }
-#endif
-#endif
-
-       } while (a == 0);
-       return (a);
-}
-
-
-int yesno(void)
-{                              /* Returns 1 for yes, 0 for no */
-       int a;
-       while (1) {
-               a = inkey();
-               a = tolower(a);
-               if (a == 'y') {
-                       scr_printf("Yes\n");
-                       return (1);
-               }
-               if (a == 'n') {
-                       scr_printf("No\n");
-                       return (0);
-               }
-       }
-}
-
-/* Returns 1 for yes, 0 for no, arg is default value */
-int yesno_d(int d)
-{
-       int a;
-       while (1) {
-               a = inkey();
-               a = tolower(a);
-               if (a == 10)
-                       a = (d ? 'y' : 'n');
-               if (a == 'y') {
-                       scr_printf("Yes\n");
-                       return (1);
-               }
-               if (a == 'n') {
-                       scr_printf("No\n");
-                       return (0);
-               }
-       }
-}
-
-
-
-
-/* Gets a line from the terminal */
-/* string == Pointer to string buffer */
-/* lim == Maximum length - if negative, no-show */
-void ctdl_getline(char *string, int lim) 
-{
-       int a, b;
-       char flag = 0;
-
-       if (lim < 0) {
-               lim = (0 - lim);
-               flag = 1;
-       }
-       strcpy(string, "");
-       gl_string = string;
-       async_ka_start();
-
-GLA:   a = inkey();
-       /* a = (a & 127); ** commented out because it isn't just an ASCII world anymore */
-       if ((a == 8 || a == 23) && (IsEmptyStr(string)))
-               goto GLA;
-       if ((a != 10) && (a != 8) && (strlen(string) == lim))
-               goto GLA;
-       if ((a == 8) && (string[0] != 0)) {
-               string[strlen(string) - 1] = 0;
-               scr_putc(8); scr_putc(32); scr_putc(8);
-               goto GLA;
-       }
-       if ((a == 23) && (string[0] != 0)) {
-               do {
-                       string[strlen(string) - 1] = 0;
-                       scr_putc(8); scr_putc(32); scr_putc(8);
-               } while (!IsEmptyStr(string) && string[strlen(string) - 1] != ' ');
-               goto GLA;
-       }
-       if ((a == 10)) {
-               scr_putc(10);
-               async_ka_end();
-               return;
-       }
-       if (a < 32)
-               a = '.';
-       b = strlen(string);
-       string[b] = a;
-       string[b + 1] = 0;
-       if (flag == 0)
-               scr_putc(a);
-       if (flag == 1)
-               scr_putc('*');
-       goto GLA;
-}
-
-
-/*
- * strprompt()  -  prompt for a string, print the existing value and
- *                 allow the user to press return to keep it...
- */
-void strprompt(char *prompt, char *str, int len)
-{
-       int i;
-       char buf[128];
-
-       print_instant();
-       color(DIM_WHITE);
-       scr_printf("%s ", prompt);
-       color(DIM_MAGENTA);
-       scr_printf("[");
-       color(BRIGHT_MAGENTA);
-
-       if (len >= 0) {
-               scr_printf("%s", str);
-       }
-       else {
-               for (i=0; !IsEmptyStr(&str[i]); ++i) {
-                       scr_printf("*");
-               }
-       }
-
-       color(DIM_MAGENTA);
-       scr_printf("]");
-       color(DIM_WHITE);
-       scr_printf(": ");
-       color(BRIGHT_CYAN);
-       ctdl_getline(buf, len);
-       if (buf[0] != 0)
-               strcpy(str, buf);
-       color(DIM_WHITE);
-}
-
-/*
- * boolprompt()  -  prompt for a yes/no, print the existing value and
- *                  allow the user to press return to keep it...
- */
-int boolprompt(char *prompt, int prev_val)
-{
-       int r;
-
-       color(DIM_WHITE);
-       scr_printf("%s ", prompt);
-       color(DIM_MAGENTA);
-       scr_printf("[");
-       color(BRIGHT_MAGENTA);
-       scr_printf("%s", (prev_val ? "Yes" : "No"));
-       color(DIM_MAGENTA);
-       scr_printf("]: ");
-       color(BRIGHT_CYAN);
-       r = (yesno_d(prev_val));
-       color(DIM_WHITE);
-       return r;
-}
-
-/* 
- * intprompt()  -  like strprompt(), except for an integer
- *                 (note that it RETURNS the new value!)
- */
-int intprompt(char *prompt, int ival, int imin, int imax)
-{
-       char buf[16];
-       int i;
-       int p;
-
-       do {
-               i = ival;
-               snprintf(buf, sizeof buf, "%d", i);
-               strprompt(prompt, buf, 15);
-               i = atoi(buf);
-               for (p=0; !IsEmptyStr(&buf[p]); ++p) {
-                       if ( (!isdigit(buf[p]))
-                          && ( (buf[p]!='-') || (p!=0) )  )
-                               i = imin - 1;
-               }
-               if (i < imin)
-                       scr_printf("*** Must be no less than %d.\n", imin);
-               if (i > imax)
-                       scr_printf("*** Must be no more than %d.\n", imax);
-       } while ((i < imin) || (i > imax));
-       return (i);
-}
-
-/* 
- * newprompt()  -  prompt for a string with no existing value
- *                 (clears out string buffer first)
- */
-void newprompt(char *prompt, char *str, int len)
-{
-       color(BRIGHT_MAGENTA);
-       scr_printf("%s", prompt);
-       color(DIM_MAGENTA);
-       ctdl_getline(str, len);
-       color(DIM_WHITE);
-}
-
-
-int lkey(void)
-{                              /* returns a lower case value */
-       int a;
-       a = inkey();
-       if (isupper(a))
-               a = tolower(a);
-       return (a);
-}
-
-/*
- * parse the citadel.rc file
- */
-void load_command_set(void)
-{
-       FILE *ccfile;
-       char buf[1024];
-       char editor_key[100];
-       struct citcmd *cptr;
-       struct citcmd *lastcmd = NULL;
-       int a, d;
-       int b = 0;
-       int i;
-
-
-       /* first, set up some defaults for non-required variables */
-
-       for (i = 0; i < MAX_EDITORS; i++)
-               strcpy(editor_paths[i], "");
-       strcpy(printcmd, "");
-       strcpy(imagecmd, "");
-       strcpy(rc_username, "");
-       strcpy(rc_password, "");
-       rc_floor_mode = 0;
-       rc_exp_beep = 1;
-       rc_allow_attachments = 0;
-       rc_remember_passwords = 0;
-       strcpy(rc_exp_cmd, "");
-       rc_display_message_numbers = 0;
-       rc_force_mail_prompts = 0;
-       rc_ansi_color = 0;
-       rc_color_use_bg = 0;
-       strcpy(rc_url_cmd, "");
-       strcpy(rc_open_cmd, "");
-       strcpy(rc_gotmail_cmd, "");
-#ifdef HAVE_OPENSSL
-       rc_encrypt = RC_DEFAULT;
-#endif
-#if defined(HAVE_CURSES_H) && !defined(DISABLE_CURSES)
-       rc_screen = RC_DEFAULT;
-#endif
-       rc_alt_semantics = 0;
-
-       /* now try to open the citadel.rc file */
-
-       ccfile = NULL;
-       if (getenv("HOME") != NULL) {
-               snprintf(buf, sizeof buf, "%s/.citadelrc", getenv("HOME"));
-               ccfile = fopen(buf, "r");
-       }
-       if (ccfile == NULL) {
-               ccfile = fopen(file_citadel_rc, "r");
-       }
-       if (ccfile == NULL) {
-               ccfile = fopen("/etc/citadel.rc", "r");
-       }
-       if (ccfile == NULL) {
-               ccfile = fopen("./citadel.rc", "r");
-       }
-       if (ccfile == NULL) {
-               perror("commands: cannot open citadel.rc");
-               logoff(NULL, 3);
-       }
-       while (fgets(buf, sizeof buf, ccfile) != NULL) {
-               while ((!IsEmptyStr(buf)) ? (isspace(buf[strlen(buf) - 1])) : 0)
-                       buf[strlen(buf) - 1] = 0;
-
-               if (!strncasecmp(buf, "encrypt=", 8)) {
-                       if (!strcasecmp(&buf[8], "yes")) {
-#ifdef HAVE_OPENSSL
-                               rc_encrypt = RC_YES;
-#else
-                               fprintf(stderr, "citadel.rc requires encryption support but citadel is not compiled with OpenSSL");
-                               logoff(NULL, 3);
-#endif
-                       }
-#ifdef HAVE_OPENSSL
-                       else if (!strcasecmp(&buf[8], "no")) {
-                               rc_encrypt = RC_NO;
-                       }
-                       else if (!strcasecmp(&buf[8], "default")) {
-                               rc_encrypt = RC_DEFAULT;
-                       }
-#endif
-               }
-
-#if defined(HAVE_CURSES_H) && !defined(DISABLE_CURSES)
-               if (!strncasecmp(buf, "fullscreen=", 11)) {
-                       if (!strcasecmp(&buf[11], "yes"))
-                               rc_screen = RC_YES;
-                       else if (!strcasecmp(&buf[11], "no"))
-                               rc_screen = RC_NO;
-               }
-#endif
-
-               if (!strncasecmp(buf, "editor=", 7))
-                       strcpy(editor_paths[0], &buf[7]);
-
-               for (i = 0; i < MAX_EDITORS; i++)
-               {
-                       sprintf(editor_key, "editor%d=", i);
-                       if (!strncasecmp(buf, editor_key, strlen(editor_key)))
-                               strcpy(editor_paths[i], &buf[strlen(editor_key)]);
-               }
-
-               if (!strncasecmp(buf, "printcmd=", 9))
-                       strcpy(printcmd, &buf[9]);
-
-               if (!strncasecmp(buf, "imagecmd=", 9))
-                       strcpy(imagecmd, &buf[9]);
-
-               if (!strncasecmp(buf, "expcmd=", 7))
-                       strcpy(rc_exp_cmd, &buf[7]);
-
-               if (!strncasecmp(buf, "local_screen_dimensions=", 24))
-                       have_xterm = (char) atoi(&buf[24]);
-
-               if (!strncasecmp(buf, "use_floors=", 11)) {
-                       if (!strcasecmp(&buf[11], "yes"))
-                               rc_floor_mode = RC_YES;
-                       if (!strcasecmp(&buf[11], "no"))
-                               rc_floor_mode = RC_NO;
-                       if (!strcasecmp(&buf[11], "default"))
-                               rc_floor_mode = RC_DEFAULT;
-               }
-               if (!strncasecmp(buf, "beep=", 5)) {
-                       rc_exp_beep = atoi(&buf[5]);
-               }
-               if (!strncasecmp(buf, "allow_attachments=", 18)) {
-                       rc_allow_attachments = atoi(&buf[18]);
-               }
-               if (!strncasecmp(buf, "idle_threshold=", 15)) {
-                       rc_idle_threshold = atol(&buf[15]);
-               }
-               if (!strncasecmp(buf, "remember_passwords=", 19)) {
-                       rc_remember_passwords = atoi(&buf[19]);
-               }
-               if (!strncasecmp(buf, "display_message_numbers=", 24)) {
-                       rc_display_message_numbers = atoi(&buf[24]);
-               }
-               if (!strncasecmp(buf, "force_mail_prompts=", 19)) {
-                       rc_force_mail_prompts = atoi(&buf[19]);
-               }
-               if (!strncasecmp(buf, "ansi_color=", 11)) {
-                       if (!strncasecmp(&buf[11], "on", 2))
-                               rc_ansi_color = 1;
-                       if (!strncasecmp(&buf[11], "auto", 4))
-                               rc_ansi_color = 2;      /* autodetect */
-                       if (!strncasecmp(&buf[11], "user", 4))
-                               rc_ansi_color = 3;      /* user config */
-               }
-               if (!strncasecmp(buf, "use_background=", 15)) {
-                       if (!strncasecmp(&buf[15], "on", 2))
-                               rc_color_use_bg = 9;
-               }
-               if (!strncasecmp(buf, "prompt_control=", 15)) {
-                       if (!strncasecmp(&buf[15], "on", 2))
-                               rc_prompt_control = 1;
-                       if (!strncasecmp(&buf[15], "user", 4))
-                               rc_prompt_control = 3;  /* user config */
-               }
-               if (!strncasecmp(buf, "username=", 9))
-                       strcpy(rc_username, &buf[9]);
-
-               if (!strncasecmp(buf, "password=", 9))
-                       strcpy(rc_password, &buf[9]);
-
-               if (!strncasecmp(buf, "urlcmd=", 7))
-                       strcpy(rc_url_cmd, &buf[7]);
-
-               if (!strncasecmp(buf, "opencmd=", 7))
-                       strcpy(rc_open_cmd, &buf[8]);
-
-               if (!strncasecmp(buf, "gotmailcmd=", 11))
-                       strcpy(rc_gotmail_cmd, &buf[11]);
-
-               if (!strncasecmp(buf, "alternate_semantics=", 20)) {
-                       if (!strncasecmp(&buf[20], "yes", 3)) {
-                               rc_alt_semantics = 1;
-                       }
-                       else {
-                               rc_alt_semantics = 0;
-                       }
-               }
-
-               if (!strncasecmp(buf, "cmd=", 4)) {
-                       strcpy(buf, &buf[4]);
-
-                       cptr = (struct citcmd *) malloc(sizeof(struct citcmd));
-
-                       cptr->c_cmdnum = atoi(buf);
-                       for (d = strlen(buf); d >= 0; --d)
-                               if (buf[d] == ',')
-                                       b = d;
-                       strcpy(buf, &buf[b + 1]);
-
-                       cptr->c_axlevel = atoi(buf);
-                       for (d = strlen(buf); d >= 0; --d)
-                               if (buf[d] == ',')
-                                       b = d;
-                       strcpy(buf, &buf[b + 1]);
-
-                       for (a = 0; a < 5; ++a)
-                               cptr->c_keys[a][0] = 0;
-
-                       a = 0;
-                       b = 0;
-                       buf[strlen(buf) + 1] = 0;
-                       while (!IsEmptyStr(buf)) {
-                               b = strlen(buf);
-                               for (d = strlen(buf); d >= 0; --d)
-                                       if (buf[d] == ',')
-                                               b = d;
-                               strncpy(cptr->c_keys[a], buf, b);
-                               cptr->c_keys[a][b] = 0;
-                               if (buf[b] == ',')
-                                       strcpy(buf, &buf[b + 1]);
-                               else
-                                       strcpy(buf, "");
-                               ++a;
-                       }
-
-                       cptr->next = NULL;
-                       if (cmdlist == NULL)
-                               cmdlist = cptr;
-                       else
-                               lastcmd->next = cptr;
-                       lastcmd = cptr;
-               }
-       }
-       fclose(ccfile);
-}
-
-
-
-/*
- * return the key associated with a command
- */
-char keycmd(char *cmdstr)
-{
-       int a;
-
-       for (a = 0; !IsEmptyStr(&cmdstr[a]); ++a)
-               if (cmdstr[a] == '&')
-                       return (tolower(cmdstr[a + 1]));
-       return (0);
-}
-
-
-/*
- * Output the string from a key command without the ampersand
- * "mode" should be set to 0 for normal or 1 for <C>ommand key highlighting
- */
-char *cmd_expand(char *strbuf, int mode)
-{
-       int a;
-       static char exp[64];
-       char buf[1024];
-
-       strcpy(exp, strbuf);
-
-       for (a = 0; exp[a]; ++a) {
-               if (strbuf[a] == '&') {
-
-                       /* dont echo these non mnemonic command keys */
-                       int noecho = strbuf[a+1] == '<' || strbuf[a+1] == '>' || strbuf[a+1] == '+' || strbuf[a+1] == '-';
-
-                       if (mode == 0) {
-                               strcpy(&exp[a], &exp[a + 1 + noecho]);
-                       }
-                       if (mode == 1) {
-                               exp[a] = '<';
-                               strcpy(buf, &exp[a + 2]);
-                               exp[a + 2] = '>';
-                               exp[a + 3] = 0;
-                               strcat(exp, buf);
-                       }
-               }
-               if (!strncmp(&exp[a], "^r", 2)) {
-                       strcpy(buf, exp);
-                       strcpy(&exp[a], room_name);
-                       strcat(exp, &buf[a + 2]);
-               }
-               if (!strncmp(&exp[a], "^c", 2)) {
-                       exp[a] = ',';
-                       strcpy(&exp[a + 1], &exp[a + 2]);
-               }
-       }
-
-       return (exp);
-}
-
-
-
-/*
- * Comparison function to determine if entered commands match a
- * command loaded from the config file.
- */
-int cmdmatch(char *cmdbuf, struct citcmd *cptr, int ncomp)
-{
-       int a;
-       int cmdax;
-
-       cmdax = 0;
-       if (is_room_aide)
-               cmdax = 1;
-       if (axlevel >= 6)
-               cmdax = 2;
-
-       for (a = 0; a < ncomp; ++a) {
-               if ((tolower(cmdbuf[a]) != keycmd(cptr->c_keys[a]))
-                   || (cptr->c_axlevel > cmdax))
-                       return (0);
-       }
-       return (1);
-}
-
-
-/*
- * This function returns 1 if a given command requires a string input
- */
-int requires_string(struct citcmd *cptr, int ncomp)
-{
-       int a;
-       char buf[64];
-
-       strcpy(buf, cptr->c_keys[ncomp - 1]);
-       for (a = 0; !IsEmptyStr(&buf[a]); ++a) {
-               if (buf[a] == ':')
-                       return (1);
-       }
-       return (0);
-}
-
-
-/*
- * Input a command at the main prompt.
- * This function returns an integer command number.  If the command prompts
- * for a string then it is placed in the supplied buffer.
- */
-int getcmd(CtdlIPC *ipc, char *argbuf)
-{
-       char cmdbuf[5];
-       int cmdspaces[5];
-       int cmdpos;
-       int ch;
-       int a;
-       int got;
-       int this_lazy_cmd;
-       struct citcmd *cptr;
-
-       /*
-        * Starting a new command now, so set sigcaught to 0.  This variable
-        * is set to nonzero (usually NEXT_KEY or STOP_KEY) if a command has
-        * been interrupted by a keypress.
-        */
-       sigcaught = 0;
-
-       /* Switch color support on or off if we're in user mode */
-       if (rc_ansi_color == 3) {
-               if (userflags & US_COLOR)
-                       enable_color = 1;
-               else
-                       enable_color = 0;
-       }
-       /* if we're running in idiot mode, display a cute little menu */
-       IFNEXPERT formout(ipc, "mainmenu");
-
-       print_instant();
-       strcpy(argbuf, "");
-       cmdpos = 0;
-       for (a = 0; a < 5; ++a)
-               cmdbuf[a] = 0;
-       /* now the room prompt... */
-       ok_to_interrupt = 1;
-       color(BRIGHT_WHITE);
-       scr_printf("\n%s", room_name);
-       color(DIM_WHITE);
-       scr_printf("%c ", room_prompt(room_flags));
-       scr_flush();
-
-       while (1) {
-               ch = inkey();
-               ok_to_interrupt = 0;
-
-               /* Handle the backspace key, but only if there's something
-                * to backspace over...
-                */
-               if ((ch == 8) && (cmdpos > 0)) {
-                       back(cmdspaces[cmdpos - 1] + 1);
-                       cmdbuf[cmdpos] = 0;
-                       --cmdpos;
-               }
-               /* Spacebar invokes "lazy traversal" commands */
-               if ((ch == 32) && (cmdpos == 0)) {
-                       this_lazy_cmd = next_lazy_cmd;
-                       if (this_lazy_cmd == 13)
-                               next_lazy_cmd = 5;
-                       if (this_lazy_cmd == 5)
-                               next_lazy_cmd = 13;
-                       for (cptr = cmdlist; cptr != NULL; cptr = cptr->next) {
-                               if (cptr->c_cmdnum == this_lazy_cmd) {
-                                       for (a = 0; a < 5; ++a)
-                                               if (cptr->c_keys[a][0] != 0)
-                                                       scr_printf("%s ", cmd_expand(
-                                                                                       cptr->c_keys[a], 0));
-                                       scr_printf("\n");
-                                       return (this_lazy_cmd);
-                               }
-                       }
-                       scr_printf("\n");
-                       return (this_lazy_cmd);
-               }
-               /* Otherwise, process the command */
-               cmdbuf[cmdpos] = tolower(ch);
-
-               for (cptr = cmdlist; cptr != NULL; cptr = cptr->next) {
-                       if (cmdmatch(cmdbuf, cptr, cmdpos + 1)) {
-
-                               scr_printf("%s", cmd_expand(cptr->c_keys[cmdpos], 0));
-                               cmdspaces[cmdpos] = strlen(
-                                   cmd_expand(cptr->c_keys[cmdpos], 0));
-                               if (cmdpos < 4)
-                                       if ((cptr->c_keys[cmdpos + 1]) != 0)
-                                               scr_putc(' ');
-                               ++cmdpos;
-                       }
-               }
-
-               for (cptr = cmdlist; cptr != NULL; cptr = cptr->next) {
-                       if (cmdmatch(cmdbuf, cptr, 5)) {
-                               /* We've found our command. */
-                               if (requires_string(cptr, cmdpos)) {
-                                       ctdl_getline(argbuf, 64);
-                               } else {
-                                       scr_printf("\n");
-                               }
-
-                               /* If this command is one that changes rooms,
-                                * then the next lazy-command (space bar)
-                                * should be "read new" instead of "goto"
-                                */
-                               if ((cptr->c_cmdnum == 5)
-                                   || (cptr->c_cmdnum == 6)
-                                   || (cptr->c_cmdnum == 47)
-                                   || (cptr->c_cmdnum == 52)
-                                   || (cptr->c_cmdnum == 16)
-                                   || (cptr->c_cmdnum == 20))
-                                       next_lazy_cmd = 13;
-
-                               /* If this command is "read new"
-                                * then the next lazy-command (space bar)
-                                * should be "goto"
-                                */
-                               if (cptr->c_cmdnum == 13)
-                                       next_lazy_cmd = 5;
-
-                               return (cptr->c_cmdnum);
-
-                       }
-               }
-
-               if (ch == '?') {
-                       pprintf("\rOne of ...                         \n");
-                       for (cptr = cmdlist; cptr != NULL; cptr = cptr->next) {
-                               if (cmdmatch(cmdbuf, cptr, cmdpos)) {
-                                       for (a = 0; a < 5; ++a) {
-                                          keyopt(cmd_expand(cptr->c_keys[a], 1));
-                                  pprintf(" ");
-                                       }
-                                       pprintf("\n");
-                               }
-                       }
-               sigcaught = 0;
-
-                       pprintf("\n%s%c ", room_name, room_prompt(room_flags));
-                       got = 0;
-                       for (cptr = cmdlist; cptr != NULL; cptr = cptr->next) {
-                               if ((got == 0) && (cmdmatch(cmdbuf, cptr, cmdpos))) {
-                                       for (a = 0; a < cmdpos; ++a) {
-                                               pprintf("%s ",
-                                                      cmd_expand(cptr->c_keys[a], 0));
-                                       }
-                                       got = 1;
-                               }
-                       }
-               }
-       }
-
-}
-
-
-
-
-
-/*
- * set tty modes.  commands are:
- * 
- * 01- set to Citadel mode
- * 2 - save current settings for later restoral
- * 3 - restore saved settings
- */
-#ifdef HAVE_TERMIOS_H
-void stty_ctdl(int cmd)
-{                              /* SysV version of stty_ctdl() */
-       struct termios live;
-       static struct termios saved_settings;
-       static int last_cmd = 0;
-
-       if (cmd == SB_LAST)
-               cmd = last_cmd;
-       else
-               last_cmd = cmd;
-
-       if ((cmd == 0) || (cmd == 1)) {
-               tcgetattr(0, &live);
-               live.c_iflag = ISTRIP | IXON | IXANY;
-               live.c_oflag = OPOST | ONLCR;
-               live.c_lflag = ISIG | NOFLSH;
-
-               live.c_cc[VINTR] = 0;
-               live.c_cc[VQUIT] = 0;
-
-#ifdef hpux
-               live.c_cc[VMIN] = 0;
-               live.c_cc[VTIME] = 0;
-#endif
-
-               /* do we even need this stuff anymore? */
-               /* live.c_line=0; */
-               live.c_cc[VERASE] = 8;
-               live.c_cc[VKILL] = 24;
-               live.c_cc[VEOF] = 1;
-               live.c_cc[VEOL] = 255;
-               live.c_cc[VEOL2] = 0;
-               live.c_cc[VSTART] = 0;
-               tcsetattr(0, TCSADRAIN, &live);
-       }
-       if (cmd == 2) {
-               tcgetattr(0, &saved_settings);
-       }
-       if (cmd == 3) {
-               tcsetattr(0, TCSADRAIN, &saved_settings);
-       }
-
-}
-#else
-void stty_ctdl(int cmd)
-{                              /* BSD version of stty_ctdl() */
-       struct sgttyb live;
-       static struct sgttyb saved_settings;
-       static int last_cmd = 0;
-
-       if (cmd == SB_LAST)
-               cmd = last_cmd;
-       else
-               last_cmd = cmd;
-
-       if ((cmd == 0) || (cmd == 1)) {
-               gtty(0, &live);
-               live.sg_flags |= CBREAK;
-               live.sg_flags |= CRMOD;
-               live.sg_flags |= NL1;
-               live.sg_flags &= ~ECHO;
-               if (cmd == 1)
-                       live.sg_flags |= NOFLSH;
-               stty(0, &live);
-       }
-       if (cmd == 2) {
-               gtty(0, &saved_settings);
-       }
-       if (cmd == 3) {
-               stty(0, &saved_settings);
-       }
-}
-#endif
-
-
-/*
- * display_help()  -  help file viewer
- */
-void display_help(CtdlIPC *ipc, char *name)
-{
-       formout(ipc, name);
-}
-
-
-/*
- * fmout() - Citadel text formatter and paginator
- */
-int fmout(
-       int width,      /* screen width to use */
-       FILE *fpin,     /* file to read from, or NULL to format given text */
-       char *text,     /* text to be formatted (when fpin is NULL */
-       FILE *fpout,    /* file to write to, or NULL to write to screen */
-       char pagin,     /* nonzero if we should use the paginator */
-       int height,     /* screen height to use */
-       int starting_lp,/* starting value for lines_printed, -1 for global */
-       int subst)      /* nonzero if we should use hypertext mode */
-{
-       char *buffer = NULL;    /* The current message */
-       char *word = NULL;      /* What we are about to actually print */
-       char *e;                /* Pointer to position in text */
-       char old = 0;           /* The previous character */
-       int column = 0;         /* Current column */
-       size_t i;               /* Generic counter */
-
-       /* Space for a single word, which can be at most screenwidth */
-       word = (char *)calloc(1, width);
-       if (!word) {
-               err_printf("Can't alloc memory to print message: %s!\n",
-                               strerror(errno));
-               logoff(NULL, 3);
-       }
-
-       /* Read the entire message body into memory */
-       if (fpin) {
-               buffer = load_message_from_file(fpin);
-               if (!buffer) {
-                       err_printf("Can't print message: %s!\n",
-                                       strerror(errno));
-                       logoff(NULL, 3);
-               }
-       } else {
-               buffer = text;
-       }
-       e = buffer;
-
-       if (starting_lp >= 0)
-               lines_printed = starting_lp;
-
-       /* Run the message body */
-       while (*e) {
-               /* Catch characters that shouldn't be there at all */
-               if (*e == '\r') {
-                       e++;
-                       continue;
-               }
-               /* First, are we looking at a newline? */
-               if (*e == '\n') {
-                       e++;
-                       if (*e == ' ') {        /* Paragraph */
-                               if (fpout) {
-                                       fprintf(fpout, "\n");
-                               } else {
-                                       scr_printf("\n");
-                                       ++lines_printed;
-                                       lines_printed = checkpagin(lines_printed, pagin, height);
-                               }
-                               column = 0;
-                       } else if (old != ' ') {/* Don't print two spaces */
-                               if (fpout) {
-                                       fprintf(fpout, " ");
-                               } else {
-                                       scr_printf(" ");
-                               }
-                               column++;
-                       }
-                       old = '\n';
-                       continue;
-               }
-
-               /* Are we looking at a nonprintable?
-                * (This section is now commented out because we could be displaying
-                * a character set like UTF-8 or ISO-8859-1.)
-               if ( (*e < 32) || (*e > 126) ) {
-                       e++;
-                       continue;
-               } */
-
-               /* Or are we looking at a space? */
-               if (*e == ' ') {
-                       e++;
-                       if (column >= width - 1) {
-                               /* Are we in the rightmost column? */
-                               if (fpout) {
-                                       fprintf(fpout, "\n");
-                               } else {
-                                       scr_printf("\n");
-                                       ++lines_printed;
-                                       lines_printed = checkpagin(lines_printed, pagin, height);
-                               }
-                               column = 0;
-                       } else if (!(column == 0 && old == ' ')) {
-                               /* Eat only the first space on a line */
-                               if (fpout) {
-                                       fprintf(fpout, " ");
-                               } else {
-                                       scr_printf(" ");
-                               }
-                               column++;
-                       }
-                       /* ONLY eat the FIRST space on a line */
-                       old = ' ';
-                       continue;
-               }
-               old = *e;
-
-               /* Read a word, slightly messy */
-               i = 0;
-               while (e[i]) {
-                       if (!isprint(e[i]) && !isspace(e[i]))
-                               e[i] = ' ';
-                       if (isspace(e[i]))
-                               break;
-                       i++;
-               }
-
-               /* We should never see these, but... slightly messy */
-               if (e[i] == '\t' || e[i] == '\f' || e[i] == '\v')
-                       e[i] = ' ';
-
-               /* Break up really long words */
-               /* TODO: auto-hyphenation someday? */
-               if (i >= width) 
-                       i = width - 1;
-               strncpy(word, e, i);
-               word[i] = 0;
-
-               /* Decide where to print the word */
-               if (column + i >= width) {
-                       /* Wrap to the next line */
-                       if (fpout) {
-                               fprintf(fpout, "\n");
-                       } else {
-                               scr_printf("\n");
-                               ++lines_printed;
-                               lines_printed = checkpagin(lines_printed, pagin, height);
-                       }
-                       column = 0;
-               }
-
-               /* Print the word */
-               if (fpout) {
-                       fprintf(fpout, "%s", word);
-               } else {
-                       scr_printf("%s", word);
-               }
-               column += i;
-               e += i;         /* Start over with the whitepsace! */
-       }
-
-       free(word);
-       if (fpin)               /* We allocated this, remember? */
-               free(buffer);
-
-       /* Is this necessary?  It makes the output kind of spacey. */
-       if (fpout) {
-               fprintf(fpout, "\n");
-       } else {
-               scr_printf("\n");
-               ++lines_printed;
-               lines_printed = checkpagin(lines_printed, pagin, height);
-       }
-
-       return sigcaught;
-}
-
-
-/*
- * support ANSI color if defined
- */
-void color(int colornum)
-{
-       static int hold_color;
-       static int current_color;
-
-       if (colornum == COLOR_PUSH) {
-               hold_color = current_color;
-               return;
-       }
-
-       if (colornum == COLOR_POP) {
-               color(hold_color);
-               return;
-       }
-
-       current_color = colornum;
-       if (enable_color) {
-#if defined(HAVE_CURSES_H) && !defined(DISABLE_CURSES)
-               if (scr_color(colornum))
-                       return;
-#endif
-               /* When switching to dim white, actually output an 'original
-                * pair' sequence -- this looks better on black-on-white
-                * terminals. - Changed to ORIGINAL_PAIR as this actually
-                * wound up looking horrible on black-on-white terminals, not
-                * to mention transparent terminals.
-                */
-               if (colornum == ORIGINAL_PAIR)
-                       printf("\033[0;39;49m");
-               else
-                       printf("\033[%d;3%d;4%dm", 
-                                       (colornum & 8) ? 1 : 0,
-                                       (colornum & 7),
-                                       rc_color_use_bg);
-
-               scr_flush();
-       }
-}
-
-void cls(int colornum)
-{
-       if (enable_color) {
-               printf("\033[4%dm\033[2J\033[H\033[0m",
-                               colornum ? colornum : rc_color_use_bg);
-               scr_flush();
-       }
-}
-
-
-/*
- * Detect whether ANSI color is available (answerback)
- */
-void send_ansi_detect(void)
-{
-       if (rc_ansi_color == 2) {
-               printf("\033[c");
-               scr_flush();
-               time(&AnsiDetect);
-       }
-}
-
-void look_for_ansi(void)
-{
-       fd_set rfds;
-       struct timeval tv;
-       char abuf[512];
-       time_t now;
-       int a, rv;
-
-       if (rc_ansi_color == 0) {
-               enable_color = 0;
-       } else if (rc_ansi_color == 1) {
-               enable_color = 1;
-       } else if (rc_ansi_color == 2) {
-
-               /* otherwise, do the auto-detect */
-
-               strcpy(abuf, "");
-
-               time(&now);
-               if ((now - AnsiDetect) < 2)
-                       sleep(1);
-
-               do {
-                       FD_ZERO(&rfds);
-                       FD_SET(0, &rfds);
-                       tv.tv_sec = 0;
-                       tv.tv_usec = 1;
-
-                       select(1, &rfds, NULL, NULL, &tv);
-                       if (FD_ISSET(0, &rfds)) {
-                               abuf[strlen(abuf) + 1] = 0;
-                               rv = read(0, &abuf[strlen(abuf)], 1);
-                       }
-               } while (FD_ISSET(0, &rfds));
-
-               for (a = 0; !IsEmptyStr(&abuf[a]); ++a) {
-                       if ((abuf[a] == 27) && (abuf[a + 1] == '[')
-                           && (abuf[a + 2] == '?')) {
-                               enable_color = 1;
-                       }
-               }
-       }
-}
-
-
-/*
- * Display key options (highlight hotkeys inside angle brackets)
- */
-void keyopt(char *buf) {
-       int i;
-
-       color(DIM_WHITE);
-       for (i=0; !IsEmptyStr(&buf[i]); ++i) {
-               if (buf[i]=='<') {
-                       pprintf("%c", buf[i]);
-                       color(BRIGHT_MAGENTA);
-               } else {
-                       if (buf[i]=='>'&& buf[i+1] != '>') {
-                               color(DIM_WHITE);
-                       }
-                       pprintf("%c", buf[i]);
-               }
-       }
-       color(DIM_WHITE);
-}
-
-
-
-/*
- * Present a key-menu line choice type of thing
- */
-char keymenu(char *menuprompt, char *menustring) {
-       int i, c, a;
-       int choices;
-       int do_prompt = 0;
-       char buf[1024];
-       int ch;
-       int display_prompt = 1;
-
-       choices = num_tokens(menustring, '|');
-
-       if (menuprompt != NULL) do_prompt = 1;
-       if ((menuprompt != NULL) && (IsEmptyStr(menuprompt))) do_prompt = 0;
-
-       while (1) {
-               if (display_prompt) {
-                       if (do_prompt) {
-                               scr_printf("%s ", menuprompt);
-                       } 
-                       else {
-                               for (i=0; i<choices; ++i) {
-                                       extract_token(buf, menustring, i, '|', sizeof buf);
-                                       keyopt(buf);
-                                       scr_printf(" ");
-                               }
-                       }
-                       scr_printf("-> ");
-                       display_prompt = 0;
-               }
-               ch = lkey();
-       
-               if ( (do_prompt) && (ch=='?') ) {
-                       scr_printf("\rOne of...                               ");
-                       scr_printf("                                      \n");
-                       for (i=0; i<choices; ++i) {
-                               extract_token(buf, menustring, i, '|', sizeof buf);
-                               scr_printf("   ");
-                               keyopt(buf);
-                               scr_printf("\n");
-                       }
-                       scr_printf("\n");
-                       display_prompt = 1;
-               }
-
-               for (i=0; i<choices; ++i) {
-                       extract_token(buf, menustring, i, '|', sizeof buf);
-                       for (c=1; !IsEmptyStr(&buf[c]); ++c) {
-                               if ( (ch == tolower(buf[c]))
-                                  && (buf[c-1]=='<')
-                                  && (buf[c+1]=='>') ) {
-                                       for (a=0; !IsEmptyStr(&buf[a]); ++a) {
-                                               if ( (a!=(c-1)) && (a!=(c+1))) {
-                                                       scr_putc(buf[a]);
-                                               }
-                                       }
-                                       scr_printf("\n");
-                                       return ch;
-                               }
-                       }
-               }
-       }
-}
diff --git a/citadel/commands.h b/citadel/commands.h
deleted file mode 100644 (file)
index 5f12b23..0000000
+++ /dev/null
@@ -1,85 +0,0 @@
-/*
- * $Id$
- *
- * Copyright (c) 1987-2009 by the citadel.org team
- *
- *  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 3 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; if not, write to the Free Software
- *  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
- */
-
-/*
- * Colors for color() command
- */
-#define DIM_BLACK      0
-#define DIM_RED                1
-#define DIM_GREEN      2
-#define DIM_YELLOW     3
-#define DIM_BLUE       4
-#define DIM_MAGENTA    5
-#define DIM_CYAN       6
-#define DIM_WHITE      7
-#define BRIGHT_BLACK   8
-#define BRIGHT_RED     9
-#define BRIGHT_GREEN   10
-#define BRIGHT_YELLOW  11
-#define BRIGHT_BLUE    12
-#define BRIGHT_MAGENTA 13
-#define BRIGHT_CYAN    14
-#define BRIGHT_WHITE   15
-#define COLOR_PUSH     16      /* Save current color */
-#define COLOR_POP      17      /* Restore saved color */
-#define ORIGINAL_PAIR  -1      /* Default terminal colors */
-
-/*
- * declarations
- */
-void load_command_set(void);
-void stty_ctdl(int cmd);
-void newprompt(char *prompt, char *str, int len);
-void strprompt(char *prompt, char *str, int len);
-int boolprompt(char *prompt, int prev_val);
-int intprompt(char *prompt, int ival, int imin, int imax);
-int fmout(int width, FILE *fpin, char *text, FILE *fpout, char pagin,
-               int height, int starting_lp, int subst);
-int getcmd(CtdlIPC *ipc, char *argbuf);
-void display_help(CtdlIPC *ipc, char *name);
-void color(int colornum);
-void cls(int colornum);
-void send_ansi_detect(void);
-void look_for_ansi(void);
-int inkey(void);
-void set_keepalives(int s);
-extern int enable_color;
-int yesno(void);
-int yesno_d(int d);
-void keyopt(char *);
-char keymenu(char *menuprompt, char *menustring);
-void async_ka_start(void);
-void async_ka_end(void);
-int checkpagin(int lp, unsigned int pagin, unsigned int height);
-char was_a_key_pressed(void);
-
-#ifdef __GNUC__
-void pprintf(const char *format, ...) __attribute__((__format__(__printf__,1,2)));
-#else
-void pprintf(const char *format, ...);
-#endif
-
-
-
-extern char rc_url_cmd[SIZ];
-extern char rc_open_cmd[SIZ];
-extern char rc_gotmail_cmd[SIZ];
-extern int lines_printed;
-extern int rc_remember_passwords;
diff --git a/citadel/ctdlmigrate.c b/citadel/ctdlmigrate.c
deleted file mode 100644 (file)
index 65f06c6..0000000
+++ /dev/null
@@ -1,314 +0,0 @@
-/*
- * $Id$
- *
- * Across-the-wire migration utility for Citadel
- *
- * Yes, we used goto, and gets(), and committed all sorts of other heinous sins here.
- * The scope of this program isn't wide enough to make a difference.  If you don't like
- * it you can rewrite it.
- *
- * Copyright (c) 2009 citadel.org
- *
- *  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 3 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; if not, write to the Free Software
- *  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
- *
- * (Note: a useful future enhancement might be to support "-h" on both sides)
- *
- */
-
-#include <stdlib.h>
-#include <unistd.h>
-#include <stdio.h>
-#include <string.h>
-#include <ctype.h>
-#include <fcntl.h>
-#include <sys/types.h>
-#include <sys/stat.h>
-#include <sys/utsname.h>
-#include <sys/wait.h>
-#include <signal.h>
-#include <netdb.h>
-#include <errno.h>
-#include <limits.h>
-#include <pwd.h>
-#include <time.h>
-#include <libcitadel.h>
-#include "citadel.h"
-#include "axdefs.h"
-#include "sysdep.h"
-#include "config.h"
-#include "citadel_dirs.h"
-#if HAVE_BACKTRACE
-#include <execinfo.h>
-#endif
-
-
-
-/*
- * Replacement for gets() that doesn't throw a compiler warning.
- * We're only using it for some simple prompts, so we don't need
- * to worry about attackers exploiting it.
- */
-void getz(char *buf) {
-       char *ptr;
-
-       ptr = fgets(buf, 32767, stdin);
-       if (!ptr) {
-               buf[0] = 0;
-               return;
-       }
-
-       ptr = strchr(buf, '\n');
-       if (ptr) *ptr = 0;
-}
-
-
-
-
-
-int main(int argc, char *argv[])
-{
-       int relh=0;
-       int home=0;
-       char relhome[PATH_MAX]="";
-       char ctdldir[PATH_MAX]=CTDLDIR;
-       char yesno[5];
-       char sendcommand[PATH_MAX];
-       int cmdexit;
-       char cmd[PATH_MAX];
-       char buf[PATH_MAX];
-       char socket_path[PATH_MAX];
-       char remote_user[256];
-       char remote_host[256];
-       char remote_sendcommand[PATH_MAX];
-       FILE *sourcefp = NULL;
-       FILE *targetfp = NULL;
-       int linecount = 0;
-       char spinning[4] = "-\\|/" ;
-       int exitcode = 0;
-       
-       calc_dirs_n_files(relh, home, relhome, ctdldir, 0);
-       CtdlMakeTempFileName(socket_path, sizeof socket_path);
-
-       cmdexit = system("clear");
-       printf( "-------------------------------------------\n"
-               "Over-the-wire migration utility for Citadel\n"
-               "-------------------------------------------\n"
-               "\n"
-               "This utility is designed to migrate your Citadel installation\n"
-               "to a new host system via a network connection, without disturbing\n"
-               "the source system.  The target may be a different CPU architecture\n"
-               "and/or operating system.  The source system should be running\n"
-               "Citadel %d.%02d or newer, and the target system should be running\n"
-               "either the same version or a newer version.  You will also need\n"
-               "the 'rsync' utility, and OpenSSH v4 or newer.\n"
-               "\n"
-               "You must run this utility on the TARGET SYSTEM.  Any existing data\n"
-               "on this system will be ERASED.\n"
-               "\n"
-               "Do you wish to continue? "
-               ,
-               EXPORT_REV_MIN / 100,
-               EXPORT_REV_MIN % 100
-       );
-
-       if ((fgets(yesno, sizeof yesno, stdin) == NULL) || (tolower(yesno[0]) != 'y')) {
-               exit(0);
-       }
-
-       printf("\n\nGreat!  First we will check some things out here on our target\n"
-               "system to make sure it is ready to receive data.\n\n");
-
-       printf("Locating 'sendcommand' and checking connectivity to Citadel...\n");
-       snprintf(sendcommand, sizeof sendcommand, "%s/sendcommand", ctdl_utilbin_dir);
-       snprintf(cmd, sizeof cmd, "%s 'NOOP'", sendcommand);
-       cmdexit = system(cmd);
-       if (cmdexit != 0) {
-               printf("\nctdlmigrate was unable to attach to the Citadel server\n"
-                       "here on the target system.  Is Citadel running?\n\n");
-               exit(1);
-       }
-       printf("\nOK, this side is ready to go.  Now we must connect to the source system.\n\n");
-
-       printf("Enter the host name or IP address of the source system\n"
-               "(example: ctdl.foo.org)\n"
-               "--> ");
-       getz(remote_host);
-       printf("\nEnter the name of a user on %s who has full access to Citadel files\n"
-               "(usually root)\n--> ",
-               remote_host);
-       getz(remote_user);
-
-       printf("\nEstablishing an SSH connection to the source system...\n\n");
-       unlink(socket_path);
-       snprintf(cmd, sizeof cmd, "ssh -MNf -S %s %s@%s", socket_path, remote_user, remote_host);
-       cmdexit = system(cmd);
-       printf("\n");
-       if (cmdexit != 0) {
-               printf("This program was unable to establish an SSH session to the source system.\n\n");
-               exitcode = cmdexit;
-               goto THEEND;
-       }
-
-       printf("\nTesting a command over the connection...\n\n");
-       snprintf(cmd, sizeof cmd, "ssh -S %s %s@%s 'echo Remote commands are executing successfully.'",
-               socket_path, remote_user, remote_host);
-       cmdexit = system(cmd);
-       printf("\n");
-       if (cmdexit != 0) {
-               printf("Remote commands are not succeeding.\n\n");
-               exitcode = cmdexit;
-               goto THEEND;
-       }
-
-       printf("\nLocating the remote 'sendcommand' and Citadel installation...\n");
-       snprintf(remote_sendcommand, sizeof remote_sendcommand, "/usr/local/citadel/sendcommand");
-       snprintf(cmd, sizeof cmd, "ssh -S %s %s@%s %s NOOP",
-               socket_path, remote_user, remote_host, remote_sendcommand);
-       cmdexit = system(cmd);
-       if (cmdexit != 0) {
-               snprintf(remote_sendcommand, sizeof remote_sendcommand, "/usr/sbin/sendcommand");
-               snprintf(cmd, sizeof cmd, "ssh -S %s %s@%s %s NOOP",
-                       socket_path, remote_user, remote_host, remote_sendcommand);
-               cmdexit = system(cmd);
-       }
-       if (cmdexit != 0) {
-               printf("\nUnable to locate Citadel programs on the remote system.  Please enter\n"
-                       "the name of the directory on %s which contains the 'sendcommand' program.\n"
-                       "(example: /opt/foo/citadel)\n"
-                       "--> ", remote_host);
-               getz(buf);
-               snprintf(remote_sendcommand, sizeof remote_sendcommand, "%s/sendcommand", buf);
-               snprintf(cmd, sizeof cmd, "ssh -S %s %s@%s %s NOOP",
-                       socket_path, remote_user, remote_host, remote_sendcommand);
-               cmdexit = system(cmd);
-       }
-       printf("\n");
-       if (cmdexit != 0) {
-               printf("ctdlmigrate was unable to attach to the remote Citadel system.\n\n");
-               exitcode = cmdexit;
-               goto THEEND;
-       }
-
-       printf("ctdlmigrate will now begin a database migration...\n");
-
-       snprintf(cmd, sizeof cmd, "ssh -S %s %s@%s %s -w3600 MIGR export",
-               socket_path, remote_user, remote_host, remote_sendcommand);
-       sourcefp = popen(cmd, "r");
-       if (!sourcefp) {
-               printf("\n%s\n\n", strerror(errno));
-               exitcode = 2;
-               goto THEEND;
-       }
-
-       snprintf(cmd, sizeof cmd, "%s -w3600 MIGR import", sendcommand);
-       targetfp = popen(cmd, "w");
-       if (!targetfp) {
-               printf("\n%s\n\n", strerror(errno));
-               exitcode = 3;
-               goto THEEND;
-       }
-
-       while (fgets(buf, sizeof buf, sourcefp) != NULL) {
-               if (fwrite(buf, strlen(buf), 1, targetfp) < 1) {
-                       exitcode = 4;
-                       printf("%s\n", strerror(errno));
-                       goto FAIL;
-               }
-               ++linecount;
-               if ((linecount % 100) == 0) {
-                       printf("%c\r", spinning[((linecount / 100) % 4)]);
-                       fflush(stdout);
-               }
-       }
-
-FAIL:  if (sourcefp) pclose(sourcefp);
-       if (targetfp) pclose(targetfp);
-       if (exitcode != 0) goto THEEND;
-
-       /* We need to copy a bunch of other stuff, and will do so using rsync */
-
-       snprintf(cmd, sizeof cmd, "ssh -S %s %s@%s %s MIGR listdirs",
-               socket_path, remote_user, remote_host, remote_sendcommand);
-       sourcefp = popen(cmd, "r");
-       if (!sourcefp) {
-               printf("\n%s\n\n", strerror(errno));
-               exitcode = 2;
-               goto THEEND;
-       }
-       while ((fgets(buf, sizeof buf, sourcefp)) && (strcmp(buf, "000"))) {
-               buf[strlen(buf)-1] = 0;
-
-               if (!strncasecmp(buf, "bio|", 4)) {
-                       snprintf(cmd, sizeof cmd, "rsync -va --rsh='ssh -S %s' %s@%s:%s/ %s/",
-                               socket_path, remote_user, remote_host, &buf[4], ctdl_bio_dir);
-               }
-               else if (!strncasecmp(buf, "files|", 6)) {
-                       snprintf(cmd, sizeof cmd, "rsync -va --rsh='ssh -S %s' %s@%s:%s/ %s/",
-                               socket_path, remote_user, remote_host, &buf[6], ctdl_file_dir);
-               }
-               else if (!strncasecmp(buf, "userpics|", 9)) {
-                       snprintf(cmd, sizeof cmd, "rsync -va --rsh='ssh -S %s' %s@%s:%s/ %s/",
-                               socket_path, remote_user, remote_host, &buf[9], ctdl_usrpic_dir);
-               }
-               else if (!strncasecmp(buf, "messages|", 9)) {
-                       snprintf(cmd, sizeof cmd, "rsync -va --rsh='ssh -S %s' %s@%s:%s/ %s/",
-                               socket_path, remote_user, remote_host, &buf[9], ctdl_message_dir);
-               }
-               else if (!strncasecmp(buf, "netconfigs|", 11)) {
-                       snprintf(cmd, sizeof cmd, "rsync -va --rsh='ssh -S %s' %s@%s:%s/ %s/",
-                               socket_path, remote_user, remote_host, &buf[11], ctdl_netcfg_dir);
-               }
-               else if (!strncasecmp(buf, "keys|", 5)) {
-                       snprintf(cmd, sizeof cmd, "rsync -va --rsh='ssh -S %s' %s@%s:%s/ %s/",
-                               socket_path, remote_user, remote_host, &buf[5], ctdl_key_dir);
-               }
-               else if (!strncasecmp(buf, "images|", 7)) {
-                       snprintf(cmd, sizeof cmd, "rsync -va --rsh='ssh -S %s' %s@%s:%s/ %s/",
-                               socket_path, remote_user, remote_host, &buf[7], ctdl_image_dir);
-               }
-               else if (!strncasecmp(buf, "info|", 5)) {
-                       snprintf(cmd, sizeof cmd, "rsync -va --rsh='ssh -S %s' %s@%s:%s/ %s/",
-                               socket_path, remote_user, remote_host, &buf[5], ctdl_info_dir);
-               }
-               else {
-                       strcpy(cmd, "false");   /* cheap and sleazy way to throw an error */
-               }
-               printf("%s\n", cmd);
-               cmdexit = system(cmd);
-               if (cmdexit != 0) {
-                       exitcode += cmdexit;
-               }
-       }
-       pclose(sourcefp);
-
-THEEND:        if (exitcode == 0) {
-               printf("\n\n *** Citadel migration was successful! *** \n\n");
-       }
-       else {
-               printf("\n\n *** Citadel migration was unsuccessful. *** \n\n");
-       }
-       printf("\nShutting down the socket connection...\n\n");
-       snprintf(cmd, sizeof cmd, "ssh -S %s -N -O exit %s@%s",
-               socket_path, remote_user, remote_host);
-       cmdexit = system(cmd);
-       printf("\n");
-       if (cmdexit != 0) {
-               printf("There was an error shutting down the socket.\n\n");
-               exitcode = cmdexit;
-       }
-
-       unlink(socket_path);
-       exit(exitcode);
-}
diff --git a/citadel/getmail.c b/citadel/getmail.c
deleted file mode 100644 (file)
index 0138b01..0000000
+++ /dev/null
@@ -1,422 +0,0 @@
-/*
- * $Id$
- *
- * Command-line utility to transmit a server command.
- *
- * Copyright (c) 1987-2009 by the citadel.org team
- *
- *  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 3 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; if not, write to the Free Software
- *  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
- */
-
-#include <stdlib.h>
-#include <unistd.h>
-#include <sys/types.h>
-#include <sys/wait.h>
-#include <string.h>
-#include <fcntl.h>
-#include <stdio.h>
-#include <ctype.h>
-
-#if TIME_WITH_SYS_TIME
-# include <sys/time.h>
-# include <time.h>
-#else
-# if HAVE_SYS_TIME_H
-#  include <sys/time.h>
-# else
-#  include <time.h>
-# endif
-#endif
-
-#include <signal.h>
-#include <errno.h>
-#include <limits.h>
-#include <libcitadel.h>
-#include "citadel.h"
-#include "citadel_ipc.h"
-#include "server.h"
-#include "config.h"
-
-#define LOCKFILE "/tmp/LCK.sendcommand"
-
-static CtdlIPC *ipc = NULL;
-
-/*
- * make sure only one copy of sendcommand runs at a time, using lock files
- */
-int set_lockfile(void)
-{
-       FILE *lfp;
-       int onppid;
-       int rv;
-
-       if ((lfp = fopen(LOCKFILE, "r")) != NULL) {
-               rv = fscanf(lfp, "%d", &onppid);
-               fclose(lfp);
-               if (!kill(onppid, 0) || errno == EPERM)
-                       return 1;
-       }
-       lfp = fopen(LOCKFILE, "w");
-       fprintf(lfp, "%ld\n", (long) getpid());
-       fclose(lfp);
-       return (0);
-}
-
-void remove_lockfile(void)
-{
-       unlink(LOCKFILE);
-}
-
-/*
- * Why both cleanup() and nq_cleanup() ?  Notice the alarm() call in
- * cleanup() .  If for some reason sendcommand hangs waiting for the server
- * to clean up, the alarm clock goes off and the program exits anyway.
- * The cleanup() routine makes a check to ensure it's not reentering, in
- * case the ipc module looped it somehow.
- */
-void nq_cleanup(int e)
-{
-       remove_lockfile();
-       exit(e);
-}
-
-/*
- * send binary to server
- */
-void serv_write(CtdlIPC *ipc, const char *buf, unsigned int nbytes)
-{
-       unsigned int bytes_written = 0;
-       int retval;
-/*
-#if defined(HAVE_OPENSSL)
-       if (ipc->ssl) {
-               serv_write_ssl(ipc, buf, nbytes);
-               return;
-       }
-#endif
-*/
-       while (bytes_written < nbytes) {
-               retval = write(ipc->sock, &buf[bytes_written],
-                              nbytes - bytes_written);
-               if (retval < 1) {
-                       connection_died(ipc, 0);
-                       return;
-               }
-               bytes_written += retval;
-       }
-}
-
-
-void cleanup(int e)
-{
-       static int nested = 0;
-
-       alarm(30);
-       signal(SIGALRM, nq_cleanup);
-       serv_write(ipc, "\n", 1);
-       if (nested++ < 1)
-               CtdlIPCQuit(ipc);
-       nq_cleanup(e);
-}
-
-/*
- * This is implemented as a function rather than as a macro because the
- * client-side IPC modules expect logoff() to be defined.  They call logoff()
- * when a problem connecting or staying connected to the server occurs.
- */
-void logoff(int e)
-{
-       cleanup(e);
-}
-
-static char *args[] =
-{"getmail", NULL};
-
-/*
- * Connect sendcommand to the Citadel server running on this computer.
- */
-void np_attach_to_server(char *host, char *port)
-{
-       char buf[SIZ];
-       char hostbuf[256] = "";
-       char portbuf[256] = "";
-       int r;
-
-       fprintf(stderr, "Attaching to server...\n");
-       strncpy(hostbuf, host, 256);
-       strncpy(portbuf, port, 256);
-       ipc = CtdlIPC_new(1, args, hostbuf, portbuf);
-       if (!ipc) {
-               fprintf(stderr, "Can't connect: %s\n", strerror(errno));
-               exit(3);
-       }
-       CtdlIPC_chat_recv(ipc, buf);
-       fprintf(stderr, "%s\n", &buf[4]);
-       snprintf(buf, sizeof buf, "IPGM %d", config.c_ipgm_secret);
-       r = CtdlIPCInternalProgram(ipc, config.c_ipgm_secret, buf);
-       fprintf(stderr, "%s\n", buf);
-       if (r / 100 != 2) {
-               cleanup(2);
-       }
-}
-
-
-void sendcommand_die(void) {
-       exit(0);
-}
-
-
-/*
- * saves filelen bytes from file at pathname
- */
-int save_buffer(void *file, size_t filelen, const char *pathname)
-{
-       size_t block = 0;
-       size_t bytes_written = 0;
-       FILE *fp;
-
-       fp = fopen(pathname, "w");
-       if (!fp) {
-               fprintf(stderr, "Cannot open '%s': %s\n", pathname, strerror(errno));
-               return 0;
-       }
-       do {
-               block = fwrite((char *)file + bytes_written, 1,
-                               filelen - bytes_written, fp);
-               bytes_written += block;
-       } while (errno == EINTR && bytes_written < filelen);
-       fclose(fp);
-
-       if (bytes_written < filelen) {
-               fprintf(stderr,"Trouble saving '%s': %s\n", pathname,
-                               strerror(errno));
-               return 0;
-       }
-       return 1;
-}
-
-
-/*
- * main
- */
-int main(int argc, char **argv)
-{
-       int a, r, i;
-       char cmd[5][SIZ];
-       char buf[SIZ];
-       int MessageToRetrieve;
-       int MessageFound = 0;
-       int relh=0;
-       int home=0;
-       int n=0;
-       char relhome[PATH_MAX]="";
-       char ctdldir[PATH_MAX]=CTDLDIR;
-       fd_set read_fd;
-       struct timeval tv;
-       int ret, err;
-       int server_shutting_down = 0;
-       struct ctdlipcroom *Room;
-       struct ctdlipcmessage *mret;
-       char cret[SIZ];
-       unsigned long *msgarr;
-       struct parts *att;
-
-       strcpy(ctdl_home_directory, DEFAULT_PORT);
-
-       /*
-        * Change directories if specified
-        */
-       for (a = 1; a < argc && n < 5; ++a) {
-               if (!strncmp(argv[a], "-h", 2)) {
-                       relh=argv[a][2]!='/';
-                       if (!relh) safestrncpy(ctdl_home_directory, &argv[a][2],
-                                                                  sizeof ctdl_home_directory);
-                       else
-                               safestrncpy(relhome, &argv[a][2],
-                                                       sizeof relhome);
-                       home=1;
-               } else {
-
-                       strcpy(cmd[n++], argv[a]);
-               }
-       }
-
-       calc_dirs_n_files(relh, home, relhome, ctdldir, 0);
-       get_config();
-
-       signal(SIGINT, cleanup);
-       signal(SIGQUIT, cleanup);
-       signal(SIGHUP, cleanup);
-       signal(SIGTERM, cleanup);
-
-       fprintf(stderr, "getmail: started (pid=%d) "
-                       "running in %s\n",
-                       (int) getpid(),
-                       ctdl_home_directory);
-       fflush(stderr);
-
-//     alarm(5);
-//     signal(SIGALRM, nq_cleanup); /* Set up a watchdog type timer in case we hang */
-       
-       np_attach_to_server(UDS, ctdl_run_dir);
-       fflush(stderr);
-       setIPCDeathHook(sendcommand_die);
-
-       fprintf(stderr, "GOTO %s\n", cmd[0]);
-       CtdlIPCGotoRoom(ipc, cmd[0], "", &Room, cret);
-       fprintf(stderr, "%s\n", cret);
-
-       MessageToRetrieve = atol(cmd[1]);
-
-       r = CtdlIPCGetMessages(ipc, 0, 0, NULL, &msgarr, buf);
-       printf("Messages: ");
-       for (i = 0; msgarr[i] > 0 ; i ++)
-       {
-//             printf(" %ld ", msgarr[i]);
-               if (msgarr[i] == MessageToRetrieve)
-                       MessageFound = 1;
-       }
-       if (!MessageFound)
-               printf("Message %d not found in the above list.", MessageToRetrieve);
-       printf("\n");
-
-       CtdlIPCGetSingleMessage(ipc,  MessageToRetrieve,0,4, &mret, cret);
-       fprintf(stderr, "%s\n", cret);
-       fprintf(stderr, "%s: %s\n", "path", mret->path);
-       fprintf(stderr, "%s: %s\n", "author", mret->author);
-       fprintf(stderr, "%s: %s\n", "subject", mret->subject);
-       fprintf(stderr, "%s: %s\n", "email", mret->email);
-       fprintf(stderr, "%s: %s\n", "text", mret->text);
-
-       att = mret->attachments;
-
-       while (att != NULL){
-               void *attachment;
-               char tmp[PATH_MAX];
-               char buf[SIZ];
-
-               fprintf(stderr, "Attachment: [%s] %s\n", att->number, att->filename);
-               r = CtdlIPCAttachmentDownload(ipc, MessageToRetrieve, att->number, &attachment, NULL, buf);
-               printf("----\%s\n----\n", buf);
-               if (r / 100 != 2) {
-                       printf("%s\n", buf);
-               } else {
-                       size_t len;
-                       
-                       len = (size_t)extract_long(buf, 0);
-                       CtdlMakeTempFileName(tmp, sizeof tmp);
-                       strcat(tmp, att->filename);
-                       printf("Saving Attachment to %s", tmp);
-                       save_buffer(attachment, len, tmp);
-                       free(attachment);
-               }
-               att = att->next;
-
-       }
-
-       ///if (
-
-
-       CtdlIPCQuit(ipc);
-       exit (1);
-
-
-
-
-
-
-       CtdlIPC_chat_send(ipc, cmd[4]);
-       CtdlIPC_chat_recv(ipc, buf);
-       fprintf(stderr, "%s\n", buf);
-
-       tv.tv_sec = 0;
-       tv.tv_usec = 1000;
-
-       if (!strncasecmp(&buf[1], "31", 2)) {
-               server_shutting_down = 1;
-       }
-
-       if (buf[0] == '1') {
-               while (CtdlIPC_chat_recv(ipc, buf), strcmp(buf, "000")) {
-                       printf("%s\n", buf);
-                       alarm(5); /* Kick the watchdog timer */
-               }
-       } else if (buf[0] == '4') {
-               do {
-                       if (fgets(buf, sizeof buf, stdin) == NULL)
-                               strcpy(buf, "000");
-                       if (!IsEmptyStr(buf))
-                               if (buf[strlen(buf) - 1] == '\n')
-                                       buf[strlen(buf) - 1] = 0;
-                       if (!IsEmptyStr(buf))
-                               if (buf[strlen(buf) - 1] == '\r')
-                                       buf[strlen(buf) - 1] = 0;
-                       if (strcmp(buf, "000"))
-                               CtdlIPC_chat_send(ipc, buf);
-                       
-                       FD_ZERO(&read_fd);
-                       FD_SET(ipc->sock, &read_fd);
-                       ret = select(ipc->sock+1, &read_fd, NULL, NULL,  &tv);
-                       err = errno;
-                       if (err!=0)
-                               printf("select failed: %d", err);
-
-                       if (ret == -1) {
-                               if (!(errno == EINTR || errno == EAGAIN))
-                                       printf("select failed: %d", err);
-                               return 1;
-                       }
-
-                       if (ret != 0) {
-                               size_t n;
-                               char rbuf[SIZ];
-
-                               rbuf[0] = '\0';
-                               n = read(ipc->sock, rbuf, SIZ);
-                               if (n>0) {
-                                       rbuf[n]='\0';
-                                       fprintf(stderr, "%s", rbuf);
-                                       fflush(stdout);
-                               }
-                       }
-                       alarm(5); /* Kick the watchdog timer */
-               } while (strcmp(buf, "000"));
-               CtdlIPC_chat_send(ipc, "\n");
-               CtdlIPC_chat_send(ipc, "000");
-       }
-       alarm(0);       /* Shutdown the watchdog timer */
-       fprintf(stderr, "sendcommand: processing ended.\n");
-
-       /* Clean up and log off ... unless the server indicated that the command
-        * we sent is shutting it down, in which case we want to just cut the
-        * connection and exit.
-        */
-       if (server_shutting_down) {
-               nq_cleanup(0);
-       }
-       else {
-               cleanup(0);
-       }
-       return 0;
-}
-
-
-/*
- * Stub function
- */
-void stty_ctdl(int cmd) {
-}
-
-
diff --git a/citadel/include/citadel_dirs.h b/citadel/include/citadel_dirs.h
new file mode 100644 (file)
index 0000000..1586ba6
--- /dev/null
@@ -0,0 +1,65 @@
+#ifndef __CITADEL_DIRS_H
+#define __CITADEL_DIRS_H
+
+#include <limits.h>
+
+
+extern char ctdl_home_directory[PATH_MAX];
+
+
+/* all our directories */
+extern char ctdl_bio_dir[PATH_MAX];
+extern char ctdl_bb_dir[PATH_MAX];
+extern char ctdl_data_dir[PATH_MAX];
+extern char ctdl_dspam_dir[PATH_MAX];
+extern char ctdl_file_dir[PATH_MAX];
+extern char ctdl_hlp_dir[PATH_MAX];
+extern char ctdl_shared_dir[PATH_MAX];
+extern char ctdl_image_dir[PATH_MAX];
+extern char ctdl_info_dir[PATH_MAX];
+extern char ctdl_key_dir[PATH_MAX];
+extern char ctdl_message_dir[PATH_MAX];
+extern char ctdl_usrpic_dir[PATH_MAX];
+extern char ctdl_etc_dir[PATH_MAX];
+extern char ctdl_autoetc_dir[PATH_MAX];
+extern char ctdl_run_dir[PATH_MAX];
+extern char ctdl_spool_dir[PATH_MAX];
+extern char ctdl_netout_dir[PATH_MAX];
+extern char ctdl_netin_dir[PATH_MAX];
+extern char ctdl_netcfg_dir[PATH_MAX];
+extern char ctdl_bbsbase_dir[PATH_MAX];
+extern char ctdl_sbin_dir[PATH_MAX];
+extern char ctdl_bin_dir[PATH_MAX];
+extern char ctdl_utilbin_dir[PATH_MAX];
+
+
+
+/* some of the frequently used files */
+extern char file_citadel_control[PATH_MAX];
+extern char file_citadel_rc[PATH_MAX];
+extern char file_citadel_config[PATH_MAX];
+extern char file_lmtp_socket[PATH_MAX];
+extern char file_lmtp_unfiltered_socket[PATH_MAX];
+extern char file_arcq[PATH_MAX];
+extern char file_citadel_socket[PATH_MAX];
+extern char file_mail_aliases[PATH_MAX];
+extern char file_pid_file[PATH_MAX];
+extern char file_pid_paniclog[PATH_MAX];
+extern char file_crpt_file_key[PATH_MAX];
+extern char file_crpt_file_csr[PATH_MAX];
+extern char file_crpt_file_cer[PATH_MAX];
+extern char file_chkpwd[PATH_MAX];
+extern char file_base64[PATH_MAX];
+extern char file_guesstimezone[PATH_MAX];
+extern char file_dpsam_conf[PATH_MAX];
+extern char file_dspam_log[PATH_MAX];
+
+extern char file_funambol_msg[PATH_MAX];
+
+extern void calc_dirs_n_files(int relh, int home, const char *relhome, char  *ctdldir, int dbg);
+
+
+void assoc_file_name(char *buf, size_t n,
+                    struct ctdlroom *qrbuf, const char *prefix);
+
+#endif /* __CITADEL_DIRS_H */
diff --git a/citadel/include/citadel_ipc.h b/citadel/include/citadel_ipc.h
new file mode 100644 (file)
index 0000000..01b0636
--- /dev/null
@@ -0,0 +1,371 @@
+/* $Id$ */
+
+#define        UDS                     "_UDS_"
+#ifdef __CYGWIN__
+#define DEFAULT_HOST           "localhost"
+#else
+#define DEFAULT_HOST           UDS
+#endif
+#define DEFAULT_PORT           "citadel"
+
+#include "sysdep.h"
+#ifdef HAVE_PTHREAD_H
+#include <pthread.h>
+#endif
+#ifdef HAVE_OPENSSL
+#include <openssl/ssl.h>
+#include <openssl/err.h>
+#include <openssl/rand.h>
+#endif
+
+#include "server.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/* Quick and dirty hack; we don't want to use malloc() in C++ */
+#ifdef __cplusplus
+#define ialloc(t)      new t()
+#define ifree(o)       delete o
+#else
+#define ialloc(t)      malloc(sizeof(t))
+#define ifree(o)       free(o);
+#endif
+
+struct CtdlServInfo {
+       int pid;
+       char nodename[32];
+       char humannode[64];
+       char fqdn[64];
+       char software[64];
+       int rev_level;
+       char site_location[64];
+       char sysadm[64];
+       char moreprompt[256];
+       int ok_floors;
+       int paging_level;
+       int supports_qnop;
+       int supports_ldap;
+       int newuser_disabled;
+       char default_cal_zone[256];
+       double load_avg;
+       double worker_avg;
+       int thread_count;
+       int has_sieve;
+       int fulltext_enabled;
+       char svn_revision[256];
+};
+
+/* This class is responsible for the server connection */
+typedef struct _CtdlIPC {
+       /* The server info for this connection */
+       struct CtdlServInfo ServInfo;
+
+#if defined(HAVE_OPENSSL)
+       /* NULL if not encrypted, non-NULL otherwise */
+       SSL *ssl;
+#endif
+#if defined(HAVE_PTHREAD_H)
+       /* Fast mutex, call CtdlIPC_lock() or CtdlIPC_unlock() to use */
+       pthread_mutex_t mutex;
+#endif
+       /* -1 if not connected, >= 0 otherwise */
+       int sock;
+       /* 1 if server is local, 0 otherwise or if not connected */
+       int isLocal;
+       /* 1 if a download is open on the server, 0 otherwise */
+       int downloading;
+       /* 1 if an upload is open on the server, 0 otherwise */
+       int uploading;
+       /* Time the last command was sent to the server */
+       time_t last_command_sent;
+       /* Our buffer for linebuffered read. */
+       char *Buf;
+       size_t BufSize;
+       size_t BufUsed;
+       char *BufPtr;
+       /* Callback for update on whether the IPC is locked */
+       void (*network_status_cb)(int state);
+} CtdlIPC;
+
+/* C constructor */
+CtdlIPC* CtdlIPC_new(int argc, char **argv, char *hostbuf, char *portbuf);
+/* C destructor */
+void CtdlIPC_delete(CtdlIPC* ipc);
+/* Convenience destructor; also nulls out caller's pointer */
+void CtdlIPC_delete_ptr(CtdlIPC** pipc);
+/* Read a line from server, discarding newline, for chat, will go away */
+void CtdlIPC_chat_recv(CtdlIPC* ipc, char *buf);
+/* Write a line to server, adding newline, for chat, will go away */
+void CtdlIPC_chat_send(CtdlIPC* ipc, const char *buf);
+
+struct ctdlipcroom {
+       char RRname[ROOMNAMELEN];       /* Name of room */
+       long RRunread;                  /* Number of unread messages */
+       long RRtotal;                   /* Total number of messages in room */
+       char RRinfoupdated;             /* Nonzero if info was updated */
+       unsigned RRflags;               /* Various flags (see LKRN) */
+       unsigned RRflags2;              /* Various flags (see LKRN) */
+       long RRhighest;                 /* Highest message number in room */
+       long RRlastread;                /* Highest message user has read */
+       char RRismailbox;               /* Is this room a mailbox room? */
+       char RRaide;                    /* User can do aide commands in room */
+       long RRnewmail;                 /* Number of new mail messages */
+       char RRfloor;                   /* Which floor this room is on */
+};
+
+
+struct parts {
+       struct parts *next;
+       char number[16];                /* part number */
+       char name[PATH_MAX];            /* Name */
+       char filename[PATH_MAX];        /* Suggested filename */
+       char mimetype[SIZ];             /* MIME type */
+       char disposition[SIZ];          /* Content disposition */
+       unsigned long length;           /* Content length */
+};
+
+
+struct ctdlipcmessage {
+       char msgid[SIZ];                /* Original message ID */
+       char path[SIZ];                 /* Return path to sender */
+       char zaps[SIZ];                 /* Message ID that this supersedes */
+       char subject[SIZ];              /* Message subject */
+       char email[SIZ];                /* Email address of sender */
+       char author[SIZ];               /* Sender of message */
+       char recipient[SIZ];            /* Recipient of message */
+       char room[SIZ];                 /* Originating room */
+       char node[SIZ];                 /* Short nodename of origin system */
+       char hnod[SIZ];                 /* Humannode of origin system */
+       struct parts *attachments;      /* Available attachments */
+       char *text;                     /* Message text */
+       int type;                       /* Message type */
+       time_t time;                    /* Time message was posted */
+       char nhdr;                      /* Suppress message header? */
+       char anonymous;                 /* An anonymous message */
+       char mime_chosen[SIZ];          /* Chosen MIME part to output */
+       char content_type[SIZ];         /* How would you like that? */
+       char references[SIZ];           /* Thread references */
+};
+
+
+struct ctdlipcfile {
+       char remote_name[PATH_MAX];     /* Filename on server */
+       char local_name[PATH_MAX];      /* Filename on client */
+       char description[80];           /* Description on server */
+       FILE *local_fd;                 /* Open file on client */
+       size_t size;                    /* Size of file in octets */
+       unsigned int upload:1;          /* uploading? 0 if downloading */
+       unsigned int complete:1;        /* Transfer has finished? */
+};
+
+
+struct ctdlipcmisc {
+       long newmail;                   /* Number of new Mail messages */
+       char needregis;                 /* Nonzero if user needs to register */
+       char needvalid;                 /* Nonzero if users need validation */
+};
+
+enum RoomList {
+       SubscribedRooms,
+       SubscribedRoomsWithNewMessages,
+       SubscribedRoomsWithNoNewMessages,
+       UnsubscribedRooms,
+       AllAccessibleRooms,
+       AllPublicRooms
+};
+#define AllFloors -1
+enum MessageList {
+       AllMessages,
+       OldMessages,
+       NewMessages,
+       LastMessages,
+       FirstMessages,
+       MessagesGreaterThan,
+       MessagesLessThan
+};
+enum MessageDirection {
+       ReadReverse = -1,
+       ReadForward = 1
+};
+
+/* Shared Diffie-Hellman parameters */
+#define DH_P           "1A74527AEE4EE2568E85D4FB2E65E18C9394B9C80C42507D7A6A0DBE9A9A54B05A9A96800C34C7AA5297095B69C88901EEFD127F969DCA26A54C0E0B5C5473EBAEB00957D2633ECAE3835775425DE66C0DE6D024DBB17445E06E6B0C78415E589B8814F08531D02FD43778451E7685541079CFFB79EF0D26EFEEBBB69D1E80383"
+#define DH_G           "2"
+#define DH_L           1024
+#define CIT_CIPHERS    "ALL:RC4+RSA:+SSLv2:+TLSv1:!MD5:@STRENGTH"      /* see ciphers(1) */
+
+int CtdlIPCNoop(CtdlIPC *ipc);
+int CtdlIPCEcho(CtdlIPC *ipc, const char *arg, char *cret);
+int CtdlIPCQuit(CtdlIPC *ipc);
+int CtdlIPCLogout(CtdlIPC *ipc);
+int CtdlIPCTryLogin(CtdlIPC *ipc, const char *username, char *cret);
+int CtdlIPCTryPassword(CtdlIPC *ipc, const char *passwd, char *cret);
+int CtdlIPCTryApopPassword(CtdlIPC *ipc, const char *response, char *cret);
+int CtdlIPCCreateUser(CtdlIPC *ipc, const char *username, int selfservice,
+               char *cret);
+int CtdlIPCChangePassword(CtdlIPC *ipc, const char *passwd, char *cret);
+int CtdlIPCKnownRooms(CtdlIPC *ipc, enum RoomList which, int floor,
+               struct march **listing, char *cret);
+int CtdlIPCGetConfig(CtdlIPC *ipc, struct ctdluser **uret, char *cret);
+int CtdlIPCSetConfig(CtdlIPC *ipc, struct ctdluser *uret, char *cret);
+int CtdlIPCGotoRoom(CtdlIPC *ipc, const char *room, const char *passwd,
+               struct ctdlipcroom **rret, char *cret);
+int CtdlIPCGetMessages(CtdlIPC *ipc, enum MessageList which, int whicharg,
+               const char *mtemplate, unsigned long **mret, char *cret);
+int CtdlIPCGetSingleMessage(CtdlIPC *ipc, long msgnum, int headers, int as_mime,
+               struct ctdlipcmessage **mret, char *cret);
+int CtdlIPCWhoKnowsRoom(CtdlIPC *ipc, char **listing, char *cret);
+int CtdlIPCServerInfo(CtdlIPC *ipc, char *cret);
+/* int CtdlIPCReadDirectory(CtdlIPC *ipc, struct ctdlipcfile **files, char *cret); */
+int CtdlIPCReadDirectory(CtdlIPC *ipc, char **listing, char *cret);
+int CtdlIPCSetLastRead(CtdlIPC *ipc, long msgnum, char *cret);
+int CtdlIPCInviteUserToRoom(CtdlIPC *ipc, const char *username, char *cret);
+int CtdlIPCKickoutUserFromRoom(CtdlIPC *ipc, const char *username, char *cret);
+int CtdlIPCGetRoomAttributes(CtdlIPC *ipc, struct ctdlroom **qret, char *cret);
+int CtdlIPCSetRoomAttributes(CtdlIPC *ipc, int forget, struct ctdlroom *qret,
+               char *cret);
+int CtdlIPCGetRoomAide(CtdlIPC *ipc, char *cret);
+int CtdlIPCSetRoomAide(CtdlIPC *ipc, const char *username, char *cret);
+int CtdlIPCPostMessage(CtdlIPC *ipc, int flag, int *subject_required, 
+                                          struct ctdlipcmessage *mr,
+                                          char *cret);
+int CtdlIPCRoomInfo(CtdlIPC *ipc, char **iret, char *cret);
+int CtdlIPCDeleteMessage(CtdlIPC *ipc, long msgnum, char *cret);
+int CtdlIPCMoveMessage(CtdlIPC *ipc, int copy, long msgnum,
+               const char *destroom, char *cret);
+int CtdlIPCDeleteRoom(CtdlIPC *ipc, int for_real, char *cret);
+int CtdlIPCCreateRoom(CtdlIPC *ipc, int for_real, const char *roomname,
+               int type, const char *password, int floor, char *cret);
+int CtdlIPCForgetRoom(CtdlIPC *ipc, char *cret);
+int CtdlIPCSystemMessage(CtdlIPC *ipc, const char *message, char **mret,
+               char *cret);
+int CtdlIPCNextUnvalidatedUser(CtdlIPC *ipc, char *cret);
+int CtdlIPCGetUserRegistration(CtdlIPC *ipc, const char *username, char **rret,
+               char *cret);
+int CtdlIPCValidateUser(CtdlIPC *ipc, const char *username, int axlevel,
+               char *cret);
+int CtdlIPCSetRoomInfo(CtdlIPC *ipc, int for_real, const char *info,
+               char *cret);
+int CtdlIPCUserListing(CtdlIPC *ipc, char *searchstring, char **list, char *cret);
+int CtdlIPCSetRegistration(CtdlIPC *ipc, const char *info, char *cret);
+int CtdlIPCMiscCheck(CtdlIPC *ipc, struct ctdlipcmisc *chek, char *cret);
+int CtdlIPCDeleteFile(CtdlIPC *ipc, const char *filename, char *cret);
+int CtdlIPCMoveFile(CtdlIPC *ipc, const char *filename, const char *destroom,
+               char *cret);
+int CtdlIPCNetSendFile(CtdlIPC *ipc, const char *filename,
+               const char *destnode, char *cret);
+int CtdlIPCOnlineUsers(CtdlIPC *ipc, char **listing, time_t *stamp, char *cret);
+int CtdlIPCFileDownload(CtdlIPC *ipc, const char *filename, void **buf,
+               size_t resume,
+               void (*progress_gauge_callback)(CtdlIPC*, unsigned long, unsigned long),
+               char *cret);
+int CtdlIPCAttachmentDownload(CtdlIPC *ipc, long msgnum, const char *part,
+               void **buf,
+               void (*progress_gauge_callback)(CtdlIPC*, unsigned long, unsigned long),
+               char *cret);
+int CtdlIPCImageDownload(CtdlIPC *ipc, const char *filename, void **buf,
+               void (*progress_gauge_callback)(CtdlIPC*, unsigned long, unsigned long),
+               char *cret);
+int CtdlIPCFileUpload(CtdlIPC *ipc, const char *save_as, const char *comment,
+               const char *path, 
+               void (*progress_gauge_callback)(CtdlIPC*, unsigned long, unsigned long),
+               char *cret);
+int CtdlIPCImageUpload(CtdlIPC *ipc, int for_real, const char *path,
+               const char *save_as,
+               void (*progress_gauge_callback)(CtdlIPC*, unsigned long, unsigned long),
+               char *cret);
+int CtdlIPCQueryUsername(CtdlIPC *ipc, const char *username, char *cret);
+int CtdlIPCFloorListing(CtdlIPC *ipc, char **listing, char *cret);
+int CtdlIPCCreateFloor(CtdlIPC *ipc, int for_real, const char *name,
+               char *cret);
+int CtdlIPCDeleteFloor(CtdlIPC *ipc, int for_real, int floornum, char *cret);
+int CtdlIPCEditFloor(CtdlIPC *ipc, int floornum, const char *floorname,
+               char *cret);
+int CtdlIPCIdentifySoftware(CtdlIPC *ipc, int developerid, int clientid,
+               int revision, const char *software_name, const char *hostname,
+               char *cret);
+int CtdlIPCSendInstantMessage(CtdlIPC *ipc, const char *username,
+               const char *text, char *cret);
+int CtdlIPCGetInstantMessage(CtdlIPC *ipc, char **listing, char *cret);
+int CtdlIPCEnableInstantMessageReceipt(CtdlIPC *ipc, int mode, char *cret);
+int CtdlIPCSetBio(CtdlIPC *ipc, char *bio, char *cret);
+int CtdlIPCGetBio(CtdlIPC *ipc, const char *username, char **listing,
+               char *cret);
+int CtdlIPCListUsersWithBios(CtdlIPC *ipc, char **listing, char *cret);
+int CtdlIPCStealthMode(CtdlIPC *ipc, int mode, char *cret);
+int CtdlIPCTerminateSession(CtdlIPC *ipc, int sid, char *cret);
+int CtdlIPCTerminateServerNow(CtdlIPC *ipc, char *cret);
+int CtdlIPCTerminateServerScheduled(CtdlIPC *ipc, int mode, char *cret);
+int CtdlIPCEnterSystemMessage(CtdlIPC *ipc, const char *filename, const char *text,
+               char *cret);
+int CtdlIPCChangeHostname(CtdlIPC *ipc, const char *hostname, char *cret);
+int CtdlIPCChangeRoomname(CtdlIPC *ipc, const char *roomname, char *cret);
+int CtdlIPCChangeUsername(CtdlIPC *ipc, const char *username, char *cret);
+time_t CtdlIPCServerTime(CtdlIPC *ipc, char *crert);
+int CtdlIPCAideGetUserParameters(CtdlIPC *ipc, const char *who,
+                                struct ctdluser **uret, char *cret);
+int CtdlIPCAideSetUserParameters(CtdlIPC *ipc, const struct ctdluser *uret, char *cret);
+int CtdlIPCRenameUser(CtdlIPC *ipc, char *oldname, char *newname, char *cret);
+int CtdlIPCGetMessageExpirationPolicy(CtdlIPC *ipc, int which,
+               struct ExpirePolicy **policy, char *cret);
+int CtdlIPCSetMessageExpirationPolicy(CtdlIPC *ipc, int which,
+               struct ExpirePolicy *policy, char *cret);
+int CtdlIPCGetSystemConfig(CtdlIPC *ipc, char **listing, char *cret);
+int CtdlIPCSetSystemConfig(CtdlIPC *ipc, const char *listing, char *cret);
+int CtdlIPCGetSystemConfigByType(CtdlIPC *ipc, const char *mimetype,
+               char **listing, char *cret);
+int CtdlIPCSetSystemConfigByType(CtdlIPC *ipc, const char *mimetype,
+              const char *listing, char *cret);
+int CtdlIPCGetRoomNetworkConfig(CtdlIPC *ipc, char **listing, char *cret);
+int CtdlIPCSetRoomNetworkConfig(CtdlIPC *ipc, const char *listing, char *cret);
+int CtdlIPCRequestClientLogout(CtdlIPC *ipc, int session, char *cret);
+int CtdlIPCSetMessageSeen(CtdlIPC *ipc, long msgnum, int seen, char *cret);
+int CtdlIPCStartEncryption(CtdlIPC *ipc, char *cret);
+int CtdlIPCDirectoryLookup(CtdlIPC *ipc, const char *address, char *cret);
+int CtdlIPCSpecifyPreferredFormats(CtdlIPC *ipc, char *cret, char *formats);
+int CtdlIPCInternalProgram(CtdlIPC *ipc, int secret, char *cret);
+int CtdlIPCMessageBaseCheck(CtdlIPC *ipc, char **mret, char *cret);
+
+/* ************************************************************************** */
+/*             Stuff below this line is not for public consumption            */
+/* ************************************************************************** */
+
+INLINE void CtdlIPC_lock(CtdlIPC *ipc);
+INLINE void CtdlIPC_unlock(CtdlIPC *ipc);
+char *CtdlIPCReadListing(CtdlIPC *ipc, char *dest);
+int CtdlIPCSendListing(CtdlIPC *ipc, const char *listing);
+size_t CtdlIPCPartialRead(CtdlIPC *ipc, void **buf, size_t offset,
+               size_t bytes, char *cret);
+int CtdlIPCEndUpload(CtdlIPC *ipc, int discard, char *cret);
+int CtdlIPCWriteUpload(CtdlIPC *ipc, FILE *uploadFP,
+               void (*progress_gauge_callback)(CtdlIPC*, unsigned long, unsigned long),
+               char *cret);
+int CtdlIPCEndDownload(CtdlIPC *ipc, char *cret);
+int CtdlIPCReadDownload(CtdlIPC *ipc, void **buf, size_t bytes, size_t resume,
+               void (*progress_gauge_callback)(CtdlIPC*, unsigned long, unsigned long),
+               char *cret);
+int CtdlIPCHighSpeedReadDownload(CtdlIPC *ipc, void **buf, size_t bytes,
+               size_t resume,
+               void (*progress_gauge_callback)(CtdlIPC*, unsigned long, unsigned long),
+               char *cret);
+int CtdlIPCGenericCommand(CtdlIPC *ipc, const char *command,
+               const char *to_send, size_t bytes_to_send, char **to_receive,
+               size_t *bytes_to_receive, char *proto_response);
+
+/* Internals */
+int starttls(CtdlIPC *ipc);
+void setCryptoStatusHook(void (*hook)(char *s));
+void CtdlIPC_SetNetworkStatusCallback(CtdlIPC *ipc, void (*hook)(int state));
+/* This is all Ford's doing.  FIXME: figure out what it's doing */
+extern int (*error_printf)(char *s, ...);
+void setIPCDeathHook(void (*hook)(void));
+void setIPCErrorPrintf(int (*func)(char *s, ...));
+void connection_died(CtdlIPC* ipc, int using_ssl);
+int CtdlIPC_getsockfd(CtdlIPC* ipc);
+char CtdlIPC_get(CtdlIPC* ipc);
+
+#ifdef __cplusplus
+}
+#endif
diff --git a/citadel/include/commands.h b/citadel/include/commands.h
new file mode 100644 (file)
index 0000000..5f12b23
--- /dev/null
@@ -0,0 +1,85 @@
+/*
+ * $Id$
+ *
+ * Copyright (c) 1987-2009 by the citadel.org team
+ *
+ *  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 3 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; if not, write to the Free Software
+ *  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ */
+
+/*
+ * Colors for color() command
+ */
+#define DIM_BLACK      0
+#define DIM_RED                1
+#define DIM_GREEN      2
+#define DIM_YELLOW     3
+#define DIM_BLUE       4
+#define DIM_MAGENTA    5
+#define DIM_CYAN       6
+#define DIM_WHITE      7
+#define BRIGHT_BLACK   8
+#define BRIGHT_RED     9
+#define BRIGHT_GREEN   10
+#define BRIGHT_YELLOW  11
+#define BRIGHT_BLUE    12
+#define BRIGHT_MAGENTA 13
+#define BRIGHT_CYAN    14
+#define BRIGHT_WHITE   15
+#define COLOR_PUSH     16      /* Save current color */
+#define COLOR_POP      17      /* Restore saved color */
+#define ORIGINAL_PAIR  -1      /* Default terminal colors */
+
+/*
+ * declarations
+ */
+void load_command_set(void);
+void stty_ctdl(int cmd);
+void newprompt(char *prompt, char *str, int len);
+void strprompt(char *prompt, char *str, int len);
+int boolprompt(char *prompt, int prev_val);
+int intprompt(char *prompt, int ival, int imin, int imax);
+int fmout(int width, FILE *fpin, char *text, FILE *fpout, char pagin,
+               int height, int starting_lp, int subst);
+int getcmd(CtdlIPC *ipc, char *argbuf);
+void display_help(CtdlIPC *ipc, char *name);
+void color(int colornum);
+void cls(int colornum);
+void send_ansi_detect(void);
+void look_for_ansi(void);
+int inkey(void);
+void set_keepalives(int s);
+extern int enable_color;
+int yesno(void);
+int yesno_d(int d);
+void keyopt(char *);
+char keymenu(char *menuprompt, char *menustring);
+void async_ka_start(void);
+void async_ka_end(void);
+int checkpagin(int lp, unsigned int pagin, unsigned int height);
+char was_a_key_pressed(void);
+
+#ifdef __GNUC__
+void pprintf(const char *format, ...) __attribute__((__format__(__printf__,1,2)));
+#else
+void pprintf(const char *format, ...);
+#endif
+
+
+
+extern char rc_url_cmd[SIZ];
+extern char rc_open_cmd[SIZ];
+extern char rc_gotmail_cmd[SIZ];
+extern int lines_printed;
+extern int rc_remember_passwords;
diff --git a/citadel/ipc_c_tcp.c b/citadel/ipc_c_tcp.c
deleted file mode 100644 (file)
index 9958fd3..0000000
+++ /dev/null
@@ -1,72 +0,0 @@
-/*
- * $Id$
- * 
- * Client-side IPC functions
- *
- */
-
-
-#include "sysdep.h"
-#undef NDEBUG
-#include <assert.h>
-#include <stdlib.h>
-#include <unistd.h>
-#include <stdio.h>
-#include <signal.h>
-#include <sys/types.h>
-#include <sys/socket.h>
-#include <netinet/in.h>
-#include <arpa/inet.h>
-#include <sys/un.h>
-#include <netdb.h>
-#include <string.h>
-#include <pwd.h>
-#include <errno.h>
-#include <stdarg.h>
-#include <libcitadel.h>
-#include "citadel.h"
-#include "citadel_ipc.h"
-#include "citadel_decls.h"
-#ifndef HAVE_SNPRINTF
-#include "snprintf.h"
-#endif
-#include "commands.h"
-
-/* Note that some of these functions may not work with multiple instances. */
-
-static void (*deathHook)(void) = NULL;
-int (*error_printf)(char *s, ...) = (int (*)(char *, ...))printf;
-
-void setIPCDeathHook(void (*hook)(void)) {
-       deathHook = hook;
-}
-
-void setIPCErrorPrintf(int (*func)(char *s, ...)) {
-       error_printf = func;
-}
-
-void connection_died(CtdlIPC* ipc, int using_ssl) {
-       if (deathHook != NULL) {
-               deathHook();
-       }
-
-       stty_ctdl(SB_RESTORE);
-       fprintf(stderr, "\r\n\n\n");
-       fprintf(stderr, "Your connection to %s is broken.\n", ipc->ServInfo.humannode);
-
-#ifdef HAVE_OPENSSL
-       if (using_ssl) {
-               fprintf(stderr, "Last error: %s\n", ERR_reason_error_string(ERR_get_error()));
-               SSL_free(ipc->ssl);
-               ipc->ssl = NULL;
-       } else
-#endif
-               fprintf(stderr, "Last error: %s\n", strerror(errno));
-
-       fprintf(stderr, "Please re-connect and log in again.\n");
-       fflush(stderr);
-       fflush(stdout);
-       shutdown(ipc->sock, 2);
-       ipc->sock = -1;
-        exit(1);
-}
diff --git a/citadel/messages.c b/citadel/messages.c
deleted file mode 100644 (file)
index fdd8d46..0000000
+++ /dev/null
@@ -1,1989 +0,0 @@
-/*
- * $Id$
- *
- * Citadel message support routines
- * see COPYING for copyright information
- */
-
-#include "sysdep.h"
-#include <stdlib.h>
-#include <unistd.h>
-#include <sys/types.h>
-#include <fcntl.h>
-#include <stdio.h>
-#include <ctype.h>
-#include <string.h>
-#include <signal.h>
-#include <errno.h>
-#include <limits.h>
-#include <sys/wait.h>
-#include <sys/stat.h>
-
-#if TIME_WITH_SYS_TIME
-# include <sys/time.h>
-# include <time.h>
-#else
-# if HAVE_SYS_TIME_H
-#  include <sys/time.h>
-# else
-#  include <time.h>
-# endif
-#endif
-
-#ifdef HAVE_PTHREAD_H
-#include <pthread.h>
-#endif
-
-#include <stdarg.h>
-#include <libcitadel.h>
-#include "citadel.h"
-#include "citadel_ipc.h"
-#include "citadel_decls.h"
-#include "messages.h"
-#include "commands.h"
-#include "rooms.h"
-#ifndef HAVE_SNPRINTF
-#include "snprintf.h"
-#endif
-#include "screen.h"
-
-#define MAXWORDBUF SIZ
-#define NO_REPLY_TO    "nobody ... xxxxxx"
-
-char reply_to[SIZ];
-char reply_subject[SIZ];
-char reply_references[SIZ];
-char reply_inreplyto[SIZ];
-
-struct cittext {
-       struct cittext *next;
-       char text[MAXWORDBUF];
-};
-
-void stty_ctdl(int cmd);
-int haschar(const char *st, int ch);
-void ctdl_getline(char *string, int lim);
-int file_checksum(char *filename);
-void progress(CtdlIPC* ipc, unsigned long curr, unsigned long cmax);
-
-unsigned long *msg_arr = NULL;
-int msg_arr_size = 0;
-int num_msgs;
-char rc_alt_semantics;
-extern char room_name[];
-extern char tempdir[];
-extern unsigned room_flags;
-extern unsigned room_flags2;
-extern long highest_msg_read;
-extern char temp[];
-extern char temp2[];
-extern int screenwidth;
-extern int screenheight;
-extern long maxmsgnum;
-extern char is_mail;
-extern char is_aide;
-extern char is_room_aide;
-extern char fullname[];
-extern char axlevel;
-extern unsigned userflags;
-extern char sigcaught;
-extern char printcmd[];
-extern int rc_allow_attachments;
-extern int rc_display_message_numbers;
-extern int rc_force_mail_prompts;
-extern int editor_pid;
-extern CtdlIPC *ipc_for_signal_handlers;       /* KLUDGE cover your eyes */
-int num_urls = 0;
-char urls[MAXURLS][SIZ];
-char imagecmd[SIZ];
-int has_images = 0;                            /* Current msg has images */
-struct parts *last_message_parts = NULL;       /* Parts from last msg */
-
-
-
-void ka_sigcatch(int signum)
-{
-       alarm(S_KEEPALIVE);
-       signal(SIGALRM, ka_sigcatch);
-       CtdlIPCNoop(ipc_for_signal_handlers);
-}
-
-
-/*
- * server keep-alive version of wait() (needed for external editor)
- */
-pid_t ka_wait(int *kstatus)
-{
-       pid_t p;
-
-       alarm(S_KEEPALIVE);
-       signal(SIGALRM, ka_sigcatch);
-       do {
-               errno = 0;
-               p = wait(kstatus);
-       } while (errno == EINTR);
-       signal(SIGALRM, SIG_IGN);
-       alarm(0);
-       return (p);
-}
-
-
-/*
- * version of system() that uses ka_wait()
- */
-int ka_system(char *shc)
-{
-       pid_t childpid;
-       pid_t waitpid;
-       int retcode;
-
-       childpid = fork();
-       if (childpid < 0) {
-               color(BRIGHT_RED);
-               perror("Cannot fork");
-               color(DIM_WHITE);
-               return ((pid_t) childpid);
-       }
-
-       if (childpid == 0) {
-               execlp("/bin/sh", "sh", "-c", shc, NULL);
-               exit(127);
-       }
-
-       if (childpid > 0) {
-               do {
-                       waitpid = ka_wait(&retcode);
-               } while (waitpid != childpid);
-               return (retcode);
-       }
-
-       return (-1);
-}
-
-
-
-/*
- * add a newline to the buffer...
- */
-void add_newline(struct cittext *textlist)
-{
-       struct cittext *ptr;
-
-       ptr = textlist;
-       while (ptr->next != NULL)
-               ptr = ptr->next;
-
-       while (ptr->text[strlen(ptr->text) - 1] == 32)
-               ptr->text[strlen(ptr->text) - 1] = 0;
-       /* strcat(ptr->text,"\n"); */
-
-       ptr->next = (struct cittext *)
-           malloc(sizeof(struct cittext));
-       ptr = ptr->next;
-       ptr->next = NULL;
-       strcpy(ptr->text, "");
-}
-
-
-/*
- * add a word to the buffer...
- */
-void add_word(struct cittext *textlist, char *wordbuf)
-{
-       struct cittext *ptr;
-
-       ptr = textlist;
-       while (ptr->next != NULL)
-               ptr = ptr->next;
-
-       if (3 + strlen(ptr->text) + strlen(wordbuf) > screenwidth) {
-               ptr->next = (struct cittext *)
-                   malloc(sizeof(struct cittext));
-               ptr = ptr->next;
-               ptr->next = NULL;
-               strcpy(ptr->text, "");
-       }
-
-       strcat(ptr->text, wordbuf);
-       strcat(ptr->text, " ");
-}
-
-
-/*
- * begin editing of an opened file pointed to by fp
- */
-void citedit(CtdlIPC *ipc, FILE * fp)
-{
-       int a, prev, finished, b, last_space;
-       int appending = 0;
-       struct cittext *textlist = NULL;
-       struct cittext *ptr;
-       char wordbuf[MAXWORDBUF];
-       int rv = 0;
-
-       /* first, load the text into the buffer */
-       fseek(fp, 0L, 0);
-       textlist = (struct cittext *) malloc(sizeof(struct cittext));
-       textlist->next = NULL;
-       strcpy(textlist->text, "");
-
-       strcpy(wordbuf, "");
-       prev = (-1);
-       while (a = getc(fp), a >= 0) {
-               appending = 1;
-               if ((a == 32) || (a == 9) || (a == 13) || (a == 10)) {
-                       add_word(textlist, wordbuf);
-                       strcpy(wordbuf, "");
-                       if ((prev == 13) || (prev == 10)) {
-                               add_word(textlist, "\n");
-                               add_newline(textlist);
-                               add_word(textlist, "");
-                       }
-               } else {
-                       wordbuf[strlen(wordbuf) + 1] = 0;
-                       wordbuf[strlen(wordbuf)] = a;
-               }
-               if (strlen(wordbuf) + 3 > screenwidth) {
-                       add_word(textlist, wordbuf);
-                       strcpy(wordbuf, "");
-               }
-               prev = a;
-       }
-
-       /* get text */
-       finished = 0;
-       prev = (appending ? 13 : (-1));
-       strcpy(wordbuf, "");
-       async_ka_start();
-       do {
-               a = inkey();
-               if (a == 10)
-                       a = 13;
-               if (a == 9)
-                       a = 32;
-               if (a == 127)
-                       a = 8;
-
-               if ((a != 32) && (prev == 13)) {
-                       add_word(textlist, "\n");
-                       scr_printf(" ");
-               }
-
-               if ((a == 32) && (prev == 13)) {
-                       add_word(textlist, "\n");
-                       add_newline(textlist);
-               }
-
-               if (a == 8) {
-                       if (!IsEmptyStr(wordbuf)) {
-                               wordbuf[strlen(wordbuf) - 1] = 0;
-                               scr_putc(8);
-                               scr_putc(32);
-                               scr_putc(8);
-                       }
-               } else if (a == 23) {
-                       do {
-                               wordbuf[strlen(wordbuf) - 1] = 0;
-                               scr_putc(8);
-                               scr_putc(32);
-                               scr_putc(8);
-                       } while (!IsEmptyStr(wordbuf) && wordbuf[strlen(wordbuf) - 1] != ' ');
-               } else if (a == 13) {
-                       scr_printf("\n");
-                       if (IsEmptyStr(wordbuf))
-                               finished = 1;
-                       else {
-                               for (b = 0; b < strlen(wordbuf); ++b)
-                                       if (wordbuf[b] == 32) {
-                                               wordbuf[b] = 0;
-                                               add_word(textlist,
-                                                        wordbuf);
-                                               strcpy(wordbuf,
-                                                      &wordbuf[b + 1]);
-                                               b = 0;
-                                       }
-                               add_word(textlist, wordbuf);
-                               strcpy(wordbuf, "");
-                       }
-               } else {
-                       scr_putc(a);
-                       wordbuf[strlen(wordbuf) + 1] = 0;
-                       wordbuf[strlen(wordbuf)] = a;
-               }
-               if ((strlen(wordbuf) + 3) > screenwidth) {
-                       last_space = (-1);
-                       for (b = 0; b < strlen(wordbuf); ++b)
-                               if (wordbuf[b] == 32)
-                                       last_space = b;
-                       if (last_space >= 0) {
-                               for (b = 0; b < strlen(wordbuf); ++b)
-                                       if (wordbuf[b] == 32) {
-                                               wordbuf[b] = 0;
-                                               add_word(textlist,
-                                                        wordbuf);
-                                               strcpy(wordbuf,
-                                                      &wordbuf[b + 1]);
-                                               b = 0;
-                                       }
-                               for (b = 0; b < strlen(wordbuf); ++b) {
-                                       scr_putc(8);
-                                       scr_putc(32);
-                                       scr_putc(8);
-                               }
-                               scr_printf("\n%s", wordbuf);
-                       } else {
-                               add_word(textlist, wordbuf);
-                               strcpy(wordbuf, "");
-                               scr_printf("\n");
-                       }
-               }
-               prev = a;
-       } while (finished == 0);
-       async_ka_end();
-
-       /* write the buffer back to disk */
-       fseek(fp, 0L, 0);
-       for (ptr = textlist; ptr != NULL; ptr = ptr->next) {
-               fprintf(fp, "%s", ptr->text);
-       }
-       putc(10, fp);
-       fflush(fp);
-       rv = ftruncate(fileno(fp), ftell(fp));
-
-       /* and deallocate the memory we used */
-       while (textlist != NULL) {
-               ptr = textlist->next;
-               free(textlist);
-               textlist = ptr;
-       }
-}
-
-
-/*
- * Free the struct parts
- */
-void free_parts(struct parts *p)
-{
-       struct parts *a_part = p;
-
-       while (a_part) {
-               struct parts *q;
-
-               q = a_part;
-               a_part = a_part->next;
-               free(q);
-       }
-}
-
-
-/*
- * Read a message from the server
- */
-int read_message(CtdlIPC *ipc,
-       long num,   /* message number */
-       int pagin, /* 0 = normal read, 1 = read with pagination, 2 = header */
-       FILE *dest) /* Destination file, NULL for screen */
-{
-       char buf[SIZ];
-       char now[SIZ];
-       int format_type = 0;
-       int fr = 0;
-       int nhdr = 0;
-       struct ctdlipcmessage *message = NULL;
-       int r;                          /* IPC response code */
-       char *converted_text = NULL;
-       char *lineptr;
-       char *nextline;
-       char *searchptr;
-       int i;
-       char ch;
-       int linelen;
-       int final_line_is_blank = 0;
-
-       has_images = 0;
-
-       sigcaught = 0;
-       stty_ctdl(1);
-
-       strcpy(reply_to, NO_REPLY_TO);
-       strcpy(reply_subject, "");
-       strcpy(reply_references, "");
-       strcpy(reply_inreplyto, "");
-
-       r = CtdlIPCGetSingleMessage(ipc, num, (pagin == READ_HEADER ? 1 : 0), 4, &message, buf);
-       if (r / 100 != 1) {
-               err_printf("*** msg #%ld: %d %s\n", num, r, buf);
-               ++lines_printed;
-               lines_printed = checkpagin(lines_printed, pagin, screenheight);
-               stty_ctdl(0);
-               free(message->text);
-               free_parts(message->attachments);
-               free(message);
-               return (0);
-       }
-
-       if (dest) {
-               fprintf(dest, "\n ");
-       } else {
-               scr_printf("\n");
-               ++lines_printed;
-               lines_printed = checkpagin(lines_printed, pagin, screenheight);
-               if (pagin != 2)
-                       scr_printf(" ");
-       }
-       if (pagin == 1 && !dest) {
-               color(BRIGHT_CYAN);
-       }
-
-       /* View headers only */
-       if (pagin == 2) {
-               pprintf("nhdr=%s\nfrom=%s\ntype=%d\nmsgn=%s\n",
-                               message->nhdr ? "yes" : "no",
-                               message->author, message->type,
-                               message->msgid);
-               if (!IsEmptyStr(message->subject)) {
-                       pprintf("subj=%s\n", message->subject);
-               }
-               if (!IsEmptyStr(message->email)) {
-                       pprintf("rfca=%s\n", message->email);
-               }
-               pprintf("hnod=%s\nroom=%s\nnode=%s\ntime=%s",
-                               message->hnod, message->room,
-                               message->node, 
-                               asctime(localtime(&message->time)));
-               if (!IsEmptyStr(message->recipient)) {
-                       pprintf("rcpt=%s\n", message->recipient);
-               }
-               if (message->attachments) {
-                       struct parts *ptr;
-
-                       for (ptr = message->attachments; ptr; ptr = ptr->next) {
-                               pprintf("part=%s|%s|%s|%s|%s|%ld\n",
-                                       ptr->name, ptr->filename, ptr->number,
-                                       ptr->disposition, ptr->mimetype,
-                                       ptr->length);
-                       }
-               }
-               pprintf("\n");
-               stty_ctdl(0);
-               free(message->text);
-               free_parts(message->attachments);
-               free(message);
-               return (0);
-       }
-
-       if (rc_display_message_numbers) {
-               if (dest) {
-                       fprintf(dest, "[#%s] ", message->msgid);
-               } else {
-                       color(DIM_WHITE);
-                       scr_printf("[");
-                       color(BRIGHT_WHITE);
-                       scr_printf("#%s", message->msgid);
-                       color(DIM_WHITE);
-                       scr_printf("] ");
-               }
-       }
-       if (nhdr == 1 && !is_room_aide) {
-               if (dest) {
-                       fprintf(dest, " ****");
-               } else {
-                       scr_printf(" ****");
-               }
-       } else {
-               fmt_date(now, sizeof now, message->time, 0);
-               if (dest) {
-                       fprintf(dest, "%s from %s ", now, message->author);
-                       if (!IsEmptyStr(message->email)) {
-                               fprintf(dest, "<%s> ", message->email);
-                       }
-               } else {
-                       color(BRIGHT_CYAN);
-                       scr_printf("%s ", now);
-                       color(DIM_WHITE);
-                       scr_printf("from ");
-                       color(BRIGHT_CYAN);
-                       scr_printf("%s ", message->author);
-                       if (!IsEmptyStr(message->email)) {
-                               color(DIM_WHITE);
-                               scr_printf("<");
-                               color(BRIGHT_BLUE);
-                               scr_printf("%s", message->email);
-                                       color(DIM_WHITE);
-                               scr_printf("> ");
-                       }
-               }
-               if (!IsEmptyStr(message->node)) {
-                       if ((room_flags & QR_NETWORK)
-                           || ((strcasecmp(message->node, ipc->ServInfo.nodename)
-                            && (strcasecmp(message->node, ipc->ServInfo.fqdn))))) {
-                               if (IsEmptyStr(message->email)) {
-                                       if (dest) {
-                                               fprintf(dest, "@%s ", message->node);
-                                       } else {
-                                               color(DIM_WHITE);
-                                               scr_printf("@");
-                                               color(BRIGHT_YELLOW);
-                                               scr_printf("%s ", message->node);
-                                       }
-                               }
-                       }
-               }
-               if (strcasecmp(message->hnod, ipc->ServInfo.humannode)
-                   && (!IsEmptyStr(message->hnod)) && (IsEmptyStr(message->email))) {
-                       if (dest) {
-                               fprintf(dest, "(%s) ", message->hnod);
-                       } else {
-                               color(DIM_WHITE);
-                               scr_printf("(");
-                               color(BRIGHT_WHITE);
-                               scr_printf("%s", message->hnod);
-                               color(DIM_WHITE);
-                               scr_printf(") ");
-                       }
-               }
-               if (strcasecmp(message->room, room_name) && (IsEmptyStr(message->email))) {
-                       if (dest) {
-                               fprintf(dest, "in %s> ", message->room);
-                       } else {
-                               color(DIM_WHITE);
-                               scr_printf("in ");
-                               color(BRIGHT_MAGENTA);
-                               scr_printf("%s> ", message->room);
-                       }
-               }
-               if (!IsEmptyStr(message->recipient)) {
-                       if (dest) {
-                               fprintf(dest, "to %s ", message->recipient);
-                       } else {
-                               color(DIM_WHITE);
-                               scr_printf("to ");
-                               color(BRIGHT_CYAN);
-                               scr_printf("%s ", message->recipient);
-                       }
-               }
-       }
-       
-       if (dest) {
-               fprintf(dest, "\n");
-       } else {
-               scr_printf("\n");
-       }
-
-       /* Set the reply-to address to an Internet e-mail address if possible
-        */
-       if ((message->email != NULL) && (!IsEmptyStr(message->email))) {
-               if (!IsEmptyStr(message->author)) {
-                       snprintf(reply_to, sizeof reply_to, "%s <%s>", message->author, message->email);
-               }
-               else {
-                       safestrncpy(reply_to, message->email, sizeof reply_to);
-               }
-       }
-
-       /* But if we can't do that, set it to a Citadel address.
-        */
-       if (!strcmp(reply_to, NO_REPLY_TO)) {
-               snprintf(reply_to, sizeof(reply_to), "%s @ %s",
-                        message->author, message->node);
-       }
-
-       if (!dest) {
-               ++lines_printed;
-               lines_printed = checkpagin(lines_printed, pagin, screenheight);
-       }
-
-
-       if (message->msgid != NULL) {
-               safestrncpy(reply_inreplyto, message->msgid, sizeof reply_inreplyto);
-       }
-
-       if (message->references != NULL) if (!IsEmptyStr(message->references)) {
-               safestrncpy(reply_references, message->references, sizeof reply_references);
-       }
-
-       if (message->subject != NULL) {
-               safestrncpy(reply_subject, message->subject, sizeof reply_subject);
-               if (!IsEmptyStr(message->subject)) {
-                       if (dest) {
-                               fprintf(dest, "Subject: %s\n",
-                                                       message->subject);
-                       } else {
-                               color(DIM_WHITE);
-                               scr_printf("Subject: ");
-                               color(BRIGHT_CYAN);
-                               scr_printf("%s\n", message->subject);
-                               ++lines_printed;
-                               lines_printed = checkpagin(lines_printed,
-                                               pagin, screenheight);
-                       }
-               }
-       }
-
-       if (pagin == 1 && !dest) {
-               color(BRIGHT_WHITE);
-       }
-
-       /******* end of header output, start of message text output *******/
-
-       /*
-        * Convert HTML to plain text, formatting for the actual width
-        * of the client screen.
-        */
-       if (!strcasecmp(message->content_type, "text/html")) {
-               converted_text = html_to_ascii(message->text, 0, screenwidth, 0);
-               if (converted_text != NULL) {
-                       free(message->text);
-                       message->text = converted_text;
-                       format_type = 1;
-               }
-       }
-
-       /* Text/plain is a different type */
-       if (!strcasecmp(message->content_type, "text/plain")) {
-               format_type = 1;
-       }
-
-       /* Extract URL's */
-       num_urls = 0;   /* Start with a clean slate */
-       searchptr = message->text;
-       while ( (searchptr != NULL) && (num_urls < MAXURLS) ) {
-               searchptr = strstr(searchptr, "http://");
-               if (searchptr != NULL) {
-                       safestrncpy(urls[num_urls], searchptr, sizeof(urls[num_urls]));
-                       for (i = 0; i < strlen(urls[num_urls]); i++) {
-                               ch = urls[num_urls][i];
-                               if (ch == '>' || ch == '\"' || ch == ')' ||
-                                   ch == ' ' || ch == '\n') {
-                                       urls[num_urls][i] = 0;
-                                       break;
-                               }
-                       }
-                       num_urls++;
-                       ++searchptr;
-               }
-       }
-
-       /*
-        * Here we go
-        */
-       if (format_type == 0) {
-               fr = fmout(screenwidth, NULL, message->text, dest,
-                          ((pagin == 1) ? 1 : 0), screenheight, (-1), 1);
-       } else {
-               /* renderer for text/plain */
-
-               lineptr = message->text;
-
-               do {
-                       nextline = strchr(lineptr, '\n');
-                       if (nextline != NULL) {
-                               *nextline = 0;
-                               ++nextline;
-                               if (*nextline == 0) nextline = NULL;
-                       }
-
-                       if (sigcaught == 0) {
-                               linelen = strlen(lineptr);
-                               if (linelen && (lineptr[linelen-1] == '\r')) {
-                                       lineptr[--linelen] = 0;
-                               }
-                               if (dest) {
-                                       fprintf(dest, "%s\n", lineptr);
-                               } else {
-                                       scr_printf("%s\n", lineptr);
-                                       lines_printed = lines_printed + 1 +
-                                           (linelen / screenwidth);
-                                       lines_printed =
-                                           checkpagin(lines_printed, pagin,
-                                                      screenheight);
-                               }
-                       }
-                       if (lineptr[0] == 0) final_line_is_blank = 1;
-                       else final_line_is_blank = 0;
-                       lineptr = nextline;
-               } while (nextline);
-               fr = sigcaught;
-       }
-       if (!final_line_is_blank) {
-               if (dest) {
-                       fprintf(dest, "\n");
-               }
-               else {
-                       scr_printf("\n");
-                       ++lines_printed;
-                       lines_printed = checkpagin(lines_printed, pagin, screenheight);
-                       fr = sigcaught;         
-               }
-       }
-
-       /* Enumerate any attachments */
-       if ( (pagin == 1) && (message->attachments) ) {
-               struct parts *ptr;
-
-               for (ptr = message->attachments; ptr; ptr = ptr->next) {
-                       if ( (!strcasecmp(ptr->disposition, "attachment"))
-                          || (!strcasecmp(ptr->disposition, "inline"))
-                          || (!strcasecmp(ptr->disposition, ""))
-                       ) {
-                               if ( (strcasecmp(ptr->number, message->mime_chosen))
-                                  && (!IsEmptyStr(ptr->mimetype))
-                               ) {
-                                       color(DIM_WHITE);
-                                       pprintf("Part ");
-                                       color(BRIGHT_MAGENTA);
-                                       pprintf("%s", ptr->number);
-                                       color(DIM_WHITE);
-                                       pprintf(": ");
-                                       color(BRIGHT_CYAN);
-                                       pprintf("%s", ptr->filename);
-                                       color(DIM_WHITE);
-                                       pprintf(" (%s, %ld bytes)\n", ptr->mimetype, ptr->length);
-                                       if (!strncmp(ptr->mimetype, "image/", 6)) {
-                                               has_images++;
-                                       }
-                               }
-                       }
-               }
-       }
-
-       /* Save the attachments info for later */
-       last_message_parts = message->attachments;
-
-       /* Now we're done */
-       free(message->text);
-       free(message);
-
-       if (pagin == 1 && !dest)
-               color(DIM_WHITE);
-       stty_ctdl(0);
-       return (fr);
-}
-
-/*
- * replace string function for the built-in editor
- */
-void replace_string(char *filename, long int startpos)
-{
-       char buf[512];
-       char srch_str[128];
-       char rplc_str[128];
-       FILE *fp;
-       int a;
-       long rpos, wpos;
-       char *ptr;
-       int substitutions = 0;
-       long msglen = 0L;
-       int rv;
-
-       scr_printf("Enter text to be replaced:\n: ");
-       ctdl_getline(srch_str, (sizeof(srch_str)-1) );
-       if (IsEmptyStr(srch_str)) {
-               return;
-       }
-
-       scr_printf("Enter text to replace it with:\n: ");
-       ctdl_getline(rplc_str, (sizeof(rplc_str)-1) );
-
-       fp = fopen(filename, "r+");
-       if (fp == NULL) {
-               return;
-       }
-
-       wpos = startpos;
-       fseek(fp, startpos, 0);
-       strcpy(buf, "");
-       while (a = getc(fp), a > 0) {
-               ++msglen;
-               buf[strlen(buf) + 1] = 0;
-               buf[strlen(buf)] = a;
-               if (strlen(buf) >= strlen(srch_str)) {
-                       ptr = (&buf[strlen(buf) - strlen(srch_str)]);
-                       if (!strncmp(ptr, srch_str, strlen(srch_str))) {
-                               strcpy(ptr, rplc_str);
-                               ++substitutions;
-                       }
-               }
-               if (strlen(buf) > 384) {
-                       rpos = ftell(fp);
-                       fseek(fp, wpos, 0);
-                       rv = fwrite((char *) buf, 128, 1, fp);
-                       strcpy(buf, &buf[128]);
-                       wpos = ftell(fp);
-                       fseek(fp, rpos, 0);
-               }
-       }
-       fseek(fp, wpos, 0);
-       if (!IsEmptyStr(buf)) {
-               rv = fwrite((char *) buf, strlen(buf), 1, fp);
-       }
-       wpos = ftell(fp);
-       fclose(fp);
-       rv = truncate(filename, wpos);
-       scr_printf("<R>eplace made %d substitution(s).\n\n", substitutions);
-}
-
-/*
- * Function to begin composing a new message
- */
-int client_make_message(CtdlIPC *ipc,
-                                               char *filename,         /* temporary file name */
-                                               char *recipient,        /* NULL if it's not mail */
-                                               int is_anonymous,
-                                               int format_type,
-                                               int mode,
-                                               char *subject,          /* buffer to store subject line */
-                                               int subject_required)
-{
-       FILE *fp;
-       int a, b, e_ex_code;
-       long beg;
-       char datestr[SIZ];
-       char header[SIZ];
-       char *editor_path = NULL;
-       int cksum = 0;
-
-       if (mode >= 2)
-       {
-               if((mode-2) < MAX_EDITORS && !IsEmptyStr(editor_paths[mode-2])) {
-                       editor_path = editor_paths[mode-2];
-               } else if (!IsEmptyStr(editor_paths[0])) {
-                       editor_path = editor_paths[0];
-               } else {
-                       err_printf("*** No editor available, "
-                               "using built-in editor\n");
-                       mode = 0;
-               }
-       }
-
-       fmt_date(datestr, sizeof datestr, time(NULL), 0);
-       header[0] = 0;
-
-       if (room_flags & QR_ANONONLY && !recipient) {
-               snprintf(header, sizeof header, " ****");
-       }
-       else {
-               snprintf(header, sizeof header,
-                       " %s from %s",
-                       datestr,
-                       (is_anonymous ? "[anonymous]" : fullname)
-                       );
-               if (!IsEmptyStr(recipient)) {
-                       size_t tmp = strlen(header);
-                       snprintf(&header[tmp], sizeof header - tmp,
-                               " to %s", recipient);
-               }
-       }
-       scr_printf("%s\n", header);
-       if (subject != NULL) if (!IsEmptyStr(subject)) {
-               scr_printf("Subject: %s\n", subject);
-       }
-       
-       if ( (subject_required) && (IsEmptyStr(subject)) ) {
-               newprompt("Subject: ", subject, 70);
-       }
-
-       beg = 0L;
-
-       if (mode == 1) {
-               scr_printf("(Press ctrl-d when finished)\n");
-       }
-
-       if (mode == 0) {
-               fp = fopen(filename, "r");
-               if (fp != NULL) {
-                       fmout(screenwidth, fp, NULL, NULL, 0,
-                               screenheight, 0, 0);
-                       beg = ftell(fp);
-                       fclose(fp);
-               } else {
-                       fp = fopen(filename, "w");
-                       if (fp == NULL) {
-                               err_printf("*** Error opening temp file!\n"
-                                       "    %s: %s\n",
-                                       filename, strerror(errno));
-                       return(1);
-                       }
-                       fclose(fp);
-               }
-       }
-
-ME1:   switch (mode) {
-
-       case 0:
-               fp = fopen(filename, "r+");
-               if (fp == NULL) {
-                       err_printf("*** Error opening temp file!\n"
-                               "    %s: %s\n",
-                               filename, strerror(errno));
-                       return(1);
-               }
-               citedit(ipc, fp);
-               fclose(fp);
-               goto MECR;
-
-       case 1:
-               fp = fopen(filename, "a");
-               if (fp == NULL) {
-                       err_printf("*** Error opening temp file!\n"
-                               "    %s: %s\n",
-                               filename, strerror(errno));
-                       return(1);
-               }
-               do {
-                       a = inkey();
-                       if (a == 255)
-                               a = 32;
-                       if (a == 13)
-                               a = 10;
-                       if (a != 4) {
-                               putc(a, fp);
-                               scr_putc(a);
-                       }
-                       if (a == 10)
-                               scr_putc(10);
-               } while (a != 4);
-               fclose(fp);
-               break;
-
-       case 2:
-       default:        /* allow 2+ modes */
-               e_ex_code = 1;  /* start with a failed exit code */
-               screen_reset();
-               stty_ctdl(SB_RESTORE);
-               editor_pid = fork();
-               cksum = file_checksum(filename);
-               if (editor_pid == 0) {
-                       char tmp[SIZ];
-
-                       chmod(filename, 0600);
-                       snprintf(tmp, sizeof tmp, "WINDOW_TITLE=%s", header);
-                       putenv(tmp);
-                       execlp(editor_path, editor_path, filename, NULL);
-                       exit(1);
-               }
-               if (editor_pid > 0)
-                       do {
-                               e_ex_code = 0;
-                               b = ka_wait(&e_ex_code);
-                       } while ((b != editor_pid) && (b >= 0));
-               editor_pid = (-1);
-               stty_ctdl(0);
-               screen_set();
-               break;
-       }
-
-MECR:  if (mode >= 2) {
-               if (file_checksum(filename) == cksum) {
-                       err_printf("*** Aborted message.\n");
-                       e_ex_code = 1;
-               }
-               if (e_ex_code == 0) {
-                       goto MEFIN;
-               }
-               goto MEABT2;
-       }
-
-       b = keymenu("Entry command (? for options)",
-                   "<A>bort|<C>ontinue|<S>ave message|<P>rint formatted|"
-                   "add s<U>bject|"
-                   "<R>eplace string|<H>old message");
-
-       if (b == 'a') goto MEABT;
-       if (b == 'c') goto ME1;
-       if (b == 's') goto MEFIN;
-       if (b == 'p') {
-               scr_printf(" %s from %s", datestr, fullname);
-               if (!IsEmptyStr(recipient)) {
-                       scr_printf(" to %s", recipient);
-               }
-               scr_printf("\n");
-               if (subject != NULL) if (!IsEmptyStr(subject)) {
-                       scr_printf("Subject: %s\n", subject);
-               }
-               fp = fopen(filename, "r");
-               if (fp != NULL) {
-                       fmout(screenwidth, fp, NULL, NULL,
-                             ((userflags & US_PAGINATOR) ? 1 : 0),
-                             screenheight, 0, 0);
-                       beg = ftell(fp);
-                       fclose(fp);
-               }
-               goto MECR;
-       }
-       if (b == 'r') {
-               replace_string(filename, 0L);
-               goto MECR;
-       }
-       if (b == 'h') {
-               return (2);
-       }
-       if (b == 'u') {
-               if (subject != NULL) {
-                       newprompt("Subject: ", subject, 70);
-               }
-               goto MECR;
-       }
-
-MEFIN: return (0);
-
-MEABT: scr_printf("Are you sure? ");
-       if (yesno() == 0) {
-               goto ME1;
-       }
-MEABT2:        unlink(filename);
-       return (2);
-}
-
-
-/*
- * Make sure there's room in msg_arr[] for at least one more.
- */
-void check_msg_arr_size(void) {
-       if ((num_msgs + 1) > msg_arr_size) {
-               msg_arr_size += 512;
-               msg_arr = realloc(msg_arr,
-                       ((sizeof(long)) * msg_arr_size) );
-       }
-}
-
-
-/*
- * break_big_lines()  -  break up lines that are >1024 characters
- *                       otherwise the server will truncate
- */
-void break_big_lines(char *msg) {
-       char *ptr;
-       char *break_here;
-
-       if (msg == NULL) {
-               return;
-       }
-
-       ptr = msg;
-       while (strlen(ptr) > 1000) {
-               break_here = strchr(&ptr[900], ' ');
-               if ((break_here == NULL) || (break_here > &ptr[999])) {
-                       break_here = &ptr[999];
-               }
-               *break_here = '\n';
-               ptr = break_here++;
-       }
-}
-
-
-/*
- * entmsg()  -  edit and create a message
- *              returns 0 if message was saved
- */
-int entmsg(CtdlIPC *ipc,
-               int is_reply,   /* nonzero if this was a <R>eply command */
-               int c,          /* mode */
-               int masquerade  /* prompt for a non-default display name? */
-) {
-       char buf[SIZ];
-       int a, b;
-       int need_recp = 0;
-       int mode;
-       long highmsg = 0L;
-       FILE *fp;
-       char subject[SIZ];
-       struct ctdlipcmessage message;
-       unsigned long *msgarr = NULL;
-       int r;                  /* IPC response code */
-       int subject_required = 0;
-
-       if (c > 0)
-               mode = 1;
-       else
-               mode = 0;
-
-       strcpy(subject, "");
-
-       /*
-        * First, check to see if we have permission to enter a message in
-        * this room.  The server will return an error code if we can't.
-        */
-       strcpy(message.recipient, "");
-       strcpy(message.author, "");
-       strcpy(message.subject, "");
-       strcpy(message.references, "");
-       message.text = "";              /* point to "", changes later */
-       message.anonymous = 0;
-       message.type = mode;
-
-       if (masquerade) {
-               newprompt("Display name for this message: ", message.author, 40);
-       }
-
-       r = CtdlIPCPostMessage(ipc, 0, &subject_required, &message, buf);
-
-       if (r / 100 != 2 && r / 10 != 57) {
-               scr_printf("%s\n", buf);
-               return (1);
-       }
-
-       /* Error code 570 is special.  It means that we CAN enter a message
-        * in this room, but a recipient needs to be specified.
-        */
-       need_recp = 0;
-       if (r / 10 == 57) {
-               need_recp = 1;
-       }
-
-       /* If the user is a dumbass, tell them how to type. */
-       if ((userflags & US_EXPERT) == 0) {
-               formout(ipc, "entermsg");
-       }
-
-       /* Handle the selection of a recipient, if necessary. */
-       strcpy(buf, "");
-       if (need_recp == 1) {
-               if (axlevel >= 2) {
-                       if (is_reply) {
-                               strcpy(buf, reply_to);
-                       } else {
-                               scr_printf("Enter recipient: ");
-                               ctdl_getline(buf, (SIZ-100) );
-                               if (IsEmptyStr(buf))
-                                       return (1);
-                       }
-               } else
-                       strcpy(buf, "sysop");
-       }
-       strcpy(message.recipient, buf);
-
-       if (is_reply) {
-
-               if (!IsEmptyStr(reply_subject)) {
-                       if (!strncasecmp(reply_subject,
-                          "Re: ", 3)) {
-                               strcpy(message.subject, reply_subject);
-                       }
-                       else {
-                               snprintf(message.subject,
-                                       sizeof message.subject,
-                                       "Re: %s",
-                                       reply_subject);
-                       }
-               }
-
-               /* Trim down excessively long lists of thread references.  We eliminate the
-                * second one in the list so that the thread root remains intact.
-                */
-               int rrtok = num_tokens(reply_references, '|');
-               int rrlen = strlen(reply_references);
-               if ( ((rrtok >= 3) && (rrlen > 900)) || (rrtok > 10) ) {
-                       remove_token(reply_references, 1, '|');
-               }
-
-               snprintf(message.references, sizeof message.references, "%s%s%s",
-                       reply_references,
-                       (IsEmptyStr(reply_references) ? "" : "|"),
-                       reply_inreplyto
-               );
-       }
-
-       if (room_flags & QR_ANONOPT) {
-               scr_printf("Anonymous (Y/N)? ");
-               if (yesno() == 1)
-                       message.anonymous = 1;
-       }
-
-       /* If it's mail, we've got to check the validity of the recipient... */
-       if (!IsEmptyStr(message.recipient)) {
-               r = CtdlIPCPostMessage(ipc, 0, &subject_required,  &message, buf);
-               if (r / 100 != 2) {
-                       scr_printf("%s\n", buf);
-                       return (1);
-               }
-       }
-
-       /* Learn the number of the newest message in in the room, so we can
-        * tell upon saving whether someone else has posted too.
-        */
-       num_msgs = 0;
-       r = CtdlIPCGetMessages(ipc, LastMessages, 1, NULL, &msgarr, buf);
-       if (r / 100 != 1) {
-               scr_printf("%s\n", buf);
-       } else {
-               for (num_msgs = 0; msgarr[num_msgs]; num_msgs++)
-                       ;
-       }
-
-       /* Now compose the message... */
-       if (client_make_message(ipc, temp, message.recipient,
-          message.anonymous, 0, c, message.subject, subject_required) != 0) {
-           if (msgarr) free(msgarr);   
-               return (2);
-       }
-
-       /* Reopen the temp file that was created, so we can send it */
-       fp = fopen(temp, "r");
-
-       if (!fp || !(message.text = load_message_from_file(fp))) {
-               err_printf("*** Internal error while trying to save message!\n"
-                       "%s: %s\n",
-                       temp, strerror(errno));
-               unlink(temp);
-               return(errno);
-       }
-
-       if (fp) fclose(fp);
-
-       /* Break lines that are >1024 characters, otherwise the server
-        * will truncate them.
-        */
-       break_big_lines(message.text);
-
-       /* Transmit message to the server */
-       r = CtdlIPCPostMessage(ipc, 1, NULL, &message, buf);
-       if (r / 100 != 4) {
-               scr_printf("%s\n", buf);
-               return (1);
-       }
-
-       /* Yes, unlink it now, so it doesn't stick around if we crash */
-       unlink(temp);
-
-       if (num_msgs >= 1) highmsg = msgarr[num_msgs - 1];
-
-       if (msgarr) free(msgarr);
-       msgarr = NULL;
-       r = CtdlIPCGetMessages(ipc, NewMessages, 0, NULL, &msgarr, buf);
-       if (r / 100 != 1) {
-               scr_printf("%s\n", buf);
-       } else {
-               for (num_msgs = 0; msgarr[num_msgs]; num_msgs++)
-                       ;
-       }
-
-       /* get new highest message number in room to set lrp for goto... */
-       maxmsgnum = msgarr[num_msgs - 1];
-
-       /* now see if anyone else has posted in here */
-       b = (-1);
-       for (a = 0; a < num_msgs; ++a) {
-               if (msgarr[a] > highmsg) {
-                       ++b;
-               }
-       }
-       if (msgarr) free(msgarr);
-       msgarr = NULL;
-
-       /* In the Mail> room, this algorithm always counts one message
-        * higher than in public rooms, so we decrement it by one.
-        */
-       if (need_recp) {
-               --b;
-       }
-
-       if (b == 1) {
-               scr_printf("*** 1 additional message has been entered "
-                       "in this room by another user.\n");
-       }
-       else if (b > 1) {
-               scr_printf("*** %d additional messages have been entered "
-                       "in this room by other users.\n", b);
-       }
-    free(message.text);
-
-       return(0);
-}
-
-/*
- * Do editing on a quoted file
- */
-void process_quote(void)
-{
-       FILE *qfile, *tfile;
-       char buf[128];
-       int line, qstart, qend;
-
-       /* Unlink the second temp file as soon as it's opened, so it'll get
-        * deleted even if the program dies
-        */
-       qfile = fopen(temp2, "r");
-       unlink(temp2);
-
-       /* Display the quotable text with line numbers added */
-       line = 0;
-       if (fgets(buf, 128, qfile) == NULL) {
-               /* we're skipping a line here */
-       }
-       while (fgets(buf, 128, qfile) != NULL) {
-               scr_printf("%3d %s", ++line, buf);
-       }
-       scr_printf("Begin quoting at [1] : ");
-       ctdl_getline(buf, 4);
-       qstart = (buf[0] == 0) ? (1) : atoi(buf);
-       scr_printf("  End quoting at [%d] : ", line);
-       ctdl_getline(buf, 4);
-       qend = (buf[0] == 0) ? (line) : atoi(buf);
-       rewind(qfile);
-       line = 0;
-       if (fgets(buf, 128, qfile) == NULL) {
-               /* we're skipping a line here */
-       }
-       tfile = fopen(temp, "w");
-       while (fgets(buf, 128, qfile) != NULL) {
-               if ((++line >= qstart) && (line <= qend))
-                       fprintf(tfile, " >%s", buf);
-       }
-       fprintf(tfile, " \n");
-       fclose(qfile);
-       fclose(tfile);
-       chmod(temp, 0666);
-}
-
-
-
-/*
- * List the URL's which were embedded in the previous message
- */
-void list_urls(CtdlIPC *ipc)
-{
-       int i;
-       char cmd[SIZ];
-       int rv;
-
-       if (num_urls == 0) {
-               scr_printf("There were no URL's in the previous message.\n\n");
-               return;
-       }
-
-       for (i = 0; i < num_urls; ++i) {
-               scr_printf("%3d %s\n", i + 1, urls[i]);
-       }
-
-       if ((i = num_urls) != 1)
-               i = intprompt("Display which one", 1, 1, num_urls);
-
-       snprintf(cmd, sizeof cmd, rc_url_cmd, urls[i - 1]);
-       rv = system(cmd);
-       scr_printf("\n");
-}
-
-
-/*
- * Run image viewer in background
- */
-int do_image_view(const char *filename)
-{
-       char cmd[SIZ];
-       pid_t childpid;
-
-       snprintf(cmd, sizeof cmd, imagecmd, filename);
-       childpid = fork();
-       if (childpid < 0) {
-               unlink(filename);
-               return childpid;
-       }
-
-       if (childpid == 0) {
-               int retcode;
-               pid_t grandchildpid;
-
-               grandchildpid = fork();
-               if (grandchildpid < 0) {
-                       return grandchildpid;
-               }
-
-               if (grandchildpid == 0) {
-                       int nullfd;
-                       int outfd = -1;
-                       int errfd = -1;
-
-                       nullfd = open("/dev/null", O_WRONLY);
-                       if (nullfd > -1) {
-                               dup2(1, outfd);
-                               dup2(2, errfd);
-                               dup2(nullfd, 1);
-                               dup2(nullfd, 2);
-                       }
-                       retcode = system(cmd);
-                       if (nullfd > -1) {
-                               dup2(outfd, 1);
-                               dup2(errfd, 2);
-                               close(nullfd);
-                       }
-                       unlink(filename);
-                       exit(retcode);
-               }
-
-               if (grandchildpid > 0) {
-                       exit(0);
-               }
-       }
-
-       if (childpid > 0) {
-               int retcode;
-
-               waitpid(childpid, &retcode, 0);
-               return retcode;
-       }
-       
-       return -1;
-}
-
-
-/*
- * View an image attached to a message
- */
-void image_view(CtdlIPC *ipc, unsigned long msg)
-{
-       struct parts *ptr = last_message_parts;
-       char part[SIZ];
-       int found = 0;
-
-       /* Run through available parts */
-       for (ptr = last_message_parts; ptr; ptr = ptr->next) {
-               if ((!strcasecmp(ptr->disposition, "attachment")
-                  || !strcasecmp(ptr->disposition, "inline"))
-                  && !strncmp(ptr->mimetype, "image/", 6)) {
-                       found++;
-                       if (found == 1) {
-                               strcpy(part, ptr->number);
-                       }
-               }
-       }
-
-       while (found > 0) {
-               if (found > 1)
-                       strprompt("View which part (0 when done)", part, SIZ-1);
-               found = -found;
-               for (ptr = last_message_parts; ptr; ptr = ptr->next) {
-                       if ((!strcasecmp(ptr->disposition, "attachment")
-                          || !strcasecmp(ptr->disposition, "inline"))
-                          && !strncmp(ptr->mimetype, "image/", 6)
-                          && !strcasecmp(ptr->number, part)) {
-                               char tmp[PATH_MAX];
-                               char buf[SIZ];
-                               void *file = NULL; /* The downloaded file */
-                               int r;
-       
-                               /* view image */
-                               found = -found;
-                               r = CtdlIPCAttachmentDownload(ipc, msg, ptr->number, &file, progress, buf);
-                               if (r / 100 != 2) {
-                                       scr_printf("%s\n", buf);
-                               } else {
-                                       size_t len;
-       
-                                       len = (size_t)extract_long(buf, 0);
-                                       progress(ipc, len, len);
-                                       scr_flush();
-                                       CtdlMakeTempFileName(tmp, sizeof tmp);
-                                       strcat(tmp, ptr->filename);
-                                       save_buffer(file, len, tmp);
-                                       free(file);
-                                       do_image_view(tmp);
-                               }
-                               break;
-                       }
-               }
-               if (found == 1)
-                       break;
-       }
-}
-
-/*
- * Read the messages in the current room
- */
-void readmsgs(CtdlIPC *ipc,
-       enum MessageList c,             /* see listing in citadel_ipc.h */
-       enum MessageDirection rdir,     /* 1=Forward (-1)=Reverse */
-       int q           /* Number of msgs to read (if c==3) */
-) {
-       int a, b, e, f, g, start;
-       int savedpos;
-       int hold_sw = 0;
-       char arcflag = 0;
-       char quotflag = 0;
-       int hold_color = 0;
-       char prtfile[PATH_MAX];
-       char pagin;
-       char cmd[SIZ];
-       char targ[ROOMNAMELEN];
-       char filename[PATH_MAX];
-       char save_to[PATH_MAX];
-       void *attachment = NULL;        /* Downloaded attachment */
-       FILE *dest = NULL;              /* Alternate destination other than screen */
-       int r;                          /* IPC response code */
-       static int att_seq = 0;         /* Attachment download sequence number */
-       int rv = 0;                     /* silence the stupid warn_unused_result warnings */
-
-       if (c < 0)
-               b = (num_msgs - 1);
-       else
-               b = 0;
-
-       CtdlMakeTempFileName(prtfile, sizeof prtfile);
-
-       if (msg_arr) {
-               free(msg_arr);
-               msg_arr = NULL;
-       }
-       r = CtdlIPCGetMessages(ipc, c, q, NULL, &msg_arr, cmd);
-       if (r / 100 != 1) {
-               scr_printf("%s\n", cmd);
-       } else {
-               for (num_msgs = 0; msg_arr[num_msgs]; num_msgs++)
-                       ;
-       }
-
-       if (num_msgs == 0) {    /* TODO look at this later */
-               if (c == LastMessages) return;
-               scr_printf("*** There are no ");
-               if (c == NewMessages) scr_printf("new ");
-               if (c == OldMessages) scr_printf("old ");
-               scr_printf("messages in this room.\n");
-               return;
-       }
-
-       lines_printed = 0;
-
-       /* this loop cycles through each message... */
-       start = ((rdir == 1) ? 0 : (num_msgs - 1));
-       for (a = start; ((a < num_msgs) && (a >= 0)); a = a + rdir) {
-               while (msg_arr[a] == 0L) {
-                       a = a + rdir;
-                       if ((a == num_msgs) || (a == (-1)))
-                               return;
-               }
-
-RAGAIN:                pagin = ((arcflag == 0)
-                        && (quotflag == 0)
-                        && (userflags & US_PAGINATOR)) ? 1 : 0;
-
-               /* If we're doing a quote, set the screenwidth to 72 */
-               if (quotflag) {
-                       hold_sw = screenwidth;
-                       screenwidth = 72;
-               }
-
-               /* If printing or archiving, set the screenwidth to 80 */
-               if (arcflag) {
-                       hold_sw = screenwidth;
-                       screenwidth = 80;
-               }
-
-               /* clear parts list */
-               free_parts(last_message_parts);
-               last_message_parts = NULL;
-
-               /* now read the message... */
-               e = read_message(ipc, msg_arr[a], pagin, dest);
-
-               /* ...and set the screenwidth back if we have to */
-               if ((quotflag) || (arcflag)) {
-                       screenwidth = hold_sw;
-               }
-RMSGREAD:      scr_flush();
-               highest_msg_read = msg_arr[a];
-               if (quotflag) {
-                       fclose(dest);
-                       dest = NULL;
-                       quotflag = 0;
-                       enable_color = hold_color;
-                       process_quote();
-                       e = 'r';
-                       goto DONE_QUOTING;
-               }
-               if (arcflag) {
-                       fclose(dest);
-                       dest = NULL;
-                       arcflag = 0;
-                       enable_color = hold_color;
-                       f = fork();
-                       if (f == 0) {
-                               if (freopen(prtfile, "r", stdin) == NULL) {
-                                       /* we probably should handle the error condition here */
-                               }
-                               screen_reset();
-                               stty_ctdl(SB_RESTORE);
-                               ka_system(printcmd);
-                               stty_ctdl(SB_NO_INTR);
-                               screen_set();
-                               unlink(prtfile);
-                               exit(0);
-                       }
-                       if (f > 0)
-                               do {
-                                       g = wait(NULL);
-                               } while ((g != f) && (g >= 0));
-                       scr_printf("Message printed.\n");
-               }
-               if (rc_alt_semantics && c == 1) {
-                       char buf[SIZ];
-
-                       r = CtdlIPCSetMessageSeen(ipc, msg_arr[a], 1, buf);
-               }
-               if (e == SIGQUIT)
-                       return;
-               if (((userflags & US_NOPROMPT) || (e == SIGINT))
-                       && (((room_flags & QR_MAILBOX) == 0)
-                       || (rc_force_mail_prompts == 0))) {
-                       e = 'n';
-               } else {
-                       color(DIM_WHITE);
-                       scr_printf("(");
-                       color(BRIGHT_WHITE);
-                       scr_printf("%d", num_msgs - a - 1);
-                       color(DIM_WHITE);
-                       scr_printf(") ");
-
-                       keyopt("<B>ack <A>gain <R>eply reply<Q>uoted <N>ext <S>top ");
-                       if (rc_url_cmd[0] && num_urls)
-                               keyopt("<U>RLview ");
-                       if (has_images > 0 && !IsEmptyStr(imagecmd))
-                               keyopt("<I>mages ");
-                       keyopt("<?>help -> ");
-
-                       do {
-                               lines_printed = 2;
-                               e = (inkey() & 127);
-                               e = tolower(e);
-/* return key same as <N> */ if (e == 10)
-                                       e = 'n';
-/* space key same as <N> */ if (e == 32)
-                                       e = 'n';
-/* del/move for aides only */
-                                   if (  (!is_room_aide)
-                                      && ((room_flags & QR_MAILBOX) == 0)
-                                      && ((room_flags2 & QR2_COLLABDEL) == 0)
-                                      ) {
-                                       if ((e == 'd') || (e == 'm'))
-                                               e = 0;
-                               }
-/* print only if available */
-                               if ((e == 'p') && (IsEmptyStr(printcmd)))
-                                       e = 0;
-/* can't file if not allowed */
-                                   if ((e == 'f')
-                                       && (rc_allow_attachments == 0))
-                                       e = 0;
-/* link only if browser avail*/
-                                   if ((e == 'u')
-                                       && (IsEmptyStr(rc_url_cmd)))
-                                       e = 0;
-                               if ((e == 'i')
-                                       && (IsEmptyStr(imagecmd) || !has_images))
-                                       e = 0;
-                       } while ((e != 'a') && (e != 'n') && (e != 's')
-                                && (e != 'd') && (e != 'm') && (e != 'p')
-                                && (e != 'q') && (e != 'b') && (e != 'h')
-                                && (e != 'r') && (e != 'f') && (e != '?')
-                                && (e != 'u') && (e != 'c') && (e != 'y')
-                                && (e != 'i') && (e != 'o') );
-                       switch (e) {
-                       case 's':
-                               scr_printf("Stop");
-                               break;
-                       case 'a':
-                               scr_printf("Again");
-                               break;
-                       case 'd':
-                               scr_printf("Delete");
-                               break;
-                       case 'm':
-                               scr_printf("Move");
-                               break;
-                       case 'c':
-                               scr_printf("Copy");
-                               break;
-                       case 'n':
-                               scr_printf("Next");
-                               break;
-                       case 'p':
-                               scr_printf("Print");
-                               break;
-                       case 'q':
-                               scr_printf("reply Quoted");
-                               break;
-                       case 'b':
-                               scr_printf("Back");
-                               break;
-                       case 'h':
-                               scr_printf("Header");
-                               break;
-                       case 'r':
-                               scr_printf("Reply");
-                               break;
-                       case 'o':
-                               scr_printf("Open attachments");
-                               break;
-                       case 'f':
-                               scr_printf("File");
-                               break;
-                       case 'u':
-                               scr_printf("URL's");
-                               break;
-                       case 'y':
-                               scr_printf("mY next");
-                               break;
-                       case 'i':
-                               break;
-                       case '?':
-                               scr_printf("? <help>");
-                               break;
-                       }
-                       if (userflags & US_DISAPPEAR || e == 'i')
-                               scr_printf("\r%79s\r", "");
-                       else
-                               scr_printf("\n");
-                       scr_flush();
-               }
-DONE_QUOTING:  switch (e) {
-               case '?':
-                       scr_printf("Options available here:\n"
-                               " ?  Help (prints this message)\n"
-                               " S  Stop reading immediately\n"
-                               " A  Again (repeats last message)\n"
-                               " N  Next (continue with next message)\n"
-                               " Y  My Next (continue with next message you authored)\n"
-                               " B  Back (go back to previous message)\n");
-                       if (  (is_room_aide)
-                          || (room_flags & QR_MAILBOX)
-                          || (room_flags2 & QR2_COLLABDEL)
-                       ) {
-                               scr_printf(" D  Delete this message\n"
-                                       " M  Move message to another room\n");
-                       }
-                       scr_printf(" C  Copy message to another room\n");
-                       if (!IsEmptyStr(printcmd))
-                               scr_printf(" P  Print this message\n");
-                       scr_printf(
-                               " Q  Reply to this message, quoting portions of it\n"
-                               " H  Headers (display message headers only)\n");
-                       if (is_mail)
-                               scr_printf(" R  Reply to this message\n");
-                       if (rc_allow_attachments) {
-                               scr_printf(" O  (Open attachments)\n");
-                               scr_printf(" F  (save attachments to a File)\n");
-                       }
-                       if (!IsEmptyStr(rc_url_cmd))
-                               scr_printf(" U  (list URL's for display)\n");
-                       if (!IsEmptyStr(imagecmd) && has_images > 0)
-                               scr_printf(" I  Image viewer\n");
-                       scr_printf("\n");
-                       goto RMSGREAD;
-               case 'p':
-                       scr_flush();
-                       dest = fopen(prtfile, "w");
-                       arcflag = 1;
-                       hold_color = enable_color;
-                       enable_color = 0;
-                       goto RAGAIN;
-               case 'q':
-                       scr_flush();
-                       dest = fopen(temp2, "w");
-                       quotflag = 1;
-                       hold_color = enable_color;
-                       enable_color = 0;
-                       goto RAGAIN;
-               case 's':
-                       return;
-               case 'a':
-                       goto RAGAIN;
-               case 'b':
-                       a = a - (rdir * 2);
-                       break;
-               case 'm':
-               case 'c':
-                       newprompt("Enter target room: ",
-                                 targ, ROOMNAMELEN - 1);
-                       if (!IsEmptyStr(targ)) {
-                               r = CtdlIPCMoveMessage(ipc, (e == 'c' ? 1 : 0),
-                                                      msg_arr[a], targ, cmd);
-                               scr_printf("%s\n", cmd);
-                               if (r / 100 == 2)
-                                       msg_arr[a] = 0L;
-                       } else {
-                               goto RMSGREAD;
-                       }
-                       if (r / 100 != 2)       /* r will be init'ed, FIXME */
-                               goto RMSGREAD;  /* the logic here sucks */
-                       break;
-               case 'o':
-               case 'f':
-                       newprompt("Which section? ", filename, ((sizeof filename) - 1));
-                       r = CtdlIPCAttachmentDownload(ipc, msg_arr[a],
-                                       filename, &attachment, progress, cmd);
-                       if (r / 100 != 2) {
-                               scr_printf("%s\n", cmd);
-                       } else {
-                               extract_token(filename, cmd, 2, '|', sizeof filename);
-                               /*
-                                * Part 1 won't have a filename; use the
-                                * subject of the message instead. IO
-                                */
-                               if (IsEmptyStr(filename)) {
-                                       strcpy(filename, reply_subject);
-                               }
-                               if (e == 'o') {         /* open attachment */
-                                       mkdir(tempdir, 0700);
-                                       snprintf(save_to, sizeof save_to, "%s/%04x.%s",
-                                               tempdir,
-                                               ++att_seq,
-                                               filename);
-                                       save_buffer(attachment, extract_unsigned_long(cmd, 0), save_to);
-                                       snprintf(cmd, sizeof cmd, rc_open_cmd, save_to);
-                                       rv = system(cmd);
-                               }
-                               else {                  /* save attachment to disk */
-                                       destination_directory(save_to, filename);
-                                       save_buffer(attachment, extract_unsigned_long(cmd, 0), save_to);
-                               }
-                       }
-                       if (attachment) {
-                               free(attachment);
-                               attachment = NULL;
-                       }
-                       goto RMSGREAD;
-               case 'd':
-                       scr_printf("*** Delete this message? ");
-                       if (yesno() == 1) {
-                               r = CtdlIPCDeleteMessage(ipc, msg_arr[a], cmd);
-                               scr_printf("%s\n", cmd);
-                               if (r / 100 == 2)
-                                       msg_arr[a] = 0L;
-                       } else {
-                               goto RMSGREAD;
-                       }
-                       break;
-               case 'h':
-                       read_message(ipc, msg_arr[a], READ_HEADER, NULL);
-                       goto RMSGREAD;
-               case 'r':
-                       savedpos = num_msgs;
-                       entmsg(ipc, 1, ((userflags & US_EXTEDIT) ? 2 : 0), 0);
-                       num_msgs = savedpos;
-                       goto RMSGREAD;
-               case 'u':
-                       list_urls(ipc);
-                       goto RMSGREAD;
-               case 'i':
-                       image_view(ipc, msg_arr[a]);
-                       goto RMSGREAD;
-           case 'y':
-          { /* hack hack hack */
-            /* find the next message by me, stay here if we find nothing */
-            int finda;
-            int lasta = a;
-            for (finda = (a + rdir); ((finda < num_msgs) && (finda >= 0)); finda += rdir)
-              {
-               /* This is repetitively dumb, but that's what computers are for.
-                  We have to load up messages until we find one by us */
-               char buf[SIZ];
-               int founda = 0;
-               struct ctdlipcmessage *msg = NULL;
-                
-               /* read the header so we can get 'from=' */
-               r = CtdlIPCGetSingleMessage(ipc, msg_arr[finda], 1, 0, &msg, buf);
-               if (!strncasecmp(msg->author, fullname, sizeof(fullname))) {
-                       a = lasta; /* meesa current */
-                       founda = 1;
-               }
-
-               free(msg);
-
-               if (founda)
-                       break; /* for */
-               lasta = finda; /* keep one behind or we skip on the reentrance to the for */
-              } /* for */
-          } /* case 'y' */
-      } /* switch */
-       }                       /* end for loop */
-}                              /* end read routine */
-
-
-
-
-/*
- * View and edit a system message
- */
-void edit_system_message(CtdlIPC *ipc, char *which_message)
-{
-       char desc[SIZ];
-       char read_cmd[SIZ];
-       char write_cmd[SIZ];
-
-       snprintf(desc, sizeof desc, "system message '%s'", which_message);
-       snprintf(read_cmd, sizeof read_cmd, "MESG %s", which_message);
-       snprintf(write_cmd, sizeof write_cmd, "EMSG %s", which_message);
-       do_edit(ipc, desc, read_cmd, "NOOP", write_cmd);
-}
-
-
-
-
-/*
- * Verify the message base
- */
-void check_message_base(CtdlIPC *ipc)
-{
-       char buf[SIZ];
-       char *transcript = NULL;
-       int r;          /* IPC response code */
-
-       scr_printf
-           ("Please read the documentation before running this command.\n"
-           "Having done so, do you still want to check the message base? ");
-       if (yesno() == 0)
-               return;
-
-       r = CtdlIPCMessageBaseCheck(ipc, &transcript, buf);
-       if (r / 100 != 1) {
-               scr_printf("%s\n", buf);
-               return;
-       }
-
-       while (transcript && !IsEmptyStr(transcript)) {
-               lines_printed = 1;
-               extract_token(buf, transcript, 0, '\n', sizeof buf);
-               remove_token(transcript, 0, '\n');
-               pprintf("%s\n", buf);
-       }
-       if (transcript) free(transcript);
-       return;
-}
-
-
-/*
- * Loads the contents of a file into memory.  Caller must free the allocated
- * memory.
- */
-char *load_message_from_file(FILE *src)
-{
-       size_t i;
-       size_t got = 0;
-       char *dest = NULL;
-
-       fseek(src, 0, SEEK_END);
-       i = ftell(src);
-       rewind(src);
-
-       dest = (char *)calloc(1, i + 1);
-       if (!dest)
-               return NULL;
-
-       while (got < i) {
-               size_t g;
-
-               g = fread(dest + got, 1, i - got, src);
-               got += g;
-               if (g < i - got) {
-                       /* Interrupted system call, keep going */
-                       if (errno == EINTR)
-                               continue;
-                       /* At this point we have either EOF or error */
-                       i = got;
-                       break;
-               }
-               dest[i] = 0;
-       }
-
-       return dest;
-}
diff --git a/citadel/messages.h b/citadel/messages.h
deleted file mode 100644 (file)
index 5b3e230..0000000
+++ /dev/null
@@ -1,24 +0,0 @@
-/* $Id$ */
-
-#define MAXURLS                50      /* Max embedded URL's per message */
-extern int num_urls;
-extern char urls[MAXURLS][SIZ];
-
-int ka_system(char *shc);
-int entmsg(CtdlIPC *ipc, int is_reply, int c, int masquerade);
-void readmsgs(CtdlIPC *ipc, enum MessageList c, enum MessageDirection rdir, int q);
-void edit_system_message(CtdlIPC *ipc, char *which_message);
-pid_t ka_wait(int *kstatus);
-void list_urls(CtdlIPC *ipc);
-void check_message_base(CtdlIPC *ipc);
-int client_make_message(CtdlIPC *ipc,
-                                               char *filename,         /* temporary file name */
-                                               char *recipient,        /* NULL if it's not mail */
-                                               int anon_type,          /* see MES_ types in header file */
-                                               int format_type,
-                                               int mode,
-                                               char *subject,
-                                               int subject_required);
-void citedit(CtdlIPC *ipc, FILE *);
-char *load_message_from_file(FILE *src);
-int file_checksum(char *filename);
diff --git a/citadel/msgform.c b/citadel/msgform.c
deleted file mode 100644 (file)
index c17a257..0000000
+++ /dev/null
@@ -1,211 +0,0 @@
-/*
- * $Id$
- * 
- * This is simply a filter that converts Citadel binary message format
- * to readable, formatted output.
- * 
- * If the -q (quiet or qwk) flag is used, only the message text prints, and
- * then it stops at the end of the first message it prints.
- * 
- * This utility isn't very useful anymore.
- *
- */
-
-#include "sysdep.h"
-#include <stdlib.h>
-#include <unistd.h>
-#include <fcntl.h>
-#include <stdio.h>
-#include <ctype.h>
-#include <string.h>
-
-#if TIME_WITH_SYS_TIME
-# include <sys/time.h>
-# include <time.h>
-#else
-# if HAVE_SYS_TIME_H
-#  include <sys/time.h>
-# else
-#  include <time.h>
-# endif
-#endif
-
-#include <errno.h>
-#include <libcitadel.h>
-
-int qwk = 0;
-
-int fpgetfield(FILE * fp, char *string);
-int fmout(int width, FILE * fp);
-
-
-#ifndef HAVE_STRERROR
-/*
- * replacement strerror() for systems that don't have it
- */
-char *strerror(int e)
-{
-       static char buf[32];
-
-       snprintf(buf, sizeof buf, "errno = %d", e);
-       return (buf);
-}
-#endif
-
-int main(int argc, char **argv)
-{
-       struct tm tm;
-       int a, b, e, mtype, aflag;
-       char bbb[1024];
-       char subject[1024];
-       FILE *fp;
-       time_t now;
-
-       if (argc == 2)
-               if (!strcmp(argv[1], "-q"))
-                       qwk = 1;
-       fp = stdin;
-       if (argc == 2)
-               if (strcmp(argv[1], "-q")) {
-                       fp = fopen(argv[1], "r");
-                       if (fp == NULL) {
-                               fprintf(stderr, "%s: cannot open %s: %s\n",
-                                       argv[0], argv[1], strerror(errno));
-                               exit(errno);
-                       }
-               }
-
-TOP:   do {
-               e = getc(fp);
-               if (e < 0)
-                       exit(0);
-       } while (e != 255);
-       strcpy(subject, "");
-       mtype = getc(fp);
-       aflag = getc(fp);
-       if (qwk == 0)
-               printf(" ");
-
-       do {
-               b = getc(fp);
-               if (b == 'M') {
-                       if (qwk == 0) {
-                               printf("\n");
-                               if (!IsEmptyStr(subject))
-                                       printf("Subject: %s\n", subject);
-                       }
-                       if (aflag != 1)
-                               fmout(80, fp);
-                       else
-                               while (a = getc(fp), a > 0) {
-                                       if (a == 13)
-                                               putc(10, stdout);
-                                       else
-                                               putc(a, stdout);
-                               }
-               }
-               if ((b != 'M') && (b > 0))
-                       fpgetfield(fp, bbb);
-               if (b == 'U')
-                       strcpy(subject, bbb);
-               if (qwk == 0) {
-                       if (b == 'A')
-                               printf("from %s ", bbb);
-                       if (b == 'N')
-                               printf("@%s ", bbb);
-                       if (b == 'O')
-                               printf("in %s> ", bbb);
-                       if (b == 'R')
-                               printf("to %s ", bbb);
-                       if (b == 'T') {
-                               now = atol(bbb);
-                               localtime_r(&now, &tm);
-                               strcpy(bbb, asctime(&tm));
-                               bbb[strlen(bbb) - 1] = 0;
-                               printf("%s ", &bbb[4]);
-                       }
-               }
-       } while ((b != 'M') && (b > 0));
-       if (qwk == 0)
-               printf("\n");
-       if (qwk == 1)
-               exit(0);
-       goto TOP;
-}
-
-int fpgetfield(FILE * fp, char *string)
-
-{                              /* level-2 break out next null-terminated string */
-       int a, b;
-       strcpy(string, "");
-       a = 0;
-       do {
-               b = getc(fp);
-               if (b < 1) {
-                       string[a] = 0;
-                       return (0);
-               }
-               string[a] = b;
-               ++a;
-       } while (b != 0);
-       return (0);
-}
-
-int fmout(int width, FILE * fp)
-{
-       int a, b, c;
-       int real = 0;
-       int old = 0;
-       char aaa[140];
-
-       strcpy(aaa, "");
-       old = 255;
-       c = 1;                  /* c is the current pos */
-FMTA:  old = real;
-       a = getc(fp);
-       real = a;
-       if (a <= 0)
-               goto FMTEND;
-
-       if (((a == 13) || (a == 10)) && (old != 13) && (old != 10))
-               a = 32;
-       if (((old == 13) || (old == 10)) && (isspace(real))) {
-               printf("\n");
-               c = 1;
-       }
-       if (a > 126)
-               goto FMTA;
-
-       if (a > 32) {
-               if (((strlen(aaa) + c) > (width - 5))
-                   && (strlen(aaa) > (width - 5))) {
-                       printf("\n%s", aaa);
-                       c = strlen(aaa);
-                       aaa[0] = 0;
-               }
-               b = strlen(aaa);
-               aaa[b] = a;
-               aaa[b + 1] = 0;
-       }
-       if (a == 32) {
-               if ((strlen(aaa) + c) > (width - 5)) {
-                       printf("\n");
-                       c = 1;
-               }
-               printf("%s ", aaa);
-               ++c;
-               c = c + strlen(aaa);
-               strcpy(aaa, "");
-               goto FMTA;
-       }
-       if ((a == 13) || (a == 10)) {
-               printf("%s\n", aaa);
-               c = 1;
-               strcpy(aaa, "");
-               goto FMTA;
-       }
-       goto FMTA;
-
-FMTEND:        printf("\n");
-       return (0);
-}
diff --git a/citadel/rooms.c b/citadel/rooms.c
deleted file mode 100644 (file)
index 5fbc76c..0000000
+++ /dev/null
@@ -1,1406 +0,0 @@
-/*
- * $Id$
- *
- * 
- * Client-side functions which perform room operations
- *
- */
-
-#include "sysdep.h"
-#include <stdlib.h>
-#include <unistd.h>
-#include <fcntl.h>
-#include <stdio.h>
-#include <ctype.h>
-#include <string.h>
-#include <signal.h>
-#include <sys/types.h>
-#include <sys/stat.h>
-#include <sys/wait.h>
-#include <errno.h>
-#include <stdarg.h>
-#include <libcitadel.h>
-#include "citadel.h"
-#include "citadel_ipc.h"
-#include "citadel_decls.h"
-#include "rooms.h"
-#include "commands.h"
-#include "messages.h"
-#ifndef HAVE_SNPRINTF
-#include "snprintf.h"
-#endif
-#include "screen.h"
-#include "citadel_dirs.h"
-
-#define IFNEXPERT if ((userflags&US_EXPERT)==0)
-
-
-void stty_ctdl(int cmd);
-void dotgoto(CtdlIPC *ipc, char *towhere, int display_name, int fromungoto);
-void progress(CtdlIPC* ipc, unsigned long curr, unsigned long cmax);
-int pattern(char *search, char *patn);
-int file_checksum(char *filename);
-int nukedir(char *dirname);
-
-extern unsigned room_flags;
-extern char room_name[];
-extern char temp[];
-extern char tempdir[];
-extern int editor_pid;
-extern int screenwidth;
-extern int screenheight;
-extern char fullname[];
-extern char sigcaught;
-extern char floor_mode;
-extern char curr_floor;
-
-
-extern int ugnum;
-extern long uglsn;
-extern char *uglist[];
-extern long uglistlsn[];
-extern int uglistsize;
-
-extern char floorlist[128][SIZ];
-
-
-void load_floorlist(CtdlIPC *ipc)
-{
-       int a;
-       char buf[SIZ];
-       char *listing = NULL;
-       int r;                  /* IPC response code */
-
-       for (a = 0; a < 128; ++a)
-               floorlist[a][0] = 0;
-
-       r = CtdlIPCFloorListing(ipc, &listing, buf);
-       if (r / 100 != 1) {
-               strcpy(floorlist[0], "Main Floor");
-               return;
-       }
-       while (*listing && !IsEmptyStr(listing)) {
-               extract_token(buf, listing, 0, '\n', sizeof buf);
-               remove_token(listing, 0, '\n');
-               extract_token(floorlist[extract_int(buf, 0)], buf, 1, '|', SIZ);
-       }
-       free(listing);
-}
-
-
-void room_tree_list(struct ctdlroomlisting *rp)
-{
-       static int c = 0;
-       char rmname[ROOMNAMELEN];
-       int f;
-
-       if (rp == NULL) {
-               c = 1;
-               return;
-       }
-
-       if (rp->lnext != NULL) {
-               room_tree_list(rp->lnext);
-       }
-
-       if (sigcaught == 0) {
-               strcpy(rmname, rp->rlname);
-               f = rp->rlflags;
-               if ((c + strlen(rmname) + 4) > screenwidth) {
-
-                       /* line break, check the paginator */
-                       pprintf("\n");
-                       c = 1;
-               }
-               if (f & QR_MAILBOX) {
-                       color(BRIGHT_YELLOW);
-               } else if (f & QR_PRIVATE) {
-                       color(BRIGHT_RED);
-               } else {
-                       color(DIM_WHITE);
-               }
-               pprintf("%s", rmname);
-               if ((f & QR_DIRECTORY) && (f & QR_NETWORK))
-                       pprintf("}  ");
-               else if (f & QR_DIRECTORY)
-                       pprintf("]  ");
-               else if (f & QR_NETWORK)
-                       pprintf(")  ");
-               else
-                       pprintf(">  ");
-               c = c + strlen(rmname) + 3;
-       }
-
-       if (rp->rnext != NULL) {
-               room_tree_list(rp->rnext);
-       }
-
-       free(rp);
-}
-
-
-/* 
- * Room ordering stuff (compare first by floor, then by order)
- */
-int rordercmp(struct ctdlroomlisting *r1, struct ctdlroomlisting *r2)
-{
-       if ((r1 == NULL) && (r2 == NULL))
-               return (0);
-       if (r1 == NULL)
-               return (-1);
-       if (r2 == NULL)
-               return (1);
-       if (r1->rlfloor < r2->rlfloor)
-               return (-1);
-       if (r1->rlfloor > r2->rlfloor)
-               return (1);
-       if (r1->rlorder < r2->rlorder)
-               return (-1);
-       if (r1->rlorder > r2->rlorder)
-               return (1);
-       return (0);
-}
-
-
-/*
- * Common code for all room listings
- */
-static void listrms(struct march *listing, int new_only, int floor_only, unsigned int flags, char *match)
-{
-       struct march *mptr;
-       struct ctdlroomlisting *rl = NULL;
-       struct ctdlroomlisting *rp;
-       struct ctdlroomlisting *rs;
-       int list_it;
-
-       for (mptr = listing; mptr != NULL; mptr = mptr->next) {
-               list_it = 1;
-
-               if ( (new_only == LISTRMS_NEW_ONLY)
-                  && ((mptr->march_access & UA_HASNEWMSGS) == 0)) 
-                       list_it = 0;
-
-               if ( (new_only == LISTRMS_OLD_ONLY)
-                  && ((mptr->march_access & UA_HASNEWMSGS) != 0)) 
-                       list_it = 0;
-
-               if ( (floor_only >= 0)
-                  && (mptr->march_floor != floor_only))
-                       list_it = 0;
-
-               if (flags && (mptr->march_flags & flags) == 0)
-                   list_it = 0;
-
-           if (match && (pattern(mptr->march_name, match) == -1))
-                       list_it = 0;
-
-               if (list_it) {
-                       rp = malloc(sizeof(struct ctdlroomlisting));
-                       strncpy(rp->rlname, mptr->march_name, ROOMNAMELEN);
-                       rp->rlflags = mptr->march_flags;
-                       rp->rlfloor = mptr->march_floor;
-                       rp->rlorder = mptr->march_order;
-                       rp->lnext = NULL;
-                       rp->rnext = NULL;
-       
-                       rs = rl;
-                       if (rl == NULL) {
-                               rl = rp;
-                       } else {
-                               while (rp != NULL) {
-                                       if (rordercmp(rp, rs) < 0) {
-                                               if (rs->lnext == NULL) {
-                                                       rs->lnext = rp;
-                                                       rp = NULL;
-                                               } else {
-                                                       rs = rs->lnext;
-                                               }
-                                       } else {
-                                               if (rs->rnext == NULL) {
-                                                       rs->rnext = rp;
-                                                       rp = NULL;
-                                               } else {
-                                                       rs = rs->rnext;
-                                               }
-                                       }
-                               }
-                       }
-               }
-       }
-
-       room_tree_list(NULL);
-       room_tree_list(rl);
-       color(DIM_WHITE);
-}
-
-
-void list_other_floors(void)
-{
-       int a, c;
-
-       c = 1;
-       for (a = 0; a < 128; ++a) {
-               if ((strlen(floorlist[a]) > 0) && (a != curr_floor)) {
-                       if ((c + strlen(floorlist[a]) + 4) > screenwidth) {
-                               pprintf("\n");
-                               c = 1;
-                       }
-                       pprintf("%s:  ", floorlist[a]);
-                       c = c + strlen(floorlist[a]) + 3;
-               }
-       }
-}
-
-
-/*
- * List known rooms.  kn_floor_mode should be set to 0 for a 'flat' listing,
- * 1 to list rooms on the current floor, or 2 to list rooms on all floors.
- */
-void knrooms(CtdlIPC *ipc, int kn_floor_mode)
-{
-       int a;
-       struct march *listing = NULL;
-       struct march *mptr;
-       int r;          /* IPC response code */
-       char buf[SIZ];
-
-
-       /* Ask the server for a room list */
-       r = CtdlIPCKnownRooms(ipc, SubscribedRooms, (-1), &listing, buf);
-       if (r / 100 != 1) {
-               listing = NULL;
-       }
-
-       load_floorlist(ipc);
-
-
-       if (kn_floor_mode == 0) {
-               color(BRIGHT_CYAN);
-               pprintf("\n   Rooms with unread messages:\n");
-               listrms(listing, LISTRMS_NEW_ONLY, -1, 0, NULL);
-               color(BRIGHT_CYAN);
-               pprintf("\n\n   No unseen messages in:\n");
-               listrms(listing, LISTRMS_OLD_ONLY, -1, 0, NULL);
-               pprintf("\n");
-       }
-
-       if (kn_floor_mode == 1) {
-               color(BRIGHT_CYAN);
-               pprintf("\n   Rooms with unread messages on %s:\n",
-                       floorlist[(int) curr_floor]);
-               listrms(listing, LISTRMS_NEW_ONLY, curr_floor, 0, NULL);
-               color(BRIGHT_CYAN);
-               pprintf("\n\n   Rooms with no new messages on %s:\n",
-                       floorlist[(int) curr_floor]);
-               listrms(listing, LISTRMS_OLD_ONLY, curr_floor, 0, NULL);
-               color(BRIGHT_CYAN);
-               pprintf("\n\n   Other floors:\n");
-               list_other_floors();
-               pprintf("\n");
-       }
-
-       if (kn_floor_mode == 2) {
-               for (a = 0; a < 128; ++a) {
-                       if (floorlist[a][0] != 0) {
-                               color(BRIGHT_CYAN);
-                               pprintf("\n   Rooms on %s:\n",
-                                       floorlist[a]);
-                               listrms(listing, LISTRMS_ALL, a, 0, NULL);
-                               pprintf("\n");
-                       }
-               }
-       }
-
-       /* Free the room list */
-       while (listing) {
-               mptr = listing->next;
-               free(listing);
-               listing = mptr;
-       };
-
-       color(DIM_WHITE);
-       IFNEXPERT hit_any_key(ipc);
-}
-
-
-void listzrooms(CtdlIPC *ipc)
-{                              /* list public forgotten rooms */
-       struct march *listing = NULL;
-       struct march *mptr;
-       int r;          /* IPC response code */
-       char buf[SIZ];
-
-
-       /* Ask the server for a room list */
-       r = CtdlIPCKnownRooms(ipc, UnsubscribedRooms, (-1), &listing, buf);
-       if (r / 100 != 1) {
-               listing = NULL;
-       }
-
-       color(BRIGHT_CYAN);
-       pprintf("\n   Forgotten public rooms:\n");
-       listrms(listing, LISTRMS_ALL, -1, 0, NULL);
-       pprintf("\n");
-
-       /* Free the room list */
-       while (listing) {
-               mptr = listing->next;
-               free(listing);
-               listing = mptr;
-       };
-
-       color(DIM_WHITE);
-       IFNEXPERT hit_any_key(ipc);
-}
-
-void dotknown(CtdlIPC *ipc, int what, char *match)
-{                              /* list rooms according to attribute */
-       struct march *listing = NULL;
-       struct march *mptr;
-       int r;          /* IPC response code */
-       char buf[SIZ];
-
-       /* Ask the server for a room list */
-       r = CtdlIPCKnownRooms(ipc, AllAccessibleRooms, (-1), &listing, buf);
-       if (r / 100 != 1) {
-               listing = NULL;
-       }
-
-       color(BRIGHT_CYAN);
-
-       switch (what) {
-    case 0:
-       pprintf("\n   Anonymous rooms:\n");
-           listrms(listing, LISTRMS_ALL, -1, QR_ANONONLY|QR_ANONOPT, NULL);
-       pprintf("\n");
-               break;
-    case 1:
-       pprintf("\n   Directory rooms:\n");
-           listrms(listing, LISTRMS_ALL, -1, QR_DIRECTORY, NULL);
-       pprintf("\n");
-               break;
-    case 2:
-       pprintf("\n   Matching \"%s\" rooms:\n", match);
-           listrms(listing, LISTRMS_ALL, -1, 0, match);
-       pprintf("\n");
-               break;
-    case 3:
-       pprintf("\n   Preferred only rooms:\n");
-           listrms(listing, LISTRMS_ALL, -1, QR_PREFONLY, NULL);
-       pprintf("\n");
-               break;
-    case 4:
-       pprintf("\n   Private rooms:\n");
-           listrms(listing, LISTRMS_ALL, -1, QR_PRIVATE, NULL);
-       pprintf("\n");
-               break;
-    case 5:
-       pprintf("\n   Read only rooms:\n");
-           listrms(listing, LISTRMS_ALL, -1, QR_READONLY, NULL);
-       pprintf("\n");
-               break;
-    case 6:
-       pprintf("\n   Shared rooms:\n");
-           listrms(listing, LISTRMS_ALL, -1, QR_NETWORK, NULL);
-       pprintf("\n");
-               break;
-       }
-
-       /* Free the room list */
-       while (listing) {
-               mptr = listing->next;
-               free(listing);
-               listing = mptr;
-       };
-
-       color(DIM_WHITE);
-       IFNEXPERT hit_any_key(ipc);
-}
-
-
-int set_room_attr(CtdlIPC *ipc, unsigned int ibuf, char *prompt, unsigned int sbit)
-{
-       int a;
-
-       a = boolprompt(prompt, (ibuf & sbit));
-       ibuf = (ibuf | sbit);
-       if (!a) {
-               ibuf = (ibuf ^ sbit);
-       }
-       return (ibuf);
-}
-
-
-
-/*
- * Select a floor (used in several commands)
- * The supplied argument is the 'default' floor number.
- * This function returns the selected floor number.
- */
-int select_floor(CtdlIPC *ipc, int rfloor)
-{
-       int a, newfloor;
-       char floorstr[SIZ];
-
-       if (floor_mode == 1) {
-               if (floorlist[(int) curr_floor][0] == 0) {
-                       load_floorlist(ipc);
-               }
-
-               do {
-                       newfloor = (-1);
-                       safestrncpy(floorstr, floorlist[rfloor],
-                                   sizeof floorstr);
-                       strprompt("Which floor", floorstr, 255);
-                       for (a = 0; a < 128; ++a) {
-                               if (!strcasecmp
-                                   (floorstr, &floorlist[a][0]))
-                                       newfloor = a;
-                               if ((newfloor < 0)
-                                   &&
-                                   (!strncasecmp
-                                    (floorstr, &floorlist[a][0],
-                                     strlen(floorstr))))
-                                       newfloor = a;
-                               if ((newfloor < 0)
-                                   && (pattern(&floorlist[a][0], floorstr)
-                                       >= 0))
-                                       newfloor = a;
-                       }
-                       if (newfloor < 0) {
-                               scr_printf("\n One of:\n");
-                               for (a = 0; a < 128; ++a) {
-                                       if (floorlist[a][0] != 0) {
-                                               scr_printf("%s\n",
-                                                      &floorlist[a][0]);
-                                       }
-                               }
-                       }
-               } while (newfloor < 0);
-               return (newfloor);
-       }
-
-       else {
-               scr_printf("Floor selection bypassed because you have "
-                       "floor mode disabled.\n");
-       }
-
-       return (rfloor);
-}
-
-
-
-
-/*
- * .<A>ide <E>dit room
- */
-void editthisroom(CtdlIPC *ipc)
-{
-       int rbump = 0;
-       char raide[USERNAME_SIZE];
-       char buf[SIZ];
-       struct ctdlroom *attr = NULL;
-       struct ExpirePolicy *eptr = NULL;
-       int r;                          /* IPC response code */
-
-       /* Fetch the existing room config */
-       r = CtdlIPCGetRoomAttributes(ipc, &attr, buf);
-       if (r / 100 != 2) {
-               scr_printf("%s\n", buf);
-               return;
-       }
-       eptr = &(attr->QRep);
-
-       /* Fetch the name of the current room aide */
-       r = CtdlIPCGetRoomAide(ipc, buf);
-       if (r / 100 == 2) {
-               safestrncpy(raide, buf, sizeof raide);
-       } else {
-               strcpy(raide, "");
-       }
-       if (IsEmptyStr(raide)) {
-               strcpy(raide, "none");
-       }
-
-       /* Fetch the expire policy (this will silently fail on old servers,
-        * resulting in "default" policy)
-        */
-       r = CtdlIPCGetMessageExpirationPolicy(ipc, 0, &eptr, buf);
-
-       /* Now interact with the user. */
-
-       strprompt("Room name", attr->QRname, ROOMNAMELEN-1);
-       attr->QRfloor = select_floor(ipc, attr->QRfloor);
-       attr->QRflags = set_room_attr(ipc, attr->QRflags, "Private room", QR_PRIVATE);
-       if (attr->QRflags & QR_PRIVATE) {
-               attr->QRflags = set_room_attr(ipc, attr->QRflags,
-                                      "Hidden room (accessible to anyone who knows the room name)",
-                                      QR_GUESSNAME);
-       }
-
-       /* if it's public, clear the privacy classes */
-       if ((attr->QRflags & QR_PRIVATE) == 0) {
-               if (attr->QRflags & QR_GUESSNAME) {
-                       attr->QRflags = attr->QRflags - QR_GUESSNAME;
-               }
-               if (attr->QRflags & QR_PASSWORDED) {
-                       attr->QRflags = attr->QRflags - QR_PASSWORDED;
-               }
-       }
-
-       /* if it's private, choose the privacy classes */
-       if ((attr->QRflags & QR_PRIVATE)
-           && ((attr->QRflags & QR_GUESSNAME) == 0)) {
-               attr->QRflags = set_room_attr(ipc, attr->QRflags,
-                                      "Accessible by entering a password",
-                                      QR_PASSWORDED);
-       }
-       if ((attr->QRflags & QR_PRIVATE)
-           && ((attr->QRflags & QR_PASSWORDED) == QR_PASSWORDED)) {
-               strprompt("Room password", attr->QRpasswd, 9);
-       }
-
-       if ((attr->QRflags & QR_PRIVATE) == QR_PRIVATE) {
-               rbump = boolprompt("Cause current users to forget room", 0);
-       }
-
-       attr->QRflags = set_room_attr(ipc, attr->QRflags,
-                                       "Preferred users only", QR_PREFONLY);
-       attr->QRflags = set_room_attr(ipc, attr->QRflags,
-                                       "Read-only room", QR_READONLY);
-       attr->QRflags2 = set_room_attr(ipc, attr->QRflags2,
-                               "Allow message deletion by anyone who can post",
-                               QR2_COLLABDEL);
-       attr->QRflags = set_room_attr(ipc, attr->QRflags,
-                                       "Permanent room", QR_PERMANENT);
-       attr->QRflags2 = set_room_attr(ipc, attr->QRflags2,
-                                                                  "Subject Required (Force "
-                                                                  "users to specify a message "
-                                   "subject)", QR2_SUBJECTREQ);
-       attr->QRflags = set_room_attr(ipc, attr->QRflags,
-                                       "Directory room", QR_DIRECTORY);
-       if (attr->QRflags & QR_DIRECTORY) {
-               strprompt("Directory name", attr->QRdirname, 14);
-               attr->QRflags =
-                   set_room_attr(ipc, attr->QRflags,
-                                               "Uploading allowed", QR_UPLOAD);
-               attr->QRflags =
-                   set_room_attr(ipc, attr->QRflags, "Downloading allowed",
-                                 QR_DOWNLOAD);
-               attr->QRflags =
-                   set_room_attr(ipc, attr->QRflags,
-                                               "Visible directory", QR_VISDIR);
-       }
-       attr->QRflags = set_room_attr(ipc, attr->QRflags,
-                                       "Network shared room", QR_NETWORK);
-       attr->QRflags2 = set_room_attr(ipc, attr->QRflags2,
-                               "Self-service list subscribe/unsubscribe",
-                               QR2_SELFLIST);
-       attr->QRflags2 = set_room_attr(ipc, attr->QRflags2,
-                               "public posting to this room via room_roomname@yourcitadel.org",
-                               QR2_SMTP_PUBLIC);
-       attr->QRflags2 = set_room_attr(ipc, attr->QRflags2,
-                               "moderated mailinglist",
-                               QR2_MODERATED);
-       attr->QRflags = set_room_attr(ipc, attr->QRflags,
-                              "Automatically make all messages anonymous",
-                              QR_ANONONLY);
-       if ((attr->QRflags & QR_ANONONLY) == 0) {
-               attr->QRflags = set_room_attr(ipc, attr->QRflags,
-                                      "Ask users whether to make messages anonymous",
-                                      QR_ANONOPT);
-       }
-       attr->QRorder = intprompt("Listing order", attr->QRorder, 0, 127);
-
-       /* Ask about the room aide */
-       do {
-               strprompt("Room aide (or 'none')", raide, 29);
-               if (!strcasecmp(raide, "none")) {
-                       strcpy(raide, "");
-                       break;
-               } else {
-                       r = CtdlIPCQueryUsername(ipc, raide, buf);
-                       if (r / 100 != 2)
-                               scr_printf("%s\n", buf);
-               }
-       } while (r / 100 != 2);
-
-       /* Angels and demons dancing in my head... */
-       do {
-               snprintf(buf, sizeof buf, "%d", attr->QRep.expire_mode);
-               strprompt("Message expire policy (? for list)", buf, 1);
-               if (buf[0] == '?') {
-                       scr_printf("\n"
-                               "0. Use the default for this floor\n"
-                               "1. Never automatically expire messages\n"
-                               "2. Expire by message count\n"
-                               "3. Expire by message age\n");
-               }
-       } while ((buf[0] < 48) || (buf[0] > 51));
-       attr->QRep.expire_mode = buf[0] - 48;
-
-       /* ...lunatics and monsters underneath my bed */
-       if (attr->QRep.expire_mode == 2) {
-               snprintf(buf, sizeof buf, "%d", attr->QRep.expire_value);
-               strprompt("Keep how many messages online?", buf, 10);
-               attr->QRep.expire_value = atol(buf);
-       }
-
-       if (attr->QRep.expire_mode == 3) {
-               snprintf(buf, sizeof buf, "%d", attr->QRep.expire_value);
-               strprompt("Keep messages for how many days?", buf, 10);
-               attr->QRep.expire_value = atol(buf);
-       }
-
-       /* Give 'em a chance to change their minds */
-       scr_printf("Save changes (y/n)? ");
-
-       if (yesno() == 1) {
-               r = CtdlIPCSetRoomAide(ipc, raide, buf);
-               if (r / 100 != 2) {
-                       scr_printf("%s\n", buf);
-               }
-
-               r = CtdlIPCSetMessageExpirationPolicy(ipc, 0, eptr, buf);
-               if (r / 100 != 2) {
-                       scr_printf("%s\n", buf);
-               }
-
-               r = CtdlIPCSetRoomAttributes(ipc, rbump, attr, buf);
-               scr_printf("%s\n", buf);
-               strncpy(buf, attr->QRname, ROOMNAMELEN);
-               free(attr);
-               if (r / 100 == 2)
-                       dotgoto(ipc, buf, 2, 0);
-       }
-       else free(attr);
-}
-
-
-/*
- * un-goto the previous room, or a specified room
- */
-void dotungoto(CtdlIPC *ipc, char *towhere)
-  {
-    /* Find this 'towhere' room in the list ungoto from this room to
-       that at the messagepointer position in that room in our ungoto list.
-       I suppose I could be a real dick and just ungoto that many places
-       in our list. */
-    int found = -1;
-    int lp;
-       char buf[SIZ];
-       struct ctdlipcroom *rret = NULL;        /* ignored */
-       int r;
-
-       if (uglistsize == 0)
-      {
-               scr_printf("No rooms to ungoto.\n");
-               return;
-      }
-       if (towhere == NULL)
-      {
-               scr_printf("Must specify a room to ungoto.\n");
-               return;
-      }
-       if (IsEmptyStr(towhere))
-      {
-               scr_printf("Must specify a room to ungoto.\n");
-               return;
-      }
-    for (lp = uglistsize-1; lp >= 0; lp--)
-      {
-        if (strcasecmp(towhere, uglist[lp]) == 0)
-          {
-            found = lp;
-            break;
-          }
-      }
-    if (found == -1)
-      {
-               scr_printf("Room: %s not in ungoto list.\n", towhere);
-       return;
-      }
-
-       r = CtdlIPCGotoRoom(ipc, uglist[found], "", &rret, buf);
-       if (rret) free(rret);   /* ignored */
-       if (r / 100 != 2) {
-               scr_printf("%s\n", buf);
-               return;
-       }
-       r = CtdlIPCSetLastRead(ipc, uglistlsn[found] ? uglistlsn[found] : 1, buf);
-       if (r / 100 != 2) {
-               scr_printf("%s\n", buf);
-       }
-       safestrncpy(buf, uglist[found], sizeof(buf));
-    /* we queue ungoto information here, because we're not really
-       ungotoing, we're really going to a random spot in some arbitrary
-       room list. */
-       dotgoto(ipc, buf, 0, 0);
-  }
-
-void ungoto(CtdlIPC *ipc)
-{
-       char buf[SIZ];
-       struct ctdlipcroom *rret = NULL;        /* ignored */
-       int r;
-
-       if (uglistsize == 0)
-               return;
-
-       r = CtdlIPCGotoRoom(ipc, uglist[uglistsize-1], "", &rret, buf);
-       if (rret) free(rret);   /* ignored */
-       if (r / 100 != 2) {
-               scr_printf("%s\n", buf);
-               return;
-       }
-       r = CtdlIPCSetLastRead(ipc, uglistlsn[uglistsize-1] ? uglistlsn[uglistsize-1] : 1, buf);
-       if (r / 100 != 2) {
-               scr_printf("%s\n", buf);
-       }
-       safestrncpy(buf, uglist[uglistsize-1], sizeof(buf));
-       uglistsize--;
-       free(uglist[uglistsize]);
-       /* Don't queue ungoto info or we end up in a loop */
-       dotgoto(ipc, buf, 0, 1);
-}
-
-
-/*
- * saves filelen bytes from file at pathname
- */
-int save_buffer(void *file, size_t filelen, const char *pathname)
-{
-       size_t block = 0;
-       size_t bytes_written = 0;
-       FILE *fp;
-
-       fp = fopen(pathname, "w");
-       if (!fp) {
-               err_printf("Cannot open '%s': %s\n", pathname, strerror(errno));
-               return 0;
-       }
-       do {
-               block = fwrite((char *)file + bytes_written, 1,
-                               filelen - bytes_written, fp);
-               bytes_written += block;
-       } while (errno == EINTR && bytes_written < filelen);
-       fclose(fp);
-
-       if (bytes_written < filelen) {
-               err_printf("Trouble saving '%s': %s\n", pathname,
-                               strerror(errno));
-               return 0;
-       }
-       return 1;
-}
-
-
-/*
- * Save supplied_filename in dest directory; gets the name only
- */
-void destination_directory(char *dest, const char *supplied_filename)
-{
-       static char save_dir[SIZ] = { 0 };
-
-       if (IsEmptyStr(save_dir)) {
-               if (getenv("HOME") == NULL) {
-                       strcpy(save_dir, ".");
-               }
-               else {
-                       sprintf(save_dir, "%s/Desktop", getenv("HOME"));
-                       if (access(save_dir, W_OK) != 0) {
-                               sprintf(save_dir, "%s", getenv("HOME"));
-                               if (access(save_dir, W_OK) != 0) {
-                                       sprintf(save_dir, ".");
-                               }
-                       }
-               }
-       }
-
-       sprintf(dest, "%s/%s", save_dir, supplied_filename);
-       strprompt("Save as", dest, PATH_MAX);
-
-       /* Remember the directory for next time */
-       strcpy(save_dir, dest);
-       if (strrchr(save_dir, '/') != NULL) {
-               strcpy(strrchr(save_dir, '/'), "");
-       }
-       else {
-               strcpy(save_dir, ".");
-       }
-}
-
-
-/*
- * download()  -  download a file or files.  The argument passed to this
- *                function determines which protocol to use.
- *  proto - 0 = paginate, 1 = xmodem, 2 = raw, 3 = ymodem, 4 = zmodem, 5 = save
- */
-void download(CtdlIPC *ipc, int proto)
-{
-       char buf[SIZ];
-       char filename[PATH_MAX];
-       char tempname[PATH_MAX];
-       char transmit_cmd[SIZ];
-       FILE *tpipe = NULL;
-       int broken = 0;
-       int r;
-       int rv = 0;
-       void *file = NULL;      /* The downloaded file */
-       size_t filelen = 0L;    /* The downloaded file length */
-
-       if ((room_flags & QR_DOWNLOAD) == 0) {
-               scr_printf("*** You cannot download from this room.\n");
-               return;
-       }
-
-       newprompt("Enter filename: ", filename, PATH_MAX);
-
-       /* Save to local disk, for folks with their own copy of the client */
-       if (proto == 5) {
-               destination_directory(tempname, filename);
-               r = CtdlIPCFileDownload(ipc, filename, &file, 0, progress, buf);
-               if (r / 100 != 2) {
-                       scr_printf("%s\n", buf);
-                       return;
-               }
-               save_buffer(file, (size_t)extract_long(buf, 0), tempname);
-               free(file);
-               return;
-       }
-
-       r = CtdlIPCFileDownload(ipc, filename, &file, 0, progress, buf);
-       if (r / 100 != 2) {
-               scr_printf("%s\n", buf);
-               return;
-       }
-       filelen = extract_unsigned_long(buf, 0);
-
-       /* Meta-download for public clients */
-       /* scr_printf("Fetching file from Citadel server...\n"); */
-       mkdir(tempdir, 0700);
-       snprintf(tempname, sizeof tempname, "%s/%s", tempdir, filename);
-       tpipe = fopen(tempname, "wb");
-       if (fwrite(file, filelen, 1, tpipe) < filelen) {
-               /* FIXME: restart syscall on EINTR */
-               broken = 1;
-       }
-       fclose(tpipe);
-       if (file) free(file);
-
-       if (proto == 0) {
-               /* FIXME: display internally instead */
-               snprintf(transmit_cmd, sizeof transmit_cmd,
-                       "SHELL=/dev/null; export SHELL; TERM=dumb; export TERM; exec more -d <%s",
-                       tempname);
-       }
-       else if (proto == 1)
-               snprintf(transmit_cmd, sizeof transmit_cmd, "exec sx %s", tempname);
-       else if (proto == 3)
-               snprintf(transmit_cmd, sizeof transmit_cmd, "exec sb %s", tempname);
-       else if (proto == 4)
-               snprintf(transmit_cmd, sizeof transmit_cmd, "exec sz %s", tempname);
-       else
-               /* FIXME: display internally instead */
-               snprintf(transmit_cmd, sizeof transmit_cmd, "exec cat %s", tempname);
-
-       screen_reset();
-       stty_ctdl(SB_RESTORE);
-       rv = system(transmit_cmd);
-       stty_ctdl(SB_NO_INTR);
-       screen_set();
-
-       /* clean up the temporary directory */
-       nukedir(tempdir);
-       ctdl_beep();    /* Beep beep! */
-}
-
-
-/*
- * read directory of this room
- */
-void roomdir(CtdlIPC *ipc)
-{
-       char flnm[256];
-       char flsz[32];
-       char comment[256];
-       char mimetype[256];
-       char buf[256];
-       char *listing = NULL;   /* Returned directory listing */
-       int r;
-
-       r = CtdlIPCReadDirectory(ipc, &listing, buf);
-       if (r / 100 != 1) {
-               pprintf("%s\n", buf);
-               return;
-       }
-
-       extract_token(comment, buf, 0, '|', sizeof comment);
-       extract_token(flnm, buf, 1, '|', sizeof flnm);
-       pprintf("\nDirectory of %s on %s\n", flnm, comment);
-       pprintf("-----------------------\n");
-       while (listing && *listing && !IsEmptyStr(listing)) {
-               extract_token(buf, listing, 0, '\n', sizeof buf);
-               remove_token(listing, 0, '\n');
-
-               extract_token(flnm, buf, 0, '|', sizeof flnm);
-               extract_token(flsz, buf, 1, '|', sizeof flsz);
-               extract_token(mimetype, buf, 2, '|', sizeof mimetype);
-               extract_token(comment, buf, 3, '|', sizeof comment);
-               if (strlen(flnm) <= 14)
-                       pprintf("%-14s %8s %s [%s]\n", flnm, flsz, comment, mimetype);
-               else
-                       pprintf("%s\n%14s %8s %s [%s]\n", flnm, "", flsz,
-                               comment, mimetype);
-       }
-       if (listing) free(listing);
-}
-
-
-/*
- * add a user to a private room
- */
-void invite(CtdlIPC *ipc)
-{
-       char username[USERNAME_SIZE];
-       char buf[SIZ];
-       int r;                          /* IPC response code */
-
-       newprompt("Name of user? ", username, USERNAME_SIZE);
-       if (username[0] == 0)
-               return;
-
-       r = CtdlIPCInviteUserToRoom(ipc, username, buf);
-       scr_printf("%s\n", buf);
-}
-
-
-/*
- * kick a user out of a room
- */
-void kickout(CtdlIPC *ipc)
-{
-       char username[USERNAME_SIZE];
-       char buf[SIZ];
-       int r;                          /* IPC response code */
-
-       newprompt("Name of user? ", username, USERNAME_SIZE);
-       if (username[0] == 0)
-               return;
-
-       r = CtdlIPCKickoutUserFromRoom(ipc, username, buf);
-       scr_printf("%s\n", buf);
-}
-
-
-/*
- * aide command: kill the current room
- */
-void killroom(CtdlIPC *ipc)
-{
-       char aaa[100];
-       int r;
-
-       r = CtdlIPCDeleteRoom(ipc, 0, aaa);
-       if (r / 100 != 2) {
-               scr_printf("%s\n", aaa);
-               return;
-       }
-
-       scr_printf("Are you sure you want to kill this room? ");
-       if (yesno() == 0)
-               return;
-
-       r = CtdlIPCDeleteRoom(ipc, 1, aaa);
-       scr_printf("%s\n", aaa);
-       if (r / 100 != 2)
-               return;
-       dotgoto(ipc, "_BASEROOM_", 0, 0);
-}
-
-void forget(CtdlIPC *ipc)
-{                              /* forget the current room */
-       char buf[SIZ];
-
-       scr_printf("Are you sure you want to forget this room? ");
-       if (yesno() == 0)
-               return;
-
-       remove_march(room_name, 0);
-       if (CtdlIPCForgetRoom(ipc, buf) / 100 != 2) {
-               scr_printf("%s\n", buf);
-               return;
-       }
-
-       /* now return to the lobby */
-       dotgoto(ipc, "_BASEROOM_", 0, 0);
-}
-
-
-/*
- * create a new room
- */
-void entroom(CtdlIPC *ipc)
-{
-       char buf[SIZ];
-       char new_room_name[ROOMNAMELEN];
-       int new_room_type;
-       char new_room_pass[10];
-       int new_room_floor;
-       int a, b;
-       int r;                          /* IPC response code */
-
-       /* Check permission to create room */
-       r = CtdlIPCCreateRoom(ipc, 0, "", 1, "", 0, buf);
-       if (r / 100 != 2) {
-               scr_printf("%s\n", buf);
-               return;
-       }
-
-       newprompt("Name for new room? ", new_room_name, ROOMNAMELEN - 1);
-       if (IsEmptyStr(new_room_name)) {
-               return;
-       }
-       for (a = 0; !IsEmptyStr(&new_room_name[a]); ++a) {
-               if (new_room_name[a] == '|') {
-                       new_room_name[a] = '_';
-               }
-       }
-
-       new_room_floor = select_floor(ipc, (int) curr_floor);
-
-       IFNEXPERT formout(ipc, "roomaccess");
-       do {
-               scr_printf("<?>Help\n"
-                       "<1>Public room (shown to all users by default)\n"
-                       "<2>Hidden room (accessible to anyone who knows the room name)\n"
-                       "<3>Passworded room (hidden, plus requires a password to enter)\n"
-                       "<4>Invitation-only room (requires access to be granted by an Aide)\n"
-                       "<5>Personal room (accessible to you only)\n"
-                       "Enter room type: "
-               );
-               do {
-                       b = inkey();
-               } while (((b < '1') || (b > '5')) && (b != '?'));
-               if (b == '?') {
-                       scr_printf("?\n");
-                       formout(ipc, "roomaccess");
-               }
-       } while ((b < '1') || (b > '5'));
-       b -= '0';                       /* Portable */
-       scr_printf("%d\n", b);
-       new_room_type = b - 1;
-       if (new_room_type == 2) {
-               newprompt("Enter a room password: ", new_room_pass, 9);
-               for (a = 0; !IsEmptyStr(&new_room_pass[a]); ++a)
-                       if (new_room_pass[a] == '|')
-                               new_room_pass[a] = '_';
-       } else {
-               strcpy(new_room_pass, "");
-       }
-
-       scr_printf("\042%s\042, a", new_room_name);
-       if (b == 1)
-               scr_printf(" public room.");
-       if (b == 2)
-               scr_printf(" hidden room.");
-       if (b == 3)
-               scr_printf(" passworded room, password: %s", new_room_pass);
-       if (b == 4)
-               scr_printf("n invitation-only room.");
-       if (b == 5)
-               scr_printf(" personal room.");
-       scr_printf("\nInstall it? (y/n) : ");
-       if (yesno() == 0) {
-               return;
-       }
-
-       r = CtdlIPCCreateRoom(ipc, 1, new_room_name, new_room_type,
-                             new_room_pass, new_room_floor, buf);
-       if (r / 100 != 2) {
-               scr_printf("%s\n", buf);
-               return;
-       }
-
-       /* command succeeded... now GO to the new room! */
-       dotgoto(ipc, new_room_name, 0, 0);
-}
-
-
-
-void readinfo(CtdlIPC *ipc)
-{                              /* read info file for current room */
-       char buf[SIZ];
-       char raide[64];
-       int r;                  /* IPC response code */
-       char *text = NULL;
-
-       /* Name of currernt room aide */
-       r = CtdlIPCGetRoomAide(ipc, buf);
-       if (r / 100 == 2)
-               safestrncpy(raide, buf, sizeof raide);
-       else
-               strcpy(raide, "");
-
-       if (!IsEmptyStr(raide))
-               scr_printf("Room aide is %s.\n\n", raide);
-
-       r = CtdlIPCRoomInfo(ipc, &text, buf);
-       if (r / 100 != 1)
-               return;
-
-       if (text) {
-               fmout(screenwidth, NULL, text, NULL,
-                     ((userflags & US_PAGINATOR) ? 1 : 0), screenheight, 
-                     (*raide) ? 2 : 0, 1);
-               free(text);
-       }
-}
-
-
-/*
- * <W>ho knows room...
- */
-void whoknows(CtdlIPC *ipc)
-{
-       char buf[256];
-       char *listing = NULL;
-       int r;
-
-       r = CtdlIPCWhoKnowsRoom(ipc, &listing, buf);
-       if (r / 100 != 1) {
-               pprintf("%s\n", buf);
-               return;
-       }
-       while (!IsEmptyStr(listing)) {
-               extract_token(buf, listing, 0, '\n', sizeof buf);
-               remove_token(listing, 0, '\n');
-               if (sigcaught == 0)
-                       pprintf("%s\n", buf);
-       }
-       free(listing);
-}
-
-
-void do_edit(CtdlIPC *ipc,
-               char *desc, char *read_cmd, char *check_cmd, char *write_cmd)
-{
-       FILE *fp;
-       char cmd[SIZ];
-       int b, cksum, editor_exit;
-
-       if (IsEmptyStr(editor_paths[0])) {
-               scr_printf("Do you wish to re-enter %s? ", desc);
-               if (yesno() == 0)
-                       return;
-       }
-
-       fp = fopen(temp, "w");
-       fclose(fp);
-
-       CtdlIPC_chat_send(ipc, check_cmd);
-       CtdlIPC_chat_recv(ipc, cmd);
-       if (cmd[0] != '2') {
-               scr_printf("%s\n", &cmd[4]);
-               return;
-       }
-
-       if (!IsEmptyStr(editor_paths[0])) {
-               CtdlIPC_chat_send(ipc, read_cmd);
-               CtdlIPC_chat_recv(ipc, cmd);
-               if (cmd[0] == '1') {
-                       fp = fopen(temp, "w");
-                       while (CtdlIPC_chat_recv(ipc, cmd), strcmp(cmd, "000")) {
-                               fprintf(fp, "%s\n", cmd);
-                       }
-                       fclose(fp);
-               }
-       }
-
-       cksum = file_checksum(temp);
-
-       if (!IsEmptyStr(editor_paths[0])) {
-               char tmp[SIZ];
-
-               snprintf(tmp, sizeof tmp, "WINDOW_TITLE=%s", desc);
-               putenv(tmp);
-               screen_reset();
-               stty_ctdl(SB_RESTORE);
-               editor_pid = fork();
-               if (editor_pid == 0) {
-                       chmod(temp, 0600);
-                       execlp(editor_paths[0], editor_paths[0], temp, NULL);
-                       exit(1);
-               }
-               if (editor_pid > 0)
-                       do {
-                               editor_exit = 0;
-                               b = ka_wait(&editor_exit);
-                       } while ((b != editor_pid) && (b >= 0));
-               editor_pid = (-1);
-               scr_printf("Executed %s\n", editor_paths[0]);
-               stty_ctdl(0);
-               screen_set();
-       } else {
-               scr_printf("Entering %s.  "
-                       "Press return twice when finished.\n", desc);
-               fp = fopen(temp, "r+");
-               citedit(ipc, fp);
-               fclose(fp);
-       }
-
-       if (file_checksum(temp) == cksum) {
-               scr_printf("*** Aborted.\n");
-       }
-
-       else {
-               CtdlIPC_chat_send(ipc, write_cmd);
-               CtdlIPC_chat_recv(ipc, cmd);
-               if (cmd[0] != '4') {
-                       scr_printf("%s\n", &cmd[4]);
-                       return;
-               }
-
-               fp = fopen(temp, "r");
-               while (fgets(cmd, SIZ - 1, fp) != NULL) {
-                       cmd[strlen(cmd) - 1] = 0;
-                       CtdlIPC_chat_send(ipc, cmd);
-               }
-               fclose(fp);
-               CtdlIPC_chat_send(ipc, "000");
-       }
-
-       unlink(temp);
-}
-
-
-void enterinfo(CtdlIPC *ipc)
-{                              /* edit info file for current room */
-       do_edit(ipc, "the Info file for this room", "RINF", "EINF 0", "EINF 1");
-}
-
-void enter_bio(CtdlIPC *ipc)
-{
-       char cmd[SIZ];
-       snprintf(cmd, sizeof cmd, "RBIO %s", fullname);
-       do_edit(ipc, "your Bio", cmd, "NOOP", "EBIO");
-}
-
-/*
- * create a new floor
- */
-void create_floor(CtdlIPC *ipc)
-{
-       char buf[SIZ];
-       char newfloorname[SIZ];
-       int r;                  /* IPC response code */
-
-       load_floorlist(ipc);
-
-       r = CtdlIPCCreateFloor(ipc, 0, "", buf);
-       if ( (r / 100 != 2) && (r != ERROR + ILLEGAL_VALUE) ) {
-               scr_printf("%s\n", buf);
-               return;
-       }
-
-       newprompt("Name for new floor: ", newfloorname, 255);
-       if (!*newfloorname) return;
-       r = CtdlIPCCreateFloor(ipc, 1, newfloorname, buf);
-       if (r / 100 == 2) {
-               scr_printf("Floor has been created.\n");
-       } else {
-               scr_printf("%s\n", buf);
-       }
-
-       load_floorlist(ipc);
-}
-
-/*
- * edit the current floor
- */
-void edit_floor(CtdlIPC *ipc)
-{
-       char buf[SIZ];
-       struct ExpirePolicy *ep = NULL;
-       int r;                          /* IPC response code */
-
-       load_floorlist(ipc);
-
-       /* Fetch the expire policy (this will silently fail on old servers,
-        * resulting in "default" policy)
-        */
-       r = CtdlIPCGetMessageExpirationPolicy(ipc, 1, &ep, buf);
-
-       /* Interact with the user */
-       scr_printf("You are editing the floor called \"%s\"\n", 
-               &floorlist[(int) curr_floor][0] );
-       strprompt("Floor name", &floorlist[(int) curr_floor][0], 255);
-
-       /* Angels and demons dancing in my head... */
-       do {
-               snprintf(buf, sizeof buf, "%d", ep->expire_mode);
-               strprompt
-                   ("Floor default message expire policy (? for list)",
-                    buf, 1);
-               if (buf[0] == '?') {
-                       scr_printf("\n"
-                               "0. Use the system default\n"
-                               "1. Never automatically expire messages\n"
-                               "2. Expire by message count\n"
-                               "3. Expire by message age\n");
-               }
-       } while ((buf[0] < '0') || (buf[0] > '3'));
-       ep->expire_mode = buf[0] - '0';
-
-       /* ...lunatics and monsters underneath my bed */
-       if (ep->expire_mode == 2) {
-               snprintf(buf, sizeof buf, "%d", ep->expire_value);
-               strprompt("Keep how many messages online?", buf, 10);
-               ep->expire_value = atol(buf);
-       }
-
-       if (ep->expire_mode == 3) {
-               snprintf(buf, sizeof buf, "%d", ep->expire_value);
-               strprompt("Keep messages for how many days?", buf, 10);
-               ep->expire_value = atol(buf);
-       }
-
-       /* Save it */
-       r = CtdlIPCSetMessageExpirationPolicy(ipc, 1, ep, buf);
-       r = CtdlIPCEditFloor(ipc, curr_floor, &floorlist[(int)curr_floor][0], buf);
-       scr_printf("%s\n", buf);
-       load_floorlist(ipc);
-}
-
-
-
-
-/*
- * kill the current floor 
- */
-void kill_floor(CtdlIPC *ipc)
-{
-       int floornum_to_delete, a;
-       char buf[SIZ];
-
-       load_floorlist(ipc);
-       do {
-               floornum_to_delete = (-1);
-               scr_printf("(Press return to abort)\n");
-               newprompt("Delete which floor? ", buf, 255);
-               if (IsEmptyStr(buf))
-                       return;
-               for (a = 0; a < 128; ++a)
-                       if (!strcasecmp(&floorlist[a][0], buf))
-                               floornum_to_delete = a;
-               if (floornum_to_delete < 0) {
-                       scr_printf("No such floor.  Select one of:\n");
-                       for (a = 0; a < 128; ++a)
-                               if (floorlist[a][0] != 0)
-                                       scr_printf("%s\n", &floorlist[a][0]);
-               }
-       } while (floornum_to_delete < 0);
-       CtdlIPCDeleteFloor(ipc, 1, floornum_to_delete, buf);
-       scr_printf("%s\n", buf);
-       load_floorlist(ipc);
-}
diff --git a/citadel/rooms.h b/citadel/rooms.h
deleted file mode 100644 (file)
index 6e32365..0000000
+++ /dev/null
@@ -1,51 +0,0 @@
-/* $Id$ */
-void listzrooms(CtdlIPC *ipc);
-void readinfo(CtdlIPC *ipc);
-void forget(CtdlIPC *ipc);
-void entroom(CtdlIPC *ipc);
-void killroom(CtdlIPC *ipc);
-void invite(CtdlIPC *ipc);
-void kickout(CtdlIPC *ipc);
-void editthisroom(CtdlIPC *ipc);
-void roomdir(CtdlIPC *ipc);
-void download(CtdlIPC *ipc, int proto);
-void ungoto(CtdlIPC *ipc);
-void dotungoto(CtdlIPC *ipc, char *towhere);
-void whoknows(CtdlIPC *ipc);
-void enterinfo(CtdlIPC *ipc);
-void knrooms(CtdlIPC *ipc, int kn_floor_mode);
-void dotknown(CtdlIPC *ipc, int what, char *match);
-void load_floorlist(CtdlIPC *ipc);
-void create_floor(CtdlIPC *ipc);
-void edit_floor(CtdlIPC *ipc);
-void kill_floor(CtdlIPC *ipc);
-void enter_bio(CtdlIPC *ipc);
-void hit_any_key(CtdlIPC *ipc);
-int save_buffer(void *file, size_t filelen, const char *pathname);
-void destination_directory(char *dest, const char *supplied_filename);
-void do_edit(CtdlIPC *ipc,
-               char *desc, char *read_cmd, char *check_cmd, char *write_cmd);
-
-
-
-/* 
- * This struct holds a list of rooms for client display.
- * (oooh, a tree!)
- */
-struct ctdlroomlisting {
-        struct ctdlroomlisting *lnext;
-       struct ctdlroomlisting *rnext;
-        char rlname[ROOMNAMELEN];
-        unsigned rlflags;
-       int rlfloor;
-        int rlorder;
-        };
-
-
-enum {
-        LISTRMS_NEW_ONLY,
-        LISTRMS_OLD_ONLY,
-        LISTRMS_ALL
-};
-
-
diff --git a/citadel/routines.c b/citadel/routines.c
deleted file mode 100644 (file)
index c8e85a7..0000000
+++ /dev/null
@@ -1,614 +0,0 @@
-/*
- * $Id$
- *
- * Client-side support functions.
- *
- */
-
-#include "sysdep.h"
-#include <stdlib.h>
-#include <unistd.h>
-#include <fcntl.h>
-#include <stdio.h>
-#include <ctype.h>
-#include <string.h>
-#include <sys/types.h>
-#include <sys/ioctl.h>
-#include <pwd.h>
-#include <signal.h>
-#include <dirent.h>
-#include <errno.h>
-
-#if TIME_WITH_SYS_TIME
-# include <sys/time.h>
-# include <time.h>
-#else
-# if HAVE_SYS_TIME_H
-#  include <sys/time.h>
-# else
-#  include <time.h>
-# endif
-#endif
-
-#ifdef HAVE_LIMITS_H
-#include <limits.h>
-#endif
-#ifdef HAVE_UTMP_H
-#include <utmp.h>
-#endif
-#ifdef HAVE_UTMPX_H
-#include <utmpx.h>
-#endif
-
-#include <libcitadel.h>
-#include "citadel.h"
-#include "citadel_ipc.h"
-#include "screen.h"
-
-#ifndef HAVE_GETUTLINE
-struct utmp *getutline(struct utmp *ut);
-#endif
-
-#define ROUTINES_C
-
-#include "citadel.h"
-#include "routines.h"
-#include "commands.h"
-#include "citadel_decls.h"
-#include "routines2.h"
-
-#define IFAIDE if(axlevel>=6)
-#define IFNAIDE if (axlevel<6)
-
-extern unsigned userflags;
-//extern char *axdefs[8];
-extern char sigcaught;
-extern char rc_floor_mode;
-extern int rc_ansi_color;
-extern int rc_prompt_control;
-
-/* Destructive backspace */
-void back(int spaces) {
-       int a;
-       for (a=0; a<spaces; ++a) {
-               scr_putc(8);
-               scr_putc(32);
-               scr_putc(8);
-       }
-}
-
-void hit_any_key(CtdlIPC *ipc) {       /* hit any key to continue */
-       int a,b;
-
-       color(COLOR_PUSH);
-       color(DIM_RED);
-       scr_printf("%s\r", ipc->ServInfo.moreprompt);
-       color(COLOR_POP);
-       stty_ctdl(0);
-       b=inkey();
-       for (a=0; !IsEmptyStr(&ipc->ServInfo.moreprompt[a]); ++a)
-               scr_putc(' ');
-       scr_putc(13);
-       stty_ctdl(1);
-       if ( (rc_prompt_control == 1)
-          || ((rc_prompt_control == 3) && (userflags & US_PROMPTCTL)) ) {
-               if (b == 'q' || b == 'Q' || b == 's' || b == 'S')
-                       b = STOP_KEY;
-               if (b == 'n' || b == 'N')
-                       b = NEXT_KEY;
-       }
-       if (b==NEXT_KEY) sigcaught = SIGINT;
-       if (b==STOP_KEY) sigcaught = SIGQUIT;
-}
-
-/*
- * Edit or delete a user (cmd=25 to edit/create, 96 to delete)
- */
-void edituser(CtdlIPC *ipc, int cmd)
-{
-       char buf[SIZ];
-       char who[USERNAME_SIZE];
-       char newname[USERNAME_SIZE];
-       struct ctdluser *user = NULL;
-       int newnow = 0;
-       int r;                          /* IPC response code */
-       int change_name = 0;
-
-       strcpy(newname, "");
-
-       newprompt("User name: ", who, 29);
-       while ((r = CtdlIPCAideGetUserParameters(ipc, who, &user, buf)) / 100 != 2) {
-               scr_printf("%s\n", buf);
-               if (cmd == 25) {
-                       scr_printf("Do you want to create this user? ");
-                       if (yesno()) {
-                               r = CtdlIPCCreateUser(ipc, who, 0, buf);
-                               if (r / 100 == 2) {
-                                       newnow = 1;
-                                       continue;
-                               }
-                               scr_printf("%s\n", buf);
-                       }
-               }
-               free(user);
-               return;
-       }
-
-       if (cmd == 25) {
-               val_user(ipc, user->fullname, 0); /* Display registration */
-
-               if (!newnow) {
-                       change_name = 1;
-                       while (change_name == 1) {
-                               if (boolprompt("Change name", 0)) {
-                                       strprompt("New name", newname, USERNAME_SIZE-1);
-                                       r = CtdlIPCRenameUser(ipc, user->fullname, newname, buf);
-                                       if (r / 100 != 2) {
-                                               scr_printf("%s\n", buf);
-                                       }
-                                       else {
-                                               strcpy(user->fullname, newname);
-                                               change_name = 0;
-                                       }
-                               }
-                               else {
-                                       change_name = 0;
-                               }
-                       }
-               }
-
-               if (newnow || boolprompt("Change password", 0)) {
-                       strprompt("Password", user->password, -19);
-               }
-       
-               user->axlevel = intprompt("Access level", user->axlevel, 0, 6);
-               if (boolprompt("Permission to send Internet mail", (user->flags & US_INTERNET)))
-                       user->flags |= US_INTERNET;
-               else
-                       user->flags &= ~US_INTERNET;
-               if (boolprompt("Ask user to register again", !(user->flags & US_REGIS)))
-                       user->flags &= ~US_REGIS;
-               else
-                       user->flags |= US_REGIS;
-               user->timescalled = intprompt("Times called",
-                               user->timescalled, 0, INT_MAX);
-               user->posted = intprompt("Messages posted",
-                                       user->posted, 0, INT_MAX);
-               user->lastcall = boolprompt("Set last call to now", 0) ?
-                                       time(NULL) : user->lastcall;
-               user->USuserpurge = intprompt("Purge time (in days, 0 for system default",
-                               user->USuserpurge, 0, INT_MAX);
-       }
-
-       if (cmd == 96) {
-               scr_printf("Do you want to delete this user? ");
-               if (!yesno()) {
-                       free(user);
-                       return;
-               }
-               user->axlevel = 0;
-       }
-
-       r = CtdlIPCAideSetUserParameters(ipc, user, buf);
-       if (r / 100 != 2) {
-               scr_printf("%s\n", buf);
-       }
-       free(user);
-}
-
-
-/* Display a prompt and flip a bit based on whether the user answers
- * yes or no.  Yes=1 and No=0, unless 'backwards' is set to a nonzero value
- * in which case No=1 and Yes=0.
- */
-int set_attr(CtdlIPC *ipc, unsigned int sval, char *prompt, unsigned int sbit, int backwards)
-{
-       int a;
-       int temp;
-
-       temp = sval;
-       color(DIM_WHITE);
-       scr_printf("%50s ", prompt);
-       color(DIM_MAGENTA);
-       scr_printf("[");
-       color(BRIGHT_MAGENTA);
-
-       if (backwards) {
-               scr_printf("%3s", ((temp&sbit) ? "No":"Yes"));
-       }
-       else {
-               scr_printf("%3s", ((temp&sbit) ? "Yes":"No"));
-       }
-
-       color(DIM_MAGENTA);
-       scr_printf("]? ");
-       color(BRIGHT_CYAN);
-       a = (temp & sbit);
-       if (a != 0) a = 1;
-       if (backwards) a = 1 - a;
-       a = yesno_d(a);
-       if (backwards) a = 1 - a;
-       color(DIM_WHITE);
-       temp = (temp|sbit);
-       if (!a) temp = (temp^sbit);
-       return(temp);
-}
-
-/*
- * modes are:  0 - .EC command, 1 - .EC for new user,
- *             2 - toggle Xpert mode  3 - toggle floor mode
- */
-void enter_config(CtdlIPC *ipc, int mode)
-{
-       char buf[SIZ];
-       struct ctdluser *user = NULL;
-       int r;                          /* IPC response code */
-
-       r = CtdlIPCGetConfig(ipc, &user, buf);
-       if (r / 100 != 2) {
-               scr_printf("%s\n", buf);
-               free(user);
-               return;
-       }
-
-       if (mode == 0 || mode == 1) {
-
-               /* Does anyone still use dialup connections with manual
-                * screen dimensions setting anymore?  For now we'll keep
-                * the system's ability to set these, but remove the prompts
-                * because they're spurious for nearly everyone.
-                * 
-               user->USscreenwidth = intprompt("Enter your screen width",
-                                               user->USscreenwidth, 20, 255);
-               user->USscreenheight = intprompt("Enter your screen height",
-                                                user->USscreenheight, 3, 255);
-                */
-               user->flags = set_attr(ipc, user->flags,
-                                      "Are you an experienced Citadel user",
-                                      US_EXPERT, 0);
-               if ((user->flags & US_EXPERT) == 0 && mode == 1) {
-                       free(user);
-                       return;
-               }
-
-               user->flags = set_attr(ipc, user->flags,
-                       "Print last old message on New message request",
-                       US_LASTOLD, 0);
-
-               user->flags = set_attr(ipc, user->flags,
-                                      "Prompt after each message",
-                                      US_NOPROMPT, 1);
-
-               if ((user->flags & US_NOPROMPT) == 0)
-                       user->flags = set_attr(ipc, user->flags,
-                                              "Use 'disappearing' prompts",
-                                              US_DISAPPEAR, 0);
-
-               user->flags = set_attr(ipc, user->flags,
-                                      "Pause after each screenful of text",
-                                      US_PAGINATOR, 0);
-
-               if (rc_prompt_control == 3 && (user->flags & US_PAGINATOR))
-                       user->flags = set_attr(ipc, user->flags,
-                               "<N>ext and <S>top work at paginator prompt",
-                               US_PROMPTCTL, 0);
-
-               if (rc_floor_mode == RC_DEFAULT)
-                       user->flags = set_attr(ipc, user->flags,
-                                              "View rooms by floor",
-                                              US_FLOORS, 0);
-
-               if (rc_ansi_color == 3)
-                       user->flags = set_attr(ipc, user->flags,
-                                              "Enable color support",
-                                              US_COLOR, 0);
-
-               if ((user->flags & US_EXPERT) == 0)
-                       formout(ipc, "unlisted");
-
-               user->flags = set_attr(ipc, user->flags,
-                                      "Be unlisted in userlog",
-                                      US_UNLISTED, 0);
-
-               if (!IsEmptyStr(editor_paths[0])) {
-                       user->flags = set_attr(ipc, user->flags,
-                               "Always enter messages with the full-screen editor",
-                               US_EXTEDIT, 0);
-               }
-
-       }
-
-       if (mode == 2) {
-               if (user->flags & US_EXPERT) {
-                       user->flags ^= US_EXPERT;
-                       scr_printf("Expert mode now OFF\n");
-               } else {
-                       user->flags |= US_EXPERT;
-                       scr_printf("Expert mode now ON\n");
-               }
-       }
-
-       if (mode == 3) {
-               if (user->flags & US_FLOORS) {
-                       user->flags ^= US_FLOORS;
-                       scr_printf("Floor mode now OFF\n");
-               } else {
-                       user->flags |= US_FLOORS;
-                       scr_printf("Floor mode now ON\n");
-               }
-       }
-
-       r = CtdlIPCSetConfig(ipc, user, buf);
-       if (r / 100 != 2) scr_printf("%s\n", buf);
-       userflags = user->flags;
-       free(user);
-}
-
-/*
- * getstring()  -  get a line of text from a file
- *                ignores lines beginning with "#"
- */
-int getstring(FILE *fp, char *string)
-{
-       int a,c;
-       do {
-               strcpy(string,"");
-               a=0;
-               do {
-                       c=getc(fp);
-                       if (c<0) {
-                               string[a]=0;
-                               return(-1);
-                       }
-                       string[a++]=c;
-               } while(c!=10);
-                       string[a-1]=0;
-       } while(string[0]=='#');
-       return(strlen(string));
-}
-
-
-/* Searches for patn in search string */
-int pattern(char *search, char *patn) {
-       int a,b,len;
-       
-       len = strlen(patn);
-       for (a=0; !IsEmptyStr(&search[a]); ++a) {
-               b=strncasecmp(&search[a],patn,len);
-               if (b==0) return(b);
-       }
-       return(-1);
-}
-
-
-void strproc(char *string)
-{
-       int a;
-
-       if (IsEmptyStr(string)) return;
-
-       /* Convert non-printable characters to blanks */
-       for (a=0; !IsEmptyStr(&string[a]); ++a) {
-               if (string[a]<32) string[a]=32;
-               if (string[a]>126) string[a]=32;
-       }
-
-       /* Remove leading and trailing blanks */
-       while(string[0]<33) strcpy(string,&string[1]);
-       while(string[strlen(string)-1]<33) string[strlen(string)-1]=0;
-
-       /* Remove double blanks */
-       for (a=0; a<strlen(string); ++a) {
-               if ((string[a]==32)&&(string[a+1]==32)) {
-                       strcpy(&string[a],&string[a+1]);
-                       a=0;
-               }
-       }
-
-       /* remove characters which would interfere with the network */
-       for (a=0; a<strlen(string); ++a) {
-               if (string[a]=='!') strcpy(&string[a],&string[a+1]);
-               if (string[a]=='@') strcpy(&string[a],&string[a+1]);
-               if (string[a]=='_') strcpy(&string[a],&string[a+1]);
-               if (string[a]==',') strcpy(&string[a],&string[a+1]);
-               if (string[a]=='%') strcpy(&string[a],&string[a+1]);
-               if (string[a]=='|') strcpy(&string[a],&string[a+1]);
-       }
-
-}
-
-
-#ifndef HAVE_STRERROR
-/*
- * replacement strerror() for systems that don't have it
- */
-char *strerror(int e)
-{
-       static char buf[128];
-
-       snprintf(buf, sizeof buf, "errno = %d",e);
-       return(buf);
-}
-#endif
-
-
-void progress(CtdlIPC* ipc, unsigned long curr, unsigned long cmax)
-{
-       static char dots[] =
-               "**************************************************";
-       char dots_printed[51];
-       char fmt[42];
-       unsigned long a;
-
-       if (curr >= cmax) {
-               sln_printf("\r%79s\r","");
-               status_line(ipc->ServInfo.humannode, ipc->ServInfo.site_location,
-                       room_name, secure, 0);
-       } else {
-               /* a will be range 0-50 rather than 0-100 */
-               a=(curr * 50) / cmax;
-               sprintf(fmt, "[%%s%%%lds] %%3ld%%%% %%10ld/%%10ld\r", 50 - a);
-               strncpy(dots_printed, dots, a);
-               dots_printed[a] = 0;
-               sln_printf(fmt, dots_printed, "",
-                               curr * 100 / cmax, curr, cmax);
-               sln_flush();
-       }
-}
-
-
-/*
- * NOT the same locate_host() in locate_host.c.  This one just does a
- * 'who am i' to try to discover where the user is...
- */
-void locate_host(CtdlIPC* ipc, char *hbuf)
-{
-#ifndef HAVE_UTMP_H
-       char buf[SIZ];
-       FILE *who;
-       int a,b;
-
-       who = (FILE *)popen("who am i","r");
-       if (who==NULL) {
-               strcpy(hbuf, ipc->ServInfo.fqdn);
-               return; 
-       }
-       fgets(buf,sizeof buf,who);
-       pclose(who);
-
-       b = 0;
-       for (a=0; !IsEmptyStr(&buf[a]); ++a) {
-               if ((buf[a]=='(')||(buf[a]==')')) ++b;
-       }
-       if (b<2) {
-               strcpy(hbuf, ipc->ServInfo.fqdn);
-               return;
-       }
-
-       for (a=0; a<strlen(buf); ++a) {
-               if (buf[a]=='(') {
-                       strcpy(buf,&buf[a+1]);
-               }
-       }
-       for (a=0; a<strlen(buf); ++a) {
-               if (buf[a]==')') buf[a] = 0;
-       }
-
-       if (IsEmptyStr(buf)) strcpy(hbuf, ipc->ServInfo.fqdn);
-       else strncpy(hbuf,buf,24);
-#else
-       char *tty = ttyname(0);
-#ifdef HAVE_GETUTXLINE
-       struct utmpx ut, *put;
-#else
-       struct utmp ut, *put;
-#endif
-
-       if (tty == NULL) {
-           fail:
-               safestrncpy(hbuf, ipc->ServInfo.fqdn, 24);
-               return;
-       }
-
-       if (strncmp(tty, "/dev/", 5))
-               goto fail;
-
-       safestrncpy(ut.ut_line, &tty[5], sizeof ut.ut_line);
-
-#ifdef HAVE_GETUTXLINE /* Solaris uses this */
-       if ((put = getutxline(&ut)) == NULL)
-#else
-       if ((put = getutline(&ut)) == NULL)
-#endif
-               goto fail;
-
-#if defined(HAVE_UT_TYPE) || defined(HAVE_GETUTXLINE)
-       if (put->ut_type == USER_PROCESS) {
-#endif
-#if defined(HAVE_UT_HOST) || defined(HAVE_GETUTXLINE)
-               if (*put->ut_host)
-                       safestrncpy(hbuf, put->ut_host, 24);
-               else
-#endif
-                       safestrncpy(hbuf, put->ut_line, 24);
-#if defined(HAVE_UT_TYPE) || defined(HAVE_GETUTXLINE)
-       }
-       else goto fail;
-#endif
-#endif /* HAVE_UTMP_H */
-}
-
-/*
- * miscellaneous server commands (testing, etc.)
- */
-void misc_server_cmd(CtdlIPC *ipc, char *cmd) {
-       char buf[SIZ];
-
-       CtdlIPC_chat_send(ipc, cmd);
-       CtdlIPC_chat_recv(ipc, buf);
-       scr_printf("%s\n",buf);
-       if (buf[0]=='1') {
-               set_keepalives(KA_HALF);
-               while (CtdlIPC_chat_recv(ipc, buf), strcmp(buf,"000")) {
-                       scr_printf("%s\n",buf);
-               }
-               set_keepalives(KA_YES);
-               return;
-       }
-       if (buf[0]=='4') {
-               do {
-                       newprompt("> ",buf,255);
-                       CtdlIPC_chat_send(ipc, buf);
-               } while(strcmp(buf,"000"));
-               return;
-       }
-}
-
-
-/*
- * compute the checksum of a file
- */
-int file_checksum(char *filename)
-{
-       int cksum = 0;
-       int ch;
-       FILE *fp;
-
-       fp = fopen(filename,"r");
-       if (fp == NULL) return(0);
-
-       /* yes, this algorithm may allow cksum to overflow, but that's ok
-        * as long as it overflows consistently, which it will.
-        */
-       while (ch=getc(fp), ch>=0) {
-               cksum = (cksum + ch);
-       }
-
-       fclose(fp);
-       return(cksum);
-}
-
-/*
- * nuke a directory and its contents
- */
-int nukedir(char *dirname)
-{
-       DIR *dp;
-       struct dirent *d;
-       char filename[SIZ];
-
-       dp = opendir(dirname);
-       if (dp == NULL) {
-               return(errno);
-       }
-
-       while (d = readdir(dp), d != NULL) {
-               snprintf(filename, sizeof filename, "%s/%s",
-                       dirname, d->d_name);
-               unlink(filename);
-       }
-
-       closedir(dp);
-       return(rmdir(dirname));
-}
diff --git a/citadel/routines.h b/citadel/routines.h
deleted file mode 100644 (file)
index 6bb516a..0000000
+++ /dev/null
@@ -1,13 +0,0 @@
-/* $Id$ */
-void edituser(CtdlIPC *ipc, int cmd);
-void interr(int errnum);
-int struncmp(char *lstr, char *rstr, int len);
-int pattern(char *search, char *patn);
-void enter_config(CtdlIPC* ipc, int mode);
-void locate_host(CtdlIPC* ipc, char *hbuf);
-void misc_server_cmd(CtdlIPC *ipc, char *cmd);
-int nukedir(char *dirname);
-void strproc(char *string);
-void back(int spaces);
-void progress(CtdlIPC* ipc, unsigned long curr, unsigned long cmax);
-int set_attr(CtdlIPC *ipc, unsigned int sval, char *prompt, unsigned int sbit, int backwards);
diff --git a/citadel/routines2.c b/citadel/routines2.c
deleted file mode 100644 (file)
index 3fbff5f..0000000
+++ /dev/null
@@ -1,632 +0,0 @@
-/* $Id$
- *
- * More client-side support functions.
- * Unlike routines.c, some of these DO use global variables.
- *
- */
-
-#include <stdlib.h>
-#include <unistd.h>
-#include <sys/types.h>
-#include <sys/stat.h>
-#include <fcntl.h>
-#include <stdio.h>
-#include <ctype.h>
-#include <string.h>
-#include <limits.h>
-
-#if TIME_WITH_SYS_TIME
-# include <sys/time.h>
-# include <time.h>
-#else
-# if HAVE_SYS_TIME_H
-#  include <sys/time.h>
-# else
-#  include <time.h>
-# endif
-#endif
-
-#include <signal.h>
-#include <pwd.h>
-#include <errno.h>
-#include <stdarg.h>
-#include <libcitadel.h>
-#include "sysdep.h"
-#include "citadel.h"
-#include "citadel_ipc.h"
-#include "citadel_decls.h"
-#include "routines2.h"
-#include "routines.h"
-#include "commands.h"
-#include "messages.h"
-#ifndef HAVE_SNPRINTF
-#include "snprintf.h"
-#endif
-#include "screen.h"
-
-/* work around solaris include files */
-#ifdef reg
-#undef reg
-#endif
-
-extern char temp[];
-extern char tempdir[];
-extern char *axdefs[8];
-extern long highest_msg_read;
-extern long maxmsgnum;
-extern unsigned room_flags;
-extern int screenwidth;
-
-
-/*
-int eopen(char *name, int mode)
-{
-       int ret;
-       ret = open(name, mode);
-       if (ret < 0) {
-               err_printf("Cannot open file '%s', mode=%d, errno=%d\n",
-                       name, mode, errno);
-               interr(errno);
-       }
-       return (ret);
-}
-*/
-
-
-int room_prompt(unsigned int qrflags)
-{                              /* return proper room prompt character */
-       int a;
-       a = '>';
-       if (qrflags & QR_DIRECTORY)
-               a = ']';
-       if ((a == ']') && (qrflags & QR_NETWORK))
-               a = '}';
-       if ((a == '>') && (qrflags & QR_NETWORK))
-               a = ')';
-       return (a);
-}
-
-void entregis(CtdlIPC *ipc)
-{                              /* register with name and address */
-
-       char buf[SIZ];
-       char tmpname[30];
-       char tmpaddr[25];
-       char tmpcity[15];
-       char tmpstate[3];
-       char tmpzip[11];
-       char tmpphone[15];
-       char tmpemail[SIZ];
-       char tmpcountry[32];
-       char diruser[256];
-       char dirnode[256];
-       char holdemail[SIZ];
-       char *reg = NULL;
-       int ok = 0;
-       int r;                          /* IPC response code */
-
-       strcpy(tmpname, "");
-       strcpy(tmpaddr, "");
-       strcpy(tmpcity, "");
-       strcpy(tmpstate, "");
-       strcpy(tmpzip, "");
-       strcpy(tmpphone, "");
-       strcpy(tmpemail, "");
-       strcpy(tmpcountry, "");
-
-       r = CtdlIPCGetUserRegistration(ipc, NULL, &reg, buf);
-       if (r / 100 == 1) {
-               int a = 0;
-
-               while (reg && !IsEmptyStr(reg)) {
-
-                       extract_token(buf, reg, 0, '\n', sizeof buf);
-                       remove_token(reg, 0, '\n');
-
-                       if (a == 2)
-                               safestrncpy(tmpname, buf, sizeof tmpname);
-                       else if (a == 3)
-                               safestrncpy(tmpaddr, buf, sizeof tmpaddr);
-                       else if (a == 4)
-                               safestrncpy(tmpcity, buf, sizeof tmpcity);
-                       else if (a == 5)
-                               safestrncpy(tmpstate, buf, sizeof tmpstate);
-                       else if (a == 6)
-                               safestrncpy(tmpzip, buf, sizeof tmpzip);
-                       else if (a == 7)
-                               safestrncpy(tmpphone, buf, sizeof tmpphone);
-                       else if (a == 9)
-                               safestrncpy(tmpemail, buf, sizeof tmpemail);
-                       else if (a == 10)
-                               safestrncpy(tmpcountry, buf, sizeof tmpcountry);
-                       ++a;
-               }
-       }
-       strprompt("REAL name", tmpname, 29);
-       strprompt("Address", tmpaddr, 24);
-       strprompt("City/town", tmpcity, 14);
-       strprompt("State/province", tmpstate, 2);
-       strprompt("ZIP/Postal Code", tmpzip, 10);
-       strprompt("Country", tmpcountry, 31);
-       strprompt("Telephone number", tmpphone, 14);
-
-       do {
-               ok = 1;
-               safestrncpy(holdemail, tmpemail, sizeof holdemail);
-               strprompt("Email address", tmpemail, 31);
-               r = CtdlIPCDirectoryLookup(ipc, tmpemail, buf);
-               if (r / 100 == 2) {
-                       extract_token(diruser, buf, 0, '@', sizeof diruser);
-                       extract_token(dirnode, buf, 1, '@', sizeof dirnode);
-                       striplt(diruser);
-                       striplt(dirnode);
-                       if ((strcasecmp(diruser, fullname))
-                          || (strcasecmp(dirnode, ipc->ServInfo.nodename))) {
-                               scr_printf(
-                                       "\nYou can't use %s as your address.\n",
-                                       tmpemail);
-                               scr_printf(
-                                       "It is already in use by %s @ %s.\n",
-                                       diruser, dirnode);
-                               ok = 0;
-                               safestrncpy(tmpemail, holdemail, sizeof tmpemail);
-                       }
-               }
-       } while (ok == 0);
-
-       /* now send the registration info back to the server */
-       reg = (char *)realloc(reg, SIZ);
-       if (reg) {
-               sprintf(reg, "%s\n%s\n%s\n%s\n%s\n%s\n%s\n%s\n",
-                       tmpname, tmpaddr, tmpcity, tmpstate,
-                       tmpzip, tmpphone, tmpemail, tmpcountry);
-               r = CtdlIPCSetRegistration(ipc, reg, buf);
-               if (r / 100 != 4)
-                       scr_printf("%s\n", buf);
-               free(reg);
-       }
-       scr_printf("\n");
-}
-
-void updatels(CtdlIPC *ipc)
-{                              /* make all messages old in current room */
-       char buf[256];
-       int r;                          /* IPC response code */
-
-       if (rc_alt_semantics) {
-               if (maxmsgnum == 0 && highest_msg_read == 0) {
-                       return;
-               }
-               r = CtdlIPCSetLastRead(ipc, (maxmsgnum > highest_msg_read) ?
-                                maxmsgnum : highest_msg_read, buf);
-       } else {
-               r = CtdlIPCSetLastRead(ipc, (maxmsgnum > highest_msg_read) ?
-                                maxmsgnum : highest_msg_read, buf);
-/*             r = CtdlIPCSetLastRead(ipc, maxmsgnum, buf); */
-/* This is a quick-and-dirty fix to all msgs becoming new in Mail>.
- * It will need to be rethought when messages.c is rewritten.
- */
-       }
-       if (r / 100 != 2)
-               scr_printf("%s\n", buf);
-}
-
-/*
- * only make messages old in this room that have been read
- */
-void updatelsa(CtdlIPC *ipc)
-{
-       char buf[256];
-       int r;                          /* IPC response code */
-
-       r = CtdlIPCSetLastRead(ipc, highest_msg_read, buf);
-       if (r / 100 != 2)
-               scr_printf("%s\n", &buf[4]);
-}
-
-
-/*
- * client-based uploads (for users with their own clientware)
- */
-void cli_upload(CtdlIPC *ipc)
-{
-       char flnm[PATH_MAX];
-       char desc[151];
-       char buf[256];
-       char tbuf[256];
-       int r;          /* IPC response code */
-       int a;
-       int fd;
-
-       if ((room_flags & QR_UPLOAD) == 0) {
-               scr_printf("*** You cannot upload to this room.\n");
-               return;
-       }
-       newprompt("File to be uploaded: ", flnm, 55);
-       fd = open(flnm, O_RDONLY);
-       if (fd < 0) {
-               scr_printf("Cannot open '%s': %s\n", flnm, strerror(errno));
-               return;
-       }
-       scr_printf("Enter a description of this file:\n");
-       newprompt(": ", desc, 75);
-
-       /* Keep generating filenames in hope of finding a unique one */
-       a = 0;
-       while (a < 10) {
-               /* basename of filename */
-               strcpy(tbuf, flnm);
-               if (haschar(tbuf, '/'))
-                       extract_token(tbuf, flnm,
-                               num_tokens(tbuf, '/') - 1,
-                               '/', sizeof tbuf
-                       );
-               /* filename.1, filename.2, etc */
-               if (a > 0) {
-                       sprintf(&tbuf[strlen(tbuf)], ".%d", a);
-               }
-               /* Try upload */
-               r = CtdlIPCFileUpload(ipc, tbuf, desc, flnm, progress, buf);
-               if (r / 100 == 5 || r < 0)
-                       scr_printf("%s\n", buf);
-               else
-                       break;
-               ++a;
-       }
-       if (a > 0) scr_printf("Saved as '%s'\n", tbuf);
-}
-
-
-/*
- * Function used for various image upload commands
- */
-void cli_image_upload(CtdlIPC *ipc, char *keyname)
-{
-       char flnm[PATH_MAX];
-       char buf[256];
-       int r;
-
-       /* Can we upload this image? */
-       r = CtdlIPCImageUpload(ipc, 0, NULL, keyname, NULL, buf);
-       if (r / 100 != 2) {
-               err_printf("%s\n", buf);
-               return;
-       }
-       newprompt("Image file to be uploaded: ", flnm, 55);
-       r = CtdlIPCImageUpload(ipc, 1, flnm, keyname, progress, buf);
-       if (r / 100 == 5) {
-               err_printf("%s\n", buf);
-       } else if (r < 0) {
-               err_printf("Cannot upload '%s': %s\n", flnm, strerror(errno));
-       }
-       /* else upload succeeded */
-}
-
-
-/*
- * protocol-based uploads (Xmodem, Ymodem, Zmodem)
- */
-void upload(CtdlIPC *ipc, int c)
-{                              /* c = upload mode */
-       char flnm[PATH_MAX];
-       char desc[151];
-       char buf[256];
-       char tbuf[4096];
-       int xfer_pid;
-       int a, b;
-       FILE *fp, *lsfp;
-       int r;
-       int rv;
-
-       if ((room_flags & QR_UPLOAD) == 0) {
-               scr_printf("*** You cannot upload to this room.\n");
-               return;
-       }
-       /* we don't need a filename when receiving batch y/z modem */
-       if ((c == 2) || (c == 3))
-               strcpy(flnm, "x");
-       else
-               newprompt("Enter filename: ", flnm, 15);
-
-       for (a = 0; !IsEmptyStr(&flnm[a]); ++a)
-               if ((flnm[a] == '/') || (flnm[a] == '\\') || (flnm[a] == '>')
-                   || (flnm[a] == '?') || (flnm[a] == '*')
-                   || (flnm[a] == ';') || (flnm[a] == '&'))
-                       flnm[a] = '_';
-
-       /* create a temporary directory... */
-       if (mkdir(tempdir, 0700) != 0) {
-               scr_printf("*** Could not create temporary directory %s: %s\n",
-                      tempdir, strerror(errno));
-               return;
-       }
-       /* now do the transfer ... in a separate process */
-       xfer_pid = fork();
-       if (xfer_pid == 0) {
-               rv = chdir(tempdir);
-               switch (c) {
-               case 0:
-                       stty_ctdl(0);
-                       scr_printf("Receiving %s - press Ctrl-D to end.\n", flnm);
-                       fp = fopen(flnm, "w");
-                       do {
-                               b = inkey();
-                               if (b == 13) {
-                                       b = 10;
-                               }
-                               if (b != 4) {
-                                       scr_printf("%c", b);
-                                       putc(b, fp);
-                               }
-                       } while (b != 4);
-                       fclose(fp);
-                       exit(0);
-               case 1:
-                       screen_reset();
-                       stty_ctdl(3);
-                       execlp("rx", "rx", flnm, NULL);
-                       exit(1);
-               case 2:
-                       screen_reset();
-                       stty_ctdl(3);
-                       execlp("rb", "rb", NULL);
-                       exit(1);
-               case 3:
-                       screen_reset();
-                       stty_ctdl(3);
-                       execlp("rz", "rz", NULL);
-                       exit(1);
-               }
-       } else
-               do {
-                       b = ka_wait(&a);
-               } while ((b != xfer_pid) && (b != (-1)));
-       stty_ctdl(0);
-       screen_set();
-
-       if (a != 0) {
-               scr_printf("\r*** Transfer unsuccessful.\n");
-               nukedir(tempdir);
-               return;
-       }
-       scr_printf("\r*** Transfer successful.\n");
-       snprintf(buf, sizeof buf, "cd %s; ls", tempdir);
-       lsfp = popen(buf, "r");
-       if (lsfp != NULL) {
-               while (fgets(flnm, sizeof flnm, lsfp) != NULL) {
-                       flnm[strlen(flnm) - 1] = 0;     /* chop newline */
-                       snprintf(buf, sizeof buf,
-                                "Enter a short description of '%s':\n: ",
-                                flnm);
-                       newprompt(buf, desc, 150);
-                       snprintf(buf, sizeof buf, "%s/%s", tempdir, flnm);
-                       r = CtdlIPCFileUpload(ipc, flnm, desc, buf, progress, tbuf);
-                       scr_printf("%s\n", tbuf);
-               }
-               pclose(lsfp);
-       }
-       nukedir(tempdir);
-}
-
-/* 
- * validate a user (returns 0 for successful validation, nonzero if quitting)
- */
-int val_user(CtdlIPC *ipc, char *user, int do_validate)
-{
-       int a;
-       char cmd[256];
-       char buf[256];
-       char *resp = NULL;
-       int ax = 0;
-       char answer[2];
-       int r;                          /* IPC response code */
-
-       scr_printf("\n");
-       r = CtdlIPCGetUserRegistration(ipc, user, &resp, cmd);
-       if (r / 100 == 1) {
-               a = 0;
-               do {
-                       extract_token(buf, resp, 0, '\n', sizeof buf);
-                       remove_token(resp, 0, '\n');
-                       ++a;
-                       if (a == 1)
-                               scr_printf("User #%s - %s  ", buf, cmd);
-                       if (a == 2)
-                               scr_printf("PW: %s\n", (IsEmptyStr(buf) ? "<NOT SET>" : "<SET>") );
-                       if (a == 3)
-                               scr_printf("%s\n", buf);
-                       if (a == 4)
-                               scr_printf("%s\n", buf);
-                       if (a == 5)
-                               scr_printf("%s, ", buf);
-                       if (a == 6)
-                               scr_printf("%s ", buf);
-                       if (a == 7)
-                               scr_printf("%s\n", buf);
-                       if (a == 8)
-                               scr_printf("%s\n", buf);
-                       if (a == 9)
-                               ax = atoi(buf);
-                       if (a == 10)
-                               scr_printf("%s\n", buf);
-                       if (a == 11)
-                               scr_printf("%s\n", buf);
-               } while (!IsEmptyStr(resp));
-
-/* TODODRW: discrepancy here. Parts of the code refer to axdefs[7] as the highest
- * but most of it limits it to axdefs[6].
- * Webcit limits to 6 as does the code here but there are 7 in axdefs.h
- */
-               scr_printf("Current access level: %d (%s)\n", ax, axdefs[ax]);
-       } else {
-               scr_printf("%s\n%s\n", user, &cmd[4]);
-       }
-       if (resp) free(resp);
-
-       if (do_validate) {
-               /* now set the access level */
-               while(1) {
-                       sprintf(answer, "%d", ax);
-                       strprompt("New access level (? for help, q to quit)",
-                               answer, 1);
-                       if ((answer[0] >= '0') && (answer[0] <= '6')) {
-                               ax = atoi(answer);
-                               r = CtdlIPCValidateUser(ipc, user, ax, cmd);
-                               if (r / 100 != 2)
-                               scr_printf("%s\n\n", cmd);
-                               return(0);
-                       }
-                       if (tolower(answer[0]) == 'q') {
-                               scr_printf("*** Aborted.\n\n");
-                               return(1);
-                       }
-                       if (answer[0] == '?') {
-                               scr_printf("Available access levels:\n");
-                               for (a=0; a<7; ++a) {
-                                       scr_printf("%d - %s\n",
-                                               a, axdefs[a]);
-                               }
-                       }
-               }
-       }
-       return(0);
-}
-
-
-void validate(CtdlIPC *ipc)
-{                              /* validate new users */
-       char cmd[256];
-       char buf[256];
-       int finished = 0;
-       int r;                          /* IPC response code */
-
-       do {
-               r = CtdlIPCNextUnvalidatedUser(ipc, cmd);
-               if (r / 100 != 3)
-                       finished = 1;
-               if (r / 100 == 2)
-                       scr_printf("%s\n", cmd);
-               if (r / 100 == 3) {
-                       extract_token(buf, cmd, 0, '|', sizeof buf);
-                       if (val_user(ipc, buf, 1) != 0) finished = 1;
-               }
-       } while (finished == 0);
-}
-
-void subshell(void)
-{
-       int a, b;
-
-       screen_reset();
-       stty_ctdl(SB_RESTORE);
-       a = fork();
-       if (a == 0) {
-               signal(SIGINT, SIG_DFL);
-               signal(SIGQUIT, SIG_DFL);
-               execlp(getenv("SHELL"), getenv("SHELL"), NULL);
-               err_printf("Could not open a shell: %s\n", strerror(errno));
-               exit(errno);
-       }
-       do {
-               b = ka_wait(NULL);
-       } while ((a != b) && (a != (-1)));
-       stty_ctdl(0);
-       screen_set();
-}
-
-/*
- * <.A>ide <F>ile <D>elete command
- */
-void deletefile(CtdlIPC *ipc)
-{
-       char filename[32];
-       char buf[256];
-
-       newprompt("Filename: ", filename, 31);
-       if (IsEmptyStr(filename))
-               return;
-       CtdlIPCDeleteFile(ipc, filename, buf);
-       err_printf("%s\n", buf);
-}
-
-
-/*
- * <.A>ide <F>ile <M>ove command
- */
-void movefile(CtdlIPC *ipc)
-{
-       char filename[64];
-       char newroom[ROOMNAMELEN];
-       char buf[256];
-
-       newprompt("Filename: ", filename, 63);
-       if (IsEmptyStr(filename))
-               return;
-       newprompt("Enter target room: ", newroom, ROOMNAMELEN - 1);
-       CtdlIPCMoveFile(ipc, filename, newroom, buf);
-       err_printf("%s\n", buf);
-}
-
-
-/* 
- * list of users who have filled out a bio
- */
-void list_bio(CtdlIPC *ipc)
-{
-       char buf[256];
-       char *resp = NULL;
-       int pos = 1;
-       int r;                  /* IPC response code */
-
-       r = CtdlIPCListUsersWithBios(ipc, &resp, buf);
-       if (r / 100 != 1) {
-               pprintf("%s\n", buf);
-               return;
-       }
-       while (resp && !IsEmptyStr(resp)) {
-               extract_token(buf, resp, 0, '\n', sizeof buf);
-               remove_token(resp, 0, '\n');
-               if ((pos + strlen(buf) + 5) > screenwidth) {
-                       pprintf("\n");
-                       pos = 1;
-               }
-               pprintf("%s, ", buf);
-               pos = pos + strlen(buf) + 2;
-       }
-       pprintf("%c%c  \n\n", 8, 8);
-       if (resp) free(resp);
-}
-
-
-/*
- * read bio
- */
-void read_bio(CtdlIPC *ipc)
-{
-       char who[256];
-       char buf[256];
-       char *resp = NULL;
-       int r;                  /* IPC response code */
-
-       do {
-               newprompt("Read bio for who ('?' for list) : ", who, 25);
-               pprintf("\n");
-               if (!strcmp(who, "?"))
-                       list_bio(ipc);
-       } while (!strcmp(who, "?"));
-
-       r = CtdlIPCGetBio(ipc, who, &resp, buf);
-       if (r / 100 != 1) {
-               pprintf("%s\n", buf);
-               return;
-       }
-       while (!IsEmptyStr(resp)) {
-               extract_token(buf, resp, 0, '\n', sizeof buf);
-               remove_token(resp, 0, '\n');
-               pprintf("%s\n", buf);
-       }
-       if (resp) free(resp);
-}
-
-
-
diff --git a/citadel/routines2.h b/citadel/routines2.h
deleted file mode 100644 (file)
index ba1c179..0000000
+++ /dev/null
@@ -1,15 +0,0 @@
-/* $Id$ */
-void updatels(CtdlIPC *ipc);
-void updatelsa(CtdlIPC *ipc);
-void movefile(CtdlIPC *ipc);
-void deletefile(CtdlIPC *ipc);
-void netsendfile(CtdlIPC *ipc);
-void entregis(CtdlIPC *ipc);
-void subshell(void);
-void upload(CtdlIPC *ipc, int c);
-void cli_upload(CtdlIPC *ipc);
-void validate(CtdlIPC *ipc);
-void read_bio(CtdlIPC *ipc);
-void cli_image_upload(CtdlIPC *ipc, char *keyname);
-int room_prompt(unsigned int qrflags);
-int val_user(CtdlIPC *ipc, char *user, int do_validate);
diff --git a/citadel/screen.c b/citadel/screen.c
deleted file mode 100644 (file)
index a1d8abd..0000000
+++ /dev/null
@@ -1,558 +0,0 @@
-/* $Id$ */
-
-/*
- * Handle full-screen curses stuff
- */
-
-#include <stdlib.h>
-#include <unistd.h>
-#include <stdio.h>
-#include <signal.h>
-#include <string.h>
-#include <stdarg.h>
-#include <sys/types.h>
-#include "sysdep.h"
-#ifdef HAVE_VW_PRINTW
-#define _vwprintw vw_printw
-#else
-/* SYSV style curses (Solaris, etc.) */
-#define _vwprintw vwprintw
-#endif
-#ifndef HAVE_SNPRINTF
-#include "snprintf.h"
-#endif
-#include "citadel.h"
-#include "citadel_ipc.h"
-#include "citadel_decls.h"
-#include "commands.h"
-#include "screen.h"
-
-#if defined(HAVE_CURSES_H) && !defined(DISABLE_CURSES)
-static SCREEN *myscreen = NULL;
-static WINDOW *mainwindow = NULL;
-static WINDOW *statuswindow = NULL;
-
-char rc_screen;
-#endif
-char arg_screen;
-
-extern int screenheight;
-extern int screenwidth;
-extern int rc_ansi_color;
-extern void check_screen_dims(void);
-
-void do_keepalive(void);
-
-
-int is_curses_enabled(void) {
-#if defined(HAVE_CURSES_H) && !defined(DISABLE_CURSES)
-       return mainwindow != NULL;
-#else
-       return 0;
-#endif
-}
-
-/*
- * status_line() is a convenience function for writing a "typical"
- * status line to the window.
- */
-void status_line(const char *humannode, const char *site_location,
-                const char *room_name, int secure, int newmailcount)
-{
-#if defined(HAVE_CURSES_H) && !defined(DISABLE_CURSES)
-       if (statuswindow) {
-               if (secure) {
-                       sln_printf("Encrypted ");
-                       waddch(statuswindow, ACS_VLINE);
-                       waddch(statuswindow, ' ');
-               }
-               if (room_name)
-                       sln_printf("%s on ", room_name);
-               if (humannode)
-                       sln_printf("%s ", humannode);
-               if (newmailcount > 0) {
-                       waddch(statuswindow, ACS_VLINE);
-                       sln_printf(" %d new mail ", newmailcount);
-               }
-               sln_printf("\n");
-       }
-#endif /* HAVE_CURSES_H */
-}
-
-
-/*
- * Display a 3270-style "wait" indicator at the bottom of the screen
- */
-#if defined(HAVE_CURSES_H) && !defined(DISABLE_CURSES)
-void wait_indicator(int state) {
-
-       if (statuswindow && !isendwin()) {
-
-               mvwinch(statuswindow, 0, screenwidth - 2);
-               switch (state) {
-               default:
-               case 0:         /* Idle */
-                       waddch(statuswindow, ' ');
-                       break;
-               case 1:         /* Waiting */
-                       waddch(statuswindow, 'X');
-                       break;
-               case 2:         /* Receiving */
-                       waddch(statuswindow, '<');
-                       break;
-               case 3:         /* Sending */
-                       waddch(statuswindow, '>');
-                       break;
-               }
-               waddch(statuswindow, '\r');
-               wrefresh(statuswindow);
-               wrefresh(mainwindow);   /* this puts the cursor back */
-       }
-}
-#else
-void wait_indicator(int state) {}
-#endif
-
-
-/*
- * Initialize the screen.  If newterm() fails, myscreen will be NULL and
- * further handlers will assume we should be in line mode.
- */
-void screen_new(void)
-{
-#if defined(HAVE_CURSES_H) && !defined(DISABLE_CURSES)
-       if (arg_screen != RC_NO && rc_screen != RC_NO)
-               myscreen = newterm(NULL, stdout, stdin);
-       if (myscreen) {
-               cbreak();
-               noecho();
-               nonl();
-               intrflush(stdscr, FALSE);
-               keypad(stdscr, TRUE);
-               /* Setup all our colors */
-               start_color();
-               if (rc_ansi_color)
-                       enable_color = 1;
-               /*init_pair(DIM_BLACK, COLOR_BLACK, COLOR_BLACK);*/
-               init_pair(DIM_RED, COLOR_RED, COLOR_BLACK);
-               init_pair(DIM_GREEN, COLOR_GREEN, COLOR_BLACK);
-               init_pair(DIM_YELLOW, COLOR_YELLOW, COLOR_BLACK);
-               init_pair(DIM_BLUE, COLOR_BLUE, COLOR_BLACK);
-               init_pair(DIM_MAGENTA, COLOR_MAGENTA, COLOR_BLACK);
-               init_pair(DIM_CYAN, COLOR_CYAN, COLOR_BLACK);
-               init_pair(DIM_WHITE, COLOR_WHITE, COLOR_BLACK);
-
-               if (COLOR_PAIRS > 8)
-                       init_pair(8, COLOR_WHITE, COLOR_BLUE);
-       } else
-#endif /* HAVE_CURSES_H */
-       {
-               send_ansi_detect();
-               look_for_ansi();
-               cls(0);
-               color(DIM_WHITE);
-       }
-       screen_set();
-       windows_new();
-}
-
-
-/*
- * Kill the screen completely (used at exit).  It is safe to call this
- * function more than once.
- */
-void screen_delete(void)
-{
-       windows_delete();
-       screen_reset();
-#if defined(HAVE_CURSES_H) && !defined(DISABLE_CURSES)
-       if (myscreen) {
-               delscreen(myscreen);
-       }
-       myscreen = NULL;
-#endif
-}
-
-/*
- * Beep.
- */
-void ctdl_beep(void) {
-#if defined(HAVE_CURSES_H) && !defined(DISABLE_CURSES)
-       if (myscreen)
-               beep();
-       else
-#endif
-       putc(7, stdout);
-}
-       
-
-
-/*
- * Set screen/IO parameters, e.g. at start of program or return from external
- * program run.
- */
-int screen_set(void)
-{
-#if defined(HAVE_CURSES_H) && !defined(DISABLE_CURSES)
-       if (myscreen) {
-               set_term(myscreen);
-               wrefresh(curscr);
-               return 1;
-       }
-#endif /* HAVE_CURSES_H */
-       return 0;
-}
-
-
-/*
- * Reset screen/IO parameters, e.g. at exit or fork of external program.
- */
-int screen_reset(void)
-{
-#if defined(HAVE_CURSES_H) && !defined(DISABLE_CURSES)
-       if (myscreen) {
-               if (!isendwin()) endwin();
-               return 1;
-       }
-#endif /* HAVE_CURSES_H */
-       return 0;
-}
-
-
-/*
- * scr_printf() outputs to the main window (or screen if not in curses)
- */
-int scr_printf(char *fmt, ...)
-{
-       va_list ap;
-       register int retval;
-
-       va_start(ap, fmt);
-#if defined(HAVE_CURSES_H) && !defined(DISABLE_CURSES)
-       if (mainwindow) {
-               retval = _vwprintw(mainwindow, fmt, ap);
-       } else
-#endif
-               retval = vprintf(fmt, ap);
-       va_end(ap);
-       return retval;
-}
-
-
-/*
- * err_printf() outputs to error status window (or stderr if not in curses)
- */
-int err_printf(char *fmt, ...)
-{
-       va_list ap;
-       register int retval;
-
-       va_start(ap, fmt);
-#if defined(HAVE_CURSES_H) && !defined(DISABLE_CURSES)
-       if (mainwindow) {               /* FIXME: direct to error window */
-               retval = _vwprintw(mainwindow, fmt, ap);
-               if (fmt[strlen(fmt) - 1] == '\n')
-                       wrefresh(mainwindow);
-       } else
-#endif
-               retval = vfprintf(stderr, fmt, ap);
-       va_end(ap);
-       return retval;
-}
-
-
-/*
- * sln_printf() outputs to error status window (or stderr if not in curses)
- */
-int sln_printf(char *fmt, ...)
-{
-       va_list ap;
-       register int retval;
-#if defined(HAVE_CURSES_H) && !defined(DISABLE_CURSES)
-       static char buf[4096];
-#endif
-
-       va_start(ap, fmt);
-#if defined(HAVE_CURSES_H) && !defined(DISABLE_CURSES)
-       if (statuswindow) {
-               register char *i;
-               
-               retval = vsnprintf(buf, 4096, fmt, ap);
-               for (i = buf; *i; i++) {
-                       if (*i == '\r' || *i == '\n')
-                               wclrtoeol(statuswindow);
-                       sln_putc(*i);
-                       if (*i == '\r' || *i == '\n') {
-                               wrefresh(statuswindow);
-                               mvwinch(statuswindow, 0, 0);
-                       }
-               }
-       } else
-#endif
-               retval = vprintf(fmt, ap);
-       va_end(ap);
-       return retval;
-}
-
-
-/*
- * sln_printf_if() outputs to status window, no output if not in curses
- */
-int sln_printf_if(char *fmt, ...)
-{
-       register int retval = 1;
-#if defined(HAVE_CURSES_H) && !defined(DISABLE_CURSES)
-       static char buf[4096];
-       va_list ap;
-
-       va_start(ap, fmt);
-       if (statuswindow) {
-               register char *i;
-               
-               retval = vsnprintf(buf, 4096, fmt, ap);
-               for (i = buf; *i; i++) {
-                       if (*i == '\r' || *i == '\n')
-                               wclrtoeol(statuswindow);
-                       sln_putc(*i);
-                       if (*i == '\r' || *i == '\n') {
-                               wrefresh(statuswindow);
-                               mvwinch(statuswindow, 0, 0);
-                       }
-               }
-       }
-       va_end(ap);
-#endif
-       return retval;
-}
-
-
-int scr_getc(int delay)
-{
-  unsigned char buf;
-
-#if defined(HAVE_CURSES_H) && !defined(DISABLE_CURSES)
-       if (mainwindow) {
-               wtimeout(mainwindow, delay);
-               return wgetch(mainwindow);
-       }
-#endif
-
-       buf = '\0';
-       if (!read (0, &buf, 1))
-               logoff(NULL, 3);
-       return buf;
-}
-
-/* the following is unused and looks broken, but there may
-   be some input problems still lurking in curses mode, so
-   i'll leave it blocked out for now for informational
-   purposes. */
-#if 0
-int scr_blockread(void)
-  {
-    int a = 0;
-#if defined(HAVE_CURSES_H) && !defined(DISABLE_CURSES)
-    wtimeout(mainwindow, S_KEEPALIVE); 
-    while (1)
-      {
-        do_keepalive();
-        a = wgetch(mainwindow); /* will block for food */
-        if (a != ERR)
-          break;
-        /* a = scr_getc(); */
-      }
-#endif
-    return a;
-  }
-#endif /* 0 */
-
-/*
- * scr_putc() outputs a single character
- */
-int scr_putc(int c)
-{
-#if defined(HAVE_CURSES_H) && !defined(DISABLE_CURSES)
-       if (mainwindow) {
-               if (c == 7) beep();
-               if (waddch(mainwindow, c) != OK)
-                       logoff(NULL, 3);
-               return c;
-       }
-#endif
-       if (putc(c, stdout) == EOF)
-               logoff(NULL, 3);
-       return c;
-}
-
-
-int sln_putc(int c)
-{
-#if defined(HAVE_CURSES_H) && !defined(DISABLE_CURSES)
-       if (statuswindow)
-               return ((waddch(statuswindow, c) == OK) ? c : EOF);
-#endif
-       return putc(c, stdout);
-}
-
-
-int sln_putc_if(int c)
-{
-#if defined(HAVE_CURSES_H) && !defined(DISABLE_CURSES)
-       if (statuswindow)
-               return ((waddch(statuswindow, c) == OK) ? c : EOF);
-#endif
-       return 1;
-}
-
-
-/*
- * scr_color() sets the window color for mainwindow
- */
-int scr_color(int colornum)
-{
-#if defined(HAVE_CURSES_H) && !defined(DISABLE_CURSES)
-       if (mainwindow) {
-#ifdef HAVE_WCOLOR_SET
-               wcolor_set(mainwindow, (colornum & 7), NULL);
-#else
-               wattron(mainwindow, COLOR_PAIR((colornum & 7)));
-#endif
-               if (colornum & 8) {
-                       wattron(mainwindow, A_BOLD);
-               } else {
-                       wattroff(mainwindow, A_BOLD);
-               }
-               return 1;
-       }
-#endif
-       return 0;
-}
-
-
-void scr_flush(void)
-{
-#if defined(HAVE_CURSES_H) && !defined(DISABLE_CURSES)
-       if (mainwindow)
-               wrefresh(mainwindow);
-       else
-#endif
-               fflush(stdout);
-}
-
-
-void err_flush(void)
-{
-#if defined(HAVE_CURSES_H) && !defined(DISABLE_CURSES)
-       if (mainwindow)         /* FIXME: error status window needed */
-               wrefresh(mainwindow);
-       else
-#endif
-               fflush(stderr);
-}
-
-
-void sln_flush(void)
-{
-#if defined(HAVE_CURSES_H) && !defined(DISABLE_CURSES)
-       if (statuswindow)
-               wrefresh(statuswindow);
-       else
-#endif
-               fflush(stdout);
-}
-
-static volatile int caught_sigwinch = 0;
-
-/*
- * this is not supposed to be called from a signal handler.
- */
-int scr_set_windowsize(CtdlIPC* ipc)
-{
-#if defined(HAVE_CURSES_H) && !defined(DISABLE_CURSES)
-       if (mainwindow && caught_sigwinch) {
-               caught_sigwinch = 0;
-#ifdef HAVE_RESIZETERM
-               resizeterm(screenheight + 1, screenwidth);
-#endif
-#ifdef HAVE_WRESIZE
-               wresize(mainwindow, screenheight, screenwidth);
-               wresize(statuswindow, 1, screenwidth);
-#endif
-               mvwin(statuswindow, screenheight, 0);
-               status_line(ipc->ServInfo.humannode, ipc->ServInfo.site_location,
-                               room_name, secure, -1);
-               wnoutrefresh(mainwindow);
-               wnoutrefresh(statuswindow);
-               doupdate();
-               return 1;
-       }
-#endif /* HAVE_CURSES_H */
-       return 0;
-}
-
-/*
- * scr_winch() handles window size changes from SIGWINCH
- * resizes all our windows for us
- */
-RETSIGTYPE scr_winch(int signum)
-{
-       /* if we receive this signal, we must be running
-          in a terminal that supports resizing. */
-       have_xterm = 1;
-       caught_sigwinch = 1;
-       check_screen_dims();
-       signal(SIGWINCH, scr_winch);
-}
-
-
-/*
- * Initialize the window(s) we will be using.
- */
-void windows_new(void)
-{
-#if defined(HAVE_CURSES_H) && !defined(DISABLE_CURSES)
-       register int x, y;
-
-       if (myscreen) {
-               getmaxyx(stdscr, y, x);
-               mainwindow = newwin(y - 1, x, 0, 0);
-               screenwidth = x;
-               screenheight = y - 1;
-               immedok(mainwindow, FALSE);
-               leaveok(mainwindow, FALSE);
-               scrollok(mainwindow, TRUE);
-               statuswindow = newwin(1, x, y - 1, 0);
-
-               if (COLOR_PAIRS > 8)
-                       wbkgdset(statuswindow, ' ' | COLOR_PAIR(8));
-               else
-                       wbkgdset(statuswindow, ' ' | COLOR_PAIR(DIM_WHITE));
-
-               werase(statuswindow);
-               immedok(statuswindow, FALSE);
-               leaveok(statuswindow, FALSE);
-               scrollok(statuswindow, FALSE);
-               wrefresh(statuswindow);
-       }
-#else /* HAVE_CURSES_H */
-
-#endif /* HAVE_CURSES_H */
-}
-
-
-/*
- * Deinitialize the window(s) we were using (at exit).
- */
-void windows_delete(void)
-{
-#if defined(HAVE_CURSES_H) && !defined(DISABLE_CURSES)
-       if (mainwindow)
-               delwin(mainwindow);
-       mainwindow = NULL;
-       if (statuswindow)
-               delwin(statuswindow);
-       statuswindow = NULL;
-#else /* HAVE_CURSES_H */
-
-#endif /* HAVE_CURSES_H */
-}
diff --git a/citadel/screen.h b/citadel/screen.h
deleted file mode 100644 (file)
index 0297503..0000000
+++ /dev/null
@@ -1,41 +0,0 @@
-/* $Id$ */
-
-/* client code may need the ERR define: */
-
-#ifndef DISABLE_CURSES
-#ifdef HAVE_NCURSES_H
-#include <ncurses.h>
-#elif defined(HAVE_CURSES_H)
-#include <curses.h>
-#endif
-#endif
-
-void status_line(const char *humannode, const char *site_location,
-                const char *room_name, int secure, int newmailcount);
-void screen_new(void);
-void screen_delete(void);
-int screen_set(void);
-int screen_reset(void);
-int scr_printf(char *fmt, ...);
-int err_printf(char *fmt, ...);
-int sln_printf(char *fmt, ...);
-int sln_printf_if(char *fmt, ...);
-
-#define SCR_NOBLOCK 0
-#define SCR_BLOCK -1
-int scr_getc(int delay);
-
-int scr_putc(int c);
-int sln_putc(int c);
-int scr_color(int colornum);
-void scr_flush(void);
-void err_flush(void);
-void sln_flush(void);
-int scr_set_windowsize(CtdlIPC* ipc);
-void windows_new(void);
-void windows_delete(void);
-int scr_blockread(void);
-int is_curses_enabled(void);
-RETSIGTYPE scr_winch(int signum);
-void wait_indicator(int state);
-void ctdl_beep(void);
diff --git a/citadel/sendcommand.c b/citadel/sendcommand.c
deleted file mode 100644 (file)
index 5012877..0000000
+++ /dev/null
@@ -1,315 +0,0 @@
-/*
- * $Id$
- *
- * Command-line utility to transmit a server command.
- *
- */
-
-
-#include <stdlib.h>
-#include <unistd.h>
-#include <sys/types.h>
-#include <sys/wait.h>
-#include <string.h>
-#include <fcntl.h>
-#include <stdio.h>
-#include <ctype.h>
-
-#if TIME_WITH_SYS_TIME
-# include <sys/time.h>
-# include <time.h>
-#else
-# if HAVE_SYS_TIME_H
-#  include <sys/time.h>
-# else
-#  include <time.h>
-# endif
-#endif
-
-#include <signal.h>
-#include <errno.h>
-#include <limits.h>
-#include <libcitadel.h>
-#include "citadel.h"
-#include "citadel_ipc.h"
-#include "server.h"
-#include "config.h"
-
-#define LOCKFILE "/tmp/LCK.sendcommand"
-
-static CtdlIPC *ipc = NULL;
-
-/*
- * make sure only one copy of sendcommand runs at a time, using lock files
- */
-int set_lockfile(void)
-{
-       FILE *lfp;
-       int onppid;
-
-       if ((lfp = fopen(LOCKFILE, "r")) != NULL) {
-               fscanf(lfp, "%d", &onppid);
-               fclose(lfp);
-               if (!kill(onppid, 0) || errno == EPERM)
-                       return 1;
-       }
-       lfp = fopen(LOCKFILE, "w");
-       fprintf(lfp, "%ld\n", (long) getpid());
-       fclose(lfp);
-       return (0);
-}
-
-void remove_lockfile(void)
-{
-       unlink(LOCKFILE);
-}
-
-/*
- * Why both cleanup() and nq_cleanup() ?  Notice the alarm() call in
- * cleanup() .  If for some reason sendcommand hangs waiting for the server
- * to clean up, the alarm clock goes off and the program exits anyway.
- * The cleanup() routine makes a check to ensure it's not reentering, in
- * case the ipc module looped it somehow.
- */
-void nq_cleanup(int e)
-{
-       if (e == SIGALRM)
-               fprintf(stderr, "\nWatch dog time out.\n");
-       remove_lockfile();
-       exit(e);
-}
-
-/*
- * send binary to server
- */
-void serv_write(CtdlIPC *ipc, const char *buf, unsigned int nbytes)
-{
-       unsigned int bytes_written = 0;
-       int retval;
-/*
-#if defined(HAVE_OPENSSL)
-       if (ipc->ssl) {
-               serv_write_ssl(ipc, buf, nbytes);
-               return;
-       }
-#endif
-*/
-       while (bytes_written < nbytes) {
-               retval = write(ipc->sock, &buf[bytes_written],
-                              nbytes - bytes_written);
-               if (retval < 1) {
-                       connection_died(ipc, 0);
-                       return;
-               }
-               bytes_written += retval;
-       }
-}
-
-
-void cleanup(int e)
-{
-       static int nested = 0;
-
-       alarm(30);
-       signal(SIGALRM, nq_cleanup);
-       serv_write(ipc, "\n", 1);
-       if (nested++ < 1)
-               CtdlIPCQuit(ipc);
-       nq_cleanup(e);
-}
-
-/*
- * This is implemented as a function rather than as a macro because the
- * client-side IPC modules expect logoff() to be defined.  They call logoff()
- * when a problem connecting or staying connected to the server occurs.
- */
-void logoff(int e)
-{
-       cleanup(e);
-}
-
-/*
- * Connect sendcommand to the Citadel server running on this computer.
- */
-void np_attach_to_server(char *host, char *port)
-{
-       char buf[SIZ];
-       char hostbuf[256], portbuf[256];
-       char *args[] =
-       {"sendcommand", NULL};
-       int r;
-
-       fprintf(stderr, "Attaching to server...\n");
-       strcpy(hostbuf, host);
-       strcpy(portbuf, port);
-       ipc = CtdlIPC_new(1, args, hostbuf, portbuf);
-       if (!ipc) {
-               fprintf(stderr, "Can't connect: %s\n", strerror(errno));
-               exit(3);
-       }
-       CtdlIPC_chat_recv(ipc, buf);
-       fprintf(stderr, "%s\n", &buf[4]);
-       snprintf(buf, sizeof buf, "IPGM %d", config.c_ipgm_secret);
-       r = CtdlIPCInternalProgram(ipc, config.c_ipgm_secret, buf);
-       fprintf(stderr, "%s\n", buf);
-       if (r / 100 != 2) {
-               cleanup(2);
-       }
-}
-
-
-void sendcommand_die(void) {
-       exit(0);
-}
-
-
-
-/*
- * main
- */
-int main(int argc, char **argv)
-{
-       int a;
-       char cmd[SIZ];
-       char buf[SIZ];
-       int watchdog = 60;
-
-       int relh=0;
-       int home=0;
-       char relhome[PATH_MAX]="";
-       char ctdldir[PATH_MAX]=CTDLDIR;
-       fd_set read_fd;
-       struct timeval tv;
-       int ret, err;
-       int server_shutting_down = 0;
-       
-       strcpy(ctdl_home_directory, DEFAULT_PORT);
-
-       strcpy(cmd, "");
-       /*
-        * Change directories if specified
-        */
-       for (a = 1; a < argc; ++a) {
-               if (!strncmp(argv[a], "-h", 2)) {
-                       relh=argv[a][2]!='/';
-                       if (!relh) safestrncpy(ctdl_home_directory, &argv[a][2],
-                                                                  sizeof ctdl_home_directory);
-                       else
-                               safestrncpy(relhome, &argv[a][2],
-                                                       sizeof relhome);
-                       home=1;
-               } else if (!strncmp(argv[a], "-w", 2)) {
-                       watchdog = atoi(&argv[a][2]);
-                       if (watchdog<1)
-                               watchdog=1;
-               } else {
-                       if (!IsEmptyStr(cmd))
-                               strcat(cmd, " ");
-                       strcat(cmd, argv[a]);
-               }
-       }
-
-       calc_dirs_n_files(relh, home, relhome, ctdldir, 0);
-       get_config();
-
-       signal(SIGINT, cleanup);
-       signal(SIGQUIT, cleanup);
-       signal(SIGHUP, cleanup);
-       signal(SIGTERM, cleanup);
-
-       fprintf(stderr, "sendcommand: started (pid=%d) "
-                       "running in %s\n",
-                       (int) getpid(),
-                       ctdl_home_directory);
-       fflush(stderr);
-
-       alarm(watchdog);
-       signal(SIGALRM, nq_cleanup); /* Set up a watchdog type timer in case we hang */
-       
-       np_attach_to_server(UDS, ctdl_home_directory);
-       fflush(stderr);
-       setIPCDeathHook(sendcommand_die);
-
-       fprintf(stderr, "%s\n", cmd);
-       CtdlIPC_chat_send(ipc, cmd);
-       CtdlIPC_chat_recv(ipc, buf);
-       fprintf(stderr, "%s\n", buf);
-
-       tv.tv_sec = 0;
-       tv.tv_usec = 1000;
-
-       if (!strncasecmp(&buf[1], "31", 2)) {
-               server_shutting_down = 1;
-       }
-
-       if (buf[0] == '1') {
-               while (CtdlIPC_chat_recv(ipc, buf), strcmp(buf, "000")) {
-                       printf("%s\n", buf);
-                       alarm(watchdog); /* Kick the watchdog timer */
-               }
-       } else if (buf[0] == '4') {
-               do {
-                       if (fgets(buf, sizeof buf, stdin) == NULL)
-                               strcpy(buf, "000");
-                       if (!IsEmptyStr(buf))
-                               if (buf[strlen(buf) - 1] == '\n')
-                                       buf[strlen(buf) - 1] = 0;
-                       if (!IsEmptyStr(buf))
-                               if (buf[strlen(buf) - 1] == '\r')
-                                       buf[strlen(buf) - 1] = 0;
-                       if (strcmp(buf, "000"))
-                               CtdlIPC_chat_send(ipc, buf);
-                       
-                       FD_ZERO(&read_fd);
-                       FD_SET(ipc->sock, &read_fd);
-                       ret = select(ipc->sock+1, &read_fd, NULL, NULL,  &tv);
-                       err = errno;
-                       if (err!=0)
-                               printf("select failed: %d", err);
-
-                       if (ret == -1) {
-                               if (!(errno == EINTR || errno == EAGAIN))
-                                       printf("select failed: %d", err);
-                               return 1;
-                       }
-
-                       if (ret != 0) {
-                               size_t n;
-                               char rbuf[SIZ];
-
-                               rbuf[0] = '\0';
-                               n = read(ipc->sock, rbuf, SIZ);
-                               if (n>0) {
-                                       rbuf[n]='\0';
-                                       fprintf (stderr, rbuf);
-                                       fflush (stdout);
-                               }
-                       }
-                       alarm(watchdog); /* Kick the watchdog timer */
-               } while (strcmp(buf, "000"));
-               CtdlIPC_chat_send(ipc, "\n");
-               CtdlIPC_chat_send(ipc, "000");
-       }
-       alarm(0);       /* Shutdown the watchdog timer */
-       fprintf(stderr, "sendcommand: processing ended.\n");
-
-       /* Clean up and log off ... unless the server indicated that the command
-        * we sent is shutting it down, in which case we want to just cut the
-        * connection and exit.
-        */
-       if (server_shutting_down) {
-               nq_cleanup(0);
-       }
-       else {
-               cleanup(0);
-       }
-       return 0;
-}
-
-/*
- * Stub function
- */
-void stty_ctdl(int cmd) {
-}
-
-
index b3d110cd8b498b4631621fb143eb0989b65d390d..d57fdcf5fa71cfdfe98f71eddf3674dfb43b6b4d 100644 (file)
@@ -55,7 +55,7 @@
 #include "user_ops.h"
 #include "housekeeping.h"
 #include "svn_revision.h"
-#include "citadel_dirs.c"
+#include "citadel_dirs.h"
 
 #include "modules_init.h"
 #include "ecrash.h"
diff --git a/citadel/setup.c b/citadel/setup.c
deleted file mode 100644 (file)
index 38c8be0..0000000
+++ /dev/null
@@ -1,1514 +0,0 @@
-/*
- * $Id$
- *
- * Citadel setup utility
- *
- */
-
-#include <stdlib.h>
-#include <unistd.h>
-#include <stdio.h>
-#include <string.h>
-#include <ctype.h>
-#include <fcntl.h>
-#include <sys/types.h>
-#include <sys/stat.h>
-#include <sys/utsname.h>
-#include <sys/wait.h>
-#include <signal.h>
-#include <netdb.h>
-#include <errno.h>
-#include <limits.h>
-#include <pwd.h>
-#include <time.h>
-#include <libcitadel.h>
-#include "citadel.h"
-#include "axdefs.h"
-#include "sysdep.h"
-#include "config.h"
-#include "citadel_dirs.h"
-#if HAVE_BACKTRACE
-#include <execinfo.h>
-#endif
-
-
-#define MAXSETUP 11    /* How many setup questions to ask */
-
-#define UI_TEXT                0       /* Default setup type -- text only */
-#define UI_DIALOG      2       /* Use the 'dialog' program */
-#define UI_SILENT      3       /* Silent running, for use in scripts */
-
-#define SERVICE_NAME   "citadel"
-#define PROTO_NAME     "tcp"
-#define NSSCONF                "/etc/nsswitch.conf"
-
-int setup_type;
-char setup_directory[PATH_MAX];
-int using_web_installer = 0;
-int enable_home = 1;
-char admin_pass[SIZ];
-char admin_cmd[SIZ];
-
-char *setup_titles[] =
-{
-       "Citadel Home Directory",
-       "System Administrator",
-       "Administrator Password",
-       "Citadel User ID",
-       "Server IP address",
-       "Server port number",
-       "Authentication mode",
-       "LDAP host",
-       "LDAP port number",
-       "LDAP base DN",
-       "LDAP bind DN",
-       "LDAP bind password"
-};
-
-/**
- * \brief print the actual stack frame.
- */
-void cit_backtrace(void)
-{
-#ifdef HAVE_BACKTRACE
-       void *stack_frames[50];
-       size_t size, i;
-       char **strings;
-
-
-       size = backtrace(stack_frames, sizeof(stack_frames) / sizeof(void*));
-       strings = backtrace_symbols(stack_frames, size);
-       for (i = 0; i < size; i++) {
-               if (strings != NULL)
-                       fprintf(stderr, "%s\n", strings[i]);
-               else
-                       fprintf(stderr, "%p\n", stack_frames[i]);
-       }
-       free(strings);
-#endif
-}
-
-struct config config;
-
-       /* calculate all our path on a central place */
-    /* where to keep our config */
-       
-
-char *setup_text[] = {
-#ifndef HAVE_RUN_DIR
-"Enter the full pathname of the directory in which the Citadel\n"
-"installation you are creating or updating resides.  If you\n"
-"specify a directory other than the default, you will need to\n"
-"specify the -h flag to the server when you start it up.\n",
-#else
-"Enter the subdirectory name for an alternate installation of "
-"Citadel. To do a default installation just leave it blank."
-"If you specify a directory other than the default, you will need to\n"
-"specify the -h flag to the server when you start it up.\n"
-"note that it may not have a leading /",
-#endif
-
-"Enter the name of the system administrator (which is probably\n"
-"you).  When an account is created with this name, it will\n"
-"automatically be given administrator-level access.\n",
-
-"Enter a password for the system administrator. When setup\n"
-"completes it will attempt to create the administrator user\n"
-"and set the password specified here.\n",
-
-"Citadel needs to run under its own user ID.  This would\n"
-"typically be called \"citadel\", but if you are running Citadel\n"
-"as a public BBS, you might also call it \"bbs\" or \"guest\".\n"
-"The server will run under this user ID.  Please specify that\n"
-"user ID here.  You may specify either a user name or a numeric\n"
-"UID.\n",
-
-"Specify the IP address on which your server will run.  If you\n"
-"leave this blank, or if you specify 0.0.0.0, Citadel will listen\n"
-"on all addresses.  You can usually skip this unless you are\n"
-"running multiple instances of Citadel on the same computer.\n",
-
-"Specify the TCP port number on which your server will run.\n"
-"Normally, this will be port 504, which is the official port\n"
-"assigned by the IANA for Citadel servers.  You will only need\n"
-"to specify a different port number if you run multiple instances\n"
-"of Citadel on the same computer and there is something else\n"
-"already using port 504.\n",
-
-
-
-"Specify which authentication mode you wish to use.\n"
-"\n"
-" 0. Self contained authentication\n"
-" 1. Host system integrated authentication\n"
-" 2. External LDAP - RFC 2307 compliant directory\n"
-" 3. External LDAP - nonstandard MS Active Directory\n"
-"\n"
-"For help: http://www.citadel.org/doku.php/faq:installation:authmodes\n"
-"\n"
-"ANSWER \"0\" UNLESS YOU COMPLETELY UNDERSTAND THIS OPTION.\n",
-
-"Please enter the host name or IP address of your LDAP server.\n",
-
-"Please enter the port number of the LDAP service (usually 389).\n",
-
-"Please enter the Base DN to search for authentication\n"
-"(for example: dc=example,dc=com)\n",
-
-"Please enter the DN of an account to use for binding to the LDAP server\n"
-"for performing queries.  The account does not require any other\n"
-"privileges.  If your LDAP server allows anonymous queries, you can.\n"
-"leave this blank.\n",
-
-"If you entered a Bind DN in the previous question, you must now enter\n"
-"the password associated with that account.  Otherwise, you can leave this\n"
-"blank.\n"
-
-};
-
-struct config config;
-int direction;
-
-
-void cleanup(int exitcode)
-{
-//     printf("Exitcode: %d\n", exitcode);
-//     cit_backtrace();
-       exit(exitcode);
-}
-
-
-
-void title(char *text)
-{
-       if (setup_type == UI_TEXT) {
-               printf("\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n<%s>\n", text);
-       }
-}
-
-
-
-int yesno(char *question, int default_value)
-{
-       int i = 0;
-       int answer = 0;
-       char buf[SIZ];
-
-       switch (setup_type) {
-
-       case UI_TEXT:
-               do {
-                       printf("%s\nYes/No [%s] --> ",
-                               question,
-                               ( default_value ? "Yes" : "No" )
-                       );
-                       if (fgets(buf, sizeof buf, stdin))
-                       {
-                               answer = tolower(buf[0]);
-                               if ((buf[0]==0) || (buf[0]==13) || (buf[0]==10))
-                                       answer = default_value;
-                               else if (answer == 'y')
-                                       answer = 1;
-                               else if (answer == 'n')
-                                       answer = 0;
-                       }
-               } while ((answer < 0) || (answer > 1));
-               break;
-
-       case UI_DIALOG:
-               sprintf(buf, "exec %s %s --yesno '%s' 15 75",
-                       getenv("CTDL_DIALOG"),
-                       ( default_value ? "" : "--defaultno" ),
-                       question);
-               i = system(buf);
-               if (i == 0) {
-                       answer = 1;
-               }
-               else {
-                       answer = 0;
-               }
-               break;
-       case UI_SILENT:
-               break;
-
-       }
-       return (answer);
-}
-
-
-void important_message(char *title, char *msgtext)
-{
-       char buf[SIZ];
-
-       switch (setup_type) {
-
-       case UI_TEXT:
-               printf("\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n");
-               printf("       %s \n\n%s\n\n", title, msgtext);
-               printf("Press return to continue...");
-               if (fgets(buf, sizeof buf, stdin));
-               break;
-
-       case UI_DIALOG:
-               sprintf(buf, "exec %s --msgbox '%s' 19 72",
-                       getenv("CTDL_DIALOG"),
-                       msgtext);
-               system(buf);
-               break;
-       case UI_SILENT:
-               fprintf(stderr, "%s\n", msgtext);
-               break;
-       }
-}
-
-void important_msgnum(int msgnum)
-{
-       important_message("Important Message", setup_text[msgnum]);
-}
-
-void display_error(char *error_message)
-{
-       important_message("Error", error_message);
-}
-
-void progress(char *text, long int curr, long int cmax)
-{
-       static long dots_printed = 0L;
-       long a = 0;
-       static FILE *fp = NULL;
-       char buf[SIZ];
-
-       switch (setup_type) {
-
-       case UI_TEXT:
-               if (curr == 0) {
-                       printf("%s\n", text);
-                       printf("..........................");
-                       printf("..........................");
-                       printf("..........................\r");
-                       fflush(stdout);
-                       dots_printed = 0;
-               } else if (curr == cmax) {
-                       printf("\r%79s\n", "");
-               } else {
-                       a = (curr * 100) / cmax;
-                       a = a * 78;
-                       a = a / 100;
-                       while (dots_printed < a) {
-                               printf("*");
-                               ++dots_printed;
-                               fflush(stdout);
-                       }
-               }
-               break;
-
-       case UI_DIALOG:
-               if (curr == 0) {
-                       sprintf(buf, "exec %s --gauge '%s' 7 72 0",
-                               getenv("CTDL_DIALOG"),
-                               text);
-                       fp = popen(buf, "w");
-                       if (fp != NULL) {
-                               fprintf(fp, "0\n");
-                               fflush(fp);
-                       }
-               } 
-               else if (curr == cmax) {
-                       if (fp != NULL) {
-                               fprintf(fp, "100\n");
-                               pclose(fp);
-                               fp = NULL;
-                       }
-               }
-               else {
-                       a = (curr * 100) / cmax;
-                       if (fp != NULL) {
-                               fprintf(fp, "%ld\n", a);
-                               fflush(fp);
-                       }
-               }
-               break;
-       case UI_SILENT:
-               break;
-
-       }
-}
-
-
-
-/*
- * check_services_entry()  -- Make sure "citadel" is in /etc/services
- *
- */
-void check_services_entry(void)
-{
-       int i;
-       FILE *sfp;
-       char errmsg[256];
-
-       if (getservbyname(SERVICE_NAME, PROTO_NAME) == NULL) {
-               for (i=0; i<=2; ++i) {
-                       progress("Adding service entry...", i, 2);
-                       if (i == 0) {
-                               sfp = fopen("/etc/services", "a");
-                               if (sfp == NULL) {
-                                       sprintf(errmsg, "Cannot open /etc/services: %s", strerror(errno));
-                                       display_error(errmsg);
-                               } else {
-                                       fprintf(sfp, "%s                504/tcp\n", SERVICE_NAME);
-                                       fclose(sfp);
-                               }
-                       }
-               }
-       }
-}
-
-
-
-
-/*
- * delete_inittab_entry()  -- Remove obsolete /etc/inittab entry for Citadel
- *
- */
-void delete_inittab_entry(void)
-{
-       FILE *infp;
-       FILE *outfp;
-       char looking_for[256];
-       char buf[1024];
-       char outfilename[32];
-       int changes_made = 0;
-
-       /* Determine the fully qualified path name of citserver */
-       snprintf(looking_for, 
-                sizeof looking_for,
-                "%s/citserver", 
-                ctdl_sbin_dir
-               );
-
-       /* Now tweak /etc/inittab */
-       infp = fopen("/etc/inittab", "r");
-       if (infp == NULL) {
-
-               /* If /etc/inittab does not exist, return quietly.
-                * Not all host platforms have it.
-                */
-               if (errno == ENOENT) {
-                       return;
-               }
-
-               /* Other errors might mean something really did go wrong.
-                */
-               sprintf(buf, "Cannot open /etc/inittab: %s", strerror(errno));
-               display_error(buf);
-               return;
-       }
-
-       strcpy(outfilename, "/tmp/ctdlsetup.XXXXXX");
-       outfp = fdopen(mkstemp(outfilename), "w+");
-       if (outfp == NULL) {
-               sprintf(buf, "Cannot open %s: %s", outfilename, strerror(errno));
-               display_error(buf);
-               fclose(infp);
-               return;
-       }
-
-       while (fgets(buf, sizeof buf, infp) != NULL) {
-               if (strstr(buf, looking_for) != NULL) {
-                       fwrite("#", 1, 1, outfp);
-                       ++changes_made;
-               }
-               fwrite(buf, strlen(buf), 1, outfp);
-       }
-
-       fclose(infp);
-       fclose(outfp);
-
-       if (changes_made) {
-               sprintf(buf, "/bin/mv -f %s /etc/inittab 2>/dev/null", outfilename);
-               system(buf);
-               system("/sbin/init q 2>/dev/null");
-       }
-       else {
-               unlink(outfilename);
-       }
-}
-
-
-/*
- * install_init_scripts()  -- Try to configure to start Citadel at boot
- *
- */
-void install_init_scripts(void)
-{
-       struct stat etcinitd;
-       FILE *fp;
-       char *initfile = "/etc/init.d/citadel";
-       char command[SIZ];
-
-       if ((stat("/etc/init.d/", &etcinitd) == -1) && 
-           (errno == ENOENT))
-       {
-               if ((stat("/etc/rc.d/init.d/", &etcinitd) == -1) &&
-                   (errno == ENOENT))
-                       initfile = CTDLDIR"/citadel.init";
-               else
-                       initfile = "/etc/rc.d/init.d/citadel";
-       }
-
-       fp = fopen(initfile, "r");
-       if (fp != NULL) {
-               if (yesno("Citadel already appears to be configured to start at boot.\n"
-                         "Would you like to keep your boot configuration as is?\n", 1) == 1) {
-                       return;
-               }
-               fclose(fp);
-               
-       }
-
-       if (yesno("Would you like to automatically start Citadel at boot?\n", 1) == 0) {
-               return;
-       }
-
-       fp = fopen(initfile, "w");
-       if (fp == NULL) {
-               display_error("Cannot create /etc/init.d/citadel");
-               return;
-       }
-
-       fprintf(fp,     "#!/bin/sh\n"
-               "#\n"
-               "# Init file for Citadel\n"
-               "#\n"
-               "# chkconfig: - 79 30\n"
-               "# description: Citadel service\n"
-               "# processname: citserver\n"
-               "# pidfile: %s/citadel.pid\n\n"
-               "# uncomment this to create coredumps as described in\n"
-               "# http://www.citadel.org/doku.php/faq:mastering_your_os:gdb#how.do.i.make.my.system.produce.core-files\n"
-               "# ulimit -c unlimited\n"
-               "\n"
-               "CITADEL_DIR=%s\n"
-               ,
-               setup_directory,
-               setup_directory
-               );
-       fprintf(fp,     "\n"
-               "test -d /var/run || exit 0\n"
-               "\n"
-               "case \"$1\" in\n"
-               "\n"
-               "start)         echo -n \"Starting Citadel... \"\n"
-               "               if $CITADEL_DIR/citserver -lmail -d -h$CITADEL_DIR\n"
-               "               then\n"
-               "                       echo \"ok\"\n"
-               "               else\n"
-               "                       echo \"failed\"\n"
-               "               fi\n");
-       fprintf(fp,     "               ;;\n"
-               "stop)          echo -n \"Stopping Citadel... \"\n"
-               "               if $CITADEL_DIR/sendcommand DOWN >/dev/null 2>&1 ; then\n"
-               "                       echo \"ok\"\n"
-               "               else\n"
-               "                       echo \"failed\"\n"
-               "               fi\n"
-               "               rm -f %s/citadel.pid 2>/dev/null\n"
-               ,
-               setup_directory
-               );
-       fprintf(fp,     "               ;;\n"
-               "restart)       if $CITADEL_DIR/sendcommand DOWN 1 >/dev/null 2>&1 ; then\n"
-               "                       echo \"ok\"\n"
-               "               else\n"
-               "                       echo \"failed\"\n"
-               "               fi\n"
-               "               ;;\n"
-               "*)             echo \"Usage: $0 {start|stop|restart}\"\n"
-               "               exit 1\n"
-               "               ;;\n"
-               "esac\n"
-               );
-
-       fclose(fp);
-       chmod(initfile, 0755);
-
-       /* Set up the run levels. */
-       system("/bin/rm -f /etc/rc?.d/[SK]??citadel 2>/dev/null");
-       snprintf(command, sizeof(command), "for x in 2 3 4 5 ; do [ -d /etc/rc$x.d ] && ln -s %s /etc/rc$x.d/S79citadel ; done 2>/dev/null", initfile);
-       system(command);
-       snprintf(command, sizeof(command),"for x in 0 6 S; do [ -d /etc/rc$x.d ] && ln -s %s /etc/rc$x.d/K30citadel ; done 2>/dev/null", initfile);
-       system(command);
-
-}
-
-
-
-
-
-
-/*
- * On systems which use xinetd, see if we can offer to install Citadel as
- * the default telnet target.
- */
-void check_xinetd_entry(void) {
-       char *filename = "/etc/xinetd.d/telnet";
-       FILE *fp;
-       char buf[SIZ];
-       int already_citadel = 0;
-
-       fp = fopen(filename, "r+");
-       if (fp == NULL) return;         /* Not there.  Oh well... */
-
-       while (fgets(buf, sizeof buf, fp) != NULL) {
-               if (strstr(buf, setup_directory) != NULL) already_citadel = 1;
-       }
-       fclose(fp);
-       if (already_citadel) return;    /* Already set up this way. */
-
-       /* Otherwise, prompt the user to create an entry. */
-       if (getenv("CREATE_XINETD_ENTRY") != NULL) {
-               if (strcasecmp(getenv("CREATE_XINETD_ENTRY"), "yes")) {
-                       return;
-               }
-       }
-       else {
-               snprintf(buf, sizeof buf,
-                        "Setup can configure the \"xinetd\" service to automatically\n"
-                        "connect incoming telnet sessions to Citadel, bypassing the\n"
-                        "host system login: prompt.  Would you like to do this?\n"
-                       );
-               if (yesno(buf, 1) == 0) {
-                       return;
-               }
-       }
-
-       fp = fopen(filename, "w");
-       fprintf(fp,
-               "# description: telnet service for Citadel users\n"
-               "service telnet\n"
-               "{\n"
-               "       disable = no\n"
-               "       flags           = REUSE\n"
-               "       socket_type     = stream\n"
-               "       wait            = no\n"
-               "       user            = root\n"
-               "       server          = /usr/sbin/in.telnetd\n"
-               "       server_args     = -h -L %s/citadel\n"
-               "       log_on_failure  += USERID\n"
-               "}\n",
-               ctdl_bin_dir);
-       fclose(fp);
-
-       /* Now try to restart the service */
-       system("/etc/init.d/xinetd restart >/dev/null 2>&1");
-}
-
-
-
-/*
- * Offer to disable other MTA's
- */
-void disable_other_mta(char *mta) {
-       char buf[SIZ];
-       FILE *fp;
-       int lines = 0;
-
-       sprintf(buf, "/bin/ls -l /etc/rc*.d/S*%s 2>/dev/null; "
-               "/bin/ls -l /etc/rc.d/rc*.d/S*%s 2>/dev/null",
-               mta, mta);
-       fp = popen(buf, "r");
-       if (fp == NULL) return;
-
-       while (fgets(buf, sizeof buf, fp) != NULL) {
-               ++lines;
-       }
-       fclose(fp);
-       if (lines == 0) return;         /* Nothing to do. */
-
-
-       /* Offer to replace other MTA with the vastly superior Citadel :)  */
-
-       snprintf(buf, sizeof buf,
-                "You appear to have the \"%s\" email program\n"
-                "running on your system.  If you want Citadel mail\n"
-                "connected with %s, you will have to manually integrate\n"
-                "them.  It is preferable to disable %s, and use Citadel's\n"
-                "SMTP, POP3, and IMAP services.\n\n"
-                "May we disable %s so that Citadel has access to ports\n"
-                "25, 110, and 143?\n",
-                mta, mta, mta, mta
-               );
-       if (yesno(buf, 1) == 0) {
-               return;
-       }
-       
-
-       sprintf(buf, "for x in /etc/rc*.d/S*%s; do mv $x `echo $x |sed s/S/K/g`; done >/dev/null 2>&1", mta);
-       system(buf);
-       sprintf(buf, "/etc/init.d/%s stop >/dev/null 2>&1", mta);
-       system(buf);
-}
-
-
-
-
-/* 
- * Check to see if our server really works.  Returns 0 on success.
- */
-int test_server(char *setup_directory, char *relhomestr, int relhome) {
-       char cmd[256];
-       char cookie[256];
-       FILE *fp;
-       char buf[4096];
-       int found_it = 0;
-
-       /* Generate a silly little cookie.  We're going to write it out
-        * to the server and try to get it back.  The cookie does not
-        * have to be secret ... just unique.
-        */
-       sprintf(cookie, "--test--%d--", getpid());
-
-       if (relhome)
-               sprintf(cmd, "%s/sendcommand -h%s ECHO %s 2>&1",
-                       ctdl_sbin_dir,
-                       relhomestr,
-                       cookie);
-       else
-               sprintf(cmd, "%s/sendcommand ECHO %s 2>&1",
-                       ctdl_sbin_dir,
-                       cookie);
-
-       fp = popen(cmd, "r");
-       if (fp == NULL) return(errno);
-
-       while (fgets(buf, sizeof buf, fp) != NULL) {
-               if ( (buf[0]=='2')
-                    && (strstr(buf, cookie) != NULL) ) {
-                       ++found_it;
-               }
-       }
-       pclose(fp);
-
-       if (found_it) {
-               return(0);
-       }
-       return(-1);
-}
-
-void strprompt(char *prompt_title, char *prompt_text, char *str)
-{
-       char buf[SIZ] = "";
-       char setupmsg[SIZ];
-       char dialog_result[PATH_MAX];
-       FILE *fp = NULL;
-
-       strcpy(setupmsg, "");
-
-       switch (setup_type) {
-       case UI_TEXT:
-               title(prompt_title);
-               printf("\n%s\n", prompt_text);
-               printf("This is currently set to:\n%s\n", str);
-               printf("Enter new value or press return to leave unchanged:\n");
-               if (fgets(buf, sizeof buf, stdin)){
-                       buf[strlen(buf) - 1] = 0;
-               }
-               if (!IsEmptyStr(buf))
-                       strcpy(str, buf);
-               break;
-
-       case UI_DIALOG:
-               CtdlMakeTempFileName(dialog_result, sizeof dialog_result);
-               sprintf(buf, "exec %s --inputbox '%s' 19 72 '%s' 2>%s",
-                       getenv("CTDL_DIALOG"),
-                       prompt_text,
-                       str,
-                       dialog_result);
-               system(buf);
-               fp = fopen(dialog_result, "r");
-               if (fp != NULL) {
-                       if (fgets(str, sizeof buf, fp)) {
-                               if (str[strlen(str)-1] == 10) {
-                                       str[strlen(str)-1] = 0;
-                               }
-                       }
-                       fclose(fp);
-                       unlink(dialog_result);
-               }
-               break;
-       case UI_SILENT:
-               break;
-       }
-}
-
-void set_bool_val(int msgpos, int *ip) {
-       title(setup_titles[msgpos]);
-       *ip = yesno(setup_text[msgpos], *ip);
-}
-
-void set_str_val(int msgpos, char *str) {
-       strprompt(setup_titles[msgpos], setup_text[msgpos], str);
-}
-
-void set_int_val(int msgpos, int *ip)
-{
-       char buf[16];
-       snprintf(buf, sizeof buf, "%d", (int) *ip);
-       set_str_val(msgpos, buf);
-       *ip = atoi(buf);
-}
-
-
-void set_char_val(int msgpos, char *ip)
-{
-       char buf[16];
-       snprintf(buf, sizeof buf, "%d", (int) *ip);
-       set_str_val(msgpos, buf);
-       *ip = (char) atoi(buf);
-}
-
-
-void set_long_val(int msgpos, long int *ip)
-{
-       char buf[16];
-       snprintf(buf, sizeof buf, "%ld", *ip);
-       set_str_val(msgpos, buf);
-       *ip = atol(buf);
-}
-
-
-void edit_value(int curr)
-{
-       int i;
-       struct passwd *pw;
-       char ctdluidname[256];
-
-       switch (curr) {
-
-       case 1:
-               if (setup_type == UI_SILENT)
-               {
-                       if (getenv("SYSADMIN_NAME")) {
-                               strcpy(config.c_sysadm, getenv("SYSADMIN_NAME"));
-                       }
-               }
-               else {
-                       set_str_val(curr, config.c_sysadm);
-               }
-               break;
-
-       case 2:
-               if (setup_type == UI_SILENT)
-               {
-                       if (getenv("SYSADMIN_PW")) {
-                               strcpy(admin_pass, getenv("SYSADMIN_PW"));
-                       }
-               }
-               else {
-                       set_str_val(curr, admin_pass);
-               }
-               break;
-       
-       case 3:
-               if (setup_type == UI_SILENT)
-               {               
-                       if (getenv("CITADEL_UID")) {
-                               config.c_ctdluid = atoi(getenv("CITADEL_UID"));
-                       }                                       
-               }
-               else
-               {
-#ifdef __CYGWIN__
-                       config.c_ctdluid = 0;   /* XXX Windows hack, prob. insecure */
-#else
-                       i = config.c_ctdluid;
-                       pw = getpwuid(i);
-                       if (pw == NULL) {
-                               set_int_val(curr, &i);
-                               config.c_ctdluid = i;
-                       }
-                       else {
-                               strcpy(ctdluidname, pw->pw_name);
-                               set_str_val(curr, ctdluidname);
-                               pw = getpwnam(ctdluidname);
-                               if (pw != NULL) {
-                                       config.c_ctdluid = pw->pw_uid;
-                               }
-                               else if (atoi(ctdluidname) > 0) {
-                                       config.c_ctdluid = atoi(ctdluidname);
-                               }
-                       }
-#endif
-               }
-               break;
-
-       case 4:
-               if (setup_type == UI_SILENT)
-               {
-                       if (getenv("IP_ADDR")) {
-                               strcpy(config.c_ip_addr, getenv("IP_ADDR"));
-                       }
-               }
-               else {
-                       set_str_val(curr, config.c_ip_addr);
-               }
-               break;
-
-       case 5:
-               if (setup_type == UI_SILENT)
-               {
-                       if (getenv("CITADEL_PORT")) {
-                               config.c_port_number = atoi(getenv("CITADEL_PORT"));
-                       }
-               }
-               else
-               {
-                       set_int_val(curr, &config.c_port_number);
-               }
-               break;
-
-       case 6:
-               if (setup_type == UI_SILENT)
-               {
-                       const char *auth;
-                       config.c_auth_mode = AUTHMODE_NATIVE;
-                       auth = getenv("ENABLE_UNIX_AUTH");
-                       if (auth != NULL)
-                       {
-                               if ((strcasecmp(auth, "yes") == 0) ||
-                                   (strcasecmp(auth, "host") == 0))
-                               {
-                                       config.c_auth_mode = AUTHMODE_HOST;
-                               }
-                               else if (strcasecmp(auth, "ldap") == 0){
-                                       config.c_auth_mode = AUTHMODE_LDAP;
-                               }
-                               else if ((strcasecmp(auth, "ldap_ad") == 0) ||
-                                        (strcasecmp(auth, "active directory") == 0)){
-                                       config.c_auth_mode = AUTHMODE_LDAP_AD;
-                               }
-                       }
-               }
-               else {
-                       set_int_val(curr, &config.c_auth_mode);
-               }
-               break;
-
-       case 7:
-               if (setup_type == UI_SILENT)
-               {
-                       if (getenv("LDAP_HOST")) {
-                               strcpy(config.c_ldap_host, getenv("LDAP_HOST"));
-                       }
-               }
-               else
-               {
-                       set_str_val(curr, config.c_ldap_host);
-               }
-               break;
-
-       case 8:
-               if (setup_type == UI_SILENT)
-               {
-                       if (getenv("LDAP_PORT")) {
-                               config.c_ldap_port = atoi(getenv("LDAP_PORT"));
-                       }
-               }
-               else
-               {
-                       set_int_val(curr, &config.c_ldap_port);
-               }
-               break;
-
-       case 9:
-               if (setup_type == UI_SILENT)
-               {
-                       if (getenv("LDAP_BASE_DN")) {
-                               strcpy(config.c_ldap_base_dn, getenv("LDAP_BASE_DN"));
-                       }
-               }
-               else
-               {
-                       set_str_val(curr, config.c_ldap_base_dn);
-               }
-               break;
-
-       case 10:
-               if (setup_type == UI_SILENT)
-               {
-                       if (getenv("LDAP_BIND_DN")) {
-                               strcpy(config.c_ldap_bind_dn, getenv("LDAP_BIND_DN"));
-                       }
-               }
-               else
-               {
-                       set_str_val(curr, config.c_ldap_bind_dn);
-               }
-               break;
-
-       case 11:
-               if (setup_type == UI_SILENT)
-               {
-                       if (getenv("LDAP_BIND_PW")) {
-                               strcpy(config.c_ldap_bind_pw, getenv("LDAP_BIND_PW"));
-                       }
-               }
-               else
-               {
-                       set_str_val(curr, config.c_ldap_bind_pw);
-               }
-               break;
-
-       }
-
-}
-
-/*
- * (re-)write the config data to disk
- */
-void write_config_to_disk(void)
-{
-       FILE *fp;
-       int fd;
-
-       if ((fd = creat(file_citadel_config, S_IRUSR | S_IWUSR)) == -1) {
-               display_error("setup: cannot open citadel.config");
-               cleanup(1);
-       }
-       fp = fdopen(fd, "wb");
-       if (fp == NULL) {
-               display_error("setup: cannot open citadel.config");
-               cleanup(1);
-       }
-       fwrite((char *) &config, sizeof(struct config), 1, fp);
-       fclose(fp);
-}
-
-
-
-
-/*
- * Figure out what type of user interface we're going to use
- */
-int discover_ui(void)
-{
-
-       /* Use "dialog" if we have it */
-       if (getenv("CTDL_DIALOG") != NULL) {
-               return UI_DIALOG;
-       }
-               
-       return UI_TEXT;
-}
-
-
-
-
-
-/*
- * Strip "db" entries out of /etc/nsswitch.conf
- */
-void fixnss(void) {
-       FILE *fp_read;
-       int fd_write;
-       char buf[256];
-       char buf_nc[256];
-       char question[512];
-       int i;
-       int changed = 0;
-       int file_changed = 0;
-       char new_filename[64];
-
-       fp_read = fopen(NSSCONF, "r");
-       if (fp_read == NULL) {
-               return;
-       }
-
-       strcpy(new_filename, "/tmp/ctdl_fixnss_XXXXXX");
-       fd_write = mkstemp(new_filename);
-       if (fd_write < 0) {
-               fclose(fp_read);
-               return;
-       }
-
-       while (fgets(buf, sizeof buf, fp_read) != NULL) {
-               changed = 0;
-               strcpy(buf_nc, buf);
-               for (i=0; i<strlen(buf_nc); ++i) {
-                       if (buf_nc[i] == '#') {
-                               buf_nc[i] = 0;
-                       }
-               }
-               for (i=0; i<strlen(buf_nc); ++i) {
-                       if (!strncasecmp(&buf_nc[i], "db", 2)) {
-                               if (i > 0) {
-                                       if ((isspace(buf_nc[i+2])) || (buf_nc[i+2]==0)) {
-                                               changed = 1;
-                                               file_changed = 1;
-                                               strcpy(&buf_nc[i], &buf_nc[i+2]);
-                                               strcpy(&buf[i], &buf[i+2]);
-                                               if (buf[i]==32) {
-                                                       strcpy(&buf_nc[i], &buf_nc[i+1]);
-                                                       strcpy(&buf[i], &buf[i+1]);
-                                               }
-                                       }
-                               }
-                       }
-               }
-               if (write(fd_write, buf, strlen(buf)) != strlen(buf)) {
-                       fclose(fp_read);
-                       close(fd_write);
-                       unlink(new_filename);
-                       return;
-               }
-       }
-
-       fclose(fp_read);
-       
-       if (!file_changed) {
-               unlink(new_filename);
-               return;
-       }
-
-       snprintf(question, sizeof question,
-               "\n"
-               "/etc/nsswitch.conf is configured to use the 'db' module for\n"
-               "one or more services.  This is not necessary on most systems,\n"
-               "and it is known to crash the Citadel server when delivering\n"
-               "mail to the Internet.\n"
-               "\n"
-               "Do you want this module to be automatically disabled?\n"
-               "\n"
-       );
-
-       if (yesno(question, 1)) {
-               sprintf(buf, "/bin/mv -f %s %s", new_filename, NSSCONF);
-               system(buf);
-               chmod(NSSCONF, 0644);
-       }
-       unlink(new_filename);
-}
-
-
-
-
-
-
-
-
-int main(int argc, char *argv[])
-{
-       int a;
-       int curr; 
-       char aaa[128];
-       FILE *fp;
-       int old_setup_level = 0;
-       int info_only = 0;
-       struct utsname my_utsname;
-       struct passwd *pw;
-       struct hostent *he;
-       gid_t gid;
-       int relh=0;
-       int home=0;
-       char relhome[PATH_MAX]="";
-       char ctdldir[PATH_MAX]=CTDLDIR;
-       int rv;
-       
-       /* set an invalid setup type */
-       setup_type = (-1);
-
-       /* Check to see if we're running the web installer */
-       if (getenv("CITADEL_INSTALLER") != NULL) {
-               using_web_installer = 1;
-       }
-
-       /* parse command line args */
-       for (a = 0; a < argc; ++a) {
-               if (!strncmp(argv[a], "-u", 2)) {
-                       strcpy(aaa, argv[a]);
-                       strcpy(aaa, &aaa[2]);
-                       setup_type = atoi(aaa);
-               }
-               else if (!strcmp(argv[a], "-i")) {
-                       info_only = 1;
-               }
-               else if (!strcmp(argv[a], "-q")) {
-                       setup_type = UI_SILENT;
-               }
-               else if (!strncmp(argv[a], "-h", 2)) {
-                       relh=argv[a][2]!='/';
-                       if (!relh) {
-                               safestrncpy(ctdl_home_directory, &argv[a][2], sizeof ctdl_home_directory);
-                       } else {
-                               safestrncpy(relhome, &argv[a][2], sizeof relhome);
-                       }
-                       home = 1;
-               }
-
-       }
-
-       calc_dirs_n_files(relh, home, relhome, ctdldir, 0);
-
-       /* If a setup type was not specified, try to determine automatically
-        * the best one to use out of all available types.
-        */
-       if (setup_type < 0) {
-               setup_type = discover_ui();
-       }
-       if (info_only == 1) {
-               important_message("Citadel Setup", CITADEL);
-               cleanup(0);
-       }
-
-       /* Get started in a valid setup directory. */
-       strcpy(setup_directory, ctdl_run_dir);
-       if ( (using_web_installer) && (getenv("CITADEL") != NULL) ) {
-               strcpy(setup_directory, getenv("CITADEL"));
-       }
-       else {
-               set_str_val(0, setup_directory);
-       }
-
-       enable_home = ( relh | home );
-
-       if (chdir(setup_directory) != 0) {
-               char errmsg[SIZ];
-               sprintf(errmsg, "The directory you specified does not exist: [%s]\n", setup_directory);
-               
-               important_message("Citadel Setup", errmsg);
-               cleanup(errno);
-       }
-
-       /* Determine our host name, in case we need to use it as a default */
-       uname(&my_utsname);
-
-       /* Try to stop Citadel if we can */
-       if (!access("/etc/init.d/citadel", X_OK)) {
-               rv = system("/etc/init.d/citadel stop");
-       }
-
-       /* Make sure Citadel is not running. */
-       if (test_server(setup_directory, relhome, enable_home) == 0) {
-               important_message("Citadel Setup",
-                       "The Citadel service is still running.\n"
-                       "Please stop the service manually and run "
-                       "setup again.");
-               cleanup(1);
-       }
-
-       /* Now begin. */
-       switch (setup_type) {
-
-       case UI_TEXT:
-               printf("\n\n\n"
-                       "              *** Citadel setup program ***\n\n");
-               break;
-
-       }
-
-       /*
-        * What we're going to try to do here is append a whole bunch of
-        * nulls to the citadel.config file, so we can keep the old config
-        * values if they exist, but if the file is missing or from an
-        * earlier version with a shorter config structure, when setup tries
-        * to read the old config parameters, they'll all come up zero.
-        * The length of the config file will be set to what it's supposed
-        * to be when we rewrite it, because we replace the old file with a
-        * completely new copy.
-        */
-       if ((a = open(file_citadel_config, O_WRONLY | O_CREAT | O_APPEND,
-                     S_IRUSR | S_IWUSR)) == -1) {
-               display_error("setup: cannot append citadel.config");
-               cleanup(errno);
-       }
-       fp = fdopen(a, "ab");
-       if (fp == NULL) {
-               display_error("setup: cannot append citadel.config");
-               cleanup(errno);
-       }
-       for (a = 0; a < sizeof(struct config); ++a) {
-               putc(0, fp);
-       }
-       fclose(fp);
-
-       /* now we re-open it, and read the old or blank configuration */
-       fp = fopen(file_citadel_config, "rb");
-       if (fp == NULL) {
-               display_error("setup: cannot open citadel.config");
-               cleanup(errno);
-       }
-       rv = fread((char *) &config, sizeof(struct config), 1, fp);
-       fclose(fp);
-
-       /* set some sample/default values in place of blanks... */
-       if (IsEmptyStr(config.c_nodename))
-               safestrncpy(config.c_nodename, my_utsname.nodename,
-                           sizeof config.c_nodename);
-       strtok(config.c_nodename, ".");
-       if (IsEmptyStr(config.c_fqdn) ) {
-               if ((he = gethostbyname(my_utsname.nodename)) != NULL) {
-                       safestrncpy(config.c_fqdn, he->h_name, sizeof config.c_fqdn);
-               } else {
-                       safestrncpy(config.c_fqdn, my_utsname.nodename, sizeof config.c_fqdn);
-               }
-       }
-       if (IsEmptyStr(config.c_humannode)) {
-               strcpy(config.c_humannode, "My System");
-       }
-       if (IsEmptyStr(config.c_phonenum)) {
-               strcpy(config.c_phonenum, "US 800 555 1212");
-       }
-       if (config.c_initax == 0) {
-               config.c_initax = 4;
-       }
-       if (IsEmptyStr(config.c_moreprompt)) strcpy(config.c_moreprompt, "<more>");
-       if (IsEmptyStr(config.c_twitroom)) strcpy(config.c_twitroom, "Trashcan");
-       if (IsEmptyStr(config.c_baseroom)) strcpy(config.c_baseroom, BASEROOM);
-       if (IsEmptyStr(config.c_aideroom)) strcpy(config.c_aideroom, "Aide");
-       if (config.c_port_number == 0) {
-               config.c_port_number = 504;
-       }
-       if (config.c_sleeping == 0) {
-               config.c_sleeping = 900;
-       }
-       if (config.c_ctdluid == 0) {
-               pw = getpwnam("citadel");
-               if (pw != NULL) {
-                       config.c_ctdluid = pw->pw_uid;
-               }
-       }
-       if (config.c_ctdluid == 0) {
-               pw = getpwnam("bbs");
-               if (pw != NULL) {
-                       config.c_ctdluid = pw->pw_uid;
-               }
-       }
-       if (config.c_ctdluid == 0) {
-               pw = getpwnam("guest");
-               if (pw != NULL) {
-                       config.c_ctdluid = pw->pw_uid;
-               }
-       }
-       if (config.c_createax == 0) {
-               config.c_createax = 3;
-       }
-       /*
-        * Negative values for maxsessions are not allowed.
-        */
-       if (config.c_maxsessions < 0) {
-               config.c_maxsessions = 0;
-       }
-       /* We need a system default message expiry policy, because this is
-        * the top level and there's no 'higher' policy to fall back on.
-        * By default, do not expire messages at all.
-        */
-       if (config.c_ep.expire_mode == 0) {
-               config.c_ep.expire_mode = EXPIRE_MANUAL;
-               config.c_ep.expire_value = 0;
-       }
-
-       /*
-        * Default port numbers for various services
-        */
-       if (config.c_smtp_port == 0) config.c_smtp_port = 25;
-       if (config.c_pop3_port == 0) config.c_pop3_port = 110;
-       if (config.c_imap_port == 0) config.c_imap_port = 143;
-       if (config.c_msa_port == 0) config.c_msa_port = 587;
-       if (config.c_smtps_port == 0) config.c_smtps_port = 465;
-       if (config.c_pop3s_port == 0) config.c_pop3s_port = 995;
-       if (config.c_imaps_port == 0) config.c_imaps_port = 993;
-       if (config.c_pftcpdict_port == 0) config.c_pftcpdict_port = -1;
-       if (config.c_managesieve_port == 0) config.c_managesieve_port = 2020;
-       if (config.c_xmpp_c2s_port == 0) config.c_xmpp_c2s_port = 5222;
-       if (config.c_xmpp_s2s_port == 0) config.c_xmpp_s2s_port = 5269;
-
-       /* Go through a series of dialogs prompting for config info */
-       for (curr = 1; curr <= MAXSETUP; ++curr) {
-               edit_value(curr);
-               if ((curr == 6) && (config.c_auth_mode != AUTHMODE_LDAP) && (config.c_auth_mode != AUTHMODE_LDAP_AD)) {
-                       curr += 5;      /* skip LDAP questions if we're not authenticating against LDAP */
-               }
-       }
-
-/***** begin version update section ***** */
-       /* take care of any updating that is necessary */
-
-       old_setup_level = config.c_setup_level;
-
-       if (old_setup_level == 0) {
-               goto NEW_INST;
-       }
-
-       if (old_setup_level < 555) {
-               important_message("Citadel Setup",
-                                 "This Citadel installation is too old "
-                                 "to be upgraded.");
-               cleanup(1);
-       }
-       write_config_to_disk();
-
-       old_setup_level = config.c_setup_level;
-
-       /* end of version update section */
-
-NEW_INST:
-       config.c_setup_level = REV_LEVEL;
-
-/******************************************/
-
-       write_config_to_disk();
-
-       rv = mkdir(ctdl_info_dir, 0700);
-       rv = chmod(ctdl_info_dir, 0700);
-       rv = chown(ctdl_info_dir, config.c_ctdluid, -1);
-
-       rv = mkdir(ctdl_bio_dir, 0700);
-       rv = chmod(ctdl_bio_dir, 0700);
-       rv = chown(ctdl_bio_dir, config.c_ctdluid, -1);
-
-       rv = mkdir(ctdl_usrpic_dir, 0700);
-       rv = chmod(ctdl_usrpic_dir, 0700);
-       rv = chown(ctdl_usrpic_dir, config.c_ctdluid, -1);
-
-       rv = mkdir(ctdl_message_dir, 0700);
-       rv = chmod(ctdl_message_dir, 0700);
-       rv = chown(ctdl_message_dir, config.c_ctdluid, -1);
-
-       rv = mkdir(ctdl_hlp_dir, 0700);
-       rv = chmod(ctdl_hlp_dir, 0700);
-       rv = chown(ctdl_hlp_dir, config.c_ctdluid, -1);
-
-       rv = mkdir(ctdl_image_dir, 0700);
-       rv = chmod(ctdl_image_dir, 0700);
-       rv = chown(ctdl_image_dir, config.c_ctdluid, -1);
-
-       rv = mkdir(ctdl_bb_dir, 0700);
-       rv = chmod(ctdl_bb_dir, 0700);
-       rv = chown(ctdl_bb_dir, config.c_ctdluid, -1);
-
-       rv = mkdir(ctdl_file_dir, 0700);
-       rv = chmod(ctdl_file_dir, 0700);
-       rv = chown(ctdl_file_dir, config.c_ctdluid, -1);
-
-       rv = mkdir(ctdl_netcfg_dir, 0700);
-       rv = chmod(ctdl_netcfg_dir, 0700);
-       rv = chown(ctdl_netcfg_dir, config.c_ctdluid, -1);
-
-       /* Delete files and directories used by older Citadel versions */
-       rv = system("exec /bin/rm -fr ./rooms ./chatpipes ./expressmsgs ./sessions 2>/dev/null");
-       unlink("citadel.log");
-       unlink("weekly");
-
-       if (((setup_type == UI_SILENT) && (getenv("ALTER_ETC_SERVICES")!=NULL)) || 
-           (setup_type != UI_SILENT))
-               check_services_entry(); /* Check /etc/services */
-#ifndef __CYGWIN__
-       delete_inittab_entry(); /* Remove obsolete /etc/inittab entry */
-       check_xinetd_entry();   /* Check /etc/xinetd.d/telnet */
-
-       if ((getenv("ACT_AS_MTA") == NULL) || 
-           (getenv("ACT_AS_MTA") &&
-            strcasecmp(getenv("ACT_AS_MTA"), "yes") == 0)) {
-               /* Offer to disable other MTA's on the system. */
-               disable_other_mta("courier-authdaemon");
-               disable_other_mta("courier-imap");
-               disable_other_mta("courier-imap-ssl");
-               disable_other_mta("courier-pop");
-               disable_other_mta("courier-pop3");
-               disable_other_mta("courier-pop3d");
-               disable_other_mta("cyrmaster");
-               disable_other_mta("cyrus");
-               disable_other_mta("dovecot");
-               disable_other_mta("exim");
-               disable_other_mta("exim4");
-               disable_other_mta("imapd");
-               disable_other_mta("mta");
-               disable_other_mta("pop3d");
-               disable_other_mta("popd");
-               disable_other_mta("postfix");
-               disable_other_mta("qmail");
-               disable_other_mta("saslauthd");
-               disable_other_mta("sendmail");
-               disable_other_mta("vmailmgrd");
-       }
-#endif
-
-       /* Check for the 'db' nss and offer to disable it */
-       fixnss();
-
-       if ((pw = getpwuid(config.c_ctdluid)) == NULL) {
-               gid = getgid();
-       } else {
-               gid = pw->pw_gid;
-       }
-
-       progress("Setting file permissions", 0, 3);
-       rv = chown(ctdl_run_dir, config.c_ctdluid, gid);
-       progress("Setting file permissions", 1, 3);
-       rv = chown(file_citadel_config, config.c_ctdluid, gid);
-       progress("Setting file permissions", 2, 3);
-       rv = chmod(file_citadel_config, S_IRUSR | S_IWUSR);
-       progress("Setting file permissions", 3, 3);
-
-       /* 
-        * If we're running on SysV, install init scripts.
-        */
-       if (!access("/var/run", W_OK)) {
-
-               if (getenv("NO_INIT_SCRIPTS") == NULL) {
-                       install_init_scripts();
-               }
-
-               if (!access("/etc/init.d/citadel", X_OK)) {
-                       rv = system("/etc/init.d/citadel start");
-                       sleep(3);
-               }
-
-               if (test_server(setup_directory, relhome, enable_home) == 0) {
-                       char buf[SIZ];
-                       int found_it = 0;
-
-                       if (config.c_auth_mode == AUTHMODE_NATIVE) {
-                               snprintf (admin_cmd, sizeof(admin_cmd), "%s/sendcommand \"CREU %s|%s\" 2>&1", 
-                                       ctdl_sbin_dir, config.c_sysadm, admin_pass);
-                               fp = popen(admin_cmd, "r");
-                               if (fp != NULL) {
-                                       while (fgets(buf, sizeof buf, fp) != NULL) 
-                                       {
-                                               if ((atol(buf) == 574) || (atol(buf) == 200))
-                                                       ++found_it;
-                                       }
-                                       pclose(fp);
-                               }
-                       
-                               if (found_it == 0) {
-                                       important_message("Error","Setup failed to create your admin user");
-                               }
-                       }
-
-                       if (setup_type != UI_SILENT)
-                               important_message("Setup finished",
-                                                 "Setup of the Citadel server is complete.\n"
-                                                 "If you will be using WebCit, please run its\n"
-                                                 "setup program now; otherwise, run './citadel'\n"
-                                                 "to log in.\n");
-               }
-               else {
-                       important_message("Setup failed",
-                               "Setup is finished, but the Citadel server failed to start.\n"
-                               "Go back and check your configuration.\n"
-                       );
-               }
-
-       }
-
-       else {
-               important_message("Setup finished",
-                       "Setup is finished.  You may now start the server.");
-       }
-
-       cleanup(0);
-       return 0;
-}
-
-
diff --git a/citadel/stress.c b/citadel/stress.c
deleted file mode 100644 (file)
index d62ea3c..0000000
+++ /dev/null
@@ -1,372 +0,0 @@
-/* $Id$ */
-
-/* This message is exactly 1024 bytes */
-char* const message =
-"The point of this little file is to stress test a Citadel server.\n"
-"It spawns n threads, where n is a command line parameter, each of\n"
-"which writes 1000 messages total to the server.\n"
-"\n"
-"-n is a command line parameter indicating how many users to simulate\n"
-"(default 100).  WARNING: Your system must be capable of creating this\n"
-"many threads!\n"
-"\n"
-"-w is a command line parameter indicating how long to wait in seconds\n"
-"between posting each message (default 10).  The actual interval\n"
-"will be randomized between w / 3 and w * 3.\n"
-"\n"
-"A run is expected to take approximately three hours, given default\n"
-"values, and assuming the server can keep up.  If the run takes much\n"
-"longer than this, there may be a performance problem with the server.\n"
-"For best results, the test should be run from a different machine than\n"
-"the server, but connected via a fast network link (e.g. 100Base-T).\n"
-"\n"
-"To get baseline results, run the test with -n 1 (simulating 1 user)\n"
-"on a machine with no other users logged in.\n"
-"\n"
-"Example:\n"
-"stress -n 500 -w 25 myserver > stress.csv\n";
-
-/* The program tries to be as small and as fast as possible.  Wherever
- * possible, we avoid allocating memory on the heap.  We do not pass data
- * between threads.  We do only a minimal amount of calculation.  In
- * particular, we only output raw timing data for the run; we do not
- * collate it, average it, or do anything else with it.  See below.
- * The program does, however, use the same CtdlIPC functions as the
- * standard Citadel text client, and incurs the same overhead as that
- * program, using those functions.
- *
- * The program first creates a new user with a randomized username which
- * begins with "testuser".  It then creates 100 rooms named test0 through
- * test99.  If they already exist, this condition is ignored.
- *
- * The program then creates n threads, all of which wait on a conditional
- * before they do anything.  Once all of the threads have been created,
- * they are signaled, and begin execution.  Each thread logs in to the
- * Citadel server separately, simulating a user login, then takes a
- * timestamp from the operating system.
- *
- * Each thread selects a room from 0-99 randomly, then writes a small
- * (1KB) test message to that room.  1K was chosen because it seems to
- * represent an average message size for messages we expect to see.
- * After writing the message, the thread sleeps for w seconds (sleep(w);)
- * and repeats the process, until it has written 1,000 messages.  The
- * program provides a status display to standard error, unless w <= 2, in
- * which case status display is disabled.
- *
- * After posting all messages, each thread takes a second timestamp, and
- * subtracts the first timestamp.  The resulting value (in seconds) is
- * sent to standard output, followed by the minimum, average, and maximum
- * amounts of time (in milliseconds) it took to post a message.  The
- * thread then exits.
- *
- * Once all threads have exited, the program exits.
- */
-
-#include <stdlib.h>
-#include <unistd.h>
-#include <stdio.h>
-#include <sys/types.h>
-#include <string.h>
-#include <libcitadel.h>
-#include "sysdep.h"
-#if TIME_WITH_SYS_TIME
-# include <sys/time.h>
-# include <time.h>
-#else
-# if HAVE_SYS_TIME_H
-#  include <sys/time.h>
-# else
-#  include <time.h>
-# endif
-#endif
-#include "citadel_ipc.h"
-
-#ifndef HAVE_PTHREAD_H
-#error This program requires threads
-#endif
-
-static int w = 10;             /* see above */
-static int n = 100;            /* see above */
-static int m = 1000;           /* Number of messages to send; see above */
-static volatile int count = 0; /* Total count of messages posted */
-static volatile int total = 0; /* Total messages to be posted */
-static pthread_mutex_t count_mutex = PTHREAD_MUTEX_INITIALIZER;
-static pthread_mutex_t arg_mutex = PTHREAD_MUTEX_INITIALIZER;
-static pthread_mutex_t output_mutex = PTHREAD_MUTEX_INITIALIZER;
-
-static char username[12];
-static char password[12];
-
-/*
- * Mutex for the random number generator
- * We don't assume that rand_r() is present, so we have to
- * provide our own locking for rand()
- */
-static pthread_mutex_t rand_mutex = PTHREAD_MUTEX_INITIALIZER;
-
-/*
- * Conditional.  All the threads wait for this signal to actually
- * start bombarding the server.
- */
-static pthread_mutex_t start_mutex = PTHREAD_MUTEX_INITIALIZER;
-static pthread_cond_t start_cond = PTHREAD_COND_INITIALIZER;
-
-
-/*
- * This is the worker thread.  It logs in and creates the 1,000 messages
- * as described above.
- */
-void* worker(void* data)
-{
-       CtdlIPC* ipc;   /* My connection to the server */
-       void** args;    /* Args sent in */
-       int r;          /* IPC return code */
-       char aaa[SIZ];  /* Generic buffer */
-       int c;          /* Message count */
-       time_t start, end;      /* Timestamps */
-       struct ctdlipcmessage msg;      /* The message we will post */
-       int argc_;
-       char** argv_;
-       long tmin = LONG_MAX, trun = 0, tmax = LONG_MIN;
-
-       args = (void*)data;
-       argc_ = (int)args[0];
-       argv_ = (char**)args[1];
-
-       /* Setup the message we will be posting */
-       msg.text = message;
-       msg.anonymous = 0;
-       msg.type = 1;
-       strcpy(msg.recipient, "");
-       strcpy(msg.subject, "Test message; ignore");
-       strcpy(msg.author, username);
-
-       pthread_mutex_lock(&arg_mutex);
-       ipc = CtdlIPC_new(argc_, argv_, NULL, NULL);
-       pthread_mutex_unlock(&arg_mutex);
-       if (!ipc)
-               return NULL;    /* oops, something happened... */
-
-       CtdlIPC_chat_recv(ipc, aaa);
-       if (aaa[0] != '2') {
-               fprintf(stderr, "Citadel refused me: %s\n", &aaa[4]);
-               return NULL;    /* server ran out of connections maybe? */
-       }
-
-       CtdlIPCIdentifySoftware(ipc, 8, 8, REV_LEVEL, "Citadel stress tester",
-               "localhost", aaa);      /* we're lying, the server knows */
-       
-       r = CtdlIPCQueryUsername(ipc, username, aaa);
-       if (r / 100 == 2) {
-               /* testuser already exists (from previous run?) */
-               r = CtdlIPCTryLogin(ipc, username, aaa);
-               if (r / 100 != 3) {
-                       fprintf(stderr, "Citadel refused username: %s\n", aaa);
-                       CtdlIPC_delete_ptr(&ipc);
-                       return NULL;    /* Gawd only knows what went wrong */
-               }
-               r = CtdlIPCTryPassword(ipc, password, aaa);
-               if (r / 100 != 2) {
-                       fprintf(stderr, "Citadel refused password: %s\n", aaa);
-                       CtdlIPC_delete_ptr(&ipc);
-                       return NULL;    /* Gawd only knows what went wrong */
-               }
-       } else {
-               /* testuser doesn't yet exist */
-               r = CtdlIPCCreateUser(ipc, username, 1, aaa);
-               if (r / 100 != 2) {
-                       fprintf(stderr, "Citadel refused create user: %s\n", aaa);
-                       CtdlIPC_delete_ptr(&ipc);
-                       return NULL;    /* Gawd only knows what went wrong */
-               }
-               r = CtdlIPCChangePassword(ipc, password, aaa);
-               if (r / 100 != 2) {
-                       fprintf(stderr, "Citadel refused change password: %s\n", aaa);
-                       CtdlIPC_delete_ptr(&ipc);
-                       return NULL;    /* Gawd only knows what went wrong */
-               }
-       }
-
-       /* Wait for the rest of the threads */
-       pthread_mutex_lock(&start_mutex);
-       pthread_cond_wait(&start_cond, &start_mutex);
-       pthread_mutex_unlock(&start_mutex);
-
-       /* And now the fun begins!  Send out a whole shitload of messages */
-       start = time(NULL);
-       for (c = 0; c < m; c++) {
-               int rm;
-               char room[7];
-               struct ctdlipcroom *rret;
-               struct timeval tv;
-               long tstart, tend;
-               int wait;
-
-               /* Wait for a while */
-               pthread_mutex_lock(&rand_mutex);
-               /* See Numerical Recipes in C or Knuth vol. 2 ch. 3 */
-               /* Randomize between w/3 to w*3 (yes, it's complicated) */
-               wait = (int)((1.0+2.7*(float)w)*rand()/(RAND_MAX+(float)w/3.0)); /* range 0-99 */
-               pthread_mutex_unlock(&rand_mutex);
-               sleep(wait);
-
-               /* Select the room to goto */
-               pthread_mutex_lock(&rand_mutex);
-               /* See Numerical Recipes in C or Knuth vol. 2 ch. 3 */
-               rm = (int)(100.0*rand()/(RAND_MAX+1.0)); /* range 0-99 */
-               pthread_mutex_unlock(&rand_mutex);
-
-               /* Goto the selected room */
-               sprintf(room, "test%d", rm);
-               /* Create the room if not existing. Ignore the return */
-               r = CtdlIPCCreateRoom(ipc, 1, room, 0, NULL, 0, aaa);
-               if (r / 100 != 2 && r != 574) { /* Already exists */
-                       fprintf(stderr, "Citadel refused room create: %s\n", aaa);
-                       pthread_mutex_lock(&count_mutex);
-                       total -= m - c;
-                       pthread_mutex_unlock(&count_mutex);
-                       CtdlIPC_delete_ptr(&ipc);
-                       return NULL;
-               }
-               gettimeofday(&tv, NULL);
-               tstart = tv.tv_sec * 1000 + tv.tv_usec / 1000; /* cvt to msec */
-               r = CtdlIPCGotoRoom(ipc, room, "", &rret, aaa);
-               if (r / 100 != 2) {
-                       fprintf(stderr, "Citadel refused room change: %s\n", aaa);
-                       pthread_mutex_lock(&count_mutex);
-                       total -= m - c;
-                       pthread_mutex_unlock(&count_mutex);
-                       CtdlIPC_delete_ptr(&ipc);
-                       return NULL;
-               }
-
-               /* Post the message */
-               r = CtdlIPCPostMessage(ipc, 1, NULL, &msg, aaa);
-               if (r / 100 != 4) {
-                       fprintf(stderr, "Citadel refused message entry: %s\n", aaa);
-                       pthread_mutex_lock(&count_mutex);
-                       total -= m - c;
-                       pthread_mutex_unlock(&count_mutex);
-                       CtdlIPC_delete_ptr(&ipc);
-                       return NULL;
-               }
-
-               /* Do a status update */
-               pthread_mutex_lock(&count_mutex);
-               count++;
-               pthread_mutex_unlock(&count_mutex);
-               fprintf(stderr, " %d/%d=%d%%             \r",
-                       count, total,
-                       (int)(100 * count / total));
-               gettimeofday(&tv, NULL);
-               tend = tv.tv_sec * 1000 + tv.tv_usec / 1000; /* cvt to msec */
-               tend -= tstart;
-               if (tend < tmin) tmin = tend;
-               if (tend > tmax) tmax = tend;
-               trun += tend;
-       }
-       end = time(NULL);
-       pthread_mutex_lock(&output_mutex);
-       fprintf(stderr, "               \r");
-       printf("%ld %ld %ld %ld\n", end - start, tmin, trun / c, tmax);
-       pthread_mutex_unlock(&output_mutex);
-       return (void*)(end - start);
-}
-
-
-/*
- * Shift argument list
- */
-int shift(int argc, char **argv, int start, int count)
-{
-       int i;
-
-       for (i = start; i < argc - count; ++i)
-               argv[i] = argv[i + count];
-       return argc - count;
-}
-
-
-/*
- * Main loop.  Start a shitload of threads, all of which will attempt to
- * kick a Citadel server square in the nuts.
- */
-int main(int argc, char** argv)
-{
-       void* data[2];          /* pass args to worker thread */
-       pthread_t* threads;     /* A shitload of threads */
-       pthread_attr_t attr;    /* Thread attributes (we use defaults) */
-       int i;                  /* Counters */
-       long runtime;           /* Run time for each thread */
-
-       /* Read argument list */
-       for (i = 0; i < argc; i++) {
-               if (!strcmp(argv[i], "-n")) {
-                       n = atoi(argv[i + 1]);
-                       argc = shift(argc, argv, i, 2);
-               }
-               if (!strcmp(argv[i], "-w")) {
-                       w = atoi(argv[i + 1]);
-                       argc = shift(argc, argv, i, 2);
-               }
-               if (!strcmp(argv[i], "-m")) {
-                       m = atoi(argv[i + 1]);
-                       argc = shift(argc, argv, i, 2);
-               }
-               if (!strcmp(argv[i], "-h") || !strcmp(argv[i], "--help")) {
-                       fprintf(stderr, "Read stress.c for usage info\n");
-                       return 1;
-               }
-       }
-
-       data[0] = (void*)argc;  /* pass args to worker thread */
-       data[1] = (void*)argv;  /* pass args to worker thread */
-
-       /* This is how many total messages will be posted */
-       total = n * m;
-
-       /* Pick a randomized username */
-       pthread_mutex_lock(&rand_mutex);
-       /* See Numerical Recipes in C or Knuth vol. 2 ch. 3 */
-       i = (int)(100.0*rand()/(RAND_MAX+1.0)); /* range 0-99 */
-       pthread_mutex_unlock(&rand_mutex);
-       sprintf(username, "testuser%d", i);
-       strcpy(password, username);
-
-       /* First, memory for our shitload of threads */
-       threads = calloc(n, sizeof(pthread_t));
-       if (!threads) {
-               perror("Not enough memory");
-               return 1;
-       }
-
-       /* Then thread attributes (all defaults for now) */
-       pthread_attr_init(&attr);
-       pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_JOINABLE);
-
-       /* Then, create some threads */
-       fprintf(stderr, "Creating threads      \r");
-       for (i = 0; i < n; ++i) {
-               pthread_create(&threads[i], &attr, worker, (void*)data);
-               
-               /* Give thread #0 time to create the user account */
-               if (i == 0) sleep(3);
-       }
-
-       //fprintf(stderr, "Starting in %d seconds\r", n);
-       //sleep(n);
-       fprintf(stderr, "                      \r");
-
-       /* Then, signal the conditional they all are waiting on */
-       pthread_mutex_lock(&start_mutex);
-       pthread_cond_broadcast(&start_cond);
-       pthread_mutex_unlock(&start_mutex);
-
-       /* Then wait for them to exit */
-       for (i = 0; i < n; i++) {
-               pthread_join(threads[i], (void*)&runtime);
-               /* We're ignoring this value for now... TODO */
-       }
-       fprintf(stderr, "\r                                                                               \r");
-       return 0;
-}
diff --git a/citadel/textclient/citadel.c b/citadel/textclient/citadel.c
new file mode 100644 (file)
index 0000000..7e5ef87
--- /dev/null
@@ -0,0 +1,2348 @@
+/*
+ * $Id$
+ *
+ * Main source module for the client program.
+ *
+ * Copyright (c) 1987-2009 by the citadel.org team
+ *
+ *  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 3 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; if not, write to the Free Software
+ *  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ */
+
+#include "sysdep.h"
+#include <stdlib.h>
+#include <unistd.h>
+#include <fcntl.h>
+#include <stdio.h>
+#include <ctype.h>
+#include <string.h>
+
+#if TIME_WITH_SYS_TIME
+# include <sys/time.h>
+# include <time.h>
+#else
+# if HAVE_SYS_TIME_H
+#  include <sys/time.h>
+# else
+#  include <time.h>
+# endif
+#endif
+
+#include <limits.h>
+#include <sys/types.h>
+#include <sys/wait.h>
+#include <sys/stat.h>
+#include <sys/ioctl.h>
+#include <signal.h>
+#include <pwd.h>
+#include <stdarg.h>
+#include <errno.h>
+#include <libcitadel.h>
+#include "citadel.h"
+#include "citadel_ipc.h"
+#include "axdefs.h"
+#include "routines.h"
+#include "routines2.h"
+#include "tuiconfig.h"
+#include "rooms.h"
+#include "messages.h"
+#include "commands.h"
+#include "client_chat.h"
+#include "client_passwords.h"
+#include "citadel_decls.h"
+#include "sysdep.h"
+#ifndef HAVE_SNPRINTF
+#include "snprintf.h"
+#endif
+#include "screen.h"
+#include "citadel_dirs.h"
+
+#include "ecrash.h"
+#include "md5.h"
+
+#define IFEXPERT if (userflags&US_EXPERT)
+#define IFNEXPERT if ((userflags&US_EXPERT)==0)
+#define IFAIDE if (axlevel>=6)
+#define IFNAIDE if (axlevel<6)
+
+int rordercmp(struct ctdlroomlisting *r1, struct ctdlroomlisting *r2);
+
+march *marchptr = NULL;
+
+/* globals associated with the client program */
+char temp[PATH_MAX];           /* Name of general-purpose temp file */
+char temp2[PATH_MAX];          /* Name of general-purpose temp file */
+char tempdir[PATH_MAX];                /* Name of general-purpose temp directory */
+char editor_paths[MAX_EDITORS][SIZ];   /* paths to external editors */
+char printcmd[SIZ];            /* print command */
+int editor_pid = (-1);
+char fullname[USERNAME_SIZE];
+int screenwidth;
+int screenheight;
+unsigned room_flags;
+unsigned room_flags2;
+char room_name[ROOMNAMELEN];
+char *uglist[UGLISTLEN]; /* size of the ungoto list */
+long uglistlsn[UGLISTLEN]; /* current read position for all the ungoto's. Not going to make any friends with this one. */
+int uglistsize = 0;
+char is_mail = 0;              /* nonzero when we're in a mail room */
+char axlevel = 0;              /* access level */
+char is_room_aide = 0;         /* boolean flag, 1 if room aide */
+int timescalled;
+int posted;
+unsigned userflags;
+long usernum = 0L;             /* user number */
+time_t lastcall = 0L;          /* Date/time of previous login */
+char newnow;
+long highest_msg_read;         /* used for <A>bandon room cmd */
+long maxmsgnum;                        /* used for <G>oto */
+char sigcaught = 0;
+char have_xterm = 0;           /* are we running on an xterm? */
+char rc_username[USERNAME_SIZE];
+char rc_password[32];
+char hostbuf[SIZ];
+char portbuf[SIZ];
+char rc_floor_mode;
+char floor_mode;
+char curr_floor = 0;           /* number of current floor */
+char floorlist[128][SIZ];      /* names of floors */
+int termn8 = 0;                        /* Set to nonzero to cause a logoff */
+int secure;                    /* Set to nonzero when wire is encrypted */
+
+extern char instant_msgs;      /* instant messages waiting! */
+extern int rc_ansi_color;      /* ansi color value from citadel.rc */
+extern int next_lazy_cmd;
+
+CtdlIPC *ipc_for_signal_handlers;      /* KLUDGE cover your eyes */
+int enable_syslog = 0;
+
+
+/*
+ * CtdlLogPrintf()  ...   Write logging information; 
+ *                  simple here to have the same 
+ *                  symbols in the client.
+ */
+
+void CtdlLogPrintf(enum LogLevel loglevel, const char *format, ...) {   
+       va_list arg_ptr;
+
+       va_start(arg_ptr, format);
+       vfprintf(stderr, format, arg_ptr);   
+       va_end(arg_ptr);   
+       fflush(stderr);
+}   
+
+/*
+ * here is our 'clean up gracefully and exit' routine
+ */
+void ctdl_logoff(char *file, int line, CtdlIPC *ipc, int code)
+{
+       int lp;
+
+       if (editor_pid > 0) {   /* kill the editor if it's running */
+               kill(editor_pid, SIGHUP);
+       }
+
+       /* Free the ungoto list */
+       for (lp = 0; lp < uglistsize; lp++) {
+               free(uglist[lp]);
+       }
+
+/* Shut down the server connection ... but not if the logoff code is 3,
+ * because that means we're exiting because we already lost the server.
+ */
+       if (code != 3) {
+               CtdlIPCQuit(ipc);
+       }
+
+/*
+ * now clean up various things
+ */
+       screen_delete();
+
+       unlink(temp);
+       unlink(temp2);
+       nukedir(tempdir);
+
+       /* Violently kill off any child processes if Citadel is
+        * the login shell. 
+        */
+       if (getppid() == 1) {
+               kill(0 - getpgrp(), SIGTERM);
+               sleep(1);
+               kill(0 - getpgrp(), SIGKILL);
+       }
+       color(ORIGINAL_PAIR);   /* Restore the old color settings */
+       stty_ctdl(SB_RESTORE);  /* return the old terminal settings */
+       /* 
+        * uncomment the following if you need to know why Citadel exited
+       printf("*** Exit code %d at %s:%d\n", code, file, line);
+       sleep(2);
+        */
+       exit(code);             /* exit with the proper exit code */
+}
+
+
+
+/*
+ * signal catching function for hangups...
+ */
+void dropcarr(int signum)
+{
+       logoff(NULL, 3);        /* No IPC when server's already gone! */
+}
+
+
+
+/*
+ * catch SIGCONT to reset terminal modes when were are put back into the
+ * foreground.
+ */
+void catch_sigcont(int signum)
+{
+       stty_ctdl(SB_LAST);
+       signal(SIGCONT, catch_sigcont);
+}
+
+
+/* general purpose routines */
+
+/* display a file */
+void formout(CtdlIPC *ipc, char *name)
+{
+       int r;                  /* IPC return code */
+       char buf[SIZ];
+       char *text = NULL;
+
+       r = CtdlIPCSystemMessage(ipc, name, &text, buf);
+       if (r / 100 != 1) {
+               scr_printf("%s\n", buf);
+               return;
+       }
+       if (text) {
+               fmout(screenwidth, NULL, text, NULL,
+                     ((userflags & US_PAGINATOR) ? 1 : 0),
+                     screenheight, 1, 1);
+               free(text);
+       }
+}
+
+
+void userlist(CtdlIPC *ipc, char *patn)
+{
+       char buf[SIZ];
+       char fl[SIZ];
+       struct tm tmbuf;
+       time_t lc;
+       int r;                          /* IPC response code */
+       char *listing = NULL;
+
+       r = CtdlIPCUserListing(ipc, patn, &listing, buf);
+       if (r / 100 != 1) {
+               pprintf("%s\n", buf);
+               return;
+       }
+
+       pprintf("       User Name           Num  L Last Visit Logins Messages\n");
+       pprintf("------------------------- ----- - ---------- ------ --------\n");
+       if (listing != NULL) while (!IsEmptyStr(listing)) {
+               extract_token(buf, listing, 0, '\n', sizeof buf);
+               remove_token(listing, 0, '\n');
+
+               if (sigcaught == 0) {
+                   extract_token(fl, buf, 0, '|', sizeof fl);
+                   if (pattern(fl, patn) >= 0) {
+                       pprintf("%-25s ", fl);
+                       pprintf("%5ld %d ", extract_long(buf, 2),
+                              extract_int(buf, 1));
+                       lc = extract_long(buf, 3);
+                       localtime_r(&lc, &tmbuf);
+                       pprintf("%02d/%02d/%04d ",
+                              (tmbuf.tm_mon + 1),
+                              tmbuf.tm_mday,
+                              (tmbuf.tm_year + 1900));
+                       pprintf("%6ld %8ld\n", extract_long(buf, 4), extract_long(buf, 5));
+                   }
+
+               }
+       }
+       free(listing);
+       pprintf("\n");
+}
+
+
+/*
+ * grab assorted info about the user...
+ */
+void load_user_info(char *params)
+{
+       extract_token(fullname, params, 0, '|', sizeof fullname);
+       axlevel = extract_int(params, 1);
+       timescalled = extract_int(params, 2);
+       posted = extract_int(params, 3);
+       userflags = extract_int(params, 4);
+       usernum = extract_long(params, 5);
+       lastcall = extract_long(params, 6);
+}
+
+
+/*
+ * Remove a room from the march list.  'floornum' is ignored unless
+ * 'roomname' is set to _FLOOR_, in which case all rooms on the requested
+ * floor will be removed from the march list.
+ */
+void remove_march(char *roomname, int floornum)
+{
+       struct march *mptr, *mptr2;
+
+       if (marchptr == NULL)
+               return;
+
+       if ((!strcasecmp(marchptr->march_name, roomname))
+           || ((!strcasecmp(roomname, "_FLOOR_")) && (marchptr->march_floor == floornum))) {
+               mptr = marchptr->next;
+               free(marchptr);
+               marchptr = mptr;
+               return;
+       }
+       mptr2 = marchptr;
+       for (mptr = marchptr; mptr != NULL; mptr = mptr->next) {
+
+               if ((!strcasecmp(mptr->march_name, roomname))
+                   || ((!strcasecmp(roomname, "_FLOOR_"))
+                       && (mptr->march_floor == floornum))) {
+
+                       mptr2->next = mptr->next;
+                       free(mptr);
+                       mptr = mptr2;
+               } else {
+                       mptr2 = mptr;
+               }
+       }
+}
+
+
+/*
+ * Locate the room on the march list which we most want to go to.  Each room
+ * is measured given a "weight" of preference based on various factors.
+ */
+char *pop_march(int desired_floor, struct march *_march)
+{
+       static char TheRoom[ROOMNAMELEN];
+       int TheFloor = 0;
+       int TheOrder = 32767;
+       int TheWeight = 0;
+       int weight;
+       struct march *mptr = NULL;
+
+       strcpy(TheRoom, "_BASEROOM_");
+       if (_march == NULL)
+               return (TheRoom);
+
+       for (mptr = _march; mptr != NULL; mptr = mptr->next) {
+               weight = 0;
+               if ((strcasecmp(mptr->march_name, "_BASEROOM_")))
+                       weight = weight + 10000;
+               if (mptr->march_floor == desired_floor)
+                       weight = weight + 5000;
+
+               weight = weight + ((128 - (mptr->march_floor)) * 128);
+               weight = weight + (128 - (mptr->march_order));
+
+               if (weight > TheWeight) {
+                       TheWeight = weight;
+                       strcpy(TheRoom, mptr->march_name);
+                       TheFloor = mptr->march_floor;
+                       TheOrder = mptr->march_order;
+               }
+       }
+       return (TheRoom);
+}
+
+
+/*
+ * jump directly to a room
+ */
+void dotgoto(CtdlIPC *ipc, char *towhere, int display_name, int fromungoto)
+{
+       char aaa[SIZ], bbb[SIZ];
+       static long ls = 0L;
+       int newmailcount = 0;
+       int partial_match, best_match;
+       char from_floor;
+       int ugpos = uglistsize;
+       int r;                          /* IPC result code */
+       struct ctdlipcroom *room = NULL;
+       int rv = 0;
+
+       /* store ungoto information */
+       if (fromungoto == 0) {
+               /* sloppy slide them all down, hey it's the client, who cares. :-) */
+               if (uglistsize >= (UGLISTLEN-1)) {
+                       int lp;
+                       free (uglist[0]);
+                       for (lp = 0; lp < (UGLISTLEN-1); lp++) {
+                               uglist[lp] = uglist[lp+1];
+                               uglistlsn[lp] = uglistlsn[lp+1];
+                       }
+                       ugpos--;
+               } else {
+                       uglistsize++;
+               }
+        
+               uglist[ugpos] = malloc(strlen(room_name)+1);
+               strcpy(uglist[ugpos], room_name);
+               uglistlsn[ugpos] = ls;
+       }
+      
+       /* first try an exact match */
+       r = CtdlIPCGotoRoom(ipc, towhere, "", &room, aaa);
+       if (r / 10 == 54) {
+               newprompt("Enter room password: ", bbb, 9);
+               r = CtdlIPCGotoRoom(ipc, towhere, bbb, &room, aaa);
+               if (r / 10 == 54) {
+                       scr_printf("Wrong password.\n");
+                       return;
+               }
+       }       
+
+       /*
+        * If a match is not found, try a partial match.
+        * Partial matches anywhere in the string carry a weight of 1,
+        * left-aligned matches carry a weight of 2.  Pick the room that
+        * has the highest-weighted match.  Do not match on forgotten
+        * rooms.
+        */
+       if (r / 100 != 2) {
+               struct march *march = NULL;
+
+               best_match = 0;
+               strcpy(bbb, "");
+
+               r = CtdlIPCKnownRooms(ipc, SubscribedRooms, AllFloors, &march, aaa);
+               if (r / 100 == 1) {
+                       /* Run the roomlist; free the data as we go */
+                       struct march *mp = march;       /* Current */
+
+                       while (mp) {
+                               partial_match = 0;
+                               if (pattern(mp->march_name, towhere) >= 0) {
+                                       partial_match = 1;
+                               }
+                               if (!strncasecmp(mp->march_name, towhere, strlen(towhere))) {
+                                       partial_match = 2;
+                               }
+                               if (partial_match > best_match) {
+                                       strcpy(bbb, mp->march_name);
+                                       best_match = partial_match;
+                               }
+                               /* Both pointers are NULL at end of list */
+                               march = mp->next;
+                               free(mp);
+                               mp = march;
+                       }
+               }
+
+               if (IsEmptyStr(bbb)) {
+                       scr_printf("No room '%s'.\n", towhere);
+                       return;
+               }
+               r = CtdlIPCGotoRoom(ipc, bbb, "", &room, aaa);
+       }
+       if (r / 100 != 1 && r / 100 != 2) {
+               scr_printf("%s\n", aaa);
+               return;
+       }
+       safestrncpy(room_name, room->RRname, ROOMNAMELEN);
+       room_flags = room->RRflags;
+       room_flags2 = room->RRflags2;
+       from_floor = curr_floor;
+       curr_floor = room->RRfloor;
+
+       remove_march(room_name, 0);
+       if (!strcasecmp(towhere, "_BASEROOM_"))
+               remove_march(towhere, 0);
+       if (!room->RRunread)
+               next_lazy_cmd = 5;      /* Don't read new if no new msgs */
+       if ((from_floor != curr_floor) && (display_name > 0) && (floor_mode == 1)) {
+               if (floorlist[(int) curr_floor][0] == 0)
+                       load_floorlist(ipc);
+               scr_printf("(Entering floor: %s)\n", &floorlist[(int) curr_floor][0]);
+       }
+       if (display_name == 1) {
+               color(BRIGHT_WHITE);
+               scr_printf("%s ", room_name);
+               color(DIM_WHITE);
+               scr_printf("- ");
+       }
+       if (display_name != 2) {
+               color(BRIGHT_YELLOW);
+               scr_printf("%d ", room->RRunread);
+               color(DIM_WHITE);
+               scr_printf("new of ");
+               color(BRIGHT_YELLOW);
+               scr_printf("%d ", room->RRtotal);
+               color(DIM_WHITE);
+               scr_printf("messages.\n");
+       }
+       highest_msg_read = room->RRlastread;
+       maxmsgnum = room->RRhighest;
+       is_mail = room->RRismailbox;
+       is_room_aide = room->RRaide;
+       ls = room->RRlastread;
+
+       /* read info file if necessary */
+       if (room->RRinfoupdated > 0)
+               readinfo(ipc);
+
+       /* check for newly arrived mail if we can */
+       newmailcount = room->RRnewmail;
+       if (newmailcount > 0) {
+               color(BRIGHT_RED);
+               if (newmailcount == 1) {
+                       scr_printf("*** A new mail message has arrived.\n");
+               }
+               else {
+                       scr_printf("*** %d new mail messages have arrived.\n",
+                                       newmailcount);
+               }
+               color(DIM_WHITE);
+               if (!IsEmptyStr(rc_gotmail_cmd)) {
+                       rv = system(rc_gotmail_cmd);
+               }
+       }
+       status_line(ipc->ServInfo.humannode, ipc->ServInfo.site_location,
+                       room_name, secure, newmailcount);
+       free(room);
+}
+
+/* Goto next room having unread messages.
+ * We want to skip over rooms that the user has already been to, and take the
+ * user back to the lobby when done.  The room we end up in is placed in
+ * newroom - which is set to 0 (the lobby) initially.
+ */
+void gotonext(CtdlIPC *ipc)
+{
+       char buf[SIZ];
+       struct march *mptr, *mptr2;
+       char next_room[ROOMNAMELEN];
+       int r;                          /* IPC response code */
+
+       /* Check to see if the march-mode list is already allocated.
+        * If it is, pop the first room off the list and go there.
+        */
+       if (marchptr == NULL) {
+               r = CtdlIPCKnownRooms(ipc, SubscribedRoomsWithNewMessages,
+                                       AllFloors, &marchptr, buf);
+
+/* add _BASEROOM_ to the end of the march list, so the user will end up
+ * in the system base room (usually the Lobby>) at the end of the loop
+ */
+               mptr = (struct march *) malloc(sizeof(struct march));
+               mptr->next = NULL;
+               mptr->march_order = 0;
+               mptr->march_floor = 0;
+               strcpy(mptr->march_name, "_BASEROOM_");
+               if (marchptr == NULL) {
+                       marchptr = mptr;
+               } else {
+                       mptr2 = marchptr;
+                       while (mptr2->next != NULL)
+                               mptr2 = mptr2->next;
+                       mptr2->next = mptr;
+               }
+/*
+ * ...and remove the room we're currently in, so a <G>oto doesn't make us
+ * walk around in circles
+ */
+               remove_march(room_name, 0);
+       }
+       if (marchptr != NULL) {
+               strcpy(next_room, pop_march(curr_floor, marchptr));
+       } else {
+               strcpy(next_room, "_BASEROOM_");
+       }
+       remove_march(next_room, 0);
+       dotgoto(ipc, next_room, 1, 0);
+}
+
+/*
+ * forget all rooms on a given floor
+ */
+void forget_all_rooms_on(CtdlIPC *ipc, int ffloor)
+{
+       char buf[SIZ];
+       struct march *flist = NULL;
+       struct march *fptr = NULL;
+       struct ctdlipcroom *room = NULL;
+       int r;                          /* IPC response code */
+
+       scr_printf("Forgetting all rooms on %s...\n", &floorlist[ffloor][0]);
+       scr_flush();
+       remove_march("_FLOOR_", ffloor);
+       r = CtdlIPCKnownRooms(ipc, AllAccessibleRooms, ffloor, &flist, buf);
+       if (r / 100 != 1) {
+               scr_printf("Error %d: %s\n", r, buf);
+               return;
+       }
+       while (flist) {
+               r = CtdlIPCGotoRoom(ipc, flist->march_name, "", &room, buf);
+               if (r / 100 == 2) {
+                       r = CtdlIPCForgetRoom(ipc, buf);
+                       if (r / 100 != 2) {
+                               scr_printf("Error %d: %s\n", r, buf);
+                       }
+
+               }
+               fptr = flist;
+               flist = flist->next;
+               free(fptr);
+       }
+       if (room) free(room);
+}
+
+
+/*
+ * routine called by gotofloor() to move to a new room on a new floor
+ */
+void gf_toroom(CtdlIPC *ipc, char *towhere, int mode)
+{
+       int floor_being_left;
+
+       floor_being_left = curr_floor;
+
+       if (mode == GF_GOTO) {          /* <;G>oto mode */
+               updatels(ipc);
+               dotgoto(ipc, towhere, 1, 0);
+       }
+       else if (mode == GF_SKIP) {     /* <;S>kip mode */
+               dotgoto(ipc, towhere, 1, 0);
+               remove_march("_FLOOR_", floor_being_left);
+       }
+       else if (mode == GF_ZAP) {      /* <;Z>ap mode */
+               dotgoto(ipc, towhere, 1, 0);
+               remove_march("_FLOOR_", floor_being_left);
+               forget_all_rooms_on(ipc, floor_being_left);
+       }
+}
+
+
+/*
+ * go to a new floor
+ */
+void gotofloor(CtdlIPC *ipc, char *towhere, int mode)
+{
+       int a, tofloor;
+       int r;          /* IPC response code */
+       struct march *mptr;
+       char buf[SIZ], targ[SIZ];
+
+       if (floorlist[0][0] == 0)
+               load_floorlist(ipc);
+       tofloor = (-1);
+       for (a = 0; a < 128; ++a)
+               if (!strcasecmp(&floorlist[a][0], towhere))
+                       tofloor = a;
+
+       if (tofloor < 0) {
+               for (a = 0; a < 128; ++a) {
+                       if (!strncasecmp(&floorlist[a][0], towhere, strlen(towhere))) {
+                               tofloor = a;
+                       }
+               }
+       }
+       if (tofloor < 0) {
+               for (a = 0; a < 128; ++a)
+                       if (pattern(towhere, &floorlist[a][0]) > 0)
+                               tofloor = a;
+       }
+       if (tofloor < 0) {
+               scr_printf("No floor '%s'.\n", towhere);
+               return;
+       }
+       for (mptr = marchptr; mptr != NULL; mptr = mptr->next) {
+               if ((mptr->march_floor) == tofloor) {
+                       gf_toroom(ipc, mptr->march_name, mode);
+                       return;
+               }
+       }
+
+       /* Find first known room on the floor */
+
+       strcpy(targ, "");
+       mptr = NULL;
+       r = CtdlIPCKnownRooms(ipc, SubscribedRooms, tofloor, &mptr, buf);
+       if (r / 100 == 1) {
+               struct march *tmp = mptr;
+
+               /*. . . according to room order */
+               if (mptr)
+           strcpy(targ, pop_march(tofloor, mptr));
+               while (mptr) {
+                       tmp = mptr->next;
+                       free(mptr);
+                       mptr = tmp;
+               }
+       }
+       if (!IsEmptyStr(targ)) {
+               gf_toroom(ipc, targ, mode);
+               return;
+       }
+
+       /* No known rooms on the floor; unzap the first one then */
+
+       strcpy(targ, "");
+       mptr = NULL;
+       r = CtdlIPCKnownRooms(ipc, AllAccessibleRooms, tofloor, &mptr, buf);
+       if (r / 100 == 1) {
+               struct march *tmp = mptr;
+               
+        /*. . . according to room order */
+               if (mptr)
+                       strcpy(targ, pop_march(tofloor, mptr));
+               while (mptr) {
+                       tmp = mptr->next;
+                       free(mptr);
+                       mptr = tmp;
+               }
+       }
+       if (!IsEmptyStr(targ)) {
+               gf_toroom(ipc, targ, mode);
+       } else {
+               scr_printf("There are no rooms on '%s'.\n", &floorlist[tofloor][0]);
+       }
+}
+
+/*
+ * Indexing mechanism for a room list, called by gotoroomstep()
+ */
+void room_tree_list_query(struct ctdlroomlisting *rp, char *findrmname, int findrmslot, char *rmname, int *rmslot, int *rmtotal)
+{
+       char roomname[ROOMNAMELEN];
+       static int cur_rmslot = 0;
+
+       if (rp == NULL) {
+               cur_rmslot = 0;
+               return;
+       }
+
+       if (rp->lnext != NULL) {
+               room_tree_list_query(rp->lnext, findrmname, findrmslot, rmname, rmslot, rmtotal);
+       }
+
+       if (sigcaught == 0) {
+               strcpy(roomname, rp->rlname);
+
+               if (rmname != NULL) {
+                       if (cur_rmslot == findrmslot) {
+                               strcpy(rmname, roomname);
+                       }
+               }
+               if (rmslot != NULL) {
+                       if (!strcmp(roomname, findrmname)) {
+                               *rmslot = cur_rmslot;
+                       }
+               }
+               cur_rmslot++;
+       }
+
+       if (rp->rnext != NULL) {
+               room_tree_list_query(rp->rnext, findrmname, findrmslot, rmname, rmslot, rmtotal);
+       }
+
+       if ((rmname == NULL) && (rmslot == NULL))
+               free(rp);
+
+       if (rmtotal != NULL) {
+               *rmtotal = cur_rmslot;
+       }
+}
+
+/*
+ * step through rooms on current floor
+ */
+void  gotoroomstep(CtdlIPC *ipc, int direction, int mode)
+{
+       struct march *listing = NULL;
+       struct march *mptr;
+       int r;          /* IPC response code */
+       char buf[SIZ];
+       struct ctdlroomlisting *rl = NULL;
+       struct ctdlroomlisting *rp;
+       struct ctdlroomlisting *rs;
+       int list_it;
+       char rmname[ROOMNAMELEN];
+       int rmslot = 0;
+       int rmtotal;
+
+       /* Ask the server for a room list */
+       r = CtdlIPCKnownRooms(ipc, SubscribedRooms, (-1), &listing, buf);
+       if (r / 100 != 1) {
+               listing = NULL;
+       }
+
+       load_floorlist(ipc);
+
+       for (mptr = listing; mptr != NULL; mptr = mptr->next) {
+               list_it = 1;
+
+               if ( floor_mode 
+                        && (mptr->march_floor != curr_floor))
+                       list_it = 0;
+
+               if (list_it) {
+                       rp = malloc(sizeof(struct ctdlroomlisting));
+                       strncpy(rp->rlname, mptr->march_name, ROOMNAMELEN);
+                       rp->rlflags = mptr->march_flags;
+                       rp->rlfloor = mptr->march_floor;
+                       rp->rlorder = mptr->march_order;
+                       rp->lnext = NULL;
+                       rp->rnext = NULL;
+
+                       rs = rl;
+                       if (rl == NULL) {
+                               rl = rp;
+                       } else {
+                               while (rp != NULL) {
+                                       if (rordercmp(rp, rs) < 0) {
+                                               if (rs->lnext == NULL) {
+                                                       rs->lnext = rp;
+                                                       rp = NULL;
+                                               } else {
+                                                       rs = rs->lnext;
+                                               }
+                                       } else {
+                                               if (rs->rnext == NULL) {
+                                                       rs->rnext = rp;
+                                                       rp = NULL;
+                                               } else {
+                                                       rs = rs->rnext;
+                                               }
+                                       }
+                               }
+                       }
+               }
+       }
+
+       /* Find position of current room */
+       room_tree_list_query(NULL, NULL, 0, NULL, NULL, NULL);
+       room_tree_list_query(rl,  room_name, 0, NULL, &rmslot, &rmtotal);
+
+       if (direction == 0) { /* Previous room */
+               /* If we're at the first room, wrap to the last room */
+               if (rmslot == 0) {
+                       rmslot = rmtotal - 1;
+               } else {
+                       rmslot--;
+               }
+       } else {                 /* Next room */
+               /* If we're at the last room, wrap to the first room */
+               if (rmslot == rmtotal - 1) {
+                       rmslot = 0; 
+               } else {
+                       rmslot++;
+               }
+       }
+
+       /* Get name of next/previous room */
+       room_tree_list_query(NULL, NULL, 0, NULL, NULL, NULL);
+       room_tree_list_query(rl,  NULL, rmslot, rmname, NULL, NULL);
+
+       /* Free the tree */
+       room_tree_list_query(rl, NULL, 0, NULL, NULL, NULL);
+
+       if (mode == 0) { /* not skipping */
+           updatels(ipc);
+       } else {
+               if (rc_alt_semantics) {
+               updatelsa(ipc);
+               }
+       }
+
+       /* Free the room list */
+       while (listing) {
+               mptr = listing->next;
+               free(listing);
+               listing = mptr;
+       };
+
+       dotgoto(ipc, rmname, 1, 0);
+}
+
+
+/*
+ * step through floors on system
+ */
+void  gotofloorstep(CtdlIPC *ipc, int direction, int mode)
+{
+       int  tofloor;
+
+       if (floorlist[0][0] == 0)
+               load_floorlist(ipc);
+
+       empty_keep_going:
+
+       if (direction == 0) { /* Previous floor */
+               if (curr_floor) tofloor = curr_floor - 1;
+               else tofloor = 127;
+
+               while (!floorlist[tofloor][0]) tofloor--;
+       } else {                   /* Next floor */
+               if (curr_floor < 127) tofloor = curr_floor + 1;
+               else tofloor = 0;
+
+               while (!floorlist[tofloor][0] && tofloor < 127) tofloor++;
+               if (!floorlist[tofloor][0])     tofloor = 0;
+       }
+       /* ;g works when not in floor mode so . . . */
+       if (!floor_mode) {
+               scr_printf("(%s)\n", floorlist[tofloor] );
+       }
+
+       gotofloor(ipc, floorlist[tofloor], mode);
+       if (curr_floor != tofloor) { /* gotofloor failed */
+            curr_floor = tofloor;
+            goto empty_keep_going;
+       }            
+}
+
+/* 
+ * Display user 'preferences'.
+ */
+extern int rc_prompt_control;
+void read_config(CtdlIPC *ipc)
+{
+       char buf[SIZ];
+       char *resp = NULL;
+       int r;                  /* IPC response code */
+    char _fullname[USERNAME_SIZE];
+       long _usernum;
+       int _axlevel, _timescalled, _posted;
+       time_t _lastcall;
+       struct ctdluser *user = NULL;
+
+       /* get misc user info */   
+       r = CtdlIPCGetBio(ipc, fullname, &resp, buf);
+       if (r / 100 != 1) {
+               pprintf("%s\n", buf);
+               return;
+       }
+       extract_token(_fullname, buf, 1, '|', sizeof fullname);
+       _usernum = extract_long(buf, 2);
+       _axlevel = extract_int(buf, 3);
+       _lastcall = extract_long(buf, 4);
+    _timescalled = extract_int(buf, 5);
+       _posted = extract_int(buf, 6);
+       free(resp);
+       resp = NULL;
+   
+       /* get preferences */
+       r = CtdlIPCGetConfig(ipc, &user, buf);
+       if (r / 100 != 2) {
+               scr_printf("%s\n", buf);
+               free(user);
+               return;
+       }
+
+       /* show misc user info */
+       scr_printf("%s\nAccess level: %d (%s)\n"
+                  "User #%ld / %d Calls / %d Posts",
+                  _fullname, _axlevel, axdefs[(int) _axlevel],
+                  _usernum, _timescalled, _posted);
+       if (_lastcall > 0L) {
+               scr_printf(" / Curr login: %s",
+                          asctime(localtime(&_lastcall)));
+       }
+       scr_printf("\n");
+
+       /* show preferences */
+       scr_printf("Your screen width: ");                                     color(BRIGHT_CYAN); scr_printf("%d",   /*user->USscreenwidth*/ screenwidth);          color(DIM_WHITE); 
+       scr_printf(", height: ");                                              color(BRIGHT_CYAN); scr_printf("%d\n", /*user->USscreenheight*/ screenheight);        color(DIM_WHITE);  
+       scr_printf("Are you an experienced Citadel user: ");                   color(BRIGHT_CYAN); scr_printf("%s\n", (user->flags & US_EXPERT) ? "Yes" : "No");     color(DIM_WHITE);
+       scr_printf("Print last old message on New message request: ");         color(BRIGHT_CYAN); scr_printf("%s\n", (user->flags & US_LASTOLD)? "Yes" : "No");     color(DIM_WHITE);
+       scr_printf("Prompt after each message: ");                             color(BRIGHT_CYAN); scr_printf("%s\n", (!(user->flags & US_NOPROMPT))? "Yes" : "No"); color(DIM_WHITE);
+       if ((user->flags & US_NOPROMPT) == 0) {
+       scr_printf("Use 'disappearing' prompts: ");                        color(BRIGHT_CYAN); scr_printf("%s\n", (user->flags & US_DISAPPEAR)? "Yes" : "No");   color(DIM_WHITE);
+       }
+       scr_printf("Pause after each screenful of text: ");                    color(BRIGHT_CYAN); scr_printf("%s\n", (user->flags & US_PAGINATOR)? "Yes" : "No");   color(DIM_WHITE);
+    if (rc_prompt_control == 3 && (user->flags & US_PAGINATOR)) {
+       scr_printf("<N>ext and <S>top work at paginator prompt: ");        color(BRIGHT_CYAN); scr_printf("%s\n", (user->flags & US_PROMPTCTL)? "Yes" : "No");   color(DIM_WHITE);
+       }
+    if (rc_floor_mode == RC_DEFAULT) {
+       scr_printf("View rooms by floor: ");                               color(BRIGHT_CYAN); scr_printf("%s\n", (user->flags & US_FLOORS)? "Yes" : "No");          color(DIM_WHITE);
+       }
+       if (rc_ansi_color == 3) {
+           scr_printf("Enable color support: ");                              color(BRIGHT_CYAN); scr_printf("%s\n", (user->flags & US_COLOR)? "Yes" : "No");       color(DIM_WHITE);
+       }
+       scr_printf("Be unlisted in userlog: ");                                color(BRIGHT_CYAN); scr_printf("%s\n", (user->flags & US_UNLISTED)? "Yes" : "No");    color(DIM_WHITE);
+       if (!IsEmptyStr(editor_paths[0])) {
+       scr_printf("Always enter messages with the full-screen editor: "); color(BRIGHT_CYAN); scr_printf("%s\n", (user->flags & US_EXTEDIT)? "Yes" : "No");     color(DIM_WHITE);
+       }
+       free(user);
+}
+
+/*
+ * Display system statistics.
+ */
+void system_info(CtdlIPC *ipc)
+{
+       char buf[SIZ];
+       char *resp = NULL;
+       size_t bytes;
+       int mrtg_users, mrtg_active_users; 
+       char mrtg_server_uptime[40];
+       long mrtg_himessage;
+       int ret;                        /* IPC response code */
+
+       /* get #users, #active & server uptime */
+       ret = CtdlIPCGenericCommand(ipc, "MRTG|users", NULL, 0, &resp, &bytes, buf);
+       mrtg_users = extract_int(resp, 0);
+       remove_token(resp, 0, '\n');
+       mrtg_active_users = extract_int(resp, 0);
+       remove_token(resp, 0, '\n');
+       extract_token(mrtg_server_uptime, resp, 0, '\n', sizeof mrtg_server_uptime);
+    free(resp);
+       resp = NULL;
+
+       /* get high message# */
+       ret = CtdlIPCGenericCommand(ipc, "MRTG|messages", NULL, 0, &resp, &bytes, buf);
+       mrtg_himessage = extract_long(resp, 0);
+       free(resp);
+       resp = NULL;
+
+       /* refresh server info just in case */
+       CtdlIPCServerInfo(ipc, buf);
+
+       scr_printf("You are connected to %s (%s) @%s\n", ipc->ServInfo.nodename, ipc->ServInfo.humannode, ipc->ServInfo.fqdn);
+       scr_printf("running %s with text client v%.2f,\n", ipc->ServInfo.software, (float)REV_LEVEL/100);
+       scr_printf("server build %s,\n", ipc->ServInfo.svn_revision, (float)REV_LEVEL/100);
+    scr_printf("and located in %s.\n", ipc->ServInfo.site_location);
+    scr_printf("Connected users %d / Active users %d / Highest message #%ld\n", mrtg_users, mrtg_active_users, mrtg_himessage);
+    scr_printf("Server uptime: %s\n", mrtg_server_uptime);
+    scr_printf("Your system administrator is %s.\n", ipc->ServInfo.sysadm);
+    scr_printf("Copyright (C)1987-2009 by the Citadel development team\n");
+}
+
+/*
+ * forget all rooms on current floor
+ */
+void forget_this_floor(CtdlIPC *ipc)
+{
+       if (curr_floor == 0) {
+               scr_printf("Can't forget this floor.\n");
+               return;
+       }
+       if (floorlist[0][0] == 0) {
+               load_floorlist(ipc);
+       }
+       scr_printf("Are you sure you want to forget all rooms on %s? ",
+              &floorlist[(int) curr_floor][0]);
+       if (yesno() == 0) {
+               return;
+       }
+
+       gf_toroom(ipc, "_BASEROOM_", GF_ZAP);
+}
+
+
+/* 
+ * Figure out the physical screen dimensions, if we can
+ * WARNING:  this is now called from a signal handler!
+ */
+void check_screen_dims(void)
+{
+#ifdef TIOCGWINSZ
+       struct {
+               unsigned short height;  /* rows */
+               unsigned short width;   /* columns */
+               unsigned short xpixels;
+               unsigned short ypixels;         /* pixels */
+       } xwinsz;
+
+       if (have_xterm) {       /* dynamically size screen if on an xterm */
+               if (ioctl(0, TIOCGWINSZ, &xwinsz) == 0) {
+                       if (xwinsz.height)
+                               screenheight = is_curses_enabled() ? (int)xwinsz.height - 1 : (int) xwinsz.height;
+                       if (xwinsz.width)
+                               screenwidth = (int) xwinsz.width;
+               }
+       }
+#endif
+}
+
+
+/*
+ * set floor mode depending on client, server, and user settings
+ */
+void set_floor_mode(CtdlIPC* ipc)
+{
+       if (ipc->ServInfo.ok_floors == 0) {
+               floor_mode = 0; /* Don't use floors if the server */
+       }
+       /* doesn't support them!          */
+       else {
+               if (rc_floor_mode == RC_NO) {   /* never use floors */
+                       floor_mode = 0;
+               }
+               if (rc_floor_mode == RC_YES) {  /* always use floors */
+                       floor_mode = 1;
+               }
+               if (rc_floor_mode == RC_DEFAULT) {      /* user choice */
+                       floor_mode = ((userflags & US_FLOORS) ? 1 : 0);
+               }
+       }
+}
+
+/*
+ * Set or change the user's password
+ */
+int set_password(CtdlIPC *ipc)
+{
+       char pass1[20];
+       char pass2[20];
+       char buf[SIZ];
+
+       if (!IsEmptyStr(rc_password)) {
+               strcpy(pass1, rc_password);
+               strcpy(pass2, rc_password);
+       } else {
+               IFNEXPERT formout(ipc, "changepw");
+               newprompt("Enter a new password: ", pass1, -19);
+               newprompt("Enter it again to confirm: ", pass2, -19);
+       }
+       strproc(pass1);
+       strproc(pass2);
+       if (!strcasecmp(pass1, pass2)) {
+               CtdlIPCChangePassword(ipc, pass1, buf);
+               scr_printf("%s\n", buf);
+               offer_to_remember_password(ipc, hostbuf, portbuf, fullname, pass1);
+               return (0);
+       } else {
+               scr_printf("*** They don't match... try again.\n");
+               return (1);
+       }
+}
+
+
+
+/*
+ * get info about the server we've connected to
+ */
+void get_serv_info(CtdlIPC *ipc, char *supplied_hostname)
+{
+       char buf[SIZ];
+
+       CtdlIPCServerInfo(ipc, buf);
+
+       /* be nice and identify ourself to the server */
+       CtdlIPCIdentifySoftware(ipc, SERVER_TYPE, 0, REV_LEVEL,
+                (ipc->isLocal ? "local" : CITADEL),
+                (supplied_hostname) ? supplied_hostname : 
+                /* Look up the , in the bible if you're confused */
+                (locate_host(ipc, buf), buf), buf);
+
+       /* Indicate to the server that we prefer to decode Base64 and
+        * quoted-printable on the client side.
+        */
+       if ((CtdlIPCSpecifyPreferredFormats(ipc, buf, "dont_decode") / 100 ) != 2) {
+               scr_printf("ERROR: Extremely old server; MSG4 framework not supported.\n");
+               logoff(ipc, 0);
+       }
+
+       /*
+        * Tell the server what our preferred content formats are.
+        *
+        * Originally we preferred HTML over plain text because we can format
+        * it to the reader's screen width, but since our HTML-to-text parser
+        * isn't really all that great, it's probably better to just go with
+        * the plain text when we have it available.
+        */
+       if ((CtdlIPCSpecifyPreferredFormats(ipc, buf, "text/plain|text/html") / 100 ) != 2) {
+               scr_printf("ERROR: Extremely old server; MSG4 framework not supported.\n");
+               logoff(ipc, 0);
+       }
+}
+
+
+
+/*
+ * Record compare function for SortOnlineUsers()
+ */
+int idlecmp(const void *rec1, const void *rec2) {
+       time_t i1, i2;
+
+       i1 = extract_long(rec1, 5);
+       i2 = extract_long(rec2, 5);
+
+       if (i1 < i2) return(1);
+       if (i1 > i2) return(-1);
+       return(0);
+}
+
+
+/*
+ * Sort the list of online users by idle time.
+ * This function frees the supplied buffer, and returns a new one
+ * to the caller.  The caller is responsible for freeing the returned buffer.
+ */
+char *SortOnlineUsers(char *listing) {
+       int rows;
+       char *sortbuf;
+       char *retbuf;
+       char buf[SIZ];
+       int i;
+
+       rows = num_tokens(listing, '\n');
+       sortbuf = malloc(rows * SIZ);
+       if (sortbuf == NULL) return(listing);
+       retbuf = malloc(rows * SIZ);
+       if (retbuf == NULL) {
+               free(sortbuf);
+               return(listing);
+       }
+
+       /* Copy the list into a fixed-record-size array for sorting */
+       for (i=0; i<rows; ++i) {
+               memset(buf, 0, SIZ);
+               extract_token(buf, listing, i, '\n', sizeof buf);
+               memcpy(&sortbuf[i*SIZ], buf, (size_t)SIZ);
+       }
+
+       /* Do the sort */
+       qsort(sortbuf, rows, SIZ, idlecmp);
+
+       /* Copy back to a \n delimited list */
+       strcpy(retbuf, "");
+       for (i=0; i<rows; ++i) {
+               strcat(retbuf, &sortbuf[i*SIZ]);
+               if (i<(rows-1)) strcat(retbuf, "\n");
+       }
+    free(listing);
+    free(sortbuf);
+       return(retbuf);
+}
+
+
+
+/*
+ * Display list of users currently logged on to the server
+ */
+void who_is_online(CtdlIPC *ipc, int longlist)
+{
+       char buf[SIZ], username[SIZ], roomname[SIZ], fromhost[SIZ];
+       char flags[SIZ];
+       char actual_user[SIZ], actual_room[SIZ], actual_host[SIZ];
+       char clientsoft[SIZ];
+       time_t timenow = 0;
+       time_t idletime, idlehours, idlemins, idlesecs;
+       int last_session = (-1);
+       int skipidle = 0;
+       char *listing = NULL;
+       int r;                          /* IPC response code */
+    
+       if (longlist == 2) {
+               longlist = 0;
+               skipidle = 1;
+       }
+
+       if (!longlist) {
+               color(BRIGHT_WHITE);
+               pprintf("           User Name               Room          ");
+               if (screenwidth >= 80) pprintf(" Idle        From host");
+               pprintf("\n");
+               color(DIM_WHITE);
+               pprintf("   ------------------------- --------------------");
+               if (screenwidth >= 80) pprintf(" ---- ------------------------");
+               pprintf("\n");
+       }
+       r = CtdlIPCOnlineUsers(ipc, &listing, &timenow, buf);
+       listing = SortOnlineUsers(listing);
+       if (r / 100 == 1) {
+               while (!IsEmptyStr(listing)) {
+                       int isidle = 0;
+                       
+                       /* Get another line */
+                       extract_token(buf, listing, 0, '\n', sizeof buf);
+                       remove_token(listing, 0, '\n');
+
+                       extract_token(username, buf, 1, '|', sizeof username);
+                       extract_token(roomname, buf, 2, '|', sizeof roomname);
+                       extract_token(fromhost, buf, 3, '|', sizeof fromhost);
+                       extract_token(clientsoft, buf, 4, '|', sizeof clientsoft);
+                       extract_token(flags, buf, 7, '|', sizeof flags);
+
+                       idletime = timenow - extract_long(buf, 5);
+                       idlehours = idletime / 3600;
+                       idlemins = (idletime - (idlehours * 3600)) / 60;
+                       idlesecs = (idletime - (idlehours * 3600) - (idlemins * 60));
+
+                       if (idletime > rc_idle_threshold) {
+                               if (skipidle) {
+                                       isidle = 1;
+                               }
+                       }
+
+                       if (longlist) {
+                               extract_token(actual_user, buf, 8, '|', sizeof actual_user);
+                               extract_token(actual_room, buf, 9, '|', sizeof actual_room);
+                               extract_token(actual_host, buf, 10, '|', sizeof actual_host);
+
+                               pprintf("  Flags: %s\n", flags);
+                               pprintf("Session: %d\n", extract_int(buf, 0));
+                               pprintf("   Name: %s\n", username);
+                               pprintf("In room: %s\n", roomname);
+                               pprintf("   Host: %s\n", fromhost);
+                               pprintf(" Client: %s\n", clientsoft);
+                               pprintf("   Idle: %ld:%02ld:%02ld\n",
+                                       (long) idlehours,
+                                       (long) idlemins,
+                                       (long) idlesecs);
+
+                               if ( (!IsEmptyStr(actual_user)&&
+                                     !IsEmptyStr(actual_room)&&
+                                     !IsEmptyStr(actual_host))) {
+                                       pprintf("(really ");
+                                       if (!IsEmptyStr(actual_user)) pprintf("<%s> ", actual_user);
+                                       if (!IsEmptyStr(actual_room)) pprintf("in <%s> ", actual_room);
+                                       if (!IsEmptyStr(actual_host)) pprintf("from <%s> ", actual_host);
+                                       pprintf(")\n");
+                               }
+                               pprintf("\n");
+
+                       } else {
+                               if (isidle == 0) {
+                                       if (extract_int(buf, 0) == last_session) {
+                                               pprintf("        ");
+                                       }
+                                       else {
+                                               color(BRIGHT_MAGENTA);
+                                               pprintf("%-3s", flags);
+                                       }
+                                       last_session = extract_int(buf, 0);
+                                       color(BRIGHT_CYAN);
+                                       pprintf("%-25s ", username);
+                                       color(BRIGHT_MAGENTA);
+                                       roomname[20] = 0;
+                                       pprintf("%-20s", roomname);
+
+                                       if (screenwidth >= 80) {
+                                               pprintf(" ");
+                                               if (idletime > rc_idle_threshold) {
+                                                       /* over 1000d, must be gone fishing */
+                                                       if (idlehours > 23999) {
+                                                               pprintf("fish");
+                                                       /* over 10 days */
+                                                       } else if (idlehours > 239) {
+                                                               pprintf("%3ldd", idlehours / 24);
+                                                       /* over 10 hours */
+                                                       } else if (idlehours > 9) {
+                                                               pprintf("%1ldd%02ld",
+                                                                       idlehours / 24,
+                                                                       idlehours % 24);
+                                                       /* less than 10 hours */
+                                                       }
+                                                       else {
+                                                               pprintf("%1ld:%02ld", idlehours, idlemins);
+                                                       }
+                                               }
+                                               else {
+                                                       pprintf("    ");
+                                               }
+                                               pprintf(" ");
+                                               color(BRIGHT_CYAN);
+                                               fromhost[24] = '\0';
+                                               pprintf("%-24s", fromhost);
+                                       }
+                                       pprintf("\n");
+                                       color(DIM_WHITE);
+                               }
+                       }
+               }
+       }
+       free(listing);
+}
+
+void enternew(CtdlIPC *ipc, char *desc, char *buf, int maxlen)
+{
+       char bbb[128];
+       snprintf(bbb, sizeof bbb, "Enter in your new %s: ", desc);
+       newprompt(bbb, buf, maxlen);
+}
+
+
+
+int shift(int argc, char **argv, int start, int count) {
+       int i;
+
+       for (i=start; i<(argc-count); ++i) {
+               argv[i] = argv[i+count];
+       }
+       argc = argc - count;
+       return argc;
+}
+
+static void statusHook(char *s) {
+       sln_printf(s);
+       sln_flush();
+}
+
+/*
+ * main
+ */
+int main(int argc, char **argv)
+{
+       int a, b, mcmd;
+       char aaa[100], bbb[100];/* general purpose variables */
+       char argbuf[64];        /* command line buf */
+       char nonce[NONCE_SIZE];
+       char *telnet_client_host = NULL;
+       char *sptr, *sptr2;     /* USed to extract the nonce */
+       char hexstring[MD5_HEXSTRING_SIZE];
+       int stored_password = 0;
+       char password[SIZ];
+       struct ctdlipcmisc chek;
+       struct ctdluser *myself = NULL;
+       CtdlIPC* ipc;                   /* Our server connection */
+       int r;                          /* IPC result code */
+       int rv = 0;                     /* fetch but ignore syscall return value to suppress warnings */
+
+       int relh=0;
+       int home=0;
+       char relhome[PATH_MAX]="";
+       char ctdldir[PATH_MAX]=CTDLDIR;
+    int lp; 
+#ifdef HAVE_BACKTRACE
+       eCrashParameters params;
+//     eCrashSymbolTable symbol_table;
+#endif
+       calc_dirs_n_files(relh, home, relhome, ctdldir, 0);
+
+#ifdef HAVE_BACKTRACE
+       bzero(&params, sizeof(params));
+       params.filename = file_pid_paniclog;
+//     panic_fd=open(file_pid_paniclog, O_APPEND|O_CREAT|O_DIRECT);
+       params.filep = fopen(file_pid_paniclog, "a+");
+       params.debugLevel = ECRASH_DEBUG_VERBOSE;
+       params.dumpAllThreads = TRUE;
+       params.useBacktraceSymbols = 1;
+///    BuildSymbolTable(&symbol_table);
+//     params.symbolTable = &symbol_table;
+       params.signals[0]=SIGSEGV;
+       params.signals[1]=SIGILL;
+       params.signals[2]=SIGBUS;
+       params.signals[3]=SIGABRT;
+
+       eCrash_Init(&params);
+#endif 
+       setIPCDeathHook(screen_delete);
+       setIPCErrorPrintf(err_printf);
+       setCryptoStatusHook(statusHook);
+       
+       /* Permissions sanity check - don't run citadel setuid/setgid */
+       if (getuid() != geteuid()) {
+               err_printf("Please do not run citadel setuid!\n");
+               logoff(NULL, 3);
+       } else if (getgid() != getegid()) {
+               err_printf("Please do not run citadel setgid!\n");
+               logoff(NULL, 3);
+       }
+
+       stty_ctdl(SB_SAVE);     /* Store the old terminal parameters */
+       load_command_set();     /* parse the citadel.rc file */
+       stty_ctdl(SB_NO_INTR);  /* Install the new ones */
+       /* signal(SIGHUP, dropcarr);FIXME */    /* Cleanup gracefully if carrier is dropped */
+       signal(SIGPIPE, dropcarr);      /* Cleanup gracefully if local conn. dropped */
+       signal(SIGTERM, dropcarr);      /* Cleanup gracefully if terminated */
+       signal(SIGCONT, catch_sigcont); /* Catch SIGCONT so we can reset terminal */
+#ifdef SIGWINCH
+       signal(SIGWINCH, scr_winch);    /* Window resize signal */
+#endif
+
+#ifdef HAVE_OPENSSL
+       arg_encrypt = RC_DEFAULT;
+#endif
+#if defined(HAVE_CURSES_H) && !defined(DISABLE_CURSES)
+       arg_screen = RC_DEFAULT;
+#endif
+
+       /* 
+        * Handle command line options as if we were called like /bin/login
+        * (i.e. from in.telnetd)
+        */
+       for (a=0; a<argc; ++a) {
+               if ((argc > a+1) && (!strcmp(argv[a], "-h")) ) {
+                       telnet_client_host = argv[a+1];
+                       argc = shift(argc, argv, a, 2);
+               }
+               if (!strcmp(argv[a], "-x")) {
+#ifdef HAVE_OPENSSL
+                       arg_encrypt = RC_NO;
+#endif
+                       argc = shift(argc, argv, a, 1);
+               }
+               if (!strcmp(argv[a], "-X")) {
+#ifdef HAVE_OPENSSL
+                       arg_encrypt = RC_YES;
+                       argc = shift(argc, argv, a, 1);
+#else
+                       fprintf(stderr, "Not compiled with encryption support");
+                       return 1;
+#endif
+               }
+               if (!strcmp(argv[a], "-s")) {
+#if defined(HAVE_CURSES_H) && !defined(DISABLE_CURSES)
+                       arg_screen = RC_NO;
+#endif
+                       argc = shift(argc, argv, a, 1);
+               }
+               if (!strcmp(argv[a], "-S")) {
+#if defined(HAVE_CURSES_H) && !defined(DISABLE_CURSES)
+                       arg_screen = RC_YES;
+#endif
+                       argc = shift(argc, argv, a, 1);
+               }
+               if (!strcmp(argv[a], "-p")) {
+                       struct stat st;
+               
+                       if (chdir(CTDLDIR) < 0) {
+                               perror("can't change to " CTDLDIR);
+                               logoff(NULL, 3);
+                       }
+
+                       /*
+                        * Drop privileges if necessary. We stat
+                        * citadel.config to get the uid/gid since it's
+                        * guaranteed to have the uid/gid we want.
+                        */
+                       if (!getuid() || !getgid()) {
+                               if (stat(file_citadel_config, &st) < 0) {
+                                       perror("couldn't stat citadel.config");
+                                       logoff(NULL, 3);
+                               }
+                               if (!getgid() && (setgid(st.st_gid) < 0)) {
+                                       perror("couldn't change gid");
+                                       logoff(NULL, 3);
+                               }
+                               if (!getuid() && (setuid(st.st_uid) < 0)) {
+                                       perror("couldn't change uid");
+                                       logoff(NULL, 3);
+                               }
+                               /*
+                                 scr_printf("Privileges changed to uid %d gid %d\n",
+                                 getuid(), getgid());
+                               */
+                       }
+                       argc = shift(argc, argv, a, 1);
+               }
+       }
+       
+
+       screen_new();
+
+#ifdef __CYGWIN__
+       newprompt("Connect to (return for local server): ", hostbuf, 64);
+#endif
+
+       sln_printf("Attaching to server... \r");
+       sln_flush();
+       ipc = CtdlIPC_new(argc, argv, hostbuf, portbuf);
+       if (!ipc) {
+               screen_delete();
+               error_printf("Can't connect: %s\n", strerror(errno));
+               logoff(NULL, 3);
+       }
+#if defined(HAVE_CURSES_H) && !defined(DISABLE_CURSES)
+       CtdlIPC_SetNetworkStatusCallback(ipc, wait_indicator);
+#endif
+       ipc_for_signal_handlers = ipc;  /* KLUDGE cover your eyes */
+
+       CtdlIPC_chat_recv(ipc, aaa);
+       if (aaa[0] != '2') {
+               scr_printf("%s\n", &aaa[4]);
+               logoff(ipc, atoi(aaa));
+       }
+
+       /* If there is a [nonce] at the end, put the nonce in <nonce>, else nonce
+        * is zeroized.
+        */
+       
+       if ((sptr = strchr(aaa, '<')) == NULL)
+               {
+                       nonce[0] = '\0';
+               }
+       else
+               {
+                       if ((sptr2 = strchr(sptr, '>')) == NULL)
+                               {
+                                       nonce[0] = '\0';
+                               }
+                       else
+                               {
+                                       sptr2++;
+                                       *sptr2 = '\0';
+                                       strncpy(nonce, sptr, (size_t)NONCE_SIZE);
+                               }
+               }
+
+#ifdef HAVE_OPENSSL
+       /* Evaluate encryption preferences */
+       if (arg_encrypt != RC_NO && rc_encrypt != RC_NO) {
+               if (!ipc->isLocal || arg_encrypt == RC_YES || rc_encrypt == RC_YES) {
+                       secure = (CtdlIPCStartEncryption(ipc, aaa) / 100 == 2) ? 1 : 0;
+                       if (!secure)
+                               error_printf("Can't encrypt: %s\n", aaa);
+               }
+       }
+#endif
+
+       get_serv_info(ipc, telnet_client_host);
+       scr_printf("%-24s\n%s\n%s\n", ipc->ServInfo.software, ipc->ServInfo.humannode,
+                  ipc->ServInfo.site_location);
+       scr_flush();
+
+       status_line(ipc->ServInfo.humannode, ipc->ServInfo.site_location, NULL,
+                   secure, -1);
+
+       screenwidth = 80;       /* default screen dimensions */
+       screenheight = 24;
+       
+       scr_printf(" pause    next    stop\n");
+       scr_printf(" ctrl-s  ctrl-o  ctrl-c\n\n");
+       formout(ipc, "hello");  /* print the opening greeting */
+       scr_printf("\n");
+
+ GSTA: /* See if we have a username and password on disk */
+       if (rc_remember_passwords) {
+               get_stored_password(hostbuf, portbuf, fullname, password);
+               if (!IsEmptyStr(fullname)) {
+                       r = CtdlIPCTryLogin(ipc, fullname, aaa);
+                       if (r / 100 == 3) {
+                               if (*nonce) {
+                                       r = CtdlIPCTryApopPassword(ipc, make_apop_string(password, nonce, hexstring, sizeof hexstring), aaa);
+                               } else {
+                                       r = CtdlIPCTryPassword(ipc, password, aaa);
+                               }
+                       }
+
+                       if (r / 100 == 2) {
+                               load_user_info(aaa);
+                               stored_password = 1;
+                               goto PWOK;
+                       } else {
+                               set_stored_password(hostbuf, portbuf, "", "");
+                       }
+               }
+       }
+
+       termn8 = 0;
+       newnow = 0;
+       do {
+               if (!IsEmptyStr(rc_username)) {
+                       strcpy(fullname, rc_username);
+               } else {
+                       newprompt("Enter your name: ", fullname, 29);
+               }
+               strproc(fullname);
+               if (!strcasecmp(fullname, "new")) {     /* just in case */
+                       scr_printf("Please enter the name you wish to log in with.\n");
+               }
+       } while (
+                (!strcasecmp(fullname, "bbs"))
+                || (!strcasecmp(fullname, "new"))
+                || (IsEmptyStr(fullname)));
+
+       if (!strcasecmp(fullname, "off")) {
+               mcmd = 29;
+               goto TERMN8;
+       }
+       /* sign on to the server */
+       r = CtdlIPCTryLogin(ipc, fullname, aaa);
+       if (r / 100 != 3)
+               goto NEWUSR;
+
+       /* password authentication */
+       if (!IsEmptyStr(rc_password)) {
+               strcpy(password, rc_password);
+       } else {
+               newprompt("\rPlease enter your password: ", password, -19);
+       }
+       strproc(password);
+
+       if (*nonce) {
+               r = CtdlIPCTryApopPassword(ipc, make_apop_string(password, nonce, hexstring, sizeof hexstring), aaa);
+       } else {
+               r = CtdlIPCTryPassword(ipc, password, aaa);
+       }
+       
+       if (r / 100 == 2) {
+               load_user_info(aaa);
+               offer_to_remember_password(ipc, hostbuf, portbuf,
+                                          fullname, password);
+               goto PWOK;
+       }
+       scr_printf("<< wrong password >>\n");
+       if (!IsEmptyStr(rc_password))
+               logoff(ipc, 2);
+       goto GSTA;
+
+NEWUSR:        if (IsEmptyStr(rc_password)) {
+               scr_printf("'%s' not found.\n", fullname);
+               scr_printf("Type 'off' if you would like to exit.\n");
+               if (ipc->ServInfo.newuser_disabled == 1) {
+                       goto GSTA;
+               }
+               scr_printf("Do you want to create a new user account called '%s'? ",
+                       fullname);
+               if (yesno() == 0) {
+                       goto GSTA;
+               }
+       }
+
+       r = CtdlIPCCreateUser(ipc, fullname, 1, aaa);
+       if (r / 100 != 2) {
+               scr_printf("%s\n", aaa);
+               goto GSTA;
+       }
+       load_user_info(aaa);
+
+       while (set_password(ipc) != 0);
+       newnow = 1;
+
+       enter_config(ipc, 1);
+
+ PWOK:
+       /* Switch color support on or off if we're in user mode */
+       if (rc_ansi_color == 3) {
+               if (userflags & US_COLOR)
+                       enable_color = 1;
+               else
+                       enable_color = 0;
+       }
+
+       color(BRIGHT_WHITE);
+       scr_printf("\n%s\nAccess level: %d (%s)\n"
+                  "User #%ld / Login #%d",
+                  fullname, axlevel, axdefs[(int) axlevel],
+                  usernum, timescalled);
+       if (lastcall > 0L) {
+               scr_printf(" / Last login: %s",
+                          asctime(localtime(&lastcall)));
+       }
+       scr_printf("\n");
+
+       r = CtdlIPCMiscCheck(ipc, &chek, aaa);
+       if (r / 100 == 2) {
+               b = chek.newmail;
+               if (b > 0) {
+                       color(BRIGHT_RED);
+                       if (b == 1)
+                               scr_printf("*** You have a new private message in Mail>\n");
+                       if (b > 1)
+                               scr_printf("*** You have %d new private messages in Mail>\n", b);
+                       color(DIM_WHITE);
+                       if (!IsEmptyStr(rc_gotmail_cmd)) {
+                               rv = system(rc_gotmail_cmd);
+                       }
+               }
+               if ((axlevel >= 6) && (chek.needvalid > 0)) {
+                       scr_printf("*** Users need validation\n");
+               }
+               if (chek.needregis > 0) {
+                       scr_printf("*** Please register.\n");
+                       formout(ipc, "register");
+                       entregis(ipc);
+               }
+       }
+       /* Make up some temporary filenames for use in various parts of the
+        * program.  Don't mess with these once they've been set, because we
+        * will be unlinking them later on in the program and we don't
+        * want to delete something that we didn't create. */
+       CtdlMakeTempFileName(temp, sizeof temp);
+       CtdlMakeTempFileName(temp2, sizeof temp2);
+       CtdlMakeTempFileName(tempdir, sizeof tempdir);
+
+       /* Get screen dimensions.  First we go to a default of 80x24.  Then
+        * we try to get the user's actual screen dimensions off the server.
+        * However, if we're running on an xterm, all this stuff is
+        * irrelevant because we're going to dynamically size the screen
+        * during the session.
+        */
+       screenwidth = 80;
+       screenheight = 24;
+       r = CtdlIPCGetConfig(ipc, &myself, aaa);
+       if (r == 2) {
+               screenwidth = myself->USscreenwidth;
+               screenheight = myself->USscreenheight;
+       }
+       if (getenv("TERM") != NULL)
+               if (!strcmp(getenv("TERM"), "xterm")) {
+                       have_xterm = 1;
+               }
+#ifdef TIOCGWINSZ
+       check_screen_dims();
+#endif
+
+       set_floor_mode(ipc);
+
+       /* Enter the lobby */
+       dotgoto(ipc, "_BASEROOM_", 1, 0);
+
+       /* Main loop for the system... user is logged in. */
+    free(uglist[0]);
+       uglistsize = 0;
+
+       if (newnow == 1)
+               readmsgs(ipc, LastMessages, ReadForward, 5);
+       else
+               readmsgs(ipc, NewMessages, ReadForward, 0);
+
+       /* MAIN COMMAND LOOP */
+       do {
+               mcmd = getcmd(ipc, argbuf);     /* Get keyboard command */
+
+#ifdef TIOCGWINSZ
+               check_screen_dims();            /* if xterm, get screen size */
+#endif
+
+               if (termn8 == 0)
+                       switch (mcmd) {
+                       case 1:
+                               formout(ipc, "help");
+                               break;
+                       case 4:
+                               entmsg(ipc, 0, ((userflags & US_EXTEDIT) ? 2 : 0), 0);
+                               break;
+                       case 36:
+                               entmsg(ipc, 0, 1, 0);
+                               break;
+                       case 46:
+                               entmsg(ipc, 0, 2, 0);
+                               break;
+                       case 78:
+                               entmsg(ipc, 0, ((userflags & US_EXTEDIT) ? 2 : 0), 1);
+                               break;
+                       case 5:                         /* <G>oto */
+                               updatels(ipc);
+                               gotonext(ipc);
+                               break;
+                       case 47:                        /* <A>bandon */
+                               if (!rc_alt_semantics) {
+                                       updatelsa(ipc);
+                               }
+                               gotonext(ipc);
+                               break;
+                       case 90:                        /* <.A>bandon */
+                               if (!rc_alt_semantics)
+                                       updatelsa(ipc);
+                               dotgoto(ipc, argbuf, 0, 0);
+                               break;
+                       case 58:                        /* <M>ail */
+                               updatelsa(ipc);
+                               dotgoto(ipc, "_MAIL_", 1, 0);
+                               break;
+                       case 20:
+                               if (!IsEmptyStr(argbuf)) {
+                                       updatels(ipc);
+                                       dotgoto(ipc, argbuf, 0, 0);
+                               }
+                               break;
+                       case 52:
+                               if (!IsEmptyStr(argbuf)) {
+                                       if (rc_alt_semantics) {
+                                               updatelsa(ipc);
+                                       }
+                                       dotgoto(ipc, argbuf, 0, 0);
+                               }
+                               break;
+                       case 95: /* what exactly is the numbering scheme supposed to be anyway? --Ford, there isn't one. -IO */
+                               dotungoto(ipc, argbuf);
+                               break;
+                       case 10:
+                               readmsgs(ipc, AllMessages, ReadForward, 0);
+                               break;
+                       case 9:
+                               readmsgs(ipc, LastMessages, ReadForward, 5);
+                               break;
+                       case 13:
+                               readmsgs(ipc, NewMessages, ReadForward, 0);
+                               break;
+                       case 11:
+                               readmsgs(ipc, AllMessages, ReadReverse, 0);
+                               break;
+                       case 12:
+                               readmsgs(ipc, OldMessages, ReadReverse, 0);
+                               break;
+                       case 71:
+                               readmsgs(ipc, LastMessages, ReadForward,
+                                               atoi(argbuf));
+                               break;
+                       case 7:
+                               forget(ipc);
+                               break;
+                       case 18:
+                               subshell();
+                               break;
+                       case 38:
+                               updatels(ipc);
+                               entroom(ipc);
+                               break;
+                       case 22:
+                               killroom(ipc);
+                               break;
+                       case 32:
+                               userlist(ipc, argbuf);
+                               break;
+                       case 27:
+                               invite(ipc);
+                               break;
+                       case 28:
+                               kickout(ipc);
+                               break;
+                       case 23:
+                               editthisroom(ipc);
+                               break;
+                       case 14:
+                               roomdir(ipc);
+                               break;
+                       case 33:
+                               download(ipc, 0);
+                               break;
+                       case 34:
+                               download(ipc, 1);
+                               break;
+                       case 31:
+                               download(ipc, 2);
+                               break;
+                       case 43:
+                               download(ipc, 3);
+                               break;
+                       case 45:
+                               download(ipc, 4);
+                               break;
+                       case 55:
+                               download(ipc, 5);
+                               break;
+                       case 39:
+                               upload(ipc, 0);
+                               break;
+                       case 40:
+                               upload(ipc, 1);
+                               break;
+                       case 42:
+                               upload(ipc, 2);
+                               break;
+                       case 44:
+                               upload(ipc, 3);
+                               break;
+                       case 57:
+                               cli_upload(ipc);
+                               break;
+                       case 16:
+                               ungoto(ipc);
+                               break;
+                       case 24:
+                               whoknows(ipc);
+                               break;
+                       case 26:
+                               validate(ipc);
+                               break;
+                       case 29:
+                       case 30:
+                               if (!rc_alt_semantics) {
+                                       updatels(ipc);
+                               }
+                               termn8 = 1;
+                               break;
+                       case 48:
+                               enterinfo(ipc);
+                               break;
+                       case 49:
+                               readinfo(ipc);
+                               break;
+                       case 72:
+                               cli_image_upload(ipc, "_userpic_");
+                               break;
+                       case 73:
+                               cli_image_upload(ipc, "_roompic_");
+                               break;
+
+                       case 74:
+                               snprintf(aaa, sizeof aaa, "_floorpic_|%d", curr_floor);
+                               cli_image_upload(ipc, aaa);
+                               break;
+
+                       case 75:
+                               enternew(ipc, "roomname", aaa, 20);
+                               r = CtdlIPCChangeRoomname(ipc, aaa, bbb);
+                               if (r / 100 != 2)
+                                       scr_printf("\n%s\n", bbb);
+                               break;
+                       case 76:
+                               enternew(ipc, "hostname", aaa, 25);
+                               r = CtdlIPCChangeHostname(ipc, aaa, bbb);
+                               if (r / 100 != 2)
+                                       scr_printf("\n%s\n", bbb);
+                               break;
+                       case 77:
+                               enternew(ipc, "username", aaa, 32);
+                               r = CtdlIPCChangeUsername(ipc, aaa, bbb);
+                               if (r / 100 != 2)
+                                       scr_printf("\n%s\n", bbb);
+                               break;
+
+                       case 35:
+                               set_password(ipc);
+                               break;
+
+                       case 21:
+                               if (argbuf[0] == 0)
+                                       strcpy(aaa, "?");
+                               display_help(ipc, argbuf);
+                               break;
+
+                       case 41:
+                               formout(ipc, "register");
+                               entregis(ipc);
+                               break;
+
+                       case 15:
+                               scr_printf("Are you sure (y/n)? ");
+                               if (yesno() == 1) {
+                                       if (!rc_alt_semantics)
+                                               updatels(ipc);
+                                       a = 0;
+                                       termn8 = 1;
+                               }
+                               break;
+
+                       case 85:
+                               scr_printf("All users will be disconnected!  "
+                                          "Really terminate the server? ");
+                               if (yesno() == 1) {
+                                       if (!rc_alt_semantics)
+                                               updatels(ipc);
+                                       r = CtdlIPCTerminateServerNow(ipc, aaa);
+                                       scr_printf("%s\n", aaa);
+                                       if (r / 100 == 2) {
+                                               a = 0;
+                                               termn8 = 1;
+                                       }
+                               }
+                               break;
+
+                       case 86:
+                               scr_printf("Do you really want to schedule a "
+                                          "server shutdown? ");
+                               if (yesno() == 1) {
+                                       r = CtdlIPCTerminateServerScheduled(ipc, 1, aaa);
+                                       if (r / 100 == 2) {
+                                               if (atoi(aaa)) {
+                                                       scr_printf(
+                                                                  "The Citadel server will terminate when all users are logged off.\n"
+                                                                  );
+                                               } else {
+                                                       scr_printf(
+                                                                  "The Citadel server will not terminate.\n"
+                                                                  );
+                                               }
+                                       }
+                               }
+                               break;
+
+                       case 87:
+                               network_config_management(ipc, "listrecp",
+                                                         "Message-by-message mailing list recipients");
+                               break;
+
+                       case 94:
+                               network_config_management(ipc, "digestrecp",
+                                                         "Digest mailing list recipients");
+                               break;
+
+                       case 89:
+                               network_config_management(ipc, "ignet_push_share",
+                                                         "Nodes with which we share this room");
+                               break;
+
+                       case 88:
+                               do_ignet_configuration(ipc);
+                               break;
+
+                       case 92:
+                               do_filterlist_configuration(ipc);
+                               break;
+
+                       case 6:
+                               if (rc_alt_semantics) {
+                                       updatelsa(ipc);
+                               }
+                               gotonext(ipc);
+                               break;
+
+                       case 3:
+                               chatmode(ipc);
+                               break;
+
+                       case 2:
+                               if (ipc->isLocal) {
+                                       screen_reset();
+                                       stty_ctdl(SB_RESTORE);
+                                       snprintf(aaa, sizeof aaa, "USERNAME=\042%s\042; export USERNAME;"
+                                                "exec ./subsystem %ld %d %d", fullname,
+                                                usernum, screenwidth, axlevel);
+                                       ka_system(aaa);
+                                       stty_ctdl(SB_NO_INTR);
+                                       screen_set();
+                               } else {
+                                       scr_printf("*** Can't run doors when server is not local.\n");
+                               }
+                               break;
+
+                       case 17:
+                               who_is_online(ipc, 0);
+                               break;
+
+                       case 79:
+                               who_is_online(ipc, 1);
+                               break;
+
+                       case 91:
+                               who_is_online(ipc, 2);
+                               break;
+                
+                       case 80:
+                               do_system_configuration(ipc);
+                               break;
+
+                       case 82:
+                               do_internet_configuration(ipc);
+                               break;
+
+                       case 83:
+                               check_message_base(ipc);
+                               break;
+
+                       case 84:
+                               quiet_mode(ipc);
+                               break;
+
+                       case 93:
+                               stealth_mode(ipc);
+                               break;
+
+                       case 50:
+                               enter_config(ipc, 2);
+                               break;
+
+                       case 37:
+                               enter_config(ipc, 0);
+                               set_floor_mode(ipc);
+                               break;
+
+                       case 59:
+                               enter_config(ipc, 3);
+                               set_floor_mode(ipc);
+                               break;
+
+                       case 60:
+                               gotofloor(ipc, argbuf, GF_GOTO);
+                               break;
+
+                       case 61:
+                               gotofloor(ipc, argbuf, GF_SKIP);
+                               break;
+
+                       case 62:
+                               forget_this_floor(ipc);
+                               break;
+
+                       case 63:
+                               create_floor(ipc);
+                               break;
+
+                       case 64:
+                               edit_floor(ipc);
+                               break;
+
+                       case 65:
+                               kill_floor(ipc);
+                               break;
+
+                       case 66:
+                               enter_bio(ipc);
+                               break;
+
+                       case 67:
+                               read_bio(ipc);
+                               break;
+
+                       case 25:
+                               edituser(ipc, 25);
+                               break;
+
+                       case 96:
+                               edituser(ipc, 96);
+                               break;
+
+                       case 8:
+                               knrooms(ipc, floor_mode);
+                               scr_printf("\n");
+                               break;
+
+                       case 68:
+                               knrooms(ipc, 2);
+                               scr_printf("\n");
+                               break;
+
+                       case 69:
+                               misc_server_cmd(ipc, argbuf);
+                               break;
+
+                       case 70:
+                               edit_system_message(ipc, argbuf);
+                               break;
+
+                       case 19:
+                               listzrooms(ipc);
+                               scr_printf("\n");
+                               break;
+
+                       case 51:
+                               deletefile(ipc);
+                               break;
+
+                       case 54:
+                               movefile(ipc);
+                               break;
+
+                       case 56:
+                               page_user(ipc);
+                               break;
+
+            case 110:           /* <+> Next room */
+                                gotoroomstep(ipc, 1, 0);
+                            break;
+
+            case 111:           /* <-> Previous room */
+                 gotoroomstep(ipc, 0, 0);
+                            break;
+
+                       case 112:           /* <>> Next floor */
+                 gotofloorstep(ipc, 1, GF_GOTO);
+                            break;
+
+                       case 113:           /* <<> Previous floor */
+                 gotofloorstep(ipc, 0, GF_GOTO);
+                                break;
+
+            case 116:           /* <.> skip to <+> Next room */
+                 gotoroomstep(ipc, 1, 1);
+                            break;
+
+            case 117:           /* <.> skip to <-> Previous room */
+                 gotoroomstep(ipc, 0, 1);
+                            break;
+
+                       case 118:           /* <.> skip to <>> Next floor */
+                 gotofloorstep(ipc, 1, GF_SKIP);
+                            break;
+
+                       case 119:           /* <.> skip to <<> Previous floor */
+                 gotofloorstep(ipc, 0, GF_SKIP);
+                                break;
+
+                       case 114:           
+                 read_config(ipc);
+                                break;
+
+                       case 115:           
+                 system_info(ipc);
+                                break;
+
+                       case 120:           /* .KAnonymous */
+                        dotknown(ipc, 0, NULL);
+                                break;
+
+                       case 121:           /* .KDirectory */
+                        dotknown(ipc, 1, NULL);
+                                break;
+
+                       case 122:           /* .KMatch */
+                        dotknown(ipc, 2, argbuf);
+                                break;
+
+                       case 123:           /* .KpreferredOnly */
+                        dotknown(ipc, 3, NULL);
+                                break;
+
+                       case 124:           /* .KPrivate */
+                        dotknown(ipc, 4, NULL);
+                                break;
+
+                       case 125:           /* .KRead only */
+                        dotknown(ipc, 5, NULL);
+                                break;
+
+                       case 126:           /* .KShared */
+                        dotknown(ipc, 6, NULL);
+                                break;
+
+                       case 127:           /* Configure POP3 aggregation */
+                               do_pop3client_configuration(ipc);
+                               break;
+
+                       case 128:           /* Configure XML/RSS feed retrieval */
+                               do_rssclient_configuration(ipc);
+                               break;
+
+                       default: /* allow some math on the command */
+                               /* commands 100... to 100+MAX_EDITORS-1 will
+                                  call the appropriate editor... in other
+                                  words, command numbers 100+ will match
+                                  the citadel.rc keys editor0, editor1, etc.*/
+                               if (mcmd >= 100 && mcmd < (100+MAX_EDITORS))
+                               {
+                                       /* entmsg mode >=2 select editor */
+                                       entmsg(ipc, 0, mcmd - 100 + 2, 0);
+                                       break;
+                               }
+                       }       /* end switch */
+       } while (termn8 == 0);
+
+TERMN8:        scr_printf("%s logged out.", fullname);
+       termn8 = 0;
+       color(ORIGINAL_PAIR);
+       scr_printf("\n");
+       while (marchptr != NULL) {
+               remove_march(marchptr->march_name, 0);
+       }
+       if (mcmd == 30) {
+               sln_printf("\n\nType 'off' to disconnect, or next user...\n");
+       }
+       CtdlIPCLogout(ipc);
+       if ((mcmd == 29) || (mcmd == 15)) {
+               screen_delete();
+               stty_ctdl(SB_RESTORE);
+               formout(ipc, "goodbye");
+               logoff(ipc, 0);
+       }
+       /* Free the ungoto list */
+       for (lp = 0; lp < uglistsize; lp++) {
+               free(uglist[lp]);
+       }
+    uglistsize = 0;
+       goto GSTA;
+
+}      /* end main() */
+
diff --git a/citadel/textclient/client_chat.c b/citadel/textclient/client_chat.c
new file mode 100644 (file)
index 0000000..c86914f
--- /dev/null
@@ -0,0 +1,333 @@
+/*
+ * $Id$
+ *
+ * front end for chat mode
+ * (the "single process" version - no more fork() anymore)
+ *
+ * Copyright (c) 1987-2009 by the citadel.org team
+ *
+ *  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 3 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; if not, write to the Free Software
+ *  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ */
+
+#include "sysdep.h"
+#include <stdlib.h>
+#include <unistd.h>
+#include <stdio.h>
+#include <fcntl.h>
+#include <ctype.h>
+#include <string.h>
+#include <signal.h>
+#include <errno.h>
+
+#if TIME_WITH_SYS_TIME
+# include <sys/time.h>
+# include <time.h>
+#else
+# if HAVE_SYS_TIME_H
+#  include <sys/time.h>
+# else
+#  include <time.h>
+# endif
+#endif
+
+#include <sys/types.h>
+#ifdef HAVE_SYS_SELECT_H
+#include <sys/select.h>
+#endif
+#include <stdarg.h>
+#include <libcitadel.h>
+#include "citadel.h"
+#include "citadel_ipc.h"
+#include "client_chat.h"
+#include "commands.h"
+#include "routines.h"
+#include "citadel_decls.h"
+#include "rooms.h"
+#include "messages.h"
+#ifndef HAVE_SNPRINTF
+#include "snprintf.h"
+#endif
+#include "screen.h"
+
+#define MIN(a, b) ((a) < (b) ? (a) : (b))
+
+extern char temp[];
+void ctdl_getline(char *, int);
+
+char last_paged[SIZ] = "";
+
+void chatmode(CtdlIPC *ipc)
+{
+       char wbuf[SIZ];
+       char buf[SIZ];
+       char c_user[SIZ];
+       char c_text[SIZ];
+       char c_room[SIZ];
+       char last_user[SIZ];
+       int send_complete_line;
+       int recv_complete_line;
+       char ch;
+       int a, pos;
+       time_t last_transmit;
+
+       fd_set rfds;
+       struct timeval tv;
+       int retval;
+
+       CtdlIPC_chat_send(ipc, "CHAT");
+       CtdlIPC_chat_recv(ipc, buf);
+       if (buf[0] != '8') {
+               scr_printf("%s\n", &buf[4]);
+               return;
+       }
+       scr_printf("Entering chat mode "
+               "(type /quit to exit, /help for other cmds)\n");
+       set_keepalives(KA_NO);
+       last_transmit = time(NULL);
+
+       strcpy(buf, "");
+       strcpy(wbuf, "");
+       strcpy(last_user, ""); 
+       color(BRIGHT_YELLOW);
+       sln_printf_if("\n");
+       sln_printf("> ");
+       send_complete_line = 0;
+       recv_complete_line = 0;
+
+       while (1) {
+               sln_flush();
+               FD_ZERO(&rfds);
+               FD_SET(0, &rfds);
+               FD_SET(CtdlIPC_getsockfd(ipc), &rfds);
+               tv.tv_sec = S_KEEPALIVE;
+               tv.tv_usec = 0;
+               retval = select(CtdlIPC_getsockfd(ipc) + 1, &rfds,
+                       NULL, NULL, &tv);
+
+               /* If there's data from the server... */
+               if (FD_ISSET(CtdlIPC_getsockfd(ipc), &rfds)) {
+                       CtdlIPC_chat_recv(ipc, buf);
+                       recv_complete_line = 1;
+                       goto RCL;       /* ugly, but we've gotta get out! */
+               }
+
+               /* If there's data from the keyboard... */
+               if (FD_ISSET(0, &rfds)) {
+                       ch = scr_getc(SCR_BLOCK);
+                       if ((ch == 10) || (ch == 13)) {
+                               send_complete_line = 1;
+                       } else if ((ch == 8) || (ch == 127)) {
+                               if (!IsEmptyStr(wbuf)) {
+                                       wbuf[strlen(wbuf) - 1] = 0;
+                                       sln_printf("%c %c", 8, 8);
+                               }
+                       } else {
+                               sln_putc(ch);
+                               wbuf[strlen(wbuf) + 1] = 0;
+                               wbuf[strlen(wbuf)] = ch;
+                       }
+               }
+
+               /* if the user hit return, send the line */
+RCL:           if (send_complete_line) {
+                       CtdlIPC_chat_send(ipc, wbuf);
+                       last_transmit = time(NULL);
+                       strcpy(wbuf, "");
+                       send_complete_line = 0;
+               }
+
+               /* if it's time to word wrap, send a partial line */
+               if (strlen(wbuf) >= (77 - strlen(fullname))) {
+                       pos = 0;
+                       for (a = 0; !IsEmptyStr(&wbuf[a]); ++a) {
+                               if (wbuf[a] == 32)
+                                       pos = a;
+                       }
+                       if (pos == 0) {
+                               CtdlIPC_chat_send(ipc, wbuf);
+                               last_transmit = time(NULL);
+                               strcpy(wbuf, "");
+                               send_complete_line = 0;
+                       } else {
+                               wbuf[pos] = 0;
+                               CtdlIPC_chat_send(ipc, wbuf);
+                               last_transmit = time(NULL);
+                               strcpy(wbuf, &wbuf[pos + 1]);
+                       }
+               }
+
+               if (recv_complete_line) {
+                       sln_printf("\r%79s\r", "");
+                       if (!strcmp(buf, "000")) {
+                               color(BRIGHT_WHITE);
+                               sln_printf("\rExiting chat mode\n");
+                               sln_flush();
+                               set_keepalives(KA_YES);
+
+                               /* Some users complained about the client and
+                                * server losing protocol synchronization when
+                                * exiting chat.  This little dialog forces
+                                * everything to be hunky-dory.
+                                */
+                               CtdlIPC_chat_send(ipc, "ECHO __ExitingChat__");
+                               do {
+                                       CtdlIPC_chat_recv(ipc, buf);
+                               } while (strcmp(buf, "200 __ExitingChat__"));
+                               return;
+                       }
+                       if (num_parms(buf) >= 2) {
+                               extract_token(c_user, buf, 0, '|', sizeof c_user);
+                               extract_token(c_text, buf, 1, '|', sizeof c_text);
+                               if (num_parms(buf) > 2) {
+                                       extract_token(c_room, buf, 2, '|', sizeof c_room);
+                                       scr_printf("Got room %s\n", c_room);
+                               }
+                               if (strcasecmp(c_text, "NOOP")) {
+                                       if (!strcmp(c_user, fullname)) {
+                                               color(BRIGHT_YELLOW);
+                                       } else if (!strcmp(c_user, ":")) {
+                                               color(BRIGHT_RED);
+                                       } else {
+                                               color(BRIGHT_GREEN);
+                                       }
+                                       if (strcmp(c_user, last_user)) {
+                                               snprintf(buf, sizeof buf, "%s: %s", c_user, c_text);
+                                       } else {
+                                               size_t i = MIN(sizeof buf - 1,
+                                                    strlen(c_user) + 2);
+
+                                               memset(buf, ' ', i);
+                                               safestrncpy(&buf[i], c_text,
+                                                        sizeof buf - i);
+                                       }
+                                       while (strlen(buf) < 79)
+                                               strcat(buf, " ");
+                                       if (strcmp(c_user, last_user)) {
+                                               sln_printf("\r%79s\n", "");
+                                               strcpy(last_user, c_user);
+                                       }
+                                       scr_printf("\r%s\n", buf);
+                                       scr_flush();
+                               }
+                       }
+                       color(BRIGHT_YELLOW);
+                       sln_printf("\r> %s", wbuf);
+                       sln_flush();
+                       recv_complete_line = 0;
+                       strcpy(buf, "");
+               }
+
+               /* If the user is sitting idle, send a half-keepalive to the
+                * server to prevent session timeout.
+                */
+               if ((time(NULL) - last_transmit) >= S_KEEPALIVE) {
+                       CtdlIPC_chat_send(ipc, "NOOP");
+                       last_transmit = time(NULL);
+               }
+
+       }
+}
+
+/*
+ * send an instant message
+ */
+void page_user(CtdlIPC *ipc)
+{
+       char buf[SIZ], touser[SIZ], msg[SIZ];
+       FILE *pagefp;
+
+       strcpy(touser, last_paged);
+       strprompt("Page who", touser, 30);
+
+       /* old server -- use inline paging */
+       if (ipc->ServInfo.paging_level == 0) {
+               newprompt("Message: ", msg, 69);
+               snprintf(buf, sizeof buf, "SEXP %s|%s", touser, msg);
+               CtdlIPC_chat_send(ipc, buf);
+               CtdlIPC_chat_recv(ipc, buf);
+               if (!strncmp(buf, "200", 3)) {
+                       strcpy(last_paged, touser);
+               }
+               scr_printf("%s\n", &buf[4]);
+               return;
+       }
+       /* new server -- use extended paging */
+       else if (ipc->ServInfo.paging_level >= 1) {
+               snprintf(buf, sizeof buf, "SEXP %s||", touser);
+               CtdlIPC_chat_send(ipc, buf);
+               CtdlIPC_chat_recv(ipc, buf);
+               if (buf[0] != '2') {
+                       scr_printf("%s\n", &buf[4]);
+                       return;
+               }
+               if (client_make_message(ipc, temp, touser, 0, 0, 0, NULL, 0) != 0) {
+                       scr_printf("No message sent.\n");
+                       return;
+               }
+               pagefp = fopen(temp, "r");
+               unlink(temp);
+               snprintf(buf, sizeof buf, "SEXP %s|-", touser);
+               CtdlIPC_chat_send(ipc, buf);
+               CtdlIPC_chat_recv(ipc, buf);
+               if (buf[0] == '4') {
+                       strcpy(last_paged, touser);
+                       while (fgets(buf, sizeof buf, pagefp) != NULL) {
+                               buf[strlen(buf) - 1] = 0;
+                               CtdlIPC_chat_send(ipc, buf);
+                       }
+                       fclose(pagefp);
+                       CtdlIPC_chat_send(ipc, "000");
+                       scr_printf("Message sent.\n");
+               } else {
+                       scr_printf("%s\n", &buf[4]);
+               }
+       }
+}
+
+
+void quiet_mode(CtdlIPC *ipc)
+{
+       static int quiet = 0;
+       char cret[SIZ];
+       int r;
+
+       r = CtdlIPCEnableInstantMessageReceipt(ipc, !quiet, cret);
+       if (r / 100 == 2) {
+               quiet = !quiet;
+               scr_printf("Quiet mode %sabled (%sother users may page you)\n",
+                               (quiet) ? "en" : "dis",
+                               (quiet) ? "no " : "");
+       } else {
+               scr_printf("Unable to change quiet mode: %s\n", cret);
+       }
+}
+
+
+void stealth_mode(CtdlIPC *ipc)
+{
+       static int stealth = 0;
+       char cret[SIZ];
+       int r;
+
+       r = CtdlIPCStealthMode(ipc, !stealth, cret);
+       if (r / 100 == 2) {
+               stealth = !stealth;
+               scr_printf("Stealth mode %sabled (you are %s)\n",
+                               (stealth) ? "en" : "dis",
+                               (stealth) ? "invisible" : "listed as online");
+       } else {
+               scr_printf("Unable to change stealth mode: %s\n", cret);
+       }
+}
diff --git a/citadel/textclient/client_chat.h b/citadel/textclient/client_chat.h
new file mode 100644 (file)
index 0000000..4515961
--- /dev/null
@@ -0,0 +1,25 @@
+/* $Id$ 
+ *
+ * Copyright (c) 1987-2009 by the citadel.org team
+ *
+ *  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 3 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; if not, write to the Free Software
+ *  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ */
+
+void chatmode(CtdlIPC *ipc);
+void page_user(CtdlIPC *ipc);
+void quiet_mode(CtdlIPC *ipc);
+void stealth_mode(CtdlIPC *ipc);
+
+extern char last_paged[];
diff --git a/citadel/textclient/client_passwords.c b/citadel/textclient/client_passwords.c
new file mode 100644 (file)
index 0000000..4922a12
--- /dev/null
@@ -0,0 +1,160 @@
+/*
+ * $Id$
+ *
+ * Functions which allow the client to remember usernames and passwords for
+ * various sites.
+ *
+ * Copyright (c) 1987-2009 by the citadel.org team
+ *
+ *  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 3 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; if not, write to the Free Software
+ *  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ */
+
+#include "sysdep.h"
+#include <stdlib.h>
+#include <unistd.h>
+#include <string.h>
+#include <ctype.h>
+#include <pwd.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <limits.h>
+#include <stdio.h>
+#include <libcitadel.h>
+#include "citadel.h"
+#include "citadel_ipc.h"
+#include "commands.h"
+#include "client_passwords.h"
+
+#define PWFILENAME "%s/.citadel.passwords"
+
+void determine_pwfilename(char *pwfile, size_t n) {
+       struct passwd *p;
+
+       p = getpwuid(getuid());
+       if (p == NULL) strcpy(pwfile, "");
+       snprintf(pwfile, n, PWFILENAME, p->pw_dir);
+}
+
+
+/*
+ * Check the password file for a host/port match; if found, stuff the user
+ * name and password into the user/pass buffers
+ */
+void get_stored_password(
+               char *host,
+               char *port,
+               char *username,
+               char *password) {
+
+       char pwfile[PATH_MAX];
+       FILE *fp;
+       char buf[SIZ];
+       char buf64[SIZ];
+       char hostbuf[256], portbuf[256], ubuf[256], pbuf[256];
+
+       strcpy(username, "");
+       strcpy(password, "");
+
+       determine_pwfilename(pwfile, sizeof pwfile);
+       if (IsEmptyStr(pwfile)) return;
+
+       fp = fopen(pwfile, "r");
+       if (fp == NULL) return;
+       while (fgets(buf64, sizeof buf64, fp) != NULL) {
+               CtdlDecodeBase64(buf, buf64, sizeof(buf64));
+               extract_token(hostbuf, buf, 0, '|', sizeof hostbuf);
+               extract_token(portbuf, buf, 1, '|', sizeof portbuf);
+               extract_token(ubuf, buf, 2, '|', sizeof ubuf);
+               extract_token(pbuf, buf, 3, '|', sizeof pbuf);
+
+               if (!strcasecmp(hostbuf, host)) {
+                       if (!strcasecmp(portbuf, port)) {
+                               strcpy(username, ubuf);
+                               strcpy(password, pbuf);
+                       }
+               }
+       }
+       fclose(fp);
+}
+
+
+/*
+ * Set (or clear) stored passwords.
+ */
+void set_stored_password(
+               char *host,
+               char *port,
+               char *username,
+               char *password) {
+
+       char pwfile[PATH_MAX];
+       FILE *fp, *oldfp;
+       char buf[SIZ];
+       char buf64[SIZ];
+       char hostbuf[256], portbuf[256], ubuf[256], pbuf[256];
+
+       determine_pwfilename(pwfile, sizeof pwfile);
+       if (IsEmptyStr(pwfile)) return;
+
+       oldfp = fopen(pwfile, "r");
+       if (oldfp == NULL) oldfp = fopen("/dev/null", "r");
+       unlink(pwfile);
+       fp = fopen(pwfile, "w");
+       if (fp == NULL) fp = fopen("/dev/null", "w");
+       while (fgets(buf64, sizeof buf64, oldfp) != NULL) {
+               CtdlDecodeBase64(buf, buf64, sizeof(buf64));
+               extract_token(hostbuf, buf, 0, '|', sizeof hostbuf);
+               extract_token(portbuf, buf, 1, '|', sizeof portbuf);
+               extract_token(ubuf, buf, 2, '|', sizeof ubuf);
+               extract_token(pbuf, buf, 3, '|', sizeof pbuf);
+
+               if ( (strcasecmp(hostbuf, host)) 
+                  || (strcasecmp(portbuf, port)) ) {
+                       snprintf(buf, sizeof buf, "%s|%s|%s|%s|",
+                               hostbuf, portbuf, ubuf, pbuf);
+                       CtdlEncodeBase64(buf64, buf, strlen(buf), 0);
+                       fprintf(fp, "%s\n", buf64);
+               }
+       }
+       if (!IsEmptyStr(username)) {
+               snprintf(buf, sizeof buf, "%s|%s|%s|%s|",
+                       host, port, username, password);
+               CtdlEncodeBase64(buf64, buf, strlen(buf), 0);
+               fprintf(fp, "%s\n", buf64);
+       }
+       fclose(oldfp);
+       fclose(fp);
+       chmod(pwfile, 0600);
+}
+
+
+/*
+ * Set the password if the user wants to, clear it otherwise 
+ */
+void offer_to_remember_password(CtdlIPC *ipc,
+               char *host,
+               char *port,
+               char *username,
+               char *password) {
+
+       if (rc_remember_passwords) {
+               if (boolprompt("Remember username/password for this site", 0)) {
+                       set_stored_password(host, port, username, password);
+               }
+               else {
+                       set_stored_password(host, port, "", "");
+               }
+       }
+}
diff --git a/citadel/textclient/client_passwords.h b/citadel/textclient/client_passwords.h
new file mode 100644 (file)
index 0000000..6f6a454
--- /dev/null
@@ -0,0 +1,37 @@
+/* 
+ * $Id$
+ *
+ * Copyright (c) 1987-2009 by the citadel.org team
+ *
+ *  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 3 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; if not, write to the Free Software
+ *  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ */
+
+
+void determine_pwfilename(char *pwfile, size_t n);
+void get_stored_password(
+               char *host,
+               char *port,
+               char *username,
+               char *password);
+void set_stored_password(
+               char *host,
+               char *port,
+               char *username,
+               char *password);
+void offer_to_remember_password(CtdlIPC *ipc,
+               char *host,
+               char *port,
+               char *username,
+               char *password);
diff --git a/citadel/textclient/commands.c b/citadel/textclient/commands.c
new file mode 100644 (file)
index 0000000..427887c
--- /dev/null
@@ -0,0 +1,1712 @@
+/*
+ * $Id$
+ *
+ * This file contains functions which implement parts of the
+ * text-mode user interface.
+ *
+ * Copyright (c) 1987-2009 by the citadel.org team
+ *
+ *  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 3 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; if not, write to the Free Software
+ *  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ */
+
+#include "sysdep.h"
+#include <stdlib.h>
+#include <unistd.h>
+#include <stdio.h>
+#include <fcntl.h>
+#include <ctype.h>
+#include <string.h>
+#include <sys/types.h>
+
+#if TIME_WITH_SYS_TIME
+# include <sys/time.h>
+# include <time.h>
+#else
+# if HAVE_SYS_TIME_H
+#  include <sys/time.h>
+# else
+#  include <time.h>
+# endif
+#endif
+
+#ifdef HAVE_TERMIOS_H
+#include <termios.h>
+#else
+#include <sgtty.h>
+#endif
+
+#ifdef HAVE_SYS_SELECT_H
+#include <sys/select.h>
+#endif
+
+#ifdef THREADED_CLIENT
+#include <pthread.h>
+#endif
+
+#include <signal.h>
+#include <errno.h>
+#include <stdarg.h>
+#include <libcitadel.h>
+#include "citadel.h"
+#include "citadel_ipc.h"
+#include "commands.h"
+#include "messages.h"
+#include "citadel_decls.h"
+#include "routines.h"
+#include "routines2.h"
+#include "rooms.h"
+#include "client_chat.h"
+#include "citadel_dirs.h"
+#ifndef HAVE_SNPRINTF
+#include "snprintf.h"
+#endif
+#include "screen.h"
+#include "ecrash.h"
+
+struct citcmd {
+       struct citcmd *next;
+       int c_cmdnum;
+       int c_axlevel;
+       char c_keys[5][64];
+};
+
+#define IFNEXPERT if ((userflags&US_EXPERT)==0)
+
+
+int rc_exp_beep;
+char rc_exp_cmd[1024];
+int rc_allow_attachments;
+int rc_display_message_numbers;
+int rc_force_mail_prompts;
+int rc_remember_passwords;
+int rc_ansi_color;
+int rc_color_use_bg;
+int rc_prompt_control = 0;
+time_t rc_idle_threshold = (time_t)900;
+char rc_url_cmd[SIZ];
+char rc_open_cmd[SIZ];
+char rc_gotmail_cmd[SIZ];
+
+char *gl_string;
+int next_lazy_cmd = 5;
+
+int lines_printed = 0;         /* line count for paginator */
+extern int screenwidth, screenheight;
+extern int termn8;
+extern CtdlIPC *ipc_for_signal_handlers;       /* KLUDGE cover your eyes */
+
+struct citcmd *cmdlist = NULL;
+
+
+/* these variables are local to this module */
+char keepalives_enabled = KA_YES;      /* send NOOPs to server when idle */
+int ok_to_interrupt = 0;               /* print instant msgs asynchronously */
+time_t AnsiDetect;                     /* when did we send the detect code? */
+int enable_color = 0;                  /* nonzero for ANSI color */
+
+
+
+
+/*
+ * If an interesting key has been pressed, return its value, otherwise 0
+ */
+char was_a_key_pressed(void) {
+       fd_set rfds;
+       struct timeval tv;
+       int the_character;
+       int retval;
+
+       FD_ZERO(&rfds);
+       FD_SET(0, &rfds);
+       tv.tv_sec = 0;
+       tv.tv_usec = 0;
+       retval = select(1, &rfds, NULL, NULL, &tv); 
+
+       /* Careful!  Disable keepalives during keyboard polling; we're probably
+        * in the middle of a data transfer from the server, in which case
+        * sending a NOOP would throw the client protocol out of sync.
+        */
+       if (FD_ISSET(0, &rfds)) {
+               set_keepalives(KA_NO);
+               the_character = inkey();
+               set_keepalives(KA_YES);
+       }
+       else {
+               the_character = 0;
+       }
+       return(the_character);
+}
+
+
+
+
+
+/*
+ * Check to see if we need to pause at the end of a screen.
+ * If we do, we have to switch to half keepalives during the pause because
+ * we are probably in the middle of a server operation and the NOOP command
+ * would confuse everything.
+ */
+int checkpagin(int lp, unsigned int pagin, unsigned int height)
+{
+       int thekey;
+
+       if (sigcaught) return(lp);
+       thekey = was_a_key_pressed();
+       if (thekey == 'q' || thekey == 'Q' || thekey == 's' || thekey == 'S')
+               thekey = STOP_KEY;
+       if (thekey == 'n' || thekey == 'N')
+               thekey = NEXT_KEY;
+       if ( (thekey == NEXT_KEY) || (thekey == STOP_KEY)) sigcaught = thekey;
+       if (sigcaught) return(lp);
+
+       if (!pagin) return(0);
+       if (lp>=(height-1)) {
+               set_keepalives(KA_HALF);
+               hit_any_key(ipc_for_signal_handlers);   /* Cheating -IO */
+               set_keepalives(KA_YES);
+               return(0);
+       }
+       return(lp);
+}
+
+
+
+
+/*
+ * pprintf()  ...   paginated version of printf()
+ */
+void pprintf(const char *format, ...) {   
+        va_list arg_ptr;
+       static char buf[4096];  /* static for performance, change if needed */
+       int i;
+
+       /* If sigcaught is nonzero, a keypress has interrupted this and we
+        * should just drain output.
+        */
+       if (sigcaught) return;
+       /* Otherwise, start spewing... */ 
+        va_start(arg_ptr, format);   
+        vsnprintf(buf, sizeof(buf), format, arg_ptr);   
+        va_end(arg_ptr);   
+
+       for (i=0; !IsEmptyStr(&buf[i]); ++i) {
+               scr_putc(buf[i]);
+               if (buf[i]==10) {
+                       ++lines_printed;
+                       lines_printed = checkpagin(lines_printed,
+                               (userflags & US_PAGINATOR),
+                               screenheight);
+               }
+       }
+}   
+
+
+
+/*
+ * print_instant()  -  print instant messages if there are any
+ */
+void print_instant(void)
+{
+       char buf[1024];
+       FILE *outpipe;
+       time_t timestamp;
+       struct tm stamp;
+       int flags = 0;
+       char sender[64];
+       char node[64];
+       char *listing = NULL;
+       int r;                  /* IPC result code */
+
+       if (instant_msgs == 0)
+               return;
+
+       if (rc_exp_beep) {
+               ctdl_beep();
+       }
+       if (IsEmptyStr(rc_exp_cmd)) {
+               color(BRIGHT_RED);
+               scr_printf("\r---");
+       }
+       
+       while (instant_msgs != 0) {
+               r = CtdlIPCGetInstantMessage(ipc_for_signal_handlers, &listing, buf);
+               if (r / 100 != 1)
+                       return;
+       
+               instant_msgs = extract_int(buf, 0);
+               timestamp = extract_long(buf, 1);
+               flags = extract_int(buf, 2);
+               extract_token(sender, buf, 3, '|', sizeof sender);
+               extract_token(node, buf, 4, '|', sizeof node);
+               strcpy(last_paged, sender);
+       
+               localtime_r(&timestamp, &stamp);
+
+               /* If the page is a Logoff Request, honor it. */
+               if (flags & 2) {
+                       termn8 = 1;
+                       return;
+               }
+       
+               if (!IsEmptyStr(rc_exp_cmd)) {
+                       outpipe = popen(rc_exp_cmd, "w");
+                       if (outpipe != NULL) {
+                               /* Header derived from flags */
+                               if (flags & 2)
+                                       fprintf(outpipe,
+                                              "Please log off now, as requested ");
+                               else if (flags & 1)
+                                       fprintf(outpipe, "Broadcast message ");
+                               else if (flags & 4)
+                                       fprintf(outpipe, "Chat request ");
+                               else
+                                       fprintf(outpipe, "Message ");
+                               /* Timestamp.  Can this be improved? */
+                               if (stamp.tm_hour == 0 || stamp.tm_hour == 12)
+                                       fprintf(outpipe, "at 12:%02d%cm",
+                                               stamp.tm_min, 
+                                               stamp.tm_hour ? 'p' : 'a');
+                               else if (stamp.tm_hour > 12)            /* pm */
+                                       fprintf(outpipe, "at %d:%02dpm",
+                                               stamp.tm_hour - 12,
+                                               stamp.tm_min);
+                               else                                    /* am */
+                                       fprintf(outpipe, "at %d:%02dam",
+                                               stamp.tm_hour, stamp.tm_min);
+                               fprintf(outpipe, " from %s", sender);
+                               if (strncmp(ipc_for_signal_handlers->ServInfo.nodename, node, 32))
+                                       fprintf(outpipe, " @%s", node);
+                               fprintf(outpipe, ":\n%s\n", listing);
+                               pclose(outpipe);
+                               if (instant_msgs == 0)
+                                       return;
+                               continue;
+                       }
+               }
+               /* fall back to built-in instant message display */
+               scr_printf("\n");
+               lines_printed++;
+
+               /* Header derived from flags */
+               if (flags & 2)
+                       scr_printf("Please log off now, as requested ");
+               else if (flags & 1)
+                       scr_printf("Broadcast message ");
+               else if (flags & 4)
+                       scr_printf("Chat request ");
+               else
+                       scr_printf("Message ");
+       
+               /* Timestamp.  Can this be improved? */
+               if (stamp.tm_hour == 0 || stamp.tm_hour == 12)/* 12am/12pm */
+                       scr_printf("at 12:%02d%cm", stamp.tm_min, 
+                               stamp.tm_hour ? 'p' : 'a');
+               else if (stamp.tm_hour > 12)                    /* pm */
+                       scr_printf("at %d:%02dpm",
+                               stamp.tm_hour - 12, stamp.tm_min);
+               else                                            /* am */
+                       scr_printf("at %d:%02dam", stamp.tm_hour, stamp.tm_min);
+               
+               /* Sender */
+               scr_printf(" from %s", sender);
+       
+               /* Remote node, if any */
+               if (strncmp(ipc_for_signal_handlers->ServInfo.nodename, node, 32))
+                       scr_printf(" @%s", node);
+       
+               scr_printf(":\n");
+               lines_printed++;
+               fmout(screenwidth, NULL, listing, NULL, 1, screenheight, -1, 0);
+               free(listing);
+
+               /* when running in curses mode, the scroll bar in most
+                  xterm-style programs becomes useless, so it makes sense to
+                  pause after a screenful of pages if the user has been idle
+                  for a while. However, this is annoying to some of the users
+                  who aren't in curses mode and tend to leave their clients
+                  idle. keepalives become disabled, resulting in getting booted
+                  when coming back to the idle session. but they probably have
+                  a working scrollback in their terminal, so disable it in this
+                  case:
+                */
+               if (!is_curses_enabled())
+                       lines_printed = 0;
+       }
+       scr_printf("\n---\n");
+       color(BRIGHT_WHITE);
+
+
+}
+
+
+void set_keepalives(int s)
+{
+       keepalives_enabled = (char) s;
+}
+
+/* 
+ * This loop handles the "keepalive" messages sent to the server when idling.
+ */
+
+static time_t idlet = 0;
+static void really_do_keepalive(void) {
+       int r;                          /* IPC response code */
+
+       time(&idlet);
+
+       /* This may sometimes get called before we are actually connected
+        * to the server.  Don't do anything if we aren't connected. -IO
+        */
+       if (!ipc_for_signal_handlers)
+               return;
+
+       /* If full keepalives are enabled, send a NOOP to the server and
+        * wait for a response.
+        */
+       if (keepalives_enabled == KA_YES) {
+               r = CtdlIPCNoop(ipc_for_signal_handlers);
+               if (instant_msgs > 0) {
+                       if (ok_to_interrupt == 1) {
+                               scr_printf("\r%64s\r", "");
+                               print_instant();
+                               scr_printf("%s%c ", room_name,
+                                      room_prompt(room_flags));
+                               scr_flush();
+                       }
+               }
+       }
+
+       /* If half keepalives are enabled, send a QNOP to the server (if the
+        * server supports it) and then do nothing.
+        */
+       if ( (keepalives_enabled == KA_HALF)
+          && (ipc_for_signal_handlers->ServInfo.supports_qnop > 0) ) {
+               CtdlIPC_chat_send(ipc_for_signal_handlers, "QNOP");
+       }
+}
+
+/* threaded nonblocking keepalive stuff starts here. I'm going for a simple
+   encapsulated interface; in theory there should be no need to touch these
+   globals outside of the async_ka_* functions. */
+
+#ifdef THREADED_CLIENT
+static pthread_t ka_thr_handle;
+static int ka_thr_active = 0;
+static int async_ka_enabled = 0;
+
+static void *ka_thread(void *arg)
+{
+#ifdef HAVE_BACKTRACE
+       char threadName[256];
+
+       // Set up our name
+       sprintf(threadName, "ka_Thread n");
+
+       // Register for tracing
+       eCrash_RegisterThread(threadName, 0);
+#endif
+       really_do_keepalive();
+       pthread_detach(ka_thr_handle);
+       ka_thr_active = 0;
+       
+#ifdef HAVE_BACKTRACE
+       eCrash_UnregisterThread();
+#endif
+       return NULL;
+}
+
+/* start up a thread to handle a keepalive in the background */
+static void async_ka_exec(void)
+{
+       if (!ka_thr_active) {
+               ka_thr_active = 1;
+               if (pthread_create(&ka_thr_handle, NULL, ka_thread, NULL)) {
+                       perror("pthread_create");
+                       exit(1);
+               }
+       }
+}
+#endif /* THREADED_CLIENT */
+
+/* I changed this from static to not because I need to call it from
+   screen.c, either that or make something in screen.c not static.
+   Fix it how you like. Why all the staticness? stu */
+   
+void do_keepalive(void)
+{
+       time_t now;
+
+       time(&now);
+       if ((now - idlet) < ((long) S_KEEPALIVE))
+               return;
+
+       /* Do a space-backspace to keep telnet sessions from idling out */
+       scr_printf(" %c", 8);
+       scr_flush();
+
+#ifdef THREADED_CLIENT
+       if (async_ka_enabled)
+               async_ka_exec();
+       else
+#endif
+               really_do_keepalive();
+}
+
+
+/* Now the actual async-keepalve API that we expose to higher levels:
+   async_ka_start() and async_ka_end(). These do nothing when we don't have
+   threading enabled, so we avoid sprinkling ifdef's throughout the code. */
+
+/* wait for a background keepalive to complete. this must be done before
+   attempting any further server requests! */
+void async_ka_end(void)
+{
+#ifdef THREADED_CLIENT
+       if (ka_thr_active)
+               pthread_join(ka_thr_handle, NULL);
+
+       async_ka_enabled--;
+#endif
+}
+
+/* tell do_keepalive() that keepalives are asynchronous. */
+void async_ka_start(void)
+{
+#ifdef THREADED_CLIENT
+       async_ka_enabled++;
+#endif
+}
+
+
+int inkey(void)
+{                              /* get a character from the keyboard, with   */
+       int a;                  /* the watchdog timer in effect if necessary */
+       fd_set rfds;
+       struct timeval tv;
+       time_t start_time;
+
+       scr_flush();
+       lines_printed = 0;
+       time(&start_time);
+
+       do {
+               /* This loop waits for keyboard input.  If the keepalive
+                * timer expires, it sends a keepalive to the server if
+                * necessary and then waits again.
+                */
+               do {
+                       scr_set_windowsize(ipc_for_signal_handlers);
+                       do_keepalive();
+                       scr_set_windowsize(ipc_for_signal_handlers);
+
+                       FD_ZERO(&rfds);
+                       FD_SET(0, &rfds);
+                       tv.tv_sec = S_KEEPALIVE;
+                       tv.tv_usec = 0;
+
+                       select(1, &rfds, NULL, NULL, &tv);
+               } while (!FD_ISSET(0, &rfds));
+
+               /* At this point, there's input, so fetch it.
+                * (There's a hole in the bucket...)
+                */
+               a = scr_getc(SCR_BLOCK);
+               if (a == 127) {
+                       a = 8;
+               }
+               if (a == 13) {
+                       a = 10;
+               }
+/* not so fast there dude, we have to handle UTF-8 and ISO-8859-1...
+               if (a > 126) {
+                       a = 0;
+               }
+               if (((a != 23) && (a != 4) && (a != 10) && (a != 8) && (a != NEXT_KEY) && (a != STOP_KEY))
+                   && ((a < 32) || (a > 126))) {
+                       a = 0;
+               }
+ */
+
+#ifndef DISABLE_CURSES
+#if defined(HAVE_CURSES_H) || defined(HAVE_NCURSES_H)
+               if (a == ERR) {
+                       logoff(NULL, 3);
+               }
+#endif
+#endif
+
+       } while (a == 0);
+       return (a);
+}
+
+
+int yesno(void)
+{                              /* Returns 1 for yes, 0 for no */
+       int a;
+       while (1) {
+               a = inkey();
+               a = tolower(a);
+               if (a == 'y') {
+                       scr_printf("Yes\n");
+                       return (1);
+               }
+               if (a == 'n') {
+                       scr_printf("No\n");
+                       return (0);
+               }
+       }
+}
+
+/* Returns 1 for yes, 0 for no, arg is default value */
+int yesno_d(int d)
+{
+       int a;
+       while (1) {
+               a = inkey();
+               a = tolower(a);
+               if (a == 10)
+                       a = (d ? 'y' : 'n');
+               if (a == 'y') {
+                       scr_printf("Yes\n");
+                       return (1);
+               }
+               if (a == 'n') {
+                       scr_printf("No\n");
+                       return (0);
+               }
+       }
+}
+
+
+
+
+/* Gets a line from the terminal */
+/* string == Pointer to string buffer */
+/* lim == Maximum length - if negative, no-show */
+void ctdl_getline(char *string, int lim) 
+{
+       int a, b;
+       char flag = 0;
+
+       if (lim < 0) {
+               lim = (0 - lim);
+               flag = 1;
+       }
+       strcpy(string, "");
+       gl_string = string;
+       async_ka_start();
+
+GLA:   a = inkey();
+       /* a = (a & 127); ** commented out because it isn't just an ASCII world anymore */
+       if ((a == 8 || a == 23) && (IsEmptyStr(string)))
+               goto GLA;
+       if ((a != 10) && (a != 8) && (strlen(string) == lim))
+               goto GLA;
+       if ((a == 8) && (string[0] != 0)) {
+               string[strlen(string) - 1] = 0;
+               scr_putc(8); scr_putc(32); scr_putc(8);
+               goto GLA;
+       }
+       if ((a == 23) && (string[0] != 0)) {
+               do {
+                       string[strlen(string) - 1] = 0;
+                       scr_putc(8); scr_putc(32); scr_putc(8);
+               } while (!IsEmptyStr(string) && string[strlen(string) - 1] != ' ');
+               goto GLA;
+       }
+       if ((a == 10)) {
+               scr_putc(10);
+               async_ka_end();
+               return;
+       }
+       if (a < 32)
+               a = '.';
+       b = strlen(string);
+       string[b] = a;
+       string[b + 1] = 0;
+       if (flag == 0)
+               scr_putc(a);
+       if (flag == 1)
+               scr_putc('*');
+       goto GLA;
+}
+
+
+/*
+ * strprompt()  -  prompt for a string, print the existing value and
+ *                 allow the user to press return to keep it...
+ */
+void strprompt(char *prompt, char *str, int len)
+{
+       int i;
+       char buf[128];
+
+       print_instant();
+       color(DIM_WHITE);
+       scr_printf("%s ", prompt);
+       color(DIM_MAGENTA);
+       scr_printf("[");
+       color(BRIGHT_MAGENTA);
+
+       if (len >= 0) {
+               scr_printf("%s", str);
+       }
+       else {
+               for (i=0; !IsEmptyStr(&str[i]); ++i) {
+                       scr_printf("*");
+               }
+       }
+
+       color(DIM_MAGENTA);
+       scr_printf("]");
+       color(DIM_WHITE);
+       scr_printf(": ");
+       color(BRIGHT_CYAN);
+       ctdl_getline(buf, len);
+       if (buf[0] != 0)
+               strcpy(str, buf);
+       color(DIM_WHITE);
+}
+
+/*
+ * boolprompt()  -  prompt for a yes/no, print the existing value and
+ *                  allow the user to press return to keep it...
+ */
+int boolprompt(char *prompt, int prev_val)
+{
+       int r;
+
+       color(DIM_WHITE);
+       scr_printf("%s ", prompt);
+       color(DIM_MAGENTA);
+       scr_printf("[");
+       color(BRIGHT_MAGENTA);
+       scr_printf("%s", (prev_val ? "Yes" : "No"));
+       color(DIM_MAGENTA);
+       scr_printf("]: ");
+       color(BRIGHT_CYAN);
+       r = (yesno_d(prev_val));
+       color(DIM_WHITE);
+       return r;
+}
+
+/* 
+ * intprompt()  -  like strprompt(), except for an integer
+ *                 (note that it RETURNS the new value!)
+ */
+int intprompt(char *prompt, int ival, int imin, int imax)
+{
+       char buf[16];
+       int i;
+       int p;
+
+       do {
+               i = ival;
+               snprintf(buf, sizeof buf, "%d", i);
+               strprompt(prompt, buf, 15);
+               i = atoi(buf);
+               for (p=0; !IsEmptyStr(&buf[p]); ++p) {
+                       if ( (!isdigit(buf[p]))
+                          && ( (buf[p]!='-') || (p!=0) )  )
+                               i = imin - 1;
+               }
+               if (i < imin)
+                       scr_printf("*** Must be no less than %d.\n", imin);
+               if (i > imax)
+                       scr_printf("*** Must be no more than %d.\n", imax);
+       } while ((i < imin) || (i > imax));
+       return (i);
+}
+
+/* 
+ * newprompt()  -  prompt for a string with no existing value
+ *                 (clears out string buffer first)
+ */
+void newprompt(char *prompt, char *str, int len)
+{
+       color(BRIGHT_MAGENTA);
+       scr_printf("%s", prompt);
+       color(DIM_MAGENTA);
+       ctdl_getline(str, len);
+       color(DIM_WHITE);
+}
+
+
+int lkey(void)
+{                              /* returns a lower case value */
+       int a;
+       a = inkey();
+       if (isupper(a))
+               a = tolower(a);
+       return (a);
+}
+
+/*
+ * parse the citadel.rc file
+ */
+void load_command_set(void)
+{
+       FILE *ccfile;
+       char buf[1024];
+       char editor_key[100];
+       struct citcmd *cptr;
+       struct citcmd *lastcmd = NULL;
+       int a, d;
+       int b = 0;
+       int i;
+
+
+       /* first, set up some defaults for non-required variables */
+
+       for (i = 0; i < MAX_EDITORS; i++)
+               strcpy(editor_paths[i], "");
+       strcpy(printcmd, "");
+       strcpy(imagecmd, "");
+       strcpy(rc_username, "");
+       strcpy(rc_password, "");
+       rc_floor_mode = 0;
+       rc_exp_beep = 1;
+       rc_allow_attachments = 0;
+       rc_remember_passwords = 0;
+       strcpy(rc_exp_cmd, "");
+       rc_display_message_numbers = 0;
+       rc_force_mail_prompts = 0;
+       rc_ansi_color = 0;
+       rc_color_use_bg = 0;
+       strcpy(rc_url_cmd, "");
+       strcpy(rc_open_cmd, "");
+       strcpy(rc_gotmail_cmd, "");
+#ifdef HAVE_OPENSSL
+       rc_encrypt = RC_DEFAULT;
+#endif
+#if defined(HAVE_CURSES_H) && !defined(DISABLE_CURSES)
+       rc_screen = RC_DEFAULT;
+#endif
+       rc_alt_semantics = 0;
+
+       /* now try to open the citadel.rc file */
+
+       ccfile = NULL;
+       if (getenv("HOME") != NULL) {
+               snprintf(buf, sizeof buf, "%s/.citadelrc", getenv("HOME"));
+               ccfile = fopen(buf, "r");
+       }
+       if (ccfile == NULL) {
+               ccfile = fopen(file_citadel_rc, "r");
+       }
+       if (ccfile == NULL) {
+               ccfile = fopen("/etc/citadel.rc", "r");
+       }
+       if (ccfile == NULL) {
+               ccfile = fopen("./citadel.rc", "r");
+       }
+       if (ccfile == NULL) {
+               perror("commands: cannot open citadel.rc");
+               logoff(NULL, 3);
+       }
+       while (fgets(buf, sizeof buf, ccfile) != NULL) {
+               while ((!IsEmptyStr(buf)) ? (isspace(buf[strlen(buf) - 1])) : 0)
+                       buf[strlen(buf) - 1] = 0;
+
+               if (!strncasecmp(buf, "encrypt=", 8)) {
+                       if (!strcasecmp(&buf[8], "yes")) {
+#ifdef HAVE_OPENSSL
+                               rc_encrypt = RC_YES;
+#else
+                               fprintf(stderr, "citadel.rc requires encryption support but citadel is not compiled with OpenSSL");
+                               logoff(NULL, 3);
+#endif
+                       }
+#ifdef HAVE_OPENSSL
+                       else if (!strcasecmp(&buf[8], "no")) {
+                               rc_encrypt = RC_NO;
+                       }
+                       else if (!strcasecmp(&buf[8], "default")) {
+                               rc_encrypt = RC_DEFAULT;
+                       }
+#endif
+               }
+
+#if defined(HAVE_CURSES_H) && !defined(DISABLE_CURSES)
+               if (!strncasecmp(buf, "fullscreen=", 11)) {
+                       if (!strcasecmp(&buf[11], "yes"))
+                               rc_screen = RC_YES;
+                       else if (!strcasecmp(&buf[11], "no"))
+                               rc_screen = RC_NO;
+               }
+#endif
+
+               if (!strncasecmp(buf, "editor=", 7))
+                       strcpy(editor_paths[0], &buf[7]);
+
+               for (i = 0; i < MAX_EDITORS; i++)
+               {
+                       sprintf(editor_key, "editor%d=", i);
+                       if (!strncasecmp(buf, editor_key, strlen(editor_key)))
+                               strcpy(editor_paths[i], &buf[strlen(editor_key)]);
+               }
+
+               if (!strncasecmp(buf, "printcmd=", 9))
+                       strcpy(printcmd, &buf[9]);
+
+               if (!strncasecmp(buf, "imagecmd=", 9))
+                       strcpy(imagecmd, &buf[9]);
+
+               if (!strncasecmp(buf, "expcmd=", 7))
+                       strcpy(rc_exp_cmd, &buf[7]);
+
+               if (!strncasecmp(buf, "local_screen_dimensions=", 24))
+                       have_xterm = (char) atoi(&buf[24]);
+
+               if (!strncasecmp(buf, "use_floors=", 11)) {
+                       if (!strcasecmp(&buf[11], "yes"))
+                               rc_floor_mode = RC_YES;
+                       if (!strcasecmp(&buf[11], "no"))
+                               rc_floor_mode = RC_NO;
+                       if (!strcasecmp(&buf[11], "default"))
+                               rc_floor_mode = RC_DEFAULT;
+               }
+               if (!strncasecmp(buf, "beep=", 5)) {
+                       rc_exp_beep = atoi(&buf[5]);
+               }
+               if (!strncasecmp(buf, "allow_attachments=", 18)) {
+                       rc_allow_attachments = atoi(&buf[18]);
+               }
+               if (!strncasecmp(buf, "idle_threshold=", 15)) {
+                       rc_idle_threshold = atol(&buf[15]);
+               }
+               if (!strncasecmp(buf, "remember_passwords=", 19)) {
+                       rc_remember_passwords = atoi(&buf[19]);
+               }
+               if (!strncasecmp(buf, "display_message_numbers=", 24)) {
+                       rc_display_message_numbers = atoi(&buf[24]);
+               }
+               if (!strncasecmp(buf, "force_mail_prompts=", 19)) {
+                       rc_force_mail_prompts = atoi(&buf[19]);
+               }
+               if (!strncasecmp(buf, "ansi_color=", 11)) {
+                       if (!strncasecmp(&buf[11], "on", 2))
+                               rc_ansi_color = 1;
+                       if (!strncasecmp(&buf[11], "auto", 4))
+                               rc_ansi_color = 2;      /* autodetect */
+                       if (!strncasecmp(&buf[11], "user", 4))
+                               rc_ansi_color = 3;      /* user config */
+               }
+               if (!strncasecmp(buf, "use_background=", 15)) {
+                       if (!strncasecmp(&buf[15], "on", 2))
+                               rc_color_use_bg = 9;
+               }
+               if (!strncasecmp(buf, "prompt_control=", 15)) {
+                       if (!strncasecmp(&buf[15], "on", 2))
+                               rc_prompt_control = 1;
+                       if (!strncasecmp(&buf[15], "user", 4))
+                               rc_prompt_control = 3;  /* user config */
+               }
+               if (!strncasecmp(buf, "username=", 9))
+                       strcpy(rc_username, &buf[9]);
+
+               if (!strncasecmp(buf, "password=", 9))
+                       strcpy(rc_password, &buf[9]);
+
+               if (!strncasecmp(buf, "urlcmd=", 7))
+                       strcpy(rc_url_cmd, &buf[7]);
+
+               if (!strncasecmp(buf, "opencmd=", 7))
+                       strcpy(rc_open_cmd, &buf[8]);
+
+               if (!strncasecmp(buf, "gotmailcmd=", 11))
+                       strcpy(rc_gotmail_cmd, &buf[11]);
+
+               if (!strncasecmp(buf, "alternate_semantics=", 20)) {
+                       if (!strncasecmp(&buf[20], "yes", 3)) {
+                               rc_alt_semantics = 1;
+                       }
+                       else {
+                               rc_alt_semantics = 0;
+                       }
+               }
+
+               if (!strncasecmp(buf, "cmd=", 4)) {
+                       strcpy(buf, &buf[4]);
+
+                       cptr = (struct citcmd *) malloc(sizeof(struct citcmd));
+
+                       cptr->c_cmdnum = atoi(buf);
+                       for (d = strlen(buf); d >= 0; --d)
+                               if (buf[d] == ',')
+                                       b = d;
+                       strcpy(buf, &buf[b + 1]);
+
+                       cptr->c_axlevel = atoi(buf);
+                       for (d = strlen(buf); d >= 0; --d)
+                               if (buf[d] == ',')
+                                       b = d;
+                       strcpy(buf, &buf[b + 1]);
+
+                       for (a = 0; a < 5; ++a)
+                               cptr->c_keys[a][0] = 0;
+
+                       a = 0;
+                       b = 0;
+                       buf[strlen(buf) + 1] = 0;
+                       while (!IsEmptyStr(buf)) {
+                               b = strlen(buf);
+                               for (d = strlen(buf); d >= 0; --d)
+                                       if (buf[d] == ',')
+                                               b = d;
+                               strncpy(cptr->c_keys[a], buf, b);
+                               cptr->c_keys[a][b] = 0;
+                               if (buf[b] == ',')
+                                       strcpy(buf, &buf[b + 1]);
+                               else
+                                       strcpy(buf, "");
+                               ++a;
+                       }
+
+                       cptr->next = NULL;
+                       if (cmdlist == NULL)
+                               cmdlist = cptr;
+                       else
+                               lastcmd->next = cptr;
+                       lastcmd = cptr;
+               }
+       }
+       fclose(ccfile);
+}
+
+
+
+/*
+ * return the key associated with a command
+ */
+char keycmd(char *cmdstr)
+{
+       int a;
+
+       for (a = 0; !IsEmptyStr(&cmdstr[a]); ++a)
+               if (cmdstr[a] == '&')
+                       return (tolower(cmdstr[a + 1]));
+       return (0);
+}
+
+
+/*
+ * Output the string from a key command without the ampersand
+ * "mode" should be set to 0 for normal or 1 for <C>ommand key highlighting
+ */
+char *cmd_expand(char *strbuf, int mode)
+{
+       int a;
+       static char exp[64];
+       char buf[1024];
+
+       strcpy(exp, strbuf);
+
+       for (a = 0; exp[a]; ++a) {
+               if (strbuf[a] == '&') {
+
+                       /* dont echo these non mnemonic command keys */
+                       int noecho = strbuf[a+1] == '<' || strbuf[a+1] == '>' || strbuf[a+1] == '+' || strbuf[a+1] == '-';
+
+                       if (mode == 0) {
+                               strcpy(&exp[a], &exp[a + 1 + noecho]);
+                       }
+                       if (mode == 1) {
+                               exp[a] = '<';
+                               strcpy(buf, &exp[a + 2]);
+                               exp[a + 2] = '>';
+                               exp[a + 3] = 0;
+                               strcat(exp, buf);
+                       }
+               }
+               if (!strncmp(&exp[a], "^r", 2)) {
+                       strcpy(buf, exp);
+                       strcpy(&exp[a], room_name);
+                       strcat(exp, &buf[a + 2]);
+               }
+               if (!strncmp(&exp[a], "^c", 2)) {
+                       exp[a] = ',';
+                       strcpy(&exp[a + 1], &exp[a + 2]);
+               }
+       }
+
+       return (exp);
+}
+
+
+
+/*
+ * Comparison function to determine if entered commands match a
+ * command loaded from the config file.
+ */
+int cmdmatch(char *cmdbuf, struct citcmd *cptr, int ncomp)
+{
+       int a;
+       int cmdax;
+
+       cmdax = 0;
+       if (is_room_aide)
+               cmdax = 1;
+       if (axlevel >= 6)
+               cmdax = 2;
+
+       for (a = 0; a < ncomp; ++a) {
+               if ((tolower(cmdbuf[a]) != keycmd(cptr->c_keys[a]))
+                   || (cptr->c_axlevel > cmdax))
+                       return (0);
+       }
+       return (1);
+}
+
+
+/*
+ * This function returns 1 if a given command requires a string input
+ */
+int requires_string(struct citcmd *cptr, int ncomp)
+{
+       int a;
+       char buf[64];
+
+       strcpy(buf, cptr->c_keys[ncomp - 1]);
+       for (a = 0; !IsEmptyStr(&buf[a]); ++a) {
+               if (buf[a] == ':')
+                       return (1);
+       }
+       return (0);
+}
+
+
+/*
+ * Input a command at the main prompt.
+ * This function returns an integer command number.  If the command prompts
+ * for a string then it is placed in the supplied buffer.
+ */
+int getcmd(CtdlIPC *ipc, char *argbuf)
+{
+       char cmdbuf[5];
+       int cmdspaces[5];
+       int cmdpos;
+       int ch;
+       int a;
+       int got;
+       int this_lazy_cmd;
+       struct citcmd *cptr;
+
+       /*
+        * Starting a new command now, so set sigcaught to 0.  This variable
+        * is set to nonzero (usually NEXT_KEY or STOP_KEY) if a command has
+        * been interrupted by a keypress.
+        */
+       sigcaught = 0;
+
+       /* Switch color support on or off if we're in user mode */
+       if (rc_ansi_color == 3) {
+               if (userflags & US_COLOR)
+                       enable_color = 1;
+               else
+                       enable_color = 0;
+       }
+       /* if we're running in idiot mode, display a cute little menu */
+       IFNEXPERT formout(ipc, "mainmenu");
+
+       print_instant();
+       strcpy(argbuf, "");
+       cmdpos = 0;
+       for (a = 0; a < 5; ++a)
+               cmdbuf[a] = 0;
+       /* now the room prompt... */
+       ok_to_interrupt = 1;
+       color(BRIGHT_WHITE);
+       scr_printf("\n%s", room_name);
+       color(DIM_WHITE);
+       scr_printf("%c ", room_prompt(room_flags));
+       scr_flush();
+
+       while (1) {
+               ch = inkey();
+               ok_to_interrupt = 0;
+
+               /* Handle the backspace key, but only if there's something
+                * to backspace over...
+                */
+               if ((ch == 8) && (cmdpos > 0)) {
+                       back(cmdspaces[cmdpos - 1] + 1);
+                       cmdbuf[cmdpos] = 0;
+                       --cmdpos;
+               }
+               /* Spacebar invokes "lazy traversal" commands */
+               if ((ch == 32) && (cmdpos == 0)) {
+                       this_lazy_cmd = next_lazy_cmd;
+                       if (this_lazy_cmd == 13)
+                               next_lazy_cmd = 5;
+                       if (this_lazy_cmd == 5)
+                               next_lazy_cmd = 13;
+                       for (cptr = cmdlist; cptr != NULL; cptr = cptr->next) {
+                               if (cptr->c_cmdnum == this_lazy_cmd) {
+                                       for (a = 0; a < 5; ++a)
+                                               if (cptr->c_keys[a][0] != 0)
+                                                       scr_printf("%s ", cmd_expand(
+                                                                                       cptr->c_keys[a], 0));
+                                       scr_printf("\n");
+                                       return (this_lazy_cmd);
+                               }
+                       }
+                       scr_printf("\n");
+                       return (this_lazy_cmd);
+               }
+               /* Otherwise, process the command */
+               cmdbuf[cmdpos] = tolower(ch);
+
+               for (cptr = cmdlist; cptr != NULL; cptr = cptr->next) {
+                       if (cmdmatch(cmdbuf, cptr, cmdpos + 1)) {
+
+                               scr_printf("%s", cmd_expand(cptr->c_keys[cmdpos], 0));
+                               cmdspaces[cmdpos] = strlen(
+                                   cmd_expand(cptr->c_keys[cmdpos], 0));
+                               if (cmdpos < 4)
+                                       if ((cptr->c_keys[cmdpos + 1]) != 0)
+                                               scr_putc(' ');
+                               ++cmdpos;
+                       }
+               }
+
+               for (cptr = cmdlist; cptr != NULL; cptr = cptr->next) {
+                       if (cmdmatch(cmdbuf, cptr, 5)) {
+                               /* We've found our command. */
+                               if (requires_string(cptr, cmdpos)) {
+                                       ctdl_getline(argbuf, 64);
+                               } else {
+                                       scr_printf("\n");
+                               }
+
+                               /* If this command is one that changes rooms,
+                                * then the next lazy-command (space bar)
+                                * should be "read new" instead of "goto"
+                                */
+                               if ((cptr->c_cmdnum == 5)
+                                   || (cptr->c_cmdnum == 6)
+                                   || (cptr->c_cmdnum == 47)
+                                   || (cptr->c_cmdnum == 52)
+                                   || (cptr->c_cmdnum == 16)
+                                   || (cptr->c_cmdnum == 20))
+                                       next_lazy_cmd = 13;
+
+                               /* If this command is "read new"
+                                * then the next lazy-command (space bar)
+                                * should be "goto"
+                                */
+                               if (cptr->c_cmdnum == 13)
+                                       next_lazy_cmd = 5;
+
+                               return (cptr->c_cmdnum);
+
+                       }
+               }
+
+               if (ch == '?') {
+                       pprintf("\rOne of ...                         \n");
+                       for (cptr = cmdlist; cptr != NULL; cptr = cptr->next) {
+                               if (cmdmatch(cmdbuf, cptr, cmdpos)) {
+                                       for (a = 0; a < 5; ++a) {
+                                          keyopt(cmd_expand(cptr->c_keys[a], 1));
+                                  pprintf(" ");
+                                       }
+                                       pprintf("\n");
+                               }
+                       }
+               sigcaught = 0;
+
+                       pprintf("\n%s%c ", room_name, room_prompt(room_flags));
+                       got = 0;
+                       for (cptr = cmdlist; cptr != NULL; cptr = cptr->next) {
+                               if ((got == 0) && (cmdmatch(cmdbuf, cptr, cmdpos))) {
+                                       for (a = 0; a < cmdpos; ++a) {
+                                               pprintf("%s ",
+                                                      cmd_expand(cptr->c_keys[a], 0));
+                                       }
+                                       got = 1;
+                               }
+                       }
+               }
+       }
+
+}
+
+
+
+
+
+/*
+ * set tty modes.  commands are:
+ * 
+ * 01- set to Citadel mode
+ * 2 - save current settings for later restoral
+ * 3 - restore saved settings
+ */
+#ifdef HAVE_TERMIOS_H
+void stty_ctdl(int cmd)
+{                              /* SysV version of stty_ctdl() */
+       struct termios live;
+       static struct termios saved_settings;
+       static int last_cmd = 0;
+
+       if (cmd == SB_LAST)
+               cmd = last_cmd;
+       else
+               last_cmd = cmd;
+
+       if ((cmd == 0) || (cmd == 1)) {
+               tcgetattr(0, &live);
+               live.c_iflag = ISTRIP | IXON | IXANY;
+               live.c_oflag = OPOST | ONLCR;
+               live.c_lflag = ISIG | NOFLSH;
+
+               live.c_cc[VINTR] = 0;
+               live.c_cc[VQUIT] = 0;
+
+#ifdef hpux
+               live.c_cc[VMIN] = 0;
+               live.c_cc[VTIME] = 0;
+#endif
+
+               /* do we even need this stuff anymore? */
+               /* live.c_line=0; */
+               live.c_cc[VERASE] = 8;
+               live.c_cc[VKILL] = 24;
+               live.c_cc[VEOF] = 1;
+               live.c_cc[VEOL] = 255;
+               live.c_cc[VEOL2] = 0;
+               live.c_cc[VSTART] = 0;
+               tcsetattr(0, TCSADRAIN, &live);
+       }
+       if (cmd == 2) {
+               tcgetattr(0, &saved_settings);
+       }
+       if (cmd == 3) {
+               tcsetattr(0, TCSADRAIN, &saved_settings);
+       }
+
+}
+#else
+void stty_ctdl(int cmd)
+{                              /* BSD version of stty_ctdl() */
+       struct sgttyb live;
+       static struct sgttyb saved_settings;
+       static int last_cmd = 0;
+
+       if (cmd == SB_LAST)
+               cmd = last_cmd;
+       else
+               last_cmd = cmd;
+
+       if ((cmd == 0) || (cmd == 1)) {
+               gtty(0, &live);
+               live.sg_flags |= CBREAK;
+               live.sg_flags |= CRMOD;
+               live.sg_flags |= NL1;
+               live.sg_flags &= ~ECHO;
+               if (cmd == 1)
+                       live.sg_flags |= NOFLSH;
+               stty(0, &live);
+       }
+       if (cmd == 2) {
+               gtty(0, &saved_settings);
+       }
+       if (cmd == 3) {
+               stty(0, &saved_settings);
+       }
+}
+#endif
+
+
+/*
+ * display_help()  -  help file viewer
+ */
+void display_help(CtdlIPC *ipc, char *name)
+{
+       formout(ipc, name);
+}
+
+
+/*
+ * fmout() - Citadel text formatter and paginator
+ */
+int fmout(
+       int width,      /* screen width to use */
+       FILE *fpin,     /* file to read from, or NULL to format given text */
+       char *text,     /* text to be formatted (when fpin is NULL */
+       FILE *fpout,    /* file to write to, or NULL to write to screen */
+       char pagin,     /* nonzero if we should use the paginator */
+       int height,     /* screen height to use */
+       int starting_lp,/* starting value for lines_printed, -1 for global */
+       int subst)      /* nonzero if we should use hypertext mode */
+{
+       char *buffer = NULL;    /* The current message */
+       char *word = NULL;      /* What we are about to actually print */
+       char *e;                /* Pointer to position in text */
+       char old = 0;           /* The previous character */
+       int column = 0;         /* Current column */
+       size_t i;               /* Generic counter */
+
+       /* Space for a single word, which can be at most screenwidth */
+       word = (char *)calloc(1, width);
+       if (!word) {
+               err_printf("Can't alloc memory to print message: %s!\n",
+                               strerror(errno));
+               logoff(NULL, 3);
+       }
+
+       /* Read the entire message body into memory */
+       if (fpin) {
+               buffer = load_message_from_file(fpin);
+               if (!buffer) {
+                       err_printf("Can't print message: %s!\n",
+                                       strerror(errno));
+                       logoff(NULL, 3);
+               }
+       } else {
+               buffer = text;
+       }
+       e = buffer;
+
+       if (starting_lp >= 0)
+               lines_printed = starting_lp;
+
+       /* Run the message body */
+       while (*e) {
+               /* Catch characters that shouldn't be there at all */
+               if (*e == '\r') {
+                       e++;
+                       continue;
+               }
+               /* First, are we looking at a newline? */
+               if (*e == '\n') {
+                       e++;
+                       if (*e == ' ') {        /* Paragraph */
+                               if (fpout) {
+                                       fprintf(fpout, "\n");
+                               } else {
+                                       scr_printf("\n");
+                                       ++lines_printed;
+                                       lines_printed = checkpagin(lines_printed, pagin, height);
+                               }
+                               column = 0;
+                       } else if (old != ' ') {/* Don't print two spaces */
+                               if (fpout) {
+                                       fprintf(fpout, " ");
+                               } else {
+                                       scr_printf(" ");
+                               }
+                               column++;
+                       }
+                       old = '\n';
+                       continue;
+               }
+
+               /* Are we looking at a nonprintable?
+                * (This section is now commented out because we could be displaying
+                * a character set like UTF-8 or ISO-8859-1.)
+               if ( (*e < 32) || (*e > 126) ) {
+                       e++;
+                       continue;
+               } */
+
+               /* Or are we looking at a space? */
+               if (*e == ' ') {
+                       e++;
+                       if (column >= width - 1) {
+                               /* Are we in the rightmost column? */
+                               if (fpout) {
+                                       fprintf(fpout, "\n");
+                               } else {
+                                       scr_printf("\n");
+                                       ++lines_printed;
+                                       lines_printed = checkpagin(lines_printed, pagin, height);
+                               }
+                               column = 0;
+                       } else if (!(column == 0 && old == ' ')) {
+                               /* Eat only the first space on a line */
+                               if (fpout) {
+                                       fprintf(fpout, " ");
+                               } else {
+                                       scr_printf(" ");
+                               }
+                               column++;
+                       }
+                       /* ONLY eat the FIRST space on a line */
+                       old = ' ';
+                       continue;
+               }
+               old = *e;
+
+               /* Read a word, slightly messy */
+               i = 0;
+               while (e[i]) {
+                       if (!isprint(e[i]) && !isspace(e[i]))
+                               e[i] = ' ';
+                       if (isspace(e[i]))
+                               break;
+                       i++;
+               }
+
+               /* We should never see these, but... slightly messy */
+               if (e[i] == '\t' || e[i] == '\f' || e[i] == '\v')
+                       e[i] = ' ';
+
+               /* Break up really long words */
+               /* TODO: auto-hyphenation someday? */
+               if (i >= width) 
+                       i = width - 1;
+               strncpy(word, e, i);
+               word[i] = 0;
+
+               /* Decide where to print the word */
+               if (column + i >= width) {
+                       /* Wrap to the next line */
+                       if (fpout) {
+                               fprintf(fpout, "\n");
+                       } else {
+                               scr_printf("\n");
+                               ++lines_printed;
+                               lines_printed = checkpagin(lines_printed, pagin, height);
+                       }
+                       column = 0;
+               }
+
+               /* Print the word */
+               if (fpout) {
+                       fprintf(fpout, "%s", word);
+               } else {
+                       scr_printf("%s", word);
+               }
+               column += i;
+               e += i;         /* Start over with the whitepsace! */
+       }
+
+       free(word);
+       if (fpin)               /* We allocated this, remember? */
+               free(buffer);
+
+       /* Is this necessary?  It makes the output kind of spacey. */
+       if (fpout) {
+               fprintf(fpout, "\n");
+       } else {
+               scr_printf("\n");
+               ++lines_printed;
+               lines_printed = checkpagin(lines_printed, pagin, height);
+       }
+
+       return sigcaught;
+}
+
+
+/*
+ * support ANSI color if defined
+ */
+void color(int colornum)
+{
+       static int hold_color;
+       static int current_color;
+
+       if (colornum == COLOR_PUSH) {
+               hold_color = current_color;
+               return;
+       }
+
+       if (colornum == COLOR_POP) {
+               color(hold_color);
+               return;
+       }
+
+       current_color = colornum;
+       if (enable_color) {
+#if defined(HAVE_CURSES_H) && !defined(DISABLE_CURSES)
+               if (scr_color(colornum))
+                       return;
+#endif
+               /* When switching to dim white, actually output an 'original
+                * pair' sequence -- this looks better on black-on-white
+                * terminals. - Changed to ORIGINAL_PAIR as this actually
+                * wound up looking horrible on black-on-white terminals, not
+                * to mention transparent terminals.
+                */
+               if (colornum == ORIGINAL_PAIR)
+                       printf("\033[0;39;49m");
+               else
+                       printf("\033[%d;3%d;4%dm", 
+                                       (colornum & 8) ? 1 : 0,
+                                       (colornum & 7),
+                                       rc_color_use_bg);
+
+               scr_flush();
+       }
+}
+
+void cls(int colornum)
+{
+       if (enable_color) {
+               printf("\033[4%dm\033[2J\033[H\033[0m",
+                               colornum ? colornum : rc_color_use_bg);
+               scr_flush();
+       }
+}
+
+
+/*
+ * Detect whether ANSI color is available (answerback)
+ */
+void send_ansi_detect(void)
+{
+       if (rc_ansi_color == 2) {
+               printf("\033[c");
+               scr_flush();
+               time(&AnsiDetect);
+       }
+}
+
+void look_for_ansi(void)
+{
+       fd_set rfds;
+       struct timeval tv;
+       char abuf[512];
+       time_t now;
+       int a, rv;
+
+       if (rc_ansi_color == 0) {
+               enable_color = 0;
+       } else if (rc_ansi_color == 1) {
+               enable_color = 1;
+       } else if (rc_ansi_color == 2) {
+
+               /* otherwise, do the auto-detect */
+
+               strcpy(abuf, "");
+
+               time(&now);
+               if ((now - AnsiDetect) < 2)
+                       sleep(1);
+
+               do {
+                       FD_ZERO(&rfds);
+                       FD_SET(0, &rfds);
+                       tv.tv_sec = 0;
+                       tv.tv_usec = 1;
+
+                       select(1, &rfds, NULL, NULL, &tv);
+                       if (FD_ISSET(0, &rfds)) {
+                               abuf[strlen(abuf) + 1] = 0;
+                               rv = read(0, &abuf[strlen(abuf)], 1);
+                       }
+               } while (FD_ISSET(0, &rfds));
+
+               for (a = 0; !IsEmptyStr(&abuf[a]); ++a) {
+                       if ((abuf[a] == 27) && (abuf[a + 1] == '[')
+                           && (abuf[a + 2] == '?')) {
+                               enable_color = 1;
+                       }
+               }
+       }
+}
+
+
+/*
+ * Display key options (highlight hotkeys inside angle brackets)
+ */
+void keyopt(char *buf) {
+       int i;
+
+       color(DIM_WHITE);
+       for (i=0; !IsEmptyStr(&buf[i]); ++i) {
+               if (buf[i]=='<') {
+                       pprintf("%c", buf[i]);
+                       color(BRIGHT_MAGENTA);
+               } else {
+                       if (buf[i]=='>'&& buf[i+1] != '>') {
+                               color(DIM_WHITE);
+                       }
+                       pprintf("%c", buf[i]);
+               }
+       }
+       color(DIM_WHITE);
+}
+
+
+
+/*
+ * Present a key-menu line choice type of thing
+ */
+char keymenu(char *menuprompt, char *menustring) {
+       int i, c, a;
+       int choices;
+       int do_prompt = 0;
+       char buf[1024];
+       int ch;
+       int display_prompt = 1;
+
+       choices = num_tokens(menustring, '|');
+
+       if (menuprompt != NULL) do_prompt = 1;
+       if ((menuprompt != NULL) && (IsEmptyStr(menuprompt))) do_prompt = 0;
+
+       while (1) {
+               if (display_prompt) {
+                       if (do_prompt) {
+                               scr_printf("%s ", menuprompt);
+                       } 
+                       else {
+                               for (i=0; i<choices; ++i) {
+                                       extract_token(buf, menustring, i, '|', sizeof buf);
+                                       keyopt(buf);
+                                       scr_printf(" ");
+                               }
+                       }
+                       scr_printf("-> ");
+                       display_prompt = 0;
+               }
+               ch = lkey();
+       
+               if ( (do_prompt) && (ch=='?') ) {
+                       scr_printf("\rOne of...                               ");
+                       scr_printf("                                      \n");
+                       for (i=0; i<choices; ++i) {
+                               extract_token(buf, menustring, i, '|', sizeof buf);
+                               scr_printf("   ");
+                               keyopt(buf);
+                               scr_printf("\n");
+                       }
+                       scr_printf("\n");
+                       display_prompt = 1;
+               }
+
+               for (i=0; i<choices; ++i) {
+                       extract_token(buf, menustring, i, '|', sizeof buf);
+                       for (c=1; !IsEmptyStr(&buf[c]); ++c) {
+                               if ( (ch == tolower(buf[c]))
+                                  && (buf[c-1]=='<')
+                                  && (buf[c+1]=='>') ) {
+                                       for (a=0; !IsEmptyStr(&buf[a]); ++a) {
+                                               if ( (a!=(c-1)) && (a!=(c+1))) {
+                                                       scr_putc(buf[a]);
+                                               }
+                                       }
+                                       scr_printf("\n");
+                                       return ch;
+                               }
+                       }
+               }
+       }
+}
diff --git a/citadel/textclient/messages.c b/citadel/textclient/messages.c
new file mode 100644 (file)
index 0000000..fdd8d46
--- /dev/null
@@ -0,0 +1,1989 @@
+/*
+ * $Id$
+ *
+ * Citadel message support routines
+ * see COPYING for copyright information
+ */
+
+#include "sysdep.h"
+#include <stdlib.h>
+#include <unistd.h>
+#include <sys/types.h>
+#include <fcntl.h>
+#include <stdio.h>
+#include <ctype.h>
+#include <string.h>
+#include <signal.h>
+#include <errno.h>
+#include <limits.h>
+#include <sys/wait.h>
+#include <sys/stat.h>
+
+#if TIME_WITH_SYS_TIME
+# include <sys/time.h>
+# include <time.h>
+#else
+# if HAVE_SYS_TIME_H
+#  include <sys/time.h>
+# else
+#  include <time.h>
+# endif
+#endif
+
+#ifdef HAVE_PTHREAD_H
+#include <pthread.h>
+#endif
+
+#include <stdarg.h>
+#include <libcitadel.h>
+#include "citadel.h"
+#include "citadel_ipc.h"
+#include "citadel_decls.h"
+#include "messages.h"
+#include "commands.h"
+#include "rooms.h"
+#ifndef HAVE_SNPRINTF
+#include "snprintf.h"
+#endif
+#include "screen.h"
+
+#define MAXWORDBUF SIZ
+#define NO_REPLY_TO    "nobody ... xxxxxx"
+
+char reply_to[SIZ];
+char reply_subject[SIZ];
+char reply_references[SIZ];
+char reply_inreplyto[SIZ];
+
+struct cittext {
+       struct cittext *next;
+       char text[MAXWORDBUF];
+};
+
+void stty_ctdl(int cmd);
+int haschar(const char *st, int ch);
+void ctdl_getline(char *string, int lim);
+int file_checksum(char *filename);
+void progress(CtdlIPC* ipc, unsigned long curr, unsigned long cmax);
+
+unsigned long *msg_arr = NULL;
+int msg_arr_size = 0;
+int num_msgs;
+char rc_alt_semantics;
+extern char room_name[];
+extern char tempdir[];
+extern unsigned room_flags;
+extern unsigned room_flags2;
+extern long highest_msg_read;
+extern char temp[];
+extern char temp2[];
+extern int screenwidth;
+extern int screenheight;
+extern long maxmsgnum;
+extern char is_mail;
+extern char is_aide;
+extern char is_room_aide;
+extern char fullname[];
+extern char axlevel;
+extern unsigned userflags;
+extern char sigcaught;
+extern char printcmd[];
+extern int rc_allow_attachments;
+extern int rc_display_message_numbers;
+extern int rc_force_mail_prompts;
+extern int editor_pid;
+extern CtdlIPC *ipc_for_signal_handlers;       /* KLUDGE cover your eyes */
+int num_urls = 0;
+char urls[MAXURLS][SIZ];
+char imagecmd[SIZ];
+int has_images = 0;                            /* Current msg has images */
+struct parts *last_message_parts = NULL;       /* Parts from last msg */
+
+
+
+void ka_sigcatch(int signum)
+{
+       alarm(S_KEEPALIVE);
+       signal(SIGALRM, ka_sigcatch);
+       CtdlIPCNoop(ipc_for_signal_handlers);
+}
+
+
+/*
+ * server keep-alive version of wait() (needed for external editor)
+ */
+pid_t ka_wait(int *kstatus)
+{
+       pid_t p;
+
+       alarm(S_KEEPALIVE);
+       signal(SIGALRM, ka_sigcatch);
+       do {
+               errno = 0;
+               p = wait(kstatus);
+       } while (errno == EINTR);
+       signal(SIGALRM, SIG_IGN);
+       alarm(0);
+       return (p);
+}
+
+
+/*
+ * version of system() that uses ka_wait()
+ */
+int ka_system(char *shc)
+{
+       pid_t childpid;
+       pid_t waitpid;
+       int retcode;
+
+       childpid = fork();
+       if (childpid < 0) {
+               color(BRIGHT_RED);
+               perror("Cannot fork");
+               color(DIM_WHITE);
+               return ((pid_t) childpid);
+       }
+
+       if (childpid == 0) {
+               execlp("/bin/sh", "sh", "-c", shc, NULL);
+               exit(127);
+       }
+
+       if (childpid > 0) {
+               do {
+                       waitpid = ka_wait(&retcode);
+               } while (waitpid != childpid);
+               return (retcode);
+       }
+
+       return (-1);
+}
+
+
+
+/*
+ * add a newline to the buffer...
+ */
+void add_newline(struct cittext *textlist)
+{
+       struct cittext *ptr;
+
+       ptr = textlist;
+       while (ptr->next != NULL)
+               ptr = ptr->next;
+
+       while (ptr->text[strlen(ptr->text) - 1] == 32)
+               ptr->text[strlen(ptr->text) - 1] = 0;
+       /* strcat(ptr->text,"\n"); */
+
+       ptr->next = (struct cittext *)
+           malloc(sizeof(struct cittext));
+       ptr = ptr->next;
+       ptr->next = NULL;
+       strcpy(ptr->text, "");
+}
+
+
+/*
+ * add a word to the buffer...
+ */
+void add_word(struct cittext *textlist, char *wordbuf)
+{
+       struct cittext *ptr;
+
+       ptr = textlist;
+       while (ptr->next != NULL)
+               ptr = ptr->next;
+
+       if (3 + strlen(ptr->text) + strlen(wordbuf) > screenwidth) {
+               ptr->next = (struct cittext *)
+                   malloc(sizeof(struct cittext));
+               ptr = ptr->next;
+               ptr->next = NULL;
+               strcpy(ptr->text, "");
+       }
+
+       strcat(ptr->text, wordbuf);
+       strcat(ptr->text, " ");
+}
+
+
+/*
+ * begin editing of an opened file pointed to by fp
+ */
+void citedit(CtdlIPC *ipc, FILE * fp)
+{
+       int a, prev, finished, b, last_space;
+       int appending = 0;
+       struct cittext *textlist = NULL;
+       struct cittext *ptr;
+       char wordbuf[MAXWORDBUF];
+       int rv = 0;
+
+       /* first, load the text into the buffer */
+       fseek(fp, 0L, 0);
+       textlist = (struct cittext *) malloc(sizeof(struct cittext));
+       textlist->next = NULL;
+       strcpy(textlist->text, "");
+
+       strcpy(wordbuf, "");
+       prev = (-1);
+       while (a = getc(fp), a >= 0) {
+               appending = 1;
+               if ((a == 32) || (a == 9) || (a == 13) || (a == 10)) {
+                       add_word(textlist, wordbuf);
+                       strcpy(wordbuf, "");
+                       if ((prev == 13) || (prev == 10)) {
+                               add_word(textlist, "\n");
+                               add_newline(textlist);
+                               add_word(textlist, "");
+                       }
+               } else {
+                       wordbuf[strlen(wordbuf) + 1] = 0;
+                       wordbuf[strlen(wordbuf)] = a;
+               }
+               if (strlen(wordbuf) + 3 > screenwidth) {
+                       add_word(textlist, wordbuf);
+                       strcpy(wordbuf, "");
+               }
+               prev = a;
+       }
+
+       /* get text */
+       finished = 0;
+       prev = (appending ? 13 : (-1));
+       strcpy(wordbuf, "");
+       async_ka_start();
+       do {
+               a = inkey();
+               if (a == 10)
+                       a = 13;
+               if (a == 9)
+                       a = 32;
+               if (a == 127)
+                       a = 8;
+
+               if ((a != 32) && (prev == 13)) {
+                       add_word(textlist, "\n");
+                       scr_printf(" ");
+               }
+
+               if ((a == 32) && (prev == 13)) {
+                       add_word(textlist, "\n");
+                       add_newline(textlist);
+               }
+
+               if (a == 8) {
+                       if (!IsEmptyStr(wordbuf)) {
+                               wordbuf[strlen(wordbuf) - 1] = 0;
+                               scr_putc(8);
+                               scr_putc(32);
+                               scr_putc(8);
+                       }
+               } else if (a == 23) {
+                       do {
+                               wordbuf[strlen(wordbuf) - 1] = 0;
+                               scr_putc(8);
+                               scr_putc(32);
+                               scr_putc(8);
+                       } while (!IsEmptyStr(wordbuf) && wordbuf[strlen(wordbuf) - 1] != ' ');
+               } else if (a == 13) {
+                       scr_printf("\n");
+                       if (IsEmptyStr(wordbuf))
+                               finished = 1;
+                       else {
+                               for (b = 0; b < strlen(wordbuf); ++b)
+                                       if (wordbuf[b] == 32) {
+                                               wordbuf[b] = 0;
+                                               add_word(textlist,
+                                                        wordbuf);
+                                               strcpy(wordbuf,
+                                                      &wordbuf[b + 1]);
+                                               b = 0;
+                                       }
+                               add_word(textlist, wordbuf);
+                               strcpy(wordbuf, "");
+                       }
+               } else {
+                       scr_putc(a);
+                       wordbuf[strlen(wordbuf) + 1] = 0;
+                       wordbuf[strlen(wordbuf)] = a;
+               }
+               if ((strlen(wordbuf) + 3) > screenwidth) {
+                       last_space = (-1);
+                       for (b = 0; b < strlen(wordbuf); ++b)
+                               if (wordbuf[b] == 32)
+                                       last_space = b;
+                       if (last_space >= 0) {
+                               for (b = 0; b < strlen(wordbuf); ++b)
+                                       if (wordbuf[b] == 32) {
+                                               wordbuf[b] = 0;
+                                               add_word(textlist,
+                                                        wordbuf);
+                                               strcpy(wordbuf,
+                                                      &wordbuf[b + 1]);
+                                               b = 0;
+                                       }
+                               for (b = 0; b < strlen(wordbuf); ++b) {
+                                       scr_putc(8);
+                                       scr_putc(32);
+                                       scr_putc(8);
+                               }
+                               scr_printf("\n%s", wordbuf);
+                       } else {
+                               add_word(textlist, wordbuf);
+                               strcpy(wordbuf, "");
+                               scr_printf("\n");
+                       }
+               }
+               prev = a;
+       } while (finished == 0);
+       async_ka_end();
+
+       /* write the buffer back to disk */
+       fseek(fp, 0L, 0);
+       for (ptr = textlist; ptr != NULL; ptr = ptr->next) {
+               fprintf(fp, "%s", ptr->text);
+       }
+       putc(10, fp);
+       fflush(fp);
+       rv = ftruncate(fileno(fp), ftell(fp));
+
+       /* and deallocate the memory we used */
+       while (textlist != NULL) {
+               ptr = textlist->next;
+               free(textlist);
+               textlist = ptr;
+       }
+}
+
+
+/*
+ * Free the struct parts
+ */
+void free_parts(struct parts *p)
+{
+       struct parts *a_part = p;
+
+       while (a_part) {
+               struct parts *q;
+
+               q = a_part;
+               a_part = a_part->next;
+               free(q);
+       }
+}
+
+
+/*
+ * Read a message from the server
+ */
+int read_message(CtdlIPC *ipc,
+       long num,   /* message number */
+       int pagin, /* 0 = normal read, 1 = read with pagination, 2 = header */
+       FILE *dest) /* Destination file, NULL for screen */
+{
+       char buf[SIZ];
+       char now[SIZ];
+       int format_type = 0;
+       int fr = 0;
+       int nhdr = 0;
+       struct ctdlipcmessage *message = NULL;
+       int r;                          /* IPC response code */
+       char *converted_text = NULL;
+       char *lineptr;
+       char *nextline;
+       char *searchptr;
+       int i;
+       char ch;
+       int linelen;
+       int final_line_is_blank = 0;
+
+       has_images = 0;
+
+       sigcaught = 0;
+       stty_ctdl(1);
+
+       strcpy(reply_to, NO_REPLY_TO);
+       strcpy(reply_subject, "");
+       strcpy(reply_references, "");
+       strcpy(reply_inreplyto, "");
+
+       r = CtdlIPCGetSingleMessage(ipc, num, (pagin == READ_HEADER ? 1 : 0), 4, &message, buf);
+       if (r / 100 != 1) {
+               err_printf("*** msg #%ld: %d %s\n", num, r, buf);
+               ++lines_printed;
+               lines_printed = checkpagin(lines_printed, pagin, screenheight);
+               stty_ctdl(0);
+               free(message->text);
+               free_parts(message->attachments);
+               free(message);
+               return (0);
+       }
+
+       if (dest) {
+               fprintf(dest, "\n ");
+       } else {
+               scr_printf("\n");
+               ++lines_printed;
+               lines_printed = checkpagin(lines_printed, pagin, screenheight);
+               if (pagin != 2)
+                       scr_printf(" ");
+       }
+       if (pagin == 1 && !dest) {
+               color(BRIGHT_CYAN);
+       }
+
+       /* View headers only */
+       if (pagin == 2) {
+               pprintf("nhdr=%s\nfrom=%s\ntype=%d\nmsgn=%s\n",
+                               message->nhdr ? "yes" : "no",
+                               message->author, message->type,
+                               message->msgid);
+               if (!IsEmptyStr(message->subject)) {
+                       pprintf("subj=%s\n", message->subject);
+               }
+               if (!IsEmptyStr(message->email)) {
+                       pprintf("rfca=%s\n", message->email);
+               }
+               pprintf("hnod=%s\nroom=%s\nnode=%s\ntime=%s",
+                               message->hnod, message->room,
+                               message->node, 
+                               asctime(localtime(&message->time)));
+               if (!IsEmptyStr(message->recipient)) {
+                       pprintf("rcpt=%s\n", message->recipient);
+               }
+               if (message->attachments) {
+                       struct parts *ptr;
+
+                       for (ptr = message->attachments; ptr; ptr = ptr->next) {
+                               pprintf("part=%s|%s|%s|%s|%s|%ld\n",
+                                       ptr->name, ptr->filename, ptr->number,
+                                       ptr->disposition, ptr->mimetype,
+                                       ptr->length);
+                       }
+               }
+               pprintf("\n");
+               stty_ctdl(0);
+               free(message->text);
+               free_parts(message->attachments);
+               free(message);
+               return (0);
+       }
+
+       if (rc_display_message_numbers) {
+               if (dest) {
+                       fprintf(dest, "[#%s] ", message->msgid);
+               } else {
+                       color(DIM_WHITE);
+                       scr_printf("[");
+                       color(BRIGHT_WHITE);
+                       scr_printf("#%s", message->msgid);
+                       color(DIM_WHITE);
+                       scr_printf("] ");
+               }
+       }
+       if (nhdr == 1 && !is_room_aide) {
+               if (dest) {
+                       fprintf(dest, " ****");
+               } else {
+                       scr_printf(" ****");
+               }
+       } else {
+               fmt_date(now, sizeof now, message->time, 0);
+               if (dest) {
+                       fprintf(dest, "%s from %s ", now, message->author);
+                       if (!IsEmptyStr(message->email)) {
+                               fprintf(dest, "<%s> ", message->email);
+                       }
+               } else {
+                       color(BRIGHT_CYAN);
+                       scr_printf("%s ", now);
+                       color(DIM_WHITE);
+                       scr_printf("from ");
+                       color(BRIGHT_CYAN);
+                       scr_printf("%s ", message->author);
+                       if (!IsEmptyStr(message->email)) {
+                               color(DIM_WHITE);
+                               scr_printf("<");
+                               color(BRIGHT_BLUE);
+                               scr_printf("%s", message->email);
+                                       color(DIM_WHITE);
+                               scr_printf("> ");
+                       }
+               }
+               if (!IsEmptyStr(message->node)) {
+                       if ((room_flags & QR_NETWORK)
+                           || ((strcasecmp(message->node, ipc->ServInfo.nodename)
+                            && (strcasecmp(message->node, ipc->ServInfo.fqdn))))) {
+                               if (IsEmptyStr(message->email)) {
+                                       if (dest) {
+                                               fprintf(dest, "@%s ", message->node);
+                                       } else {
+                                               color(DIM_WHITE);
+                                               scr_printf("@");
+                                               color(BRIGHT_YELLOW);
+                                               scr_printf("%s ", message->node);
+                                       }
+                               }
+                       }
+               }
+               if (strcasecmp(message->hnod, ipc->ServInfo.humannode)
+                   && (!IsEmptyStr(message->hnod)) && (IsEmptyStr(message->email))) {
+                       if (dest) {
+                               fprintf(dest, "(%s) ", message->hnod);
+                       } else {
+                               color(DIM_WHITE);
+                               scr_printf("(");
+                               color(BRIGHT_WHITE);
+                               scr_printf("%s", message->hnod);
+                               color(DIM_WHITE);
+                               scr_printf(") ");
+                       }
+               }
+               if (strcasecmp(message->room, room_name) && (IsEmptyStr(message->email))) {
+                       if (dest) {
+                               fprintf(dest, "in %s> ", message->room);
+                       } else {
+                               color(DIM_WHITE);
+                               scr_printf("in ");
+                               color(BRIGHT_MAGENTA);
+                               scr_printf("%s> ", message->room);
+                       }
+               }
+               if (!IsEmptyStr(message->recipient)) {
+                       if (dest) {
+                               fprintf(dest, "to %s ", message->recipient);
+                       } else {
+                               color(DIM_WHITE);
+                               scr_printf("to ");
+                               color(BRIGHT_CYAN);
+                               scr_printf("%s ", message->recipient);
+                       }
+               }
+       }
+       
+       if (dest) {
+               fprintf(dest, "\n");
+       } else {
+               scr_printf("\n");
+       }
+
+       /* Set the reply-to address to an Internet e-mail address if possible
+        */
+       if ((message->email != NULL) && (!IsEmptyStr(message->email))) {
+               if (!IsEmptyStr(message->author)) {
+                       snprintf(reply_to, sizeof reply_to, "%s <%s>", message->author, message->email);
+               }
+               else {
+                       safestrncpy(reply_to, message->email, sizeof reply_to);
+               }
+       }
+
+       /* But if we can't do that, set it to a Citadel address.
+        */
+       if (!strcmp(reply_to, NO_REPLY_TO)) {
+               snprintf(reply_to, sizeof(reply_to), "%s @ %s",
+                        message->author, message->node);
+       }
+
+       if (!dest) {
+               ++lines_printed;
+               lines_printed = checkpagin(lines_printed, pagin, screenheight);
+       }
+
+
+       if (message->msgid != NULL) {
+               safestrncpy(reply_inreplyto, message->msgid, sizeof reply_inreplyto);
+       }
+
+       if (message->references != NULL) if (!IsEmptyStr(message->references)) {
+               safestrncpy(reply_references, message->references, sizeof reply_references);
+       }
+
+       if (message->subject != NULL) {
+               safestrncpy(reply_subject, message->subject, sizeof reply_subject);
+               if (!IsEmptyStr(message->subject)) {
+                       if (dest) {
+                               fprintf(dest, "Subject: %s\n",
+                                                       message->subject);
+                       } else {
+                               color(DIM_WHITE);
+                               scr_printf("Subject: ");
+                               color(BRIGHT_CYAN);
+                               scr_printf("%s\n", message->subject);
+                               ++lines_printed;
+                               lines_printed = checkpagin(lines_printed,
+                                               pagin, screenheight);
+                       }
+               }
+       }
+
+       if (pagin == 1 && !dest) {
+               color(BRIGHT_WHITE);
+       }
+
+       /******* end of header output, start of message text output *******/
+
+       /*
+        * Convert HTML to plain text, formatting for the actual width
+        * of the client screen.
+        */
+       if (!strcasecmp(message->content_type, "text/html")) {
+               converted_text = html_to_ascii(message->text, 0, screenwidth, 0);
+               if (converted_text != NULL) {
+                       free(message->text);
+                       message->text = converted_text;
+                       format_type = 1;
+               }
+       }
+
+       /* Text/plain is a different type */
+       if (!strcasecmp(message->content_type, "text/plain")) {
+               format_type = 1;
+       }
+
+       /* Extract URL's */
+       num_urls = 0;   /* Start with a clean slate */
+       searchptr = message->text;
+       while ( (searchptr != NULL) && (num_urls < MAXURLS) ) {
+               searchptr = strstr(searchptr, "http://");
+               if (searchptr != NULL) {
+                       safestrncpy(urls[num_urls], searchptr, sizeof(urls[num_urls]));
+                       for (i = 0; i < strlen(urls[num_urls]); i++) {
+                               ch = urls[num_urls][i];
+                               if (ch == '>' || ch == '\"' || ch == ')' ||
+                                   ch == ' ' || ch == '\n') {
+                                       urls[num_urls][i] = 0;
+                                       break;
+                               }
+                       }
+                       num_urls++;
+                       ++searchptr;
+               }
+       }
+
+       /*
+        * Here we go
+        */
+       if (format_type == 0) {
+               fr = fmout(screenwidth, NULL, message->text, dest,
+                          ((pagin == 1) ? 1 : 0), screenheight, (-1), 1);
+       } else {
+               /* renderer for text/plain */
+
+               lineptr = message->text;
+
+               do {
+                       nextline = strchr(lineptr, '\n');
+                       if (nextline != NULL) {
+                               *nextline = 0;
+                               ++nextline;
+                               if (*nextline == 0) nextline = NULL;
+                       }
+
+                       if (sigcaught == 0) {
+                               linelen = strlen(lineptr);
+                               if (linelen && (lineptr[linelen-1] == '\r')) {
+                                       lineptr[--linelen] = 0;
+                               }
+                               if (dest) {
+                                       fprintf(dest, "%s\n", lineptr);
+                               } else {
+                                       scr_printf("%s\n", lineptr);
+                                       lines_printed = lines_printed + 1 +
+                                           (linelen / screenwidth);
+                                       lines_printed =
+                                           checkpagin(lines_printed, pagin,
+                                                      screenheight);
+                               }
+                       }
+                       if (lineptr[0] == 0) final_line_is_blank = 1;
+                       else final_line_is_blank = 0;
+                       lineptr = nextline;
+               } while (nextline);
+               fr = sigcaught;
+       }
+       if (!final_line_is_blank) {
+               if (dest) {
+                       fprintf(dest, "\n");
+               }
+               else {
+                       scr_printf("\n");
+                       ++lines_printed;
+                       lines_printed = checkpagin(lines_printed, pagin, screenheight);
+                       fr = sigcaught;         
+               }
+       }
+
+       /* Enumerate any attachments */
+       if ( (pagin == 1) && (message->attachments) ) {
+               struct parts *ptr;
+
+               for (ptr = message->attachments; ptr; ptr = ptr->next) {
+                       if ( (!strcasecmp(ptr->disposition, "attachment"))
+                          || (!strcasecmp(ptr->disposition, "inline"))
+                          || (!strcasecmp(ptr->disposition, ""))
+                       ) {
+                               if ( (strcasecmp(ptr->number, message->mime_chosen))
+                                  && (!IsEmptyStr(ptr->mimetype))
+                               ) {
+                                       color(DIM_WHITE);
+                                       pprintf("Part ");
+                                       color(BRIGHT_MAGENTA);
+                                       pprintf("%s", ptr->number);
+                                       color(DIM_WHITE);
+                                       pprintf(": ");
+                                       color(BRIGHT_CYAN);
+                                       pprintf("%s", ptr->filename);
+                                       color(DIM_WHITE);
+                                       pprintf(" (%s, %ld bytes)\n", ptr->mimetype, ptr->length);
+                                       if (!strncmp(ptr->mimetype, "image/", 6)) {
+                                               has_images++;
+                                       }
+                               }
+                       }
+               }
+       }
+
+       /* Save the attachments info for later */
+       last_message_parts = message->attachments;
+
+       /* Now we're done */
+       free(message->text);
+       free(message);
+
+       if (pagin == 1 && !dest)
+               color(DIM_WHITE);
+       stty_ctdl(0);
+       return (fr);
+}
+
+/*
+ * replace string function for the built-in editor
+ */
+void replace_string(char *filename, long int startpos)
+{
+       char buf[512];
+       char srch_str[128];
+       char rplc_str[128];
+       FILE *fp;
+       int a;
+       long rpos, wpos;
+       char *ptr;
+       int substitutions = 0;
+       long msglen = 0L;
+       int rv;
+
+       scr_printf("Enter text to be replaced:\n: ");
+       ctdl_getline(srch_str, (sizeof(srch_str)-1) );
+       if (IsEmptyStr(srch_str)) {
+               return;
+       }
+
+       scr_printf("Enter text to replace it with:\n: ");
+       ctdl_getline(rplc_str, (sizeof(rplc_str)-1) );
+
+       fp = fopen(filename, "r+");
+       if (fp == NULL) {
+               return;
+       }
+
+       wpos = startpos;
+       fseek(fp, startpos, 0);
+       strcpy(buf, "");
+       while (a = getc(fp), a > 0) {
+               ++msglen;
+               buf[strlen(buf) + 1] = 0;
+               buf[strlen(buf)] = a;
+               if (strlen(buf) >= strlen(srch_str)) {
+                       ptr = (&buf[strlen(buf) - strlen(srch_str)]);
+                       if (!strncmp(ptr, srch_str, strlen(srch_str))) {
+                               strcpy(ptr, rplc_str);
+                               ++substitutions;
+                       }
+               }
+               if (strlen(buf) > 384) {
+                       rpos = ftell(fp);
+                       fseek(fp, wpos, 0);
+                       rv = fwrite((char *) buf, 128, 1, fp);
+                       strcpy(buf, &buf[128]);
+                       wpos = ftell(fp);
+                       fseek(fp, rpos, 0);
+               }
+       }
+       fseek(fp, wpos, 0);
+       if (!IsEmptyStr(buf)) {
+               rv = fwrite((char *) buf, strlen(buf), 1, fp);
+       }
+       wpos = ftell(fp);
+       fclose(fp);
+       rv = truncate(filename, wpos);
+       scr_printf("<R>eplace made %d substitution(s).\n\n", substitutions);
+}
+
+/*
+ * Function to begin composing a new message
+ */
+int client_make_message(CtdlIPC *ipc,
+                                               char *filename,         /* temporary file name */
+                                               char *recipient,        /* NULL if it's not mail */
+                                               int is_anonymous,
+                                               int format_type,
+                                               int mode,
+                                               char *subject,          /* buffer to store subject line */
+                                               int subject_required)
+{
+       FILE *fp;
+       int a, b, e_ex_code;
+       long beg;
+       char datestr[SIZ];
+       char header[SIZ];
+       char *editor_path = NULL;
+       int cksum = 0;
+
+       if (mode >= 2)
+       {
+               if((mode-2) < MAX_EDITORS && !IsEmptyStr(editor_paths[mode-2])) {
+                       editor_path = editor_paths[mode-2];
+               } else if (!IsEmptyStr(editor_paths[0])) {
+                       editor_path = editor_paths[0];
+               } else {
+                       err_printf("*** No editor available, "
+                               "using built-in editor\n");
+                       mode = 0;
+               }
+       }
+
+       fmt_date(datestr, sizeof datestr, time(NULL), 0);
+       header[0] = 0;
+
+       if (room_flags & QR_ANONONLY && !recipient) {
+               snprintf(header, sizeof header, " ****");
+       }
+       else {
+               snprintf(header, sizeof header,
+                       " %s from %s",
+                       datestr,
+                       (is_anonymous ? "[anonymous]" : fullname)
+                       );
+               if (!IsEmptyStr(recipient)) {
+                       size_t tmp = strlen(header);
+                       snprintf(&header[tmp], sizeof header - tmp,
+                               " to %s", recipient);
+               }
+       }
+       scr_printf("%s\n", header);
+       if (subject != NULL) if (!IsEmptyStr(subject)) {
+               scr_printf("Subject: %s\n", subject);
+       }
+       
+       if ( (subject_required) && (IsEmptyStr(subject)) ) {
+               newprompt("Subject: ", subject, 70);
+       }
+
+       beg = 0L;
+
+       if (mode == 1) {
+               scr_printf("(Press ctrl-d when finished)\n");
+       }
+
+       if (mode == 0) {
+               fp = fopen(filename, "r");
+               if (fp != NULL) {
+                       fmout(screenwidth, fp, NULL, NULL, 0,
+                               screenheight, 0, 0);
+                       beg = ftell(fp);
+                       fclose(fp);
+               } else {
+                       fp = fopen(filename, "w");
+                       if (fp == NULL) {
+                               err_printf("*** Error opening temp file!\n"
+                                       "    %s: %s\n",
+                                       filename, strerror(errno));
+                       return(1);
+                       }
+                       fclose(fp);
+               }
+       }
+
+ME1:   switch (mode) {
+
+       case 0:
+               fp = fopen(filename, "r+");
+               if (fp == NULL) {
+                       err_printf("*** Error opening temp file!\n"
+                               "    %s: %s\n",
+                               filename, strerror(errno));
+                       return(1);
+               }
+               citedit(ipc, fp);
+               fclose(fp);
+               goto MECR;
+
+       case 1:
+               fp = fopen(filename, "a");
+               if (fp == NULL) {
+                       err_printf("*** Error opening temp file!\n"
+                               "    %s: %s\n",
+                               filename, strerror(errno));
+                       return(1);
+               }
+               do {
+                       a = inkey();
+                       if (a == 255)
+                               a = 32;
+                       if (a == 13)
+                               a = 10;
+                       if (a != 4) {
+                               putc(a, fp);
+                               scr_putc(a);
+                       }
+                       if (a == 10)
+                               scr_putc(10);
+               } while (a != 4);
+               fclose(fp);
+               break;
+
+       case 2:
+       default:        /* allow 2+ modes */
+               e_ex_code = 1;  /* start with a failed exit code */
+               screen_reset();
+               stty_ctdl(SB_RESTORE);
+               editor_pid = fork();
+               cksum = file_checksum(filename);
+               if (editor_pid == 0) {
+                       char tmp[SIZ];
+
+                       chmod(filename, 0600);
+                       snprintf(tmp, sizeof tmp, "WINDOW_TITLE=%s", header);
+                       putenv(tmp);
+                       execlp(editor_path, editor_path, filename, NULL);
+                       exit(1);
+               }
+               if (editor_pid > 0)
+                       do {
+                               e_ex_code = 0;
+                               b = ka_wait(&e_ex_code);
+                       } while ((b != editor_pid) && (b >= 0));
+               editor_pid = (-1);
+               stty_ctdl(0);
+               screen_set();
+               break;
+       }
+
+MECR:  if (mode >= 2) {
+               if (file_checksum(filename) == cksum) {
+                       err_printf("*** Aborted message.\n");
+                       e_ex_code = 1;
+               }
+               if (e_ex_code == 0) {
+                       goto MEFIN;
+               }
+               goto MEABT2;
+       }
+
+       b = keymenu("Entry command (? for options)",
+                   "<A>bort|<C>ontinue|<S>ave message|<P>rint formatted|"
+                   "add s<U>bject|"
+                   "<R>eplace string|<H>old message");
+
+       if (b == 'a') goto MEABT;
+       if (b == 'c') goto ME1;
+       if (b == 's') goto MEFIN;
+       if (b == 'p') {
+               scr_printf(" %s from %s", datestr, fullname);
+               if (!IsEmptyStr(recipient)) {
+                       scr_printf(" to %s", recipient);
+               }
+               scr_printf("\n");
+               if (subject != NULL) if (!IsEmptyStr(subject)) {
+                       scr_printf("Subject: %s\n", subject);
+               }
+               fp = fopen(filename, "r");
+               if (fp != NULL) {
+                       fmout(screenwidth, fp, NULL, NULL,
+                             ((userflags & US_PAGINATOR) ? 1 : 0),
+                             screenheight, 0, 0);
+                       beg = ftell(fp);
+                       fclose(fp);
+               }
+               goto MECR;
+       }
+       if (b == 'r') {
+               replace_string(filename, 0L);
+               goto MECR;
+       }
+       if (b == 'h') {
+               return (2);
+       }
+       if (b == 'u') {
+               if (subject != NULL) {
+                       newprompt("Subject: ", subject, 70);
+               }
+               goto MECR;
+       }
+
+MEFIN: return (0);
+
+MEABT: scr_printf("Are you sure? ");
+       if (yesno() == 0) {
+               goto ME1;
+       }
+MEABT2:        unlink(filename);
+       return (2);
+}
+
+
+/*
+ * Make sure there's room in msg_arr[] for at least one more.
+ */
+void check_msg_arr_size(void) {
+       if ((num_msgs + 1) > msg_arr_size) {
+               msg_arr_size += 512;
+               msg_arr = realloc(msg_arr,
+                       ((sizeof(long)) * msg_arr_size) );
+       }
+}
+
+
+/*
+ * break_big_lines()  -  break up lines that are >1024 characters
+ *                       otherwise the server will truncate
+ */
+void break_big_lines(char *msg) {
+       char *ptr;
+       char *break_here;
+
+       if (msg == NULL) {
+               return;
+       }
+
+       ptr = msg;
+       while (strlen(ptr) > 1000) {
+               break_here = strchr(&ptr[900], ' ');
+               if ((break_here == NULL) || (break_here > &ptr[999])) {
+                       break_here = &ptr[999];
+               }
+               *break_here = '\n';
+               ptr = break_here++;
+       }
+}
+
+
+/*
+ * entmsg()  -  edit and create a message
+ *              returns 0 if message was saved
+ */
+int entmsg(CtdlIPC *ipc,
+               int is_reply,   /* nonzero if this was a <R>eply command */
+               int c,          /* mode */
+               int masquerade  /* prompt for a non-default display name? */
+) {
+       char buf[SIZ];
+       int a, b;
+       int need_recp = 0;
+       int mode;
+       long highmsg = 0L;
+       FILE *fp;
+       char subject[SIZ];
+       struct ctdlipcmessage message;
+       unsigned long *msgarr = NULL;
+       int r;                  /* IPC response code */
+       int subject_required = 0;
+
+       if (c > 0)
+               mode = 1;
+       else
+               mode = 0;
+
+       strcpy(subject, "");
+
+       /*
+        * First, check to see if we have permission to enter a message in
+        * this room.  The server will return an error code if we can't.
+        */
+       strcpy(message.recipient, "");
+       strcpy(message.author, "");
+       strcpy(message.subject, "");
+       strcpy(message.references, "");
+       message.text = "";              /* point to "", changes later */
+       message.anonymous = 0;
+       message.type = mode;
+
+       if (masquerade) {
+               newprompt("Display name for this message: ", message.author, 40);
+       }
+
+       r = CtdlIPCPostMessage(ipc, 0, &subject_required, &message, buf);
+
+       if (r / 100 != 2 && r / 10 != 57) {
+               scr_printf("%s\n", buf);
+               return (1);
+       }
+
+       /* Error code 570 is special.  It means that we CAN enter a message
+        * in this room, but a recipient needs to be specified.
+        */
+       need_recp = 0;
+       if (r / 10 == 57) {
+               need_recp = 1;
+       }
+
+       /* If the user is a dumbass, tell them how to type. */
+       if ((userflags & US_EXPERT) == 0) {
+               formout(ipc, "entermsg");
+       }
+
+       /* Handle the selection of a recipient, if necessary. */
+       strcpy(buf, "");
+       if (need_recp == 1) {
+               if (axlevel >= 2) {
+                       if (is_reply) {
+                               strcpy(buf, reply_to);
+                       } else {
+                               scr_printf("Enter recipient: ");
+                               ctdl_getline(buf, (SIZ-100) );
+                               if (IsEmptyStr(buf))
+                                       return (1);
+                       }
+               } else
+                       strcpy(buf, "sysop");
+       }
+       strcpy(message.recipient, buf);
+
+       if (is_reply) {
+
+               if (!IsEmptyStr(reply_subject)) {
+                       if (!strncasecmp(reply_subject,
+                          "Re: ", 3)) {
+                               strcpy(message.subject, reply_subject);
+                       }
+                       else {
+                               snprintf(message.subject,
+                                       sizeof message.subject,
+                                       "Re: %s",
+                                       reply_subject);
+                       }
+               }
+
+               /* Trim down excessively long lists of thread references.  We eliminate the
+                * second one in the list so that the thread root remains intact.
+                */
+               int rrtok = num_tokens(reply_references, '|');
+               int rrlen = strlen(reply_references);
+               if ( ((rrtok >= 3) && (rrlen > 900)) || (rrtok > 10) ) {
+                       remove_token(reply_references, 1, '|');
+               }
+
+               snprintf(message.references, sizeof message.references, "%s%s%s",
+                       reply_references,
+                       (IsEmptyStr(reply_references) ? "" : "|"),
+                       reply_inreplyto
+               );
+       }
+
+       if (room_flags & QR_ANONOPT) {
+               scr_printf("Anonymous (Y/N)? ");
+               if (yesno() == 1)
+                       message.anonymous = 1;
+       }
+
+       /* If it's mail, we've got to check the validity of the recipient... */
+       if (!IsEmptyStr(message.recipient)) {
+               r = CtdlIPCPostMessage(ipc, 0, &subject_required,  &message, buf);
+               if (r / 100 != 2) {
+                       scr_printf("%s\n", buf);
+                       return (1);
+               }
+       }
+
+       /* Learn the number of the newest message in in the room, so we can
+        * tell upon saving whether someone else has posted too.
+        */
+       num_msgs = 0;
+       r = CtdlIPCGetMessages(ipc, LastMessages, 1, NULL, &msgarr, buf);
+       if (r / 100 != 1) {
+               scr_printf("%s\n", buf);
+       } else {
+               for (num_msgs = 0; msgarr[num_msgs]; num_msgs++)
+                       ;
+       }
+
+       /* Now compose the message... */
+       if (client_make_message(ipc, temp, message.recipient,
+          message.anonymous, 0, c, message.subject, subject_required) != 0) {
+           if (msgarr) free(msgarr);   
+               return (2);
+       }
+
+       /* Reopen the temp file that was created, so we can send it */
+       fp = fopen(temp, "r");
+
+       if (!fp || !(message.text = load_message_from_file(fp))) {
+               err_printf("*** Internal error while trying to save message!\n"
+                       "%s: %s\n",
+                       temp, strerror(errno));
+               unlink(temp);
+               return(errno);
+       }
+
+       if (fp) fclose(fp);
+
+       /* Break lines that are >1024 characters, otherwise the server
+        * will truncate them.
+        */
+       break_big_lines(message.text);
+
+       /* Transmit message to the server */
+       r = CtdlIPCPostMessage(ipc, 1, NULL, &message, buf);
+       if (r / 100 != 4) {
+               scr_printf("%s\n", buf);
+               return (1);
+       }
+
+       /* Yes, unlink it now, so it doesn't stick around if we crash */
+       unlink(temp);
+
+       if (num_msgs >= 1) highmsg = msgarr[num_msgs - 1];
+
+       if (msgarr) free(msgarr);
+       msgarr = NULL;
+       r = CtdlIPCGetMessages(ipc, NewMessages, 0, NULL, &msgarr, buf);
+       if (r / 100 != 1) {
+               scr_printf("%s\n", buf);
+       } else {
+               for (num_msgs = 0; msgarr[num_msgs]; num_msgs++)
+                       ;
+       }
+
+       /* get new highest message number in room to set lrp for goto... */
+       maxmsgnum = msgarr[num_msgs - 1];
+
+       /* now see if anyone else has posted in here */
+       b = (-1);
+       for (a = 0; a < num_msgs; ++a) {
+               if (msgarr[a] > highmsg) {
+                       ++b;
+               }
+       }
+       if (msgarr) free(msgarr);
+       msgarr = NULL;
+
+       /* In the Mail> room, this algorithm always counts one message
+        * higher than in public rooms, so we decrement it by one.
+        */
+       if (need_recp) {
+               --b;
+       }
+
+       if (b == 1) {
+               scr_printf("*** 1 additional message has been entered "
+                       "in this room by another user.\n");
+       }
+       else if (b > 1) {
+               scr_printf("*** %d additional messages have been entered "
+                       "in this room by other users.\n", b);
+       }
+    free(message.text);
+
+       return(0);
+}
+
+/*
+ * Do editing on a quoted file
+ */
+void process_quote(void)
+{
+       FILE *qfile, *tfile;
+       char buf[128];
+       int line, qstart, qend;
+
+       /* Unlink the second temp file as soon as it's opened, so it'll get
+        * deleted even if the program dies
+        */
+       qfile = fopen(temp2, "r");
+       unlink(temp2);
+
+       /* Display the quotable text with line numbers added */
+       line = 0;
+       if (fgets(buf, 128, qfile) == NULL) {
+               /* we're skipping a line here */
+       }
+       while (fgets(buf, 128, qfile) != NULL) {
+               scr_printf("%3d %s", ++line, buf);
+       }
+       scr_printf("Begin quoting at [1] : ");
+       ctdl_getline(buf, 4);
+       qstart = (buf[0] == 0) ? (1) : atoi(buf);
+       scr_printf("  End quoting at [%d] : ", line);
+       ctdl_getline(buf, 4);
+       qend = (buf[0] == 0) ? (line) : atoi(buf);
+       rewind(qfile);
+       line = 0;
+       if (fgets(buf, 128, qfile) == NULL) {
+               /* we're skipping a line here */
+       }
+       tfile = fopen(temp, "w");
+       while (fgets(buf, 128, qfile) != NULL) {
+               if ((++line >= qstart) && (line <= qend))
+                       fprintf(tfile, " >%s", buf);
+       }
+       fprintf(tfile, " \n");
+       fclose(qfile);
+       fclose(tfile);
+       chmod(temp, 0666);
+}
+
+
+
+/*
+ * List the URL's which were embedded in the previous message
+ */
+void list_urls(CtdlIPC *ipc)
+{
+       int i;
+       char cmd[SIZ];
+       int rv;
+
+       if (num_urls == 0) {
+               scr_printf("There were no URL's in the previous message.\n\n");
+               return;
+       }
+
+       for (i = 0; i < num_urls; ++i) {
+               scr_printf("%3d %s\n", i + 1, urls[i]);
+       }
+
+       if ((i = num_urls) != 1)
+               i = intprompt("Display which one", 1, 1, num_urls);
+
+       snprintf(cmd, sizeof cmd, rc_url_cmd, urls[i - 1]);
+       rv = system(cmd);
+       scr_printf("\n");
+}
+
+
+/*
+ * Run image viewer in background
+ */
+int do_image_view(const char *filename)
+{
+       char cmd[SIZ];
+       pid_t childpid;
+
+       snprintf(cmd, sizeof cmd, imagecmd, filename);
+       childpid = fork();
+       if (childpid < 0) {
+               unlink(filename);
+               return childpid;
+       }
+
+       if (childpid == 0) {
+               int retcode;
+               pid_t grandchildpid;
+
+               grandchildpid = fork();
+               if (grandchildpid < 0) {
+                       return grandchildpid;
+               }
+
+               if (grandchildpid == 0) {
+                       int nullfd;
+                       int outfd = -1;
+                       int errfd = -1;
+
+                       nullfd = open("/dev/null", O_WRONLY);
+                       if (nullfd > -1) {
+                               dup2(1, outfd);
+                               dup2(2, errfd);
+                               dup2(nullfd, 1);
+                               dup2(nullfd, 2);
+                       }
+                       retcode = system(cmd);
+                       if (nullfd > -1) {
+                               dup2(outfd, 1);
+                               dup2(errfd, 2);
+                               close(nullfd);
+                       }
+                       unlink(filename);
+                       exit(retcode);
+               }
+
+               if (grandchildpid > 0) {
+                       exit(0);
+               }
+       }
+
+       if (childpid > 0) {
+               int retcode;
+
+               waitpid(childpid, &retcode, 0);
+               return retcode;
+       }
+       
+       return -1;
+}
+
+
+/*
+ * View an image attached to a message
+ */
+void image_view(CtdlIPC *ipc, unsigned long msg)
+{
+       struct parts *ptr = last_message_parts;
+       char part[SIZ];
+       int found = 0;
+
+       /* Run through available parts */
+       for (ptr = last_message_parts; ptr; ptr = ptr->next) {
+               if ((!strcasecmp(ptr->disposition, "attachment")
+                  || !strcasecmp(ptr->disposition, "inline"))
+                  && !strncmp(ptr->mimetype, "image/", 6)) {
+                       found++;
+                       if (found == 1) {
+                               strcpy(part, ptr->number);
+                       }
+               }
+       }
+
+       while (found > 0) {
+               if (found > 1)
+                       strprompt("View which part (0 when done)", part, SIZ-1);
+               found = -found;
+               for (ptr = last_message_parts; ptr; ptr = ptr->next) {
+                       if ((!strcasecmp(ptr->disposition, "attachment")
+                          || !strcasecmp(ptr->disposition, "inline"))
+                          && !strncmp(ptr->mimetype, "image/", 6)
+                          && !strcasecmp(ptr->number, part)) {
+                               char tmp[PATH_MAX];
+                               char buf[SIZ];
+                               void *file = NULL; /* The downloaded file */
+                               int r;
+       
+                               /* view image */
+                               found = -found;
+                               r = CtdlIPCAttachmentDownload(ipc, msg, ptr->number, &file, progress, buf);
+                               if (r / 100 != 2) {
+                                       scr_printf("%s\n", buf);
+                               } else {
+                                       size_t len;
+       
+                                       len = (size_t)extract_long(buf, 0);
+                                       progress(ipc, len, len);
+                                       scr_flush();
+                                       CtdlMakeTempFileName(tmp, sizeof tmp);
+                                       strcat(tmp, ptr->filename);
+                                       save_buffer(file, len, tmp);
+                                       free(file);
+                                       do_image_view(tmp);
+                               }
+                               break;
+                       }
+               }
+               if (found == 1)
+                       break;
+       }
+}
+
+/*
+ * Read the messages in the current room
+ */
+void readmsgs(CtdlIPC *ipc,
+       enum MessageList c,             /* see listing in citadel_ipc.h */
+       enum MessageDirection rdir,     /* 1=Forward (-1)=Reverse */
+       int q           /* Number of msgs to read (if c==3) */
+) {
+       int a, b, e, f, g, start;
+       int savedpos;
+       int hold_sw = 0;
+       char arcflag = 0;
+       char quotflag = 0;
+       int hold_color = 0;
+       char prtfile[PATH_MAX];
+       char pagin;
+       char cmd[SIZ];
+       char targ[ROOMNAMELEN];
+       char filename[PATH_MAX];
+       char save_to[PATH_MAX];
+       void *attachment = NULL;        /* Downloaded attachment */
+       FILE *dest = NULL;              /* Alternate destination other than screen */
+       int r;                          /* IPC response code */
+       static int att_seq = 0;         /* Attachment download sequence number */
+       int rv = 0;                     /* silence the stupid warn_unused_result warnings */
+
+       if (c < 0)
+               b = (num_msgs - 1);
+       else
+               b = 0;
+
+       CtdlMakeTempFileName(prtfile, sizeof prtfile);
+
+       if (msg_arr) {
+               free(msg_arr);
+               msg_arr = NULL;
+       }
+       r = CtdlIPCGetMessages(ipc, c, q, NULL, &msg_arr, cmd);
+       if (r / 100 != 1) {
+               scr_printf("%s\n", cmd);
+       } else {
+               for (num_msgs = 0; msg_arr[num_msgs]; num_msgs++)
+                       ;
+       }
+
+       if (num_msgs == 0) {    /* TODO look at this later */
+               if (c == LastMessages) return;
+               scr_printf("*** There are no ");
+               if (c == NewMessages) scr_printf("new ");
+               if (c == OldMessages) scr_printf("old ");
+               scr_printf("messages in this room.\n");
+               return;
+       }
+
+       lines_printed = 0;
+
+       /* this loop cycles through each message... */
+       start = ((rdir == 1) ? 0 : (num_msgs - 1));
+       for (a = start; ((a < num_msgs) && (a >= 0)); a = a + rdir) {
+               while (msg_arr[a] == 0L) {
+                       a = a + rdir;
+                       if ((a == num_msgs) || (a == (-1)))
+                               return;
+               }
+
+RAGAIN:                pagin = ((arcflag == 0)
+                        && (quotflag == 0)
+                        && (userflags & US_PAGINATOR)) ? 1 : 0;
+
+               /* If we're doing a quote, set the screenwidth to 72 */
+               if (quotflag) {
+                       hold_sw = screenwidth;
+                       screenwidth = 72;
+               }
+
+               /* If printing or archiving, set the screenwidth to 80 */
+               if (arcflag) {
+                       hold_sw = screenwidth;
+                       screenwidth = 80;
+               }
+
+               /* clear parts list */
+               free_parts(last_message_parts);
+               last_message_parts = NULL;
+
+               /* now read the message... */
+               e = read_message(ipc, msg_arr[a], pagin, dest);
+
+               /* ...and set the screenwidth back if we have to */
+               if ((quotflag) || (arcflag)) {
+                       screenwidth = hold_sw;
+               }
+RMSGREAD:      scr_flush();
+               highest_msg_read = msg_arr[a];
+               if (quotflag) {
+                       fclose(dest);
+                       dest = NULL;
+                       quotflag = 0;
+                       enable_color = hold_color;
+                       process_quote();
+                       e = 'r';
+                       goto DONE_QUOTING;
+               }
+               if (arcflag) {
+                       fclose(dest);
+                       dest = NULL;
+                       arcflag = 0;
+                       enable_color = hold_color;
+                       f = fork();
+                       if (f == 0) {
+                               if (freopen(prtfile, "r", stdin) == NULL) {
+                                       /* we probably should handle the error condition here */
+                               }
+                               screen_reset();
+                               stty_ctdl(SB_RESTORE);
+                               ka_system(printcmd);
+                               stty_ctdl(SB_NO_INTR);
+                               screen_set();
+                               unlink(prtfile);
+                               exit(0);
+                       }
+                       if (f > 0)
+                               do {
+                                       g = wait(NULL);
+                               } while ((g != f) && (g >= 0));
+                       scr_printf("Message printed.\n");
+               }
+               if (rc_alt_semantics && c == 1) {
+                       char buf[SIZ];
+
+                       r = CtdlIPCSetMessageSeen(ipc, msg_arr[a], 1, buf);
+               }
+               if (e == SIGQUIT)
+                       return;
+               if (((userflags & US_NOPROMPT) || (e == SIGINT))
+                       && (((room_flags & QR_MAILBOX) == 0)
+                       || (rc_force_mail_prompts == 0))) {
+                       e = 'n';
+               } else {
+                       color(DIM_WHITE);
+                       scr_printf("(");
+                       color(BRIGHT_WHITE);
+                       scr_printf("%d", num_msgs - a - 1);
+                       color(DIM_WHITE);
+                       scr_printf(") ");
+
+                       keyopt("<B>ack <A>gain <R>eply reply<Q>uoted <N>ext <S>top ");
+                       if (rc_url_cmd[0] && num_urls)
+                               keyopt("<U>RLview ");
+                       if (has_images > 0 && !IsEmptyStr(imagecmd))
+                               keyopt("<I>mages ");
+                       keyopt("<?>help -> ");
+
+                       do {
+                               lines_printed = 2;
+                               e = (inkey() & 127);
+                               e = tolower(e);
+/* return key same as <N> */ if (e == 10)
+                                       e = 'n';
+/* space key same as <N> */ if (e == 32)
+                                       e = 'n';
+/* del/move for aides only */
+                                   if (  (!is_room_aide)
+                                      && ((room_flags & QR_MAILBOX) == 0)
+                                      && ((room_flags2 & QR2_COLLABDEL) == 0)
+                                      ) {
+                                       if ((e == 'd') || (e == 'm'))
+                                               e = 0;
+                               }
+/* print only if available */
+                               if ((e == 'p') && (IsEmptyStr(printcmd)))
+                                       e = 0;
+/* can't file if not allowed */
+                                   if ((e == 'f')
+                                       && (rc_allow_attachments == 0))
+                                       e = 0;
+/* link only if browser avail*/
+                                   if ((e == 'u')
+                                       && (IsEmptyStr(rc_url_cmd)))
+                                       e = 0;
+                               if ((e == 'i')
+                                       && (IsEmptyStr(imagecmd) || !has_images))
+                                       e = 0;
+                       } while ((e != 'a') && (e != 'n') && (e != 's')
+                                && (e != 'd') && (e != 'm') && (e != 'p')
+                                && (e != 'q') && (e != 'b') && (e != 'h')
+                                && (e != 'r') && (e != 'f') && (e != '?')
+                                && (e != 'u') && (e != 'c') && (e != 'y')
+                                && (e != 'i') && (e != 'o') );
+                       switch (e) {
+                       case 's':
+                               scr_printf("Stop");
+                               break;
+                       case 'a':
+                               scr_printf("Again");
+                               break;
+                       case 'd':
+                               scr_printf("Delete");
+                               break;
+                       case 'm':
+                               scr_printf("Move");
+                               break;
+                       case 'c':
+                               scr_printf("Copy");
+                               break;
+                       case 'n':
+                               scr_printf("Next");
+                               break;
+                       case 'p':
+                               scr_printf("Print");
+                               break;
+                       case 'q':
+                               scr_printf("reply Quoted");
+                               break;
+                       case 'b':
+                               scr_printf("Back");
+                               break;
+                       case 'h':
+                               scr_printf("Header");
+                               break;
+                       case 'r':
+                               scr_printf("Reply");
+                               break;
+                       case 'o':
+                               scr_printf("Open attachments");
+                               break;
+                       case 'f':
+                               scr_printf("File");
+                               break;
+                       case 'u':
+                               scr_printf("URL's");
+                               break;
+                       case 'y':
+                               scr_printf("mY next");
+                               break;
+                       case 'i':
+                               break;
+                       case '?':
+                               scr_printf("? <help>");
+                               break;
+                       }
+                       if (userflags & US_DISAPPEAR || e == 'i')
+                               scr_printf("\r%79s\r", "");
+                       else
+                               scr_printf("\n");
+                       scr_flush();
+               }
+DONE_QUOTING:  switch (e) {
+               case '?':
+                       scr_printf("Options available here:\n"
+                               " ?  Help (prints this message)\n"
+                               " S  Stop reading immediately\n"
+                               " A  Again (repeats last message)\n"
+                               " N  Next (continue with next message)\n"
+                               " Y  My Next (continue with next message you authored)\n"
+                               " B  Back (go back to previous message)\n");
+                       if (  (is_room_aide)
+                          || (room_flags & QR_MAILBOX)
+                          || (room_flags2 & QR2_COLLABDEL)
+                       ) {
+                               scr_printf(" D  Delete this message\n"
+                                       " M  Move message to another room\n");
+                       }
+                       scr_printf(" C  Copy message to another room\n");
+                       if (!IsEmptyStr(printcmd))
+                               scr_printf(" P  Print this message\n");
+                       scr_printf(
+                               " Q  Reply to this message, quoting portions of it\n"
+                               " H  Headers (display message headers only)\n");
+                       if (is_mail)
+                               scr_printf(" R  Reply to this message\n");
+                       if (rc_allow_attachments) {
+                               scr_printf(" O  (Open attachments)\n");
+                               scr_printf(" F  (save attachments to a File)\n");
+                       }
+                       if (!IsEmptyStr(rc_url_cmd))
+                               scr_printf(" U  (list URL's for display)\n");
+                       if (!IsEmptyStr(imagecmd) && has_images > 0)
+                               scr_printf(" I  Image viewer\n");
+                       scr_printf("\n");
+                       goto RMSGREAD;
+               case 'p':
+                       scr_flush();
+                       dest = fopen(prtfile, "w");
+                       arcflag = 1;
+                       hold_color = enable_color;
+                       enable_color = 0;
+                       goto RAGAIN;
+               case 'q':
+                       scr_flush();
+                       dest = fopen(temp2, "w");
+                       quotflag = 1;
+                       hold_color = enable_color;
+                       enable_color = 0;
+                       goto RAGAIN;
+               case 's':
+                       return;
+               case 'a':
+                       goto RAGAIN;
+               case 'b':
+                       a = a - (rdir * 2);
+                       break;
+               case 'm':
+               case 'c':
+                       newprompt("Enter target room: ",
+                                 targ, ROOMNAMELEN - 1);
+                       if (!IsEmptyStr(targ)) {
+                               r = CtdlIPCMoveMessage(ipc, (e == 'c' ? 1 : 0),
+                                                      msg_arr[a], targ, cmd);
+                               scr_printf("%s\n", cmd);
+                               if (r / 100 == 2)
+                                       msg_arr[a] = 0L;
+                       } else {
+                               goto RMSGREAD;
+                       }
+                       if (r / 100 != 2)       /* r will be init'ed, FIXME */
+                               goto RMSGREAD;  /* the logic here sucks */
+                       break;
+               case 'o':
+               case 'f':
+                       newprompt("Which section? ", filename, ((sizeof filename) - 1));
+                       r = CtdlIPCAttachmentDownload(ipc, msg_arr[a],
+                                       filename, &attachment, progress, cmd);
+                       if (r / 100 != 2) {
+                               scr_printf("%s\n", cmd);
+                       } else {
+                               extract_token(filename, cmd, 2, '|', sizeof filename);
+                               /*
+                                * Part 1 won't have a filename; use the
+                                * subject of the message instead. IO
+                                */
+                               if (IsEmptyStr(filename)) {
+                                       strcpy(filename, reply_subject);
+                               }
+                               if (e == 'o') {         /* open attachment */
+                                       mkdir(tempdir, 0700);
+                                       snprintf(save_to, sizeof save_to, "%s/%04x.%s",
+                                               tempdir,
+                                               ++att_seq,
+                                               filename);
+                                       save_buffer(attachment, extract_unsigned_long(cmd, 0), save_to);
+                                       snprintf(cmd, sizeof cmd, rc_open_cmd, save_to);
+                                       rv = system(cmd);
+                               }
+                               else {                  /* save attachment to disk */
+                                       destination_directory(save_to, filename);
+                                       save_buffer(attachment, extract_unsigned_long(cmd, 0), save_to);
+                               }
+                       }
+                       if (attachment) {
+                               free(attachment);
+                               attachment = NULL;
+                       }
+                       goto RMSGREAD;
+               case 'd':
+                       scr_printf("*** Delete this message? ");
+                       if (yesno() == 1) {
+                               r = CtdlIPCDeleteMessage(ipc, msg_arr[a], cmd);
+                               scr_printf("%s\n", cmd);
+                               if (r / 100 == 2)
+                                       msg_arr[a] = 0L;
+                       } else {
+                               goto RMSGREAD;
+                       }
+                       break;
+               case 'h':
+                       read_message(ipc, msg_arr[a], READ_HEADER, NULL);
+                       goto RMSGREAD;
+               case 'r':
+                       savedpos = num_msgs;
+                       entmsg(ipc, 1, ((userflags & US_EXTEDIT) ? 2 : 0), 0);
+                       num_msgs = savedpos;
+                       goto RMSGREAD;
+               case 'u':
+                       list_urls(ipc);
+                       goto RMSGREAD;
+               case 'i':
+                       image_view(ipc, msg_arr[a]);
+                       goto RMSGREAD;
+           case 'y':
+          { /* hack hack hack */
+            /* find the next message by me, stay here if we find nothing */
+            int finda;
+            int lasta = a;
+            for (finda = (a + rdir); ((finda < num_msgs) && (finda >= 0)); finda += rdir)
+              {
+               /* This is repetitively dumb, but that's what computers are for.
+                  We have to load up messages until we find one by us */
+               char buf[SIZ];
+               int founda = 0;
+               struct ctdlipcmessage *msg = NULL;
+                
+               /* read the header so we can get 'from=' */
+               r = CtdlIPCGetSingleMessage(ipc, msg_arr[finda], 1, 0, &msg, buf);
+               if (!strncasecmp(msg->author, fullname, sizeof(fullname))) {
+                       a = lasta; /* meesa current */
+                       founda = 1;
+               }
+
+               free(msg);
+
+               if (founda)
+                       break; /* for */
+               lasta = finda; /* keep one behind or we skip on the reentrance to the for */
+              } /* for */
+          } /* case 'y' */
+      } /* switch */
+       }                       /* end for loop */
+}                              /* end read routine */
+
+
+
+
+/*
+ * View and edit a system message
+ */
+void edit_system_message(CtdlIPC *ipc, char *which_message)
+{
+       char desc[SIZ];
+       char read_cmd[SIZ];
+       char write_cmd[SIZ];
+
+       snprintf(desc, sizeof desc, "system message '%s'", which_message);
+       snprintf(read_cmd, sizeof read_cmd, "MESG %s", which_message);
+       snprintf(write_cmd, sizeof write_cmd, "EMSG %s", which_message);
+       do_edit(ipc, desc, read_cmd, "NOOP", write_cmd);
+}
+
+
+
+
+/*
+ * Verify the message base
+ */
+void check_message_base(CtdlIPC *ipc)
+{
+       char buf[SIZ];
+       char *transcript = NULL;
+       int r;          /* IPC response code */
+
+       scr_printf
+           ("Please read the documentation before running this command.\n"
+           "Having done so, do you still want to check the message base? ");
+       if (yesno() == 0)
+               return;
+
+       r = CtdlIPCMessageBaseCheck(ipc, &transcript, buf);
+       if (r / 100 != 1) {
+               scr_printf("%s\n", buf);
+               return;
+       }
+
+       while (transcript && !IsEmptyStr(transcript)) {
+               lines_printed = 1;
+               extract_token(buf, transcript, 0, '\n', sizeof buf);
+               remove_token(transcript, 0, '\n');
+               pprintf("%s\n", buf);
+       }
+       if (transcript) free(transcript);
+       return;
+}
+
+
+/*
+ * Loads the contents of a file into memory.  Caller must free the allocated
+ * memory.
+ */
+char *load_message_from_file(FILE *src)
+{
+       size_t i;
+       size_t got = 0;
+       char *dest = NULL;
+
+       fseek(src, 0, SEEK_END);
+       i = ftell(src);
+       rewind(src);
+
+       dest = (char *)calloc(1, i + 1);
+       if (!dest)
+               return NULL;
+
+       while (got < i) {
+               size_t g;
+
+               g = fread(dest + got, 1, i - got, src);
+               got += g;
+               if (g < i - got) {
+                       /* Interrupted system call, keep going */
+                       if (errno == EINTR)
+                               continue;
+                       /* At this point we have either EOF or error */
+                       i = got;
+                       break;
+               }
+               dest[i] = 0;
+       }
+
+       return dest;
+}
diff --git a/citadel/textclient/messages.h b/citadel/textclient/messages.h
new file mode 100644 (file)
index 0000000..5b3e230
--- /dev/null
@@ -0,0 +1,24 @@
+/* $Id$ */
+
+#define MAXURLS                50      /* Max embedded URL's per message */
+extern int num_urls;
+extern char urls[MAXURLS][SIZ];
+
+int ka_system(char *shc);
+int entmsg(CtdlIPC *ipc, int is_reply, int c, int masquerade);
+void readmsgs(CtdlIPC *ipc, enum MessageList c, enum MessageDirection rdir, int q);
+void edit_system_message(CtdlIPC *ipc, char *which_message);
+pid_t ka_wait(int *kstatus);
+void list_urls(CtdlIPC *ipc);
+void check_message_base(CtdlIPC *ipc);
+int client_make_message(CtdlIPC *ipc,
+                                               char *filename,         /* temporary file name */
+                                               char *recipient,        /* NULL if it's not mail */
+                                               int anon_type,          /* see MES_ types in header file */
+                                               int format_type,
+                                               int mode,
+                                               char *subject,
+                                               int subject_required);
+void citedit(CtdlIPC *ipc, FILE *);
+char *load_message_from_file(FILE *src);
+int file_checksum(char *filename);
diff --git a/citadel/textclient/rooms.c b/citadel/textclient/rooms.c
new file mode 100644 (file)
index 0000000..5fbc76c
--- /dev/null
@@ -0,0 +1,1406 @@
+/*
+ * $Id$
+ *
+ * 
+ * Client-side functions which perform room operations
+ *
+ */
+
+#include "sysdep.h"
+#include <stdlib.h>
+#include <unistd.h>
+#include <fcntl.h>
+#include <stdio.h>
+#include <ctype.h>
+#include <string.h>
+#include <signal.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <sys/wait.h>
+#include <errno.h>
+#include <stdarg.h>
+#include <libcitadel.h>
+#include "citadel.h"
+#include "citadel_ipc.h"
+#include "citadel_decls.h"
+#include "rooms.h"
+#include "commands.h"
+#include "messages.h"
+#ifndef HAVE_SNPRINTF
+#include "snprintf.h"
+#endif
+#include "screen.h"
+#include "citadel_dirs.h"
+
+#define IFNEXPERT if ((userflags&US_EXPERT)==0)
+
+
+void stty_ctdl(int cmd);
+void dotgoto(CtdlIPC *ipc, char *towhere, int display_name, int fromungoto);
+void progress(CtdlIPC* ipc, unsigned long curr, unsigned long cmax);
+int pattern(char *search, char *patn);
+int file_checksum(char *filename);
+int nukedir(char *dirname);
+
+extern unsigned room_flags;
+extern char room_name[];
+extern char temp[];
+extern char tempdir[];
+extern int editor_pid;
+extern int screenwidth;
+extern int screenheight;
+extern char fullname[];
+extern char sigcaught;
+extern char floor_mode;
+extern char curr_floor;
+
+
+extern int ugnum;
+extern long uglsn;
+extern char *uglist[];
+extern long uglistlsn[];
+extern int uglistsize;
+
+extern char floorlist[128][SIZ];
+
+
+void load_floorlist(CtdlIPC *ipc)
+{
+       int a;
+       char buf[SIZ];
+       char *listing = NULL;
+       int r;                  /* IPC response code */
+
+       for (a = 0; a < 128; ++a)
+               floorlist[a][0] = 0;
+
+       r = CtdlIPCFloorListing(ipc, &listing, buf);
+       if (r / 100 != 1) {
+               strcpy(floorlist[0], "Main Floor");
+               return;
+       }
+       while (*listing && !IsEmptyStr(listing)) {
+               extract_token(buf, listing, 0, '\n', sizeof buf);
+               remove_token(listing, 0, '\n');
+               extract_token(floorlist[extract_int(buf, 0)], buf, 1, '|', SIZ);
+       }
+       free(listing);
+}
+
+
+void room_tree_list(struct ctdlroomlisting *rp)
+{
+       static int c = 0;
+       char rmname[ROOMNAMELEN];
+       int f;
+
+       if (rp == NULL) {
+               c = 1;
+               return;
+       }
+
+       if (rp->lnext != NULL) {
+               room_tree_list(rp->lnext);
+       }
+
+       if (sigcaught == 0) {
+               strcpy(rmname, rp->rlname);
+               f = rp->rlflags;
+               if ((c + strlen(rmname) + 4) > screenwidth) {
+
+                       /* line break, check the paginator */
+                       pprintf("\n");
+                       c = 1;
+               }
+               if (f & QR_MAILBOX) {
+                       color(BRIGHT_YELLOW);
+               } else if (f & QR_PRIVATE) {
+                       color(BRIGHT_RED);
+               } else {
+                       color(DIM_WHITE);
+               }
+               pprintf("%s", rmname);
+               if ((f & QR_DIRECTORY) && (f & QR_NETWORK))
+                       pprintf("}  ");
+               else if (f & QR_DIRECTORY)
+                       pprintf("]  ");
+               else if (f & QR_NETWORK)
+                       pprintf(")  ");
+               else
+                       pprintf(">  ");
+               c = c + strlen(rmname) + 3;
+       }
+
+       if (rp->rnext != NULL) {
+               room_tree_list(rp->rnext);
+       }
+
+       free(rp);
+}
+
+
+/* 
+ * Room ordering stuff (compare first by floor, then by order)
+ */
+int rordercmp(struct ctdlroomlisting *r1, struct ctdlroomlisting *r2)
+{
+       if ((r1 == NULL) && (r2 == NULL))
+               return (0);
+       if (r1 == NULL)
+               return (-1);
+       if (r2 == NULL)
+               return (1);
+       if (r1->rlfloor < r2->rlfloor)
+               return (-1);
+       if (r1->rlfloor > r2->rlfloor)
+               return (1);
+       if (r1->rlorder < r2->rlorder)
+               return (-1);
+       if (r1->rlorder > r2->rlorder)
+               return (1);
+       return (0);
+}
+
+
+/*
+ * Common code for all room listings
+ */
+static void listrms(struct march *listing, int new_only, int floor_only, unsigned int flags, char *match)
+{
+       struct march *mptr;
+       struct ctdlroomlisting *rl = NULL;
+       struct ctdlroomlisting *rp;
+       struct ctdlroomlisting *rs;
+       int list_it;
+
+       for (mptr = listing; mptr != NULL; mptr = mptr->next) {
+               list_it = 1;
+
+               if ( (new_only == LISTRMS_NEW_ONLY)
+                  && ((mptr->march_access & UA_HASNEWMSGS) == 0)) 
+                       list_it = 0;
+
+               if ( (new_only == LISTRMS_OLD_ONLY)
+                  && ((mptr->march_access & UA_HASNEWMSGS) != 0)) 
+                       list_it = 0;
+
+               if ( (floor_only >= 0)
+                  && (mptr->march_floor != floor_only))
+                       list_it = 0;
+
+               if (flags && (mptr->march_flags & flags) == 0)
+                   list_it = 0;
+
+           if (match && (pattern(mptr->march_name, match) == -1))
+                       list_it = 0;
+
+               if (list_it) {
+                       rp = malloc(sizeof(struct ctdlroomlisting));
+                       strncpy(rp->rlname, mptr->march_name, ROOMNAMELEN);
+                       rp->rlflags = mptr->march_flags;
+                       rp->rlfloor = mptr->march_floor;
+                       rp->rlorder = mptr->march_order;
+                       rp->lnext = NULL;
+                       rp->rnext = NULL;
+       
+                       rs = rl;
+                       if (rl == NULL) {
+                               rl = rp;
+                       } else {
+                               while (rp != NULL) {
+                                       if (rordercmp(rp, rs) < 0) {
+                                               if (rs->lnext == NULL) {
+                                                       rs->lnext = rp;
+                                                       rp = NULL;
+                                               } else {
+                                                       rs = rs->lnext;
+                                               }
+                                       } else {
+                                               if (rs->rnext == NULL) {
+                                                       rs->rnext = rp;
+                                                       rp = NULL;
+                                               } else {
+                                                       rs = rs->rnext;
+                                               }
+                                       }
+                               }
+                       }
+               }
+       }
+
+       room_tree_list(NULL);
+       room_tree_list(rl);
+       color(DIM_WHITE);
+}
+
+
+void list_other_floors(void)
+{
+       int a, c;
+
+       c = 1;
+       for (a = 0; a < 128; ++a) {
+               if ((strlen(floorlist[a]) > 0) && (a != curr_floor)) {
+                       if ((c + strlen(floorlist[a]) + 4) > screenwidth) {
+                               pprintf("\n");
+                               c = 1;
+                       }
+                       pprintf("%s:  ", floorlist[a]);
+                       c = c + strlen(floorlist[a]) + 3;
+               }
+       }
+}
+
+
+/*
+ * List known rooms.  kn_floor_mode should be set to 0 for a 'flat' listing,
+ * 1 to list rooms on the current floor, or 2 to list rooms on all floors.
+ */
+void knrooms(CtdlIPC *ipc, int kn_floor_mode)
+{
+       int a;
+       struct march *listing = NULL;
+       struct march *mptr;
+       int r;          /* IPC response code */
+       char buf[SIZ];
+
+
+       /* Ask the server for a room list */
+       r = CtdlIPCKnownRooms(ipc, SubscribedRooms, (-1), &listing, buf);
+       if (r / 100 != 1) {
+               listing = NULL;
+       }
+
+       load_floorlist(ipc);
+
+
+       if (kn_floor_mode == 0) {
+               color(BRIGHT_CYAN);
+               pprintf("\n   Rooms with unread messages:\n");
+               listrms(listing, LISTRMS_NEW_ONLY, -1, 0, NULL);
+               color(BRIGHT_CYAN);
+               pprintf("\n\n   No unseen messages in:\n");
+               listrms(listing, LISTRMS_OLD_ONLY, -1, 0, NULL);
+               pprintf("\n");
+       }
+
+       if (kn_floor_mode == 1) {
+               color(BRIGHT_CYAN);
+               pprintf("\n   Rooms with unread messages on %s:\n",
+                       floorlist[(int) curr_floor]);
+               listrms(listing, LISTRMS_NEW_ONLY, curr_floor, 0, NULL);
+               color(BRIGHT_CYAN);
+               pprintf("\n\n   Rooms with no new messages on %s:\n",
+                       floorlist[(int) curr_floor]);
+               listrms(listing, LISTRMS_OLD_ONLY, curr_floor, 0, NULL);
+               color(BRIGHT_CYAN);
+               pprintf("\n\n   Other floors:\n");
+               list_other_floors();
+               pprintf("\n");
+       }
+
+       if (kn_floor_mode == 2) {
+               for (a = 0; a < 128; ++a) {
+                       if (floorlist[a][0] != 0) {
+                               color(BRIGHT_CYAN);
+                               pprintf("\n   Rooms on %s:\n",
+                                       floorlist[a]);
+                               listrms(listing, LISTRMS_ALL, a, 0, NULL);
+                               pprintf("\n");
+                       }
+               }
+       }
+
+       /* Free the room list */
+       while (listing) {
+               mptr = listing->next;
+               free(listing);
+               listing = mptr;
+       };
+
+       color(DIM_WHITE);
+       IFNEXPERT hit_any_key(ipc);
+}
+
+
+void listzrooms(CtdlIPC *ipc)
+{                              /* list public forgotten rooms */
+       struct march *listing = NULL;
+       struct march *mptr;
+       int r;          /* IPC response code */
+       char buf[SIZ];
+
+
+       /* Ask the server for a room list */
+       r = CtdlIPCKnownRooms(ipc, UnsubscribedRooms, (-1), &listing, buf);
+       if (r / 100 != 1) {
+               listing = NULL;
+       }
+
+       color(BRIGHT_CYAN);
+       pprintf("\n   Forgotten public rooms:\n");
+       listrms(listing, LISTRMS_ALL, -1, 0, NULL);
+       pprintf("\n");
+
+       /* Free the room list */
+       while (listing) {
+               mptr = listing->next;
+               free(listing);
+               listing = mptr;
+       };
+
+       color(DIM_WHITE);
+       IFNEXPERT hit_any_key(ipc);
+}
+
+void dotknown(CtdlIPC *ipc, int what, char *match)
+{                              /* list rooms according to attribute */
+       struct march *listing = NULL;
+       struct march *mptr;
+       int r;          /* IPC response code */
+       char buf[SIZ];
+
+       /* Ask the server for a room list */
+       r = CtdlIPCKnownRooms(ipc, AllAccessibleRooms, (-1), &listing, buf);
+       if (r / 100 != 1) {
+               listing = NULL;
+       }
+
+       color(BRIGHT_CYAN);
+
+       switch (what) {
+    case 0:
+       pprintf("\n   Anonymous rooms:\n");
+           listrms(listing, LISTRMS_ALL, -1, QR_ANONONLY|QR_ANONOPT, NULL);
+       pprintf("\n");
+               break;
+    case 1:
+       pprintf("\n   Directory rooms:\n");
+           listrms(listing, LISTRMS_ALL, -1, QR_DIRECTORY, NULL);
+       pprintf("\n");
+               break;
+    case 2:
+       pprintf("\n   Matching \"%s\" rooms:\n", match);
+           listrms(listing, LISTRMS_ALL, -1, 0, match);
+       pprintf("\n");
+               break;
+    case 3:
+       pprintf("\n   Preferred only rooms:\n");
+           listrms(listing, LISTRMS_ALL, -1, QR_PREFONLY, NULL);
+       pprintf("\n");
+               break;
+    case 4:
+       pprintf("\n   Private rooms:\n");
+           listrms(listing, LISTRMS_ALL, -1, QR_PRIVATE, NULL);
+       pprintf("\n");
+               break;
+    case 5:
+       pprintf("\n   Read only rooms:\n");
+           listrms(listing, LISTRMS_ALL, -1, QR_READONLY, NULL);
+       pprintf("\n");
+               break;
+    case 6:
+       pprintf("\n   Shared rooms:\n");
+           listrms(listing, LISTRMS_ALL, -1, QR_NETWORK, NULL);
+       pprintf("\n");
+               break;
+       }
+
+       /* Free the room list */
+       while (listing) {
+               mptr = listing->next;
+               free(listing);
+               listing = mptr;
+       };
+
+       color(DIM_WHITE);
+       IFNEXPERT hit_any_key(ipc);
+}
+
+
+int set_room_attr(CtdlIPC *ipc, unsigned int ibuf, char *prompt, unsigned int sbit)
+{
+       int a;
+
+       a = boolprompt(prompt, (ibuf & sbit));
+       ibuf = (ibuf | sbit);
+       if (!a) {
+               ibuf = (ibuf ^ sbit);
+       }
+       return (ibuf);
+}
+
+
+
+/*
+ * Select a floor (used in several commands)
+ * The supplied argument is the 'default' floor number.
+ * This function returns the selected floor number.
+ */
+int select_floor(CtdlIPC *ipc, int rfloor)
+{
+       int a, newfloor;
+       char floorstr[SIZ];
+
+       if (floor_mode == 1) {
+               if (floorlist[(int) curr_floor][0] == 0) {
+                       load_floorlist(ipc);
+               }
+
+               do {
+                       newfloor = (-1);
+                       safestrncpy(floorstr, floorlist[rfloor],
+                                   sizeof floorstr);
+                       strprompt("Which floor", floorstr, 255);
+                       for (a = 0; a < 128; ++a) {
+                               if (!strcasecmp
+                                   (floorstr, &floorlist[a][0]))
+                                       newfloor = a;
+                               if ((newfloor < 0)
+                                   &&
+                                   (!strncasecmp
+                                    (floorstr, &floorlist[a][0],
+                                     strlen(floorstr))))
+                                       newfloor = a;
+                               if ((newfloor < 0)
+                                   && (pattern(&floorlist[a][0], floorstr)
+                                       >= 0))
+                                       newfloor = a;
+                       }
+                       if (newfloor < 0) {
+                               scr_printf("\n One of:\n");
+                               for (a = 0; a < 128; ++a) {
+                                       if (floorlist[a][0] != 0) {
+                                               scr_printf("%s\n",
+                                                      &floorlist[a][0]);
+                                       }
+                               }
+                       }
+               } while (newfloor < 0);
+               return (newfloor);
+       }
+
+       else {
+               scr_printf("Floor selection bypassed because you have "
+                       "floor mode disabled.\n");
+       }
+
+       return (rfloor);
+}
+
+
+
+
+/*
+ * .<A>ide <E>dit room
+ */
+void editthisroom(CtdlIPC *ipc)
+{
+       int rbump = 0;
+       char raide[USERNAME_SIZE];
+       char buf[SIZ];
+       struct ctdlroom *attr = NULL;
+       struct ExpirePolicy *eptr = NULL;
+       int r;                          /* IPC response code */
+
+       /* Fetch the existing room config */
+       r = CtdlIPCGetRoomAttributes(ipc, &attr, buf);
+       if (r / 100 != 2) {
+               scr_printf("%s\n", buf);
+               return;
+       }
+       eptr = &(attr->QRep);
+
+       /* Fetch the name of the current room aide */
+       r = CtdlIPCGetRoomAide(ipc, buf);
+       if (r / 100 == 2) {
+               safestrncpy(raide, buf, sizeof raide);
+       } else {
+               strcpy(raide, "");
+       }
+       if (IsEmptyStr(raide)) {
+               strcpy(raide, "none");
+       }
+
+       /* Fetch the expire policy (this will silently fail on old servers,
+        * resulting in "default" policy)
+        */
+       r = CtdlIPCGetMessageExpirationPolicy(ipc, 0, &eptr, buf);
+
+       /* Now interact with the user. */
+
+       strprompt("Room name", attr->QRname, ROOMNAMELEN-1);
+       attr->QRfloor = select_floor(ipc, attr->QRfloor);
+       attr->QRflags = set_room_attr(ipc, attr->QRflags, "Private room", QR_PRIVATE);
+       if (attr->QRflags & QR_PRIVATE) {
+               attr->QRflags = set_room_attr(ipc, attr->QRflags,
+                                      "Hidden room (accessible to anyone who knows the room name)",
+                                      QR_GUESSNAME);
+       }
+
+       /* if it's public, clear the privacy classes */
+       if ((attr->QRflags & QR_PRIVATE) == 0) {
+               if (attr->QRflags & QR_GUESSNAME) {
+                       attr->QRflags = attr->QRflags - QR_GUESSNAME;
+               }
+               if (attr->QRflags & QR_PASSWORDED) {
+                       attr->QRflags = attr->QRflags - QR_PASSWORDED;
+               }
+       }
+
+       /* if it's private, choose the privacy classes */
+       if ((attr->QRflags & QR_PRIVATE)
+           && ((attr->QRflags & QR_GUESSNAME) == 0)) {
+               attr->QRflags = set_room_attr(ipc, attr->QRflags,
+                                      "Accessible by entering a password",
+                                      QR_PASSWORDED);
+       }
+       if ((attr->QRflags & QR_PRIVATE)
+           && ((attr->QRflags & QR_PASSWORDED) == QR_PASSWORDED)) {
+               strprompt("Room password", attr->QRpasswd, 9);
+       }
+
+       if ((attr->QRflags & QR_PRIVATE) == QR_PRIVATE) {
+               rbump = boolprompt("Cause current users to forget room", 0);
+       }
+
+       attr->QRflags = set_room_attr(ipc, attr->QRflags,
+                                       "Preferred users only", QR_PREFONLY);
+       attr->QRflags = set_room_attr(ipc, attr->QRflags,
+                                       "Read-only room", QR_READONLY);
+       attr->QRflags2 = set_room_attr(ipc, attr->QRflags2,
+                               "Allow message deletion by anyone who can post",
+                               QR2_COLLABDEL);
+       attr->QRflags = set_room_attr(ipc, attr->QRflags,
+                                       "Permanent room", QR_PERMANENT);
+       attr->QRflags2 = set_room_attr(ipc, attr->QRflags2,
+                                                                  "Subject Required (Force "
+                                                                  "users to specify a message "
+                                   "subject)", QR2_SUBJECTREQ);
+       attr->QRflags = set_room_attr(ipc, attr->QRflags,
+                                       "Directory room", QR_DIRECTORY);
+       if (attr->QRflags & QR_DIRECTORY) {
+               strprompt("Directory name", attr->QRdirname, 14);
+               attr->QRflags =
+                   set_room_attr(ipc, attr->QRflags,
+                                               "Uploading allowed", QR_UPLOAD);
+               attr->QRflags =
+                   set_room_attr(ipc, attr->QRflags, "Downloading allowed",
+                                 QR_DOWNLOAD);
+               attr->QRflags =
+                   set_room_attr(ipc, attr->QRflags,
+                                               "Visible directory", QR_VISDIR);
+       }
+       attr->QRflags = set_room_attr(ipc, attr->QRflags,
+                                       "Network shared room", QR_NETWORK);
+       attr->QRflags2 = set_room_attr(ipc, attr->QRflags2,
+                               "Self-service list subscribe/unsubscribe",
+                               QR2_SELFLIST);
+       attr->QRflags2 = set_room_attr(ipc, attr->QRflags2,
+                               "public posting to this room via room_roomname@yourcitadel.org",
+                               QR2_SMTP_PUBLIC);
+       attr->QRflags2 = set_room_attr(ipc, attr->QRflags2,
+                               "moderated mailinglist",
+                               QR2_MODERATED);
+       attr->QRflags = set_room_attr(ipc, attr->QRflags,
+                              "Automatically make all messages anonymous",
+                              QR_ANONONLY);
+       if ((attr->QRflags & QR_ANONONLY) == 0) {
+               attr->QRflags = set_room_attr(ipc, attr->QRflags,
+                                      "Ask users whether to make messages anonymous",
+                                      QR_ANONOPT);
+       }
+       attr->QRorder = intprompt("Listing order", attr->QRorder, 0, 127);
+
+       /* Ask about the room aide */
+       do {
+               strprompt("Room aide (or 'none')", raide, 29);
+               if (!strcasecmp(raide, "none")) {
+                       strcpy(raide, "");
+                       break;
+               } else {
+                       r = CtdlIPCQueryUsername(ipc, raide, buf);
+                       if (r / 100 != 2)
+                               scr_printf("%s\n", buf);
+               }
+       } while (r / 100 != 2);
+
+       /* Angels and demons dancing in my head... */
+       do {
+               snprintf(buf, sizeof buf, "%d", attr->QRep.expire_mode);
+               strprompt("Message expire policy (? for list)", buf, 1);
+               if (buf[0] == '?') {
+                       scr_printf("\n"
+                               "0. Use the default for this floor\n"
+                               "1. Never automatically expire messages\n"
+                               "2. Expire by message count\n"
+                               "3. Expire by message age\n");
+               }
+       } while ((buf[0] < 48) || (buf[0] > 51));
+       attr->QRep.expire_mode = buf[0] - 48;
+
+       /* ...lunatics and monsters underneath my bed */
+       if (attr->QRep.expire_mode == 2) {
+               snprintf(buf, sizeof buf, "%d", attr->QRep.expire_value);
+               strprompt("Keep how many messages online?", buf, 10);
+               attr->QRep.expire_value = atol(buf);
+       }
+
+       if (attr->QRep.expire_mode == 3) {
+               snprintf(buf, sizeof buf, "%d", attr->QRep.expire_value);
+               strprompt("Keep messages for how many days?", buf, 10);
+               attr->QRep.expire_value = atol(buf);
+       }
+
+       /* Give 'em a chance to change their minds */
+       scr_printf("Save changes (y/n)? ");
+
+       if (yesno() == 1) {
+               r = CtdlIPCSetRoomAide(ipc, raide, buf);
+               if (r / 100 != 2) {
+                       scr_printf("%s\n", buf);
+               }
+
+               r = CtdlIPCSetMessageExpirationPolicy(ipc, 0, eptr, buf);
+               if (r / 100 != 2) {
+                       scr_printf("%s\n", buf);
+               }
+
+               r = CtdlIPCSetRoomAttributes(ipc, rbump, attr, buf);
+               scr_printf("%s\n", buf);
+               strncpy(buf, attr->QRname, ROOMNAMELEN);
+               free(attr);
+               if (r / 100 == 2)
+                       dotgoto(ipc, buf, 2, 0);
+       }
+       else free(attr);
+}
+
+
+/*
+ * un-goto the previous room, or a specified room
+ */
+void dotungoto(CtdlIPC *ipc, char *towhere)
+  {
+    /* Find this 'towhere' room in the list ungoto from this room to
+       that at the messagepointer position in that room in our ungoto list.
+       I suppose I could be a real dick and just ungoto that many places
+       in our list. */
+    int found = -1;
+    int lp;
+       char buf[SIZ];
+       struct ctdlipcroom *rret = NULL;        /* ignored */
+       int r;
+
+       if (uglistsize == 0)
+      {
+               scr_printf("No rooms to ungoto.\n");
+               return;
+      }
+       if (towhere == NULL)
+      {
+               scr_printf("Must specify a room to ungoto.\n");
+               return;
+      }
+       if (IsEmptyStr(towhere))
+      {
+               scr_printf("Must specify a room to ungoto.\n");
+               return;
+      }
+    for (lp = uglistsize-1; lp >= 0; lp--)
+      {
+        if (strcasecmp(towhere, uglist[lp]) == 0)
+          {
+            found = lp;
+            break;
+          }
+      }
+    if (found == -1)
+      {
+               scr_printf("Room: %s not in ungoto list.\n", towhere);
+       return;
+      }
+
+       r = CtdlIPCGotoRoom(ipc, uglist[found], "", &rret, buf);
+       if (rret) free(rret);   /* ignored */
+       if (r / 100 != 2) {
+               scr_printf("%s\n", buf);
+               return;
+       }
+       r = CtdlIPCSetLastRead(ipc, uglistlsn[found] ? uglistlsn[found] : 1, buf);
+       if (r / 100 != 2) {
+               scr_printf("%s\n", buf);
+       }
+       safestrncpy(buf, uglist[found], sizeof(buf));
+    /* we queue ungoto information here, because we're not really
+       ungotoing, we're really going to a random spot in some arbitrary
+       room list. */
+       dotgoto(ipc, buf, 0, 0);
+  }
+
+void ungoto(CtdlIPC *ipc)
+{
+       char buf[SIZ];
+       struct ctdlipcroom *rret = NULL;        /* ignored */
+       int r;
+
+       if (uglistsize == 0)
+               return;
+
+       r = CtdlIPCGotoRoom(ipc, uglist[uglistsize-1], "", &rret, buf);
+       if (rret) free(rret);   /* ignored */
+       if (r / 100 != 2) {
+               scr_printf("%s\n", buf);
+               return;
+       }
+       r = CtdlIPCSetLastRead(ipc, uglistlsn[uglistsize-1] ? uglistlsn[uglistsize-1] : 1, buf);
+       if (r / 100 != 2) {
+               scr_printf("%s\n", buf);
+       }
+       safestrncpy(buf, uglist[uglistsize-1], sizeof(buf));
+       uglistsize--;
+       free(uglist[uglistsize]);
+       /* Don't queue ungoto info or we end up in a loop */
+       dotgoto(ipc, buf, 0, 1);
+}
+
+
+/*
+ * saves filelen bytes from file at pathname
+ */
+int save_buffer(void *file, size_t filelen, const char *pathname)
+{
+       size_t block = 0;
+       size_t bytes_written = 0;
+       FILE *fp;
+
+       fp = fopen(pathname, "w");
+       if (!fp) {
+               err_printf("Cannot open '%s': %s\n", pathname, strerror(errno));
+               return 0;
+       }
+       do {
+               block = fwrite((char *)file + bytes_written, 1,
+                               filelen - bytes_written, fp);
+               bytes_written += block;
+       } while (errno == EINTR && bytes_written < filelen);
+       fclose(fp);
+
+       if (bytes_written < filelen) {
+               err_printf("Trouble saving '%s': %s\n", pathname,
+                               strerror(errno));
+               return 0;
+       }
+       return 1;
+}
+
+
+/*
+ * Save supplied_filename in dest directory; gets the name only
+ */
+void destination_directory(char *dest, const char *supplied_filename)
+{
+       static char save_dir[SIZ] = { 0 };
+
+       if (IsEmptyStr(save_dir)) {
+               if (getenv("HOME") == NULL) {
+                       strcpy(save_dir, ".");
+               }
+               else {
+                       sprintf(save_dir, "%s/Desktop", getenv("HOME"));
+                       if (access(save_dir, W_OK) != 0) {
+                               sprintf(save_dir, "%s", getenv("HOME"));
+                               if (access(save_dir, W_OK) != 0) {
+                                       sprintf(save_dir, ".");
+                               }
+                       }
+               }
+       }
+
+       sprintf(dest, "%s/%s", save_dir, supplied_filename);
+       strprompt("Save as", dest, PATH_MAX);
+
+       /* Remember the directory for next time */
+       strcpy(save_dir, dest);
+       if (strrchr(save_dir, '/') != NULL) {
+               strcpy(strrchr(save_dir, '/'), "");
+       }
+       else {
+               strcpy(save_dir, ".");
+       }
+}
+
+
+/*
+ * download()  -  download a file or files.  The argument passed to this
+ *                function determines which protocol to use.
+ *  proto - 0 = paginate, 1 = xmodem, 2 = raw, 3 = ymodem, 4 = zmodem, 5 = save
+ */
+void download(CtdlIPC *ipc, int proto)
+{
+       char buf[SIZ];
+       char filename[PATH_MAX];
+       char tempname[PATH_MAX];
+       char transmit_cmd[SIZ];
+       FILE *tpipe = NULL;
+       int broken = 0;
+       int r;
+       int rv = 0;
+       void *file = NULL;      /* The downloaded file */
+       size_t filelen = 0L;    /* The downloaded file length */
+
+       if ((room_flags & QR_DOWNLOAD) == 0) {
+               scr_printf("*** You cannot download from this room.\n");
+               return;
+       }
+
+       newprompt("Enter filename: ", filename, PATH_MAX);
+
+       /* Save to local disk, for folks with their own copy of the client */
+       if (proto == 5) {
+               destination_directory(tempname, filename);
+               r = CtdlIPCFileDownload(ipc, filename, &file, 0, progress, buf);
+               if (r / 100 != 2) {
+                       scr_printf("%s\n", buf);
+                       return;
+               }
+               save_buffer(file, (size_t)extract_long(buf, 0), tempname);
+               free(file);
+               return;
+       }
+
+       r = CtdlIPCFileDownload(ipc, filename, &file, 0, progress, buf);
+       if (r / 100 != 2) {
+               scr_printf("%s\n", buf);
+               return;
+       }
+       filelen = extract_unsigned_long(buf, 0);
+
+       /* Meta-download for public clients */
+       /* scr_printf("Fetching file from Citadel server...\n"); */
+       mkdir(tempdir, 0700);
+       snprintf(tempname, sizeof tempname, "%s/%s", tempdir, filename);
+       tpipe = fopen(tempname, "wb");
+       if (fwrite(file, filelen, 1, tpipe) < filelen) {
+               /* FIXME: restart syscall on EINTR */
+               broken = 1;
+       }
+       fclose(tpipe);
+       if (file) free(file);
+
+       if (proto == 0) {
+               /* FIXME: display internally instead */
+               snprintf(transmit_cmd, sizeof transmit_cmd,
+                       "SHELL=/dev/null; export SHELL; TERM=dumb; export TERM; exec more -d <%s",
+                       tempname);
+       }
+       else if (proto == 1)
+               snprintf(transmit_cmd, sizeof transmit_cmd, "exec sx %s", tempname);
+       else if (proto == 3)
+               snprintf(transmit_cmd, sizeof transmit_cmd, "exec sb %s", tempname);
+       else if (proto == 4)
+               snprintf(transmit_cmd, sizeof transmit_cmd, "exec sz %s", tempname);
+       else
+               /* FIXME: display internally instead */
+               snprintf(transmit_cmd, sizeof transmit_cmd, "exec cat %s", tempname);
+
+       screen_reset();
+       stty_ctdl(SB_RESTORE);
+       rv = system(transmit_cmd);
+       stty_ctdl(SB_NO_INTR);
+       screen_set();
+
+       /* clean up the temporary directory */
+       nukedir(tempdir);
+       ctdl_beep();    /* Beep beep! */
+}
+
+
+/*
+ * read directory of this room
+ */
+void roomdir(CtdlIPC *ipc)
+{
+       char flnm[256];
+       char flsz[32];
+       char comment[256];
+       char mimetype[256];
+       char buf[256];
+       char *listing = NULL;   /* Returned directory listing */
+       int r;
+
+       r = CtdlIPCReadDirectory(ipc, &listing, buf);
+       if (r / 100 != 1) {
+               pprintf("%s\n", buf);
+               return;
+       }
+
+       extract_token(comment, buf, 0, '|', sizeof comment);
+       extract_token(flnm, buf, 1, '|', sizeof flnm);
+       pprintf("\nDirectory of %s on %s\n", flnm, comment);
+       pprintf("-----------------------\n");
+       while (listing && *listing && !IsEmptyStr(listing)) {
+               extract_token(buf, listing, 0, '\n', sizeof buf);
+               remove_token(listing, 0, '\n');
+
+               extract_token(flnm, buf, 0, '|', sizeof flnm);
+               extract_token(flsz, buf, 1, '|', sizeof flsz);
+               extract_token(mimetype, buf, 2, '|', sizeof mimetype);
+               extract_token(comment, buf, 3, '|', sizeof comment);
+               if (strlen(flnm) <= 14)
+                       pprintf("%-14s %8s %s [%s]\n", flnm, flsz, comment, mimetype);
+               else
+                       pprintf("%s\n%14s %8s %s [%s]\n", flnm, "", flsz,
+                               comment, mimetype);
+       }
+       if (listing) free(listing);
+}
+
+
+/*
+ * add a user to a private room
+ */
+void invite(CtdlIPC *ipc)
+{
+       char username[USERNAME_SIZE];
+       char buf[SIZ];
+       int r;                          /* IPC response code */
+
+       newprompt("Name of user? ", username, USERNAME_SIZE);
+       if (username[0] == 0)
+               return;
+
+       r = CtdlIPCInviteUserToRoom(ipc, username, buf);
+       scr_printf("%s\n", buf);
+}
+
+
+/*
+ * kick a user out of a room
+ */
+void kickout(CtdlIPC *ipc)
+{
+       char username[USERNAME_SIZE];
+       char buf[SIZ];
+       int r;                          /* IPC response code */
+
+       newprompt("Name of user? ", username, USERNAME_SIZE);
+       if (username[0] == 0)
+               return;
+
+       r = CtdlIPCKickoutUserFromRoom(ipc, username, buf);
+       scr_printf("%s\n", buf);
+}
+
+
+/*
+ * aide command: kill the current room
+ */
+void killroom(CtdlIPC *ipc)
+{
+       char aaa[100];
+       int r;
+
+       r = CtdlIPCDeleteRoom(ipc, 0, aaa);
+       if (r / 100 != 2) {
+               scr_printf("%s\n", aaa);
+               return;
+       }
+
+       scr_printf("Are you sure you want to kill this room? ");
+       if (yesno() == 0)
+               return;
+
+       r = CtdlIPCDeleteRoom(ipc, 1, aaa);
+       scr_printf("%s\n", aaa);
+       if (r / 100 != 2)
+               return;
+       dotgoto(ipc, "_BASEROOM_", 0, 0);
+}
+
+void forget(CtdlIPC *ipc)
+{                              /* forget the current room */
+       char buf[SIZ];
+
+       scr_printf("Are you sure you want to forget this room? ");
+       if (yesno() == 0)
+               return;
+
+       remove_march(room_name, 0);
+       if (CtdlIPCForgetRoom(ipc, buf) / 100 != 2) {
+               scr_printf("%s\n", buf);
+               return;
+       }
+
+       /* now return to the lobby */
+       dotgoto(ipc, "_BASEROOM_", 0, 0);
+}
+
+
+/*
+ * create a new room
+ */
+void entroom(CtdlIPC *ipc)
+{
+       char buf[SIZ];
+       char new_room_name[ROOMNAMELEN];
+       int new_room_type;
+       char new_room_pass[10];
+       int new_room_floor;
+       int a, b;
+       int r;                          /* IPC response code */
+
+       /* Check permission to create room */
+       r = CtdlIPCCreateRoom(ipc, 0, "", 1, "", 0, buf);
+       if (r / 100 != 2) {
+               scr_printf("%s\n", buf);
+               return;
+       }
+
+       newprompt("Name for new room? ", new_room_name, ROOMNAMELEN - 1);
+       if (IsEmptyStr(new_room_name)) {
+               return;
+       }
+       for (a = 0; !IsEmptyStr(&new_room_name[a]); ++a) {
+               if (new_room_name[a] == '|') {
+                       new_room_name[a] = '_';
+               }
+       }
+
+       new_room_floor = select_floor(ipc, (int) curr_floor);
+
+       IFNEXPERT formout(ipc, "roomaccess");
+       do {
+               scr_printf("<?>Help\n"
+                       "<1>Public room (shown to all users by default)\n"
+                       "<2>Hidden room (accessible to anyone who knows the room name)\n"
+                       "<3>Passworded room (hidden, plus requires a password to enter)\n"
+                       "<4>Invitation-only room (requires access to be granted by an Aide)\n"
+                       "<5>Personal room (accessible to you only)\n"
+                       "Enter room type: "
+               );
+               do {
+                       b = inkey();
+               } while (((b < '1') || (b > '5')) && (b != '?'));
+               if (b == '?') {
+                       scr_printf("?\n");
+                       formout(ipc, "roomaccess");
+               }
+       } while ((b < '1') || (b > '5'));
+       b -= '0';                       /* Portable */
+       scr_printf("%d\n", b);
+       new_room_type = b - 1;
+       if (new_room_type == 2) {
+               newprompt("Enter a room password: ", new_room_pass, 9);
+               for (a = 0; !IsEmptyStr(&new_room_pass[a]); ++a)
+                       if (new_room_pass[a] == '|')
+                               new_room_pass[a] = '_';
+       } else {
+               strcpy(new_room_pass, "");
+       }
+
+       scr_printf("\042%s\042, a", new_room_name);
+       if (b == 1)
+               scr_printf(" public room.");
+       if (b == 2)
+               scr_printf(" hidden room.");
+       if (b == 3)
+               scr_printf(" passworded room, password: %s", new_room_pass);
+       if (b == 4)
+               scr_printf("n invitation-only room.");
+       if (b == 5)
+               scr_printf(" personal room.");
+       scr_printf("\nInstall it? (y/n) : ");
+       if (yesno() == 0) {
+               return;
+       }
+
+       r = CtdlIPCCreateRoom(ipc, 1, new_room_name, new_room_type,
+                             new_room_pass, new_room_floor, buf);
+       if (r / 100 != 2) {
+               scr_printf("%s\n", buf);
+               return;
+       }
+
+       /* command succeeded... now GO to the new room! */
+       dotgoto(ipc, new_room_name, 0, 0);
+}
+
+
+
+void readinfo(CtdlIPC *ipc)
+{                              /* read info file for current room */
+       char buf[SIZ];
+       char raide[64];
+       int r;                  /* IPC response code */
+       char *text = NULL;
+
+       /* Name of currernt room aide */
+       r = CtdlIPCGetRoomAide(ipc, buf);
+       if (r / 100 == 2)
+               safestrncpy(raide, buf, sizeof raide);
+       else
+               strcpy(raide, "");
+
+       if (!IsEmptyStr(raide))
+               scr_printf("Room aide is %s.\n\n", raide);
+
+       r = CtdlIPCRoomInfo(ipc, &text, buf);
+       if (r / 100 != 1)
+               return;
+
+       if (text) {
+               fmout(screenwidth, NULL, text, NULL,
+                     ((userflags & US_PAGINATOR) ? 1 : 0), screenheight, 
+                     (*raide) ? 2 : 0, 1);
+               free(text);
+       }
+}
+
+
+/*
+ * <W>ho knows room...
+ */
+void whoknows(CtdlIPC *ipc)
+{
+       char buf[256];
+       char *listing = NULL;
+       int r;
+
+       r = CtdlIPCWhoKnowsRoom(ipc, &listing, buf);
+       if (r / 100 != 1) {
+               pprintf("%s\n", buf);
+               return;
+       }
+       while (!IsEmptyStr(listing)) {
+               extract_token(buf, listing, 0, '\n', sizeof buf);
+               remove_token(listing, 0, '\n');
+               if (sigcaught == 0)
+                       pprintf("%s\n", buf);
+       }
+       free(listing);
+}
+
+
+void do_edit(CtdlIPC *ipc,
+               char *desc, char *read_cmd, char *check_cmd, char *write_cmd)
+{
+       FILE *fp;
+       char cmd[SIZ];
+       int b, cksum, editor_exit;
+
+       if (IsEmptyStr(editor_paths[0])) {
+               scr_printf("Do you wish to re-enter %s? ", desc);
+               if (yesno() == 0)
+                       return;
+       }
+
+       fp = fopen(temp, "w");
+       fclose(fp);
+
+       CtdlIPC_chat_send(ipc, check_cmd);
+       CtdlIPC_chat_recv(ipc, cmd);
+       if (cmd[0] != '2') {
+               scr_printf("%s\n", &cmd[4]);
+               return;
+       }
+
+       if (!IsEmptyStr(editor_paths[0])) {
+               CtdlIPC_chat_send(ipc, read_cmd);
+               CtdlIPC_chat_recv(ipc, cmd);
+               if (cmd[0] == '1') {
+                       fp = fopen(temp, "w");
+                       while (CtdlIPC_chat_recv(ipc, cmd), strcmp(cmd, "000")) {
+                               fprintf(fp, "%s\n", cmd);
+                       }
+                       fclose(fp);
+               }
+       }
+
+       cksum = file_checksum(temp);
+
+       if (!IsEmptyStr(editor_paths[0])) {
+               char tmp[SIZ];
+
+               snprintf(tmp, sizeof tmp, "WINDOW_TITLE=%s", desc);
+               putenv(tmp);
+               screen_reset();
+               stty_ctdl(SB_RESTORE);
+               editor_pid = fork();
+               if (editor_pid == 0) {
+                       chmod(temp, 0600);
+                       execlp(editor_paths[0], editor_paths[0], temp, NULL);
+                       exit(1);
+               }
+               if (editor_pid > 0)
+                       do {
+                               editor_exit = 0;
+                               b = ka_wait(&editor_exit);
+                       } while ((b != editor_pid) && (b >= 0));
+               editor_pid = (-1);
+               scr_printf("Executed %s\n", editor_paths[0]);
+               stty_ctdl(0);
+               screen_set();
+       } else {
+               scr_printf("Entering %s.  "
+                       "Press return twice when finished.\n", desc);
+               fp = fopen(temp, "r+");
+               citedit(ipc, fp);
+               fclose(fp);
+       }
+
+       if (file_checksum(temp) == cksum) {
+               scr_printf("*** Aborted.\n");
+       }
+
+       else {
+               CtdlIPC_chat_send(ipc, write_cmd);
+               CtdlIPC_chat_recv(ipc, cmd);
+               if (cmd[0] != '4') {
+                       scr_printf("%s\n", &cmd[4]);
+                       return;
+               }
+
+               fp = fopen(temp, "r");
+               while (fgets(cmd, SIZ - 1, fp) != NULL) {
+                       cmd[strlen(cmd) - 1] = 0;
+                       CtdlIPC_chat_send(ipc, cmd);
+               }
+               fclose(fp);
+               CtdlIPC_chat_send(ipc, "000");
+       }
+
+       unlink(temp);
+}
+
+
+void enterinfo(CtdlIPC *ipc)
+{                              /* edit info file for current room */
+       do_edit(ipc, "the Info file for this room", "RINF", "EINF 0", "EINF 1");
+}
+
+void enter_bio(CtdlIPC *ipc)
+{
+       char cmd[SIZ];
+       snprintf(cmd, sizeof cmd, "RBIO %s", fullname);
+       do_edit(ipc, "your Bio", cmd, "NOOP", "EBIO");
+}
+
+/*
+ * create a new floor
+ */
+void create_floor(CtdlIPC *ipc)
+{
+       char buf[SIZ];
+       char newfloorname[SIZ];
+       int r;                  /* IPC response code */
+
+       load_floorlist(ipc);
+
+       r = CtdlIPCCreateFloor(ipc, 0, "", buf);
+       if ( (r / 100 != 2) && (r != ERROR + ILLEGAL_VALUE) ) {
+               scr_printf("%s\n", buf);
+               return;
+       }
+
+       newprompt("Name for new floor: ", newfloorname, 255);
+       if (!*newfloorname) return;
+       r = CtdlIPCCreateFloor(ipc, 1, newfloorname, buf);
+       if (r / 100 == 2) {
+               scr_printf("Floor has been created.\n");
+       } else {
+               scr_printf("%s\n", buf);
+       }
+
+       load_floorlist(ipc);
+}
+
+/*
+ * edit the current floor
+ */
+void edit_floor(CtdlIPC *ipc)
+{
+       char buf[SIZ];
+       struct ExpirePolicy *ep = NULL;
+       int r;                          /* IPC response code */
+
+       load_floorlist(ipc);
+
+       /* Fetch the expire policy (this will silently fail on old servers,
+        * resulting in "default" policy)
+        */
+       r = CtdlIPCGetMessageExpirationPolicy(ipc, 1, &ep, buf);
+
+       /* Interact with the user */
+       scr_printf("You are editing the floor called \"%s\"\n", 
+               &floorlist[(int) curr_floor][0] );
+       strprompt("Floor name", &floorlist[(int) curr_floor][0], 255);
+
+       /* Angels and demons dancing in my head... */
+       do {
+               snprintf(buf, sizeof buf, "%d", ep->expire_mode);
+               strprompt
+                   ("Floor default message expire policy (? for list)",
+                    buf, 1);
+               if (buf[0] == '?') {
+                       scr_printf("\n"
+                               "0. Use the system default\n"
+                               "1. Never automatically expire messages\n"
+                               "2. Expire by message count\n"
+                               "3. Expire by message age\n");
+               }
+       } while ((buf[0] < '0') || (buf[0] > '3'));
+       ep->expire_mode = buf[0] - '0';
+
+       /* ...lunatics and monsters underneath my bed */
+       if (ep->expire_mode == 2) {
+               snprintf(buf, sizeof buf, "%d", ep->expire_value);
+               strprompt("Keep how many messages online?", buf, 10);
+               ep->expire_value = atol(buf);
+       }
+
+       if (ep->expire_mode == 3) {
+               snprintf(buf, sizeof buf, "%d", ep->expire_value);
+               strprompt("Keep messages for how many days?", buf, 10);
+               ep->expire_value = atol(buf);
+       }
+
+       /* Save it */
+       r = CtdlIPCSetMessageExpirationPolicy(ipc, 1, ep, buf);
+       r = CtdlIPCEditFloor(ipc, curr_floor, &floorlist[(int)curr_floor][0], buf);
+       scr_printf("%s\n", buf);
+       load_floorlist(ipc);
+}
+
+
+
+
+/*
+ * kill the current floor 
+ */
+void kill_floor(CtdlIPC *ipc)
+{
+       int floornum_to_delete, a;
+       char buf[SIZ];
+
+       load_floorlist(ipc);
+       do {
+               floornum_to_delete = (-1);
+               scr_printf("(Press return to abort)\n");
+               newprompt("Delete which floor? ", buf, 255);
+               if (IsEmptyStr(buf))
+                       return;
+               for (a = 0; a < 128; ++a)
+                       if (!strcasecmp(&floorlist[a][0], buf))
+                               floornum_to_delete = a;
+               if (floornum_to_delete < 0) {
+                       scr_printf("No such floor.  Select one of:\n");
+                       for (a = 0; a < 128; ++a)
+                               if (floorlist[a][0] != 0)
+                                       scr_printf("%s\n", &floorlist[a][0]);
+               }
+       } while (floornum_to_delete < 0);
+       CtdlIPCDeleteFloor(ipc, 1, floornum_to_delete, buf);
+       scr_printf("%s\n", buf);
+       load_floorlist(ipc);
+}
diff --git a/citadel/textclient/rooms.h b/citadel/textclient/rooms.h
new file mode 100644 (file)
index 0000000..6e32365
--- /dev/null
@@ -0,0 +1,51 @@
+/* $Id$ */
+void listzrooms(CtdlIPC *ipc);
+void readinfo(CtdlIPC *ipc);
+void forget(CtdlIPC *ipc);
+void entroom(CtdlIPC *ipc);
+void killroom(CtdlIPC *ipc);
+void invite(CtdlIPC *ipc);
+void kickout(CtdlIPC *ipc);
+void editthisroom(CtdlIPC *ipc);
+void roomdir(CtdlIPC *ipc);
+void download(CtdlIPC *ipc, int proto);
+void ungoto(CtdlIPC *ipc);
+void dotungoto(CtdlIPC *ipc, char *towhere);
+void whoknows(CtdlIPC *ipc);
+void enterinfo(CtdlIPC *ipc);
+void knrooms(CtdlIPC *ipc, int kn_floor_mode);
+void dotknown(CtdlIPC *ipc, int what, char *match);
+void load_floorlist(CtdlIPC *ipc);
+void create_floor(CtdlIPC *ipc);
+void edit_floor(CtdlIPC *ipc);
+void kill_floor(CtdlIPC *ipc);
+void enter_bio(CtdlIPC *ipc);
+void hit_any_key(CtdlIPC *ipc);
+int save_buffer(void *file, size_t filelen, const char *pathname);
+void destination_directory(char *dest, const char *supplied_filename);
+void do_edit(CtdlIPC *ipc,
+               char *desc, char *read_cmd, char *check_cmd, char *write_cmd);
+
+
+
+/* 
+ * This struct holds a list of rooms for client display.
+ * (oooh, a tree!)
+ */
+struct ctdlroomlisting {
+        struct ctdlroomlisting *lnext;
+       struct ctdlroomlisting *rnext;
+        char rlname[ROOMNAMELEN];
+        unsigned rlflags;
+       int rlfloor;
+        int rlorder;
+        };
+
+
+enum {
+        LISTRMS_NEW_ONLY,
+        LISTRMS_OLD_ONLY,
+        LISTRMS_ALL
+};
+
+
diff --git a/citadel/textclient/routines.c b/citadel/textclient/routines.c
new file mode 100644 (file)
index 0000000..c8e85a7
--- /dev/null
@@ -0,0 +1,614 @@
+/*
+ * $Id$
+ *
+ * Client-side support functions.
+ *
+ */
+
+#include "sysdep.h"
+#include <stdlib.h>
+#include <unistd.h>
+#include <fcntl.h>
+#include <stdio.h>
+#include <ctype.h>
+#include <string.h>
+#include <sys/types.h>
+#include <sys/ioctl.h>
+#include <pwd.h>
+#include <signal.h>
+#include <dirent.h>
+#include <errno.h>
+
+#if TIME_WITH_SYS_TIME
+# include <sys/time.h>
+# include <time.h>
+#else
+# if HAVE_SYS_TIME_H
+#  include <sys/time.h>
+# else
+#  include <time.h>
+# endif
+#endif
+
+#ifdef HAVE_LIMITS_H
+#include <limits.h>
+#endif
+#ifdef HAVE_UTMP_H
+#include <utmp.h>
+#endif
+#ifdef HAVE_UTMPX_H
+#include <utmpx.h>
+#endif
+
+#include <libcitadel.h>
+#include "citadel.h"
+#include "citadel_ipc.h"
+#include "screen.h"
+
+#ifndef HAVE_GETUTLINE
+struct utmp *getutline(struct utmp *ut);
+#endif
+
+#define ROUTINES_C
+
+#include "citadel.h"
+#include "routines.h"
+#include "commands.h"
+#include "citadel_decls.h"
+#include "routines2.h"
+
+#define IFAIDE if(axlevel>=6)
+#define IFNAIDE if (axlevel<6)
+
+extern unsigned userflags;
+//extern char *axdefs[8];
+extern char sigcaught;
+extern char rc_floor_mode;
+extern int rc_ansi_color;
+extern int rc_prompt_control;
+
+/* Destructive backspace */
+void back(int spaces) {
+       int a;
+       for (a=0; a<spaces; ++a) {
+               scr_putc(8);
+               scr_putc(32);
+               scr_putc(8);
+       }
+}
+
+void hit_any_key(CtdlIPC *ipc) {       /* hit any key to continue */
+       int a,b;
+
+       color(COLOR_PUSH);
+       color(DIM_RED);
+       scr_printf("%s\r", ipc->ServInfo.moreprompt);
+       color(COLOR_POP);
+       stty_ctdl(0);
+       b=inkey();
+       for (a=0; !IsEmptyStr(&ipc->ServInfo.moreprompt[a]); ++a)
+               scr_putc(' ');
+       scr_putc(13);
+       stty_ctdl(1);
+       if ( (rc_prompt_control == 1)
+          || ((rc_prompt_control == 3) && (userflags & US_PROMPTCTL)) ) {
+               if (b == 'q' || b == 'Q' || b == 's' || b == 'S')
+                       b = STOP_KEY;
+               if (b == 'n' || b == 'N')
+                       b = NEXT_KEY;
+       }
+       if (b==NEXT_KEY) sigcaught = SIGINT;
+       if (b==STOP_KEY) sigcaught = SIGQUIT;
+}
+
+/*
+ * Edit or delete a user (cmd=25 to edit/create, 96 to delete)
+ */
+void edituser(CtdlIPC *ipc, int cmd)
+{
+       char buf[SIZ];
+       char who[USERNAME_SIZE];
+       char newname[USERNAME_SIZE];
+       struct ctdluser *user = NULL;
+       int newnow = 0;
+       int r;                          /* IPC response code */
+       int change_name = 0;
+
+       strcpy(newname, "");
+
+       newprompt("User name: ", who, 29);
+       while ((r = CtdlIPCAideGetUserParameters(ipc, who, &user, buf)) / 100 != 2) {
+               scr_printf("%s\n", buf);
+               if (cmd == 25) {
+                       scr_printf("Do you want to create this user? ");
+                       if (yesno()) {
+                               r = CtdlIPCCreateUser(ipc, who, 0, buf);
+                               if (r / 100 == 2) {
+                                       newnow = 1;
+                                       continue;
+                               }
+                               scr_printf("%s\n", buf);
+                       }
+               }
+               free(user);
+               return;
+       }
+
+       if (cmd == 25) {
+               val_user(ipc, user->fullname, 0); /* Display registration */
+
+               if (!newnow) {
+                       change_name = 1;
+                       while (change_name == 1) {
+                               if (boolprompt("Change name", 0)) {
+                                       strprompt("New name", newname, USERNAME_SIZE-1);
+                                       r = CtdlIPCRenameUser(ipc, user->fullname, newname, buf);
+                                       if (r / 100 != 2) {
+                                               scr_printf("%s\n", buf);
+                                       }
+                                       else {
+                                               strcpy(user->fullname, newname);
+                                               change_name = 0;
+                                       }
+                               }
+                               else {
+                                       change_name = 0;
+                               }
+                       }
+               }
+
+               if (newnow || boolprompt("Change password", 0)) {
+                       strprompt("Password", user->password, -19);
+               }
+       
+               user->axlevel = intprompt("Access level", user->axlevel, 0, 6);
+               if (boolprompt("Permission to send Internet mail", (user->flags & US_INTERNET)))
+                       user->flags |= US_INTERNET;
+               else
+                       user->flags &= ~US_INTERNET;
+               if (boolprompt("Ask user to register again", !(user->flags & US_REGIS)))
+                       user->flags &= ~US_REGIS;
+               else
+                       user->flags |= US_REGIS;
+               user->timescalled = intprompt("Times called",
+                               user->timescalled, 0, INT_MAX);
+               user->posted = intprompt("Messages posted",
+                                       user->posted, 0, INT_MAX);
+               user->lastcall = boolprompt("Set last call to now", 0) ?
+                                       time(NULL) : user->lastcall;
+               user->USuserpurge = intprompt("Purge time (in days, 0 for system default",
+                               user->USuserpurge, 0, INT_MAX);
+       }
+
+       if (cmd == 96) {
+               scr_printf("Do you want to delete this user? ");
+               if (!yesno()) {
+                       free(user);
+                       return;
+               }
+               user->axlevel = 0;
+       }
+
+       r = CtdlIPCAideSetUserParameters(ipc, user, buf);
+       if (r / 100 != 2) {
+               scr_printf("%s\n", buf);
+       }
+       free(user);
+}
+
+
+/* Display a prompt and flip a bit based on whether the user answers
+ * yes or no.  Yes=1 and No=0, unless 'backwards' is set to a nonzero value
+ * in which case No=1 and Yes=0.
+ */
+int set_attr(CtdlIPC *ipc, unsigned int sval, char *prompt, unsigned int sbit, int backwards)
+{
+       int a;
+       int temp;
+
+       temp = sval;
+       color(DIM_WHITE);
+       scr_printf("%50s ", prompt);
+       color(DIM_MAGENTA);
+       scr_printf("[");
+       color(BRIGHT_MAGENTA);
+
+       if (backwards) {
+               scr_printf("%3s", ((temp&sbit) ? "No":"Yes"));
+       }
+       else {
+               scr_printf("%3s", ((temp&sbit) ? "Yes":"No"));
+       }
+
+       color(DIM_MAGENTA);
+       scr_printf("]? ");
+       color(BRIGHT_CYAN);
+       a = (temp & sbit);
+       if (a != 0) a = 1;
+       if (backwards) a = 1 - a;
+       a = yesno_d(a);
+       if (backwards) a = 1 - a;
+       color(DIM_WHITE);
+       temp = (temp|sbit);
+       if (!a) temp = (temp^sbit);
+       return(temp);
+}
+
+/*
+ * modes are:  0 - .EC command, 1 - .EC for new user,
+ *             2 - toggle Xpert mode  3 - toggle floor mode
+ */
+void enter_config(CtdlIPC *ipc, int mode)
+{
+       char buf[SIZ];
+       struct ctdluser *user = NULL;
+       int r;                          /* IPC response code */
+
+       r = CtdlIPCGetConfig(ipc, &user, buf);
+       if (r / 100 != 2) {
+               scr_printf("%s\n", buf);
+               free(user);
+               return;
+       }
+
+       if (mode == 0 || mode == 1) {
+
+               /* Does anyone still use dialup connections with manual
+                * screen dimensions setting anymore?  For now we'll keep
+                * the system's ability to set these, but remove the prompts
+                * because they're spurious for nearly everyone.
+                * 
+               user->USscreenwidth = intprompt("Enter your screen width",
+                                               user->USscreenwidth, 20, 255);
+               user->USscreenheight = intprompt("Enter your screen height",
+                                                user->USscreenheight, 3, 255);
+                */
+               user->flags = set_attr(ipc, user->flags,
+                                      "Are you an experienced Citadel user",
+                                      US_EXPERT, 0);
+               if ((user->flags & US_EXPERT) == 0 && mode == 1) {
+                       free(user);
+                       return;
+               }
+
+               user->flags = set_attr(ipc, user->flags,
+                       "Print last old message on New message request",
+                       US_LASTOLD, 0);
+
+               user->flags = set_attr(ipc, user->flags,
+                                      "Prompt after each message",
+                                      US_NOPROMPT, 1);
+
+               if ((user->flags & US_NOPROMPT) == 0)
+                       user->flags = set_attr(ipc, user->flags,
+                                              "Use 'disappearing' prompts",
+                                              US_DISAPPEAR, 0);
+
+               user->flags = set_attr(ipc, user->flags,
+                                      "Pause after each screenful of text",
+                                      US_PAGINATOR, 0);
+
+               if (rc_prompt_control == 3 && (user->flags & US_PAGINATOR))
+                       user->flags = set_attr(ipc, user->flags,
+                               "<N>ext and <S>top work at paginator prompt",
+                               US_PROMPTCTL, 0);
+
+               if (rc_floor_mode == RC_DEFAULT)
+                       user->flags = set_attr(ipc, user->flags,
+                                              "View rooms by floor",
+                                              US_FLOORS, 0);
+
+               if (rc_ansi_color == 3)
+                       user->flags = set_attr(ipc, user->flags,
+                                              "Enable color support",
+                                              US_COLOR, 0);
+
+               if ((user->flags & US_EXPERT) == 0)
+                       formout(ipc, "unlisted");
+
+               user->flags = set_attr(ipc, user->flags,
+                                      "Be unlisted in userlog",
+                                      US_UNLISTED, 0);
+
+               if (!IsEmptyStr(editor_paths[0])) {
+                       user->flags = set_attr(ipc, user->flags,
+                               "Always enter messages with the full-screen editor",
+                               US_EXTEDIT, 0);
+               }
+
+       }
+
+       if (mode == 2) {
+               if (user->flags & US_EXPERT) {
+                       user->flags ^= US_EXPERT;
+                       scr_printf("Expert mode now OFF\n");
+               } else {
+                       user->flags |= US_EXPERT;
+                       scr_printf("Expert mode now ON\n");
+               }
+       }
+
+       if (mode == 3) {
+               if (user->flags & US_FLOORS) {
+                       user->flags ^= US_FLOORS;
+                       scr_printf("Floor mode now OFF\n");
+               } else {
+                       user->flags |= US_FLOORS;
+                       scr_printf("Floor mode now ON\n");
+               }
+       }
+
+       r = CtdlIPCSetConfig(ipc, user, buf);
+       if (r / 100 != 2) scr_printf("%s\n", buf);
+       userflags = user->flags;
+       free(user);
+}
+
+/*
+ * getstring()  -  get a line of text from a file
+ *                ignores lines beginning with "#"
+ */
+int getstring(FILE *fp, char *string)
+{
+       int a,c;
+       do {
+               strcpy(string,"");
+               a=0;
+               do {
+                       c=getc(fp);
+                       if (c<0) {
+                               string[a]=0;
+                               return(-1);
+                       }
+                       string[a++]=c;
+               } while(c!=10);
+                       string[a-1]=0;
+       } while(string[0]=='#');
+       return(strlen(string));
+}
+
+
+/* Searches for patn in search string */
+int pattern(char *search, char *patn) {
+       int a,b,len;
+       
+       len = strlen(patn);
+       for (a=0; !IsEmptyStr(&search[a]); ++a) {
+               b=strncasecmp(&search[a],patn,len);
+               if (b==0) return(b);
+       }
+       return(-1);
+}
+
+
+void strproc(char *string)
+{
+       int a;
+
+       if (IsEmptyStr(string)) return;
+
+       /* Convert non-printable characters to blanks */
+       for (a=0; !IsEmptyStr(&string[a]); ++a) {
+               if (string[a]<32) string[a]=32;
+               if (string[a]>126) string[a]=32;
+       }
+
+       /* Remove leading and trailing blanks */
+       while(string[0]<33) strcpy(string,&string[1]);
+       while(string[strlen(string)-1]<33) string[strlen(string)-1]=0;
+
+       /* Remove double blanks */
+       for (a=0; a<strlen(string); ++a) {
+               if ((string[a]==32)&&(string[a+1]==32)) {
+                       strcpy(&string[a],&string[a+1]);
+                       a=0;
+               }
+       }
+
+       /* remove characters which would interfere with the network */
+       for (a=0; a<strlen(string); ++a) {
+               if (string[a]=='!') strcpy(&string[a],&string[a+1]);
+               if (string[a]=='@') strcpy(&string[a],&string[a+1]);
+               if (string[a]=='_') strcpy(&string[a],&string[a+1]);
+               if (string[a]==',') strcpy(&string[a],&string[a+1]);
+               if (string[a]=='%') strcpy(&string[a],&string[a+1]);
+               if (string[a]=='|') strcpy(&string[a],&string[a+1]);
+       }
+
+}
+
+
+#ifndef HAVE_STRERROR
+/*
+ * replacement strerror() for systems that don't have it
+ */
+char *strerror(int e)
+{
+       static char buf[128];
+
+       snprintf(buf, sizeof buf, "errno = %d",e);
+       return(buf);
+}
+#endif
+
+
+void progress(CtdlIPC* ipc, unsigned long curr, unsigned long cmax)
+{
+       static char dots[] =
+               "**************************************************";
+       char dots_printed[51];
+       char fmt[42];
+       unsigned long a;
+
+       if (curr >= cmax) {
+               sln_printf("\r%79s\r","");
+               status_line(ipc->ServInfo.humannode, ipc->ServInfo.site_location,
+                       room_name, secure, 0);
+       } else {
+               /* a will be range 0-50 rather than 0-100 */
+               a=(curr * 50) / cmax;
+               sprintf(fmt, "[%%s%%%lds] %%3ld%%%% %%10ld/%%10ld\r", 50 - a);
+               strncpy(dots_printed, dots, a);
+               dots_printed[a] = 0;
+               sln_printf(fmt, dots_printed, "",
+                               curr * 100 / cmax, curr, cmax);
+               sln_flush();
+       }
+}
+
+
+/*
+ * NOT the same locate_host() in locate_host.c.  This one just does a
+ * 'who am i' to try to discover where the user is...
+ */
+void locate_host(CtdlIPC* ipc, char *hbuf)
+{
+#ifndef HAVE_UTMP_H
+       char buf[SIZ];
+       FILE *who;
+       int a,b;
+
+       who = (FILE *)popen("who am i","r");
+       if (who==NULL) {
+               strcpy(hbuf, ipc->ServInfo.fqdn);
+               return; 
+       }
+       fgets(buf,sizeof buf,who);
+       pclose(who);
+
+       b = 0;
+       for (a=0; !IsEmptyStr(&buf[a]); ++a) {
+               if ((buf[a]=='(')||(buf[a]==')')) ++b;
+       }
+       if (b<2) {
+               strcpy(hbuf, ipc->ServInfo.fqdn);
+               return;
+       }
+
+       for (a=0; a<strlen(buf); ++a) {
+               if (buf[a]=='(') {
+                       strcpy(buf,&buf[a+1]);
+               }
+       }
+       for (a=0; a<strlen(buf); ++a) {
+               if (buf[a]==')') buf[a] = 0;
+       }
+
+       if (IsEmptyStr(buf)) strcpy(hbuf, ipc->ServInfo.fqdn);
+       else strncpy(hbuf,buf,24);
+#else
+       char *tty = ttyname(0);
+#ifdef HAVE_GETUTXLINE
+       struct utmpx ut, *put;
+#else
+       struct utmp ut, *put;
+#endif
+
+       if (tty == NULL) {
+           fail:
+               safestrncpy(hbuf, ipc->ServInfo.fqdn, 24);
+               return;
+       }
+
+       if (strncmp(tty, "/dev/", 5))
+               goto fail;
+
+       safestrncpy(ut.ut_line, &tty[5], sizeof ut.ut_line);
+
+#ifdef HAVE_GETUTXLINE /* Solaris uses this */
+       if ((put = getutxline(&ut)) == NULL)
+#else
+       if ((put = getutline(&ut)) == NULL)
+#endif
+               goto fail;
+
+#if defined(HAVE_UT_TYPE) || defined(HAVE_GETUTXLINE)
+       if (put->ut_type == USER_PROCESS) {
+#endif
+#if defined(HAVE_UT_HOST) || defined(HAVE_GETUTXLINE)
+               if (*put->ut_host)
+                       safestrncpy(hbuf, put->ut_host, 24);
+               else
+#endif
+                       safestrncpy(hbuf, put->ut_line, 24);
+#if defined(HAVE_UT_TYPE) || defined(HAVE_GETUTXLINE)
+       }
+       else goto fail;
+#endif
+#endif /* HAVE_UTMP_H */
+}
+
+/*
+ * miscellaneous server commands (testing, etc.)
+ */
+void misc_server_cmd(CtdlIPC *ipc, char *cmd) {
+       char buf[SIZ];
+
+       CtdlIPC_chat_send(ipc, cmd);
+       CtdlIPC_chat_recv(ipc, buf);
+       scr_printf("%s\n",buf);
+       if (buf[0]=='1') {
+               set_keepalives(KA_HALF);
+               while (CtdlIPC_chat_recv(ipc, buf), strcmp(buf,"000")) {
+                       scr_printf("%s\n",buf);
+               }
+               set_keepalives(KA_YES);
+               return;
+       }
+       if (buf[0]=='4') {
+               do {
+                       newprompt("> ",buf,255);
+                       CtdlIPC_chat_send(ipc, buf);
+               } while(strcmp(buf,"000"));
+               return;
+       }
+}
+
+
+/*
+ * compute the checksum of a file
+ */
+int file_checksum(char *filename)
+{
+       int cksum = 0;
+       int ch;
+       FILE *fp;
+
+       fp = fopen(filename,"r");
+       if (fp == NULL) return(0);
+
+       /* yes, this algorithm may allow cksum to overflow, but that's ok
+        * as long as it overflows consistently, which it will.
+        */
+       while (ch=getc(fp), ch>=0) {
+               cksum = (cksum + ch);
+       }
+
+       fclose(fp);
+       return(cksum);
+}
+
+/*
+ * nuke a directory and its contents
+ */
+int nukedir(char *dirname)
+{
+       DIR *dp;
+       struct dirent *d;
+       char filename[SIZ];
+
+       dp = opendir(dirname);
+       if (dp == NULL) {
+               return(errno);
+       }
+
+       while (d = readdir(dp), d != NULL) {
+               snprintf(filename, sizeof filename, "%s/%s",
+                       dirname, d->d_name);
+               unlink(filename);
+       }
+
+       closedir(dp);
+       return(rmdir(dirname));
+}
diff --git a/citadel/textclient/routines.h b/citadel/textclient/routines.h
new file mode 100644 (file)
index 0000000..6bb516a
--- /dev/null
@@ -0,0 +1,13 @@
+/* $Id$ */
+void edituser(CtdlIPC *ipc, int cmd);
+void interr(int errnum);
+int struncmp(char *lstr, char *rstr, int len);
+int pattern(char *search, char *patn);
+void enter_config(CtdlIPC* ipc, int mode);
+void locate_host(CtdlIPC* ipc, char *hbuf);
+void misc_server_cmd(CtdlIPC *ipc, char *cmd);
+int nukedir(char *dirname);
+void strproc(char *string);
+void back(int spaces);
+void progress(CtdlIPC* ipc, unsigned long curr, unsigned long cmax);
+int set_attr(CtdlIPC *ipc, unsigned int sval, char *prompt, unsigned int sbit, int backwards);
diff --git a/citadel/textclient/routines2.c b/citadel/textclient/routines2.c
new file mode 100644 (file)
index 0000000..3fbff5f
--- /dev/null
@@ -0,0 +1,632 @@
+/* $Id$
+ *
+ * More client-side support functions.
+ * Unlike routines.c, some of these DO use global variables.
+ *
+ */
+
+#include <stdlib.h>
+#include <unistd.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+#include <stdio.h>
+#include <ctype.h>
+#include <string.h>
+#include <limits.h>
+
+#if TIME_WITH_SYS_TIME
+# include <sys/time.h>
+# include <time.h>
+#else
+# if HAVE_SYS_TIME_H
+#  include <sys/time.h>
+# else
+#  include <time.h>
+# endif
+#endif
+
+#include <signal.h>
+#include <pwd.h>
+#include <errno.h>
+#include <stdarg.h>
+#include <libcitadel.h>
+#include "sysdep.h"
+#include "citadel.h"
+#include "citadel_ipc.h"
+#include "citadel_decls.h"
+#include "routines2.h"
+#include "routines.h"
+#include "commands.h"
+#include "messages.h"
+#ifndef HAVE_SNPRINTF
+#include "snprintf.h"
+#endif
+#include "screen.h"
+
+/* work around solaris include files */
+#ifdef reg
+#undef reg
+#endif
+
+extern char temp[];
+extern char tempdir[];
+extern char *axdefs[8];
+extern long highest_msg_read;
+extern long maxmsgnum;
+extern unsigned room_flags;
+extern int screenwidth;
+
+
+/*
+int eopen(char *name, int mode)
+{
+       int ret;
+       ret = open(name, mode);
+       if (ret < 0) {
+               err_printf("Cannot open file '%s', mode=%d, errno=%d\n",
+                       name, mode, errno);
+               interr(errno);
+       }
+       return (ret);
+}
+*/
+
+
+int room_prompt(unsigned int qrflags)
+{                              /* return proper room prompt character */
+       int a;
+       a = '>';
+       if (qrflags & QR_DIRECTORY)
+               a = ']';
+       if ((a == ']') && (qrflags & QR_NETWORK))
+               a = '}';
+       if ((a == '>') && (qrflags & QR_NETWORK))
+               a = ')';
+       return (a);
+}
+
+void entregis(CtdlIPC *ipc)
+{                              /* register with name and address */
+
+       char buf[SIZ];
+       char tmpname[30];
+       char tmpaddr[25];
+       char tmpcity[15];
+       char tmpstate[3];
+       char tmpzip[11];
+       char tmpphone[15];
+       char tmpemail[SIZ];
+       char tmpcountry[32];
+       char diruser[256];
+       char dirnode[256];
+       char holdemail[SIZ];
+       char *reg = NULL;
+       int ok = 0;
+       int r;                          /* IPC response code */
+
+       strcpy(tmpname, "");
+       strcpy(tmpaddr, "");
+       strcpy(tmpcity, "");
+       strcpy(tmpstate, "");
+       strcpy(tmpzip, "");
+       strcpy(tmpphone, "");
+       strcpy(tmpemail, "");
+       strcpy(tmpcountry, "");
+
+       r = CtdlIPCGetUserRegistration(ipc, NULL, &reg, buf);
+       if (r / 100 == 1) {
+               int a = 0;
+
+               while (reg && !IsEmptyStr(reg)) {
+
+                       extract_token(buf, reg, 0, '\n', sizeof buf);
+                       remove_token(reg, 0, '\n');
+
+                       if (a == 2)
+                               safestrncpy(tmpname, buf, sizeof tmpname);
+                       else if (a == 3)
+                               safestrncpy(tmpaddr, buf, sizeof tmpaddr);
+                       else if (a == 4)
+                               safestrncpy(tmpcity, buf, sizeof tmpcity);
+                       else if (a == 5)
+                               safestrncpy(tmpstate, buf, sizeof tmpstate);
+                       else if (a == 6)
+                               safestrncpy(tmpzip, buf, sizeof tmpzip);
+                       else if (a == 7)
+                               safestrncpy(tmpphone, buf, sizeof tmpphone);
+                       else if (a == 9)
+                               safestrncpy(tmpemail, buf, sizeof tmpemail);
+                       else if (a == 10)
+                               safestrncpy(tmpcountry, buf, sizeof tmpcountry);
+                       ++a;
+               }
+       }
+       strprompt("REAL name", tmpname, 29);
+       strprompt("Address", tmpaddr, 24);
+       strprompt("City/town", tmpcity, 14);
+       strprompt("State/province", tmpstate, 2);
+       strprompt("ZIP/Postal Code", tmpzip, 10);
+       strprompt("Country", tmpcountry, 31);
+       strprompt("Telephone number", tmpphone, 14);
+
+       do {
+               ok = 1;
+               safestrncpy(holdemail, tmpemail, sizeof holdemail);
+               strprompt("Email address", tmpemail, 31);
+               r = CtdlIPCDirectoryLookup(ipc, tmpemail, buf);
+               if (r / 100 == 2) {
+                       extract_token(diruser, buf, 0, '@', sizeof diruser);
+                       extract_token(dirnode, buf, 1, '@', sizeof dirnode);
+                       striplt(diruser);
+                       striplt(dirnode);
+                       if ((strcasecmp(diruser, fullname))
+                          || (strcasecmp(dirnode, ipc->ServInfo.nodename))) {
+                               scr_printf(
+                                       "\nYou can't use %s as your address.\n",
+                                       tmpemail);
+                               scr_printf(
+                                       "It is already in use by %s @ %s.\n",
+                                       diruser, dirnode);
+                               ok = 0;
+                               safestrncpy(tmpemail, holdemail, sizeof tmpemail);
+                       }
+               }
+       } while (ok == 0);
+
+       /* now send the registration info back to the server */
+       reg = (char *)realloc(reg, SIZ);
+       if (reg) {
+               sprintf(reg, "%s\n%s\n%s\n%s\n%s\n%s\n%s\n%s\n",
+                       tmpname, tmpaddr, tmpcity, tmpstate,
+                       tmpzip, tmpphone, tmpemail, tmpcountry);
+               r = CtdlIPCSetRegistration(ipc, reg, buf);
+               if (r / 100 != 4)
+                       scr_printf("%s\n", buf);
+               free(reg);
+       }
+       scr_printf("\n");
+}
+
+void updatels(CtdlIPC *ipc)
+{                              /* make all messages old in current room */
+       char buf[256];
+       int r;                          /* IPC response code */
+
+       if (rc_alt_semantics) {
+               if (maxmsgnum == 0 && highest_msg_read == 0) {
+                       return;
+               }
+               r = CtdlIPCSetLastRead(ipc, (maxmsgnum > highest_msg_read) ?
+                                maxmsgnum : highest_msg_read, buf);
+       } else {
+               r = CtdlIPCSetLastRead(ipc, (maxmsgnum > highest_msg_read) ?
+                                maxmsgnum : highest_msg_read, buf);
+/*             r = CtdlIPCSetLastRead(ipc, maxmsgnum, buf); */
+/* This is a quick-and-dirty fix to all msgs becoming new in Mail>.
+ * It will need to be rethought when messages.c is rewritten.
+ */
+       }
+       if (r / 100 != 2)
+               scr_printf("%s\n", buf);
+}
+
+/*
+ * only make messages old in this room that have been read
+ */
+void updatelsa(CtdlIPC *ipc)
+{
+       char buf[256];
+       int r;                          /* IPC response code */
+
+       r = CtdlIPCSetLastRead(ipc, highest_msg_read, buf);
+       if (r / 100 != 2)
+               scr_printf("%s\n", &buf[4]);
+}
+
+
+/*
+ * client-based uploads (for users with their own clientware)
+ */
+void cli_upload(CtdlIPC *ipc)
+{
+       char flnm[PATH_MAX];
+       char desc[151];
+       char buf[256];
+       char tbuf[256];
+       int r;          /* IPC response code */
+       int a;
+       int fd;
+
+       if ((room_flags & QR_UPLOAD) == 0) {
+               scr_printf("*** You cannot upload to this room.\n");
+               return;
+       }
+       newprompt("File to be uploaded: ", flnm, 55);
+       fd = open(flnm, O_RDONLY);
+       if (fd < 0) {
+               scr_printf("Cannot open '%s': %s\n", flnm, strerror(errno));
+               return;
+       }
+       scr_printf("Enter a description of this file:\n");
+       newprompt(": ", desc, 75);
+
+       /* Keep generating filenames in hope of finding a unique one */
+       a = 0;
+       while (a < 10) {
+               /* basename of filename */
+               strcpy(tbuf, flnm);
+               if (haschar(tbuf, '/'))
+                       extract_token(tbuf, flnm,
+                               num_tokens(tbuf, '/') - 1,
+                               '/', sizeof tbuf
+                       );
+               /* filename.1, filename.2, etc */
+               if (a > 0) {
+                       sprintf(&tbuf[strlen(tbuf)], ".%d", a);
+               }
+               /* Try upload */
+               r = CtdlIPCFileUpload(ipc, tbuf, desc, flnm, progress, buf);
+               if (r / 100 == 5 || r < 0)
+                       scr_printf("%s\n", buf);
+               else
+                       break;
+               ++a;
+       }
+       if (a > 0) scr_printf("Saved as '%s'\n", tbuf);
+}
+
+
+/*
+ * Function used for various image upload commands
+ */
+void cli_image_upload(CtdlIPC *ipc, char *keyname)
+{
+       char flnm[PATH_MAX];
+       char buf[256];
+       int r;
+
+       /* Can we upload this image? */
+       r = CtdlIPCImageUpload(ipc, 0, NULL, keyname, NULL, buf);
+       if (r / 100 != 2) {
+               err_printf("%s\n", buf);
+               return;
+       }
+       newprompt("Image file to be uploaded: ", flnm, 55);
+       r = CtdlIPCImageUpload(ipc, 1, flnm, keyname, progress, buf);
+       if (r / 100 == 5) {
+               err_printf("%s\n", buf);
+       } else if (r < 0) {
+               err_printf("Cannot upload '%s': %s\n", flnm, strerror(errno));
+       }
+       /* else upload succeeded */
+}
+
+
+/*
+ * protocol-based uploads (Xmodem, Ymodem, Zmodem)
+ */
+void upload(CtdlIPC *ipc, int c)
+{                              /* c = upload mode */
+       char flnm[PATH_MAX];
+       char desc[151];
+       char buf[256];
+       char tbuf[4096];
+       int xfer_pid;
+       int a, b;
+       FILE *fp, *lsfp;
+       int r;
+       int rv;
+
+       if ((room_flags & QR_UPLOAD) == 0) {
+               scr_printf("*** You cannot upload to this room.\n");
+               return;
+       }
+       /* we don't need a filename when receiving batch y/z modem */
+       if ((c == 2) || (c == 3))
+               strcpy(flnm, "x");
+       else
+               newprompt("Enter filename: ", flnm, 15);
+
+       for (a = 0; !IsEmptyStr(&flnm[a]); ++a)
+               if ((flnm[a] == '/') || (flnm[a] == '\\') || (flnm[a] == '>')
+                   || (flnm[a] == '?') || (flnm[a] == '*')
+                   || (flnm[a] == ';') || (flnm[a] == '&'))
+                       flnm[a] = '_';
+
+       /* create a temporary directory... */
+       if (mkdir(tempdir, 0700) != 0) {
+               scr_printf("*** Could not create temporary directory %s: %s\n",
+                      tempdir, strerror(errno));
+               return;
+       }
+       /* now do the transfer ... in a separate process */
+       xfer_pid = fork();
+       if (xfer_pid == 0) {
+               rv = chdir(tempdir);
+               switch (c) {
+               case 0:
+                       stty_ctdl(0);
+                       scr_printf("Receiving %s - press Ctrl-D to end.\n", flnm);
+                       fp = fopen(flnm, "w");
+                       do {
+                               b = inkey();
+                               if (b == 13) {
+                                       b = 10;
+                               }
+                               if (b != 4) {
+                                       scr_printf("%c", b);
+                                       putc(b, fp);
+                               }
+                       } while (b != 4);
+                       fclose(fp);
+                       exit(0);
+               case 1:
+                       screen_reset();
+                       stty_ctdl(3);
+                       execlp("rx", "rx", flnm, NULL);
+                       exit(1);
+               case 2:
+                       screen_reset();
+                       stty_ctdl(3);
+                       execlp("rb", "rb", NULL);
+                       exit(1);
+               case 3:
+                       screen_reset();
+                       stty_ctdl(3);
+                       execlp("rz", "rz", NULL);
+                       exit(1);
+               }
+       } else
+               do {
+                       b = ka_wait(&a);
+               } while ((b != xfer_pid) && (b != (-1)));
+       stty_ctdl(0);
+       screen_set();
+
+       if (a != 0) {
+               scr_printf("\r*** Transfer unsuccessful.\n");
+               nukedir(tempdir);
+               return;
+       }
+       scr_printf("\r*** Transfer successful.\n");
+       snprintf(buf, sizeof buf, "cd %s; ls", tempdir);
+       lsfp = popen(buf, "r");
+       if (lsfp != NULL) {
+               while (fgets(flnm, sizeof flnm, lsfp) != NULL) {
+                       flnm[strlen(flnm) - 1] = 0;     /* chop newline */
+                       snprintf(buf, sizeof buf,
+                                "Enter a short description of '%s':\n: ",
+                                flnm);
+                       newprompt(buf, desc, 150);
+                       snprintf(buf, sizeof buf, "%s/%s", tempdir, flnm);
+                       r = CtdlIPCFileUpload(ipc, flnm, desc, buf, progress, tbuf);
+                       scr_printf("%s\n", tbuf);
+               }
+               pclose(lsfp);
+       }
+       nukedir(tempdir);
+}
+
+/* 
+ * validate a user (returns 0 for successful validation, nonzero if quitting)
+ */
+int val_user(CtdlIPC *ipc, char *user, int do_validate)
+{
+       int a;
+       char cmd[256];
+       char buf[256];
+       char *resp = NULL;
+       int ax = 0;
+       char answer[2];
+       int r;                          /* IPC response code */
+
+       scr_printf("\n");
+       r = CtdlIPCGetUserRegistration(ipc, user, &resp, cmd);
+       if (r / 100 == 1) {
+               a = 0;
+               do {
+                       extract_token(buf, resp, 0, '\n', sizeof buf);
+                       remove_token(resp, 0, '\n');
+                       ++a;
+                       if (a == 1)
+                               scr_printf("User #%s - %s  ", buf, cmd);
+                       if (a == 2)
+                               scr_printf("PW: %s\n", (IsEmptyStr(buf) ? "<NOT SET>" : "<SET>") );
+                       if (a == 3)
+                               scr_printf("%s\n", buf);
+                       if (a == 4)
+                               scr_printf("%s\n", buf);
+                       if (a == 5)
+                               scr_printf("%s, ", buf);
+                       if (a == 6)
+                               scr_printf("%s ", buf);
+                       if (a == 7)
+                               scr_printf("%s\n", buf);
+                       if (a == 8)
+                               scr_printf("%s\n", buf);
+                       if (a == 9)
+                               ax = atoi(buf);
+                       if (a == 10)
+                               scr_printf("%s\n", buf);
+                       if (a == 11)
+                               scr_printf("%s\n", buf);
+               } while (!IsEmptyStr(resp));
+
+/* TODODRW: discrepancy here. Parts of the code refer to axdefs[7] as the highest
+ * but most of it limits it to axdefs[6].
+ * Webcit limits to 6 as does the code here but there are 7 in axdefs.h
+ */
+               scr_printf("Current access level: %d (%s)\n", ax, axdefs[ax]);
+       } else {
+               scr_printf("%s\n%s\n", user, &cmd[4]);
+       }
+       if (resp) free(resp);
+
+       if (do_validate) {
+               /* now set the access level */
+               while(1) {
+                       sprintf(answer, "%d", ax);
+                       strprompt("New access level (? for help, q to quit)",
+                               answer, 1);
+                       if ((answer[0] >= '0') && (answer[0] <= '6')) {
+                               ax = atoi(answer);
+                               r = CtdlIPCValidateUser(ipc, user, ax, cmd);
+                               if (r / 100 != 2)
+                               scr_printf("%s\n\n", cmd);
+                               return(0);
+                       }
+                       if (tolower(answer[0]) == 'q') {
+                               scr_printf("*** Aborted.\n\n");
+                               return(1);
+                       }
+                       if (answer[0] == '?') {
+                               scr_printf("Available access levels:\n");
+                               for (a=0; a<7; ++a) {
+                                       scr_printf("%d - %s\n",
+                                               a, axdefs[a]);
+                               }
+                       }
+               }
+       }
+       return(0);
+}
+
+
+void validate(CtdlIPC *ipc)
+{                              /* validate new users */
+       char cmd[256];
+       char buf[256];
+       int finished = 0;
+       int r;                          /* IPC response code */
+
+       do {
+               r = CtdlIPCNextUnvalidatedUser(ipc, cmd);
+               if (r / 100 != 3)
+                       finished = 1;
+               if (r / 100 == 2)
+                       scr_printf("%s\n", cmd);
+               if (r / 100 == 3) {
+                       extract_token(buf, cmd, 0, '|', sizeof buf);
+                       if (val_user(ipc, buf, 1) != 0) finished = 1;
+               }
+       } while (finished == 0);
+}
+
+void subshell(void)
+{
+       int a, b;
+
+       screen_reset();
+       stty_ctdl(SB_RESTORE);
+       a = fork();
+       if (a == 0) {
+               signal(SIGINT, SIG_DFL);
+               signal(SIGQUIT, SIG_DFL);
+               execlp(getenv("SHELL"), getenv("SHELL"), NULL);
+               err_printf("Could not open a shell: %s\n", strerror(errno));
+               exit(errno);
+       }
+       do {
+               b = ka_wait(NULL);
+       } while ((a != b) && (a != (-1)));
+       stty_ctdl(0);
+       screen_set();
+}
+
+/*
+ * <.A>ide <F>ile <D>elete command
+ */
+void deletefile(CtdlIPC *ipc)
+{
+       char filename[32];
+       char buf[256];
+
+       newprompt("Filename: ", filename, 31);
+       if (IsEmptyStr(filename))
+               return;
+       CtdlIPCDeleteFile(ipc, filename, buf);
+       err_printf("%s\n", buf);
+}
+
+
+/*
+ * <.A>ide <F>ile <M>ove command
+ */
+void movefile(CtdlIPC *ipc)
+{
+       char filename[64];
+       char newroom[ROOMNAMELEN];
+       char buf[256];
+
+       newprompt("Filename: ", filename, 63);
+       if (IsEmptyStr(filename))
+               return;
+       newprompt("Enter target room: ", newroom, ROOMNAMELEN - 1);
+       CtdlIPCMoveFile(ipc, filename, newroom, buf);
+       err_printf("%s\n", buf);
+}
+
+
+/* 
+ * list of users who have filled out a bio
+ */
+void list_bio(CtdlIPC *ipc)
+{
+       char buf[256];
+       char *resp = NULL;
+       int pos = 1;
+       int r;                  /* IPC response code */
+
+       r = CtdlIPCListUsersWithBios(ipc, &resp, buf);
+       if (r / 100 != 1) {
+               pprintf("%s\n", buf);
+               return;
+       }
+       while (resp && !IsEmptyStr(resp)) {
+               extract_token(buf, resp, 0, '\n', sizeof buf);
+               remove_token(resp, 0, '\n');
+               if ((pos + strlen(buf) + 5) > screenwidth) {
+                       pprintf("\n");
+                       pos = 1;
+               }
+               pprintf("%s, ", buf);
+               pos = pos + strlen(buf) + 2;
+       }
+       pprintf("%c%c  \n\n", 8, 8);
+       if (resp) free(resp);
+}
+
+
+/*
+ * read bio
+ */
+void read_bio(CtdlIPC *ipc)
+{
+       char who[256];
+       char buf[256];
+       char *resp = NULL;
+       int r;                  /* IPC response code */
+
+       do {
+               newprompt("Read bio for who ('?' for list) : ", who, 25);
+               pprintf("\n");
+               if (!strcmp(who, "?"))
+                       list_bio(ipc);
+       } while (!strcmp(who, "?"));
+
+       r = CtdlIPCGetBio(ipc, who, &resp, buf);
+       if (r / 100 != 1) {
+               pprintf("%s\n", buf);
+               return;
+       }
+       while (!IsEmptyStr(resp)) {
+               extract_token(buf, resp, 0, '\n', sizeof buf);
+               remove_token(resp, 0, '\n');
+               pprintf("%s\n", buf);
+       }
+       if (resp) free(resp);
+}
+
+
+
diff --git a/citadel/textclient/routines2.h b/citadel/textclient/routines2.h
new file mode 100644 (file)
index 0000000..ba1c179
--- /dev/null
@@ -0,0 +1,15 @@
+/* $Id$ */
+void updatels(CtdlIPC *ipc);
+void updatelsa(CtdlIPC *ipc);
+void movefile(CtdlIPC *ipc);
+void deletefile(CtdlIPC *ipc);
+void netsendfile(CtdlIPC *ipc);
+void entregis(CtdlIPC *ipc);
+void subshell(void);
+void upload(CtdlIPC *ipc, int c);
+void cli_upload(CtdlIPC *ipc);
+void validate(CtdlIPC *ipc);
+void read_bio(CtdlIPC *ipc);
+void cli_image_upload(CtdlIPC *ipc, char *keyname);
+int room_prompt(unsigned int qrflags);
+int val_user(CtdlIPC *ipc, char *user, int do_validate);
diff --git a/citadel/textclient/screen.c b/citadel/textclient/screen.c
new file mode 100644 (file)
index 0000000..a1d8abd
--- /dev/null
@@ -0,0 +1,558 @@
+/* $Id$ */
+
+/*
+ * Handle full-screen curses stuff
+ */
+
+#include <stdlib.h>
+#include <unistd.h>
+#include <stdio.h>
+#include <signal.h>
+#include <string.h>
+#include <stdarg.h>
+#include <sys/types.h>
+#include "sysdep.h"
+#ifdef HAVE_VW_PRINTW
+#define _vwprintw vw_printw
+#else
+/* SYSV style curses (Solaris, etc.) */
+#define _vwprintw vwprintw
+#endif
+#ifndef HAVE_SNPRINTF
+#include "snprintf.h"
+#endif
+#include "citadel.h"
+#include "citadel_ipc.h"
+#include "citadel_decls.h"
+#include "commands.h"
+#include "screen.h"
+
+#if defined(HAVE_CURSES_H) && !defined(DISABLE_CURSES)
+static SCREEN *myscreen = NULL;
+static WINDOW *mainwindow = NULL;
+static WINDOW *statuswindow = NULL;
+
+char rc_screen;
+#endif
+char arg_screen;
+
+extern int screenheight;
+extern int screenwidth;
+extern int rc_ansi_color;
+extern void check_screen_dims(void);
+
+void do_keepalive(void);
+
+
+int is_curses_enabled(void) {
+#if defined(HAVE_CURSES_H) && !defined(DISABLE_CURSES)
+       return mainwindow != NULL;
+#else
+       return 0;
+#endif
+}
+
+/*
+ * status_line() is a convenience function for writing a "typical"
+ * status line to the window.
+ */
+void status_line(const char *humannode, const char *site_location,
+                const char *room_name, int secure, int newmailcount)
+{
+#if defined(HAVE_CURSES_H) && !defined(DISABLE_CURSES)
+       if (statuswindow) {
+               if (secure) {
+                       sln_printf("Encrypted ");
+                       waddch(statuswindow, ACS_VLINE);
+                       waddch(statuswindow, ' ');
+               }
+               if (room_name)
+                       sln_printf("%s on ", room_name);
+               if (humannode)
+                       sln_printf("%s ", humannode);
+               if (newmailcount > 0) {
+                       waddch(statuswindow, ACS_VLINE);
+                       sln_printf(" %d new mail ", newmailcount);
+               }
+               sln_printf("\n");
+       }
+#endif /* HAVE_CURSES_H */
+}
+
+
+/*
+ * Display a 3270-style "wait" indicator at the bottom of the screen
+ */
+#if defined(HAVE_CURSES_H) && !defined(DISABLE_CURSES)
+void wait_indicator(int state) {
+
+       if (statuswindow && !isendwin()) {
+
+               mvwinch(statuswindow, 0, screenwidth - 2);
+               switch (state) {
+               default:
+               case 0:         /* Idle */
+                       waddch(statuswindow, ' ');
+                       break;
+               case 1:         /* Waiting */
+                       waddch(statuswindow, 'X');
+                       break;
+               case 2:         /* Receiving */
+                       waddch(statuswindow, '<');
+                       break;
+               case 3:         /* Sending */
+                       waddch(statuswindow, '>');
+                       break;
+               }
+               waddch(statuswindow, '\r');
+               wrefresh(statuswindow);
+               wrefresh(mainwindow);   /* this puts the cursor back */
+       }
+}
+#else
+void wait_indicator(int state) {}
+#endif
+
+
+/*
+ * Initialize the screen.  If newterm() fails, myscreen will be NULL and
+ * further handlers will assume we should be in line mode.
+ */
+void screen_new(void)
+{
+#if defined(HAVE_CURSES_H) && !defined(DISABLE_CURSES)
+       if (arg_screen != RC_NO && rc_screen != RC_NO)
+               myscreen = newterm(NULL, stdout, stdin);
+       if (myscreen) {
+               cbreak();
+               noecho();
+               nonl();
+               intrflush(stdscr, FALSE);
+               keypad(stdscr, TRUE);
+               /* Setup all our colors */
+               start_color();
+               if (rc_ansi_color)
+                       enable_color = 1;
+               /*init_pair(DIM_BLACK, COLOR_BLACK, COLOR_BLACK);*/
+               init_pair(DIM_RED, COLOR_RED, COLOR_BLACK);
+               init_pair(DIM_GREEN, COLOR_GREEN, COLOR_BLACK);
+               init_pair(DIM_YELLOW, COLOR_YELLOW, COLOR_BLACK);
+               init_pair(DIM_BLUE, COLOR_BLUE, COLOR_BLACK);
+               init_pair(DIM_MAGENTA, COLOR_MAGENTA, COLOR_BLACK);
+               init_pair(DIM_CYAN, COLOR_CYAN, COLOR_BLACK);
+               init_pair(DIM_WHITE, COLOR_WHITE, COLOR_BLACK);
+
+               if (COLOR_PAIRS > 8)
+                       init_pair(8, COLOR_WHITE, COLOR_BLUE);
+       } else
+#endif /* HAVE_CURSES_H */
+       {
+               send_ansi_detect();
+               look_for_ansi();
+               cls(0);
+               color(DIM_WHITE);
+       }
+       screen_set();
+       windows_new();
+}
+
+
+/*
+ * Kill the screen completely (used at exit).  It is safe to call this
+ * function more than once.
+ */
+void screen_delete(void)
+{
+       windows_delete();
+       screen_reset();
+#if defined(HAVE_CURSES_H) && !defined(DISABLE_CURSES)
+       if (myscreen) {
+               delscreen(myscreen);
+       }
+       myscreen = NULL;
+#endif
+}
+
+/*
+ * Beep.
+ */
+void ctdl_beep(void) {
+#if defined(HAVE_CURSES_H) && !defined(DISABLE_CURSES)
+       if (myscreen)
+               beep();
+       else
+#endif
+       putc(7, stdout);
+}
+       
+
+
+/*
+ * Set screen/IO parameters, e.g. at start of program or return from external
+ * program run.
+ */
+int screen_set(void)
+{
+#if defined(HAVE_CURSES_H) && !defined(DISABLE_CURSES)
+       if (myscreen) {
+               set_term(myscreen);
+               wrefresh(curscr);
+               return 1;
+       }
+#endif /* HAVE_CURSES_H */
+       return 0;
+}
+
+
+/*
+ * Reset screen/IO parameters, e.g. at exit or fork of external program.
+ */
+int screen_reset(void)
+{
+#if defined(HAVE_CURSES_H) && !defined(DISABLE_CURSES)
+       if (myscreen) {
+               if (!isendwin()) endwin();
+               return 1;
+       }
+#endif /* HAVE_CURSES_H */
+       return 0;
+}
+
+
+/*
+ * scr_printf() outputs to the main window (or screen if not in curses)
+ */
+int scr_printf(char *fmt, ...)
+{
+       va_list ap;
+       register int retval;
+
+       va_start(ap, fmt);
+#if defined(HAVE_CURSES_H) && !defined(DISABLE_CURSES)
+       if (mainwindow) {
+               retval = _vwprintw(mainwindow, fmt, ap);
+       } else
+#endif
+               retval = vprintf(fmt, ap);
+       va_end(ap);
+       return retval;
+}
+
+
+/*
+ * err_printf() outputs to error status window (or stderr if not in curses)
+ */
+int err_printf(char *fmt, ...)
+{
+       va_list ap;
+       register int retval;
+
+       va_start(ap, fmt);
+#if defined(HAVE_CURSES_H) && !defined(DISABLE_CURSES)
+       if (mainwindow) {               /* FIXME: direct to error window */
+               retval = _vwprintw(mainwindow, fmt, ap);
+               if (fmt[strlen(fmt) - 1] == '\n')
+                       wrefresh(mainwindow);
+       } else
+#endif
+               retval = vfprintf(stderr, fmt, ap);
+       va_end(ap);
+       return retval;
+}
+
+
+/*
+ * sln_printf() outputs to error status window (or stderr if not in curses)
+ */
+int sln_printf(char *fmt, ...)
+{
+       va_list ap;
+       register int retval;
+#if defined(HAVE_CURSES_H) && !defined(DISABLE_CURSES)
+       static char buf[4096];
+#endif
+
+       va_start(ap, fmt);
+#if defined(HAVE_CURSES_H) && !defined(DISABLE_CURSES)
+       if (statuswindow) {
+               register char *i;
+               
+               retval = vsnprintf(buf, 4096, fmt, ap);
+               for (i = buf; *i; i++) {
+                       if (*i == '\r' || *i == '\n')
+                               wclrtoeol(statuswindow);
+                       sln_putc(*i);
+                       if (*i == '\r' || *i == '\n') {
+                               wrefresh(statuswindow);
+                               mvwinch(statuswindow, 0, 0);
+                       }
+               }
+       } else
+#endif
+               retval = vprintf(fmt, ap);
+       va_end(ap);
+       return retval;
+}
+
+
+/*
+ * sln_printf_if() outputs to status window, no output if not in curses
+ */
+int sln_printf_if(char *fmt, ...)
+{
+       register int retval = 1;
+#if defined(HAVE_CURSES_H) && !defined(DISABLE_CURSES)
+       static char buf[4096];
+       va_list ap;
+
+       va_start(ap, fmt);
+       if (statuswindow) {
+               register char *i;
+               
+               retval = vsnprintf(buf, 4096, fmt, ap);
+               for (i = buf; *i; i++) {
+                       if (*i == '\r' || *i == '\n')
+                               wclrtoeol(statuswindow);
+                       sln_putc(*i);
+                       if (*i == '\r' || *i == '\n') {
+                               wrefresh(statuswindow);
+                               mvwinch(statuswindow, 0, 0);
+                       }
+               }
+       }
+       va_end(ap);
+#endif
+       return retval;
+}
+
+
+int scr_getc(int delay)
+{
+  unsigned char buf;
+
+#if defined(HAVE_CURSES_H) && !defined(DISABLE_CURSES)
+       if (mainwindow) {
+               wtimeout(mainwindow, delay);
+               return wgetch(mainwindow);
+       }
+#endif
+
+       buf = '\0';
+       if (!read (0, &buf, 1))
+               logoff(NULL, 3);
+       return buf;
+}
+
+/* the following is unused and looks broken, but there may
+   be some input problems still lurking in curses mode, so
+   i'll leave it blocked out for now for informational
+   purposes. */
+#if 0
+int scr_blockread(void)
+  {
+    int a = 0;
+#if defined(HAVE_CURSES_H) && !defined(DISABLE_CURSES)
+    wtimeout(mainwindow, S_KEEPALIVE); 
+    while (1)
+      {
+        do_keepalive();
+        a = wgetch(mainwindow); /* will block for food */
+        if (a != ERR)
+          break;
+        /* a = scr_getc(); */
+      }
+#endif
+    return a;
+  }
+#endif /* 0 */
+
+/*
+ * scr_putc() outputs a single character
+ */
+int scr_putc(int c)
+{
+#if defined(HAVE_CURSES_H) && !defined(DISABLE_CURSES)
+       if (mainwindow) {
+               if (c == 7) beep();
+               if (waddch(mainwindow, c) != OK)
+                       logoff(NULL, 3);
+               return c;
+       }
+#endif
+       if (putc(c, stdout) == EOF)
+               logoff(NULL, 3);
+       return c;
+}
+
+
+int sln_putc(int c)
+{
+#if defined(HAVE_CURSES_H) && !defined(DISABLE_CURSES)
+       if (statuswindow)
+               return ((waddch(statuswindow, c) == OK) ? c : EOF);
+#endif
+       return putc(c, stdout);
+}
+
+
+int sln_putc_if(int c)
+{
+#if defined(HAVE_CURSES_H) && !defined(DISABLE_CURSES)
+       if (statuswindow)
+               return ((waddch(statuswindow, c) == OK) ? c : EOF);
+#endif
+       return 1;
+}
+
+
+/*
+ * scr_color() sets the window color for mainwindow
+ */
+int scr_color(int colornum)
+{
+#if defined(HAVE_CURSES_H) && !defined(DISABLE_CURSES)
+       if (mainwindow) {
+#ifdef HAVE_WCOLOR_SET
+               wcolor_set(mainwindow, (colornum & 7), NULL);
+#else
+               wattron(mainwindow, COLOR_PAIR((colornum & 7)));
+#endif
+               if (colornum & 8) {
+                       wattron(mainwindow, A_BOLD);
+               } else {
+                       wattroff(mainwindow, A_BOLD);
+               }
+               return 1;
+       }
+#endif
+       return 0;
+}
+
+
+void scr_flush(void)
+{
+#if defined(HAVE_CURSES_H) && !defined(DISABLE_CURSES)
+       if (mainwindow)
+               wrefresh(mainwindow);
+       else
+#endif
+               fflush(stdout);
+}
+
+
+void err_flush(void)
+{
+#if defined(HAVE_CURSES_H) && !defined(DISABLE_CURSES)
+       if (mainwindow)         /* FIXME: error status window needed */
+               wrefresh(mainwindow);
+       else
+#endif
+               fflush(stderr);
+}
+
+
+void sln_flush(void)
+{
+#if defined(HAVE_CURSES_H) && !defined(DISABLE_CURSES)
+       if (statuswindow)
+               wrefresh(statuswindow);
+       else
+#endif
+               fflush(stdout);
+}
+
+static volatile int caught_sigwinch = 0;
+
+/*
+ * this is not supposed to be called from a signal handler.
+ */
+int scr_set_windowsize(CtdlIPC* ipc)
+{
+#if defined(HAVE_CURSES_H) && !defined(DISABLE_CURSES)
+       if (mainwindow && caught_sigwinch) {
+               caught_sigwinch = 0;
+#ifdef HAVE_RESIZETERM
+               resizeterm(screenheight + 1, screenwidth);
+#endif
+#ifdef HAVE_WRESIZE
+               wresize(mainwindow, screenheight, screenwidth);
+               wresize(statuswindow, 1, screenwidth);
+#endif
+               mvwin(statuswindow, screenheight, 0);
+               status_line(ipc->ServInfo.humannode, ipc->ServInfo.site_location,
+                               room_name, secure, -1);
+               wnoutrefresh(mainwindow);
+               wnoutrefresh(statuswindow);
+               doupdate();
+               return 1;
+       }
+#endif /* HAVE_CURSES_H */
+       return 0;
+}
+
+/*
+ * scr_winch() handles window size changes from SIGWINCH
+ * resizes all our windows for us
+ */
+RETSIGTYPE scr_winch(int signum)
+{
+       /* if we receive this signal, we must be running
+          in a terminal that supports resizing. */
+       have_xterm = 1;
+       caught_sigwinch = 1;
+       check_screen_dims();
+       signal(SIGWINCH, scr_winch);
+}
+
+
+/*
+ * Initialize the window(s) we will be using.
+ */
+void windows_new(void)
+{
+#if defined(HAVE_CURSES_H) && !defined(DISABLE_CURSES)
+       register int x, y;
+
+       if (myscreen) {
+               getmaxyx(stdscr, y, x);
+               mainwindow = newwin(y - 1, x, 0, 0);
+               screenwidth = x;
+               screenheight = y - 1;
+               immedok(mainwindow, FALSE);
+               leaveok(mainwindow, FALSE);
+               scrollok(mainwindow, TRUE);
+               statuswindow = newwin(1, x, y - 1, 0);
+
+               if (COLOR_PAIRS > 8)
+                       wbkgdset(statuswindow, ' ' | COLOR_PAIR(8));
+               else
+                       wbkgdset(statuswindow, ' ' | COLOR_PAIR(DIM_WHITE));
+
+               werase(statuswindow);
+               immedok(statuswindow, FALSE);
+               leaveok(statuswindow, FALSE);
+               scrollok(statuswindow, FALSE);
+               wrefresh(statuswindow);
+       }
+#else /* HAVE_CURSES_H */
+
+#endif /* HAVE_CURSES_H */
+}
+
+
+/*
+ * Deinitialize the window(s) we were using (at exit).
+ */
+void windows_delete(void)
+{
+#if defined(HAVE_CURSES_H) && !defined(DISABLE_CURSES)
+       if (mainwindow)
+               delwin(mainwindow);
+       mainwindow = NULL;
+       if (statuswindow)
+               delwin(statuswindow);
+       statuswindow = NULL;
+#else /* HAVE_CURSES_H */
+
+#endif /* HAVE_CURSES_H */
+}
diff --git a/citadel/textclient/screen.h b/citadel/textclient/screen.h
new file mode 100644 (file)
index 0000000..0297503
--- /dev/null
@@ -0,0 +1,41 @@
+/* $Id$ */
+
+/* client code may need the ERR define: */
+
+#ifndef DISABLE_CURSES
+#ifdef HAVE_NCURSES_H
+#include <ncurses.h>
+#elif defined(HAVE_CURSES_H)
+#include <curses.h>
+#endif
+#endif
+
+void status_line(const char *humannode, const char *site_location,
+                const char *room_name, int secure, int newmailcount);
+void screen_new(void);
+void screen_delete(void);
+int screen_set(void);
+int screen_reset(void);
+int scr_printf(char *fmt, ...);
+int err_printf(char *fmt, ...);
+int sln_printf(char *fmt, ...);
+int sln_printf_if(char *fmt, ...);
+
+#define SCR_NOBLOCK 0
+#define SCR_BLOCK -1
+int scr_getc(int delay);
+
+int scr_putc(int c);
+int sln_putc(int c);
+int scr_color(int colornum);
+void scr_flush(void);
+void err_flush(void);
+void sln_flush(void);
+int scr_set_windowsize(CtdlIPC* ipc);
+void windows_new(void);
+void windows_delete(void);
+int scr_blockread(void);
+int is_curses_enabled(void);
+RETSIGTYPE scr_winch(int signum);
+void wait_indicator(int state);
+void ctdl_beep(void);
diff --git a/citadel/textclient/tuiconfig.c b/citadel/textclient/tuiconfig.c
new file mode 100644 (file)
index 0000000..a6cfde3
--- /dev/null
@@ -0,0 +1,1245 @@
+/* $Id$
+ *
+ * Configuration screens that are part of the text mode client.
+ *
+ */
+
+#include <stdlib.h>
+#include <unistd.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+#include <stdio.h>
+#include <ctype.h>
+#include <string.h>
+#include <limits.h>
+
+#if TIME_WITH_SYS_TIME
+# include <sys/time.h>
+# include <time.h>
+#else
+# if HAVE_SYS_TIME_H
+#  include <sys/time.h>
+# else
+#  include <time.h>
+# endif
+#endif
+
+#include <signal.h>
+#include <pwd.h>
+#include <errno.h>
+#include <stdarg.h>
+#include <libcitadel.h>
+#include "sysdep.h"
+#include "citadel.h"
+#include "citadel_ipc.h"
+#include "citadel_decls.h"
+#include "tuiconfig.h"
+#include "messages.h"
+#include "routines.h"
+#include "commands.h"
+#ifndef HAVE_SNPRINTF
+#include "snprintf.h"
+#endif
+#include "screen.h"
+
+/* work around solaris include files */
+#ifdef reg
+#undef reg
+#endif
+
+extern char temp[];
+extern char tempdir[];
+extern char *axdefs[8];
+extern long highest_msg_read;
+extern long maxmsgnum;
+extern unsigned room_flags;
+extern int screenwidth;
+
+
+/* 
+ * General system configuration command
+ */
+void do_system_configuration(CtdlIPC *ipc)
+{
+
+#define NUM_CONFIGS 67
+
+       char buf[256];
+       char sc[NUM_CONFIGS][256];
+       char *resp = NULL;
+       struct ExpirePolicy *site_expirepolicy = NULL;
+       struct ExpirePolicy *mbx_expirepolicy = NULL;
+       int a;
+       int logpages = 0;
+       int r;                  /* IPC response code */
+       int server_configs = 0;
+
+       /* Clear out the config buffers */
+       memset(&sc[0][0], 0, sizeof(sc));
+
+       /* Fetch the current config */
+       r = CtdlIPCGetSystemConfig(ipc, &resp, buf);
+       if (r / 100 == 1) {
+               server_configs = num_tokens(resp, '\n');
+               for (a=0; a<server_configs; ++a) {
+                       if (a < NUM_CONFIGS) {
+                               extract_token(&sc[a][0], resp, a, '\n', sizeof sc[a]);
+                       }
+               }
+       }
+       if (resp) free(resp);
+       resp = NULL;
+       /* Fetch the expire policy (this will silently fail on old servers,
+        * resulting in "default" policy)
+        */
+       r = CtdlIPCGetMessageExpirationPolicy(ipc, 2, &site_expirepolicy, buf);
+       r = CtdlIPCGetMessageExpirationPolicy(ipc, 3, &mbx_expirepolicy, buf);
+
+       /* Identification parameters */
+
+       strprompt("Node name", &sc[0][0], 15);
+       strprompt("Fully qualified domain name", &sc[1][0], 63);
+       strprompt("Human readable node name", &sc[2][0], 20);
+       strprompt("Telephone number", &sc[3][0], 15);
+       strprompt("Geographic location of this system", &sc[12][0], 31);
+       strprompt("Name of system administrator", &sc[13][0], 25);
+       strprompt("Paginator prompt", &sc[10][0], 79);
+
+       /* Security parameters */
+
+       snprintf(sc[7], sizeof sc[7], "%d", (boolprompt(
+               "Require registration for new users",
+               atoi(&sc[7][0]))));
+       snprintf(sc[29], sizeof sc[29], "%d", (boolprompt(
+               "Disable self-service user account creation",
+               atoi(&sc[29][0]))));
+       strprompt("Initial access level for new users", &sc[6][0], 1);
+       strprompt("Access level required to create rooms", &sc[19][0], 1);
+       snprintf(sc[4], sizeof sc[4], "%d", (boolprompt(
+               "Automatically give room aide privs to a user who creates a private room",
+               atoi(&sc[4][0]))));
+
+       snprintf(sc[8], sizeof sc[8], "%d", (boolprompt(
+               "Automatically move problem user messages to twit room",
+               atoi(&sc[8][0]))));
+
+       strprompt("Name of twit room", &sc[9][0], ROOMNAMELEN);
+       snprintf(sc[11], sizeof sc[11], "%d", (boolprompt(
+               "Restrict Internet mail to only those with that privilege",
+               atoi(&sc[11][0]))));
+       snprintf(sc[26], sizeof sc[26], "%d", (boolprompt(
+               "Allow Aides to Zap (forget) rooms",
+               atoi(&sc[26][0]))));
+
+       if (!IsEmptyStr(&sc[18][0])) {
+               logpages = 1;
+       }
+       else {
+               logpages = 0;
+       }
+       logpages = boolprompt("Log all instant messages", logpages);
+       if (logpages) {
+               strprompt("Name of logging room", &sc[18][0], ROOMNAMELEN);
+       }
+       else {
+               sc[18][0] = 0;
+       }
+
+       /* Commented out because this setting isn't really appropriate to
+        * change while the server is running.
+        *
+        * snprintf(sc[52], sizeof sc[52], "%d", (boolprompt(
+        *      "Use system authentication",
+        *      atoi(&sc[52][0]))));
+        */
+
+       /* Server tuning */
+
+       strprompt("Server connection idle timeout (in seconds)", &sc[5][0], 4);
+       strprompt("Maximum concurrent sessions", &sc[14][0], 4);
+       strprompt("Maximum message length", &sc[20][0], 20);
+       strprompt("Minimum number of worker threads", &sc[21][0], 3);
+       strprompt("Maximum number of worker threads", &sc[22][0], 3);
+       snprintf(sc[43], sizeof sc[43], "%d", (boolprompt(
+               "Automatically delete committed database logs",
+               atoi(&sc[43][0]))));
+
+       strprompt("Server IP address (0.0.0.0 for 'any')", &sc[37][0], 15);
+       strprompt("POP3 server port (-1 to disable)", &sc[23][0], 5);
+       strprompt("POP3S server port (-1 to disable)", &sc[40][0], 5);
+       strprompt("IMAP server port (-1 to disable)", &sc[27][0], 5);
+       strprompt("IMAPS server port (-1 to disable)", &sc[39][0], 5);
+       strprompt("SMTP MTA server port (-1 to disable)", &sc[24][0], 5);
+       strprompt("SMTP MSA server port (-1 to disable)", &sc[38][0], 5);
+       strprompt("SMTPS server port (-1 to disable)", &sc[41][0], 5);
+       strprompt("Postfix TCP Dictionary Port server port (-1 to disable)", &sc[50][0], 5);
+       strprompt("ManageSieve server port (-1 to disable)", &sc[51][0], 5);
+
+       strprompt("XMPP (Jabber) client to server port (-1 to disable)", &sc[62][0], 5);
+       /* No prompt because we don't implement this service yet, it's just a placeholder */
+       /* strprompt("XMPP (Jabber) server to server port (-1 to disable)", &sc[63][0], 5); */
+
+       /* This logic flips the question around, because it's one of those
+        * situations where 0=yes and 1=no
+        */
+       a = atoi(sc[25]);
+       a = (a ? 0 : 1);
+       a = boolprompt("Correct forged From: lines during authenticated SMTP",
+               a);
+       a = (a ? 0 : 1);
+       snprintf(sc[25], sizeof sc[25], "%d", a);
+
+       snprintf(sc[66], sizeof sc[66], "%d", (boolprompt(
+               "Flag messages as spam instead of rejecting",
+               atoi(&sc[66][0]))));
+
+       /* This logic flips the question around, because it's one of those
+        * situations where 0=yes and 1=no
+        */
+       a = atoi(sc[61]);
+       a = (a ? 0 : 1);
+       a = boolprompt("Force IMAP posts in public rooms to be from the user who submitted them", a);
+       a = (a ? 0 : 1);
+       snprintf(sc[61], sizeof sc[61], "%d", a);
+
+       snprintf(sc[45], sizeof sc[45], "%d", (boolprompt(
+               "Allow unauthenticated SMTP clients to spoof my domains",
+               atoi(&sc[45][0]))));
+       snprintf(sc[57], sizeof sc[57], "%d", (boolprompt(
+               "Perform RBL checks at greeting instead of after RCPT",
+               atoi(&sc[57][0]))));
+       snprintf(sc[44], sizeof sc[44], "%d", (boolprompt(
+               "Instantly expunge deleted IMAP messages",
+               atoi(&sc[44][0]))));
+
+       /* LDAP settings */
+       if (ipc->ServInfo.supports_ldap) {
+               a = strlen(&sc[32][0]);
+               a = (a ? 1 : 0);        /* Set only to 1 or 0 */
+               a = boolprompt("Do you want to configure LDAP authentication?", a);
+               if (a) {
+                       strprompt("Host name of LDAP server",
+                               &sc[32][0], 127);
+                       strprompt("Port number of LDAP service",
+                               &sc[33][0], 5);
+                       strprompt("Base DN", &sc[34][0], 255);
+                       strprompt("Bind DN (or blank for anonymous bind)", &sc[35][0], 255);
+                       strprompt("Password for bind DN (or blank for anonymous bind)", &sc[36][0], 255);
+               }
+               else {
+                       strcpy(&sc[32][0], "");
+               }
+       }
+
+       /* Expiry settings */
+       strprompt("Default user purge time (days)", &sc[16][0], 5);
+       strprompt("Default room purge time (days)", &sc[17][0], 5);
+
+       /* Angels and demons dancing in my head... */
+       do {
+               snprintf(buf, sizeof buf, "%d", site_expirepolicy->expire_mode);
+               strprompt("System default message expire policy (? for list)",
+                         buf, 1);
+               if (buf[0] == '?') {
+                       scr_printf("\n"
+                               "1. Never automatically expire messages\n"
+                               "2. Expire by message count\n"
+                               "3. Expire by message age\n");
+               }
+       } while ((buf[0] < '1') || (buf[0] > '3'));
+       site_expirepolicy->expire_mode = buf[0] - '0';
+
+       /* ...lunatics and monsters underneath my bed */
+       if (site_expirepolicy->expire_mode == 2) {
+               snprintf(buf, sizeof buf, "%d", site_expirepolicy->expire_value);
+               strprompt("Keep how many messages online?", buf, 10);
+               site_expirepolicy->expire_value = atol(buf);
+       }
+       if (site_expirepolicy->expire_mode == 3) {
+               snprintf(buf, sizeof buf, "%d", site_expirepolicy->expire_value);
+               strprompt("Keep messages for how many days?", buf, 10);
+               site_expirepolicy->expire_value = atol(buf);
+       }
+
+       /* Media messiahs preying on my fears... */
+       do {
+               snprintf(buf, sizeof buf, "%d", mbx_expirepolicy->expire_mode);
+               strprompt("Mailbox default message expire policy (? for list)",
+                         buf, 1);
+               if (buf[0] == '?') {
+                       scr_printf("\n"
+                               "0. Go with the system default\n"
+                               "1. Never automatically expire messages\n"
+                               "2. Expire by message count\n"
+                               "3. Expire by message age\n");
+               }
+       } while ((buf[0] < '0') || (buf[0] > '3'));
+       mbx_expirepolicy->expire_mode = buf[0] - '0';
+
+       /* ...Pop culture prophets playing in my ears */
+       if (mbx_expirepolicy->expire_mode == 2) {
+               snprintf(buf, sizeof buf, "%d", mbx_expirepolicy->expire_value);
+               strprompt("Keep how many messages online?", buf, 10);
+               mbx_expirepolicy->expire_value = atol(buf);
+       }
+       if (mbx_expirepolicy->expire_mode == 3) {
+               snprintf(buf, sizeof buf, "%d", mbx_expirepolicy->expire_value);
+               strprompt("Keep messages for how many days?", buf, 10);
+               mbx_expirepolicy->expire_value = atol(buf);
+       }
+
+       strprompt("How often to run network jobs (in seconds)", &sc[28][0], 5);
+       strprompt("Default frequency to run POP3 collection (in seconds)", &sc[64][0], 5);
+       strprompt("Fastest frequency to run POP3 collection (in seconds)", &sc[65][0], 5);
+       strprompt("Hour to run purges (0-23)", &sc[31][0], 2);
+       snprintf(sc[42], sizeof sc[42], "%d", (boolprompt(
+               "Enable full text search index (warning: resource intensive)",
+               atoi(&sc[42][0]))));
+
+       snprintf(sc[46], sizeof sc[46], "%d", (boolprompt(
+               "Perform journaling of email messages",
+               atoi(&sc[46][0]))));
+       snprintf(sc[47], sizeof sc[47], "%d", (boolprompt(
+               "Perform journaling of non-email messages",
+               atoi(&sc[47][0]))));
+       if ( (atoi(&sc[46][0])) || (atoi(&sc[47][0])) ) {
+               strprompt("Email destination of journalized messages",
+                       &sc[48][0], 127);
+       }
+
+       /* Funambol push stuff */
+       int yes_funambol = 0;
+       if (strlen(sc[53]) > 0) yes_funambol = 1;
+       yes_funambol = boolprompt("Connect to an external Funambol sync server", yes_funambol);
+       if (yes_funambol) {
+               strprompt("Funambol server (blank to disable)", &sc[53][0], 63);
+               strprompt("Funambol server port", &sc[54][0], 5);
+               strprompt("Funambol sync source", &sc[55][0], 63);
+               strprompt("Funambol authentication details (user:pass in Base64)", &sc[56][0],63);
+       }
+       else {
+               sc[53][0] = 0;
+               sc[54][0] = 0;
+               sc[55][0] = 0;
+               sc[56][0] = 0;
+       }
+
+       /* External pager stuff */
+       int yes_pager = 0;
+       if (strlen(sc[60]) > 0) yes_pager = 1;
+       yes_pager = boolprompt("Configure an external pager tool", yes_pager);
+       if (yes_pager) {
+               strprompt("External pager tool", &sc[60][0], 255);
+       }
+       else {
+               sc[60][0] = 0;
+       }
+
+       /* Master user account */
+       int yes_muacct = 0;
+       if (strlen(sc[58]) > 0) yes_muacct = 1;
+       yes_muacct = boolprompt("Enable a 'master user' account", yes_muacct);
+       if (yes_muacct) {
+               strprompt("Master user name", &sc[58][0], 31);
+               strprompt("Master user password", &sc[59][0], -31);
+       }
+       else {
+               strcpy(&sc[58][0], "");
+               strcpy(&sc[59][0], "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx");
+       }
+
+       /* Save it */
+       scr_printf("Save this configuration? ");
+       if (yesno()) {
+               r = 1;
+               for (a = 0; a < NUM_CONFIGS; a++)
+                       r += 1 + strlen(sc[a]);
+               resp = (char *)calloc(1, r);
+               if (!resp) {
+                       err_printf("Can't save config - out of memory!\n");
+                       logoff(ipc, 1);
+               }
+               for (a = 0; a < NUM_CONFIGS; a++) {
+                       strcat(resp, sc[a]);
+                       strcat(resp, "\n");
+               }
+               r = CtdlIPCSetSystemConfig(ipc, resp, buf);
+               if (r / 100 != 4) {
+                       err_printf("%s\n", buf);
+               }
+               free(resp);
+
+               r = CtdlIPCSetMessageExpirationPolicy(ipc, 2, site_expirepolicy, buf);
+               if (r / 100 != 2) {
+                       err_printf("%s\n", buf);
+               }
+
+               r = CtdlIPCSetMessageExpirationPolicy(ipc, 3, mbx_expirepolicy, buf);
+               if (r / 100 != 2) {
+                       err_printf("%s\n", buf);
+               }
+
+       }
+    if (site_expirepolicy) free(site_expirepolicy);
+    if (mbx_expirepolicy) free(mbx_expirepolicy);
+}
+
+
+/*
+ * support function for do_internet_configuration()
+ */
+void get_inet_rec_type(CtdlIPC *ipc, char *buf) {
+       int sel;
+
+       keyopt(" <1> localhost      (Alias for this computer)\n");
+       keyopt(" <2> smart-host     (Forward all outbound mail to this host)\n");
+       keyopt(" <3> directory      (Consult the Global Address Book)\n");
+       keyopt(" <4> SpamAssassin   (Address of SpamAssassin server)\n");
+       keyopt(" <5> RBL            (domain suffix of spam hunting RBL)\n");
+       keyopt(" <6> masq domains   (Domains as which users are allowed to masquerade)\n");
+       keyopt(" <7> ClamAV         (Address of ClamAV clamd server)\n");
+       sel = intprompt("Which one", 1, 1, 7);
+       switch(sel) {
+               case 1: strcpy(buf, "localhost");
+                       return;
+               case 2: strcpy(buf, "smarthost");
+                       return;
+               case 3: strcpy(buf, "directory");
+                       return;
+               case 4: strcpy(buf, "spamassassin");
+                       return;
+               case 5: strcpy(buf, "rbl");
+                       return;
+               case 6: strcpy(buf, "masqdomain");
+                       return;
+               case 7: strcpy(buf, "clamav");
+                       return;
+       }
+}
+
+
+/*
+ * Internet mail configuration
+ */
+void do_internet_configuration(CtdlIPC *ipc)
+{
+       char buf[256];
+       char *resp = NULL;
+       int num_recs = 0;
+       char **recs = NULL;
+       char ch;
+       int badkey;
+       int i, j;
+       int quitting = 0;
+       int modified = 0;
+       int r;
+       
+       r = CtdlIPCGetSystemConfigByType(ipc, INTERNETCFG, &resp, buf);
+       if (r / 100 == 1) {
+               while (!IsEmptyStr(resp)) {
+                       extract_token(buf, resp, 0, '\n', sizeof buf);
+                       remove_token(resp, 0, '\n');
+                       ++num_recs;
+                       if (num_recs == 1) recs = malloc(sizeof(char *));
+                       else recs = realloc(recs, (sizeof(char *)) * num_recs);
+                       recs[num_recs-1] = malloc(strlen(buf) + 1);
+                       strcpy(recs[num_recs-1], buf);
+               }
+       }
+       if (resp) free(resp);
+
+       do {
+               scr_printf("\n");
+               color(BRIGHT_WHITE);
+               scr_printf("###                    Host or domain                     Record type      \n");
+               color(DIM_WHITE);
+               scr_printf("--- -------------------------------------------------- --------------------\n");
+               for (i=0; i<num_recs; ++i) {
+               color(DIM_WHITE);
+               scr_printf("%3d ", i+1);
+               extract_token(buf, recs[i], 0, '|', sizeof buf);
+               color(BRIGHT_CYAN);
+               scr_printf("%-50s ", buf);
+               extract_token(buf, recs[i], 1, '|', sizeof buf);
+               color(BRIGHT_MAGENTA);
+               scr_printf("%-20s\n", buf);
+               color(DIM_WHITE);
+               }
+
+               ch = keymenu("", "<A>dd|<D>elete|<S>ave|<Q>uit");
+               switch(ch) {
+                       case 'a':
+                               newprompt("Enter host name: ",
+                                       buf, 50);
+                               striplt(buf);
+                               if (!IsEmptyStr(buf)) {
+                                       ++num_recs;
+                                       if (num_recs == 1)
+                                               recs = malloc(sizeof(char *));
+                                       else recs = realloc(recs,
+                                               (sizeof(char *)) * num_recs);
+                                       strcat(buf, "|");
+                                       get_inet_rec_type(ipc,
+                                                       &buf[strlen(buf)]);
+                                       recs[num_recs-1] = strdup(buf);
+                               }
+                               modified = 1;
+                               break;
+                       case 'd':
+                               i = intprompt("Delete which one",
+                                       1, 1, num_recs) - 1;
+                               free(recs[i]);
+                               --num_recs;
+                               for (j=i; j<num_recs; ++j)
+                                       recs[j] = recs[j+1];
+                               modified = 1;
+                               break;
+                       case 's':
+                               r = 1;
+                               for (i = 0; i < num_recs; i++)
+                                       r += 1 + strlen(recs[i]);
+                               resp = (char *)calloc(1, r);
+                               if (!resp) {
+                                       err_printf("Can't save config - out of memory!\n");
+                                       logoff(ipc, 1);
+                               }
+                               if (num_recs) for (i = 0; i < num_recs; i++) {
+                                       strcat(resp, recs[i]);
+                                       strcat(resp, "\n");
+                               }
+                               r = CtdlIPCSetSystemConfigByType(ipc, INTERNETCFG, resp, buf);
+                               if (r / 100 != 4) {
+                                       err_printf("%s\n", buf);
+                               } else {
+                                       scr_printf("Wrote %d records.\n", num_recs);
+                                       modified = 0;
+                               }
+                free(resp);
+                               break;
+                       case 'q':
+                               quitting = !modified || boolprompt(
+                                       "Quit without saving", 0);
+                               break;
+                       default:
+                               badkey = 1;
+               }
+       } while (!quitting);
+
+       if (recs != NULL) {
+               for (i=0; i<num_recs; ++i) free(recs[i]);
+               free(recs);
+       }
+}
+
+
+
+/*
+ * Edit network configuration for room sharing, mailing lists, etc.
+ */
+void network_config_management(CtdlIPC *ipc, char *entrytype, char *comment)
+{
+       char filename[PATH_MAX];
+       char changefile[PATH_MAX];
+       int e_ex_code;
+       pid_t editor_pid;
+       int cksum;
+       int b, i, tokens;
+       char buf[1024];
+       char instr[1024];
+       char addr[1024];
+       FILE *tempfp;
+       FILE *changefp;
+       char *listing = NULL;
+       int r;
+
+       if (IsEmptyStr(editor_paths[0])) {
+               scr_printf("You must have an external editor configured in"
+                       " order to use this function.\n");
+               return;
+       }
+
+       CtdlMakeTempFileName(filename, sizeof filename);
+       CtdlMakeTempFileName(changefile, sizeof changefile);
+
+       tempfp = fopen(filename, "w");
+       if (tempfp == NULL) {
+               err_printf("Cannot open %s: %s\n", filename, strerror(errno));
+               return;
+       }
+
+       fprintf(tempfp, "# Configuration for room: %s\n", room_name);
+       fprintf(tempfp, "# %s\n", comment);
+       fprintf(tempfp, "# Specify one per line.\n"
+                       "\n\n");
+
+       r = CtdlIPCGetRoomNetworkConfig(ipc, &listing, buf);
+       if (r / 100 == 1) {
+               while(listing && !IsEmptyStr(listing)) {
+                       extract_token(buf, listing, 0, '\n', sizeof buf);
+                       remove_token(listing, 0, '\n');
+                       extract_token(instr, buf, 0, '|', sizeof instr);
+                       if (!strcasecmp(instr, entrytype)) {
+                               tokens = num_tokens(buf, '|');
+                               for (i=1; i<tokens; ++i) {
+                                       extract_token(addr, buf, i, '|', sizeof addr);
+                                       fprintf(tempfp, "%s", addr);
+                                       if (i < (tokens-1)) {
+                                               fprintf(tempfp, "|");
+                                       }
+                               }
+                               fprintf(tempfp, "\n");
+                       }
+               }
+       }
+       if (listing) {
+               free(listing);
+               listing = NULL;
+       }
+       fclose(tempfp);
+
+       e_ex_code = 1;  /* start with a failed exit code */
+       screen_reset();
+       stty_ctdl(SB_RESTORE);
+       editor_pid = fork();
+       cksum = file_checksum(filename);
+       if (editor_pid == 0) {
+               chmod(filename, 0600);
+               putenv("WINDOW_TITLE=Network configuration");
+               execlp(editor_paths[0], editor_paths[0], filename, NULL);
+               exit(1);
+       }
+       if (editor_pid > 0) {
+               do {
+                       e_ex_code = 0;
+                       b = ka_wait(&e_ex_code);
+               } while ((b != editor_pid) && (b >= 0));
+       editor_pid = (-1);
+       stty_ctdl(0);
+       screen_set();
+       }
+
+       if (file_checksum(filename) == cksum) {
+               err_printf("*** No changes to save.\n");
+               e_ex_code = 1;
+       }
+
+       if (e_ex_code == 0) {           /* Save changes */
+               changefp = fopen(changefile, "w");
+
+               /* Load all netconfig entries that are *not* of the type we are editing */
+               r = CtdlIPCGetRoomNetworkConfig(ipc, &listing, buf);
+               if (r / 100 == 1) {
+                       while(listing && !IsEmptyStr(listing)) {
+                               extract_token(buf, listing, 0, '\n', sizeof buf);
+                               remove_token(listing, 0, '\n');
+                               extract_token(instr, buf, 0, '|', sizeof instr);
+                               if (strcasecmp(instr, entrytype)) {
+                                       fprintf(changefp, "%s\n", buf);
+                               }
+                       }
+               }
+               if (listing) {
+                       free(listing);
+                       listing = NULL;
+               }
+
+               /* ...and merge that with the data we just edited */
+               tempfp = fopen(filename, "r");
+               while (fgets(buf, sizeof buf, tempfp) != NULL) {
+                       for (i=0; i<strlen(buf); ++i) {
+                               if (buf[i] == '#') buf[i] = 0;
+                       }
+                       striplt(buf);
+                       if (!IsEmptyStr(buf)) {
+                               fprintf(changefp, "%s|%s\n", entrytype, buf);
+                       }
+               }
+               fclose(tempfp);
+               fclose(changefp);
+
+               /* now write it to the server... */
+               changefp = fopen(changefile, "r");
+               if (changefp != NULL) {
+                       listing = load_message_from_file(changefp);
+                       if (listing) {
+                               r = CtdlIPCSetRoomNetworkConfig(ipc, listing, buf);
+                               free(listing);
+                               listing = NULL;
+                       }
+                       fclose(changefp);
+               }
+       }
+
+       unlink(filename);               /* Delete the temporary files */
+       unlink(changefile);
+}
+
+
+/*
+ * IGnet node configuration
+ */
+void do_ignet_configuration(CtdlIPC *ipc) {
+       char buf[SIZ];
+       int num_recs = 0;
+       char **recs = NULL;
+       char ch;
+       int badkey;
+       int i, j;
+       int quitting = 0;
+       int modified = 0;
+       char *listing = NULL;
+       int r;
+
+       r = CtdlIPCGetSystemConfigByType(ipc, IGNETCFG, &listing, buf);
+       if (r / 100 == 1) while (*listing && !IsEmptyStr(listing)) {
+               extract_token(buf, listing, 0, '\n', sizeof buf);
+               remove_token(listing, 0, '\n');
+
+               ++num_recs;
+               if (num_recs == 1) recs = malloc(sizeof(char *));
+               else recs = realloc(recs, (sizeof(char *)) * num_recs);
+               recs[num_recs-1] = malloc(SIZ);
+               strcpy(recs[num_recs-1], buf);
+       }
+       if (listing) free(listing);
+
+       do {
+               scr_printf("\n");
+               color(BRIGHT_WHITE);
+               scr_printf(     "### "
+                       "   Node          "
+                       "  Secret           "
+                       "          Host or IP             "
+                       "Port#\n");
+               color(DIM_WHITE);
+               scr_printf(     "--- "
+                       "---------------- "
+                       "------------------ "
+                       "-------------------------------- "
+                       "-----\n");
+               for (i=0; i<num_recs; ++i) {
+               color(DIM_WHITE);
+               scr_printf("%3d ", i+1);
+               extract_token(buf, recs[i], 0, '|', sizeof buf);
+               color(BRIGHT_CYAN);
+               scr_printf("%-16s ", buf);
+               extract_token(buf, recs[i], 1, '|', sizeof buf);
+               color(BRIGHT_MAGENTA);
+               scr_printf("%-18s ", buf);
+               extract_token(buf, recs[i], 2, '|', sizeof buf);
+               color(BRIGHT_CYAN);
+               scr_printf("%-32s ", buf);
+               extract_token(buf, recs[i], 3, '|', sizeof buf);
+               color(BRIGHT_MAGENTA);
+               scr_printf("%-3s\n", buf);
+               color(DIM_WHITE);
+               }
+               scr_printf("\n");
+
+               ch = keymenu("", "<A>dd|<D>elete|<S>ave|<Q>uit");
+               switch(ch) {
+                       case 'a':
+                               ++num_recs;
+                               if (num_recs == 1)
+                                       recs = malloc(sizeof(char *));
+                               else recs = realloc(recs,
+                                       (sizeof(char *)) * num_recs);
+                               newprompt("Enter node name    : ", buf, 16);
+                               strcat(buf, "|");
+                               newprompt("Enter shared secret: ",
+                                       &buf[strlen(buf)], 18);
+                               strcat(buf, "|");
+                               newprompt("Enter host or IP   : ",
+                                       &buf[strlen(buf)], 32);
+                               strcat(buf, "|504");
+                               strprompt("Enter port number  : ",
+                                       &buf[strlen(buf)-3], 5);
+                               recs[num_recs-1] = strdup(buf);
+                               modified = 1;
+                               break;
+                       case 'd':
+                               i = intprompt("Delete which one",
+                                       1, 1, num_recs) - 1;
+                               free(recs[i]);
+                               --num_recs;
+                               for (j=i; j<num_recs; ++j)
+                                       recs[j] = recs[j+1];
+                               modified = 1;
+                               break;
+                       case 's':
+                               r = 1;
+                               for (i = 0; i < num_recs; ++i)
+                                       r += 1 + strlen(recs[i]);
+                               listing = (char*) calloc(1, r);
+                               if (!listing) {
+                                       err_printf("Can't save config - out of memory!\n");
+                                       logoff(ipc, 1);
+                               }
+                               if (num_recs) for (i = 0; i < num_recs; ++i) {
+                                       strcat(listing, recs[i]);
+                                       strcat(listing, "\n");
+                               }
+                               r = CtdlIPCSetSystemConfigByType(ipc, IGNETCFG, listing, buf);
+                               if (r / 100 != 4) {
+                                       scr_printf("%s\n", buf);
+                               } else {
+                                       scr_printf("Wrote %d records.\n", num_recs);
+                                       modified = 0;
+                               }
+                free(listing);
+                               break;
+                       case 'q':
+                               quitting = !modified || boolprompt(
+                                       "Quit without saving", 0);
+                               break;
+                       default:
+                               badkey = 1;
+               }
+       } while (!quitting);
+
+       if (recs != NULL) {
+               for (i=0; i<num_recs; ++i) free(recs[i]);
+               free(recs);
+       }
+}
+
+
+/*
+ * Filter list configuration
+ */
+void do_filterlist_configuration(CtdlIPC *ipc)
+{
+       char buf[SIZ];
+       int num_recs = 0;
+       char **recs = NULL;
+       char ch;
+       int badkey;
+       int i, j;
+       int quitting = 0;
+       int modified = 0;
+       char *listing = NULL;
+       int r;
+
+       r = CtdlIPCGetSystemConfigByType(ipc, FILTERLIST, &listing, buf);
+       if (r / 100 == 1) while (*listing && !IsEmptyStr(listing)) {
+               extract_token(buf, listing, 0, '\n', sizeof buf);
+               remove_token(listing, 0, '\n');
+
+               ++num_recs;
+               if (num_recs == 1) recs = malloc(sizeof(char *));
+               else recs = realloc(recs, (sizeof(char *)) * num_recs);
+               recs[num_recs-1] = malloc(SIZ);
+               strcpy(recs[num_recs-1], buf);
+       }
+       if (listing) free(listing);
+
+       do {
+               scr_printf("\n");
+               color(BRIGHT_WHITE);
+               scr_printf(     "### "
+                       "         User name           "
+                       "         Room name           "
+                       "    Node name    "
+                       "\n");
+               color(DIM_WHITE);
+               scr_printf(     "--- "
+                       "---------------------------- "
+                       "---------------------------- "
+                       "---------------- "
+                       "\n");
+               for (i=0; i<num_recs; ++i) {
+               color(DIM_WHITE);
+               scr_printf("%3d ", i+1);
+               extract_token(buf, recs[i], 0, '|', sizeof buf);
+               color(BRIGHT_CYAN);
+               scr_printf("%-28s ", buf);
+               extract_token(buf, recs[i], 1, '|', sizeof buf);
+               color(BRIGHT_MAGENTA);
+               scr_printf("%-28s ", buf);
+               extract_token(buf, recs[i], 2, '|', sizeof buf);
+               color(BRIGHT_CYAN);
+               scr_printf("%-16s\n", buf);
+               extract_token(buf, recs[i], 3, '|', sizeof buf);
+               color(DIM_WHITE);
+               }
+
+               ch = keymenu("", "<A>dd|<D>elete|<S>ave|<Q>uit");
+               switch(ch) {
+                       case 'a':
+                               ++num_recs;
+                               if (num_recs == 1)
+                                       recs = malloc(sizeof(char *));
+                               else recs = realloc(recs,
+                                       (sizeof(char *)) * num_recs);
+                               newprompt("Enter user name: ", buf, 28);
+                               strcat(buf, "|");
+                               newprompt("Enter room name: ",
+                                       &buf[strlen(buf)], 28);
+                               strcat(buf, "|");
+                               newprompt("Enter node name: ",
+                                       &buf[strlen(buf)], 16);
+                               strcat(buf, "|");
+                               recs[num_recs-1] = strdup(buf);
+                               modified = 1;
+                               break;
+                       case 'd':
+                               i = intprompt("Delete which one",
+                                       1, 1, num_recs) - 1;
+                               free(recs[i]);
+                               --num_recs;
+                               for (j=i; j<num_recs; ++j)
+                                       recs[j] = recs[j+1];
+                               modified = 1;
+                               break;
+                       case 's':
+                               r = 1;
+                               for (i = 0; i < num_recs; ++i)
+                                       r += 1 + strlen(recs[i]);
+                               listing = (char*) calloc(1, r);
+                               if (!listing) {
+                                       err_printf("Can't save config - out of memory!\n");
+                                       logoff(ipc, 1);
+                               }
+                               if (num_recs) for (i = 0; i < num_recs; ++i) {
+                                       strcat(listing, recs[i]);
+                                       strcat(listing, "\n");
+                               }
+                               r = CtdlIPCSetSystemConfigByType(ipc, FILTERLIST, listing, buf);
+                               if (r / 100 != 4) {
+                                       scr_printf("%s\n", buf);
+                               } else {
+                                       scr_printf("Wrote %d records.\n", num_recs);
+                                       modified = 0;
+                               }
+                free(listing);
+                               break;
+                       case 'q':
+                               quitting = !modified || boolprompt(
+                                       "Quit without saving", 0);
+                               break;
+                       default:
+                               badkey = 1;
+               }
+       } while (!quitting);
+
+       if (recs != NULL) {
+               for (i=0; i<num_recs; ++i) free(recs[i]);
+               free(recs);
+       }
+}
+
+
+
+
+/*
+ * POP3 aggregation client configuration
+ */
+void do_pop3client_configuration(CtdlIPC *ipc)
+{
+       char buf[SIZ];
+       int num_recs = 0;
+       char **recs = NULL;
+       char ch;
+       int badkey;
+       int i, j;
+       int quitting = 0;
+       int modified = 0;
+       char *listing = NULL;
+       char *other_listing = NULL;
+       int r;
+       char instr[SIZ];
+
+       r = CtdlIPCGetRoomNetworkConfig(ipc, &listing, buf);
+       if (r / 100 == 1) {
+               while(listing && !IsEmptyStr(listing)) {
+                       extract_token(buf, listing, 0, '\n', sizeof buf);
+                       remove_token(listing, 0, '\n');
+                       extract_token(instr, buf, 0, '|', sizeof instr);
+                       if (!strcasecmp(instr, "pop3client")) {
+
+                               ++num_recs;
+                               if (num_recs == 1) recs = malloc(sizeof(char *));
+                               else recs = realloc(recs, (sizeof(char *)) * num_recs);
+                               recs[num_recs-1] = malloc(SIZ);
+                               strcpy(recs[num_recs-1], buf);
+
+                       }
+               }
+       }
+       if (listing) {
+               free(listing);
+               listing = NULL;
+       }
+
+       do {
+               scr_printf("\n");
+               color(BRIGHT_WHITE);
+               scr_printf(     "### "
+                       "      Remote POP3 host       "
+                       "         User name           "
+                       "Keep on server? "
+                       "Fetching inteval"
+                       "\n");
+               color(DIM_WHITE);
+               scr_printf(     "--- "
+                       "---------------------------- "
+                       "---------------------------- "
+                       "--------------- "
+                       "---------------- "
+                       "\n");
+               for (i=0; i<num_recs; ++i) {
+               color(DIM_WHITE);
+               scr_printf("%3d ", i+1);
+
+               extract_token(buf, recs[i], 1, '|', sizeof buf);
+               color(BRIGHT_CYAN);
+               scr_printf("%-28s ", buf);
+
+               extract_token(buf, recs[i], 2, '|', sizeof buf);
+               color(BRIGHT_MAGENTA);
+               scr_printf("%-28s ", buf);
+
+               color(BRIGHT_CYAN);
+               scr_printf("%-15s ", (extract_int(recs[i], 4) ? "Yes" : "No") );
+               color(BRIGHT_MAGENTA);
+               scr_printf("%ld\n", extract_long(recs[i], 5) );
+               color(DIM_WHITE);
+               }
+
+               ch = keymenu("", "<A>dd|<D>elete|<S>ave|<Q>uit");
+               switch(ch) {
+                       case 'a':
+                               ++num_recs;
+                               if (num_recs == 1) {
+                                       recs = malloc(sizeof(char *));
+                               }
+                               else {
+                                       recs = realloc(recs, (sizeof(char *)) * num_recs);
+                               }
+                               strcpy(buf, "pop3client|");
+                               newprompt("Enter host name: ", &buf[strlen(buf)], 28);
+                               strcat(buf, "|");
+                               newprompt("Enter user name: ", &buf[strlen(buf)], 28);
+                               strcat(buf, "|");
+                               newprompt("Enter password : ", &buf[strlen(buf)], 16);
+                               strcat(buf, "|");
+                               scr_printf("Keep messages on server instead of deleting them? ");
+                               sprintf(&buf[strlen(buf)], "%d", yesno());
+                               strcat(buf, "|");
+                               newprompt("Enter interval : ", &buf[strlen(buf)], 5);
+                               strcat(buf, "|");
+                               recs[num_recs-1] = strdup(buf);
+                               modified = 1;
+                               break;
+                       case 'd':
+                               i = intprompt("Delete which one",
+                                       1, 1, num_recs) - 1;
+                               free(recs[i]);
+                               --num_recs;
+                               for (j=i; j<num_recs; ++j)
+                                       recs[j] = recs[j+1];
+                               modified = 1;
+                               break;
+                       case 's':
+                               r = 1;
+                               for (i = 0; i < num_recs; ++i) {
+                                       r += 1 + strlen(recs[i]);
+                               }
+                               listing = (char*) calloc(1, r);
+                               if (!listing) {
+                                       err_printf("Can't save config - out of memory!\n");
+                                       logoff(ipc, 1);
+                               }
+                               if (num_recs) for (i = 0; i < num_recs; ++i) {
+                                       strcat(listing, recs[i]);
+                                       strcat(listing, "\n");
+                               }
+
+                               /* Retrieve all the *other* records for merging */
+                               r = CtdlIPCGetRoomNetworkConfig(ipc, &other_listing, buf);
+                               if (r / 100 == 1) {
+                                       for (i=0; i<num_tokens(other_listing, '\n'); ++i) {
+                                               extract_token(buf, other_listing, i, '\n', sizeof buf);
+                                               if (strncasecmp(buf, "pop3client|", 11)) {
+                                                       listing = realloc(listing, strlen(listing) +
+                                                               strlen(buf) + 10);
+                                                       strcat(listing, buf);
+                                                       strcat(listing, "\n");
+                                               }
+                                       }
+                               }
+                               free(other_listing);
+                               r = CtdlIPCSetRoomNetworkConfig(ipc, listing, buf);
+                               free(listing);
+                               listing = NULL;
+
+                               if (r / 100 != 4) {
+                                       scr_printf("%s\n", buf);
+                               } else {
+                                       scr_printf("Wrote %d records.\n", num_recs);
+                                       modified = 0;
+                               }
+                               quitting = 1;
+                               break;
+                       case 'q':
+                               quitting = !modified || boolprompt(
+                                       "Quit without saving", 0);
+                               break;
+                       default:
+                               badkey = 1;
+               }
+       } while (!quitting);
+
+       if (recs != NULL) {
+               for (i=0; i<num_recs; ++i) free(recs[i]);
+               free(recs);
+       }
+}
+
+
+
+
+
+
+/*
+ * RSS feed retrieval client configuration
+ */
+void do_rssclient_configuration(CtdlIPC *ipc)
+{
+       char buf[SIZ];
+       int num_recs = 0;
+       char **recs = NULL;
+       char ch;
+       int badkey;
+       int i, j;
+       int quitting = 0;
+       int modified = 0;
+       char *listing = NULL;
+       char *other_listing = NULL;
+       int r;
+       char instr[SIZ];
+
+       r = CtdlIPCGetRoomNetworkConfig(ipc, &listing, buf);
+       if (r / 100 == 1) {
+               while(listing && !IsEmptyStr(listing)) {
+                       extract_token(buf, listing, 0, '\n', sizeof buf);
+                       remove_token(listing, 0, '\n');
+                       extract_token(instr, buf, 0, '|', sizeof instr);
+                       if (!strcasecmp(instr, "rssclient")) {
+
+                               ++num_recs;
+                               if (num_recs == 1) recs = malloc(sizeof(char *));
+                               else recs = realloc(recs, (sizeof(char *)) * num_recs);
+                               recs[num_recs-1] = malloc(SIZ);
+                               strcpy(recs[num_recs-1], buf);
+
+                       }
+               }
+       }
+       if (listing) {
+               free(listing);
+               listing = NULL;
+       }
+
+       do {
+               scr_printf("\n");
+               color(BRIGHT_WHITE);
+               scr_printf("### Feed URL\n");
+               color(DIM_WHITE);
+               scr_printf("--- "
+                       "---------------------------------------------------------------------------"
+                       "\n");
+               
+               for (i=0; i<num_recs; ++i) {
+               color(DIM_WHITE);
+               scr_printf("%3d ", i+1);
+
+               extract_token(buf, recs[i], 1, '|', sizeof buf);
+               color(BRIGHT_CYAN);
+               scr_printf("%-75s\n", buf);
+
+               color(DIM_WHITE);
+               }
+
+               ch = keymenu("", "<A>dd|<D>elete|<S>ave|<Q>uit");
+               switch(ch) {
+                       case 'a':
+                               ++num_recs;
+                               if (num_recs == 1) {
+                                       recs = malloc(sizeof(char *));
+                               }
+                               else {
+                                       recs = realloc(recs, (sizeof(char *)) * num_recs);
+                               }
+                               strcpy(buf, "rssclient|");
+                               newprompt("Enter feed URL: ", &buf[strlen(buf)], 75);
+                               strcat(buf, "|");
+                               recs[num_recs-1] = strdup(buf);
+                               modified = 1;
+                               break;
+                       case 'd':
+                               i = intprompt("Delete which one", 1, 1, num_recs) - 1;
+                               free(recs[i]);
+                               --num_recs;
+                               for (j=i; j<num_recs; ++j)
+                                       recs[j] = recs[j+1];
+                               modified = 1;
+                               break;
+                       case 's':
+                               r = 1;
+                               for (i = 0; i < num_recs; ++i) {
+                                       r += 1 + strlen(recs[i]);
+                               }
+                               listing = (char*) calloc(1, r);
+                               if (!listing) {
+                                       err_printf("Can't save config - out of memory!\n");
+                                       logoff(ipc, 1);
+                               }
+                               if (num_recs) for (i = 0; i < num_recs; ++i) {
+                                       strcat(listing, recs[i]);
+                                       strcat(listing, "\n");
+                               }
+
+                               /* Retrieve all the *other* records for merging */
+                               r = CtdlIPCGetRoomNetworkConfig(ipc, &other_listing, buf);
+                               if (r / 100 == 1) {
+                                       for (i=0; i<num_tokens(other_listing, '\n'); ++i) {
+                                               extract_token(buf, other_listing, i, '\n', sizeof buf);
+                                               if (strncasecmp(buf, "rssclient|", 10)) {
+                                                       listing = realloc(listing, strlen(listing) +
+                                                               strlen(buf) + 10);
+                                                       strcat(listing, buf);
+                                                       strcat(listing, "\n");
+                                               }
+                                       }
+                               }
+                               free(other_listing);
+                               r = CtdlIPCSetRoomNetworkConfig(ipc, listing, buf);
+                               free(listing);
+                               listing = NULL;
+
+                               if (r / 100 != 4) {
+                                       scr_printf("%s\n", buf);
+                               } else {
+                                       scr_printf("Wrote %d records.\n", num_recs);
+                                       modified = 0;
+                               }
+                               quitting = 1;
+                               break;
+                       case 'q':
+                               quitting = !modified || boolprompt(
+                                       "Quit without saving", 0);
+                               break;
+                       default:
+                               badkey = 1;
+               }
+       } while (!quitting);
+
+       if (recs != NULL) {
+               for (i=0; i<num_recs; ++i) free(recs[i]);
+               free(recs);
+       }
+}
+
+
diff --git a/citadel/textclient/tuiconfig.h b/citadel/textclient/tuiconfig.h
new file mode 100644 (file)
index 0000000..2d0a5d4
--- /dev/null
@@ -0,0 +1,8 @@
+/* $Id$ */
+void do_internet_configuration(CtdlIPC *ipc);
+void do_ignet_configuration(CtdlIPC *ipc);
+void network_config_management(CtdlIPC *ipc, char *entrytype, char *comment);
+void do_filterlist_configuration(CtdlIPC *ipc);
+void do_pop3client_configuration(CtdlIPC *ipc);
+void do_rssclient_configuration(CtdlIPC *ipc);
+void do_system_configuration(CtdlIPC *ipc);
diff --git a/citadel/tuiconfig.c b/citadel/tuiconfig.c
deleted file mode 100644 (file)
index a6cfde3..0000000
+++ /dev/null
@@ -1,1245 +0,0 @@
-/* $Id$
- *
- * Configuration screens that are part of the text mode client.
- *
- */
-
-#include <stdlib.h>
-#include <unistd.h>
-#include <sys/types.h>
-#include <sys/stat.h>
-#include <fcntl.h>
-#include <stdio.h>
-#include <ctype.h>
-#include <string.h>
-#include <limits.h>
-
-#if TIME_WITH_SYS_TIME
-# include <sys/time.h>
-# include <time.h>
-#else
-# if HAVE_SYS_TIME_H
-#  include <sys/time.h>
-# else
-#  include <time.h>
-# endif
-#endif
-
-#include <signal.h>
-#include <pwd.h>
-#include <errno.h>
-#include <stdarg.h>
-#include <libcitadel.h>
-#include "sysdep.h"
-#include "citadel.h"
-#include "citadel_ipc.h"
-#include "citadel_decls.h"
-#include "tuiconfig.h"
-#include "messages.h"
-#include "routines.h"
-#include "commands.h"
-#ifndef HAVE_SNPRINTF
-#include "snprintf.h"
-#endif
-#include "screen.h"
-
-/* work around solaris include files */
-#ifdef reg
-#undef reg
-#endif
-
-extern char temp[];
-extern char tempdir[];
-extern char *axdefs[8];
-extern long highest_msg_read;
-extern long maxmsgnum;
-extern unsigned room_flags;
-extern int screenwidth;
-
-
-/* 
- * General system configuration command
- */
-void do_system_configuration(CtdlIPC *ipc)
-{
-
-#define NUM_CONFIGS 67
-
-       char buf[256];
-       char sc[NUM_CONFIGS][256];
-       char *resp = NULL;
-       struct ExpirePolicy *site_expirepolicy = NULL;
-       struct ExpirePolicy *mbx_expirepolicy = NULL;
-       int a;
-       int logpages = 0;
-       int r;                  /* IPC response code */
-       int server_configs = 0;
-
-       /* Clear out the config buffers */
-       memset(&sc[0][0], 0, sizeof(sc));
-
-       /* Fetch the current config */
-       r = CtdlIPCGetSystemConfig(ipc, &resp, buf);
-       if (r / 100 == 1) {
-               server_configs = num_tokens(resp, '\n');
-               for (a=0; a<server_configs; ++a) {
-                       if (a < NUM_CONFIGS) {
-                               extract_token(&sc[a][0], resp, a, '\n', sizeof sc[a]);
-                       }
-               }
-       }
-       if (resp) free(resp);
-       resp = NULL;
-       /* Fetch the expire policy (this will silently fail on old servers,
-        * resulting in "default" policy)
-        */
-       r = CtdlIPCGetMessageExpirationPolicy(ipc, 2, &site_expirepolicy, buf);
-       r = CtdlIPCGetMessageExpirationPolicy(ipc, 3, &mbx_expirepolicy, buf);
-
-       /* Identification parameters */
-
-       strprompt("Node name", &sc[0][0], 15);
-       strprompt("Fully qualified domain name", &sc[1][0], 63);
-       strprompt("Human readable node name", &sc[2][0], 20);
-       strprompt("Telephone number", &sc[3][0], 15);
-       strprompt("Geographic location of this system", &sc[12][0], 31);
-       strprompt("Name of system administrator", &sc[13][0], 25);
-       strprompt("Paginator prompt", &sc[10][0], 79);
-
-       /* Security parameters */
-
-       snprintf(sc[7], sizeof sc[7], "%d", (boolprompt(
-               "Require registration for new users",
-               atoi(&sc[7][0]))));
-       snprintf(sc[29], sizeof sc[29], "%d", (boolprompt(
-               "Disable self-service user account creation",
-               atoi(&sc[29][0]))));
-       strprompt("Initial access level for new users", &sc[6][0], 1);
-       strprompt("Access level required to create rooms", &sc[19][0], 1);
-       snprintf(sc[4], sizeof sc[4], "%d", (boolprompt(
-               "Automatically give room aide privs to a user who creates a private room",
-               atoi(&sc[4][0]))));
-
-       snprintf(sc[8], sizeof sc[8], "%d", (boolprompt(
-               "Automatically move problem user messages to twit room",
-               atoi(&sc[8][0]))));
-
-       strprompt("Name of twit room", &sc[9][0], ROOMNAMELEN);
-       snprintf(sc[11], sizeof sc[11], "%d", (boolprompt(
-               "Restrict Internet mail to only those with that privilege",
-               atoi(&sc[11][0]))));
-       snprintf(sc[26], sizeof sc[26], "%d", (boolprompt(
-               "Allow Aides to Zap (forget) rooms",
-               atoi(&sc[26][0]))));
-
-       if (!IsEmptyStr(&sc[18][0])) {
-               logpages = 1;
-       }
-       else {
-               logpages = 0;
-       }
-       logpages = boolprompt("Log all instant messages", logpages);
-       if (logpages) {
-               strprompt("Name of logging room", &sc[18][0], ROOMNAMELEN);
-       }
-       else {
-               sc[18][0] = 0;
-       }
-
-       /* Commented out because this setting isn't really appropriate to
-        * change while the server is running.
-        *
-        * snprintf(sc[52], sizeof sc[52], "%d", (boolprompt(
-        *      "Use system authentication",
-        *      atoi(&sc[52][0]))));
-        */
-
-       /* Server tuning */
-
-       strprompt("Server connection idle timeout (in seconds)", &sc[5][0], 4);
-       strprompt("Maximum concurrent sessions", &sc[14][0], 4);
-       strprompt("Maximum message length", &sc[20][0], 20);
-       strprompt("Minimum number of worker threads", &sc[21][0], 3);
-       strprompt("Maximum number of worker threads", &sc[22][0], 3);
-       snprintf(sc[43], sizeof sc[43], "%d", (boolprompt(
-               "Automatically delete committed database logs",
-               atoi(&sc[43][0]))));
-
-       strprompt("Server IP address (0.0.0.0 for 'any')", &sc[37][0], 15);
-       strprompt("POP3 server port (-1 to disable)", &sc[23][0], 5);
-       strprompt("POP3S server port (-1 to disable)", &sc[40][0], 5);
-       strprompt("IMAP server port (-1 to disable)", &sc[27][0], 5);
-       strprompt("IMAPS server port (-1 to disable)", &sc[39][0], 5);
-       strprompt("SMTP MTA server port (-1 to disable)", &sc[24][0], 5);
-       strprompt("SMTP MSA server port (-1 to disable)", &sc[38][0], 5);
-       strprompt("SMTPS server port (-1 to disable)", &sc[41][0], 5);
-       strprompt("Postfix TCP Dictionary Port server port (-1 to disable)", &sc[50][0], 5);
-       strprompt("ManageSieve server port (-1 to disable)", &sc[51][0], 5);
-
-       strprompt("XMPP (Jabber) client to server port (-1 to disable)", &sc[62][0], 5);
-       /* No prompt because we don't implement this service yet, it's just a placeholder */
-       /* strprompt("XMPP (Jabber) server to server port (-1 to disable)", &sc[63][0], 5); */
-
-       /* This logic flips the question around, because it's one of those
-        * situations where 0=yes and 1=no
-        */
-       a = atoi(sc[25]);
-       a = (a ? 0 : 1);
-       a = boolprompt("Correct forged From: lines during authenticated SMTP",
-               a);
-       a = (a ? 0 : 1);
-       snprintf(sc[25], sizeof sc[25], "%d", a);
-
-       snprintf(sc[66], sizeof sc[66], "%d", (boolprompt(
-               "Flag messages as spam instead of rejecting",
-               atoi(&sc[66][0]))));
-
-       /* This logic flips the question around, because it's one of those
-        * situations where 0=yes and 1=no
-        */
-       a = atoi(sc[61]);
-       a = (a ? 0 : 1);
-       a = boolprompt("Force IMAP posts in public rooms to be from the user who submitted them", a);
-       a = (a ? 0 : 1);
-       snprintf(sc[61], sizeof sc[61], "%d", a);
-
-       snprintf(sc[45], sizeof sc[45], "%d", (boolprompt(
-               "Allow unauthenticated SMTP clients to spoof my domains",
-               atoi(&sc[45][0]))));
-       snprintf(sc[57], sizeof sc[57], "%d", (boolprompt(
-               "Perform RBL checks at greeting instead of after RCPT",
-               atoi(&sc[57][0]))));
-       snprintf(sc[44], sizeof sc[44], "%d", (boolprompt(
-               "Instantly expunge deleted IMAP messages",
-               atoi(&sc[44][0]))));
-
-       /* LDAP settings */
-       if (ipc->ServInfo.supports_ldap) {
-               a = strlen(&sc[32][0]);
-               a = (a ? 1 : 0);        /* Set only to 1 or 0 */
-               a = boolprompt("Do you want to configure LDAP authentication?", a);
-               if (a) {
-                       strprompt("Host name of LDAP server",
-                               &sc[32][0], 127);
-                       strprompt("Port number of LDAP service",
-                               &sc[33][0], 5);
-                       strprompt("Base DN", &sc[34][0], 255);
-                       strprompt("Bind DN (or blank for anonymous bind)", &sc[35][0], 255);
-                       strprompt("Password for bind DN (or blank for anonymous bind)", &sc[36][0], 255);
-               }
-               else {
-                       strcpy(&sc[32][0], "");
-               }
-       }
-
-       /* Expiry settings */
-       strprompt("Default user purge time (days)", &sc[16][0], 5);
-       strprompt("Default room purge time (days)", &sc[17][0], 5);
-
-       /* Angels and demons dancing in my head... */
-       do {
-               snprintf(buf, sizeof buf, "%d", site_expirepolicy->expire_mode);
-               strprompt("System default message expire policy (? for list)",
-                         buf, 1);
-               if (buf[0] == '?') {
-                       scr_printf("\n"
-                               "1. Never automatically expire messages\n"
-                               "2. Expire by message count\n"
-                               "3. Expire by message age\n");
-               }
-       } while ((buf[0] < '1') || (buf[0] > '3'));
-       site_expirepolicy->expire_mode = buf[0] - '0';
-
-       /* ...lunatics and monsters underneath my bed */
-       if (site_expirepolicy->expire_mode == 2) {
-               snprintf(buf, sizeof buf, "%d", site_expirepolicy->expire_value);
-               strprompt("Keep how many messages online?", buf, 10);
-               site_expirepolicy->expire_value = atol(buf);
-       }
-       if (site_expirepolicy->expire_mode == 3) {
-               snprintf(buf, sizeof buf, "%d", site_expirepolicy->expire_value);
-               strprompt("Keep messages for how many days?", buf, 10);
-               site_expirepolicy->expire_value = atol(buf);
-       }
-
-       /* Media messiahs preying on my fears... */
-       do {
-               snprintf(buf, sizeof buf, "%d", mbx_expirepolicy->expire_mode);
-               strprompt("Mailbox default message expire policy (? for list)",
-                         buf, 1);
-               if (buf[0] == '?') {
-                       scr_printf("\n"
-                               "0. Go with the system default\n"
-                               "1. Never automatically expire messages\n"
-                               "2. Expire by message count\n"
-                               "3. Expire by message age\n");
-               }
-       } while ((buf[0] < '0') || (buf[0] > '3'));
-       mbx_expirepolicy->expire_mode = buf[0] - '0';
-
-       /* ...Pop culture prophets playing in my ears */
-       if (mbx_expirepolicy->expire_mode == 2) {
-               snprintf(buf, sizeof buf, "%d", mbx_expirepolicy->expire_value);
-               strprompt("Keep how many messages online?", buf, 10);
-               mbx_expirepolicy->expire_value = atol(buf);
-       }
-       if (mbx_expirepolicy->expire_mode == 3) {
-               snprintf(buf, sizeof buf, "%d", mbx_expirepolicy->expire_value);
-               strprompt("Keep messages for how many days?", buf, 10);
-               mbx_expirepolicy->expire_value = atol(buf);
-       }
-
-       strprompt("How often to run network jobs (in seconds)", &sc[28][0], 5);
-       strprompt("Default frequency to run POP3 collection (in seconds)", &sc[64][0], 5);
-       strprompt("Fastest frequency to run POP3 collection (in seconds)", &sc[65][0], 5);
-       strprompt("Hour to run purges (0-23)", &sc[31][0], 2);
-       snprintf(sc[42], sizeof sc[42], "%d", (boolprompt(
-               "Enable full text search index (warning: resource intensive)",
-               atoi(&sc[42][0]))));
-
-       snprintf(sc[46], sizeof sc[46], "%d", (boolprompt(
-               "Perform journaling of email messages",
-               atoi(&sc[46][0]))));
-       snprintf(sc[47], sizeof sc[47], "%d", (boolprompt(
-               "Perform journaling of non-email messages",
-               atoi(&sc[47][0]))));
-       if ( (atoi(&sc[46][0])) || (atoi(&sc[47][0])) ) {
-               strprompt("Email destination of journalized messages",
-                       &sc[48][0], 127);
-       }
-
-       /* Funambol push stuff */
-       int yes_funambol = 0;
-       if (strlen(sc[53]) > 0) yes_funambol = 1;
-       yes_funambol = boolprompt("Connect to an external Funambol sync server", yes_funambol);
-       if (yes_funambol) {
-               strprompt("Funambol server (blank to disable)", &sc[53][0], 63);
-               strprompt("Funambol server port", &sc[54][0], 5);
-               strprompt("Funambol sync source", &sc[55][0], 63);
-               strprompt("Funambol authentication details (user:pass in Base64)", &sc[56][0],63);
-       }
-       else {
-               sc[53][0] = 0;
-               sc[54][0] = 0;
-               sc[55][0] = 0;
-               sc[56][0] = 0;
-       }
-
-       /* External pager stuff */
-       int yes_pager = 0;
-       if (strlen(sc[60]) > 0) yes_pager = 1;
-       yes_pager = boolprompt("Configure an external pager tool", yes_pager);
-       if (yes_pager) {
-               strprompt("External pager tool", &sc[60][0], 255);
-       }
-       else {
-               sc[60][0] = 0;
-       }
-
-       /* Master user account */
-       int yes_muacct = 0;
-       if (strlen(sc[58]) > 0) yes_muacct = 1;
-       yes_muacct = boolprompt("Enable a 'master user' account", yes_muacct);
-       if (yes_muacct) {
-               strprompt("Master user name", &sc[58][0], 31);
-               strprompt("Master user password", &sc[59][0], -31);
-       }
-       else {
-               strcpy(&sc[58][0], "");
-               strcpy(&sc[59][0], "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx");
-       }
-
-       /* Save it */
-       scr_printf("Save this configuration? ");
-       if (yesno()) {
-               r = 1;
-               for (a = 0; a < NUM_CONFIGS; a++)
-                       r += 1 + strlen(sc[a]);
-               resp = (char *)calloc(1, r);
-               if (!resp) {
-                       err_printf("Can't save config - out of memory!\n");
-                       logoff(ipc, 1);
-               }
-               for (a = 0; a < NUM_CONFIGS; a++) {
-                       strcat(resp, sc[a]);
-                       strcat(resp, "\n");
-               }
-               r = CtdlIPCSetSystemConfig(ipc, resp, buf);
-               if (r / 100 != 4) {
-                       err_printf("%s\n", buf);
-               }
-               free(resp);
-
-               r = CtdlIPCSetMessageExpirationPolicy(ipc, 2, site_expirepolicy, buf);
-               if (r / 100 != 2) {
-                       err_printf("%s\n", buf);
-               }
-
-               r = CtdlIPCSetMessageExpirationPolicy(ipc, 3, mbx_expirepolicy, buf);
-               if (r / 100 != 2) {
-                       err_printf("%s\n", buf);
-               }
-
-       }
-    if (site_expirepolicy) free(site_expirepolicy);
-    if (mbx_expirepolicy) free(mbx_expirepolicy);
-}
-
-
-/*
- * support function for do_internet_configuration()
- */
-void get_inet_rec_type(CtdlIPC *ipc, char *buf) {
-       int sel;
-
-       keyopt(" <1> localhost      (Alias for this computer)\n");
-       keyopt(" <2> smart-host     (Forward all outbound mail to this host)\n");
-       keyopt(" <3> directory      (Consult the Global Address Book)\n");
-       keyopt(" <4> SpamAssassin   (Address of SpamAssassin server)\n");
-       keyopt(" <5> RBL            (domain suffix of spam hunting RBL)\n");
-       keyopt(" <6> masq domains   (Domains as which users are allowed to masquerade)\n");
-       keyopt(" <7> ClamAV         (Address of ClamAV clamd server)\n");
-       sel = intprompt("Which one", 1, 1, 7);
-       switch(sel) {
-               case 1: strcpy(buf, "localhost");
-                       return;
-               case 2: strcpy(buf, "smarthost");
-                       return;
-               case 3: strcpy(buf, "directory");
-                       return;
-               case 4: strcpy(buf, "spamassassin");
-                       return;
-               case 5: strcpy(buf, "rbl");
-                       return;
-               case 6: strcpy(buf, "masqdomain");
-                       return;
-               case 7: strcpy(buf, "clamav");
-                       return;
-       }
-}
-
-
-/*
- * Internet mail configuration
- */
-void do_internet_configuration(CtdlIPC *ipc)
-{
-       char buf[256];
-       char *resp = NULL;
-       int num_recs = 0;
-       char **recs = NULL;
-       char ch;
-       int badkey;
-       int i, j;
-       int quitting = 0;
-       int modified = 0;
-       int r;
-       
-       r = CtdlIPCGetSystemConfigByType(ipc, INTERNETCFG, &resp, buf);
-       if (r / 100 == 1) {
-               while (!IsEmptyStr(resp)) {
-                       extract_token(buf, resp, 0, '\n', sizeof buf);
-                       remove_token(resp, 0, '\n');
-                       ++num_recs;
-                       if (num_recs == 1) recs = malloc(sizeof(char *));
-                       else recs = realloc(recs, (sizeof(char *)) * num_recs);
-                       recs[num_recs-1] = malloc(strlen(buf) + 1);
-                       strcpy(recs[num_recs-1], buf);
-               }
-       }
-       if (resp) free(resp);
-
-       do {
-               scr_printf("\n");
-               color(BRIGHT_WHITE);
-               scr_printf("###                    Host or domain                     Record type      \n");
-               color(DIM_WHITE);
-               scr_printf("--- -------------------------------------------------- --------------------\n");
-               for (i=0; i<num_recs; ++i) {
-               color(DIM_WHITE);
-               scr_printf("%3d ", i+1);
-               extract_token(buf, recs[i], 0, '|', sizeof buf);
-               color(BRIGHT_CYAN);
-               scr_printf("%-50s ", buf);
-               extract_token(buf, recs[i], 1, '|', sizeof buf);
-               color(BRIGHT_MAGENTA);
-               scr_printf("%-20s\n", buf);
-               color(DIM_WHITE);
-               }
-
-               ch = keymenu("", "<A>dd|<D>elete|<S>ave|<Q>uit");
-               switch(ch) {
-                       case 'a':
-                               newprompt("Enter host name: ",
-                                       buf, 50);
-                               striplt(buf);
-                               if (!IsEmptyStr(buf)) {
-                                       ++num_recs;
-                                       if (num_recs == 1)
-                                               recs = malloc(sizeof(char *));
-                                       else recs = realloc(recs,
-                                               (sizeof(char *)) * num_recs);
-                                       strcat(buf, "|");
-                                       get_inet_rec_type(ipc,
-                                                       &buf[strlen(buf)]);
-                                       recs[num_recs-1] = strdup(buf);
-                               }
-                               modified = 1;
-                               break;
-                       case 'd':
-                               i = intprompt("Delete which one",
-                                       1, 1, num_recs) - 1;
-                               free(recs[i]);
-                               --num_recs;
-                               for (j=i; j<num_recs; ++j)
-                                       recs[j] = recs[j+1];
-                               modified = 1;
-                               break;
-                       case 's':
-                               r = 1;
-                               for (i = 0; i < num_recs; i++)
-                                       r += 1 + strlen(recs[i]);
-                               resp = (char *)calloc(1, r);
-                               if (!resp) {
-                                       err_printf("Can't save config - out of memory!\n");
-                                       logoff(ipc, 1);
-                               }
-                               if (num_recs) for (i = 0; i < num_recs; i++) {
-                                       strcat(resp, recs[i]);
-                                       strcat(resp, "\n");
-                               }
-                               r = CtdlIPCSetSystemConfigByType(ipc, INTERNETCFG, resp, buf);
-                               if (r / 100 != 4) {
-                                       err_printf("%s\n", buf);
-                               } else {
-                                       scr_printf("Wrote %d records.\n", num_recs);
-                                       modified = 0;
-                               }
-                free(resp);
-                               break;
-                       case 'q':
-                               quitting = !modified || boolprompt(
-                                       "Quit without saving", 0);
-                               break;
-                       default:
-                               badkey = 1;
-               }
-       } while (!quitting);
-
-       if (recs != NULL) {
-               for (i=0; i<num_recs; ++i) free(recs[i]);
-               free(recs);
-       }
-}
-
-
-
-/*
- * Edit network configuration for room sharing, mailing lists, etc.
- */
-void network_config_management(CtdlIPC *ipc, char *entrytype, char *comment)
-{
-       char filename[PATH_MAX];
-       char changefile[PATH_MAX];
-       int e_ex_code;
-       pid_t editor_pid;
-       int cksum;
-       int b, i, tokens;
-       char buf[1024];
-       char instr[1024];
-       char addr[1024];
-       FILE *tempfp;
-       FILE *changefp;
-       char *listing = NULL;
-       int r;
-
-       if (IsEmptyStr(editor_paths[0])) {
-               scr_printf("You must have an external editor configured in"
-                       " order to use this function.\n");
-               return;
-       }
-
-       CtdlMakeTempFileName(filename, sizeof filename);
-       CtdlMakeTempFileName(changefile, sizeof changefile);
-
-       tempfp = fopen(filename, "w");
-       if (tempfp == NULL) {
-               err_printf("Cannot open %s: %s\n", filename, strerror(errno));
-               return;
-       }
-
-       fprintf(tempfp, "# Configuration for room: %s\n", room_name);
-       fprintf(tempfp, "# %s\n", comment);
-       fprintf(tempfp, "# Specify one per line.\n"
-                       "\n\n");
-
-       r = CtdlIPCGetRoomNetworkConfig(ipc, &listing, buf);
-       if (r / 100 == 1) {
-               while(listing && !IsEmptyStr(listing)) {
-                       extract_token(buf, listing, 0, '\n', sizeof buf);
-                       remove_token(listing, 0, '\n');
-                       extract_token(instr, buf, 0, '|', sizeof instr);
-                       if (!strcasecmp(instr, entrytype)) {
-                               tokens = num_tokens(buf, '|');
-                               for (i=1; i<tokens; ++i) {
-                                       extract_token(addr, buf, i, '|', sizeof addr);
-                                       fprintf(tempfp, "%s", addr);
-                                       if (i < (tokens-1)) {
-                                               fprintf(tempfp, "|");
-                                       }
-                               }
-                               fprintf(tempfp, "\n");
-                       }
-               }
-       }
-       if (listing) {
-               free(listing);
-               listing = NULL;
-       }
-       fclose(tempfp);
-
-       e_ex_code = 1;  /* start with a failed exit code */
-       screen_reset();
-       stty_ctdl(SB_RESTORE);
-       editor_pid = fork();
-       cksum = file_checksum(filename);
-       if (editor_pid == 0) {
-               chmod(filename, 0600);
-               putenv("WINDOW_TITLE=Network configuration");
-               execlp(editor_paths[0], editor_paths[0], filename, NULL);
-               exit(1);
-       }
-       if (editor_pid > 0) {
-               do {
-                       e_ex_code = 0;
-                       b = ka_wait(&e_ex_code);
-               } while ((b != editor_pid) && (b >= 0));
-       editor_pid = (-1);
-       stty_ctdl(0);
-       screen_set();
-       }
-
-       if (file_checksum(filename) == cksum) {
-               err_printf("*** No changes to save.\n");
-               e_ex_code = 1;
-       }
-
-       if (e_ex_code == 0) {           /* Save changes */
-               changefp = fopen(changefile, "w");
-
-               /* Load all netconfig entries that are *not* of the type we are editing */
-               r = CtdlIPCGetRoomNetworkConfig(ipc, &listing, buf);
-               if (r / 100 == 1) {
-                       while(listing && !IsEmptyStr(listing)) {
-                               extract_token(buf, listing, 0, '\n', sizeof buf);
-                               remove_token(listing, 0, '\n');
-                               extract_token(instr, buf, 0, '|', sizeof instr);
-                               if (strcasecmp(instr, entrytype)) {
-                                       fprintf(changefp, "%s\n", buf);
-                               }
-                       }
-               }
-               if (listing) {
-                       free(listing);
-                       listing = NULL;
-               }
-
-               /* ...and merge that with the data we just edited */
-               tempfp = fopen(filename, "r");
-               while (fgets(buf, sizeof buf, tempfp) != NULL) {
-                       for (i=0; i<strlen(buf); ++i) {
-                               if (buf[i] == '#') buf[i] = 0;
-                       }
-                       striplt(buf);
-                       if (!IsEmptyStr(buf)) {
-                               fprintf(changefp, "%s|%s\n", entrytype, buf);
-                       }
-               }
-               fclose(tempfp);
-               fclose(changefp);
-
-               /* now write it to the server... */
-               changefp = fopen(changefile, "r");
-               if (changefp != NULL) {
-                       listing = load_message_from_file(changefp);
-                       if (listing) {
-                               r = CtdlIPCSetRoomNetworkConfig(ipc, listing, buf);
-                               free(listing);
-                               listing = NULL;
-                       }
-                       fclose(changefp);
-               }
-       }
-
-       unlink(filename);               /* Delete the temporary files */
-       unlink(changefile);
-}
-
-
-/*
- * IGnet node configuration
- */
-void do_ignet_configuration(CtdlIPC *ipc) {
-       char buf[SIZ];
-       int num_recs = 0;
-       char **recs = NULL;
-       char ch;
-       int badkey;
-       int i, j;
-       int quitting = 0;
-       int modified = 0;
-       char *listing = NULL;
-       int r;
-
-       r = CtdlIPCGetSystemConfigByType(ipc, IGNETCFG, &listing, buf);
-       if (r / 100 == 1) while (*listing && !IsEmptyStr(listing)) {
-               extract_token(buf, listing, 0, '\n', sizeof buf);
-               remove_token(listing, 0, '\n');
-
-               ++num_recs;
-               if (num_recs == 1) recs = malloc(sizeof(char *));
-               else recs = realloc(recs, (sizeof(char *)) * num_recs);
-               recs[num_recs-1] = malloc(SIZ);
-               strcpy(recs[num_recs-1], buf);
-       }
-       if (listing) free(listing);
-
-       do {
-               scr_printf("\n");
-               color(BRIGHT_WHITE);
-               scr_printf(     "### "
-                       "   Node          "
-                       "  Secret           "
-                       "          Host or IP             "
-                       "Port#\n");
-               color(DIM_WHITE);
-               scr_printf(     "--- "
-                       "---------------- "
-                       "------------------ "
-                       "-------------------------------- "
-                       "-----\n");
-               for (i=0; i<num_recs; ++i) {
-               color(DIM_WHITE);
-               scr_printf("%3d ", i+1);
-               extract_token(buf, recs[i], 0, '|', sizeof buf);
-               color(BRIGHT_CYAN);
-               scr_printf("%-16s ", buf);
-               extract_token(buf, recs[i], 1, '|', sizeof buf);
-               color(BRIGHT_MAGENTA);
-               scr_printf("%-18s ", buf);
-               extract_token(buf, recs[i], 2, '|', sizeof buf);
-               color(BRIGHT_CYAN);
-               scr_printf("%-32s ", buf);
-               extract_token(buf, recs[i], 3, '|', sizeof buf);
-               color(BRIGHT_MAGENTA);
-               scr_printf("%-3s\n", buf);
-               color(DIM_WHITE);
-               }
-               scr_printf("\n");
-
-               ch = keymenu("", "<A>dd|<D>elete|<S>ave|<Q>uit");
-               switch(ch) {
-                       case 'a':
-                               ++num_recs;
-                               if (num_recs == 1)
-                                       recs = malloc(sizeof(char *));
-                               else recs = realloc(recs,
-                                       (sizeof(char *)) * num_recs);
-                               newprompt("Enter node name    : ", buf, 16);
-                               strcat(buf, "|");
-                               newprompt("Enter shared secret: ",
-                                       &buf[strlen(buf)], 18);
-                               strcat(buf, "|");
-                               newprompt("Enter host or IP   : ",
-                                       &buf[strlen(buf)], 32);
-                               strcat(buf, "|504");
-                               strprompt("Enter port number  : ",
-                                       &buf[strlen(buf)-3], 5);
-                               recs[num_recs-1] = strdup(buf);
-                               modified = 1;
-                               break;
-                       case 'd':
-                               i = intprompt("Delete which one",
-                                       1, 1, num_recs) - 1;
-                               free(recs[i]);
-                               --num_recs;
-                               for (j=i; j<num_recs; ++j)
-                                       recs[j] = recs[j+1];
-                               modified = 1;
-                               break;
-                       case 's':
-                               r = 1;
-                               for (i = 0; i < num_recs; ++i)
-                                       r += 1 + strlen(recs[i]);
-                               listing = (char*) calloc(1, r);
-                               if (!listing) {
-                                       err_printf("Can't save config - out of memory!\n");
-                                       logoff(ipc, 1);
-                               }
-                               if (num_recs) for (i = 0; i < num_recs; ++i) {
-                                       strcat(listing, recs[i]);
-                                       strcat(listing, "\n");
-                               }
-                               r = CtdlIPCSetSystemConfigByType(ipc, IGNETCFG, listing, buf);
-                               if (r / 100 != 4) {
-                                       scr_printf("%s\n", buf);
-                               } else {
-                                       scr_printf("Wrote %d records.\n", num_recs);
-                                       modified = 0;
-                               }
-                free(listing);
-                               break;
-                       case 'q':
-                               quitting = !modified || boolprompt(
-                                       "Quit without saving", 0);
-                               break;
-                       default:
-                               badkey = 1;
-               }
-       } while (!quitting);
-
-       if (recs != NULL) {
-               for (i=0; i<num_recs; ++i) free(recs[i]);
-               free(recs);
-       }
-}
-
-
-/*
- * Filter list configuration
- */
-void do_filterlist_configuration(CtdlIPC *ipc)
-{
-       char buf[SIZ];
-       int num_recs = 0;
-       char **recs = NULL;
-       char ch;
-       int badkey;
-       int i, j;
-       int quitting = 0;
-       int modified = 0;
-       char *listing = NULL;
-       int r;
-
-       r = CtdlIPCGetSystemConfigByType(ipc, FILTERLIST, &listing, buf);
-       if (r / 100 == 1) while (*listing && !IsEmptyStr(listing)) {
-               extract_token(buf, listing, 0, '\n', sizeof buf);
-               remove_token(listing, 0, '\n');
-
-               ++num_recs;
-               if (num_recs == 1) recs = malloc(sizeof(char *));
-               else recs = realloc(recs, (sizeof(char *)) * num_recs);
-               recs[num_recs-1] = malloc(SIZ);
-               strcpy(recs[num_recs-1], buf);
-       }
-       if (listing) free(listing);
-
-       do {
-               scr_printf("\n");
-               color(BRIGHT_WHITE);
-               scr_printf(     "### "
-                       "         User name           "
-                       "         Room name           "
-                       "    Node name    "
-                       "\n");
-               color(DIM_WHITE);
-               scr_printf(     "--- "
-                       "---------------------------- "
-                       "---------------------------- "
-                       "---------------- "
-                       "\n");
-               for (i=0; i<num_recs; ++i) {
-               color(DIM_WHITE);
-               scr_printf("%3d ", i+1);
-               extract_token(buf, recs[i], 0, '|', sizeof buf);
-               color(BRIGHT_CYAN);
-               scr_printf("%-28s ", buf);
-               extract_token(buf, recs[i], 1, '|', sizeof buf);
-               color(BRIGHT_MAGENTA);
-               scr_printf("%-28s ", buf);
-               extract_token(buf, recs[i], 2, '|', sizeof buf);
-               color(BRIGHT_CYAN);
-               scr_printf("%-16s\n", buf);
-               extract_token(buf, recs[i], 3, '|', sizeof buf);
-               color(DIM_WHITE);
-               }
-
-               ch = keymenu("", "<A>dd|<D>elete|<S>ave|<Q>uit");
-               switch(ch) {
-                       case 'a':
-                               ++num_recs;
-                               if (num_recs == 1)
-                                       recs = malloc(sizeof(char *));
-                               else recs = realloc(recs,
-                                       (sizeof(char *)) * num_recs);
-                               newprompt("Enter user name: ", buf, 28);
-                               strcat(buf, "|");
-                               newprompt("Enter room name: ",
-                                       &buf[strlen(buf)], 28);
-                               strcat(buf, "|");
-                               newprompt("Enter node name: ",
-                                       &buf[strlen(buf)], 16);
-                               strcat(buf, "|");
-                               recs[num_recs-1] = strdup(buf);
-                               modified = 1;
-                               break;
-                       case 'd':
-                               i = intprompt("Delete which one",
-                                       1, 1, num_recs) - 1;
-                               free(recs[i]);
-                               --num_recs;
-                               for (j=i; j<num_recs; ++j)
-                                       recs[j] = recs[j+1];
-                               modified = 1;
-                               break;
-                       case 's':
-                               r = 1;
-                               for (i = 0; i < num_recs; ++i)
-                                       r += 1 + strlen(recs[i]);
-                               listing = (char*) calloc(1, r);
-                               if (!listing) {
-                                       err_printf("Can't save config - out of memory!\n");
-                                       logoff(ipc, 1);
-                               }
-                               if (num_recs) for (i = 0; i < num_recs; ++i) {
-                                       strcat(listing, recs[i]);
-                                       strcat(listing, "\n");
-                               }
-                               r = CtdlIPCSetSystemConfigByType(ipc, FILTERLIST, listing, buf);
-                               if (r / 100 != 4) {
-                                       scr_printf("%s\n", buf);
-                               } else {
-                                       scr_printf("Wrote %d records.\n", num_recs);
-                                       modified = 0;
-                               }
-                free(listing);
-                               break;
-                       case 'q':
-                               quitting = !modified || boolprompt(
-                                       "Quit without saving", 0);
-                               break;
-                       default:
-                               badkey = 1;
-               }
-       } while (!quitting);
-
-       if (recs != NULL) {
-               for (i=0; i<num_recs; ++i) free(recs[i]);
-               free(recs);
-       }
-}
-
-
-
-
-/*
- * POP3 aggregation client configuration
- */
-void do_pop3client_configuration(CtdlIPC *ipc)
-{
-       char buf[SIZ];
-       int num_recs = 0;
-       char **recs = NULL;
-       char ch;
-       int badkey;
-       int i, j;
-       int quitting = 0;
-       int modified = 0;
-       char *listing = NULL;
-       char *other_listing = NULL;
-       int r;
-       char instr[SIZ];
-
-       r = CtdlIPCGetRoomNetworkConfig(ipc, &listing, buf);
-       if (r / 100 == 1) {
-               while(listing && !IsEmptyStr(listing)) {
-                       extract_token(buf, listing, 0, '\n', sizeof buf);
-                       remove_token(listing, 0, '\n');
-                       extract_token(instr, buf, 0, '|', sizeof instr);
-                       if (!strcasecmp(instr, "pop3client")) {
-
-                               ++num_recs;
-                               if (num_recs == 1) recs = malloc(sizeof(char *));
-                               else recs = realloc(recs, (sizeof(char *)) * num_recs);
-                               recs[num_recs-1] = malloc(SIZ);
-                               strcpy(recs[num_recs-1], buf);
-
-                       }
-               }
-       }
-       if (listing) {
-               free(listing);
-               listing = NULL;
-       }
-
-       do {
-               scr_printf("\n");
-               color(BRIGHT_WHITE);
-               scr_printf(     "### "
-                       "      Remote POP3 host       "
-                       "         User name           "
-                       "Keep on server? "
-                       "Fetching inteval"
-                       "\n");
-               color(DIM_WHITE);
-               scr_printf(     "--- "
-                       "---------------------------- "
-                       "---------------------------- "
-                       "--------------- "
-                       "---------------- "
-                       "\n");
-               for (i=0; i<num_recs; ++i) {
-               color(DIM_WHITE);
-               scr_printf("%3d ", i+1);
-
-               extract_token(buf, recs[i], 1, '|', sizeof buf);
-               color(BRIGHT_CYAN);
-               scr_printf("%-28s ", buf);
-
-               extract_token(buf, recs[i], 2, '|', sizeof buf);
-               color(BRIGHT_MAGENTA);
-               scr_printf("%-28s ", buf);
-
-               color(BRIGHT_CYAN);
-               scr_printf("%-15s ", (extract_int(recs[i], 4) ? "Yes" : "No") );
-               color(BRIGHT_MAGENTA);
-               scr_printf("%ld\n", extract_long(recs[i], 5) );
-               color(DIM_WHITE);
-               }
-
-               ch = keymenu("", "<A>dd|<D>elete|<S>ave|<Q>uit");
-               switch(ch) {
-                       case 'a':
-                               ++num_recs;
-                               if (num_recs == 1) {
-                                       recs = malloc(sizeof(char *));
-                               }
-                               else {
-                                       recs = realloc(recs, (sizeof(char *)) * num_recs);
-                               }
-                               strcpy(buf, "pop3client|");
-                               newprompt("Enter host name: ", &buf[strlen(buf)], 28);
-                               strcat(buf, "|");
-                               newprompt("Enter user name: ", &buf[strlen(buf)], 28);
-                               strcat(buf, "|");
-                               newprompt("Enter password : ", &buf[strlen(buf)], 16);
-                               strcat(buf, "|");
-                               scr_printf("Keep messages on server instead of deleting them? ");
-                               sprintf(&buf[strlen(buf)], "%d", yesno());
-                               strcat(buf, "|");
-                               newprompt("Enter interval : ", &buf[strlen(buf)], 5);
-                               strcat(buf, "|");
-                               recs[num_recs-1] = strdup(buf);
-                               modified = 1;
-                               break;
-                       case 'd':
-                               i = intprompt("Delete which one",
-                                       1, 1, num_recs) - 1;
-                               free(recs[i]);
-                               --num_recs;
-                               for (j=i; j<num_recs; ++j)
-                                       recs[j] = recs[j+1];
-                               modified = 1;
-                               break;
-                       case 's':
-                               r = 1;
-                               for (i = 0; i < num_recs; ++i) {
-                                       r += 1 + strlen(recs[i]);
-                               }
-                               listing = (char*) calloc(1, r);
-                               if (!listing) {
-                                       err_printf("Can't save config - out of memory!\n");
-                                       logoff(ipc, 1);
-                               }
-                               if (num_recs) for (i = 0; i < num_recs; ++i) {
-                                       strcat(listing, recs[i]);
-                                       strcat(listing, "\n");
-                               }
-
-                               /* Retrieve all the *other* records for merging */
-                               r = CtdlIPCGetRoomNetworkConfig(ipc, &other_listing, buf);
-                               if (r / 100 == 1) {
-                                       for (i=0; i<num_tokens(other_listing, '\n'); ++i) {
-                                               extract_token(buf, other_listing, i, '\n', sizeof buf);
-                                               if (strncasecmp(buf, "pop3client|", 11)) {
-                                                       listing = realloc(listing, strlen(listing) +
-                                                               strlen(buf) + 10);
-                                                       strcat(listing, buf);
-                                                       strcat(listing, "\n");
-                                               }
-                                       }
-                               }
-                               free(other_listing);
-                               r = CtdlIPCSetRoomNetworkConfig(ipc, listing, buf);
-                               free(listing);
-                               listing = NULL;
-
-                               if (r / 100 != 4) {
-                                       scr_printf("%s\n", buf);
-                               } else {
-                                       scr_printf("Wrote %d records.\n", num_recs);
-                                       modified = 0;
-                               }
-                               quitting = 1;
-                               break;
-                       case 'q':
-                               quitting = !modified || boolprompt(
-                                       "Quit without saving", 0);
-                               break;
-                       default:
-                               badkey = 1;
-               }
-       } while (!quitting);
-
-       if (recs != NULL) {
-               for (i=0; i<num_recs; ++i) free(recs[i]);
-               free(recs);
-       }
-}
-
-
-
-
-
-
-/*
- * RSS feed retrieval client configuration
- */
-void do_rssclient_configuration(CtdlIPC *ipc)
-{
-       char buf[SIZ];
-       int num_recs = 0;
-       char **recs = NULL;
-       char ch;
-       int badkey;
-       int i, j;
-       int quitting = 0;
-       int modified = 0;
-       char *listing = NULL;
-       char *other_listing = NULL;
-       int r;
-       char instr[SIZ];
-
-       r = CtdlIPCGetRoomNetworkConfig(ipc, &listing, buf);
-       if (r / 100 == 1) {
-               while(listing && !IsEmptyStr(listing)) {
-                       extract_token(buf, listing, 0, '\n', sizeof buf);
-                       remove_token(listing, 0, '\n');
-                       extract_token(instr, buf, 0, '|', sizeof instr);
-                       if (!strcasecmp(instr, "rssclient")) {
-
-                               ++num_recs;
-                               if (num_recs == 1) recs = malloc(sizeof(char *));
-                               else recs = realloc(recs, (sizeof(char *)) * num_recs);
-                               recs[num_recs-1] = malloc(SIZ);
-                               strcpy(recs[num_recs-1], buf);
-
-                       }
-               }
-       }
-       if (listing) {
-               free(listing);
-               listing = NULL;
-       }
-
-       do {
-               scr_printf("\n");
-               color(BRIGHT_WHITE);
-               scr_printf("### Feed URL\n");
-               color(DIM_WHITE);
-               scr_printf("--- "
-                       "---------------------------------------------------------------------------"
-                       "\n");
-               
-               for (i=0; i<num_recs; ++i) {
-               color(DIM_WHITE);
-               scr_printf("%3d ", i+1);
-
-               extract_token(buf, recs[i], 1, '|', sizeof buf);
-               color(BRIGHT_CYAN);
-               scr_printf("%-75s\n", buf);
-
-               color(DIM_WHITE);
-               }
-
-               ch = keymenu("", "<A>dd|<D>elete|<S>ave|<Q>uit");
-               switch(ch) {
-                       case 'a':
-                               ++num_recs;
-                               if (num_recs == 1) {
-                                       recs = malloc(sizeof(char *));
-                               }
-                               else {
-                                       recs = realloc(recs, (sizeof(char *)) * num_recs);
-                               }
-                               strcpy(buf, "rssclient|");
-                               newprompt("Enter feed URL: ", &buf[strlen(buf)], 75);
-                               strcat(buf, "|");
-                               recs[num_recs-1] = strdup(buf);
-                               modified = 1;
-                               break;
-                       case 'd':
-                               i = intprompt("Delete which one", 1, 1, num_recs) - 1;
-                               free(recs[i]);
-                               --num_recs;
-                               for (j=i; j<num_recs; ++j)
-                                       recs[j] = recs[j+1];
-                               modified = 1;
-                               break;
-                       case 's':
-                               r = 1;
-                               for (i = 0; i < num_recs; ++i) {
-                                       r += 1 + strlen(recs[i]);
-                               }
-                               listing = (char*) calloc(1, r);
-                               if (!listing) {
-                                       err_printf("Can't save config - out of memory!\n");
-                                       logoff(ipc, 1);
-                               }
-                               if (num_recs) for (i = 0; i < num_recs; ++i) {
-                                       strcat(listing, recs[i]);
-                                       strcat(listing, "\n");
-                               }
-
-                               /* Retrieve all the *other* records for merging */
-                               r = CtdlIPCGetRoomNetworkConfig(ipc, &other_listing, buf);
-                               if (r / 100 == 1) {
-                                       for (i=0; i<num_tokens(other_listing, '\n'); ++i) {
-                                               extract_token(buf, other_listing, i, '\n', sizeof buf);
-                                               if (strncasecmp(buf, "rssclient|", 10)) {
-                                                       listing = realloc(listing, strlen(listing) +
-                                                               strlen(buf) + 10);
-                                                       strcat(listing, buf);
-                                                       strcat(listing, "\n");
-                                               }
-                                       }
-                               }
-                               free(other_listing);
-                               r = CtdlIPCSetRoomNetworkConfig(ipc, listing, buf);
-                               free(listing);
-                               listing = NULL;
-
-                               if (r / 100 != 4) {
-                                       scr_printf("%s\n", buf);
-                               } else {
-                                       scr_printf("Wrote %d records.\n", num_recs);
-                                       modified = 0;
-                               }
-                               quitting = 1;
-                               break;
-                       case 'q':
-                               quitting = !modified || boolprompt(
-                                       "Quit without saving", 0);
-                               break;
-                       default:
-                               badkey = 1;
-               }
-       } while (!quitting);
-
-       if (recs != NULL) {
-               for (i=0; i<num_recs; ++i) free(recs[i]);
-               free(recs);
-       }
-}
-
-
diff --git a/citadel/tuiconfig.h b/citadel/tuiconfig.h
deleted file mode 100644 (file)
index 2d0a5d4..0000000
+++ /dev/null
@@ -1,8 +0,0 @@
-/* $Id$ */
-void do_internet_configuration(CtdlIPC *ipc);
-void do_ignet_configuration(CtdlIPC *ipc);
-void network_config_management(CtdlIPC *ipc, char *entrytype, char *comment);
-void do_filterlist_configuration(CtdlIPC *ipc);
-void do_pop3client_configuration(CtdlIPC *ipc);
-void do_rssclient_configuration(CtdlIPC *ipc);
-void do_system_configuration(CtdlIPC *ipc);
diff --git a/citadel/userlist.c b/citadel/userlist.c
deleted file mode 100644 (file)
index 3f78cea..0000000
+++ /dev/null
@@ -1,115 +0,0 @@
-/*
- * $Id$
- *
- * Command-line user list utility.
- *
- */
-
-#include <stdlib.h>
-#include <unistd.h>
-#include <stdio.h>
-#include <string.h>
-
-#if TIME_WITH_SYS_TIME
-# include <sys/time.h>
-# include <time.h>
-#else
-# if HAVE_SYS_TIME_H
-#  include <sys/time.h>
-# else
-#  include <time.h>
-# endif
-#endif
-
-#include <libcitadel.h>
-#include "citadel.h"
-#include <unistd.h>
-#include "citadel_ipc.h"
-#include "citadel_dirs.h"
-
-void logoff(int code)
-{
-       exit(code);
-}
-
-void userlist(CtdlIPC *ipc) { 
-       char buf[SIZ];
-       char fl[SIZ];
-       struct tm tmbuf;
-       time_t lc;
-       char *listing = NULL;
-       int r;
-
-       r = CtdlIPCUserListing(ipc, "", &listing, buf);
-       if (r / 100 != 1) {
-               printf("%s\n", buf);
-               return;
-       }
-       printf("       User Name           Num  L Last Visit Logins Messages\n");
-       printf("------------------------- ----- - ---------- ------ --------\n");
-       while (strlen(listing) > 0) {
-               extract_token(buf, listing, 0, '\n', sizeof buf);
-               remove_token(listing, 0, '\n');
-               extract_token(fl, buf, 0, '|', sizeof fl);
-               printf("%-25s ",fl);
-               printf("%5ld %d ", extract_long(buf,2),
-                       extract_int(buf,1));
-               lc = extract_long(buf,3);
-               localtime_r(&lc, &tmbuf);
-               printf("%02d/%02d/%04d ",
-                       (tmbuf.tm_mon+1),
-                       tmbuf.tm_mday,
-                       (tmbuf.tm_year + 1900));
-               printf("%6ld %8ld\n",
-                       extract_long(buf,4),extract_long(buf,5));
-       }
-       printf("\n");
-}
-
-
-int main(int argc, char **argv)
-{
-       char buf[SIZ];
-       char hostbuf[SIZ], portbuf[SIZ];
-       CtdlIPC *ipc = NULL;
-       int relh=0;
-       int home=0;
-       char relhome[PATH_MAX]="";
-       char ctdldir[PATH_MAX]=CTDLDIR;
-
-       calc_dirs_n_files(relh, home, relhome, ctdldir, 0);
-
-       ipc = CtdlIPC_new(argc, argv, hostbuf, portbuf);
-       CtdlIPC_chat_recv(ipc, buf);
-       if ((buf[0]!='2')&&(strncmp(buf,"551",3))) {
-               fprintf(stderr,"%s: %s\n",argv[0],&buf[4]);
-               logoff(atoi(buf));
-       }
-
-       userlist(ipc);
-
-       CtdlIPCQuit(ipc);
-       exit(0);
-}
-
-
-#ifndef HAVE_STRERROR
-/*
- * replacement strerror() for systems that don't have it
- */
-char *strerror(int e)
-{
-       static char buf[32];
-
-       snprintf(buf, sizeof buf, "errno = %d",e);
-       return(buf);
-}
-#endif
-
-
-/*
- * Stub function
- */
-void stty_ctdl(int cmd) {
-}
-
diff --git a/citadel/utillib/citadel_dirs.c b/citadel/utillib/citadel_dirs.c
new file mode 100644 (file)
index 0000000..9bc46c8
--- /dev/null
@@ -0,0 +1,377 @@
+/*
+ * citadel_dirs.c : calculate pathnames for various files used in the Citadel system
+ *
+ * Copyright (c) 1987-2009 by the citadel.org team
+ *
+ *  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 3 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; if not, write to the Free Software
+ *  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ */
+#include <unistd.h>
+#include <stdio.h>
+#include <sys/stat.h>
+#include <ctype.h>
+#include <string.h>
+#include <limits.h>
+
+
+#if TIME_WITH_SYS_TIME
+# include <sys/time.h>
+# include <time.h>
+#else
+# if HAVE_SYS_TIME_H
+#  include <sys/time.h>
+# else
+#  include <time.h>
+# endif
+#endif
+
+#include <errno.h>
+#include <libcitadel.h>
+
+
+#include "citadel.h"
+
+/* our directories... */
+char ctdl_home_directory[PATH_MAX] = "";
+char ctdl_bio_dir[PATH_MAX]="bio";
+char ctdl_bb_dir[PATH_MAX]="bitbucket";
+char ctdl_data_dir[PATH_MAX]="data";
+char ctdl_dspam_dir[PATH_MAX]="dspam";
+char ctdl_file_dir[PATH_MAX]="files";
+char ctdl_hlp_dir[PATH_MAX]="help";
+char ctdl_shared_dir[PATH_MAX]="";
+char ctdl_image_dir[PATH_MAX]="images";
+char ctdl_info_dir[PATH_MAX]="info";
+char ctdl_key_dir[PATH_MAX]=SSL_DIR;
+char ctdl_message_dir[PATH_MAX]="messages";
+char ctdl_usrpic_dir[PATH_MAX]="userpics";
+char ctdl_bbsbase_dir[PATH_MAX]="";
+char ctdl_etc_dir[PATH_MAX]="";
+char ctdl_autoetc_dir[PATH_MAX]="";
+/* attention! this may be non volatile on some oses */
+char ctdl_run_dir[PATH_MAX]="";
+char ctdl_spool_dir[PATH_MAX]="network";
+char ctdl_netout_dir[PATH_MAX]="network/spoolout";
+char ctdl_netin_dir[PATH_MAX]="network/spoolin";
+char ctdl_netcfg_dir[PATH_MAX]="netconfigs";
+char ctdl_utilbin_dir[PATH_MAX]="";
+char ctdl_sbin_dir[PATH_MAX]="";
+char ctdl_bin_dir[PATH_MAX]="";
+
+/* some of our files, that are needed in several places */
+char file_citadel_control[PATH_MAX]="";
+char file_citadel_rc[PATH_MAX]="";
+char file_citadel_config[PATH_MAX]="";
+char file_lmtp_socket[PATH_MAX]="";
+char file_lmtp_unfiltered_socket[PATH_MAX]="";
+char file_arcq[PATH_MAX]="";
+char file_citadel_socket[PATH_MAX]="";
+char file_mail_aliases[PATH_MAX]="";
+char file_pid_file[PATH_MAX]="";
+char file_pid_paniclog[PATH_MAX]="";
+char file_crpt_file_key[PATH_MAX]="";
+char file_crpt_file_csr[PATH_MAX]="";
+char file_crpt_file_cer[PATH_MAX]="";
+char file_chkpwd[PATH_MAX]="";
+char file_base64[PATH_MAX]="";
+char file_guesstimezone[PATH_MAX]="";
+char file_funambol_msg[PATH_MAX] = "";
+char file_dpsam_conf[PATH_MAX] = "";
+char file_dspam_log[PATH_MAX] = "";
+
+
+
+
+
+#define COMPUTE_DIRECTORY(SUBDIR) memcpy(dirbuffer,SUBDIR, sizeof dirbuffer);\
+       snprintf(SUBDIR,sizeof SUBDIR,  "%s%s%s%s%s%s%s", \
+                        (home&!relh)?ctdl_home_directory:basedir, \
+             ((basedir!=ctdldir)&(home&!relh))?basedir:"/", \
+             ((basedir!=ctdldir)&(home&!relh))?"/":"", \
+                        relhome, \
+             (relhome[0]!='\0')?"/":"",\
+                        dirbuffer,\
+                        (dirbuffer[0]!='\0')?"/":"");
+
+#define DBG_PRINT(A) if (dbg==1) fprintf (stderr,"%s : %s \n", #A, A)
+
+
+void calc_dirs_n_files(int relh, int home, const char *relhome, char  *ctdldir, int dbg)
+{
+       const char* basedir = "";
+       char dirbuffer[PATH_MAX] = "";
+
+       /*
+        * Ok, we keep our binaries either in the citadel base dir,
+        * or in /usr/sbin / /usr/bin
+        */
+       StripSlashes(ctdldir, 1);
+#ifdef HAVE_ETC_DIR
+       snprintf(ctdl_sbin_dir, sizeof ctdl_sbin_dir, "/usr/sbin/");
+       snprintf(ctdl_bin_dir, sizeof ctdl_bin_dir, "/usr/bin/");
+#else
+       snprintf(ctdl_sbin_dir, sizeof ctdl_sbin_dir, ctdldir);
+       snprintf(ctdl_bin_dir, sizeof ctdl_bin_dir, ctdldir);
+#endif
+       StripSlashes(ctdl_sbin_dir, 1);
+       StripSlashes(ctdl_bin_dir, 1);
+
+#ifndef HAVE_AUTO_ETC_DIR
+       basedir=ctdldir;
+#else
+       basedir=AUTO_ETC_DIR;
+#endif
+       COMPUTE_DIRECTORY(ctdl_autoetc_dir);
+       StripSlashes(ctdl_autoetc_dir, 1);
+
+#ifndef HAVE_ETC_DIR
+       basedir=ctdldir;
+#else
+       basedir=ETC_DIR;
+#endif
+       COMPUTE_DIRECTORY(ctdl_netcfg_dir);
+       COMPUTE_DIRECTORY(ctdl_etc_dir);
+       StripSlashes(ctdl_netcfg_dir, 1);
+       StripSlashes(ctdl_etc_dir, 1);
+
+#ifndef HAVE_UTILBIN_DIR
+       basedir=ctdldir;
+#else
+       basedir=UTILBIN_DIR;
+#endif
+       COMPUTE_DIRECTORY(ctdl_utilbin_dir);
+       StripSlashes(ctdl_utilbin_dir, 1);
+
+#ifndef HAVE_RUN_DIR
+       basedir=ctdldir;
+#else
+       basedir=RUN_DIR;
+#endif
+       COMPUTE_DIRECTORY(ctdl_run_dir);
+       StripSlashes(ctdl_run_dir, 1);
+
+#ifndef HAVE_STATICDATA_DIR
+       basedir=ctdldir;
+#else
+       basedir=STATICDATA_DIR;
+#endif
+       COMPUTE_DIRECTORY(ctdl_message_dir);
+       StripSlashes(ctdl_message_dir, 1);
+
+#ifndef HAVE_HELP_DIR
+       basedir=ctdldir;
+#else
+       basedir=HELP_DIR;
+#endif
+       COMPUTE_DIRECTORY(ctdl_hlp_dir);
+       StripSlashes(ctdl_hlp_dir, 1);
+       COMPUTE_DIRECTORY(ctdl_shared_dir);
+       StripSlashes(ctdl_shared_dir, 1);
+
+#ifndef HAVE_DATA_DIR
+       basedir=ctdldir;
+#else
+       basedir=DATA_DIR;
+#endif
+       COMPUTE_DIRECTORY(ctdl_bio_dir);
+       COMPUTE_DIRECTORY(ctdl_bb_dir);
+       COMPUTE_DIRECTORY(ctdl_data_dir);
+       COMPUTE_DIRECTORY(ctdl_dspam_dir);
+       COMPUTE_DIRECTORY(ctdl_file_dir);
+       COMPUTE_DIRECTORY(ctdl_image_dir);
+       COMPUTE_DIRECTORY(ctdl_info_dir);
+       COMPUTE_DIRECTORY(ctdl_usrpic_dir);
+       COMPUTE_DIRECTORY(ctdl_bbsbase_dir);
+
+       StripSlashes(ctdl_bio_dir, 1);
+       StripSlashes(ctdl_bb_dir, 1);
+       StripSlashes(ctdl_data_dir, 1);
+       StripSlashes(ctdl_dspam_dir, 1);
+       StripSlashes(ctdl_file_dir, 1);
+       StripSlashes(ctdl_image_dir, 1);
+       StripSlashes(ctdl_info_dir, 1);
+       StripSlashes(ctdl_usrpic_dir, 1);
+       StripSlashes(ctdl_bbsbase_dir, 1);
+
+#ifndef HAVE_SPOOL_DIR
+       basedir=ctdldir;
+#else
+       basedir=SPOOL_DIR;
+#endif
+       COMPUTE_DIRECTORY(ctdl_spool_dir);
+       COMPUTE_DIRECTORY(ctdl_netout_dir);
+       COMPUTE_DIRECTORY(ctdl_netin_dir);
+
+       StripSlashes(ctdl_spool_dir, 1);
+       StripSlashes(ctdl_netout_dir, 1);
+       StripSlashes(ctdl_netin_dir, 1);
+
+       /* ok, now we know the dirs, calc some commonly used files */
+
+       snprintf(file_arcq, 
+                        sizeof file_arcq,
+                        "%srefcount_adjustments.dat",
+                        ctdl_autoetc_dir);
+       StripSlashes(file_arcq, 0);
+       snprintf(file_citadel_control, 
+                        sizeof file_citadel_control,
+                        "%scitadel.control",
+                        ctdl_autoetc_dir
+                        );
+       StripSlashes(file_citadel_control, 0);
+       snprintf(file_citadel_config, 
+                        sizeof file_citadel_config,
+                        "%scitadel.config",
+                        ctdl_autoetc_dir);
+       StripSlashes(file_citadel_config, 0);
+       snprintf(file_citadel_rc, 
+                        sizeof file_citadel_rc,
+                        "%scitadel.rc",
+                        ctdl_etc_dir);
+       StripSlashes(file_citadel_rc, 0);
+       snprintf(file_lmtp_socket, 
+                        sizeof file_lmtp_socket,
+                        "%slmtp.socket",
+                        ctdl_run_dir);
+       StripSlashes(file_lmtp_socket, 0);
+       snprintf(file_lmtp_unfiltered_socket, 
+                        sizeof file_lmtp_socket,
+                        "%slmtp-unfiltered.socket",
+                        ctdl_run_dir);
+       StripSlashes(file_lmtp_unfiltered_socket, 0);
+       snprintf(file_citadel_socket, 
+                        sizeof file_citadel_socket,
+                               "%scitadel.socket",
+                        ctdl_run_dir);
+       StripSlashes(file_citadel_socket, 0);
+       snprintf(file_pid_file, 
+                sizeof file_pid_file,
+                "%scitadel.pid",
+                ctdl_run_dir);
+       StripSlashes(file_pid_file, 0);
+       snprintf(file_pid_paniclog, 
+                sizeof file_pid_paniclog, 
+                "%spanic.log",
+                ctdl_home_directory);
+       StripSlashes(file_pid_paniclog, 0);
+       snprintf(file_crpt_file_key,
+                sizeof file_crpt_file_key, 
+                "%s/citadel.key",
+                ctdl_key_dir);
+       StripSlashes(file_crpt_file_key, 0);
+       snprintf(file_crpt_file_csr,
+                sizeof file_crpt_file_csr, 
+                "%s/citadel.csr",
+                ctdl_key_dir);
+       StripSlashes(file_crpt_file_csr, 0);
+       snprintf(file_crpt_file_cer,
+                sizeof file_crpt_file_cer, 
+                "%s/citadel.cer",
+                ctdl_key_dir);
+       StripSlashes(file_crpt_file_cer, 0);
+       snprintf(file_chkpwd,
+                sizeof file_chkpwd, 
+                "%schkpwd",
+                ctdl_utilbin_dir);
+       StripSlashes(file_chkpwd, 0);
+       snprintf(file_base64,
+                sizeof file_base64,
+                "%sbase64",
+                ctdl_utilbin_dir);
+       StripSlashes(file_base64, 0);
+       snprintf(file_guesstimezone,
+                sizeof file_guesstimezone,
+                "%sguesstimezone.sh",
+                ctdl_utilbin_dir);
+
+       snprintf(file_dpsam_conf,
+                sizeof file_dpsam_conf,
+                "%sdspam.conf",
+                ctdl_etc_dir);
+       StripSlashes(file_dpsam_conf, 0);
+       snprintf(file_dspam_log, 
+                sizeof file_dspam_log, 
+                "%sdspam.log",
+                ctdl_home_directory);
+       StripSlashes(file_dspam_log, 0);
+       /* 
+        * DIRTY HACK FOLLOWS! due to configs in the network dir in the 
+        * legacy installations, we need to calculate ifdeffed here.
+        */
+       snprintf(file_mail_aliases, 
+                sizeof file_mail_aliases,
+                "%smail.aliases",
+#ifdef HAVE_ETC_DIR
+                ctdl_etc_dir
+#else
+                ctdl_spool_dir
+#endif
+               );
+       StripSlashes(file_mail_aliases, 0);
+        snprintf(file_funambol_msg,
+                sizeof file_funambol_msg,
+                "%sfunambol_newmail_soap.xml",
+                ctdl_shared_dir);
+       StripSlashes(file_funambol_msg, 0);
+
+       DBG_PRINT(ctdl_bio_dir);
+       DBG_PRINT(ctdl_bb_dir);
+       DBG_PRINT(ctdl_data_dir);
+       DBG_PRINT(ctdl_dspam_dir);
+       DBG_PRINT(ctdl_file_dir);
+       DBG_PRINT(ctdl_hlp_dir);
+       DBG_PRINT(ctdl_image_dir);
+       DBG_PRINT(ctdl_info_dir);
+       DBG_PRINT(ctdl_key_dir);
+       DBG_PRINT(ctdl_message_dir);
+       DBG_PRINT(ctdl_usrpic_dir);
+       DBG_PRINT(ctdl_etc_dir);
+       DBG_PRINT(ctdl_run_dir);
+       DBG_PRINT(ctdl_spool_dir);
+       DBG_PRINT(ctdl_netout_dir);
+       DBG_PRINT(ctdl_netin_dir);
+       DBG_PRINT(ctdl_netcfg_dir);
+       DBG_PRINT(ctdl_bbsbase_dir);
+       DBG_PRINT(ctdl_sbin_dir);
+       DBG_PRINT(ctdl_bin_dir);
+       DBG_PRINT(ctdl_utilbin_dir);
+       DBG_PRINT(file_citadel_control);
+       DBG_PRINT(file_citadel_rc);
+       DBG_PRINT(file_citadel_config);
+       DBG_PRINT(file_lmtp_socket);
+       DBG_PRINT(file_lmtp_unfiltered_socket);
+       DBG_PRINT(file_arcq);
+       DBG_PRINT(file_citadel_socket);
+       DBG_PRINT(file_mail_aliases);
+       DBG_PRINT(file_pid_file);
+       DBG_PRINT(file_pid_paniclog);
+       DBG_PRINT(file_crpt_file_key);
+       DBG_PRINT(file_crpt_file_csr);
+       DBG_PRINT(file_crpt_file_cer);
+       DBG_PRINT(file_chkpwd);
+       DBG_PRINT(file_base64);
+       DBG_PRINT(file_guesstimezone);
+       DBG_PRINT(file_funambol_msg);
+}
+
+
+/*
+ * Generate an associated file name for a room
+ */
+void assoc_file_name(char *buf, size_t n,
+                    struct ctdlroom *qrbuf, const char *prefix)
+{
+       snprintf(buf, n, "%s%ld", prefix, qrbuf->QRnumber);
+}
+
diff --git a/citadel/utillib/citadel_ipc.c b/citadel/utillib/citadel_ipc.c
new file mode 100644 (file)
index 0000000..ef90228
--- /dev/null
@@ -0,0 +1,3328 @@
+/* $Id$ 
+ *
+ * Copyright (c) 1987-2009 by the citadel.org team
+ *
+ *  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 3 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; if not, write to the Free Software
+ *  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ */
+
+#include "sysdep.h"
+#if TIME_WITH_SYS_TIME
+# include <sys/time.h>
+# include <time.h>
+#else
+# if HAVE_SYS_TIME_H
+#  include <sys/time.h>
+# else
+#  include <time.h>
+# endif
+#endif
+#include <unistd.h>
+#include <stdio.h>
+#include <sys/types.h>
+#include <string.h>
+#ifdef HAVE_MALLOC_H
+#include <malloc.h>
+#endif
+#include <stdlib.h>
+#include <ctype.h>
+#include <sys/socket.h>
+#include <arpa/inet.h>
+#include <netinet/in.h>
+#include <netdb.h>
+#include <sys/un.h>
+#include <errno.h>
+#ifdef THREADED_CLIENT
+#include <pthread.h>
+#endif
+#include <libcitadel.h>
+#include "citadel.h"
+#include "citadel_ipc.h"
+#include "citadel_decls.h"
+#include "citadel_dirs.h"
+#ifdef THREADED_CLIENT
+pthread_mutex_t rwlock;
+#endif
+
+#ifdef HAVE_OPENSSL
+static SSL_CTX *ssl_ctx;
+char arg_encrypt;
+char rc_encrypt;
+#ifdef THREADED_CLIENT
+pthread_mutex_t **Critters;                    /* Things that need locking */
+#endif /* THREADED_CLIENT */
+
+#endif /* HAVE_OPENSSL */
+
+#ifndef INADDR_NONE
+#define INADDR_NONE 0xffffffff
+#endif
+
+static void (*status_hook)(char *s) = NULL;
+
+void setCryptoStatusHook(void (*hook)(char *s)) {
+       status_hook = hook;
+}
+
+void CtdlIPC_SetNetworkStatusCallback(CtdlIPC *ipc, void (*hook)(int state)) {
+       ipc->network_status_cb = hook;
+}
+
+
+char instant_msgs = 0;
+
+
+static void serv_read(CtdlIPC *ipc, char *buf, unsigned int bytes);
+static void serv_write(CtdlIPC *ipc, const char *buf, unsigned int nbytes);
+#ifdef HAVE_OPENSSL
+static void serv_read_ssl(CtdlIPC *ipc, char *buf, unsigned int bytes);
+static void serv_write_ssl(CtdlIPC *ipc, const char *buf, unsigned int nbytes);
+static void endtls(SSL *ssl);
+#ifdef THREADED_CLIENT
+static unsigned long id_callback(void);
+#endif /* THREADED_CLIENT */
+#endif /* HAVE_OPENSSL */
+static void CtdlIPC_getline(CtdlIPC* ipc, char *buf);
+static void CtdlIPC_putline(CtdlIPC *ipc, const char *buf);
+
+
+
+const char *svn_revision(void);
+
+/*
+ * Does nothing.  The server should always return 200.
+ */
+int CtdlIPCNoop(CtdlIPC *ipc)
+{
+       char aaa[128];
+
+       return CtdlIPCGenericCommand(ipc, "NOOP", NULL, 0, NULL, NULL, aaa);
+}
+
+
+/*
+ * Does nothing interesting.  The server should always return 200
+ * along with your string.
+ */
+int CtdlIPCEcho(CtdlIPC *ipc, const char *arg, char *cret)
+{
+       register int ret;
+       char *aaa;
+       
+       if (!arg) return -2;
+       if (!cret) return -2;
+
+       aaa = (char *)malloc((size_t)(strlen(arg) + 6));
+       if (!aaa) return -1;
+
+       sprintf(aaa, "ECHO %s", arg);
+       ret = CtdlIPCGenericCommand(ipc, aaa, NULL, 0, NULL, NULL, cret);
+       free(aaa);
+       return ret;
+}
+
+
+/*
+ * Asks the server to close the connecction.
+ * Should always return 200.
+ */
+int CtdlIPCQuit(CtdlIPC *ipc)
+{
+       register int ret = 221;         /* Default to successful quit */
+       char aaa[SIZ]; 
+
+       CtdlIPC_lock(ipc);
+       if (ipc->sock > -1) {
+               CtdlIPC_putline(ipc, "QUIT");
+               CtdlIPC_getline(ipc, aaa);
+               ret = atoi(aaa);
+       }
+#ifdef HAVE_OPENSSL
+       if (ipc->ssl)
+               SSL_shutdown(ipc->ssl);
+       ipc->ssl = NULL;
+#endif
+       if (ipc->sock)
+               shutdown(ipc->sock, 2); /* Close connection; we're dead */
+       ipc->sock = -1;
+       CtdlIPC_unlock(ipc);
+       return ret;
+}
+
+
+/*
+ * Asks the server to log out.  Should always return 200, even if no user
+ * was logged in.  The user will not be logged in after this!
+ */
+int CtdlIPCLogout(CtdlIPC *ipc)
+{
+       register int ret;
+       char aaa[SIZ];
+
+       CtdlIPC_lock(ipc);
+       CtdlIPC_putline(ipc, "LOUT");
+       CtdlIPC_getline(ipc, aaa);
+       ret = atoi(aaa);
+       CtdlIPC_unlock(ipc);
+       return ret;
+}
+
+
+/*
+ * First stage of authentication - pass the username.  Returns 300 if the
+ * username is able to log in, with the username correctly spelled in cret.
+ * Returns various 500 error codes if the user doesn't exist, etc.
+ */
+int CtdlIPCTryLogin(CtdlIPC *ipc, const char *username, char *cret)
+{
+       register int ret;
+       char *aaa;
+
+       if (!username) return -2;
+       if (!cret) return -2;
+
+       aaa = (char *)malloc((size_t)(strlen(username) + 6));
+       if (!aaa) return -1;
+
+       sprintf(aaa, "USER %s", username);
+       ret = CtdlIPCGenericCommand(ipc, aaa, NULL, 0, NULL, NULL, cret);
+       free(aaa);
+       return ret;
+}
+
+
+/*
+ * Second stage of authentication - provide password.  The server returns
+ * 200 and several arguments in cret relating to the user's account.
+ */
+int CtdlIPCTryPassword(CtdlIPC *ipc, const char *passwd, char *cret)
+{
+       register int ret;
+       char *aaa;
+
+       if (!passwd) return -2;
+       if (!cret) return -2;
+
+       aaa = (char *)malloc((size_t)(strlen(passwd) + 6));
+       if (!aaa) return -1;
+
+       sprintf(aaa, "PASS %s", passwd);
+       ret = CtdlIPCGenericCommand(ipc, aaa, NULL, 0, NULL, NULL, cret);
+       free(aaa);
+       return ret;
+}
+
+
+/*
+ * Second stage of authentication - provide password.  The server returns
+ * 200 and several arguments in cret relating to the user's account.
+ */
+int CtdlIPCTryApopPassword(CtdlIPC *ipc, const char *response, char *cret)
+{
+       register int ret;
+       char *aaa;
+
+       if (!response) return -2;
+       if (!cret) return -2;
+
+       aaa = (char *)malloc((size_t)(strlen(response) + 6));
+       if (!aaa) return -1;
+
+       sprintf(aaa, "PAS2 %s", response);
+       ret = CtdlIPCGenericCommand(ipc, aaa, NULL, 0, NULL, NULL, cret);
+       free(aaa);
+       return ret;
+}
+
+
+/*
+ * Create a new user.  This returns 200 plus the same arguments as TryPassword
+ * if selfservice is nonzero, unless there was a problem creating the account.
+ * If selfservice is zero, creates a new user but does not log out the existing
+ * user - intended for use by system administrators to create accounts on
+ * behalf of other users.
+ */
+int CtdlIPCCreateUser(CtdlIPC *ipc, const char *username, int selfservice, char *cret)
+{
+       register int ret;
+       char *aaa;
+
+       if (!username) return -2;
+       if (!cret) return -2;
+
+       aaa = (char *)malloc((size_t)(strlen(username) + 6));
+       if (!aaa) return -1;
+
+       sprintf(aaa, "%s %s", selfservice ? "NEWU" : "CREU",  username);
+       ret = CtdlIPCGenericCommand(ipc, aaa, NULL, 0, NULL, NULL, cret);
+       free(aaa);
+       return ret;
+}
+
+
+/*
+ * Changes the user's password.  Returns 200 if changed, errors otherwise.
+ */
+int CtdlIPCChangePassword(CtdlIPC *ipc, const char *passwd, char *cret)
+{
+       register int ret;
+       char *aaa;
+
+       if (!passwd) return -2;
+       if (!cret) return -2;
+
+       aaa = (char *)malloc((size_t)(strlen(passwd) + 6));
+       if (!aaa) return -1;
+
+       sprintf(aaa, "SETP %s", passwd);
+       ret = CtdlIPCGenericCommand(ipc, aaa, NULL, 0, NULL, NULL, cret);
+       free(aaa);
+       return ret;
+}
+
+
+/* LKRN */
+/* Caller must free the march list */
+/* Room types are defined in enum RoomList; keep these in sync! */
+/* floor is -1 for all, or floornum */
+int CtdlIPCKnownRooms(CtdlIPC *ipc, enum RoomList which, int floor, struct march **listing, char *cret)
+{
+       register int ret;
+       struct march *march = NULL;
+       static char *proto[] =
+               {"LKRA", "LKRN", "LKRO", "LZRM", "LRMS", "LPRM" };
+       char aaa[SIZ];
+       char *bbb = NULL;
+       size_t bbb_len;
+
+       if (!listing) return -2;
+       if (*listing) return -2;        /* Free the listing first */
+       if (!cret) return -2;
+       /* if (which < 0 || which > 4) return -2; */
+       if (floor < -1) return -2;      /* Can't validate upper bound, sorry */
+
+       sprintf(aaa, "%s %d", proto[which], floor);
+       ret = CtdlIPCGenericCommand(ipc, aaa, NULL, 0, &bbb, &bbb_len, cret);
+       if (ret / 100 == 1) {
+               struct march *mptr;
+
+               while (bbb && strlen(bbb)) {
+                       int a;
+
+                       extract_token(aaa, bbb, 0, '\n', sizeof aaa);
+                       a = strlen(aaa);
+                       memmove(bbb, bbb + a + 1, strlen(bbb) - a);
+                       mptr = (struct march *) malloc(sizeof (struct march));
+                       if (mptr) {
+                               mptr->next = NULL;
+                               extract_token(mptr->march_name, aaa, 0, '|', sizeof mptr->march_name);
+                               mptr->march_flags = (unsigned int) extract_int(aaa, 1);
+                               mptr->march_floor = (char) extract_int(aaa, 2);
+                               mptr->march_order = (char) extract_int(aaa, 3);
+                               mptr->march_flags2 = (unsigned int) extract_int(aaa, 4);
+                               mptr->march_access = (char) extract_int(aaa, 5);
+                               if (march == NULL)
+                                       march = mptr;
+                               else {
+                                       struct march *mptr2;
+
+                                       mptr2 = march;
+                                       while (mptr2->next != NULL)
+                                               mptr2 = mptr2->next;
+                                       mptr2->next = mptr;
+                               }
+                       }
+               }
+       }
+       *listing = march;
+       if (bbb) free(bbb);
+       return ret;
+}
+
+
+/* GETU */
+/* Caller must free the struct ctdluser; caller may pass an existing one */
+int CtdlIPCGetConfig(CtdlIPC *ipc, struct ctdluser **uret, char *cret)
+{
+       register int ret;
+
+       if (!cret) return -2;
+       if (!uret) return -2;
+       if (!*uret) *uret = (struct ctdluser *)calloc(1, sizeof (struct ctdluser));
+       if (!*uret) return -1;
+
+       ret = CtdlIPCGenericCommand(ipc, "GETU", NULL, 0, NULL, NULL, cret);
+       if (ret / 100 == 2) {
+               uret[0]->USscreenwidth = extract_int(cret, 0);
+               uret[0]->USscreenheight = extract_int(cret, 1);
+               uret[0]->flags = extract_int(cret, 2);
+       }
+       return ret;
+}
+
+
+/* SETU */
+int CtdlIPCSetConfig(CtdlIPC *ipc, struct ctdluser *uret, char *cret)
+{
+       char aaa[48];
+
+       if (!uret) return -2;
+       if (!cret) return -2;
+
+       sprintf(aaa, "SETU %d|%d|%d",
+                       uret->USscreenwidth, uret->USscreenheight,
+                       uret->flags);
+       return CtdlIPCGenericCommand(ipc, aaa, NULL, 0, NULL, NULL, cret);
+}
+
+
+/* RENU */
+int CtdlIPCRenameUser(CtdlIPC *ipc, char *oldname, char *newname, char *cret)
+{
+       register int ret;
+       char cmd[256];
+
+       if (!oldname) return -2;
+       if (!newname) return -2;
+       if (!cret) return -2;
+
+       snprintf(cmd, sizeof cmd, "RENU %s|%s", oldname, newname);
+       ret = CtdlIPCGenericCommand(ipc, cmd, NULL, 0, NULL, NULL, cret);
+       return ret;
+}
+
+
+/* GOTO */
+int CtdlIPCGotoRoom(CtdlIPC *ipc, const char *room, const char *passwd,
+               struct ctdlipcroom **rret, char *cret)
+{
+       register int ret;
+       char *aaa;
+
+       if (!cret) return -2;
+       if (!rret) return -2;
+       if (!*rret) *rret = (struct ctdlipcroom *)calloc(1, sizeof (struct ctdlipcroom));
+       if (!*rret) return -1;
+
+       if (passwd) {
+               aaa = (char *)malloc(strlen(room) + strlen(passwd) + 7);
+               if (!aaa) {
+                       free(*rret);
+                       return -1;
+               }
+               sprintf(aaa, "GOTO %s|%s", room, passwd);
+       } else {
+               aaa = (char *)malloc(strlen(room) + 6);
+               if (!aaa) {
+                       free(*rret);
+                       return -1;
+               }
+               sprintf(aaa, "GOTO %s", room);
+       }
+       ret = CtdlIPCGenericCommand(ipc, aaa, NULL, 0, NULL, NULL, cret);
+       if (ret / 100 == 2) {
+               extract_token(rret[0]->RRname, cret, 0, '|', sizeof rret[0]->RRname);
+               rret[0]->RRunread = extract_long(cret, 1);
+               rret[0]->RRtotal = extract_long(cret, 2);
+               rret[0]->RRinfoupdated = extract_int(cret, 3);
+               rret[0]->RRflags = extract_int(cret, 4);
+               rret[0]->RRhighest = extract_long(cret, 5);
+               rret[0]->RRlastread = extract_long(cret, 6);
+               rret[0]->RRismailbox = extract_int(cret, 7);
+               rret[0]->RRaide = extract_int(cret, 8);
+               rret[0]->RRnewmail = extract_long(cret, 9);
+               rret[0]->RRfloor = extract_int(cret, 10);
+               rret[0]->RRflags2 = extract_int(cret, 14);
+       } else {
+               free(*rret);
+               *rret = NULL;
+       }
+       free(aaa);
+       return ret;
+}
+
+
+/* MSGS */
+/* which is 0 = all, 1 = old, 2 = new, 3 = last, 4 = first, 5 = gt, 6 = lt */
+/* whicharg is number of messages, applies to last, first, gt, lt */
+int CtdlIPCGetMessages(CtdlIPC *ipc, enum MessageList which, int whicharg,
+               const char *mtemplate, unsigned long **mret, char *cret)
+{
+       register int ret;
+       register unsigned long count = 0;
+       static char *proto[] =
+               { "ALL", "OLD", "NEW", "LAST", "FIRST", "GT", "LT" };
+       char aaa[33];
+       char *bbb = NULL;
+       size_t bbb_len;
+
+       if (!cret) return -2;
+       if (!mret) return -2;
+       if (*mret) return -2;
+       if (which < 0 || which > 6) return -2;
+
+       if (which <= 2)
+               sprintf(aaa, "MSGS %s||%d", proto[which],
+                               (mtemplate) ? 1 : 0);
+       else
+               sprintf(aaa, "MSGS %s|%d|%d", proto[which], whicharg,
+                               (mtemplate) ? 1 : 0);
+       if (mtemplate) count = strlen(mtemplate);
+       ret = CtdlIPCGenericCommand(ipc, aaa, mtemplate, count, &bbb, &bbb_len, cret);
+       if (ret / 100 != 1)
+               return ret;
+       count = 0;
+       *mret = (unsigned long *)calloc(1, sizeof(unsigned long));
+       if (!*mret)
+               return -1;
+       while (bbb && strlen(bbb)) {
+               extract_token(aaa, bbb, 0, '\n', sizeof aaa);
+               remove_token(bbb, 0, '\n');
+               *mret = (unsigned long *)realloc(*mret, (size_t)((count + 2) *
+                                       sizeof (unsigned long)));
+               if (*mret) {
+                       (*mret)[count++] = atol(aaa);
+                       (*mret)[count] = 0L;
+               } else {
+                       break;
+               }
+       }
+       if (bbb) free(bbb);
+       return ret;
+}
+
+
+/* MSG0, MSG2 */
+int CtdlIPCGetSingleMessage(CtdlIPC *ipc, long msgnum, int headers, int as_mime,
+               struct ctdlipcmessage **mret, char *cret)
+{
+       register int ret;
+       char aaa[SIZ];
+       char *bbb = NULL;
+       size_t bbb_len;
+       int multipart_hunting = 0;
+       char multipart_prefix[128];
+       char encoding[256];
+
+       if (!cret) return -1;
+       if (!mret) return -1;
+       if (!*mret) *mret = (struct ctdlipcmessage *)calloc(1, sizeof (struct ctdlipcmessage));
+       if (!*mret) return -1;
+       if (!msgnum) return -1;
+
+       strcpy(encoding, "");
+       strcpy(mret[0]->content_type, "");
+       sprintf(aaa, "MSG%d %ld|%d", as_mime, msgnum, headers);
+       ret = CtdlIPCGenericCommand(ipc, aaa, NULL, 0, &bbb, &bbb_len, cret);
+       if (ret / 100 == 1) {
+               if (as_mime != 2) {
+                       strcpy(mret[0]->mime_chosen, "1");      /* Default chosen-part is "1" */
+                       while (strlen(bbb) > 4 && bbb[4] == '=') {
+                               extract_token(aaa, bbb, 0, '\n', sizeof aaa);
+                               remove_token(bbb, 0, '\n');
+
+                               if (!strncasecmp(aaa, "nhdr=yes", 8))
+                                       mret[0]->nhdr = 1;
+                               else if (!strncasecmp(aaa, "from=", 5))
+                                       safestrncpy(mret[0]->author, &aaa[5], SIZ);
+                               else if (!strncasecmp(aaa, "type=", 5))
+                                       mret[0]->type = atoi(&aaa[5]);
+                               else if (!strncasecmp(aaa, "msgn=", 5))
+                                       safestrncpy(mret[0]->msgid, &aaa[5], SIZ);
+                               else if (!strncasecmp(aaa, "subj=", 5))
+                                       safestrncpy(mret[0]->subject, &aaa[5], SIZ);
+                               else if (!strncasecmp(aaa, "rfca=", 5))
+                                       safestrncpy(mret[0]->email, &aaa[5], SIZ);
+                               else if (!strncasecmp(aaa, "hnod=", 5))
+                                       safestrncpy(mret[0]->hnod, &aaa[5], SIZ);
+                               else if (!strncasecmp(aaa, "room=", 5))
+                                       safestrncpy(mret[0]->room, &aaa[5], SIZ);
+                               else if (!strncasecmp(aaa, "node=", 5))
+                                       safestrncpy(mret[0]->node, &aaa[5], SIZ);
+                               else if (!strncasecmp(aaa, "rcpt=", 5))
+                                       safestrncpy(mret[0]->recipient, &aaa[5], SIZ);
+                               else if (!strncasecmp(aaa, "wefw=", 5))
+                                       safestrncpy(mret[0]->references, &aaa[5], SIZ);
+                               else if (!strncasecmp(aaa, "time=", 5))
+                                       mret[0]->time = atol(&aaa[5]);
+
+                               /* Multipart/alternative prefix & suffix strings help
+                                * us to determine which part we want to download.
+                                */
+                               else if (!strncasecmp(aaa, "pref=", 5)) {
+                                       extract_token(multipart_prefix, &aaa[5], 1, '|', sizeof multipart_prefix);
+                                       if (!strcasecmp(multipart_prefix,
+                                          "multipart/alternative")) {
+                                               ++multipart_hunting;
+                                       }
+                               }
+                               else if (!strncasecmp(aaa, "suff=", 5)) {
+                                       extract_token(multipart_prefix, &aaa[5], 1, '|', sizeof multipart_prefix);
+                                       if (!strcasecmp(multipart_prefix,
+                                          "multipart/alternative")) {
+                                               ++multipart_hunting;
+                                       }
+                               }
+
+                               else if (!strncasecmp(aaa, "part=", 5)) {
+                                       struct parts *ptr, *chain;
+       
+                                       ptr = (struct parts *)calloc(1, sizeof (struct parts));
+                                       if (ptr) {
+
+                                               /* Fill the buffers for the caller */
+                                               extract_token(ptr->name, &aaa[5], 0, '|', sizeof ptr->name);
+                                               extract_token(ptr->filename, &aaa[5], 1, '|', sizeof ptr->filename);
+                                               extract_token(ptr->number, &aaa[5], 2, '|', sizeof ptr->number);
+                                               extract_token(ptr->disposition, &aaa[5], 3, '|', sizeof ptr->disposition);
+                                               extract_token(ptr->mimetype, &aaa[5], 4, '|', sizeof ptr->mimetype);
+                                               ptr->length = extract_long(&aaa[5], 5);
+                                               if (!mret[0]->attachments)
+                                                       mret[0]->attachments = ptr;
+                                               else {
+                                                       chain = mret[0]->attachments;
+                                                       while (chain->next)
+                                                               chain = chain->next;
+                                                       chain->next = ptr;
+                                               }
+
+                                               /* Now handle multipart/alternative */
+                                               if (multipart_hunting > 0) {
+                                                       if ( (!strcasecmp(ptr->mimetype,
+                                                            "text/plain"))
+                                                          || (!strcasecmp(ptr->mimetype,
+                                                             "text/html")) ) {
+                                                               strcpy(mret[0]->mime_chosen,
+                                                                       ptr->number);
+                                                       }
+                                               }
+
+                                       }
+                               }
+                       }
+                       /* Eliminate "text\n" */
+                       remove_token(bbb, 0, '\n');
+
+                       /* If doing a MIME thing, pull out the extra headers */
+                       if (as_mime == 4) {
+                               do {
+                                       if (!strncasecmp(bbb, "Content-type:", 13)) {
+                                               extract_token(mret[0]->content_type, bbb, 0, '\n', sizeof mret[0]->content_type);
+                                               strcpy(mret[0]->content_type, &mret[0]->content_type[13]);
+                                               striplt(mret[0]->content_type);
+
+                                               /* strip out ";charset=" portion.  FIXME do something with
+                                                * the charset (like... convert it) instead of just throwing
+                                                * it away
+                                                */
+                                               if (strstr(mret[0]->content_type, ";") != NULL) {
+                                                       strcpy(strstr(mret[0]->content_type, ";"), "");
+                                               }
+
+                                       }
+                                       if (!strncasecmp(bbb, "X-Citadel-MSG4-Partnum:", 23)) {
+                                               extract_token(mret[0]->mime_chosen, bbb, 0, '\n', sizeof mret[0]->mime_chosen);
+                                               strcpy(mret[0]->mime_chosen, &mret[0]->mime_chosen[23]);
+                                               striplt(mret[0]->mime_chosen);
+                                       }
+                                       if (!strncasecmp(bbb, "Content-transfer-encoding:", 26)) {
+                                               extract_token(encoding, bbb, 0, '\n', sizeof encoding);
+                                               strcpy(encoding, &encoding[26]);
+                                               striplt(encoding);
+                                       }
+                                       remove_token(bbb, 0, '\n');
+                               } while ((bbb[0] != 0) && (bbb[0] != '\n'));
+                               remove_token(bbb, 0, '\n');
+                       }
+
+
+               }
+               if (strlen(bbb)) {
+
+                       if ( (!strcasecmp(encoding, "base64")) || (!strcasecmp(encoding, "quoted-printable")) ) {
+                               char *ccc = NULL;
+                               int bytes_decoded = 0;
+                               ccc = malloc(strlen(bbb) + 32768);
+                               if (!strcasecmp(encoding, "base64")) {
+                                       bytes_decoded = CtdlDecodeBase64(ccc, bbb, strlen(bbb));
+                               }
+                               else if (!strcasecmp(encoding, "quoted-printable")) {
+                                       bytes_decoded = CtdlDecodeQuotedPrintable(ccc, bbb, strlen(bbb));
+                               }
+                               ccc[bytes_decoded] = 0;
+                               free(bbb);
+                               bbb = ccc;
+                       }
+
+                       /* FIXME: Strip trailing whitespace */
+                       bbb = (char *)realloc(bbb, (size_t)(strlen(bbb) + 1));
+
+               } else {
+                       bbb = (char *)realloc(bbb, 1);
+                       *bbb = '\0';
+               }
+               mret[0]->text = bbb;
+       }
+       return ret;
+}
+
+
+/* WHOK */
+int CtdlIPCWhoKnowsRoom(CtdlIPC *ipc, char **listing, char *cret)
+{
+       register int ret;
+       size_t bytes;
+
+       if (!cret) return -2;
+       if (!listing) return -2;
+       if (*listing) return -2;
+
+       ret = CtdlIPCGenericCommand(ipc, "WHOK", NULL, 0, listing, &bytes, cret);
+       return ret;
+}
+
+
+/* INFO */
+int CtdlIPCServerInfo(CtdlIPC *ipc, char *cret)
+{
+       register int ret;
+       size_t bytes;
+       char *listing = NULL;
+       char buf[SIZ];
+
+       if (!cret) return -2;
+
+       ret = CtdlIPCGenericCommand(ipc, "INFO", NULL, 0, &listing, &bytes, cret);
+       if (ret / 100 == 1) {
+               int line = 0;
+
+               while (*listing && strlen(listing)) {
+                       extract_token(buf, listing, 0, '\n', sizeof buf);
+                       remove_token(listing, 0, '\n');
+                       switch (line++) {
+                       case 0:         ipc->ServInfo.pid = atoi(buf);
+                                       break;
+                       case 1:         strcpy(ipc->ServInfo.nodename,buf);
+                                       break;
+                       case 2:         strcpy(ipc->ServInfo.humannode,buf);
+                                       break;
+                       case 3:         strcpy(ipc->ServInfo.fqdn,buf);
+                                       break;
+                       case 4:         strcpy(ipc->ServInfo.software,buf);
+                                       break;
+                       case 5:         ipc->ServInfo.rev_level = atoi(buf);
+                                       break;
+                       case 6:         strcpy(ipc->ServInfo.site_location,buf);
+                                       break;
+                       case 7:         strcpy(ipc->ServInfo.sysadm,buf);
+                                       break;
+                       case 9:         strcpy(ipc->ServInfo.moreprompt,buf);
+                                       break;
+                       case 10:        ipc->ServInfo.ok_floors = atoi(buf);
+                                       break;
+                       case 11:        ipc->ServInfo.paging_level = atoi(buf);
+                                       break;
+                       case 13:        ipc->ServInfo.supports_qnop = atoi(buf);
+                                       break;
+                       case 14:        ipc->ServInfo.supports_ldap = atoi(buf);
+                                       break;
+                       case 15:        ipc->ServInfo.newuser_disabled = atoi(buf);
+                                       break;
+                       case 16:        strcpy(ipc->ServInfo.default_cal_zone, buf);
+                                       break;
+                       case 17:        ipc->ServInfo.load_avg = atof(buf);
+                                       break;
+                       case 18:        ipc->ServInfo.worker_avg = atof(buf);
+                                       break;
+                       case 19:        ipc->ServInfo.thread_count = atoi(buf);
+                                       break;
+                       case 20:        ipc->ServInfo.has_sieve = atoi(buf);
+                                       break;
+                       case 21:        ipc->ServInfo.fulltext_enabled = atoi(buf);
+                                       break;
+                       case 22:        strcpy(ipc->ServInfo.svn_revision, buf);
+                                       break;
+                       }
+               }
+
+       }
+       if (listing) free(listing);
+       return ret;
+}
+
+
+/* RDIR */
+int CtdlIPCReadDirectory(CtdlIPC *ipc, char **listing, char *cret)
+{
+       register int ret;
+       size_t bytes;
+
+       if (!cret) return -2;
+       if (!listing) return -2;
+       if (*listing) return -2;
+
+       ret = CtdlIPCGenericCommand(ipc, "RDIR", NULL, 0, listing, &bytes, cret);
+       return ret;
+}
+
+
+/*
+ * Set last-read pointer in this room to msgnum, or 0 for HIGHEST.
+ */
+int CtdlIPCSetLastRead(CtdlIPC *ipc, long msgnum, char *cret)
+{
+       register int ret;
+       char aaa[16];
+
+       if (!cret) return -2;
+
+       if (msgnum) {
+               sprintf(aaa, "SLRP %ld", msgnum);
+       }
+       else {
+               sprintf(aaa, "SLRP HIGHEST");
+       }
+       ret = CtdlIPCGenericCommand(ipc, aaa, NULL, 0, NULL, NULL, cret);
+       return ret;
+}
+
+
+/* INVT */
+int CtdlIPCInviteUserToRoom(CtdlIPC *ipc, const char *username, char *cret)
+{
+       register int ret;
+       char *aaa;
+
+       if (!cret) return -2;
+       if (!username) return -2;
+
+       aaa = (char *)malloc(strlen(username) + 6);
+       if (!aaa) return -1;
+
+       sprintf(aaa, "INVT %s", username);
+       ret = CtdlIPCGenericCommand(ipc, aaa, NULL, 0, NULL, NULL, cret);
+       free(aaa);
+       return ret;
+}
+
+
+/* KICK */
+int CtdlIPCKickoutUserFromRoom(CtdlIPC *ipc, const char *username, char *cret)
+{
+       register int ret;
+       char *aaa;
+
+       if (!cret) return -1;
+       if (!username) return -1;
+
+       aaa = (char *)malloc(strlen(username) + 6);
+
+       sprintf(aaa, "KICK %s", username);
+       ret = CtdlIPCGenericCommand(ipc, aaa, NULL, 0, NULL, NULL, cret);
+       free(aaa);
+       return ret;
+}
+
+
+/* GETR */
+int CtdlIPCGetRoomAttributes(CtdlIPC *ipc, struct ctdlroom **qret, char *cret)
+{
+       register int ret;
+
+       if (!cret) return -2;
+       if (!qret) return -2;
+       if (!*qret) *qret = (struct ctdlroom *)calloc(1, sizeof (struct ctdlroom));
+       if (!*qret) return -1;
+
+       ret = CtdlIPCGenericCommand(ipc, "GETR", NULL, 0, NULL, NULL, cret);
+       if (ret / 100 == 2) {
+               extract_token(qret[0]->QRname, cret, 0, '|', sizeof qret[0]->QRname);
+               extract_token(qret[0]->QRpasswd, cret, 1, '|', sizeof qret[0]->QRpasswd);
+               extract_token(qret[0]->QRdirname, cret, 2, '|', sizeof qret[0]->QRdirname);
+               qret[0]->QRflags = extract_int(cret, 3);
+               qret[0]->QRfloor = extract_int(cret, 4);
+               qret[0]->QRorder = extract_int(cret, 5);
+               qret[0]->QRdefaultview = extract_int(cret, 6);
+               qret[0]->QRflags2 = extract_int(cret, 7);
+       }
+       return ret;
+}
+
+
+/* SETR */
+/* set forget to kick all users out of room */
+int CtdlIPCSetRoomAttributes(CtdlIPC *ipc, int forget, struct ctdlroom *qret, char *cret)
+{
+       register int ret;
+       char *aaa;
+
+       if (!cret) return -2;
+       if (!qret) return -2;
+
+       aaa = (char *)malloc(strlen(qret->QRname) + strlen(qret->QRpasswd) +
+                       strlen(qret->QRdirname) + 64);
+       if (!aaa) return -1;
+
+       sprintf(aaa, "SETR %s|%s|%s|%d|%d|%d|%d|%d|%d",
+                       qret->QRname, qret->QRpasswd, qret->QRdirname,
+                       qret->QRflags, forget, qret->QRfloor, qret->QRorder,
+                       qret->QRdefaultview, qret->QRflags2);
+       ret = CtdlIPCGenericCommand(ipc, aaa, NULL, 0, NULL, NULL, cret);
+       free(aaa);
+       return ret;
+}
+
+
+/* GETA */
+int CtdlIPCGetRoomAide(CtdlIPC *ipc, char *cret)
+{
+       if (!cret) return -1;
+
+       return CtdlIPCGenericCommand(ipc, "GETA", NULL, 0, NULL, NULL, cret);
+}
+
+
+/* SETA */
+int CtdlIPCSetRoomAide(CtdlIPC *ipc, const char *username, char *cret)
+{
+       register int ret;
+       char *aaa;
+
+       if (!cret) return -2;
+       if (!username) return -2;
+
+       aaa = (char *)malloc(strlen(username) + 6);
+       if (!aaa) return -1;
+
+       sprintf(aaa, "SETA %s", username);
+       ret = CtdlIPCGenericCommand(ipc, aaa, NULL, 0, NULL, NULL, cret);
+       free(aaa);
+       return ret;
+}
+
+
+/* ENT0 */
+int CtdlIPCPostMessage(CtdlIPC *ipc, int flag, int *subject_required,  struct ctdlipcmessage *mr, char *cret)
+{
+       register int ret;
+       char cmd[SIZ];
+       char *ptr;
+
+       if (!cret) return -2;
+       if (!mr) return -2;
+
+       if (mr->references) {
+               for (ptr=mr->references; *ptr != 0; ++ptr) {
+                       if (*ptr == '|') *ptr = '!';
+               }
+       }
+
+       snprintf(cmd, sizeof cmd,
+                       "ENT0 %d|%s|%d|%d|%s|%s||||||%s|", flag, mr->recipient,
+                       mr->anonymous, mr->type, mr->subject, mr->author, mr->references);
+       ret = CtdlIPCGenericCommand(ipc, cmd, mr->text, strlen(mr->text), NULL,
+                       NULL, cret);
+       if ((flag == 0) && (subject_required != NULL)) {
+               /* Is the server strongly recommending that the user enter a message subject? */
+               if ((cret[3] != '\0') && (cret[4] != '\0')) {
+                       *subject_required = extract_int(&cret[4], 1);
+               }
+
+               
+       }
+       return ret;
+}
+
+
+/* RINF */
+int CtdlIPCRoomInfo(CtdlIPC *ipc, char **iret, char *cret)
+{
+       size_t bytes;
+
+       if (!cret) return -2;
+       if (!iret) return -2;
+       if (*iret) return -2;
+
+       return CtdlIPCGenericCommand(ipc, "RINF", NULL, 0, iret, &bytes, cret);
+}
+
+
+/* DELE */
+int CtdlIPCDeleteMessage(CtdlIPC *ipc, long msgnum, char *cret)
+{
+       char aaa[16];
+
+       if (!cret) return -2;
+       if (!msgnum) return -2;
+
+       sprintf(aaa, "DELE %ld", msgnum);
+       return CtdlIPCGenericCommand(ipc, aaa, NULL, 0, NULL, NULL, cret);
+}
+
+
+/* MOVE */
+int CtdlIPCMoveMessage(CtdlIPC *ipc, int copy, long msgnum, const char *destroom, char *cret)
+{
+       register int ret;
+       char *aaa;
+
+       if (!cret) return -2;
+       if (!destroom) return -2;
+       if (!msgnum) return -2;
+
+       aaa = (char *)malloc(strlen(destroom) + 28);
+       if (!aaa) return -1;
+
+       sprintf(aaa, "MOVE %ld|%s|%d", msgnum, destroom, copy);
+       ret = CtdlIPCGenericCommand(ipc, aaa, NULL, 0, NULL, NULL, cret);
+       free(aaa);
+       return ret;
+}
+
+
+/* KILL */
+int CtdlIPCDeleteRoom(CtdlIPC *ipc, int for_real, char *cret)
+{
+       char aaa[16];
+
+       if (!cret) return -2;
+
+       sprintf(aaa, "KILL %d", for_real);
+       return CtdlIPCGenericCommand(ipc, aaa, NULL, 0, NULL, NULL, cret);
+}
+
+
+/* CRE8 */
+int CtdlIPCCreateRoom(CtdlIPC *ipc, int for_real, const char *roomname, int type,
+               const char *password, int floor, char *cret)
+{
+       register int ret;
+       char *aaa;
+
+       if (!cret) return -2;
+       if (!roomname) return -2;
+
+       if (password) {
+               aaa = (char *)malloc(strlen(roomname) + strlen(password) + 40);
+               if (!aaa) return -1;
+               sprintf(aaa, "CRE8 %d|%s|%d|%s|%d", for_real, roomname, type,
+                               password, floor);
+       } else {
+               aaa = (char *)malloc(strlen(roomname) + 40);
+               if (!aaa) return -1;
+               sprintf(aaa, "CRE8 %d|%s|%d||%d", for_real, roomname, type,
+                               floor);
+       }
+       ret = CtdlIPCGenericCommand(ipc, aaa, NULL, 0, NULL, NULL, cret);
+       free(aaa);
+       return ret;
+}
+
+
+/* FORG */
+int CtdlIPCForgetRoom(CtdlIPC *ipc, char *cret)
+{
+       if (!cret) return -2;
+
+       return CtdlIPCGenericCommand(ipc, "FORG", NULL, 0, NULL, NULL, cret);
+}
+
+
+/* MESG */
+int CtdlIPCSystemMessage(CtdlIPC *ipc, const char *message, char **mret, char *cret)
+{
+       register int ret;
+       char *aaa;
+       size_t bytes;
+
+       if (!cret) return -2;
+       if (!mret) return -2;
+       if (*mret) return -2;
+       if (!message) return -2;
+
+       aaa = (char *)malloc(strlen(message) + 6);
+       if (!aaa) return -1;
+
+       sprintf(aaa, "MESG %s", message);
+       ret = CtdlIPCGenericCommand(ipc, aaa, NULL, 0, mret, &bytes, cret);
+       free(aaa);
+       return ret;
+}
+
+
+/* GNUR */
+int CtdlIPCNextUnvalidatedUser(CtdlIPC *ipc, char *cret)
+{
+       if (!cret) return -2;
+
+       return CtdlIPCGenericCommand(ipc, "GNUR", NULL, 0, NULL, NULL, cret);
+}
+
+
+/* GREG */
+int CtdlIPCGetUserRegistration(CtdlIPC *ipc, const char *username, char **rret, char *cret)
+{
+       register int ret;
+       char *aaa;
+       size_t bytes;
+
+       if (!cret) return -2;
+       if (!rret) return -2;
+       if (*rret) return -2;
+
+       if (username)
+               aaa = (char *)malloc(strlen(username) + 6);
+       else
+               aaa = (char *)malloc(12);
+       if (!aaa) return -1;
+
+       if (username)
+               sprintf(aaa, "GREG %s", username);
+       else
+               sprintf(aaa, "GREG _SELF_");
+       ret = CtdlIPCGenericCommand(ipc, aaa, NULL, 0, rret, &bytes, cret);
+       free(aaa);
+       return ret;
+}
+
+
+/* VALI */
+int CtdlIPCValidateUser(CtdlIPC *ipc, const char *username, int axlevel, char *cret)
+{
+       register int ret;
+       char *aaa;
+
+       if (!cret) return -2;
+       if (!username) return -2;
+       if (axlevel < 0 || axlevel > 7) return -2;
+
+       aaa = (char *)malloc(strlen(username) + 17);
+       if (!aaa) return -1;
+
+       sprintf(aaa, "VALI %s|%d", username, axlevel);
+       ret = CtdlIPCGenericCommand(ipc, aaa, NULL, 0, NULL, NULL, cret);
+       free(aaa);
+       return ret;
+}
+
+
+/* EINF */
+int CtdlIPCSetRoomInfo(CtdlIPC *ipc, int for_real, const char *info, char *cret)
+{
+       char aaa[16];
+
+       if (!cret) return -1;
+       if (!info) return -1;
+
+       sprintf(aaa, "EINF %d", for_real);
+       return CtdlIPCGenericCommand(ipc, aaa, NULL, 0, NULL, NULL, cret);
+}
+
+
+/* LIST */
+int CtdlIPCUserListing(CtdlIPC *ipc, char *searchstring, char **listing, char *cret)
+{
+       size_t bytes;
+       char *cmd;
+       int ret;
+
+       if (!cret) return -1;
+       if (!listing) return -1;
+       if (*listing) return -1;
+       if (!searchstring) return -1;
+
+       cmd = malloc(strlen(searchstring) + 10);
+       sprintf(cmd, "LIST %s", searchstring);
+
+       ret = CtdlIPCGenericCommand(ipc, cmd, NULL, 0, listing, &bytes, cret);
+       free(cmd);
+       return(ret);
+}
+
+
+/* REGI */
+int CtdlIPCSetRegistration(CtdlIPC *ipc, const char *info, char *cret)
+{
+       if (!cret) return -1;
+       if (!info) return -1;
+
+       return CtdlIPCGenericCommand(ipc, "REGI", info, strlen(info),
+                       NULL, NULL, cret);
+}
+
+
+/* CHEK */
+int CtdlIPCMiscCheck(CtdlIPC *ipc, struct ctdlipcmisc *chek, char *cret)
+{
+       register int ret;
+
+       if (!cret) return -1;
+       if (!chek) return -1;
+
+       ret = CtdlIPCGenericCommand(ipc, "CHEK", NULL, 0, NULL, NULL, cret);
+       if (ret / 100 == 2) {
+               chek->newmail = extract_long(cret, 0);
+               chek->needregis = extract_int(cret, 1);
+               chek->needvalid = extract_int(cret, 2);
+       }
+       return ret;
+}
+
+
+/* DELF */
+int CtdlIPCDeleteFile(CtdlIPC *ipc, const char *filename, char *cret)
+{
+       register int ret;
+       char *aaa;
+
+       if (!cret) return -2;
+       if (!filename) return -2;
+       
+       aaa = (char *)malloc(strlen(filename) + 6);
+       if (!aaa) return -1;
+
+       sprintf(aaa, "DELF %s", filename);
+       ret = CtdlIPCGenericCommand(ipc, aaa, NULL, 0, NULL, NULL, cret);
+       free(aaa);
+       return ret;
+}
+
+
+/* MOVF */
+int CtdlIPCMoveFile(CtdlIPC *ipc, const char *filename, const char *destroom, char *cret)
+{
+       register int ret;
+       char *aaa;
+
+       if (!cret) return -2;
+       if (!filename) return -2;
+       if (!destroom) return -2;
+
+       aaa = (char *)malloc(strlen(filename) + strlen(destroom) + 7);
+       if (!aaa) return -1;
+
+       sprintf(aaa, "MOVF %s|%s", filename, destroom);
+       ret = CtdlIPCGenericCommand(ipc, aaa, NULL, 0, NULL, NULL, cret);
+       free(aaa);
+       return ret;
+}
+
+
+/* RWHO */
+int CtdlIPCOnlineUsers(CtdlIPC *ipc, char **listing, time_t *stamp, char *cret)
+{
+       register int ret;
+       size_t bytes;
+
+       if (!cret) return -1;
+       if (!listing) return -1;
+       if (*listing) return -1;
+
+       *stamp = CtdlIPCServerTime(ipc, cret);
+       if (!*stamp)
+               *stamp = time(NULL);
+       ret = CtdlIPCGenericCommand(ipc, "RWHO", NULL, 0, listing, &bytes, cret);
+       return ret;
+}
+
+
+/* OPEN */
+int CtdlIPCFileDownload(CtdlIPC *ipc, const char *filename, void **buf,
+               size_t resume,
+               void (*progress_gauge_callback)
+                       (CtdlIPC*, unsigned long, unsigned long),
+               char *cret)
+{
+       register int ret;
+       size_t bytes;
+       time_t last_mod;
+       char mimetype[SIZ];
+       char *aaa;
+
+       if (!cret) return -2;
+       if (!filename) return -2;
+       if (!buf) return -2;
+       if (*buf) return -2;
+       if (ipc->downloading) return -2;
+
+       aaa = (char *)malloc(strlen(filename) + 6);
+       if (!aaa) return -1;
+
+       sprintf(aaa, "OPEN %s", filename);
+       ret = CtdlIPCGenericCommand(ipc, aaa, NULL, 0, NULL, NULL, cret);
+       free(aaa);
+       if (ret / 100 == 2) {
+               ipc->downloading = 1;
+               bytes = extract_long(cret, 0);
+               last_mod = extract_int(cret, 1);
+               extract_token(mimetype, cret, 2, '|', sizeof mimetype);
+
+               ret = CtdlIPCReadDownload(ipc, buf, bytes, resume,
+                                       progress_gauge_callback, cret);
+               /*
+               ret = CtdlIPCHighSpeedReadDownload(ipc, buf, bytes, resume,
+                                       progress_gauge_callback, cret);
+               */
+
+               ret = CtdlIPCEndDownload(ipc, cret);
+               if (ret / 100 == 2)
+                       sprintf(cret, "%d|%ld|%s|%s", (int)bytes, last_mod,
+                                       filename, mimetype);
+       }
+       return ret;
+}
+
+
+/* OPNA */
+int CtdlIPCAttachmentDownload(CtdlIPC *ipc, long msgnum, const char *part,
+               void **buf,
+               void (*progress_gauge_callback)
+                       (CtdlIPC*, unsigned long, unsigned long),
+               char *cret)
+{
+       register int ret;
+       size_t bytes;
+       time_t last_mod;
+       char filename[SIZ];
+       char mimetype[SIZ];
+       char aaa[SIZ];
+
+       if (!cret) return -2;
+       if (!buf) return -2;
+       if (*buf) return -2;
+       if (!part) return -2;
+       if (!msgnum) return -2;
+       if (ipc->downloading) return -2;
+
+       sprintf(aaa, "OPNA %ld|%s", msgnum, part);
+       ret = CtdlIPCGenericCommand(ipc, aaa, NULL, 0, NULL, NULL, cret);
+       if (ret / 100 == 2) {
+               ipc->downloading = 1;
+               bytes = extract_long(cret, 0);
+               last_mod = extract_int(cret, 1);
+               extract_token(filename, cret, 2, '|', sizeof filename);
+               extract_token(mimetype, cret, 3, '|', sizeof mimetype);
+               /* ret = CtdlIPCReadDownload(ipc, buf, bytes, 0, progress_gauge_callback, cret); */
+               ret = CtdlIPCHighSpeedReadDownload(ipc, buf, bytes, 0, progress_gauge_callback, cret);
+               ret = CtdlIPCEndDownload(ipc, cret);
+               if (ret / 100 == 2)
+                       sprintf(cret, "%d|%ld|%s|%s", (int)bytes, last_mod,
+                                       filename, mimetype);
+       }
+       return ret;
+}
+
+
+/* OIMG */
+int CtdlIPCImageDownload(CtdlIPC *ipc, const char *filename, void **buf,
+               void (*progress_gauge_callback)
+                       (CtdlIPC*, unsigned long, unsigned long),
+               char *cret)
+{
+       register int ret;
+       size_t bytes;
+       time_t last_mod;
+       char mimetype[SIZ];
+       char *aaa;
+
+       if (!cret) return -1;
+       if (!buf) return -1;
+       if (*buf) return -1;
+       if (!filename) return -1;
+       if (ipc->downloading) return -1;
+
+       aaa = (char *)malloc(strlen(filename) + 6);
+       if (!aaa) return -1;
+
+       sprintf(aaa, "OIMG %s", filename);
+       ret = CtdlIPCGenericCommand(ipc, aaa, NULL, 0, NULL, NULL, cret);
+       free(aaa);
+       if (ret / 100 == 2) {
+               ipc->downloading = 1;
+               bytes = extract_long(cret, 0);
+               last_mod = extract_int(cret, 1);
+               extract_token(mimetype, cret, 2, '|', sizeof mimetype);
+/*             ret = CtdlIPCReadDownload(ipc, buf, bytes, 0, progress_gauge_callback, cret); */
+               ret = CtdlIPCHighSpeedReadDownload(ipc, buf, bytes, 0, progress_gauge_callback, cret);
+               ret = CtdlIPCEndDownload(ipc, cret);
+               if (ret / 100 == 2)
+                       sprintf(cret, "%d|%ld|%s|%s", (int)bytes, last_mod,
+                                       filename, mimetype);
+       }
+       return ret;
+}
+
+
+/* UOPN */
+int CtdlIPCFileUpload(CtdlIPC *ipc, const char *save_as, const char *comment, 
+               const char *path, 
+               void (*progress_gauge_callback)
+                       (CtdlIPC*, unsigned long, unsigned long),
+               char *cret)
+{
+       register int ret;
+       char *aaa;
+       FILE *uploadFP;
+       char MimeTestBuf[64];
+       const char *MimeType;
+       long len;
+
+       if (!cret) return -1;
+       if (!save_as) return -1;
+       if (!comment) return -1;
+       if (!path) return -1;
+       if (!*path) return -1;
+       if (ipc->uploading) return -1;
+
+       uploadFP = fopen(path, "r");
+       if (!uploadFP) return -2;
+
+       len = fread(&MimeTestBuf[0], 1, 64, uploadFP);
+       rewind (uploadFP);
+       if (len < 0) 
+               return -3;
+
+       MimeType = GuessMimeType(&MimeTestBuf[0], len);
+       aaa = (char *)malloc(strlen(save_as) + strlen(comment) + 7);
+       if (!aaa) return -1;
+
+       sprintf(aaa, "UOPN %s|%s|%s", save_as, MimeType,  comment);
+       ret = CtdlIPCGenericCommand(ipc, aaa, NULL, 0, NULL, NULL, cret);
+       free(aaa);
+       if (ret / 100 == 2) {
+               ipc->uploading = 1;
+               ret = CtdlIPCWriteUpload(ipc, uploadFP, progress_gauge_callback, cret);
+               ret = CtdlIPCEndUpload(ipc, (ret == -2 ? 1 : 0), cret);
+               ipc->uploading = 0;
+       }
+       return ret;
+}
+
+
+/* UIMG */
+int CtdlIPCImageUpload(CtdlIPC *ipc, int for_real, const char *path,
+               const char *save_as,
+               void (*progress_gauge_callback)
+                       (CtdlIPC*, unsigned long, unsigned long),
+               char *cret)
+{
+       register int ret;
+       FILE *uploadFP;
+       char *aaa;
+       char MimeTestBuf[64];
+       const char *MimeType;
+       long len;
+
+       if (!cret) return -1;
+       if (!save_as) return -1;
+       if (!path && for_real) return -1;
+       if (!*path && for_real) return -1;
+       if (ipc->uploading) return -1;
+
+       aaa = (char *)malloc(strlen(save_as) + 17);
+       if (!aaa) return -1;
+
+       uploadFP = fopen(path, "r");
+       if (!uploadFP) return -2;
+
+       len = fread(&MimeTestBuf[0], 1, 64, uploadFP);
+       rewind (uploadFP);
+       if (len < 0) 
+               return -3;
+       MimeType = GuessMimeType(&MimeTestBuf[0], 64);
+
+       sprintf(aaa, "UIMG %d|%s|%s", for_real, MimeType, save_as);
+       ret = CtdlIPCGenericCommand(ipc, aaa, NULL, 0, NULL, NULL, cret);
+       free(aaa);
+       if (ret / 100 == 2 && for_real) {
+               ipc->uploading = 1;
+               ret = CtdlIPCWriteUpload(ipc, uploadFP, progress_gauge_callback, cret);
+               ret = CtdlIPCEndUpload(ipc, (ret == -2 ? 1 : 0), cret);
+               ipc->uploading = 0;
+       }
+       return ret;
+}
+
+
+/* QUSR */
+int CtdlIPCQueryUsername(CtdlIPC *ipc, const char *username, char *cret)
+{
+       register int ret;
+       char *aaa;
+
+       if (!cret) return -2;
+       if (!username) return -2;
+
+       aaa = (char *)malloc(strlen(username) + 6);
+       if (!aaa) return -1;
+
+       sprintf(aaa, "QUSR %s", username);
+       ret = CtdlIPCGenericCommand(ipc, aaa, NULL, 0, NULL, NULL, cret);
+       free(aaa);
+       return ret;
+}
+
+
+/* LFLR */
+int CtdlIPCFloorListing(CtdlIPC *ipc, char **listing, char *cret)
+{
+       size_t bytes;
+
+       if (!cret) return -2;
+       if (!listing) return -2;
+       if (*listing) return -2;
+
+       return CtdlIPCGenericCommand(ipc, "LFLR", NULL, 0, listing, &bytes, cret);
+}
+
+
+/* CFLR */
+int CtdlIPCCreateFloor(CtdlIPC *ipc, int for_real, const char *name, char *cret)
+{
+       register int ret;
+       char aaa[SIZ];
+
+       if (!cret) return -2;
+       if (!name) return -2;
+
+       sprintf(aaa, "CFLR %s|%d", name, for_real);
+       ret = CtdlIPCGenericCommand(ipc, aaa, NULL, 0, NULL, NULL, cret);
+       return ret;
+}
+
+
+/* KFLR */
+int CtdlIPCDeleteFloor(CtdlIPC *ipc, int for_real, int floornum, char *cret)
+{
+       char aaa[SIZ];
+
+       if (!cret) return -1;
+       if (floornum < 0) return -1;
+
+       sprintf(aaa, "KFLR %d|%d", floornum, for_real);
+       return CtdlIPCGenericCommand(ipc, aaa, NULL, 0, NULL, NULL, cret);
+}
+
+
+/* EFLR */
+int CtdlIPCEditFloor(CtdlIPC *ipc, int floornum, const char *floorname, char *cret)
+{
+       register int ret;
+       char aaa[SIZ];
+
+       if (!cret) return -2;
+       if (!floorname) return -2;
+       if (floornum < 0) return -2;
+
+       sprintf(aaa, "EFLR %d|%s", floornum, floorname);
+       ret = CtdlIPCGenericCommand(ipc, aaa, NULL, 0, NULL, NULL, cret);
+       return ret;
+}
+
+
+/*
+ * IDEN 
+ *
+ * You only need to fill out hostname, the defaults will be used if any of the
+ * other fields are not set properly.
+ */
+int CtdlIPCIdentifySoftware(CtdlIPC *ipc, int developerid, int clientid,
+               int revision, const char *software_name, const char *hostname,
+               char *cret)
+{
+       register int ret;
+       char *aaa;
+
+       if (developerid < 0 || clientid < 0 || revision < 0 ||
+           !software_name) {
+               developerid = 8;
+               clientid = 0;
+               revision = REV_LEVEL - 600;
+               software_name = "Citadel (libcitadel)";
+       }
+       if (!hostname) return -2;
+
+       aaa = (char *)malloc(strlen(software_name) + strlen(hostname) + 29);
+       if (!aaa) return -1;
+
+       sprintf(aaa, "IDEN %d|%d|%d|%s|%s", developerid, clientid,
+                       revision, software_name, hostname);
+       ret = CtdlIPCGenericCommand(ipc, aaa, NULL, 0, NULL, NULL, cret);
+       free(aaa);
+       return ret;
+}
+
+
+/* SEXP */
+int CtdlIPCSendInstantMessage(CtdlIPC *ipc, const char *username, const char *text,
+               char *cret)
+{
+       register int ret;
+       char *aaa;
+
+       if (!cret) return -2;
+       if (!username) return -2;
+
+       aaa = (char *)malloc(strlen(username) + 8);
+       if (!aaa) return -1;
+
+       if (text) {
+               sprintf(aaa, "SEXP %s|-", username);
+               ret = CtdlIPCGenericCommand(ipc, aaa, text, strlen(text),
+                               NULL, NULL, cret);
+       } else {
+               sprintf(aaa, "SEXP %s||", username);
+               ret = CtdlIPCGenericCommand(ipc, aaa, NULL, 0, NULL, NULL, cret);
+       }
+       free(aaa);
+       return ret;
+}
+
+
+/* GEXP */
+int CtdlIPCGetInstantMessage(CtdlIPC *ipc, char **listing, char *cret)
+{
+       size_t bytes;
+
+       if (!cret) return -2;
+       if (!listing) return -2;
+       if (*listing) return -2;
+
+       return CtdlIPCGenericCommand(ipc, "GEXP", NULL, 0, listing, &bytes, cret);
+}
+
+
+/* DEXP */
+/* mode is 0 = enable, 1 = disable, 2 = status */
+int CtdlIPCEnableInstantMessageReceipt(CtdlIPC *ipc, int mode, char *cret)
+{
+       char aaa[16];
+
+       if (!cret) return -2;
+
+       sprintf(aaa, "DEXP %d", mode);
+       return CtdlIPCGenericCommand(ipc, aaa, NULL, 0, NULL, NULL, cret);
+}
+
+
+/* EBIO */
+int CtdlIPCSetBio(CtdlIPC *ipc, char *bio, char *cret)
+{
+       if (!cret) return -2;
+       if (!bio) return -2;
+
+       return CtdlIPCGenericCommand(ipc, "EBIO", bio, strlen(bio),
+                       NULL, NULL, cret);
+}
+
+
+/* RBIO */
+int CtdlIPCGetBio(CtdlIPC *ipc, const char *username, char **listing, char *cret)
+{
+       register int ret;
+       size_t bytes;
+       char *aaa;
+
+       if (!cret) return -2;
+       if (!username) return -2;
+       if (!listing) return -2;
+       if (*listing) return -2;
+
+       aaa = (char *)malloc(strlen(username) + 6);
+       if (!aaa) return -1;
+
+       sprintf(aaa, "RBIO %s", username);
+       ret = CtdlIPCGenericCommand(ipc, aaa, NULL, 0, listing, &bytes, cret);
+       free(aaa);
+       return ret;
+}
+
+
+/* LBIO */
+int CtdlIPCListUsersWithBios(CtdlIPC *ipc, char **listing, char *cret)
+{
+       size_t bytes;
+
+       if (!cret) return -2;
+       if (!listing) return -2;
+       if (*listing) return -2;
+
+       return CtdlIPCGenericCommand(ipc, "LBIO", NULL, 0, listing, &bytes, cret);
+}
+
+
+/* STEL */
+int CtdlIPCStealthMode(CtdlIPC *ipc, int mode, char *cret)
+{
+       char aaa[16];
+
+       if (!cret) return -1;
+
+       sprintf(aaa, "STEL %d", mode ? 1 : 0);
+       return CtdlIPCGenericCommand(ipc, aaa, NULL, 0, NULL, NULL, cret);
+}
+
+
+/* TERM */
+int CtdlIPCTerminateSession(CtdlIPC *ipc, int sid, char *cret)
+{
+       char aaa[16];
+
+       if (!cret) return -1;
+
+       sprintf(aaa, "TERM %d", sid);
+       return CtdlIPCGenericCommand(ipc, aaa, NULL, 0, NULL, NULL, cret);
+}
+
+
+/* DOWN */
+int CtdlIPCTerminateServerNow(CtdlIPC *ipc, char *cret)
+{
+       if (!cret) return -1;
+
+       return CtdlIPCGenericCommand(ipc, "DOWN", NULL, 0, NULL, NULL, cret);
+}
+
+
+/* SCDN */
+int CtdlIPCTerminateServerScheduled(CtdlIPC *ipc, int mode, char *cret)
+{
+       char aaa[16];
+
+       if (!cret) return -1;
+
+       sprintf(aaa, "SCDN %d", mode ? 1 : 0);
+       return CtdlIPCGenericCommand(ipc, aaa, NULL, 0, NULL, NULL, cret);
+}
+
+
+/* EMSG */
+int CtdlIPCEnterSystemMessage(CtdlIPC *ipc, const char *filename, const char *text,
+               char *cret)
+{
+       register int ret;
+       char *aaa;
+
+       if (!cret) return -2;
+       if (!text) return -2;
+       if (!filename) return -2;
+
+       aaa = (char *)malloc(strlen(filename) + 6);
+       if (!aaa) return -1;
+
+       sprintf(aaa, "EMSG %s", filename);
+       ret = CtdlIPCGenericCommand(ipc, aaa, text, strlen(text), NULL, NULL, cret);
+       free(aaa);
+       return ret;
+}
+
+
+/* HCHG */
+int CtdlIPCChangeHostname(CtdlIPC *ipc, const char *hostname, char *cret)
+{
+       register int ret;
+       char *aaa;
+
+       if (!cret) return -2;
+       if (!hostname) return -2;
+
+       aaa = (char *)malloc(strlen(hostname) + 6);
+       if (!aaa) return -1;
+
+       sprintf(aaa, "HCHG %s", hostname);
+       ret = CtdlIPCGenericCommand(ipc, aaa, NULL, 0, NULL, NULL, cret);
+       free(aaa);
+       return ret;
+}
+
+
+/* RCHG */
+int CtdlIPCChangeRoomname(CtdlIPC *ipc, const char *roomname, char *cret)
+{
+       register int ret;
+       char *aaa;
+
+       if (!cret) return -2;
+       if (!roomname) return -2;
+
+       aaa = (char *)malloc(strlen(roomname) + 6);
+       if (!aaa) return -1;
+
+       sprintf(aaa, "RCHG %s", roomname);
+       ret = CtdlIPCGenericCommand(ipc, aaa, NULL, 0, NULL, NULL, cret);
+       free(aaa);
+       return ret;
+}
+
+
+/* UCHG */
+int CtdlIPCChangeUsername(CtdlIPC *ipc, const char *username, char *cret)
+{
+       register int ret;
+       char *aaa;
+
+       if (!cret) return -2;
+       if (!username) return -2;
+
+       aaa = (char *)malloc(strlen(username) + 6);
+       if (!aaa) return -1;
+
+       sprintf(aaa, "UCHG %s", username);
+       ret = CtdlIPCGenericCommand(ipc, aaa, NULL, 0, NULL, NULL, cret);
+       free(aaa);
+       return ret;
+}
+
+
+/* TIME */
+/* This function returns the actual server time reported, or 0 if error */
+time_t CtdlIPCServerTime(CtdlIPC *ipc, char *cret)
+{
+       register time_t tret;
+       register int ret;
+
+       ret = CtdlIPCGenericCommand(ipc, "TIME", NULL, 0, NULL, NULL, cret);
+       if (ret / 100 == 2) {
+               tret = extract_long(cret, 0);
+       } else {
+               tret = 0L;
+       }
+       return tret;
+}
+
+
+/* AGUP */
+int CtdlIPCAideGetUserParameters(CtdlIPC *ipc, const char *who,
+                                struct ctdluser **uret, char *cret)
+{
+       register int ret;
+       char aaa[SIZ];
+
+       if (!cret) return -2;
+       if (!uret) return -2;
+       if (!*uret) *uret = (struct ctdluser *)calloc(1, sizeof(struct ctdluser));
+       if (!*uret) return -1;
+
+       sprintf(aaa, "AGUP %s", who);
+       ret = CtdlIPCGenericCommand(ipc, aaa, NULL, 0, NULL, NULL, cret);
+
+       if (ret / 100 == 2) {
+               extract_token(uret[0]->fullname, cret, 0, '|', sizeof uret[0]->fullname);
+               extract_token(uret[0]->password, cret, 1, '|', sizeof uret[0]->password);
+               uret[0]->flags = extract_int(cret, 2);
+               uret[0]->timescalled = extract_long(cret, 3);
+               uret[0]->posted = extract_long(cret, 4);
+               uret[0]->axlevel = extract_int(cret, 5);
+               uret[0]->usernum = extract_long(cret, 6);
+               uret[0]->lastcall = extract_long(cret, 7);
+               uret[0]->USuserpurge = extract_int(cret, 8);
+       }
+       return ret;
+}
+
+
+/* ASUP */
+int CtdlIPCAideSetUserParameters(CtdlIPC *ipc, const struct ctdluser *uret, char *cret)
+{
+       register int ret;
+       char *aaa;
+
+       if (!cret) return -2;
+       if (!uret) return -2;
+
+       aaa = (char *)malloc(strlen(uret->fullname) + strlen(uret->password) + 84);
+       if (!aaa) return -1;
+
+       sprintf(aaa, "ASUP %s|%s|%d|%ld|%ld|%d|%ld|%ld|%d",
+                       uret->fullname, uret->password, uret->flags,
+                       uret->timescalled, uret->posted, uret->axlevel,
+                       uret->usernum, uret->lastcall, uret->USuserpurge);
+       ret = CtdlIPCGenericCommand(ipc, aaa, NULL, 0, NULL, NULL, cret);
+       free(aaa);
+       return ret;
+}
+
+
+/* GPEX */
+/* which is 0 = room, 1 = floor, 2 = site, 3 = default for mailboxes */
+/* caller must free the struct ExpirePolicy */
+int CtdlIPCGetMessageExpirationPolicy(CtdlIPC *ipc, int which,
+               struct ExpirePolicy **policy, char *cret)
+{
+       static char *proto[] = {"room", "floor", "site", "mailboxes" };
+       char cmd[256];
+       register int ret;
+
+       if (!cret) return -2;
+       if (!policy) return -2;
+       if (!*policy) *policy = (struct ExpirePolicy *)calloc(1, sizeof(struct ExpirePolicy));
+       if (!*policy) return -1;
+       if (which < 0 || which > 3) return -2;
+       
+       sprintf(cmd, "GPEX %s", proto[which]);
+       ret = CtdlIPCGenericCommand(ipc, cmd, NULL, 0, NULL, NULL, cret);
+       if (ret / 100 == 2) {
+               policy[0]->expire_mode = extract_int(cret, 0);
+               policy[0]->expire_value = extract_int(cret, 1);
+       }
+       return ret;
+}
+
+
+/* SPEX */
+/* which is 0 = room, 1 = floor, 2 = site, 3 = default for mailboxes */
+/* policy is 0 = inherit, 1 = no purge, 2 = by count, 3 = by age (days) */
+int CtdlIPCSetMessageExpirationPolicy(CtdlIPC *ipc, int which,
+               struct ExpirePolicy *policy, char *cret)
+{
+       char aaa[38];
+       char *whichvals[] = { "room", "floor", "site", "mailboxes" };
+
+       if (!cret) return -2;
+       if (which < 0 || which > 3) return -2;
+       if (!policy) return -2;
+       if (policy->expire_mode < 0 || policy->expire_mode > 3) return -2;
+       if (policy->expire_mode >= 2 && policy->expire_value < 1) return -2;
+
+       sprintf(aaa, "SPEX %s|%d|%d", whichvals[which],
+                       policy->expire_mode, policy->expire_value);
+       return CtdlIPCGenericCommand(ipc, aaa, NULL, 0, NULL, NULL, cret);
+}
+
+
+/* CONF GET */
+int CtdlIPCGetSystemConfig(CtdlIPC *ipc, char **listing, char *cret)
+{
+       size_t bytes;
+
+       if (!cret) return -2;
+       if (!listing) return -2;
+       if (*listing) return -2;
+
+       return CtdlIPCGenericCommand(ipc, "CONF GET", NULL, 0,
+                       listing, &bytes, cret);
+}
+
+
+/* CONF SET */
+int CtdlIPCSetSystemConfig(CtdlIPC *ipc, const char *listing, char *cret)
+{
+       if (!cret) return -2;
+       if (!listing) return -2;
+
+       return CtdlIPCGenericCommand(ipc, "CONF SET", listing, strlen(listing),
+                       NULL, NULL, cret);
+}
+
+
+/* CONF GETSYS */
+int CtdlIPCGetSystemConfigByType(CtdlIPC *ipc, const char *mimetype,
+               char **listing, char *cret)
+{
+       register int ret;
+       char *aaa;
+       size_t bytes;
+
+       if (!cret) return -2;
+       if (!mimetype) return -2;
+       if (!listing) return -2;
+       if (*listing) return -2;
+
+       aaa = malloc(strlen(mimetype) + 13);
+       if (!aaa) return -1;
+       sprintf(aaa, "CONF GETSYS|%s", mimetype);
+       ret = CtdlIPCGenericCommand(ipc, aaa, NULL, 0,
+                       listing, &bytes, cret);
+    free(aaa);
+    return ret;
+}
+
+
+/* CONF PUTSYS */
+int CtdlIPCSetSystemConfigByType(CtdlIPC *ipc, const char *mimetype,
+              const char *listing, char *cret)
+{
+    register int ret;
+       char *aaa;
+
+       if (!cret) return -2;
+       if (!mimetype) return -2;
+       if (!listing) return -2;
+
+       aaa = malloc(strlen(mimetype) + 13);
+       if (!aaa) return -1;
+       sprintf(aaa, "CONF PUTSYS|%s", mimetype);
+       ret = CtdlIPCGenericCommand(ipc, aaa, listing, strlen(listing),
+                       NULL, NULL, cret);
+    free(aaa);
+    return ret;
+}
+
+
+/* GNET */
+int CtdlIPCGetRoomNetworkConfig(CtdlIPC *ipc, char **listing, char *cret)
+{
+       size_t bytes;
+
+       if (!cret) return -2;
+       if (!listing) return -2;
+       if (*listing) return -2;
+
+       return CtdlIPCGenericCommand(ipc, "GNET", NULL, 0,
+                       listing, &bytes, cret);
+}
+
+
+/* SNET */
+int CtdlIPCSetRoomNetworkConfig(CtdlIPC *ipc, const char *listing, char *cret)
+{
+       if (!cret) return -2;
+       if (!listing) return -2;
+
+       return CtdlIPCGenericCommand(ipc, "SNET", listing, strlen(listing),
+                       NULL, NULL, cret);
+}
+
+
+/* REQT */
+int CtdlIPCRequestClientLogout(CtdlIPC *ipc, int session, char *cret)
+{
+       char aaa[16];
+
+       if (!cret) return -2;
+       if (session < 0) return -2;
+
+       sprintf(aaa, "REQT %d", session);
+       return CtdlIPCGenericCommand(ipc, aaa, NULL, 0, NULL, NULL, cret);
+}
+
+
+/* SEEN */
+int CtdlIPCSetMessageSeen(CtdlIPC *ipc, long msgnum, int seen, char *cret)
+{
+       char aaa[27];
+
+       if (!cret) return -2;
+       if (msgnum < 0) return -2;
+
+       sprintf(aaa, "SEEN %ld|%d", msgnum, seen ? 1 : 0);
+       return CtdlIPCGenericCommand(ipc, aaa, NULL, 0, NULL, NULL, cret);
+}
+
+
+/* STLS */
+int CtdlIPCStartEncryption(CtdlIPC *ipc, char *cret)
+{
+       int a;
+       int r;
+       char buf[SIZ];
+
+#ifdef HAVE_OPENSSL
+       SSL *temp_ssl;
+
+       /* New SSL object */
+       temp_ssl = SSL_new(ssl_ctx);
+       if (!temp_ssl) {
+               error_printf("SSL_new failed: %s\n",
+                               ERR_reason_error_string(ERR_get_error()));
+               return -2;
+       }
+       /* Pointless flag waving */
+#if SSLEAY_VERSION_NUMBER >= 0x0922
+       SSL_set_session_id_context(temp_ssl, "Citadel SID", 14);
+#endif
+
+       if (!access(EGD_POOL, F_OK))
+               RAND_egd(EGD_POOL);
+
+       if (!RAND_status()) {
+               error_printf("PRNG not properly seeded\n");
+               return -2;
+       }
+
+       /* Associate network connection with SSL object */
+       if (SSL_set_fd(temp_ssl, ipc->sock) < 1) {
+               error_printf("SSL_set_fd failed: %s\n",
+                               ERR_reason_error_string(ERR_get_error()));
+               return -2;
+       }
+
+       if (status_hook != NULL)
+               status_hook("Requesting encryption...\r");
+
+       /* Ready to start SSL/TLS */
+       /* Old code
+       CtdlIPC_putline(ipc, "STLS");
+       CtdlIPC_getline(ipc, buf);
+       if (buf[0] != '2') {
+               error_printf("Server can't start TLS: %s\n", buf);
+               return 0;
+       }
+       */
+       r = CtdlIPCGenericCommand(ipc,
+                                 "STLS", NULL, 0, NULL, NULL, cret);
+       if (r / 100 != 2) {
+               error_printf("Server can't start TLS: %s\n", buf);
+               endtls(temp_ssl);
+               return r;
+       }
+
+       /* Do SSL/TLS handshake */
+       if ((a = SSL_connect(temp_ssl)) < 1) {
+               error_printf("SSL_connect failed: %s\n",
+                               ERR_reason_error_string(ERR_get_error()));
+               endtls(temp_ssl);
+               return -2;
+       }
+       ipc->ssl = temp_ssl;
+
+       BIO_set_close(ipc->ssl->rbio, BIO_NOCLOSE);
+       {
+               int bits, alg_bits;
+
+               bits = SSL_CIPHER_get_bits(SSL_get_current_cipher(ipc->ssl), &alg_bits);
+               error_printf("Encrypting with %s cipher %s (%d of %d bits)\n",
+                               SSL_CIPHER_get_version(SSL_get_current_cipher(ipc->ssl)),
+                               SSL_CIPHER_get_name(SSL_get_current_cipher(ipc->ssl)),
+                               bits, alg_bits);
+       }
+       return r;
+#else
+       return 0;
+#endif /* HAVE_OPENSSL */
+}
+
+
+#ifdef HAVE_OPENSSL
+static void endtls(SSL *ssl)
+{
+       if (ssl) {
+               SSL_shutdown(ssl);
+               SSL_free(ssl);
+       }
+}
+#endif
+
+
+/* QDIR */
+int CtdlIPCDirectoryLookup(CtdlIPC *ipc, const char *address, char *cret)
+{
+    register int ret;
+       char *aaa;
+
+       if (!address) return -2;
+       if (!cret) return -2;
+
+       aaa = (char *)malloc(strlen(address) + 6);
+       if (!aaa) return -1;
+
+       sprintf(aaa, "QDIR %s", address);
+       ret = CtdlIPCGenericCommand(ipc, aaa, NULL, 0, NULL, NULL, cret);
+    free(aaa);
+    return ret;
+}
+
+
+/* IPGM */
+int CtdlIPCInternalProgram(CtdlIPC *ipc, int secret, char *cret)
+{
+       char aaa[30];
+
+       if (!cret) return -2;
+       sprintf(aaa, "IPGM %d", secret);
+       return CtdlIPCGenericCommand(ipc, aaa, NULL, 0, NULL, NULL, cret);
+}
+
+
+/* FSCK */
+int CtdlIPCMessageBaseCheck(CtdlIPC *ipc, char **mret, char *cret)
+{
+       size_t size = 0;
+
+       if (!cret) return -2;
+       if (!mret) return -2;
+       if (*mret) return -2;
+
+       return CtdlIPCGenericCommand(ipc, "FSCK", NULL, 0, mret, &size, cret);
+}
+
+
+/*
+ * Not implemented:
+ * 
+ * CHAT
+ * ETLS
+ * EXPI
+ * GTLS
+ * IGAB
+ * MSG3
+ * MSG4
+ * NDOP
+ * NETP
+ * NUOP
+ * SMTP
+ */
+
+
+/* ************************************************************************** */
+/*             Stuff below this line is not for public consumption            */
+/* ************************************************************************** */
+
+
+INLINE void CtdlIPC_lock(CtdlIPC *ipc)
+{
+       if (ipc->network_status_cb) ipc->network_status_cb(1);
+#ifdef THREADED_CLIENT
+       pthread_mutex_lock(&(ipc->mutex));
+#endif
+}
+
+
+INLINE void CtdlIPC_unlock(CtdlIPC *ipc)
+{
+#ifdef THREADED_CLIENT
+       pthread_mutex_unlock(&(ipc->mutex));
+#endif
+       if (ipc->network_status_cb) ipc->network_status_cb(0);
+}
+
+
+/* Read a listing from the server up to 000.  Append to dest if it exists */
+char *CtdlIPCReadListing(CtdlIPC *ipc, char *dest)
+{
+       size_t length = 0;
+       size_t linelength;
+       char *ret = NULL;
+       char aaa[SIZ];
+
+       ret = dest;
+       if (ret != NULL) {
+               length = strlen(ret);
+       } else {
+               length = 0;
+       }
+
+       while (CtdlIPC_getline(ipc, aaa), strcmp(aaa, "000")) {
+               linelength = strlen(aaa);
+               ret = (char *)realloc(ret, (size_t)(length + linelength + 2));
+               if (ret) {
+                       strcpy(&ret[length], aaa);
+                       length += linelength;
+                       strcpy(&ret[length++], "\n");
+               }
+       }
+
+       return(ret);
+}
+
+
+/* Send a listing to the server; generate the ending 000. */
+int CtdlIPCSendListing(CtdlIPC *ipc, const char *listing)
+{
+       char *text;
+
+       text = (char *)malloc(strlen(listing) + 6);
+       if (text) {
+               strcpy(text, listing);
+               while (text[strlen(text) - 1] == '\n')
+                       text[strlen(text) - 1] = '\0';
+               strcat(text, "\n000");
+               CtdlIPC_putline(ipc, text);
+               free(text);
+               text = NULL;
+       } else {
+               /* Malloc failed but we are committed to send */
+               /* This may result in extra blanks at the bottom */
+               CtdlIPC_putline(ipc, text);
+               CtdlIPC_putline(ipc, "000");
+       }
+       return 0;
+}
+
+
+/* Partial read of file from server */
+size_t CtdlIPCPartialRead(CtdlIPC *ipc, void **buf, size_t offset, size_t bytes, char *cret)
+{
+       register size_t len = 0;
+       char aaa[SIZ];
+
+       if (!buf) return 0;
+       if (!cret) return 0;
+       if (bytes < 1) return 0;
+
+       CtdlIPC_lock(ipc);
+       sprintf(aaa, "READ %d|%d", (int)offset, (int)bytes);
+       CtdlIPC_putline(ipc, aaa);
+       CtdlIPC_getline(ipc, aaa);
+       if (aaa[0] != '6')
+               strcpy(cret, &aaa[4]);
+       else {
+               len = extract_long(&aaa[4], 0);
+               *buf = (void *)realloc(*buf, (size_t)(offset + len));
+               if (*buf) {
+                       /* I know what I'm doing */
+                       serv_read(ipc, ((char *)(*buf) + offset), len);
+               } else {
+                       /* We have to read regardless */
+                       serv_read(ipc, aaa, len);
+                       len = 0;
+               }
+       }
+       CtdlIPC_unlock(ipc);
+       return len;
+}
+
+
+/* CLOS */
+int CtdlIPCEndDownload(CtdlIPC *ipc, char *cret)
+{
+       register int ret;
+
+       if (!cret) return -2;
+       if (!ipc->downloading) return -2;
+
+       ret = CtdlIPCGenericCommand(ipc, "CLOS", NULL, 0, NULL, NULL, cret);
+       if (ret / 100 == 2)
+               ipc->downloading = 0;
+       return ret;
+}
+
+
+/* MSGP */
+int CtdlIPCSpecifyPreferredFormats(CtdlIPC *ipc, char *cret, char *formats) {
+       register int ret;
+       char cmd[SIZ];
+       
+       snprintf(cmd, sizeof cmd, "MSGP %s", formats);
+       ret = CtdlIPCGenericCommand(ipc, cmd, NULL, 0, NULL, NULL, cret);
+       return ret;
+}
+
+
+
+/* READ */
+int CtdlIPCReadDownload(CtdlIPC *ipc, void **buf, size_t bytes, size_t resume,
+               void (*progress_gauge_callback)
+                       (CtdlIPC*, unsigned long, unsigned long),
+              char *cret)
+{
+       register size_t len;
+
+       if (!cret) return -1;
+       if (!buf) return -1;
+       if (*buf) return -1;
+       if (!ipc->downloading) return -1;
+
+       len = resume;
+       if (progress_gauge_callback)
+               progress_gauge_callback(ipc, len, bytes);
+       while (len < bytes) {
+               register size_t block;
+
+               block = CtdlIPCPartialRead(ipc, buf, len, 4096, cret);
+               if (block == 0) {
+                       free(*buf);
+                       return 0;
+               }
+               len += block;
+               if (progress_gauge_callback)
+                       progress_gauge_callback(ipc, len, bytes);
+       }
+       return len;
+}
+
+/* READ - pipelined */
+int CtdlIPCHighSpeedReadDownload(CtdlIPC *ipc, void **buf, size_t bytes,
+              size_t resume,
+               void (*progress_gauge_callback)
+                       (CtdlIPC*, unsigned long, unsigned long),
+              char *cret)
+{
+       register size_t len;
+       register int calls;     /* How many calls in the pipeline */
+       register int i;         /* iterator */
+       char aaa[4096];
+
+       if (!cret) return -1;
+       if (!buf) return -1;
+       if (*buf) return -1;
+       if (!ipc->downloading) return -1;
+
+       *buf = (void *)realloc(*buf, bytes - resume);
+       if (!*buf) return -1;
+
+       len = 0;
+       CtdlIPC_lock(ipc);
+       if (progress_gauge_callback)
+               progress_gauge_callback(ipc, len, bytes);
+
+       /* How many calls will be in the pipeline? */
+       calls = (bytes - resume) / 4096;
+       if ((bytes - resume) % 4096) calls++;
+
+       /* Send all requests at once */
+       for (i = 0; i < calls; i++) {
+               sprintf(aaa, "READ %d|4096", (int)(i * 4096 + resume) );
+               CtdlIPC_putline(ipc, aaa);
+       }
+
+       /* Receive all responses at once */
+       for (i = 0; i < calls; i++) {
+               CtdlIPC_getline(ipc, aaa);
+               if (aaa[0] != '6')
+                       strcpy(cret, &aaa[4]);
+               else {
+                       len = extract_long(&aaa[4], 0);
+                       /* I know what I'm doing */
+                       serv_read(ipc, ((char *)(*buf) + (i * 4096)), len);
+               }
+               if (progress_gauge_callback)
+                       progress_gauge_callback(ipc, i * 4096 + len, bytes);
+       }
+       CtdlIPC_unlock(ipc);
+       return len;
+}
+
+
+/* UCLS */
+int CtdlIPCEndUpload(CtdlIPC *ipc, int discard, char *cret)
+{
+       register int ret;
+       char cmd[8];
+
+       if (!cret) return -1;
+       if (!ipc->uploading) return -1;
+
+       sprintf(cmd, "UCLS %d", discard ? 0 : 1);
+       ret = CtdlIPCGenericCommand(ipc, cmd, NULL, 0, NULL, NULL, cret);
+       ipc->uploading = 0;
+       return ret;
+}
+
+
+/* WRIT */
+int CtdlIPCWriteUpload(CtdlIPC *ipc, FILE *uploadFP,
+               void (*progress_gauge_callback)
+                       (CtdlIPC*, unsigned long, unsigned long),
+               char *cret)
+{
+       register int ret = -1;
+       register size_t offset = 0;
+       size_t bytes;
+       char aaa[SIZ];
+       char buf[4096];
+       FILE *fd = uploadFP;
+       int ferr;
+
+       if (!cret) return -1;
+
+       fseek(fd, 0L, SEEK_END);
+       bytes = ftell(fd);
+       rewind(fd);
+
+       if (progress_gauge_callback)
+               progress_gauge_callback(ipc, 0, bytes);
+
+       while (offset < bytes) {
+               register size_t to_write;
+
+               /* Read some data in */
+               to_write = fread(buf, 1, 4096, fd);
+               if (!to_write) {
+                       if (feof(fd) || ferror(fd)) break;
+               }
+               sprintf(aaa, "WRIT %d", (int)to_write);
+               CtdlIPC_putline(ipc, aaa);
+               CtdlIPC_getline(ipc, aaa);
+               strcpy(cret, &aaa[4]);
+               ret = atoi(aaa);
+               if (aaa[0] == '7') {
+                       to_write = extract_long(&aaa[4], 0);
+                       
+                       serv_write(ipc, buf, to_write);
+                       offset += to_write;
+                       if (progress_gauge_callback)
+                               progress_gauge_callback(ipc, offset, bytes);
+                       /* Detect short reads and back up if needed */
+                       /* offset will never be negative anyway */
+                       fseek(fd, (signed)offset, SEEK_SET);
+               } else {
+                       break;
+               }
+       }
+       if (progress_gauge_callback)
+               progress_gauge_callback(ipc, 1, 1);
+       ferr = ferror(fd);
+       fclose(fd);
+       return (!ferr ? ret : -2);
+}
+
+
+/*
+ * Generic command method.  This method should handle any server command
+ * except for CHAT.  It takes the following arguments:
+ *
+ * ipc                 The server to speak with
+ * command             Preformatted command to send to server
+ * to_send             A text or binary file to send to server
+ *                     (only sent if server requests it)
+ * bytes_to_send       The number of bytes in to_send (required if
+ *                     sending binary, optional if sending listing)
+ * to_receive          Pointer to a NULL pointer, if the server
+ *                     sends text or binary we will allocate memory
+ *                     for the file and stuff it here
+ * bytes_to_receive    If a file is received, we will store its
+ *                     byte count here
+ * proto_response      The protocol response.  Caller must provide
+ *                     this buffer and ensure that it is at least
+ *                     128 bytes in length.
+ *
+ * This function returns a number equal to the protocol response number,
+ * -1 if an internal error occurred, -2 if caller provided bad values,
+ * or 0 - the protocol response number if bad values were found during
+ * the protocol exchange.
+ * It stores the protocol response string (minus the number) in 
+ * protocol_response as described above.  Some commands send additional
+ * data in this string.
+ */
+int CtdlIPCGenericCommand(CtdlIPC *ipc,
+               const char *command, const char *to_send,
+               size_t bytes_to_send, char **to_receive, 
+               size_t *bytes_to_receive, char *proto_response)
+{
+       char buf[SIZ];
+       register int ret;
+       int watch_ssl = 0;
+
+       if (!command) return -2;
+       if (!proto_response) return -2;
+
+#ifdef HAVE_OPENSSL
+       if (ipc->ssl) watch_ssl = 1;
+#endif
+
+       CtdlIPC_lock(ipc);
+       CtdlIPC_putline(ipc, command);
+       while (1) {
+               CtdlIPC_getline(ipc, proto_response);
+               if (proto_response[3] == '*')
+                       instant_msgs = 1;
+               ret = atoi(proto_response);
+               strcpy(proto_response, &proto_response[4]);
+               switch (ret / 100) {
+               default:                        /* Unknown, punt */
+               case 2:                         /* OK */
+               case 3:                         /* MORE_DATA */
+               case 5:                         /* ERROR */
+                       /* Don't need to do anything */
+                       break;
+               case 1:                         /* LISTING_FOLLOWS */
+                       if (to_receive && !*to_receive && bytes_to_receive) {
+                               *to_receive = CtdlIPCReadListing(ipc, NULL);
+                       } else { /* Drain */
+                               while (CtdlIPC_getline(ipc, buf), strcmp(buf, "000")) ;
+                               ret = -ret;
+                       }
+                       break;
+               case 4:                         /* SEND_LISTING */
+                       if (to_send) {
+                               CtdlIPCSendListing(ipc, to_send);
+                       } else {
+                               /* No listing given, fake it */
+                               CtdlIPC_putline(ipc, "000");
+                               ret = -ret;
+                       }
+                       break;
+               case 6:                         /* BINARY_FOLLOWS */
+                       if (to_receive && !*to_receive && bytes_to_receive) {
+                               *bytes_to_receive =
+                                       extract_long(proto_response, 0);
+                               *to_receive = (char *)
+                                       malloc((size_t)*bytes_to_receive);
+                               if (!*to_receive) {
+                                       ret = -1;
+                               } else {
+                                       serv_read(ipc, *to_receive,
+                                                       *bytes_to_receive);
+                               }
+                       } else {
+                               /* Drain */
+                               size_t drain;
+
+                               drain = extract_long(proto_response, 0);
+                               while (drain > SIZ) {
+                                       serv_read(ipc, buf, SIZ);
+                                       drain -= SIZ;
+                               }
+                               serv_read(ipc, buf, drain);
+                               ret = -ret;
+                       }
+                       break;
+               case 7:                         /* SEND_BINARY */
+                       if (to_send && bytes_to_send) {
+                               serv_write(ipc, to_send, bytes_to_send);
+                       } else if (bytes_to_send) {
+                               /* Fake it, send nulls */
+                               size_t fake;
+
+                               fake = bytes_to_send;
+                               memset(buf, '\0', SIZ);
+                               while (fake > SIZ) {
+                                       serv_write(ipc, buf, SIZ);
+                                       fake -= SIZ;
+                               }
+                               serv_write(ipc, buf, fake);
+                               ret = -ret;
+                       } /* else who knows?  DANGER WILL ROBINSON */
+                       break;
+               case 8:                         /* START_CHAT_MODE */
+                       if (!strncasecmp(command, "CHAT", 4)) {
+                               /* Don't call chatmode with generic! */
+                               CtdlIPC_putline(ipc, "/quit");
+                               ret = -ret;
+                       } else {
+                               /* In this mode we send then receive listing */
+                               if (to_send) {
+                                       CtdlIPCSendListing(ipc, to_send);
+                               } else {
+                                       /* No listing given, fake it */
+                                       CtdlIPC_putline(ipc, "000");
+                                       ret = -ret;
+                               }
+                               if (to_receive && !*to_receive
+                                               && bytes_to_receive) {
+                                       *to_receive = CtdlIPCReadListing(ipc, NULL);
+                               } else { /* Drain */
+                                       while (CtdlIPC_getline(ipc, buf),
+                                                       strcmp(buf, "000")) ;
+                                       ret = -ret;
+                               }
+                       }
+                       break;
+               case 9:                         /* ASYNC_MSG */
+                       /* CtdlIPCDoAsync(ret, proto_response); */
+                       free(CtdlIPCReadListing(ipc, NULL));    /* STUB FIXME */
+                       break;
+               }
+               if (ret / 100 != 9)
+                       break;
+       }
+       CtdlIPC_unlock(ipc);
+       return ret;
+}
+
+
+static int connectsock(char *host, char *service, char *protocol, int defaultPort)
+{
+       struct hostent *phe;
+       struct servent *pse;
+       struct protoent *ppe;
+       struct sockaddr_in sin;
+       int s, type;
+
+       memset(&sin, 0, sizeof(sin));
+       sin.sin_family = AF_INET;
+
+       pse = getservbyname(service, protocol);
+       if (pse != NULL) {
+               sin.sin_port = pse->s_port;
+       }
+       else if (atoi(service) > 0) {
+               sin.sin_port = htons(atoi(service));
+       }
+       else {
+               sin.sin_port = htons(defaultPort);
+       }
+       phe = gethostbyname(host);
+       if (phe) {
+               memcpy(&sin.sin_addr, phe->h_addr, phe->h_length);
+       } else if ((sin.sin_addr.s_addr = inet_addr(host)) == INADDR_NONE) {
+               return -1;
+       }
+       if ((ppe = getprotobyname(protocol)) == 0) {
+               return -1;
+       }
+       if (!strcmp(protocol, "udp")) {
+               type = SOCK_DGRAM;
+       } else {
+               type = SOCK_STREAM;
+       }
+
+       s = socket(PF_INET, type, ppe->p_proto);
+       if (s < 0) {
+               return -1;
+       }
+
+       if (connect(s, (struct sockaddr *) &sin, sizeof(sin)) < 0) {
+               close(s);
+               return -1;
+       }
+
+       return (s);
+}
+
+static int uds_connectsock(int *isLocal, char *sockpath)
+{
+       struct sockaddr_un addr;
+       int s;
+
+       memset(&addr, 0, sizeof(addr));
+       addr.sun_family = AF_UNIX;
+       safestrncpy(addr.sun_path, sockpath, sizeof addr.sun_path);
+
+       s = socket(AF_UNIX, SOCK_STREAM, 0);
+       if (s < 0) {
+               return -1;
+       }
+
+       if (connect(s, (struct sockaddr *) &addr, sizeof(addr)) < 0) {
+               close(s);
+               return -1;
+       }
+
+       *isLocal = 1;
+       return s;
+}
+
+
+/*
+ * input binary data from socket
+ */
+static void serv_read(CtdlIPC *ipc, char *buf, unsigned int bytes)
+{
+       unsigned int len, rlen;
+
+#if defined(HAVE_OPENSSL)
+       if (ipc->ssl) {
+               serv_read_ssl(ipc, buf, bytes);
+               return;
+       }
+#endif
+       len = 0;
+       while (len < bytes) {
+               rlen = read(ipc->sock, &buf[len], bytes - len);
+               if (rlen < 1) {
+                       connection_died(ipc, 0);
+                       return;
+               }
+               len += rlen;
+       }
+}
+
+
+/*
+ * send binary to server
+ */
+void serv_write(CtdlIPC *ipc, const char *buf, unsigned int nbytes)
+{
+       unsigned int bytes_written = 0;
+       int retval;
+
+#if defined(HAVE_OPENSSL)
+       if (ipc->ssl) {
+               serv_write_ssl(ipc, buf, nbytes);
+               return;
+       }
+#endif
+       while (bytes_written < nbytes) {
+               retval = write(ipc->sock, &buf[bytes_written],
+                              nbytes - bytes_written);
+               if (retval < 1) {
+                       connection_died(ipc, 0);
+                       return;
+               }
+               bytes_written += retval;
+       }
+}
+
+
+#ifdef HAVE_OPENSSL
+/*
+ * input binary data from encrypted connection
+ */
+static void serv_read_ssl(CtdlIPC* ipc, char *buf, unsigned int bytes)
+{
+       int len, rlen;
+       char junk[1];
+
+       len = 0;
+       while (len < bytes) {
+               if (SSL_want_read(ipc->ssl)) {
+                       if ((SSL_write(ipc->ssl, junk, 0)) < 1) {
+                               error_printf("SSL_write in serv_read:\n");
+                               ERR_print_errors_fp(stderr);
+                       }
+               }
+               rlen = SSL_read(ipc->ssl, &buf[len], bytes - len);
+               if (rlen < 1) {
+                       long errval;
+
+                       errval = SSL_get_error(ipc->ssl, rlen);
+                       if (errval == SSL_ERROR_WANT_READ ||
+                                       errval == SSL_ERROR_WANT_WRITE) {
+                               sleep(1);
+                               continue;
+                       }
+/***
+ Not sure why we'd want to handle these error codes any differently,
+ but this definitely isn't the way to handle them.  Someone must have
+ naively assumed that we could fall back to unencrypted communications,
+ but all it does is just recursively blow the stack.
+                       if (errval == SSL_ERROR_ZERO_RETURN ||
+                                       errval == SSL_ERROR_SSL) {
+                               serv_read(ipc, &buf[len], bytes - len);
+                               return;
+                       }
+ ***/
+                       error_printf("SSL_read in serv_read: %s\n",
+                                       ERR_reason_error_string(ERR_peek_error()));
+                       connection_died(ipc, 1);
+                       return;
+               }
+               len += rlen;
+       }
+}
+
+
+/*
+ * send binary to server encrypted
+ */
+static void serv_write_ssl(CtdlIPC *ipc, const char *buf, unsigned int nbytes)
+{
+       unsigned int bytes_written = 0;
+       int retval;
+       char junk[1];
+
+       while (bytes_written < nbytes) {
+               if (SSL_want_write(ipc->ssl)) {
+                       if ((SSL_read(ipc->ssl, junk, 0)) < 1) {
+                               error_printf("SSL_read in serv_write:\n");
+                               ERR_print_errors_fp(stderr);
+                       }
+               }
+               retval = SSL_write(ipc->ssl, &buf[bytes_written],
+                               nbytes - bytes_written);
+               if (retval < 1) {
+                       long errval;
+
+                       errval = SSL_get_error(ipc->ssl, retval);
+                       if (errval == SSL_ERROR_WANT_READ ||
+                                       errval == SSL_ERROR_WANT_WRITE) {
+                               sleep(1);
+                               continue;
+                       }
+                       if (errval == SSL_ERROR_ZERO_RETURN ||
+                                       errval == SSL_ERROR_SSL) {
+                               serv_write(ipc, &buf[bytes_written],
+                                               nbytes - bytes_written);
+                               return;
+                       }
+                       error_printf("SSL_write in serv_write: %s\n",
+                                       ERR_reason_error_string(ERR_peek_error()));
+                       connection_died(ipc, 1);
+                       return;
+               }
+               bytes_written += retval;
+       }
+}
+
+
+#ifdef THREADED_CLIENT
+static void ssl_lock(int mode, int n, const char *file, int line)
+{
+       if (mode & CRYPTO_LOCK)
+               pthread_mutex_lock(Critters[n]);
+       else
+               pthread_mutex_unlock(Critters[n]);
+}
+#endif /* THREADED_CLIENT */
+
+
+static void CtdlIPC_init_OpenSSL(void)
+{
+       int a;
+       SSL_METHOD *ssl_method;
+       DH *dh;
+       
+       /* already done init */
+       if (ssl_ctx) {
+               return;
+       }
+
+       /* Get started */
+       a = 0;
+       ssl_ctx = NULL;
+       dh = NULL;
+       SSL_load_error_strings();
+       SSLeay_add_ssl_algorithms();
+
+       /* Set up the SSL context in which we will oeprate */
+       ssl_method = SSLv23_client_method();
+       ssl_ctx = SSL_CTX_new(ssl_method);
+       if (!ssl_ctx) {
+               error_printf("SSL_CTX_new failed: %s\n",
+                               ERR_reason_error_string(ERR_get_error()));
+               return;
+       }
+       /* Any reasonable cipher we can get */
+       if (!(SSL_CTX_set_cipher_list(ssl_ctx, CIT_CIPHERS))) {
+               error_printf("No ciphers available for encryption\n");
+               return;
+       }
+       SSL_CTX_set_session_cache_mode(ssl_ctx, SSL_SESS_CACHE_BOTH);
+       
+       /* Load DH parameters into the context */
+       dh = DH_new();
+       if (!dh) {
+               error_printf("Can't allocate a DH object: %s\n",
+                               ERR_reason_error_string(ERR_get_error()));
+               return;
+       }
+       if (!(BN_hex2bn(&(dh->p), DH_P))) {
+               error_printf("Can't assign DH_P: %s\n",
+                               ERR_reason_error_string(ERR_get_error()));
+               DH_free(dh);
+               return;
+       }
+       if (!(BN_hex2bn(&(dh->g), DH_G))) {
+               error_printf("Can't assign DH_G: %s\n",
+                               ERR_reason_error_string(ERR_get_error()));
+               DH_free(dh);
+               return;
+       }
+       dh->length = DH_L;
+       SSL_CTX_set_tmp_dh(ssl_ctx, dh);
+       DH_free(dh);
+
+#ifdef THREADED_CLIENT
+       /* OpenSSL requires callbacks for threaded clients */
+       CRYPTO_set_locking_callback(ssl_lock);
+       CRYPTO_set_id_callback(id_callback);
+
+       /* OpenSSL requires us to do semaphores for threaded clients */
+       Critters = malloc(CRYPTO_num_locks() * sizeof (pthread_mutex_t *));
+       if (!Critters) {
+               perror("malloc failed");
+               exit(1);
+       } else {
+               for (a = 0; a < CRYPTO_num_locks(); a++) {
+                       Critters[a] = malloc(sizeof (pthread_mutex_t));
+                       if (!Critters[a]) {
+                               perror("malloc failed");
+                               exit(1);
+                       }
+                       pthread_mutex_init(Critters[a], NULL);
+               }
+       }
+#endif /* THREADED_CLIENT */       
+}
+
+
+
+#ifdef THREADED_CLIENT
+static unsigned long id_callback(void) {
+       return (unsigned long)pthread_self();
+}
+#endif /* THREADED_CLIENT */
+#endif /* HAVE_OPENSSL */
+
+
+int
+ReadNetworkChunk(CtdlIPC* ipc)
+{
+       fd_set read_fd;
+       int tries;
+       int ret = 0;
+       int err = 0;
+       struct timeval tv;
+       size_t n;
+
+       tv.tv_sec = 1;
+       tv.tv_usec = 1000;
+       tries = 0;
+       n = 0;
+       while (1)
+       {
+               errno=0;
+               FD_ZERO(&read_fd);
+               FD_SET(ipc->sock, &read_fd);
+               ret = select(ipc->sock+1, &read_fd, NULL, NULL,  &tv);
+               
+//             fprintf(stderr, "\nselect failed: %d %d %s\n", ret,  err, strerror(err));
+               
+               if (ret > 0) {
+                       
+                       *(ipc->BufPtr) = '\0';
+//                     n = read(ipc->sock, ipc->BufPtr, ipc->BufSize  - (ipc->BufPtr - ipc->Buf) - 1);
+                       n = recv(ipc->sock, ipc->BufPtr, ipc->BufSize  - (ipc->BufPtr - ipc->Buf) - 1, 0);
+                       if (n > 0) {
+                               ipc->BufPtr[n]='\0';
+                               ipc->BufUsed += n;
+                               return n;
+                       }
+                       else 
+                               return n;
+               }
+               else if (ret < 0) {
+                       if (!(errno == EINTR || errno == EAGAIN))
+                               error_printf( "\nselect failed: %d %s\n", err, strerror(err));
+                       return -1;
+               }/*
+               else {
+                       tries ++;
+                       if (tries >= 10)
+                       n = read(ipc->sock, ipc->BufPtr, ipc->BufSize  - (ipc->BufPtr - ipc->Buf) - 1);
+                       if (n > 0) {
+                               ipc->BufPtr[n]='\0';
+                               ipc->BufUsed += n;
+                               return n;
+                       }
+                       else {
+                               connection_died(ipc, 0);
+                               return -1;
+                       }
+                       }*/
+       }
+}
+
+/*
+ * input string from socket - implemented in terms of serv_read()
+ */
+#ifdef CHUNKED_READ
+
+static void CtdlIPC_getline(CtdlIPC* ipc, char *buf)
+{
+       int i, ntries;
+       char *aptr, *bptr, *aeptr, *beptr;
+
+//     error_printf("---\n");
+
+       beptr = buf + SIZ;
+#if defined(HAVE_OPENSSL)
+       if (ipc->ssl) {
+               
+               /* Read one character at a time. */
+               for (i = 0;; i++) {
+                       serv_read(ipc, &buf[i], 1);
+                       if (buf[i] == '\n' || i == (SIZ-1))
+                               break;
+               }
+               
+               /* If we got a long line, discard characters until the newline. */
+               if (i == (SIZ-1))
+                       while (buf[i] != '\n')
+                               serv_read(ipc, &buf[i], 1);
+               
+               /* Strip the trailing newline (and carriage return, if present) */
+               if (i>=0 && buf[i] == 10) buf[i--] = 0;
+               if (i>=0 && buf[i] == 13) buf[i--] = 0;
+       }
+       else
+#endif
+       {
+               if (ipc->Buf == NULL)
+               {
+                       ipc->BufSize = SIZ;
+                       ipc->Buf = (char*) malloc(ipc->BufSize + 10);
+                       *(ipc->Buf) = '\0';
+                       ipc->BufPtr = ipc->Buf;
+               }
+
+               ntries = 0;
+//             while ((ipc->BufUsed == 0)||(ntries++ > 10))
+               if (ipc->BufUsed == 0)
+                       ReadNetworkChunk(ipc);
+
+////           if (ipc->BufUsed != 0) while (1)
+               bptr = buf;
+
+               while (1)
+               {
+                       aptr = ipc->BufPtr;
+                       aeptr = ipc->Buf + ipc->BufSize;
+                       while ((aptr < aeptr) && 
+                              (bptr < beptr) &&
+                              (*aptr != '\0') && 
+                              (*aptr != '\n'))
+                               *(bptr++) = *(aptr++);
+                       if ((*aptr == '\n') && (aptr < aeptr))
+                       {
+                               /* Terminate it right, remove the line breaks */
+                               while ((aptr < aeptr) && ((*aptr == '\n') || (*aptr == '\r')))
+                                       aptr ++;
+                               while ((aptr < aeptr ) && (*(aptr + 1) == '\0') )
+                                       aptr ++;
+                               *(bptr++) = '\0';
+//                             fprintf(stderr, "parsing %d %d %d - %d %d %d %s\n", ipc->BufPtr - ipc->Buf, aptr - ipc->BufPtr, ipc->BufUsed , *aptr, *(aptr-1), *(aptr+1), buf);
+                               if ((bptr > buf + 1) && (*(bptr-1) == '\r'))
+                                       *(--bptr) = '\0';
+                               
+                               /* is there more in the buffer we need to read later? */
+                               if (ipc->Buf + ipc->BufUsed > aptr)
+                               {
+                                       ipc->BufPtr = aptr;
+                               }
+                               else
+                               {
+                                       ipc->BufUsed = 0;
+                                       ipc->BufPtr = ipc->Buf;
+                               }
+//                             error_printf("----bla6\n");
+                               return;
+                               
+                       }/* should we move our read stuf to the bufferstart so we have more space at the end? */
+                       else if ((ipc->BufPtr != ipc->Buf) && 
+                                (ipc->BufUsed > (ipc->BufSize  - (ipc->BufSize / 4))))
+                       {
+                               size_t NewBufSize = ipc->BufSize * 2;
+                               int delta = (ipc->BufPtr - ipc->Buf);
+                               char *NewBuf;
+
+                               /* if the line would end after our buffer, we should use a bigger buffer. */
+                               NewBuf = (char *)malloc (NewBufSize + 10);
+                               memcpy (NewBuf, ipc->BufPtr, ipc->BufUsed - delta);
+                               free(ipc->Buf);
+                               ipc->Buf = ipc->BufPtr = NewBuf;
+                               ipc->BufUsed -= delta;
+                               ipc->BufSize = NewBufSize;
+                       }
+                       if (ReadNetworkChunk(ipc) <0)
+                       {
+//                             error_printf("----bla\n");
+                               return;
+                       }
+               }
+///            error_printf("----bl45761%s\nipc->BufUsed");
+       }
+//     error_printf("----bla1\n");
+}
+
+#else  /* CHUNKED_READ */
+
+static void CtdlIPC_getline(CtdlIPC* ipc, char *buf)
+{
+       int i;
+
+       /* Read one character at a time. */
+       for (i = 0;; i++) {
+               serv_read(ipc, &buf[i], 1);
+               if (buf[i] == '\n' || i == (SIZ-1))
+                       break;
+       }
+
+       /* If we got a long line, discard characters until the newline. */
+       if (i == (SIZ-1))
+               while (buf[i] != '\n')
+                       serv_read(ipc, &buf[i], 1);
+
+       /* Strip the trailing newline (and carriage return, if present) */
+       if (i>=0 && buf[i] == 10) buf[i--] = 0;
+       if (i>=0 && buf[i] == 13) buf[i--] = 0;
+}
+
+
+#endif /* CHUNKED_READ */
+
+
+void CtdlIPC_chat_recv(CtdlIPC* ipc, char* buf)
+{
+       CtdlIPC_getline(ipc, buf);
+}
+
+/*
+ * send line to server - implemented in terms of serv_write()
+ */
+static void CtdlIPC_putline(CtdlIPC *ipc, const char *buf)
+{
+       char *cmd = NULL;
+       int len;
+
+       len = strlen(buf);
+       cmd = malloc(len + 2);
+       if (!cmd) {
+               /* This requires no extra memory */
+               serv_write(ipc, buf, len);
+               serv_write(ipc, "\n", 1);
+       } else {
+               /* This is network-optimized */
+               strncpy(cmd, buf, len);
+               strcpy(cmd + len, "\n");
+               serv_write(ipc, cmd, len + 1);
+               free(cmd);
+       }
+
+       ipc->last_command_sent = time(NULL);
+}
+
+void CtdlIPC_chat_send(CtdlIPC* ipc, const char* buf)
+{
+       CtdlIPC_putline(ipc, buf);
+}
+
+
+/*
+ * attach to server
+ */
+CtdlIPC* CtdlIPC_new(int argc, char **argv, char *hostbuf, char *portbuf)
+{
+       int a;
+       char cithost[SIZ];
+       char citport[SIZ];
+       char sockpath[SIZ];
+       CtdlIPC* ipc;
+
+       ipc = ialloc(CtdlIPC);
+       if (!ipc) {
+               return 0;
+       }
+#if defined(HAVE_OPENSSL)
+       ipc->ssl = NULL;
+       CtdlIPC_init_OpenSSL();
+#endif
+#if defined(HAVE_PTHREAD_H)
+       pthread_mutex_init(&(ipc->mutex), NULL); /* Default fast mutex */
+#endif
+       ipc->sock = -1;                 /* Not connected */
+       ipc->isLocal = 0;               /* Not local, of course! */
+       ipc->downloading = 0;
+       ipc->uploading = 0;
+       ipc->last_command_sent = 0L;
+       ipc->network_status_cb = NULL;
+       ipc->Buf = NULL;
+       ipc->BufUsed = 0;
+       ipc->BufPtr = NULL;
+
+       strcpy(cithost, DEFAULT_HOST);  /* default host */
+       strcpy(citport, DEFAULT_PORT);  /* default port */
+
+       /* Allow caller to supply our values (Windows) */
+       if (hostbuf && strlen(hostbuf) > 0)
+               strcpy(cithost, hostbuf);
+       if (portbuf && strlen(portbuf) > 0)
+               strcpy(citport, portbuf);
+
+       /* Read host/port from command line if present */
+       for (a = 0; a < argc; ++a) {
+               if (a == 0) {
+                       /* do nothing */
+               } else if (a == 1) {
+                       strcpy(cithost, argv[a]);
+               } else if (a == 2) {
+                       strcpy(citport, argv[a]);
+               } else {
+                       error_printf("%s: usage: ",argv[0]);
+                       error_printf("%s [host] [port] ",argv[0]);
+                       ifree(ipc);
+                       errno = EINVAL;
+                       return 0;
+               }
+       }
+
+       if ((!strcmp(cithost, "localhost"))
+          || (!strcmp(cithost, "127.0.0.1"))) {
+               ipc->isLocal = 1;
+       }
+
+       /* If we're using a unix domain socket we can do a bunch of stuff */
+       if (!strcmp(cithost, UDS)) {
+               if (!strcasecmp(citport, DEFAULT_PORT)) {
+                       snprintf(sockpath, sizeof sockpath, file_citadel_socket);
+               }
+               else {
+                       snprintf(sockpath, sizeof sockpath, "%s/%s", citport, "citadel.socket");
+               }
+               ipc->sock = uds_connectsock(&(ipc->isLocal), sockpath);
+               if (ipc->sock == -1) {
+                       ifree(ipc);
+                       return 0;
+               }
+               if (hostbuf != NULL) strcpy(hostbuf, cithost);
+               if (portbuf != NULL) strcpy(portbuf, sockpath);
+               return ipc;
+       }
+
+       ipc->sock = connectsock(cithost, citport, "tcp", 504);
+       if (ipc->sock == -1) {
+               ifree(ipc);
+               return 0;
+       }
+       if (hostbuf != NULL) strcpy(hostbuf, cithost);
+       if (portbuf != NULL) strcpy(portbuf, citport);
+       return ipc;
+}
+
+
+/*
+ * Disconnect and delete the IPC class (destructor)
+ */
+void CtdlIPC_delete(CtdlIPC* ipc)
+{
+#ifdef HAVE_OPENSSL
+       if (ipc->ssl) {
+               SSL_shutdown(ipc->ssl);
+               SSL_free(ipc->ssl);
+               ipc->ssl = NULL;
+       }
+#endif
+       if (ipc->sock > -1) {
+               shutdown(ipc->sock, 2); /* Close it up */
+               ipc->sock = -1;
+       }
+       if (ipc->Buf != NULL)
+               free (ipc->Buf);
+       ipc->Buf = NULL;
+       ipc->BufPtr = NULL;
+       ifree(ipc);
+}
+
+
+/*
+ * Disconnect and delete the IPC class (destructor)
+ * Also NULLs out the pointer
+ */
+void CtdlIPC_delete_ptr(CtdlIPC** pipc)
+{
+       CtdlIPC_delete(*pipc);
+       *pipc = NULL;
+}
+
+
+/*
+ * return the file descriptor of the server socket so we can select() on it.
+ *
+ * FIXME: This is only used in chat mode; eliminate it when chat mode gets
+ * rewritten...
+ */
+int CtdlIPC_getsockfd(CtdlIPC* ipc)
+{
+       return ipc->sock;
+}
+
+
+/*
+ * return one character
+ *
+ * FIXME: This is only used in chat mode; eliminate it when chat mode gets
+ * rewritten...
+ */
+char CtdlIPC_get(CtdlIPC* ipc)
+{
+       char buf[2];
+       char ch;
+
+       serv_read(ipc, buf, 1);
+       ch = (int) buf[0];
+
+       return (ch);
+}
diff --git a/citadel/utils/aidepost.c b/citadel/utils/aidepost.c
new file mode 100644 (file)
index 0000000..7b637f5
--- /dev/null
@@ -0,0 +1,168 @@
+/*
+ * $Id$
+ *
+ * This is just a little hack to copy standard input to a message in Aide>
+ *
+ * Copyright (c) 1987-2009 by the citadel.org team
+ *
+ *  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 3 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; if not, write to the Free Software
+ *  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ */
+
+#include <stdlib.h>
+#include <unistd.h>
+#include <stdio.h>
+
+#if TIME_WITH_SYS_TIME
+# include <sys/time.h>
+# include <time.h>
+#else
+# if HAVE_SYS_TIME_H
+#  include <sys/time.h>
+# else
+#  include <time.h>
+# endif
+#endif
+
+#include <limits.h>
+#include <errno.h>
+#include <string.h>
+#include "citadel.h"
+#include "citadel_dirs.h"
+#include "config.h"
+
+#ifndef HAVE_SNPRINTF
+#include "snprintf.h"
+#endif
+
+
+/*
+ * Simplified function to generate a message in our format
+ */
+static void ap_make_message(FILE *fp, char *target_room, char *author, char *subject)
+{
+       int a;
+       long bb, cc;
+       time_t now;
+       time(&now);
+       putc(255, fp);
+       putc(MES_NORMAL, fp);
+       putc(1, fp);
+       fprintf(fp, "Proom_aide");
+       putc(0, fp);
+       fprintf(fp, "T%ld", (long)now);
+       putc(0, fp);
+       fprintf(fp, "A%s", author);
+       putc(0, fp);
+       fprintf(fp, "O%s", target_room);
+       putc(0, fp);
+       if (strlen(subject) > 0) {
+               fprintf(fp, "U%s%c", subject, 0);
+       }
+       fprintf(fp, "N%s", NODENAME);
+       putc(0, fp);
+       putc('M', fp);
+       bb = ftell(fp);
+       while (a = getc(stdin), a > 0) {
+               if (a != 8)
+                       putc(a, fp);
+               else {
+                       cc = ftell(fp);
+                       if (cc != bb)
+                               fseek(fp, (-1L), 1);
+               }
+       }
+       putc(0, fp);
+       putc(0, fp);
+       putc(0, fp);
+}
+
+int main(int argc, char **argv)
+{
+       char tempspool[64];
+       char target_room[ROOMNAMELEN];
+       char author[64];
+       char subject[256];
+       FILE *tempfp, *spoolfp;
+       int ch;
+       int i;
+
+       int relh=0;
+       int home=0;
+       char relhome[PATH_MAX]="";
+       char ctdldir[PATH_MAX]=CTDLDIR;
+
+       /* TODO: should we be able to calculate relative dirs? */
+       calc_dirs_n_files(relh, home, relhome, ctdldir, 0);
+
+
+       get_config();
+
+       strcpy(target_room, "Aide");
+       strcpy(author, "Citadel");
+       strcpy(subject, "");
+       for (i=1; i<argc; ++i) {
+               if (!strncasecmp(argv[i], "-r", 2)) {
+                       strncpy(target_room, &argv[i][2], sizeof(target_room));
+                       target_room[sizeof(target_room)-1] = 0;
+               }
+               else if (!strncasecmp(argv[i], "-a", 2)) {
+                       strncpy(author, &argv[i][2], sizeof(author));
+                       author[sizeof(author)-1] = 0;
+               }
+               else if (!strncasecmp(argv[i], "-s", 2)) {
+                       strncpy(subject, &argv[i][2], sizeof(subject));
+                       subject[sizeof(subject)-1] = 0;
+               } else {
+                       fprintf(stderr, "%s: usage: %s "
+                                       "[-rTargetRoom] [-aAuthor] [-sSubject]\n",
+                               argv[0], argv[0]);
+                       exit(1);
+               }
+       }
+
+       snprintf(tempspool, sizeof tempspool,
+                        "%s/ap.%04lx",
+                        ctdl_netin_dir,
+               (long)getpid());
+
+       unlink(tempspool);
+
+       tempfp = fopen(tempspool, "w+b");
+       unlink(tempspool);
+       if (tempfp == NULL) {
+               perror("cannot open temp file");
+               exit(errno);
+       }
+
+       /* Generate a message from stdin */
+       ap_make_message(tempfp, target_room, author, subject);
+
+       /* Copy it to a new temp file in the spool directory */
+       rewind(tempfp);
+
+       spoolfp = fopen(tempspool, "wb");
+       if (spoolfp == NULL) {
+               perror("cannot open spool file");
+               exit(errno);
+       }
+       while (ch = getc(tempfp), (ch >= 0)) {
+               putc(ch, spoolfp);
+       }
+
+       fclose(tempfp);
+       fclose(spoolfp);
+
+       exit(0);
+}
diff --git a/citadel/utils/base64.c b/citadel/utils/base64.c
new file mode 100644 (file)
index 0000000..388606c
--- /dev/null
@@ -0,0 +1,336 @@
+/*
+ * $Id$
+ *
+ * Encode or decode file as MIME base64 (RFC 1341)
+ * Public domain by John Walker, August 11 1997
+ * Modified slightly for the Citadel system, June 1999
+ *
+ *  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 3 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; if not, write to the Free Software
+ *  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ */
+
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <ctype.h>
+#include <string.h>
+
+#define TRUE  1
+#define FALSE 0
+
+#define LINELEN 72                   /* Encoded line length (max 76) */
+
+typedef unsigned char byte;          /* Byte type */
+
+FILE *fi;                            /* Input file */
+FILE *fo;                            /* Output file */
+static byte iobuf[256];              /* I/O buffer */
+static int iolen = 0;                /* Bytes left in I/O buffer */
+static int iocp = 256;               /* Character removal pointer */
+static int ateof = FALSE;            /* EOF encountered */
+static byte dtable[256];             /* Encode / decode table */
+static int linelength = 0;           /* Length of encoded output line */
+static char eol[] = "\r\n";           /* End of line sequence */
+static int errcheck = TRUE;          /* Check decode input for errors ? */
+
+/*  INBUF  --  Fill input buffer with data  */
+
+static int inbuf(void)
+{
+    int l;
+
+    if (ateof) {
+       return FALSE;
+    }
+    l = fread(iobuf, 1, sizeof iobuf, fi);     /* Read input buffer */
+    if (l <= 0) {
+       if (ferror(fi)) {
+           exit(1);
+       }
+       ateof = TRUE;
+       return FALSE;
+    }
+    iolen = l;
+    iocp = 0;
+    return TRUE;
+}
+
+/*  INCHAR  -- Return next character from input  */
+
+static int inchar(void)
+{
+    if (iocp >= iolen) {
+       if (!inbuf()) {
+         return EOF;
+       }
+    }
+
+    return iobuf[iocp++];
+}
+
+/*  OCHAR  --  Output an encoded character, inserting line breaks
+              where required.  */
+
+static void ochar(int c)
+{
+    if (linelength >= LINELEN) {
+       if (fputs(eol, fo) == EOF) {
+           exit(1);
+       }
+       linelength = 0;
+    }
+    if (putc(((byte) c), fo) == EOF) {
+       exit(1);
+    }
+    linelength++;
+}
+
+/*  ENCODE  -- Encode binary file into base64.  */
+
+static void encode(void)
+{
+    int i, hiteof = FALSE;
+
+    /* Fill dtable with character encodings.  */
+
+    for (i = 0; i < 26; i++) {
+        dtable[i] = 'A' + i;
+        dtable[26 + i] = 'a' + i;
+    }
+    for (i = 0; i < 10; i++) {
+        dtable[52 + i] = '0' + i;
+    }
+    dtable[62] = '+';
+    dtable[63] = '/';
+
+    while (!hiteof) {
+       byte igroup[3], ogroup[4];
+       int c, n;
+
+       igroup[0] = igroup[1] = igroup[2] = 0;
+       for (n = 0; n < 3; n++) {
+           c = inchar();
+           if (c == EOF) {
+               hiteof = TRUE;
+               break;
+           }
+           igroup[n] = (byte) c;
+       }
+       if (n > 0) {
+           ogroup[0] = dtable[igroup[0] >> 2];
+           ogroup[1] = dtable[((igroup[0] & 3) << 4) | (igroup[1] >> 4)];
+           ogroup[2] = dtable[((igroup[1] & 0xF) << 2) | (igroup[2] >> 6)];
+           ogroup[3] = dtable[igroup[2] & 0x3F];
+
+            /* Replace characters in output stream with "=" pad
+              characters if fewer than three characters were
+              read from the end of the input stream. */
+
+           if (n < 3) {
+                ogroup[3] = '=';
+               if (n < 2) {
+                    ogroup[2] = '=';
+               }
+           }
+           for (i = 0; i < 4; i++) {
+               ochar(ogroup[i]);
+           }
+       }
+    }
+    if (fputs(eol, fo) == EOF) {
+       exit(1);
+    }
+}
+
+/*  INSIG  --  Return next significant input  */
+
+static int insig(void)
+{
+    int c;
+
+    /*CONSTANTCONDITION*/
+    while (TRUE) {
+       c = inchar();
+        if (c == EOF || (c > ' ')) {
+           return c;
+       }
+    }
+    /*NOTREACHED*/
+}
+
+/*  DECODE  -- Decode base64.  */
+
+static void decode(void)
+{
+    int i;
+
+    for (i = 0; i < 255; i++) {
+       dtable[i] = 0x80;
+    }
+    for (i = 'A'; i <= 'Z'; i++) {
+        dtable[i] = 0 + (i - 'A');
+    }
+    for (i = 'a'; i <= 'z'; i++) {
+        dtable[i] = 26 + (i - 'a');
+    }
+    for (i = '0'; i <= '9'; i++) {
+        dtable[i] = 52 + (i - '0');
+    }
+    dtable['+'] = 62;
+    dtable['/'] = 63;
+    dtable['='] = 0;
+
+    /*CONSTANTCONDITION*/
+    while (TRUE) {
+       byte a[4], b[4], o[3];
+
+       for (i = 0; i < 4; i++) {
+           int c = insig();
+
+           if (c == EOF) {
+               if (errcheck && (i > 0)) {
+                    fprintf(stderr, "Input file incomplete.\n");
+                   exit(1);
+               }
+               return;
+           }
+           if (dtable[c] & 0x80) {
+               if (errcheck) {
+                    fprintf(stderr, "Illegal character '%c' in input file.\n", c);
+                   exit(1);
+               }
+               /* Ignoring errors: discard invalid character. */
+               i--;
+               continue;
+           }
+           a[i] = (byte) c;
+           b[i] = (byte) dtable[c];
+       }
+       o[0] = (b[0] << 2) | (b[1] >> 4);
+       o[1] = (b[1] << 4) | (b[2] >> 2);
+       o[2] = (b[2] << 6) | b[3];
+        i = a[2] == '=' ? 1 : (a[3] == '=' ? 2 : 3);
+       if (fwrite(o, i, 1, fo) == EOF) {
+           exit(1);
+       }
+       if (i < 3) {
+           return;
+       }
+    }
+}
+
+/*  USAGE  --  Print how-to-call information.  */
+
+static void usage(char *pname)
+{
+    fprintf(stderr, "%s  --  Encode/decode file as base64.  Call:\n", pname);
+    fprintf(stderr,
+    "            %s [-e[ncode] / -d[ecode]] [-n] [infile] [outfile]\n", pname);
+    fprintf(stderr, "\n");
+    fprintf(stderr, "Options:\n");
+    fprintf(stderr, "           -D         Decode base64 encoded file\n");
+    fprintf(stderr, "           -E         Encode file into base64\n");
+    fprintf(stderr, "           -N         Ignore errors when decoding\n");
+    fprintf(stderr, "           -U         Print this message\n");
+    fprintf(stderr, "\n");
+    fprintf(stderr, "by John Walker\n");
+    fprintf(stderr, "   WWW:    http://www.fourmilab.ch/\n");
+}
+
+/*  Main program  */
+
+int main(int argc, char *argv[])
+{
+    int i, f = 0, decoding = FALSE;
+    char *cp, opt;
+
+    fi = stdin;
+    fo = stdout;
+
+    for (i = 1; i < argc; i++) {
+       cp = argv[i];
+        if (*cp == '-') {
+           opt = *(++cp);
+           if (islower(opt)) {
+               opt = toupper(opt);
+           }
+           switch (opt) {
+
+                case 'D':             /* -D  Decode */
+                   decoding = TRUE;
+                   break;
+
+                case 'E':             /* -E  Encode */
+                   decoding = FALSE;
+                   break;
+
+                case 'N':             /* -N  Suppress error checking */
+                   errcheck = FALSE;
+                   break;
+
+                case 'U':             /* -U  Print how-to-call information */
+                case '?':
+                   usage(argv[0]);
+                   return 0;
+          }
+       } else {
+           switch (f) {
+
+               /** Warning!  On systems which distinguish text mode and
+                   binary I/O (MS-DOS, Macintosh, etc.) the modes in these
+                   open statements will have to be made conditional based
+                   upon whether an encode or decode is being done, which
+                    will have to be specified earlier.  But it's worse: if
+                   input or output is from standard input or output, the 
+                   mode will have to be changed on the fly, which is
+                    generally system and compiler dependent.  'Twasn't me
+                    who couldn't conform to Unix CR/LF convention, so 
+                    don't ask me to write the code to work around
+                    Apple and Microsoft's incompatible standards. **/
+
+               case 0:
+                    if (strcmp(cp, "-") != 0) {
+                        if ((fi = fopen(cp, "r")) == NULL) {
+                            fprintf(stderr, "Cannot open input file %s\n", cp);
+                           return 2;
+                       }
+                   }
+                   f++;
+                   break;
+
+               case 1:
+                    if (strcmp(cp, "-") != 0) {
+                        if ((fo = fopen(cp, "w")) == NULL) {
+                            fprintf(stderr, "Cannot open output file %s\n", cp);
+                           return 2;
+                       }
+                   }
+                   f++;
+                   break;
+
+               default:
+                    fprintf(stderr, "Too many file names specified.\n");
+                   usage(argv[0]);
+                   return 2;
+           }
+       }
+    }
+
+    if (decoding) {
+       decode();
+    } else {
+       encode();
+    }
+    return 0;
+}
diff --git a/citadel/utils/chkpw.c b/citadel/utils/chkpw.c
new file mode 100644 (file)
index 0000000..ce5faaf
--- /dev/null
@@ -0,0 +1,141 @@
+/* 
+ *
+ *
+ * Copyright (c) 1987-2009 by the citadel.org team
+ *
+ *  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 3 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; if not, write to the Free Software
+ *  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ */
+
+#include <errno.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <stdio.h>
+#include <fcntl.h>
+#include <signal.h>
+#include <pwd.h>
+#include <ctype.h>
+#include <sys/types.h>
+#include <sys/wait.h>
+#include <sys/stat.h>
+#include <unistd.h>
+#include <string.h>
+#include <limits.h>
+#include <dirent.h>
+
+
+#include "citadel.h"
+#include "sysdep.h"
+#include "citadel_dirs.h"
+/* These pipes are used to talk to the chkpwd daemon, which is forked during startup */
+int chkpwd_write_pipe[2];
+int chkpwd_read_pipe[2];
+
+/*
+ * Validate a password on the host unix system by talking to the chkpwd daemon
+ */
+static int validpw(uid_t uid, const char *pass)
+{
+       char buf[256];
+
+       write(chkpwd_write_pipe[1], &uid, sizeof(uid_t));
+       write(chkpwd_write_pipe[1], pass, 256);
+       read(chkpwd_read_pipe[0], buf, 4);
+
+       if (!strncmp(buf, "PASS", 4)) {
+               printf("pass\n");
+               return(1);
+       }
+
+       printf("fail\n");
+       return 0;
+}
+
+/* 
+ * Start up the chkpwd daemon so validpw() has something to talk to
+ */
+void start_chkpwd_daemon(void) {
+       pid_t chkpwd_pid;
+       struct stat filestats;
+       int i;
+
+       printf("Starting chkpwd daemon for host authentication mode\n");
+
+       if ((stat(file_chkpwd, &filestats)==-1) ||
+           (filestats.st_size==0)){
+               printf("didn't find chkpwd daemon in %s: %s\n", file_chkpwd, strerror(errno));
+               abort();
+       }
+       if (pipe(chkpwd_write_pipe) != 0) {
+               printf("Unable to create pipe for chkpwd daemon: %s\n", strerror(errno));
+               abort();
+       }
+       if (pipe(chkpwd_read_pipe) != 0) {
+               printf("Unable to create pipe for chkpwd daemon: %s\n", strerror(errno));
+               abort();
+       }
+
+       chkpwd_pid = fork();
+       if (chkpwd_pid < 0) {
+               printf("Unable to fork chkpwd daemon: %s\n", strerror(errno));
+               abort();
+       }
+       if (chkpwd_pid == 0) {
+               dup2(chkpwd_write_pipe[0], 0);
+               dup2(chkpwd_read_pipe[1], 1);
+               for (i=2; i<256; ++i) close(i);
+               execl(file_chkpwd, file_chkpwd, NULL);
+               printf("Unable to exec chkpwd daemon: %s\n", strerror(errno));
+               abort();
+               exit(errno);
+       }
+}
+
+
+
+int main(int argc, char **argv) {
+       char buf[256];
+       struct passwd *p;
+       int uid;
+       char ctdldir[PATH_MAX]=CTDLDIR;
+       
+       calc_dirs_n_files(0,0,"", ctdldir, 0);
+       
+       printf("\n\n ** host auth mode test utility **\n\n");
+       start_chkpwd_daemon();
+
+       if (getuid() != 0){
+               printf("\n\nERROR: you need to be root to run this!\n\n");
+               return(1);
+       }
+       while(1) {
+               printf("\n\nUsername: ");
+               fgets(buf, sizeof buf, stdin);
+               buf[strlen(buf)-1] = 0;
+               p = getpwnam(buf);
+               if (p == NULL) {
+                       printf("Not found\n");
+               }
+               else {
+                       uid = p->pw_uid;
+                       printf("     uid: %d\n", uid);
+                       printf("Password: ");
+                       fgets(buf, sizeof buf, stdin);
+                       buf[strlen(buf)-1] = 0;
+                       validpw(uid, buf);
+               }
+       }
+
+       return(0);
+}
diff --git a/citadel/utils/chkpwd.c b/citadel/utils/chkpwd.c
new file mode 100644 (file)
index 0000000..604ff9b
--- /dev/null
@@ -0,0 +1,60 @@
+/*
+ * $Id$
+ *
+ * a setuid helper program for machines which use shadow passwords
+ * by Nathan Bryant, March 1999
+ *
+ *
+ * Copyright (c) 1987-2009 by the citadel.org team
+ *
+ *  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 3 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; if not, write to the Free Software
+ *  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ */
+
+#include <pwd.h>
+#include <stdio.h>
+#include <limits.h>
+#include <stdlib.h>
+#include <string.h>
+#include <syslog.h>
+#include <unistd.h>
+#include <sys/types.h>
+
+#include "auth.h"
+#include "config.h"
+#include "citadel_dirs.h"
+#include "citadel.h"
+
+int main(void)
+{
+       uid_t uid;
+       char buf[SIZ];
+
+       while (1) {
+               buf[0] = '\0';
+               read(0, &uid, sizeof(uid_t));   /* uid */
+               read(0, buf, 256);      /* password */
+
+               if (buf[0] == '\0') 
+                       return (0);
+               if (validate_password(uid, buf)) {
+                       write(1, "PASS", 4);
+               }
+               else {
+                       write(1, "FAIL", 4);
+               }
+       }
+
+       return(0);
+}
diff --git a/citadel/utils/citmail.c b/citadel/utils/citmail.c
new file mode 100644 (file)
index 0000000..2f1bdc2
--- /dev/null
@@ -0,0 +1,340 @@
+/*
+ * $Id$
+ *
+ * This program attempts to act like a local MDA if you're using sendmail or
+ * some other non-Citadel MTA.  It basically just contacts the Citadel LMTP
+ * listener on a unix domain socket and transmits the message.
+ *
+ * Copyright (c) 1987-2009 by the citadel.org team
+ *
+ *  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 3 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; if not, write to the Free Software
+ *  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ */
+
+#include "sysdep.h"
+#include <stdlib.h>
+#include <unistd.h>
+#include <ctype.h>
+#include <stdio.h>
+#include <signal.h>
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <sys/un.h>
+#include <netdb.h>
+#include <string.h>
+#include <pwd.h>
+#include <errno.h>
+#include <stdarg.h>
+#include <limits.h>
+#include <libcitadel.h>
+#include "citadel.h"
+#ifndef HAVE_SNPRINTF
+#include "snprintf.h"
+#endif
+#include "citadel_dirs.h"
+
+int serv_sock;
+int debug = 0;
+
+void strip_trailing_nonprint(char *buf)
+{
+        while ( (!IsEmptyStr(buf)) && (!isprint(buf[strlen(buf) - 1])) )
+                buf[strlen(buf) - 1] = 0;
+}
+
+
+void timeout(int signum)
+{
+       exit(signum);
+}
+
+
+int uds_connectsock(char *sockpath)
+{
+       int s;
+       struct sockaddr_un addr;
+
+       memset(&addr, 0, sizeof(addr));
+       addr.sun_family = AF_UNIX;
+       strncpy(addr.sun_path, sockpath, sizeof addr.sun_path);
+
+       s = socket(AF_UNIX, SOCK_STREAM, 0);
+       if (s < 0) {
+               fprintf(stderr, "citmail: Can't create socket: %s\n",
+                       strerror(errno));
+               exit(3);
+       }
+
+       if (connect(s, (struct sockaddr *) &addr, sizeof(addr)) < 0) {
+               fprintf(stderr, "citmail: can't connect: %s\n",
+                       strerror(errno));
+               close(s);
+               exit(3);
+       }
+
+       return s;
+}
+
+
+/*
+ * input binary data from socket
+ */
+void serv_read(char *buf, int bytes)
+{
+       int len, rlen;
+
+       len = 0;
+       while (len < bytes) {
+               rlen = read(serv_sock, &buf[len], bytes - len);
+               if (rlen < 1) {
+                       return;
+               }
+               len = len + rlen;
+       }
+}
+
+
+/*
+ * send binary to server
+ */
+void serv_write(char *buf, int nbytes)
+{
+       int bytes_written = 0;
+       int retval;
+       while (bytes_written < nbytes) {
+               retval = write(serv_sock, &buf[bytes_written],
+                              nbytes - bytes_written);
+               if (retval < 1) {
+                       return;
+               }
+               bytes_written = bytes_written + retval;
+       }
+}
+
+
+
+/*
+ * input string from socket - implemented in terms of serv_read()
+ */
+void serv_gets(char *buf)
+{
+       int i;
+
+       /* Read one character at a time.
+        */
+       for (i = 0;; i++) {
+               serv_read(&buf[i], 1);
+               if (buf[i] == '\n' || i == (SIZ-1))
+                       break;
+       }
+
+       /* If we got a long line, discard characters until the newline.
+        */
+       if (i == (SIZ-1))
+               while (buf[i] != '\n')
+                       serv_read(&buf[i], 1);
+
+       /* Strip all trailing nonprintables (crlf)
+        */
+       buf[i] = 0;
+       strip_trailing_nonprint(buf);
+       if (debug) fprintf(stderr, "> %s\n", buf);
+}
+
+
+/*
+ * send line to server - implemented in terms of serv_write()
+ */
+void serv_puts(char *buf)
+{
+       if (debug) fprintf(stderr, "< %s\n", buf);
+       serv_write(buf, strlen(buf));
+       serv_write("\n", 1);
+}
+
+
+
+void cleanup(int exitcode) {
+       char buf[1024];
+
+       if (exitcode != 0) {
+               fprintf(stderr, "citmail: error #%d occurred while sending mail.\n", exitcode);
+               fprintf(stderr, "Please check your Citadel configuration.\n");
+       }
+       serv_puts("QUIT");
+       serv_gets(buf);
+       exit(exitcode);
+}
+
+
+
+int main(int argc, char **argv) {
+       char buf[1024];
+       char fromline[1024];
+       FILE *fp;
+       int i;
+       struct passwd *pw;
+       int from_header = 0;
+       int in_body = 0;
+       int relh=0;
+       int home=0;
+       char relhome[PATH_MAX]="";
+       char ctdldir[PATH_MAX]=CTDLDIR;
+       char *sp, *ep;
+       char hostname[256];
+       char **recipients = NULL;
+       int num_recipients = 0;
+       int to_or_cc = 0;
+       int read_recipients_from_headers = 0;
+       char *add_these_recipients = NULL;
+
+       for (i=1; i<argc; ++i) {
+               if (!strcmp(argv[i], "-d")) {
+                       debug = 1;
+               }
+               else if (!strcmp(argv[i], "-t")) {
+                       read_recipients_from_headers = 1;
+               }
+               else if (argv[i][0] != '-') {
+                       ++num_recipients;
+                       recipients = realloc(recipients, (num_recipients * sizeof (char *)));
+                       recipients[num_recipients - 1] = strdup(argv[i]);
+               }
+       }
+              
+       /* TODO: should we be able to calculate relative dirs? */
+       calc_dirs_n_files(relh, home, relhome, ctdldir, 0);
+
+       pw = getpwuid(getuid());
+
+       fp = tmpfile();
+       if (fp == NULL) return(errno);
+       serv_sock = uds_connectsock(file_lmtp_socket);  /* FIXME: if called as 'sendmail' connect to file_lmtp_unfiltered_socket */
+       serv_gets(buf);
+       if (buf[0] != '2') {
+               fprintf(stderr, "%s\n", &buf[4]);
+               if (debug) fprintf(stderr, "citmail: could not connect to LMTP socket.\n");
+               cleanup(1);
+       }
+
+       sp = strchr (buf, ' ');
+       if (sp == NULL) {
+               if (debug) fprintf(stderr, "citmail: ould not calculate hostname.\n");
+               cleanup(2);
+       }
+       sp ++;
+       ep = strchr (sp, ' ');
+       if (ep == NULL) cleanup(3);
+       *ep = '\0';
+       strncpy(hostname, sp, sizeof hostname);
+
+       snprintf(fromline, sizeof fromline, "From: %s@%s", pw->pw_name, hostname);
+       while (fgets(buf, 1024, stdin) != NULL) {
+               if ( ( (buf[0] == 13) || (buf[0] == 10)) && (in_body == 0) ) {
+                       in_body = 1;
+                       if (from_header == 0) {
+                               fprintf(fp, "%s%s", fromline, buf);
+                       }
+               }
+               if (in_body == 0 && !strncasecmp(buf, "From:", 5)) {
+                       strcpy(fromline, buf);
+                       from_header = 1;
+               }
+
+               if (read_recipients_from_headers) {
+                       add_these_recipients = NULL;
+                       if ((isspace(buf[0])) && (to_or_cc)) {
+                               add_these_recipients = buf;
+                       }
+                       else {
+                               if ((!strncasecmp(buf, "To:", 3)) || (!strncasecmp(buf, "Cc:", 3))) {
+                                       to_or_cc = 1;
+                               }
+                               else {
+                                       to_or_cc = 0;
+                               }
+                               if (to_or_cc) {
+                                       add_these_recipients = &buf[3];
+                               }
+                       }
+
+                       if (add_these_recipients) {
+                               int num_recp_on_this_line;
+                               char this_recp[256];
+
+                               num_recp_on_this_line = num_tokens(add_these_recipients, ',');
+                               for (i=0; i<num_recp_on_this_line; ++i) {
+                                       extract_token(this_recp, add_these_recipients,
+                                               i, ',', sizeof this_recp);
+                                       striplt(this_recp);
+                                       if (!IsEmptyStr(this_recp)) {
+                                               ++num_recipients;
+                                               recipients = realloc(recipients,
+                                                       (num_recipients * sizeof (char *)));
+                                               recipients[num_recipients - 1] = strdup(this_recp);
+                                       }
+                               }
+                       }
+               }
+
+               fprintf(fp, "%s", buf);
+       }
+       strip_trailing_nonprint(fromline);
+
+       sprintf(buf, "LHLO %s", hostname);
+       serv_puts(buf);
+       do {
+               serv_gets(buf);
+               strcat(buf, "    ");
+       } while (buf[3] == '-');
+       if (buf[0] != '2') cleanup(4);
+
+       snprintf(buf, sizeof buf, "MAIL %s", fromline);
+       serv_puts(buf);
+       serv_gets(buf);
+       if (buf[0] != '2') cleanup(5);
+
+       for (i=0; i<num_recipients; ++i) {
+               snprintf(buf, sizeof buf, "RCPT To: %s", recipients[i]);
+               serv_puts(buf);
+               serv_gets(buf);
+               free(recipients[i]);
+       }
+       free(recipients);
+
+       serv_puts("DATA");
+       serv_gets(buf);
+       if (buf[0]!='3') cleanup(6);
+
+       rewind(fp);
+       while (fgets(buf, sizeof buf, fp) != NULL) {
+               strip_trailing_nonprint(buf);
+               serv_puts(buf);
+       }
+       serv_puts(".");
+       serv_gets(buf);
+       if (buf[0] != '2') {
+               fprintf(stderr, "%s\n", &buf[4]);
+               cleanup(7);
+       }
+       else {
+               cleanup(0);
+       }
+
+       /* We won't actually reach this statement but the compiler will
+        * display a spurious warning about an invalid return type if
+        * we don't return an int.
+        */
+       return(0);
+}
diff --git a/citadel/utils/ctdlmigrate.c b/citadel/utils/ctdlmigrate.c
new file mode 100644 (file)
index 0000000..65f06c6
--- /dev/null
@@ -0,0 +1,314 @@
+/*
+ * $Id$
+ *
+ * Across-the-wire migration utility for Citadel
+ *
+ * Yes, we used goto, and gets(), and committed all sorts of other heinous sins here.
+ * The scope of this program isn't wide enough to make a difference.  If you don't like
+ * it you can rewrite it.
+ *
+ * Copyright (c) 2009 citadel.org
+ *
+ *  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 3 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; if not, write to the Free Software
+ *  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ *
+ * (Note: a useful future enhancement might be to support "-h" on both sides)
+ *
+ */
+
+#include <stdlib.h>
+#include <unistd.h>
+#include <stdio.h>
+#include <string.h>
+#include <ctype.h>
+#include <fcntl.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <sys/utsname.h>
+#include <sys/wait.h>
+#include <signal.h>
+#include <netdb.h>
+#include <errno.h>
+#include <limits.h>
+#include <pwd.h>
+#include <time.h>
+#include <libcitadel.h>
+#include "citadel.h"
+#include "axdefs.h"
+#include "sysdep.h"
+#include "config.h"
+#include "citadel_dirs.h"
+#if HAVE_BACKTRACE
+#include <execinfo.h>
+#endif
+
+
+
+/*
+ * Replacement for gets() that doesn't throw a compiler warning.
+ * We're only using it for some simple prompts, so we don't need
+ * to worry about attackers exploiting it.
+ */
+void getz(char *buf) {
+       char *ptr;
+
+       ptr = fgets(buf, 32767, stdin);
+       if (!ptr) {
+               buf[0] = 0;
+               return;
+       }
+
+       ptr = strchr(buf, '\n');
+       if (ptr) *ptr = 0;
+}
+
+
+
+
+
+int main(int argc, char *argv[])
+{
+       int relh=0;
+       int home=0;
+       char relhome[PATH_MAX]="";
+       char ctdldir[PATH_MAX]=CTDLDIR;
+       char yesno[5];
+       char sendcommand[PATH_MAX];
+       int cmdexit;
+       char cmd[PATH_MAX];
+       char buf[PATH_MAX];
+       char socket_path[PATH_MAX];
+       char remote_user[256];
+       char remote_host[256];
+       char remote_sendcommand[PATH_MAX];
+       FILE *sourcefp = NULL;
+       FILE *targetfp = NULL;
+       int linecount = 0;
+       char spinning[4] = "-\\|/" ;
+       int exitcode = 0;
+       
+       calc_dirs_n_files(relh, home, relhome, ctdldir, 0);
+       CtdlMakeTempFileName(socket_path, sizeof socket_path);
+
+       cmdexit = system("clear");
+       printf( "-------------------------------------------\n"
+               "Over-the-wire migration utility for Citadel\n"
+               "-------------------------------------------\n"
+               "\n"
+               "This utility is designed to migrate your Citadel installation\n"
+               "to a new host system via a network connection, without disturbing\n"
+               "the source system.  The target may be a different CPU architecture\n"
+               "and/or operating system.  The source system should be running\n"
+               "Citadel %d.%02d or newer, and the target system should be running\n"
+               "either the same version or a newer version.  You will also need\n"
+               "the 'rsync' utility, and OpenSSH v4 or newer.\n"
+               "\n"
+               "You must run this utility on the TARGET SYSTEM.  Any existing data\n"
+               "on this system will be ERASED.\n"
+               "\n"
+               "Do you wish to continue? "
+               ,
+               EXPORT_REV_MIN / 100,
+               EXPORT_REV_MIN % 100
+       );
+
+       if ((fgets(yesno, sizeof yesno, stdin) == NULL) || (tolower(yesno[0]) != 'y')) {
+               exit(0);
+       }
+
+       printf("\n\nGreat!  First we will check some things out here on our target\n"
+               "system to make sure it is ready to receive data.\n\n");
+
+       printf("Locating 'sendcommand' and checking connectivity to Citadel...\n");
+       snprintf(sendcommand, sizeof sendcommand, "%s/sendcommand", ctdl_utilbin_dir);
+       snprintf(cmd, sizeof cmd, "%s 'NOOP'", sendcommand);
+       cmdexit = system(cmd);
+       if (cmdexit != 0) {
+               printf("\nctdlmigrate was unable to attach to the Citadel server\n"
+                       "here on the target system.  Is Citadel running?\n\n");
+               exit(1);
+       }
+       printf("\nOK, this side is ready to go.  Now we must connect to the source system.\n\n");
+
+       printf("Enter the host name or IP address of the source system\n"
+               "(example: ctdl.foo.org)\n"
+               "--> ");
+       getz(remote_host);
+       printf("\nEnter the name of a user on %s who has full access to Citadel files\n"
+               "(usually root)\n--> ",
+               remote_host);
+       getz(remote_user);
+
+       printf("\nEstablishing an SSH connection to the source system...\n\n");
+       unlink(socket_path);
+       snprintf(cmd, sizeof cmd, "ssh -MNf -S %s %s@%s", socket_path, remote_user, remote_host);
+       cmdexit = system(cmd);
+       printf("\n");
+       if (cmdexit != 0) {
+               printf("This program was unable to establish an SSH session to the source system.\n\n");
+               exitcode = cmdexit;
+               goto THEEND;
+       }
+
+       printf("\nTesting a command over the connection...\n\n");
+       snprintf(cmd, sizeof cmd, "ssh -S %s %s@%s 'echo Remote commands are executing successfully.'",
+               socket_path, remote_user, remote_host);
+       cmdexit = system(cmd);
+       printf("\n");
+       if (cmdexit != 0) {
+               printf("Remote commands are not succeeding.\n\n");
+               exitcode = cmdexit;
+               goto THEEND;
+       }
+
+       printf("\nLocating the remote 'sendcommand' and Citadel installation...\n");
+       snprintf(remote_sendcommand, sizeof remote_sendcommand, "/usr/local/citadel/sendcommand");
+       snprintf(cmd, sizeof cmd, "ssh -S %s %s@%s %s NOOP",
+               socket_path, remote_user, remote_host, remote_sendcommand);
+       cmdexit = system(cmd);
+       if (cmdexit != 0) {
+               snprintf(remote_sendcommand, sizeof remote_sendcommand, "/usr/sbin/sendcommand");
+               snprintf(cmd, sizeof cmd, "ssh -S %s %s@%s %s NOOP",
+                       socket_path, remote_user, remote_host, remote_sendcommand);
+               cmdexit = system(cmd);
+       }
+       if (cmdexit != 0) {
+               printf("\nUnable to locate Citadel programs on the remote system.  Please enter\n"
+                       "the name of the directory on %s which contains the 'sendcommand' program.\n"
+                       "(example: /opt/foo/citadel)\n"
+                       "--> ", remote_host);
+               getz(buf);
+               snprintf(remote_sendcommand, sizeof remote_sendcommand, "%s/sendcommand", buf);
+               snprintf(cmd, sizeof cmd, "ssh -S %s %s@%s %s NOOP",
+                       socket_path, remote_user, remote_host, remote_sendcommand);
+               cmdexit = system(cmd);
+       }
+       printf("\n");
+       if (cmdexit != 0) {
+               printf("ctdlmigrate was unable to attach to the remote Citadel system.\n\n");
+               exitcode = cmdexit;
+               goto THEEND;
+       }
+
+       printf("ctdlmigrate will now begin a database migration...\n");
+
+       snprintf(cmd, sizeof cmd, "ssh -S %s %s@%s %s -w3600 MIGR export",
+               socket_path, remote_user, remote_host, remote_sendcommand);
+       sourcefp = popen(cmd, "r");
+       if (!sourcefp) {
+               printf("\n%s\n\n", strerror(errno));
+               exitcode = 2;
+               goto THEEND;
+       }
+
+       snprintf(cmd, sizeof cmd, "%s -w3600 MIGR import", sendcommand);
+       targetfp = popen(cmd, "w");
+       if (!targetfp) {
+               printf("\n%s\n\n", strerror(errno));
+               exitcode = 3;
+               goto THEEND;
+       }
+
+       while (fgets(buf, sizeof buf, sourcefp) != NULL) {
+               if (fwrite(buf, strlen(buf), 1, targetfp) < 1) {
+                       exitcode = 4;
+                       printf("%s\n", strerror(errno));
+                       goto FAIL;
+               }
+               ++linecount;
+               if ((linecount % 100) == 0) {
+                       printf("%c\r", spinning[((linecount / 100) % 4)]);
+                       fflush(stdout);
+               }
+       }
+
+FAIL:  if (sourcefp) pclose(sourcefp);
+       if (targetfp) pclose(targetfp);
+       if (exitcode != 0) goto THEEND;
+
+       /* We need to copy a bunch of other stuff, and will do so using rsync */
+
+       snprintf(cmd, sizeof cmd, "ssh -S %s %s@%s %s MIGR listdirs",
+               socket_path, remote_user, remote_host, remote_sendcommand);
+       sourcefp = popen(cmd, "r");
+       if (!sourcefp) {
+               printf("\n%s\n\n", strerror(errno));
+               exitcode = 2;
+               goto THEEND;
+       }
+       while ((fgets(buf, sizeof buf, sourcefp)) && (strcmp(buf, "000"))) {
+               buf[strlen(buf)-1] = 0;
+
+               if (!strncasecmp(buf, "bio|", 4)) {
+                       snprintf(cmd, sizeof cmd, "rsync -va --rsh='ssh -S %s' %s@%s:%s/ %s/",
+                               socket_path, remote_user, remote_host, &buf[4], ctdl_bio_dir);
+               }
+               else if (!strncasecmp(buf, "files|", 6)) {
+                       snprintf(cmd, sizeof cmd, "rsync -va --rsh='ssh -S %s' %s@%s:%s/ %s/",
+                               socket_path, remote_user, remote_host, &buf[6], ctdl_file_dir);
+               }
+               else if (!strncasecmp(buf, "userpics|", 9)) {
+                       snprintf(cmd, sizeof cmd, "rsync -va --rsh='ssh -S %s' %s@%s:%s/ %s/",
+                               socket_path, remote_user, remote_host, &buf[9], ctdl_usrpic_dir);
+               }
+               else if (!strncasecmp(buf, "messages|", 9)) {
+                       snprintf(cmd, sizeof cmd, "rsync -va --rsh='ssh -S %s' %s@%s:%s/ %s/",
+                               socket_path, remote_user, remote_host, &buf[9], ctdl_message_dir);
+               }
+               else if (!strncasecmp(buf, "netconfigs|", 11)) {
+                       snprintf(cmd, sizeof cmd, "rsync -va --rsh='ssh -S %s' %s@%s:%s/ %s/",
+                               socket_path, remote_user, remote_host, &buf[11], ctdl_netcfg_dir);
+               }
+               else if (!strncasecmp(buf, "keys|", 5)) {
+                       snprintf(cmd, sizeof cmd, "rsync -va --rsh='ssh -S %s' %s@%s:%s/ %s/",
+                               socket_path, remote_user, remote_host, &buf[5], ctdl_key_dir);
+               }
+               else if (!strncasecmp(buf, "images|", 7)) {
+                       snprintf(cmd, sizeof cmd, "rsync -va --rsh='ssh -S %s' %s@%s:%s/ %s/",
+                               socket_path, remote_user, remote_host, &buf[7], ctdl_image_dir);
+               }
+               else if (!strncasecmp(buf, "info|", 5)) {
+                       snprintf(cmd, sizeof cmd, "rsync -va --rsh='ssh -S %s' %s@%s:%s/ %s/",
+                               socket_path, remote_user, remote_host, &buf[5], ctdl_info_dir);
+               }
+               else {
+                       strcpy(cmd, "false");   /* cheap and sleazy way to throw an error */
+               }
+               printf("%s\n", cmd);
+               cmdexit = system(cmd);
+               if (cmdexit != 0) {
+                       exitcode += cmdexit;
+               }
+       }
+       pclose(sourcefp);
+
+THEEND:        if (exitcode == 0) {
+               printf("\n\n *** Citadel migration was successful! *** \n\n");
+       }
+       else {
+               printf("\n\n *** Citadel migration was unsuccessful. *** \n\n");
+       }
+       printf("\nShutting down the socket connection...\n\n");
+       snprintf(cmd, sizeof cmd, "ssh -S %s -N -O exit %s@%s",
+               socket_path, remote_user, remote_host);
+       cmdexit = system(cmd);
+       printf("\n");
+       if (cmdexit != 0) {
+               printf("There was an error shutting down the socket.\n\n");
+               exitcode = cmdexit;
+       }
+
+       unlink(socket_path);
+       exit(exitcode);
+}
diff --git a/citadel/utils/getmail.c b/citadel/utils/getmail.c
new file mode 100644 (file)
index 0000000..0138b01
--- /dev/null
@@ -0,0 +1,422 @@
+/*
+ * $Id$
+ *
+ * Command-line utility to transmit a server command.
+ *
+ * Copyright (c) 1987-2009 by the citadel.org team
+ *
+ *  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 3 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; if not, write to the Free Software
+ *  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ */
+
+#include <stdlib.h>
+#include <unistd.h>
+#include <sys/types.h>
+#include <sys/wait.h>
+#include <string.h>
+#include <fcntl.h>
+#include <stdio.h>
+#include <ctype.h>
+
+#if TIME_WITH_SYS_TIME
+# include <sys/time.h>
+# include <time.h>
+#else
+# if HAVE_SYS_TIME_H
+#  include <sys/time.h>
+# else
+#  include <time.h>
+# endif
+#endif
+
+#include <signal.h>
+#include <errno.h>
+#include <limits.h>
+#include <libcitadel.h>
+#include "citadel.h"
+#include "citadel_ipc.h"
+#include "server.h"
+#include "config.h"
+
+#define LOCKFILE "/tmp/LCK.sendcommand"
+
+static CtdlIPC *ipc = NULL;
+
+/*
+ * make sure only one copy of sendcommand runs at a time, using lock files
+ */
+int set_lockfile(void)
+{
+       FILE *lfp;
+       int onppid;
+       int rv;
+
+       if ((lfp = fopen(LOCKFILE, "r")) != NULL) {
+               rv = fscanf(lfp, "%d", &onppid);
+               fclose(lfp);
+               if (!kill(onppid, 0) || errno == EPERM)
+                       return 1;
+       }
+       lfp = fopen(LOCKFILE, "w");
+       fprintf(lfp, "%ld\n", (long) getpid());
+       fclose(lfp);
+       return (0);
+}
+
+void remove_lockfile(void)
+{
+       unlink(LOCKFILE);
+}
+
+/*
+ * Why both cleanup() and nq_cleanup() ?  Notice the alarm() call in
+ * cleanup() .  If for some reason sendcommand hangs waiting for the server
+ * to clean up, the alarm clock goes off and the program exits anyway.
+ * The cleanup() routine makes a check to ensure it's not reentering, in
+ * case the ipc module looped it somehow.
+ */
+void nq_cleanup(int e)
+{
+       remove_lockfile();
+       exit(e);
+}
+
+/*
+ * send binary to server
+ */
+void serv_write(CtdlIPC *ipc, const char *buf, unsigned int nbytes)
+{
+       unsigned int bytes_written = 0;
+       int retval;
+/*
+#if defined(HAVE_OPENSSL)
+       if (ipc->ssl) {
+               serv_write_ssl(ipc, buf, nbytes);
+               return;
+       }
+#endif
+*/
+       while (bytes_written < nbytes) {
+               retval = write(ipc->sock, &buf[bytes_written],
+                              nbytes - bytes_written);
+               if (retval < 1) {
+                       connection_died(ipc, 0);
+                       return;
+               }
+               bytes_written += retval;
+       }
+}
+
+
+void cleanup(int e)
+{
+       static int nested = 0;
+
+       alarm(30);
+       signal(SIGALRM, nq_cleanup);
+       serv_write(ipc, "\n", 1);
+       if (nested++ < 1)
+               CtdlIPCQuit(ipc);
+       nq_cleanup(e);
+}
+
+/*
+ * This is implemented as a function rather than as a macro because the
+ * client-side IPC modules expect logoff() to be defined.  They call logoff()
+ * when a problem connecting or staying connected to the server occurs.
+ */
+void logoff(int e)
+{
+       cleanup(e);
+}
+
+static char *args[] =
+{"getmail", NULL};
+
+/*
+ * Connect sendcommand to the Citadel server running on this computer.
+ */
+void np_attach_to_server(char *host, char *port)
+{
+       char buf[SIZ];
+       char hostbuf[256] = "";
+       char portbuf[256] = "";
+       int r;
+
+       fprintf(stderr, "Attaching to server...\n");
+       strncpy(hostbuf, host, 256);
+       strncpy(portbuf, port, 256);
+       ipc = CtdlIPC_new(1, args, hostbuf, portbuf);
+       if (!ipc) {
+               fprintf(stderr, "Can't connect: %s\n", strerror(errno));
+               exit(3);
+       }
+       CtdlIPC_chat_recv(ipc, buf);
+       fprintf(stderr, "%s\n", &buf[4]);
+       snprintf(buf, sizeof buf, "IPGM %d", config.c_ipgm_secret);
+       r = CtdlIPCInternalProgram(ipc, config.c_ipgm_secret, buf);
+       fprintf(stderr, "%s\n", buf);
+       if (r / 100 != 2) {
+               cleanup(2);
+       }
+}
+
+
+void sendcommand_die(void) {
+       exit(0);
+}
+
+
+/*
+ * saves filelen bytes from file at pathname
+ */
+int save_buffer(void *file, size_t filelen, const char *pathname)
+{
+       size_t block = 0;
+       size_t bytes_written = 0;
+       FILE *fp;
+
+       fp = fopen(pathname, "w");
+       if (!fp) {
+               fprintf(stderr, "Cannot open '%s': %s\n", pathname, strerror(errno));
+               return 0;
+       }
+       do {
+               block = fwrite((char *)file + bytes_written, 1,
+                               filelen - bytes_written, fp);
+               bytes_written += block;
+       } while (errno == EINTR && bytes_written < filelen);
+       fclose(fp);
+
+       if (bytes_written < filelen) {
+               fprintf(stderr,"Trouble saving '%s': %s\n", pathname,
+                               strerror(errno));
+               return 0;
+       }
+       return 1;
+}
+
+
+/*
+ * main
+ */
+int main(int argc, char **argv)
+{
+       int a, r, i;
+       char cmd[5][SIZ];
+       char buf[SIZ];
+       int MessageToRetrieve;
+       int MessageFound = 0;
+       int relh=0;
+       int home=0;
+       int n=0;
+       char relhome[PATH_MAX]="";
+       char ctdldir[PATH_MAX]=CTDLDIR;
+       fd_set read_fd;
+       struct timeval tv;
+       int ret, err;
+       int server_shutting_down = 0;
+       struct ctdlipcroom *Room;
+       struct ctdlipcmessage *mret;
+       char cret[SIZ];
+       unsigned long *msgarr;
+       struct parts *att;
+
+       strcpy(ctdl_home_directory, DEFAULT_PORT);
+
+       /*
+        * Change directories if specified
+        */
+       for (a = 1; a < argc && n < 5; ++a) {
+               if (!strncmp(argv[a], "-h", 2)) {
+                       relh=argv[a][2]!='/';
+                       if (!relh) safestrncpy(ctdl_home_directory, &argv[a][2],
+                                                                  sizeof ctdl_home_directory);
+                       else
+                               safestrncpy(relhome, &argv[a][2],
+                                                       sizeof relhome);
+                       home=1;
+               } else {
+
+                       strcpy(cmd[n++], argv[a]);
+               }
+       }
+
+       calc_dirs_n_files(relh, home, relhome, ctdldir, 0);
+       get_config();
+
+       signal(SIGINT, cleanup);
+       signal(SIGQUIT, cleanup);
+       signal(SIGHUP, cleanup);
+       signal(SIGTERM, cleanup);
+
+       fprintf(stderr, "getmail: started (pid=%d) "
+                       "running in %s\n",
+                       (int) getpid(),
+                       ctdl_home_directory);
+       fflush(stderr);
+
+//     alarm(5);
+//     signal(SIGALRM, nq_cleanup); /* Set up a watchdog type timer in case we hang */
+       
+       np_attach_to_server(UDS, ctdl_run_dir);
+       fflush(stderr);
+       setIPCDeathHook(sendcommand_die);
+
+       fprintf(stderr, "GOTO %s\n", cmd[0]);
+       CtdlIPCGotoRoom(ipc, cmd[0], "", &Room, cret);
+       fprintf(stderr, "%s\n", cret);
+
+       MessageToRetrieve = atol(cmd[1]);
+
+       r = CtdlIPCGetMessages(ipc, 0, 0, NULL, &msgarr, buf);
+       printf("Messages: ");
+       for (i = 0; msgarr[i] > 0 ; i ++)
+       {
+//             printf(" %ld ", msgarr[i]);
+               if (msgarr[i] == MessageToRetrieve)
+                       MessageFound = 1;
+       }
+       if (!MessageFound)
+               printf("Message %d not found in the above list.", MessageToRetrieve);
+       printf("\n");
+
+       CtdlIPCGetSingleMessage(ipc,  MessageToRetrieve,0,4, &mret, cret);
+       fprintf(stderr, "%s\n", cret);
+       fprintf(stderr, "%s: %s\n", "path", mret->path);
+       fprintf(stderr, "%s: %s\n", "author", mret->author);
+       fprintf(stderr, "%s: %s\n", "subject", mret->subject);
+       fprintf(stderr, "%s: %s\n", "email", mret->email);
+       fprintf(stderr, "%s: %s\n", "text", mret->text);
+
+       att = mret->attachments;
+
+       while (att != NULL){
+               void *attachment;
+               char tmp[PATH_MAX];
+               char buf[SIZ];
+
+               fprintf(stderr, "Attachment: [%s] %s\n", att->number, att->filename);
+               r = CtdlIPCAttachmentDownload(ipc, MessageToRetrieve, att->number, &attachment, NULL, buf);
+               printf("----\%s\n----\n", buf);
+               if (r / 100 != 2) {
+                       printf("%s\n", buf);
+               } else {
+                       size_t len;
+                       
+                       len = (size_t)extract_long(buf, 0);
+                       CtdlMakeTempFileName(tmp, sizeof tmp);
+                       strcat(tmp, att->filename);
+                       printf("Saving Attachment to %s", tmp);
+                       save_buffer(attachment, len, tmp);
+                       free(attachment);
+               }
+               att = att->next;
+
+       }
+
+       ///if (
+
+
+       CtdlIPCQuit(ipc);
+       exit (1);
+
+
+
+
+
+
+       CtdlIPC_chat_send(ipc, cmd[4]);
+       CtdlIPC_chat_recv(ipc, buf);
+       fprintf(stderr, "%s\n", buf);
+
+       tv.tv_sec = 0;
+       tv.tv_usec = 1000;
+
+       if (!strncasecmp(&buf[1], "31", 2)) {
+               server_shutting_down = 1;
+       }
+
+       if (buf[0] == '1') {
+               while (CtdlIPC_chat_recv(ipc, buf), strcmp(buf, "000")) {
+                       printf("%s\n", buf);
+                       alarm(5); /* Kick the watchdog timer */
+               }
+       } else if (buf[0] == '4') {
+               do {
+                       if (fgets(buf, sizeof buf, stdin) == NULL)
+                               strcpy(buf, "000");
+                       if (!IsEmptyStr(buf))
+                               if (buf[strlen(buf) - 1] == '\n')
+                                       buf[strlen(buf) - 1] = 0;
+                       if (!IsEmptyStr(buf))
+                               if (buf[strlen(buf) - 1] == '\r')
+                                       buf[strlen(buf) - 1] = 0;
+                       if (strcmp(buf, "000"))
+                               CtdlIPC_chat_send(ipc, buf);
+                       
+                       FD_ZERO(&read_fd);
+                       FD_SET(ipc->sock, &read_fd);
+                       ret = select(ipc->sock+1, &read_fd, NULL, NULL,  &tv);
+                       err = errno;
+                       if (err!=0)
+                               printf("select failed: %d", err);
+
+                       if (ret == -1) {
+                               if (!(errno == EINTR || errno == EAGAIN))
+                                       printf("select failed: %d", err);
+                               return 1;
+                       }
+
+                       if (ret != 0) {
+                               size_t n;
+                               char rbuf[SIZ];
+
+                               rbuf[0] = '\0';
+                               n = read(ipc->sock, rbuf, SIZ);
+                               if (n>0) {
+                                       rbuf[n]='\0';
+                                       fprintf(stderr, "%s", rbuf);
+                                       fflush(stdout);
+                               }
+                       }
+                       alarm(5); /* Kick the watchdog timer */
+               } while (strcmp(buf, "000"));
+               CtdlIPC_chat_send(ipc, "\n");
+               CtdlIPC_chat_send(ipc, "000");
+       }
+       alarm(0);       /* Shutdown the watchdog timer */
+       fprintf(stderr, "sendcommand: processing ended.\n");
+
+       /* Clean up and log off ... unless the server indicated that the command
+        * we sent is shutting it down, in which case we want to just cut the
+        * connection and exit.
+        */
+       if (server_shutting_down) {
+               nq_cleanup(0);
+       }
+       else {
+               cleanup(0);
+       }
+       return 0;
+}
+
+
+/*
+ * Stub function
+ */
+void stty_ctdl(int cmd) {
+}
+
+
diff --git a/citadel/utils/msgform.c b/citadel/utils/msgform.c
new file mode 100644 (file)
index 0000000..c17a257
--- /dev/null
@@ -0,0 +1,211 @@
+/*
+ * $Id$
+ * 
+ * This is simply a filter that converts Citadel binary message format
+ * to readable, formatted output.
+ * 
+ * If the -q (quiet or qwk) flag is used, only the message text prints, and
+ * then it stops at the end of the first message it prints.
+ * 
+ * This utility isn't very useful anymore.
+ *
+ */
+
+#include "sysdep.h"
+#include <stdlib.h>
+#include <unistd.h>
+#include <fcntl.h>
+#include <stdio.h>
+#include <ctype.h>
+#include <string.h>
+
+#if TIME_WITH_SYS_TIME
+# include <sys/time.h>
+# include <time.h>
+#else
+# if HAVE_SYS_TIME_H
+#  include <sys/time.h>
+# else
+#  include <time.h>
+# endif
+#endif
+
+#include <errno.h>
+#include <libcitadel.h>
+
+int qwk = 0;
+
+int fpgetfield(FILE * fp, char *string);
+int fmout(int width, FILE * fp);
+
+
+#ifndef HAVE_STRERROR
+/*
+ * replacement strerror() for systems that don't have it
+ */
+char *strerror(int e)
+{
+       static char buf[32];
+
+       snprintf(buf, sizeof buf, "errno = %d", e);
+       return (buf);
+}
+#endif
+
+int main(int argc, char **argv)
+{
+       struct tm tm;
+       int a, b, e, mtype, aflag;
+       char bbb[1024];
+       char subject[1024];
+       FILE *fp;
+       time_t now;
+
+       if (argc == 2)
+               if (!strcmp(argv[1], "-q"))
+                       qwk = 1;
+       fp = stdin;
+       if (argc == 2)
+               if (strcmp(argv[1], "-q")) {
+                       fp = fopen(argv[1], "r");
+                       if (fp == NULL) {
+                               fprintf(stderr, "%s: cannot open %s: %s\n",
+                                       argv[0], argv[1], strerror(errno));
+                               exit(errno);
+                       }
+               }
+
+TOP:   do {
+               e = getc(fp);
+               if (e < 0)
+                       exit(0);
+       } while (e != 255);
+       strcpy(subject, "");
+       mtype = getc(fp);
+       aflag = getc(fp);
+       if (qwk == 0)
+               printf(" ");
+
+       do {
+               b = getc(fp);
+               if (b == 'M') {
+                       if (qwk == 0) {
+                               printf("\n");
+                               if (!IsEmptyStr(subject))
+                                       printf("Subject: %s\n", subject);
+                       }
+                       if (aflag != 1)
+                               fmout(80, fp);
+                       else
+                               while (a = getc(fp), a > 0) {
+                                       if (a == 13)
+                                               putc(10, stdout);
+                                       else
+                                               putc(a, stdout);
+                               }
+               }
+               if ((b != 'M') && (b > 0))
+                       fpgetfield(fp, bbb);
+               if (b == 'U')
+                       strcpy(subject, bbb);
+               if (qwk == 0) {
+                       if (b == 'A')
+                               printf("from %s ", bbb);
+                       if (b == 'N')
+                               printf("@%s ", bbb);
+                       if (b == 'O')
+                               printf("in %s> ", bbb);
+                       if (b == 'R')
+                               printf("to %s ", bbb);
+                       if (b == 'T') {
+                               now = atol(bbb);
+                               localtime_r(&now, &tm);
+                               strcpy(bbb, asctime(&tm));
+                               bbb[strlen(bbb) - 1] = 0;
+                               printf("%s ", &bbb[4]);
+                       }
+               }
+       } while ((b != 'M') && (b > 0));
+       if (qwk == 0)
+               printf("\n");
+       if (qwk == 1)
+               exit(0);
+       goto TOP;
+}
+
+int fpgetfield(FILE * fp, char *string)
+
+{                              /* level-2 break out next null-terminated string */
+       int a, b;
+       strcpy(string, "");
+       a = 0;
+       do {
+               b = getc(fp);
+               if (b < 1) {
+                       string[a] = 0;
+                       return (0);
+               }
+               string[a] = b;
+               ++a;
+       } while (b != 0);
+       return (0);
+}
+
+int fmout(int width, FILE * fp)
+{
+       int a, b, c;
+       int real = 0;
+       int old = 0;
+       char aaa[140];
+
+       strcpy(aaa, "");
+       old = 255;
+       c = 1;                  /* c is the current pos */
+FMTA:  old = real;
+       a = getc(fp);
+       real = a;
+       if (a <= 0)
+               goto FMTEND;
+
+       if (((a == 13) || (a == 10)) && (old != 13) && (old != 10))
+               a = 32;
+       if (((old == 13) || (old == 10)) && (isspace(real))) {
+               printf("\n");
+               c = 1;
+       }
+       if (a > 126)
+               goto FMTA;
+
+       if (a > 32) {
+               if (((strlen(aaa) + c) > (width - 5))
+                   && (strlen(aaa) > (width - 5))) {
+                       printf("\n%s", aaa);
+                       c = strlen(aaa);
+                       aaa[0] = 0;
+               }
+               b = strlen(aaa);
+               aaa[b] = a;
+               aaa[b + 1] = 0;
+       }
+       if (a == 32) {
+               if ((strlen(aaa) + c) > (width - 5)) {
+                       printf("\n");
+                       c = 1;
+               }
+               printf("%s ", aaa);
+               ++c;
+               c = c + strlen(aaa);
+               strcpy(aaa, "");
+               goto FMTA;
+       }
+       if ((a == 13) || (a == 10)) {
+               printf("%s\n", aaa);
+               c = 1;
+               strcpy(aaa, "");
+               goto FMTA;
+       }
+       goto FMTA;
+
+FMTEND:        printf("\n");
+       return (0);
+}
diff --git a/citadel/utils/sendcommand.c b/citadel/utils/sendcommand.c
new file mode 100644 (file)
index 0000000..5012877
--- /dev/null
@@ -0,0 +1,315 @@
+/*
+ * $Id$
+ *
+ * Command-line utility to transmit a server command.
+ *
+ */
+
+
+#include <stdlib.h>
+#include <unistd.h>
+#include <sys/types.h>
+#include <sys/wait.h>
+#include <string.h>
+#include <fcntl.h>
+#include <stdio.h>
+#include <ctype.h>
+
+#if TIME_WITH_SYS_TIME
+# include <sys/time.h>
+# include <time.h>
+#else
+# if HAVE_SYS_TIME_H
+#  include <sys/time.h>
+# else
+#  include <time.h>
+# endif
+#endif
+
+#include <signal.h>
+#include <errno.h>
+#include <limits.h>
+#include <libcitadel.h>
+#include "citadel.h"
+#include "citadel_ipc.h"
+#include "server.h"
+#include "config.h"
+
+#define LOCKFILE "/tmp/LCK.sendcommand"
+
+static CtdlIPC *ipc = NULL;
+
+/*
+ * make sure only one copy of sendcommand runs at a time, using lock files
+ */
+int set_lockfile(void)
+{
+       FILE *lfp;
+       int onppid;
+
+       if ((lfp = fopen(LOCKFILE, "r")) != NULL) {
+               fscanf(lfp, "%d", &onppid);
+               fclose(lfp);
+               if (!kill(onppid, 0) || errno == EPERM)
+                       return 1;
+       }
+       lfp = fopen(LOCKFILE, "w");
+       fprintf(lfp, "%ld\n", (long) getpid());
+       fclose(lfp);
+       return (0);
+}
+
+void remove_lockfile(void)
+{
+       unlink(LOCKFILE);
+}
+
+/*
+ * Why both cleanup() and nq_cleanup() ?  Notice the alarm() call in
+ * cleanup() .  If for some reason sendcommand hangs waiting for the server
+ * to clean up, the alarm clock goes off and the program exits anyway.
+ * The cleanup() routine makes a check to ensure it's not reentering, in
+ * case the ipc module looped it somehow.
+ */
+void nq_cleanup(int e)
+{
+       if (e == SIGALRM)
+               fprintf(stderr, "\nWatch dog time out.\n");
+       remove_lockfile();
+       exit(e);
+}
+
+/*
+ * send binary to server
+ */
+void serv_write(CtdlIPC *ipc, const char *buf, unsigned int nbytes)
+{
+       unsigned int bytes_written = 0;
+       int retval;
+/*
+#if defined(HAVE_OPENSSL)
+       if (ipc->ssl) {
+               serv_write_ssl(ipc, buf, nbytes);
+               return;
+       }
+#endif
+*/
+       while (bytes_written < nbytes) {
+               retval = write(ipc->sock, &buf[bytes_written],
+                              nbytes - bytes_written);
+               if (retval < 1) {
+                       connection_died(ipc, 0);
+                       return;
+               }
+               bytes_written += retval;
+       }
+}
+
+
+void cleanup(int e)
+{
+       static int nested = 0;
+
+       alarm(30);
+       signal(SIGALRM, nq_cleanup);
+       serv_write(ipc, "\n", 1);
+       if (nested++ < 1)
+               CtdlIPCQuit(ipc);
+       nq_cleanup(e);
+}
+
+/*
+ * This is implemented as a function rather than as a macro because the
+ * client-side IPC modules expect logoff() to be defined.  They call logoff()
+ * when a problem connecting or staying connected to the server occurs.
+ */
+void logoff(int e)
+{
+       cleanup(e);
+}
+
+/*
+ * Connect sendcommand to the Citadel server running on this computer.
+ */
+void np_attach_to_server(char *host, char *port)
+{
+       char buf[SIZ];
+       char hostbuf[256], portbuf[256];
+       char *args[] =
+       {"sendcommand", NULL};
+       int r;
+
+       fprintf(stderr, "Attaching to server...\n");
+       strcpy(hostbuf, host);
+       strcpy(portbuf, port);
+       ipc = CtdlIPC_new(1, args, hostbuf, portbuf);
+       if (!ipc) {
+               fprintf(stderr, "Can't connect: %s\n", strerror(errno));
+               exit(3);
+       }
+       CtdlIPC_chat_recv(ipc, buf);
+       fprintf(stderr, "%s\n", &buf[4]);
+       snprintf(buf, sizeof buf, "IPGM %d", config.c_ipgm_secret);
+       r = CtdlIPCInternalProgram(ipc, config.c_ipgm_secret, buf);
+       fprintf(stderr, "%s\n", buf);
+       if (r / 100 != 2) {
+               cleanup(2);
+       }
+}
+
+
+void sendcommand_die(void) {
+       exit(0);
+}
+
+
+
+/*
+ * main
+ */
+int main(int argc, char **argv)
+{
+       int a;
+       char cmd[SIZ];
+       char buf[SIZ];
+       int watchdog = 60;
+
+       int relh=0;
+       int home=0;
+       char relhome[PATH_MAX]="";
+       char ctdldir[PATH_MAX]=CTDLDIR;
+       fd_set read_fd;
+       struct timeval tv;
+       int ret, err;
+       int server_shutting_down = 0;
+       
+       strcpy(ctdl_home_directory, DEFAULT_PORT);
+
+       strcpy(cmd, "");
+       /*
+        * Change directories if specified
+        */
+       for (a = 1; a < argc; ++a) {
+               if (!strncmp(argv[a], "-h", 2)) {
+                       relh=argv[a][2]!='/';
+                       if (!relh) safestrncpy(ctdl_home_directory, &argv[a][2],
+                                                                  sizeof ctdl_home_directory);
+                       else
+                               safestrncpy(relhome, &argv[a][2],
+                                                       sizeof relhome);
+                       home=1;
+               } else if (!strncmp(argv[a], "-w", 2)) {
+                       watchdog = atoi(&argv[a][2]);
+                       if (watchdog<1)
+                               watchdog=1;
+               } else {
+                       if (!IsEmptyStr(cmd))
+                               strcat(cmd, " ");
+                       strcat(cmd, argv[a]);
+               }
+       }
+
+       calc_dirs_n_files(relh, home, relhome, ctdldir, 0);
+       get_config();
+
+       signal(SIGINT, cleanup);
+       signal(SIGQUIT, cleanup);
+       signal(SIGHUP, cleanup);
+       signal(SIGTERM, cleanup);
+
+       fprintf(stderr, "sendcommand: started (pid=%d) "
+                       "running in %s\n",
+                       (int) getpid(),
+                       ctdl_home_directory);
+       fflush(stderr);
+
+       alarm(watchdog);
+       signal(SIGALRM, nq_cleanup); /* Set up a watchdog type timer in case we hang */
+       
+       np_attach_to_server(UDS, ctdl_home_directory);
+       fflush(stderr);
+       setIPCDeathHook(sendcommand_die);
+
+       fprintf(stderr, "%s\n", cmd);
+       CtdlIPC_chat_send(ipc, cmd);
+       CtdlIPC_chat_recv(ipc, buf);
+       fprintf(stderr, "%s\n", buf);
+
+       tv.tv_sec = 0;
+       tv.tv_usec = 1000;
+
+       if (!strncasecmp(&buf[1], "31", 2)) {
+               server_shutting_down = 1;
+       }
+
+       if (buf[0] == '1') {
+               while (CtdlIPC_chat_recv(ipc, buf), strcmp(buf, "000")) {
+                       printf("%s\n", buf);
+                       alarm(watchdog); /* Kick the watchdog timer */
+               }
+       } else if (buf[0] == '4') {
+               do {
+                       if (fgets(buf, sizeof buf, stdin) == NULL)
+                               strcpy(buf, "000");
+                       if (!IsEmptyStr(buf))
+                               if (buf[strlen(buf) - 1] == '\n')
+                                       buf[strlen(buf) - 1] = 0;
+                       if (!IsEmptyStr(buf))
+                               if (buf[strlen(buf) - 1] == '\r')
+                                       buf[strlen(buf) - 1] = 0;
+                       if (strcmp(buf, "000"))
+                               CtdlIPC_chat_send(ipc, buf);
+                       
+                       FD_ZERO(&read_fd);
+                       FD_SET(ipc->sock, &read_fd);
+                       ret = select(ipc->sock+1, &read_fd, NULL, NULL,  &tv);
+                       err = errno;
+                       if (err!=0)
+                               printf("select failed: %d", err);
+
+                       if (ret == -1) {
+                               if (!(errno == EINTR || errno == EAGAIN))
+                                       printf("select failed: %d", err);
+                               return 1;
+                       }
+
+                       if (ret != 0) {
+                               size_t n;
+                               char rbuf[SIZ];
+
+                               rbuf[0] = '\0';
+                               n = read(ipc->sock, rbuf, SIZ);
+                               if (n>0) {
+                                       rbuf[n]='\0';
+                                       fprintf (stderr, rbuf);
+                                       fflush (stdout);
+                               }
+                       }
+                       alarm(watchdog); /* Kick the watchdog timer */
+               } while (strcmp(buf, "000"));
+               CtdlIPC_chat_send(ipc, "\n");
+               CtdlIPC_chat_send(ipc, "000");
+       }
+       alarm(0);       /* Shutdown the watchdog timer */
+       fprintf(stderr, "sendcommand: processing ended.\n");
+
+       /* Clean up and log off ... unless the server indicated that the command
+        * we sent is shutting it down, in which case we want to just cut the
+        * connection and exit.
+        */
+       if (server_shutting_down) {
+               nq_cleanup(0);
+       }
+       else {
+               cleanup(0);
+       }
+       return 0;
+}
+
+/*
+ * Stub function
+ */
+void stty_ctdl(int cmd) {
+}
+
+
diff --git a/citadel/utils/setup.c b/citadel/utils/setup.c
new file mode 100644 (file)
index 0000000..38c8be0
--- /dev/null
@@ -0,0 +1,1514 @@
+/*
+ * $Id$
+ *
+ * Citadel setup utility
+ *
+ */
+
+#include <stdlib.h>
+#include <unistd.h>
+#include <stdio.h>
+#include <string.h>
+#include <ctype.h>
+#include <fcntl.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <sys/utsname.h>
+#include <sys/wait.h>
+#include <signal.h>
+#include <netdb.h>
+#include <errno.h>
+#include <limits.h>
+#include <pwd.h>
+#include <time.h>
+#include <libcitadel.h>
+#include "citadel.h"
+#include "axdefs.h"
+#include "sysdep.h"
+#include "config.h"
+#include "citadel_dirs.h"
+#if HAVE_BACKTRACE
+#include <execinfo.h>
+#endif
+
+
+#define MAXSETUP 11    /* How many setup questions to ask */
+
+#define UI_TEXT                0       /* Default setup type -- text only */
+#define UI_DIALOG      2       /* Use the 'dialog' program */
+#define UI_SILENT      3       /* Silent running, for use in scripts */
+
+#define SERVICE_NAME   "citadel"
+#define PROTO_NAME     "tcp"
+#define NSSCONF                "/etc/nsswitch.conf"
+
+int setup_type;
+char setup_directory[PATH_MAX];
+int using_web_installer = 0;
+int enable_home = 1;
+char admin_pass[SIZ];
+char admin_cmd[SIZ];
+
+char *setup_titles[] =
+{
+       "Citadel Home Directory",
+       "System Administrator",
+       "Administrator Password",
+       "Citadel User ID",
+       "Server IP address",
+       "Server port number",
+       "Authentication mode",
+       "LDAP host",
+       "LDAP port number",
+       "LDAP base DN",
+       "LDAP bind DN",
+       "LDAP bind password"
+};
+
+/**
+ * \brief print the actual stack frame.
+ */
+void cit_backtrace(void)
+{
+#ifdef HAVE_BACKTRACE
+       void *stack_frames[50];
+       size_t size, i;
+       char **strings;
+
+
+       size = backtrace(stack_frames, sizeof(stack_frames) / sizeof(void*));
+       strings = backtrace_symbols(stack_frames, size);
+       for (i = 0; i < size; i++) {
+               if (strings != NULL)
+                       fprintf(stderr, "%s\n", strings[i]);
+               else
+                       fprintf(stderr, "%p\n", stack_frames[i]);
+       }
+       free(strings);
+#endif
+}
+
+struct config config;
+
+       /* calculate all our path on a central place */
+    /* where to keep our config */
+       
+
+char *setup_text[] = {
+#ifndef HAVE_RUN_DIR
+"Enter the full pathname of the directory in which the Citadel\n"
+"installation you are creating or updating resides.  If you\n"
+"specify a directory other than the default, you will need to\n"
+"specify the -h flag to the server when you start it up.\n",
+#else
+"Enter the subdirectory name for an alternate installation of "
+"Citadel. To do a default installation just leave it blank."
+"If you specify a directory other than the default, you will need to\n"
+"specify the -h flag to the server when you start it up.\n"
+"note that it may not have a leading /",
+#endif
+
+"Enter the name of the system administrator (which is probably\n"
+"you).  When an account is created with this name, it will\n"
+"automatically be given administrator-level access.\n",
+
+"Enter a password for the system administrator. When setup\n"
+"completes it will attempt to create the administrator user\n"
+"and set the password specified here.\n",
+
+"Citadel needs to run under its own user ID.  This would\n"
+"typically be called \"citadel\", but if you are running Citadel\n"
+"as a public BBS, you might also call it \"bbs\" or \"guest\".\n"
+"The server will run under this user ID.  Please specify that\n"
+"user ID here.  You may specify either a user name or a numeric\n"
+"UID.\n",
+
+"Specify the IP address on which your server will run.  If you\n"
+"leave this blank, or if you specify 0.0.0.0, Citadel will listen\n"
+"on all addresses.  You can usually skip this unless you are\n"
+"running multiple instances of Citadel on the same computer.\n",
+
+"Specify the TCP port number on which your server will run.\n"
+"Normally, this will be port 504, which is the official port\n"
+"assigned by the IANA for Citadel servers.  You will only need\n"
+"to specify a different port number if you run multiple instances\n"
+"of Citadel on the same computer and there is something else\n"
+"already using port 504.\n",
+
+
+
+"Specify which authentication mode you wish to use.\n"
+"\n"
+" 0. Self contained authentication\n"
+" 1. Host system integrated authentication\n"
+" 2. External LDAP - RFC 2307 compliant directory\n"
+" 3. External LDAP - nonstandard MS Active Directory\n"
+"\n"
+"For help: http://www.citadel.org/doku.php/faq:installation:authmodes\n"
+"\n"
+"ANSWER \"0\" UNLESS YOU COMPLETELY UNDERSTAND THIS OPTION.\n",
+
+"Please enter the host name or IP address of your LDAP server.\n",
+
+"Please enter the port number of the LDAP service (usually 389).\n",
+
+"Please enter the Base DN to search for authentication\n"
+"(for example: dc=example,dc=com)\n",
+
+"Please enter the DN of an account to use for binding to the LDAP server\n"
+"for performing queries.  The account does not require any other\n"
+"privileges.  If your LDAP server allows anonymous queries, you can.\n"
+"leave this blank.\n",
+
+"If you entered a Bind DN in the previous question, you must now enter\n"
+"the password associated with that account.  Otherwise, you can leave this\n"
+"blank.\n"
+
+};
+
+struct config config;
+int direction;
+
+
+void cleanup(int exitcode)
+{
+//     printf("Exitcode: %d\n", exitcode);
+//     cit_backtrace();
+       exit(exitcode);
+}
+
+
+
+void title(char *text)
+{
+       if (setup_type == UI_TEXT) {
+               printf("\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n<%s>\n", text);
+       }
+}
+
+
+
+int yesno(char *question, int default_value)
+{
+       int i = 0;
+       int answer = 0;
+       char buf[SIZ];
+
+       switch (setup_type) {
+
+       case UI_TEXT:
+               do {
+                       printf("%s\nYes/No [%s] --> ",
+                               question,
+                               ( default_value ? "Yes" : "No" )
+                       );
+                       if (fgets(buf, sizeof buf, stdin))
+                       {
+                               answer = tolower(buf[0]);
+                               if ((buf[0]==0) || (buf[0]==13) || (buf[0]==10))
+                                       answer = default_value;
+                               else if (answer == 'y')
+                                       answer = 1;
+                               else if (answer == 'n')
+                                       answer = 0;
+                       }
+               } while ((answer < 0) || (answer > 1));
+               break;
+
+       case UI_DIALOG:
+               sprintf(buf, "exec %s %s --yesno '%s' 15 75",
+                       getenv("CTDL_DIALOG"),
+                       ( default_value ? "" : "--defaultno" ),
+                       question);
+               i = system(buf);
+               if (i == 0) {
+                       answer = 1;
+               }
+               else {
+                       answer = 0;
+               }
+               break;
+       case UI_SILENT:
+               break;
+
+       }
+       return (answer);
+}
+
+
+void important_message(char *title, char *msgtext)
+{
+       char buf[SIZ];
+
+       switch (setup_type) {
+
+       case UI_TEXT:
+               printf("\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n");
+               printf("       %s \n\n%s\n\n", title, msgtext);
+               printf("Press return to continue...");
+               if (fgets(buf, sizeof buf, stdin));
+               break;
+
+       case UI_DIALOG:
+               sprintf(buf, "exec %s --msgbox '%s' 19 72",
+                       getenv("CTDL_DIALOG"),
+                       msgtext);
+               system(buf);
+               break;
+       case UI_SILENT:
+               fprintf(stderr, "%s\n", msgtext);
+               break;
+       }
+}
+
+void important_msgnum(int msgnum)
+{
+       important_message("Important Message", setup_text[msgnum]);
+}
+
+void display_error(char *error_message)
+{
+       important_message("Error", error_message);
+}
+
+void progress(char *text, long int curr, long int cmax)
+{
+       static long dots_printed = 0L;
+       long a = 0;
+       static FILE *fp = NULL;
+       char buf[SIZ];
+
+       switch (setup_type) {
+
+       case UI_TEXT:
+               if (curr == 0) {
+                       printf("%s\n", text);
+                       printf("..........................");
+                       printf("..........................");
+                       printf("..........................\r");
+                       fflush(stdout);
+                       dots_printed = 0;
+               } else if (curr == cmax) {
+                       printf("\r%79s\n", "");
+               } else {
+                       a = (curr * 100) / cmax;
+                       a = a * 78;
+                       a = a / 100;
+                       while (dots_printed < a) {
+                               printf("*");
+                               ++dots_printed;
+                               fflush(stdout);
+                       }
+               }
+               break;
+
+       case UI_DIALOG:
+               if (curr == 0) {
+                       sprintf(buf, "exec %s --gauge '%s' 7 72 0",
+                               getenv("CTDL_DIALOG"),
+                               text);
+                       fp = popen(buf, "w");
+                       if (fp != NULL) {
+                               fprintf(fp, "0\n");
+                               fflush(fp);
+                       }
+               } 
+               else if (curr == cmax) {
+                       if (fp != NULL) {
+                               fprintf(fp, "100\n");
+                               pclose(fp);
+                               fp = NULL;
+                       }
+               }
+               else {
+                       a = (curr * 100) / cmax;
+                       if (fp != NULL) {
+                               fprintf(fp, "%ld\n", a);
+                               fflush(fp);
+                       }
+               }
+               break;
+       case UI_SILENT:
+               break;
+
+       }
+}
+
+
+
+/*
+ * check_services_entry()  -- Make sure "citadel" is in /etc/services
+ *
+ */
+void check_services_entry(void)
+{
+       int i;
+       FILE *sfp;
+       char errmsg[256];
+
+       if (getservbyname(SERVICE_NAME, PROTO_NAME) == NULL) {
+               for (i=0; i<=2; ++i) {
+                       progress("Adding service entry...", i, 2);
+                       if (i == 0) {
+                               sfp = fopen("/etc/services", "a");
+                               if (sfp == NULL) {
+                                       sprintf(errmsg, "Cannot open /etc/services: %s", strerror(errno));
+                                       display_error(errmsg);
+                               } else {
+                                       fprintf(sfp, "%s                504/tcp\n", SERVICE_NAME);
+                                       fclose(sfp);
+                               }
+                       }
+               }
+       }
+}
+
+
+
+
+/*
+ * delete_inittab_entry()  -- Remove obsolete /etc/inittab entry for Citadel
+ *
+ */
+void delete_inittab_entry(void)
+{
+       FILE *infp;
+       FILE *outfp;
+       char looking_for[256];
+       char buf[1024];
+       char outfilename[32];
+       int changes_made = 0;
+
+       /* Determine the fully qualified path name of citserver */
+       snprintf(looking_for, 
+                sizeof looking_for,
+                "%s/citserver", 
+                ctdl_sbin_dir
+               );
+
+       /* Now tweak /etc/inittab */
+       infp = fopen("/etc/inittab", "r");
+       if (infp == NULL) {
+
+               /* If /etc/inittab does not exist, return quietly.
+                * Not all host platforms have it.
+                */
+               if (errno == ENOENT) {
+                       return;
+               }
+
+               /* Other errors might mean something really did go wrong.
+                */
+               sprintf(buf, "Cannot open /etc/inittab: %s", strerror(errno));
+               display_error(buf);
+               return;
+       }
+
+       strcpy(outfilename, "/tmp/ctdlsetup.XXXXXX");
+       outfp = fdopen(mkstemp(outfilename), "w+");
+       if (outfp == NULL) {
+               sprintf(buf, "Cannot open %s: %s", outfilename, strerror(errno));
+               display_error(buf);
+               fclose(infp);
+               return;
+       }
+
+       while (fgets(buf, sizeof buf, infp) != NULL) {
+               if (strstr(buf, looking_for) != NULL) {
+                       fwrite("#", 1, 1, outfp);
+                       ++changes_made;
+               }
+               fwrite(buf, strlen(buf), 1, outfp);
+       }
+
+       fclose(infp);
+       fclose(outfp);
+
+       if (changes_made) {
+               sprintf(buf, "/bin/mv -f %s /etc/inittab 2>/dev/null", outfilename);
+               system(buf);
+               system("/sbin/init q 2>/dev/null");
+       }
+       else {
+               unlink(outfilename);
+       }
+}
+
+
+/*
+ * install_init_scripts()  -- Try to configure to start Citadel at boot
+ *
+ */
+void install_init_scripts(void)
+{
+       struct stat etcinitd;
+       FILE *fp;
+       char *initfile = "/etc/init.d/citadel";
+       char command[SIZ];
+
+       if ((stat("/etc/init.d/", &etcinitd) == -1) && 
+           (errno == ENOENT))
+       {
+               if ((stat("/etc/rc.d/init.d/", &etcinitd) == -1) &&
+                   (errno == ENOENT))
+                       initfile = CTDLDIR"/citadel.init";
+               else
+                       initfile = "/etc/rc.d/init.d/citadel";
+       }
+
+       fp = fopen(initfile, "r");
+       if (fp != NULL) {
+               if (yesno("Citadel already appears to be configured to start at boot.\n"
+                         "Would you like to keep your boot configuration as is?\n", 1) == 1) {
+                       return;
+               }
+               fclose(fp);
+               
+       }
+
+       if (yesno("Would you like to automatically start Citadel at boot?\n", 1) == 0) {
+               return;
+       }
+
+       fp = fopen(initfile, "w");
+       if (fp == NULL) {
+               display_error("Cannot create /etc/init.d/citadel");
+               return;
+       }
+
+       fprintf(fp,     "#!/bin/sh\n"
+               "#\n"
+               "# Init file for Citadel\n"
+               "#\n"
+               "# chkconfig: - 79 30\n"
+               "# description: Citadel service\n"
+               "# processname: citserver\n"
+               "# pidfile: %s/citadel.pid\n\n"
+               "# uncomment this to create coredumps as described in\n"
+               "# http://www.citadel.org/doku.php/faq:mastering_your_os:gdb#how.do.i.make.my.system.produce.core-files\n"
+               "# ulimit -c unlimited\n"
+               "\n"
+               "CITADEL_DIR=%s\n"
+               ,
+               setup_directory,
+               setup_directory
+               );
+       fprintf(fp,     "\n"
+               "test -d /var/run || exit 0\n"
+               "\n"
+               "case \"$1\" in\n"
+               "\n"
+               "start)         echo -n \"Starting Citadel... \"\n"
+               "               if $CITADEL_DIR/citserver -lmail -d -h$CITADEL_DIR\n"
+               "               then\n"
+               "                       echo \"ok\"\n"
+               "               else\n"
+               "                       echo \"failed\"\n"
+               "               fi\n");
+       fprintf(fp,     "               ;;\n"
+               "stop)          echo -n \"Stopping Citadel... \"\n"
+               "               if $CITADEL_DIR/sendcommand DOWN >/dev/null 2>&1 ; then\n"
+               "                       echo \"ok\"\n"
+               "               else\n"
+               "                       echo \"failed\"\n"
+               "               fi\n"
+               "               rm -f %s/citadel.pid 2>/dev/null\n"
+               ,
+               setup_directory
+               );
+       fprintf(fp,     "               ;;\n"
+               "restart)       if $CITADEL_DIR/sendcommand DOWN 1 >/dev/null 2>&1 ; then\n"
+               "                       echo \"ok\"\n"
+               "               else\n"
+               "                       echo \"failed\"\n"
+               "               fi\n"
+               "               ;;\n"
+               "*)             echo \"Usage: $0 {start|stop|restart}\"\n"
+               "               exit 1\n"
+               "               ;;\n"
+               "esac\n"
+               );
+
+       fclose(fp);
+       chmod(initfile, 0755);
+
+       /* Set up the run levels. */
+       system("/bin/rm -f /etc/rc?.d/[SK]??citadel 2>/dev/null");
+       snprintf(command, sizeof(command), "for x in 2 3 4 5 ; do [ -d /etc/rc$x.d ] && ln -s %s /etc/rc$x.d/S79citadel ; done 2>/dev/null", initfile);
+       system(command);
+       snprintf(command, sizeof(command),"for x in 0 6 S; do [ -d /etc/rc$x.d ] && ln -s %s /etc/rc$x.d/K30citadel ; done 2>/dev/null", initfile);
+       system(command);
+
+}
+
+
+
+
+
+
+/*
+ * On systems which use xinetd, see if we can offer to install Citadel as
+ * the default telnet target.
+ */
+void check_xinetd_entry(void) {
+       char *filename = "/etc/xinetd.d/telnet";
+       FILE *fp;
+       char buf[SIZ];
+       int already_citadel = 0;
+
+       fp = fopen(filename, "r+");
+       if (fp == NULL) return;         /* Not there.  Oh well... */
+
+       while (fgets(buf, sizeof buf, fp) != NULL) {
+               if (strstr(buf, setup_directory) != NULL) already_citadel = 1;
+       }
+       fclose(fp);
+       if (already_citadel) return;    /* Already set up this way. */
+
+       /* Otherwise, prompt the user to create an entry. */
+       if (getenv("CREATE_XINETD_ENTRY") != NULL) {
+               if (strcasecmp(getenv("CREATE_XINETD_ENTRY"), "yes")) {
+                       return;
+               }
+       }
+       else {
+               snprintf(buf, sizeof buf,
+                        "Setup can configure the \"xinetd\" service to automatically\n"
+                        "connect incoming telnet sessions to Citadel, bypassing the\n"
+                        "host system login: prompt.  Would you like to do this?\n"
+                       );
+               if (yesno(buf, 1) == 0) {
+                       return;
+               }
+       }
+
+       fp = fopen(filename, "w");
+       fprintf(fp,
+               "# description: telnet service for Citadel users\n"
+               "service telnet\n"
+               "{\n"
+               "       disable = no\n"
+               "       flags           = REUSE\n"
+               "       socket_type     = stream\n"
+               "       wait            = no\n"
+               "       user            = root\n"
+               "       server          = /usr/sbin/in.telnetd\n"
+               "       server_args     = -h -L %s/citadel\n"
+               "       log_on_failure  += USERID\n"
+               "}\n",
+               ctdl_bin_dir);
+       fclose(fp);
+
+       /* Now try to restart the service */
+       system("/etc/init.d/xinetd restart >/dev/null 2>&1");
+}
+
+
+
+/*
+ * Offer to disable other MTA's
+ */
+void disable_other_mta(char *mta) {
+       char buf[SIZ];
+       FILE *fp;
+       int lines = 0;
+
+       sprintf(buf, "/bin/ls -l /etc/rc*.d/S*%s 2>/dev/null; "
+               "/bin/ls -l /etc/rc.d/rc*.d/S*%s 2>/dev/null",
+               mta, mta);
+       fp = popen(buf, "r");
+       if (fp == NULL) return;
+
+       while (fgets(buf, sizeof buf, fp) != NULL) {
+               ++lines;
+       }
+       fclose(fp);
+       if (lines == 0) return;         /* Nothing to do. */
+
+
+       /* Offer to replace other MTA with the vastly superior Citadel :)  */
+
+       snprintf(buf, sizeof buf,
+                "You appear to have the \"%s\" email program\n"
+                "running on your system.  If you want Citadel mail\n"
+                "connected with %s, you will have to manually integrate\n"
+                "them.  It is preferable to disable %s, and use Citadel's\n"
+                "SMTP, POP3, and IMAP services.\n\n"
+                "May we disable %s so that Citadel has access to ports\n"
+                "25, 110, and 143?\n",
+                mta, mta, mta, mta
+               );
+       if (yesno(buf, 1) == 0) {
+               return;
+       }
+       
+
+       sprintf(buf, "for x in /etc/rc*.d/S*%s; do mv $x `echo $x |sed s/S/K/g`; done >/dev/null 2>&1", mta);
+       system(buf);
+       sprintf(buf, "/etc/init.d/%s stop >/dev/null 2>&1", mta);
+       system(buf);
+}
+
+
+
+
+/* 
+ * Check to see if our server really works.  Returns 0 on success.
+ */
+int test_server(char *setup_directory, char *relhomestr, int relhome) {
+       char cmd[256];
+       char cookie[256];
+       FILE *fp;
+       char buf[4096];
+       int found_it = 0;
+
+       /* Generate a silly little cookie.  We're going to write it out
+        * to the server and try to get it back.  The cookie does not
+        * have to be secret ... just unique.
+        */
+       sprintf(cookie, "--test--%d--", getpid());
+
+       if (relhome)
+               sprintf(cmd, "%s/sendcommand -h%s ECHO %s 2>&1",
+                       ctdl_sbin_dir,
+                       relhomestr,
+                       cookie);
+       else
+               sprintf(cmd, "%s/sendcommand ECHO %s 2>&1",
+                       ctdl_sbin_dir,
+                       cookie);
+
+       fp = popen(cmd, "r");
+       if (fp == NULL) return(errno);
+
+       while (fgets(buf, sizeof buf, fp) != NULL) {
+               if ( (buf[0]=='2')
+                    && (strstr(buf, cookie) != NULL) ) {
+                       ++found_it;
+               }
+       }
+       pclose(fp);
+
+       if (found_it) {
+               return(0);
+       }
+       return(-1);
+}
+
+void strprompt(char *prompt_title, char *prompt_text, char *str)
+{
+       char buf[SIZ] = "";
+       char setupmsg[SIZ];
+       char dialog_result[PATH_MAX];
+       FILE *fp = NULL;
+
+       strcpy(setupmsg, "");
+
+       switch (setup_type) {
+       case UI_TEXT:
+               title(prompt_title);
+               printf("\n%s\n", prompt_text);
+               printf("This is currently set to:\n%s\n", str);
+               printf("Enter new value or press return to leave unchanged:\n");
+               if (fgets(buf, sizeof buf, stdin)){
+                       buf[strlen(buf) - 1] = 0;
+               }
+               if (!IsEmptyStr(buf))
+                       strcpy(str, buf);
+               break;
+
+       case UI_DIALOG:
+               CtdlMakeTempFileName(dialog_result, sizeof dialog_result);
+               sprintf(buf, "exec %s --inputbox '%s' 19 72 '%s' 2>%s",
+                       getenv("CTDL_DIALOG"),
+                       prompt_text,
+                       str,
+                       dialog_result);
+               system(buf);
+               fp = fopen(dialog_result, "r");
+               if (fp != NULL) {
+                       if (fgets(str, sizeof buf, fp)) {
+                               if (str[strlen(str)-1] == 10) {
+                                       str[strlen(str)-1] = 0;
+                               }
+                       }
+                       fclose(fp);
+                       unlink(dialog_result);
+               }
+               break;
+       case UI_SILENT:
+               break;
+       }
+}
+
+void set_bool_val(int msgpos, int *ip) {
+       title(setup_titles[msgpos]);
+       *ip = yesno(setup_text[msgpos], *ip);
+}
+
+void set_str_val(int msgpos, char *str) {
+       strprompt(setup_titles[msgpos], setup_text[msgpos], str);
+}
+
+void set_int_val(int msgpos, int *ip)
+{
+       char buf[16];
+       snprintf(buf, sizeof buf, "%d", (int) *ip);
+       set_str_val(msgpos, buf);
+       *ip = atoi(buf);
+}
+
+
+void set_char_val(int msgpos, char *ip)
+{
+       char buf[16];
+       snprintf(buf, sizeof buf, "%d", (int) *ip);
+       set_str_val(msgpos, buf);
+       *ip = (char) atoi(buf);
+}
+
+
+void set_long_val(int msgpos, long int *ip)
+{
+       char buf[16];
+       snprintf(buf, sizeof buf, "%ld", *ip);
+       set_str_val(msgpos, buf);
+       *ip = atol(buf);
+}
+
+
+void edit_value(int curr)
+{
+       int i;
+       struct passwd *pw;
+       char ctdluidname[256];
+
+       switch (curr) {
+
+       case 1:
+               if (setup_type == UI_SILENT)
+               {
+                       if (getenv("SYSADMIN_NAME")) {
+                               strcpy(config.c_sysadm, getenv("SYSADMIN_NAME"));
+                       }
+               }
+               else {
+                       set_str_val(curr, config.c_sysadm);
+               }
+               break;
+
+       case 2:
+               if (setup_type == UI_SILENT)
+               {
+                       if (getenv("SYSADMIN_PW")) {
+                               strcpy(admin_pass, getenv("SYSADMIN_PW"));
+                       }
+               }
+               else {
+                       set_str_val(curr, admin_pass);
+               }
+               break;
+       
+       case 3:
+               if (setup_type == UI_SILENT)
+               {               
+                       if (getenv("CITADEL_UID")) {
+                               config.c_ctdluid = atoi(getenv("CITADEL_UID"));
+                       }                                       
+               }
+               else
+               {
+#ifdef __CYGWIN__
+                       config.c_ctdluid = 0;   /* XXX Windows hack, prob. insecure */
+#else
+                       i = config.c_ctdluid;
+                       pw = getpwuid(i);
+                       if (pw == NULL) {
+                               set_int_val(curr, &i);
+                               config.c_ctdluid = i;
+                       }
+                       else {
+                               strcpy(ctdluidname, pw->pw_name);
+                               set_str_val(curr, ctdluidname);
+                               pw = getpwnam(ctdluidname);
+                               if (pw != NULL) {
+                                       config.c_ctdluid = pw->pw_uid;
+                               }
+                               else if (atoi(ctdluidname) > 0) {
+                                       config.c_ctdluid = atoi(ctdluidname);
+                               }
+                       }
+#endif
+               }
+               break;
+
+       case 4:
+               if (setup_type == UI_SILENT)
+               {
+                       if (getenv("IP_ADDR")) {
+                               strcpy(config.c_ip_addr, getenv("IP_ADDR"));
+                       }
+               }
+               else {
+                       set_str_val(curr, config.c_ip_addr);
+               }
+               break;
+
+       case 5:
+               if (setup_type == UI_SILENT)
+               {
+                       if (getenv("CITADEL_PORT")) {
+                               config.c_port_number = atoi(getenv("CITADEL_PORT"));
+                       }
+               }
+               else
+               {
+                       set_int_val(curr, &config.c_port_number);
+               }
+               break;
+
+       case 6:
+               if (setup_type == UI_SILENT)
+               {
+                       const char *auth;
+                       config.c_auth_mode = AUTHMODE_NATIVE;
+                       auth = getenv("ENABLE_UNIX_AUTH");
+                       if (auth != NULL)
+                       {
+                               if ((strcasecmp(auth, "yes") == 0) ||
+                                   (strcasecmp(auth, "host") == 0))
+                               {
+                                       config.c_auth_mode = AUTHMODE_HOST;
+                               }
+                               else if (strcasecmp(auth, "ldap") == 0){
+                                       config.c_auth_mode = AUTHMODE_LDAP;
+                               }
+                               else if ((strcasecmp(auth, "ldap_ad") == 0) ||
+                                        (strcasecmp(auth, "active directory") == 0)){
+                                       config.c_auth_mode = AUTHMODE_LDAP_AD;
+                               }
+                       }
+               }
+               else {
+                       set_int_val(curr, &config.c_auth_mode);
+               }
+               break;
+
+       case 7:
+               if (setup_type == UI_SILENT)
+               {
+                       if (getenv("LDAP_HOST")) {
+                               strcpy(config.c_ldap_host, getenv("LDAP_HOST"));
+                       }
+               }
+               else
+               {
+                       set_str_val(curr, config.c_ldap_host);
+               }
+               break;
+
+       case 8:
+               if (setup_type == UI_SILENT)
+               {
+                       if (getenv("LDAP_PORT")) {
+                               config.c_ldap_port = atoi(getenv("LDAP_PORT"));
+                       }
+               }
+               else
+               {
+                       set_int_val(curr, &config.c_ldap_port);
+               }
+               break;
+
+       case 9:
+               if (setup_type == UI_SILENT)
+               {
+                       if (getenv("LDAP_BASE_DN")) {
+                               strcpy(config.c_ldap_base_dn, getenv("LDAP_BASE_DN"));
+                       }
+               }
+               else
+               {
+                       set_str_val(curr, config.c_ldap_base_dn);
+               }
+               break;
+
+       case 10:
+               if (setup_type == UI_SILENT)
+               {
+                       if (getenv("LDAP_BIND_DN")) {
+                               strcpy(config.c_ldap_bind_dn, getenv("LDAP_BIND_DN"));
+                       }
+               }
+               else
+               {
+                       set_str_val(curr, config.c_ldap_bind_dn);
+               }
+               break;
+
+       case 11:
+               if (setup_type == UI_SILENT)
+               {
+                       if (getenv("LDAP_BIND_PW")) {
+                               strcpy(config.c_ldap_bind_pw, getenv("LDAP_BIND_PW"));
+                       }
+               }
+               else
+               {
+                       set_str_val(curr, config.c_ldap_bind_pw);
+               }
+               break;
+
+       }
+
+}
+
+/*
+ * (re-)write the config data to disk
+ */
+void write_config_to_disk(void)
+{
+       FILE *fp;
+       int fd;
+
+       if ((fd = creat(file_citadel_config, S_IRUSR | S_IWUSR)) == -1) {
+               display_error("setup: cannot open citadel.config");
+               cleanup(1);
+       }
+       fp = fdopen(fd, "wb");
+       if (fp == NULL) {
+               display_error("setup: cannot open citadel.config");
+               cleanup(1);
+       }
+       fwrite((char *) &config, sizeof(struct config), 1, fp);
+       fclose(fp);
+}
+
+
+
+
+/*
+ * Figure out what type of user interface we're going to use
+ */
+int discover_ui(void)
+{
+
+       /* Use "dialog" if we have it */
+       if (getenv("CTDL_DIALOG") != NULL) {
+               return UI_DIALOG;
+       }
+               
+       return UI_TEXT;
+}
+
+
+
+
+
+/*
+ * Strip "db" entries out of /etc/nsswitch.conf
+ */
+void fixnss(void) {
+       FILE *fp_read;
+       int fd_write;
+       char buf[256];
+       char buf_nc[256];
+       char question[512];
+       int i;
+       int changed = 0;
+       int file_changed = 0;
+       char new_filename[64];
+
+       fp_read = fopen(NSSCONF, "r");
+       if (fp_read == NULL) {
+               return;
+       }
+
+       strcpy(new_filename, "/tmp/ctdl_fixnss_XXXXXX");
+       fd_write = mkstemp(new_filename);
+       if (fd_write < 0) {
+               fclose(fp_read);
+               return;
+       }
+
+       while (fgets(buf, sizeof buf, fp_read) != NULL) {
+               changed = 0;
+               strcpy(buf_nc, buf);
+               for (i=0; i<strlen(buf_nc); ++i) {
+                       if (buf_nc[i] == '#') {
+                               buf_nc[i] = 0;
+                       }
+               }
+               for (i=0; i<strlen(buf_nc); ++i) {
+                       if (!strncasecmp(&buf_nc[i], "db", 2)) {
+                               if (i > 0) {
+                                       if ((isspace(buf_nc[i+2])) || (buf_nc[i+2]==0)) {
+                                               changed = 1;
+                                               file_changed = 1;
+                                               strcpy(&buf_nc[i], &buf_nc[i+2]);
+                                               strcpy(&buf[i], &buf[i+2]);
+                                               if (buf[i]==32) {
+                                                       strcpy(&buf_nc[i], &buf_nc[i+1]);
+                                                       strcpy(&buf[i], &buf[i+1]);
+                                               }
+                                       }
+                               }
+                       }
+               }
+               if (write(fd_write, buf, strlen(buf)) != strlen(buf)) {
+                       fclose(fp_read);
+                       close(fd_write);
+                       unlink(new_filename);
+                       return;
+               }
+       }
+
+       fclose(fp_read);
+       
+       if (!file_changed) {
+               unlink(new_filename);
+               return;
+       }
+
+       snprintf(question, sizeof question,
+               "\n"
+               "/etc/nsswitch.conf is configured to use the 'db' module for\n"
+               "one or more services.  This is not necessary on most systems,\n"
+               "and it is known to crash the Citadel server when delivering\n"
+               "mail to the Internet.\n"
+               "\n"
+               "Do you want this module to be automatically disabled?\n"
+               "\n"
+       );
+
+       if (yesno(question, 1)) {
+               sprintf(buf, "/bin/mv -f %s %s", new_filename, NSSCONF);
+               system(buf);
+               chmod(NSSCONF, 0644);
+       }
+       unlink(new_filename);
+}
+
+
+
+
+
+
+
+
+int main(int argc, char *argv[])
+{
+       int a;
+       int curr; 
+       char aaa[128];
+       FILE *fp;
+       int old_setup_level = 0;
+       int info_only = 0;
+       struct utsname my_utsname;
+       struct passwd *pw;
+       struct hostent *he;
+       gid_t gid;
+       int relh=0;
+       int home=0;
+       char relhome[PATH_MAX]="";
+       char ctdldir[PATH_MAX]=CTDLDIR;
+       int rv;
+       
+       /* set an invalid setup type */
+       setup_type = (-1);
+
+       /* Check to see if we're running the web installer */
+       if (getenv("CITADEL_INSTALLER") != NULL) {
+               using_web_installer = 1;
+       }
+
+       /* parse command line args */
+       for (a = 0; a < argc; ++a) {
+               if (!strncmp(argv[a], "-u", 2)) {
+                       strcpy(aaa, argv[a]);
+                       strcpy(aaa, &aaa[2]);
+                       setup_type = atoi(aaa);
+               }
+               else if (!strcmp(argv[a], "-i")) {
+                       info_only = 1;
+               }
+               else if (!strcmp(argv[a], "-q")) {
+                       setup_type = UI_SILENT;
+               }
+               else if (!strncmp(argv[a], "-h", 2)) {
+                       relh=argv[a][2]!='/';
+                       if (!relh) {
+                               safestrncpy(ctdl_home_directory, &argv[a][2], sizeof ctdl_home_directory);
+                       } else {
+                               safestrncpy(relhome, &argv[a][2], sizeof relhome);
+                       }
+                       home = 1;
+               }
+
+       }
+
+       calc_dirs_n_files(relh, home, relhome, ctdldir, 0);
+
+       /* If a setup type was not specified, try to determine automatically
+        * the best one to use out of all available types.
+        */
+       if (setup_type < 0) {
+               setup_type = discover_ui();
+       }
+       if (info_only == 1) {
+               important_message("Citadel Setup", CITADEL);
+               cleanup(0);
+       }
+
+       /* Get started in a valid setup directory. */
+       strcpy(setup_directory, ctdl_run_dir);
+       if ( (using_web_installer) && (getenv("CITADEL") != NULL) ) {
+               strcpy(setup_directory, getenv("CITADEL"));
+       }
+       else {
+               set_str_val(0, setup_directory);
+       }
+
+       enable_home = ( relh | home );
+
+       if (chdir(setup_directory) != 0) {
+               char errmsg[SIZ];
+               sprintf(errmsg, "The directory you specified does not exist: [%s]\n", setup_directory);
+               
+               important_message("Citadel Setup", errmsg);
+               cleanup(errno);
+       }
+
+       /* Determine our host name, in case we need to use it as a default */
+       uname(&my_utsname);
+
+       /* Try to stop Citadel if we can */
+       if (!access("/etc/init.d/citadel", X_OK)) {
+               rv = system("/etc/init.d/citadel stop");
+       }
+
+       /* Make sure Citadel is not running. */
+       if (test_server(setup_directory, relhome, enable_home) == 0) {
+               important_message("Citadel Setup",
+                       "The Citadel service is still running.\n"
+                       "Please stop the service manually and run "
+                       "setup again.");
+               cleanup(1);
+       }
+
+       /* Now begin. */
+       switch (setup_type) {
+
+       case UI_TEXT:
+               printf("\n\n\n"
+                       "              *** Citadel setup program ***\n\n");
+               break;
+
+       }
+
+       /*
+        * What we're going to try to do here is append a whole bunch of
+        * nulls to the citadel.config file, so we can keep the old config
+        * values if they exist, but if the file is missing or from an
+        * earlier version with a shorter config structure, when setup tries
+        * to read the old config parameters, they'll all come up zero.
+        * The length of the config file will be set to what it's supposed
+        * to be when we rewrite it, because we replace the old file with a
+        * completely new copy.
+        */
+       if ((a = open(file_citadel_config, O_WRONLY | O_CREAT | O_APPEND,
+                     S_IRUSR | S_IWUSR)) == -1) {
+               display_error("setup: cannot append citadel.config");
+               cleanup(errno);
+       }
+       fp = fdopen(a, "ab");
+       if (fp == NULL) {
+               display_error("setup: cannot append citadel.config");
+               cleanup(errno);
+       }
+       for (a = 0; a < sizeof(struct config); ++a) {
+               putc(0, fp);
+       }
+       fclose(fp);
+
+       /* now we re-open it, and read the old or blank configuration */
+       fp = fopen(file_citadel_config, "rb");
+       if (fp == NULL) {
+               display_error("setup: cannot open citadel.config");
+               cleanup(errno);
+       }
+       rv = fread((char *) &config, sizeof(struct config), 1, fp);
+       fclose(fp);
+
+       /* set some sample/default values in place of blanks... */
+       if (IsEmptyStr(config.c_nodename))
+               safestrncpy(config.c_nodename, my_utsname.nodename,
+                           sizeof config.c_nodename);
+       strtok(config.c_nodename, ".");
+       if (IsEmptyStr(config.c_fqdn) ) {
+               if ((he = gethostbyname(my_utsname.nodename)) != NULL) {
+                       safestrncpy(config.c_fqdn, he->h_name, sizeof config.c_fqdn);
+               } else {
+                       safestrncpy(config.c_fqdn, my_utsname.nodename, sizeof config.c_fqdn);
+               }
+       }
+       if (IsEmptyStr(config.c_humannode)) {
+               strcpy(config.c_humannode, "My System");
+       }
+       if (IsEmptyStr(config.c_phonenum)) {
+               strcpy(config.c_phonenum, "US 800 555 1212");
+       }
+       if (config.c_initax == 0) {
+               config.c_initax = 4;
+       }
+       if (IsEmptyStr(config.c_moreprompt)) strcpy(config.c_moreprompt, "<more>");
+       if (IsEmptyStr(config.c_twitroom)) strcpy(config.c_twitroom, "Trashcan");
+       if (IsEmptyStr(config.c_baseroom)) strcpy(config.c_baseroom, BASEROOM);
+       if (IsEmptyStr(config.c_aideroom)) strcpy(config.c_aideroom, "Aide");
+       if (config.c_port_number == 0) {
+               config.c_port_number = 504;
+       }
+       if (config.c_sleeping == 0) {
+               config.c_sleeping = 900;
+       }
+       if (config.c_ctdluid == 0) {
+               pw = getpwnam("citadel");
+               if (pw != NULL) {
+                       config.c_ctdluid = pw->pw_uid;
+               }
+       }
+       if (config.c_ctdluid == 0) {
+               pw = getpwnam("bbs");
+               if (pw != NULL) {
+                       config.c_ctdluid = pw->pw_uid;
+               }
+       }
+       if (config.c_ctdluid == 0) {
+               pw = getpwnam("guest");
+               if (pw != NULL) {
+                       config.c_ctdluid = pw->pw_uid;
+               }
+       }
+       if (config.c_createax == 0) {
+               config.c_createax = 3;
+       }
+       /*
+        * Negative values for maxsessions are not allowed.
+        */
+       if (config.c_maxsessions < 0) {
+               config.c_maxsessions = 0;
+       }
+       /* We need a system default message expiry policy, because this is
+        * the top level and there's no 'higher' policy to fall back on.
+        * By default, do not expire messages at all.
+        */
+       if (config.c_ep.expire_mode == 0) {
+               config.c_ep.expire_mode = EXPIRE_MANUAL;
+               config.c_ep.expire_value = 0;
+       }
+
+       /*
+        * Default port numbers for various services
+        */
+       if (config.c_smtp_port == 0) config.c_smtp_port = 25;
+       if (config.c_pop3_port == 0) config.c_pop3_port = 110;
+       if (config.c_imap_port == 0) config.c_imap_port = 143;
+       if (config.c_msa_port == 0) config.c_msa_port = 587;
+       if (config.c_smtps_port == 0) config.c_smtps_port = 465;
+       if (config.c_pop3s_port == 0) config.c_pop3s_port = 995;
+       if (config.c_imaps_port == 0) config.c_imaps_port = 993;
+       if (config.c_pftcpdict_port == 0) config.c_pftcpdict_port = -1;
+       if (config.c_managesieve_port == 0) config.c_managesieve_port = 2020;
+       if (config.c_xmpp_c2s_port == 0) config.c_xmpp_c2s_port = 5222;
+       if (config.c_xmpp_s2s_port == 0) config.c_xmpp_s2s_port = 5269;
+
+       /* Go through a series of dialogs prompting for config info */
+       for (curr = 1; curr <= MAXSETUP; ++curr) {
+               edit_value(curr);
+               if ((curr == 6) && (config.c_auth_mode != AUTHMODE_LDAP) && (config.c_auth_mode != AUTHMODE_LDAP_AD)) {
+                       curr += 5;      /* skip LDAP questions if we're not authenticating against LDAP */
+               }
+       }
+
+/***** begin version update section ***** */
+       /* take care of any updating that is necessary */
+
+       old_setup_level = config.c_setup_level;
+
+       if (old_setup_level == 0) {
+               goto NEW_INST;
+       }
+
+       if (old_setup_level < 555) {
+               important_message("Citadel Setup",
+                                 "This Citadel installation is too old "
+                                 "to be upgraded.");
+               cleanup(1);
+       }
+       write_config_to_disk();
+
+       old_setup_level = config.c_setup_level;
+
+       /* end of version update section */
+
+NEW_INST:
+       config.c_setup_level = REV_LEVEL;
+
+/******************************************/
+
+       write_config_to_disk();
+
+       rv = mkdir(ctdl_info_dir, 0700);
+       rv = chmod(ctdl_info_dir, 0700);
+       rv = chown(ctdl_info_dir, config.c_ctdluid, -1);
+
+       rv = mkdir(ctdl_bio_dir, 0700);
+       rv = chmod(ctdl_bio_dir, 0700);
+       rv = chown(ctdl_bio_dir, config.c_ctdluid, -1);
+
+       rv = mkdir(ctdl_usrpic_dir, 0700);
+       rv = chmod(ctdl_usrpic_dir, 0700);
+       rv = chown(ctdl_usrpic_dir, config.c_ctdluid, -1);
+
+       rv = mkdir(ctdl_message_dir, 0700);
+       rv = chmod(ctdl_message_dir, 0700);
+       rv = chown(ctdl_message_dir, config.c_ctdluid, -1);
+
+       rv = mkdir(ctdl_hlp_dir, 0700);
+       rv = chmod(ctdl_hlp_dir, 0700);
+       rv = chown(ctdl_hlp_dir, config.c_ctdluid, -1);
+
+       rv = mkdir(ctdl_image_dir, 0700);
+       rv = chmod(ctdl_image_dir, 0700);
+       rv = chown(ctdl_image_dir, config.c_ctdluid, -1);
+
+       rv = mkdir(ctdl_bb_dir, 0700);
+       rv = chmod(ctdl_bb_dir, 0700);
+       rv = chown(ctdl_bb_dir, config.c_ctdluid, -1);
+
+       rv = mkdir(ctdl_file_dir, 0700);
+       rv = chmod(ctdl_file_dir, 0700);
+       rv = chown(ctdl_file_dir, config.c_ctdluid, -1);
+
+       rv = mkdir(ctdl_netcfg_dir, 0700);
+       rv = chmod(ctdl_netcfg_dir, 0700);
+       rv = chown(ctdl_netcfg_dir, config.c_ctdluid, -1);
+
+       /* Delete files and directories used by older Citadel versions */
+       rv = system("exec /bin/rm -fr ./rooms ./chatpipes ./expressmsgs ./sessions 2>/dev/null");
+       unlink("citadel.log");
+       unlink("weekly");
+
+       if (((setup_type == UI_SILENT) && (getenv("ALTER_ETC_SERVICES")!=NULL)) || 
+           (setup_type != UI_SILENT))
+               check_services_entry(); /* Check /etc/services */
+#ifndef __CYGWIN__
+       delete_inittab_entry(); /* Remove obsolete /etc/inittab entry */
+       check_xinetd_entry();   /* Check /etc/xinetd.d/telnet */
+
+       if ((getenv("ACT_AS_MTA") == NULL) || 
+           (getenv("ACT_AS_MTA") &&
+            strcasecmp(getenv("ACT_AS_MTA"), "yes") == 0)) {
+               /* Offer to disable other MTA's on the system. */
+               disable_other_mta("courier-authdaemon");
+               disable_other_mta("courier-imap");
+               disable_other_mta("courier-imap-ssl");
+               disable_other_mta("courier-pop");
+               disable_other_mta("courier-pop3");
+               disable_other_mta("courier-pop3d");
+               disable_other_mta("cyrmaster");
+               disable_other_mta("cyrus");
+               disable_other_mta("dovecot");
+               disable_other_mta("exim");
+               disable_other_mta("exim4");
+               disable_other_mta("imapd");
+               disable_other_mta("mta");
+               disable_other_mta("pop3d");
+               disable_other_mta("popd");
+               disable_other_mta("postfix");
+               disable_other_mta("qmail");
+               disable_other_mta("saslauthd");
+               disable_other_mta("sendmail");
+               disable_other_mta("vmailmgrd");
+       }
+#endif
+
+       /* Check for the 'db' nss and offer to disable it */
+       fixnss();
+
+       if ((pw = getpwuid(config.c_ctdluid)) == NULL) {
+               gid = getgid();
+       } else {
+               gid = pw->pw_gid;
+       }
+
+       progress("Setting file permissions", 0, 3);
+       rv = chown(ctdl_run_dir, config.c_ctdluid, gid);
+       progress("Setting file permissions", 1, 3);
+       rv = chown(file_citadel_config, config.c_ctdluid, gid);
+       progress("Setting file permissions", 2, 3);
+       rv = chmod(file_citadel_config, S_IRUSR | S_IWUSR);
+       progress("Setting file permissions", 3, 3);
+
+       /* 
+        * If we're running on SysV, install init scripts.
+        */
+       if (!access("/var/run", W_OK)) {
+
+               if (getenv("NO_INIT_SCRIPTS") == NULL) {
+                       install_init_scripts();
+               }
+
+               if (!access("/etc/init.d/citadel", X_OK)) {
+                       rv = system("/etc/init.d/citadel start");
+                       sleep(3);
+               }
+
+               if (test_server(setup_directory, relhome, enable_home) == 0) {
+                       char buf[SIZ];
+                       int found_it = 0;
+
+                       if (config.c_auth_mode == AUTHMODE_NATIVE) {
+                               snprintf (admin_cmd, sizeof(admin_cmd), "%s/sendcommand \"CREU %s|%s\" 2>&1", 
+                                       ctdl_sbin_dir, config.c_sysadm, admin_pass);
+                               fp = popen(admin_cmd, "r");
+                               if (fp != NULL) {
+                                       while (fgets(buf, sizeof buf, fp) != NULL) 
+                                       {
+                                               if ((atol(buf) == 574) || (atol(buf) == 200))
+                                                       ++found_it;
+                                       }
+                                       pclose(fp);
+                               }
+                       
+                               if (found_it == 0) {
+                                       important_message("Error","Setup failed to create your admin user");
+                               }
+                       }
+
+                       if (setup_type != UI_SILENT)
+                               important_message("Setup finished",
+                                                 "Setup of the Citadel server is complete.\n"
+                                                 "If you will be using WebCit, please run its\n"
+                                                 "setup program now; otherwise, run './citadel'\n"
+                                                 "to log in.\n");
+               }
+               else {
+                       important_message("Setup failed",
+                               "Setup is finished, but the Citadel server failed to start.\n"
+                               "Go back and check your configuration.\n"
+                       );
+               }
+
+       }
+
+       else {
+               important_message("Setup finished",
+                       "Setup is finished.  You may now start the server.");
+       }
+
+       cleanup(0);
+       return 0;
+}
+
+
diff --git a/citadel/utils/stress.c b/citadel/utils/stress.c
new file mode 100644 (file)
index 0000000..d62ea3c
--- /dev/null
@@ -0,0 +1,372 @@
+/* $Id$ */
+
+/* This message is exactly 1024 bytes */
+char* const message =
+"The point of this little file is to stress test a Citadel server.\n"
+"It spawns n threads, where n is a command line parameter, each of\n"
+"which writes 1000 messages total to the server.\n"
+"\n"
+"-n is a command line parameter indicating how many users to simulate\n"
+"(default 100).  WARNING: Your system must be capable of creating this\n"
+"many threads!\n"
+"\n"
+"-w is a command line parameter indicating how long to wait in seconds\n"
+"between posting each message (default 10).  The actual interval\n"
+"will be randomized between w / 3 and w * 3.\n"
+"\n"
+"A run is expected to take approximately three hours, given default\n"
+"values, and assuming the server can keep up.  If the run takes much\n"
+"longer than this, there may be a performance problem with the server.\n"
+"For best results, the test should be run from a different machine than\n"
+"the server, but connected via a fast network link (e.g. 100Base-T).\n"
+"\n"
+"To get baseline results, run the test with -n 1 (simulating 1 user)\n"
+"on a machine with no other users logged in.\n"
+"\n"
+"Example:\n"
+"stress -n 500 -w 25 myserver > stress.csv\n";
+
+/* The program tries to be as small and as fast as possible.  Wherever
+ * possible, we avoid allocating memory on the heap.  We do not pass data
+ * between threads.  We do only a minimal amount of calculation.  In
+ * particular, we only output raw timing data for the run; we do not
+ * collate it, average it, or do anything else with it.  See below.
+ * The program does, however, use the same CtdlIPC functions as the
+ * standard Citadel text client, and incurs the same overhead as that
+ * program, using those functions.
+ *
+ * The program first creates a new user with a randomized username which
+ * begins with "testuser".  It then creates 100 rooms named test0 through
+ * test99.  If they already exist, this condition is ignored.
+ *
+ * The program then creates n threads, all of which wait on a conditional
+ * before they do anything.  Once all of the threads have been created,
+ * they are signaled, and begin execution.  Each thread logs in to the
+ * Citadel server separately, simulating a user login, then takes a
+ * timestamp from the operating system.
+ *
+ * Each thread selects a room from 0-99 randomly, then writes a small
+ * (1KB) test message to that room.  1K was chosen because it seems to
+ * represent an average message size for messages we expect to see.
+ * After writing the message, the thread sleeps for w seconds (sleep(w);)
+ * and repeats the process, until it has written 1,000 messages.  The
+ * program provides a status display to standard error, unless w <= 2, in
+ * which case status display is disabled.
+ *
+ * After posting all messages, each thread takes a second timestamp, and
+ * subtracts the first timestamp.  The resulting value (in seconds) is
+ * sent to standard output, followed by the minimum, average, and maximum
+ * amounts of time (in milliseconds) it took to post a message.  The
+ * thread then exits.
+ *
+ * Once all threads have exited, the program exits.
+ */
+
+#include <stdlib.h>
+#include <unistd.h>
+#include <stdio.h>
+#include <sys/types.h>
+#include <string.h>
+#include <libcitadel.h>
+#include "sysdep.h"
+#if TIME_WITH_SYS_TIME
+# include <sys/time.h>
+# include <time.h>
+#else
+# if HAVE_SYS_TIME_H
+#  include <sys/time.h>
+# else
+#  include <time.h>
+# endif
+#endif
+#include "citadel_ipc.h"
+
+#ifndef HAVE_PTHREAD_H
+#error This program requires threads
+#endif
+
+static int w = 10;             /* see above */
+static int n = 100;            /* see above */
+static int m = 1000;           /* Number of messages to send; see above */
+static volatile int count = 0; /* Total count of messages posted */
+static volatile int total = 0; /* Total messages to be posted */
+static pthread_mutex_t count_mutex = PTHREAD_MUTEX_INITIALIZER;
+static pthread_mutex_t arg_mutex = PTHREAD_MUTEX_INITIALIZER;
+static pthread_mutex_t output_mutex = PTHREAD_MUTEX_INITIALIZER;
+
+static char username[12];
+static char password[12];
+
+/*
+ * Mutex for the random number generator
+ * We don't assume that rand_r() is present, so we have to
+ * provide our own locking for rand()
+ */
+static pthread_mutex_t rand_mutex = PTHREAD_MUTEX_INITIALIZER;
+
+/*
+ * Conditional.  All the threads wait for this signal to actually
+ * start bombarding the server.
+ */
+static pthread_mutex_t start_mutex = PTHREAD_MUTEX_INITIALIZER;
+static pthread_cond_t start_cond = PTHREAD_COND_INITIALIZER;
+
+
+/*
+ * This is the worker thread.  It logs in and creates the 1,000 messages
+ * as described above.
+ */
+void* worker(void* data)
+{
+       CtdlIPC* ipc;   /* My connection to the server */
+       void** args;    /* Args sent in */
+       int r;          /* IPC return code */
+       char aaa[SIZ];  /* Generic buffer */
+       int c;          /* Message count */
+       time_t start, end;      /* Timestamps */
+       struct ctdlipcmessage msg;      /* The message we will post */
+       int argc_;
+       char** argv_;
+       long tmin = LONG_MAX, trun = 0, tmax = LONG_MIN;
+
+       args = (void*)data;
+       argc_ = (int)args[0];
+       argv_ = (char**)args[1];
+
+       /* Setup the message we will be posting */
+       msg.text = message;
+       msg.anonymous = 0;
+       msg.type = 1;
+       strcpy(msg.recipient, "");
+       strcpy(msg.subject, "Test message; ignore");
+       strcpy(msg.author, username);
+
+       pthread_mutex_lock(&arg_mutex);
+       ipc = CtdlIPC_new(argc_, argv_, NULL, NULL);
+       pthread_mutex_unlock(&arg_mutex);
+       if (!ipc)
+               return NULL;    /* oops, something happened... */
+
+       CtdlIPC_chat_recv(ipc, aaa);
+       if (aaa[0] != '2') {
+               fprintf(stderr, "Citadel refused me: %s\n", &aaa[4]);
+               return NULL;    /* server ran out of connections maybe? */
+       }
+
+       CtdlIPCIdentifySoftware(ipc, 8, 8, REV_LEVEL, "Citadel stress tester",
+               "localhost", aaa);      /* we're lying, the server knows */
+       
+       r = CtdlIPCQueryUsername(ipc, username, aaa);
+       if (r / 100 == 2) {
+               /* testuser already exists (from previous run?) */
+               r = CtdlIPCTryLogin(ipc, username, aaa);
+               if (r / 100 != 3) {
+                       fprintf(stderr, "Citadel refused username: %s\n", aaa);
+                       CtdlIPC_delete_ptr(&ipc);
+                       return NULL;    /* Gawd only knows what went wrong */
+               }
+               r = CtdlIPCTryPassword(ipc, password, aaa);
+               if (r / 100 != 2) {
+                       fprintf(stderr, "Citadel refused password: %s\n", aaa);
+                       CtdlIPC_delete_ptr(&ipc);
+                       return NULL;    /* Gawd only knows what went wrong */
+               }
+       } else {
+               /* testuser doesn't yet exist */
+               r = CtdlIPCCreateUser(ipc, username, 1, aaa);
+               if (r / 100 != 2) {
+                       fprintf(stderr, "Citadel refused create user: %s\n", aaa);
+                       CtdlIPC_delete_ptr(&ipc);
+                       return NULL;    /* Gawd only knows what went wrong */
+               }
+               r = CtdlIPCChangePassword(ipc, password, aaa);
+               if (r / 100 != 2) {
+                       fprintf(stderr, "Citadel refused change password: %s\n", aaa);
+                       CtdlIPC_delete_ptr(&ipc);
+                       return NULL;    /* Gawd only knows what went wrong */
+               }
+       }
+
+       /* Wait for the rest of the threads */
+       pthread_mutex_lock(&start_mutex);
+       pthread_cond_wait(&start_cond, &start_mutex);
+       pthread_mutex_unlock(&start_mutex);
+
+       /* And now the fun begins!  Send out a whole shitload of messages */
+       start = time(NULL);
+       for (c = 0; c < m; c++) {
+               int rm;
+               char room[7];
+               struct ctdlipcroom *rret;
+               struct timeval tv;
+               long tstart, tend;
+               int wait;
+
+               /* Wait for a while */
+               pthread_mutex_lock(&rand_mutex);
+               /* See Numerical Recipes in C or Knuth vol. 2 ch. 3 */
+               /* Randomize between w/3 to w*3 (yes, it's complicated) */
+               wait = (int)((1.0+2.7*(float)w)*rand()/(RAND_MAX+(float)w/3.0)); /* range 0-99 */
+               pthread_mutex_unlock(&rand_mutex);
+               sleep(wait);
+
+               /* Select the room to goto */
+               pthread_mutex_lock(&rand_mutex);
+               /* See Numerical Recipes in C or Knuth vol. 2 ch. 3 */
+               rm = (int)(100.0*rand()/(RAND_MAX+1.0)); /* range 0-99 */
+               pthread_mutex_unlock(&rand_mutex);
+
+               /* Goto the selected room */
+               sprintf(room, "test%d", rm);
+               /* Create the room if not existing. Ignore the return */
+               r = CtdlIPCCreateRoom(ipc, 1, room, 0, NULL, 0, aaa);
+               if (r / 100 != 2 && r != 574) { /* Already exists */
+                       fprintf(stderr, "Citadel refused room create: %s\n", aaa);
+                       pthread_mutex_lock(&count_mutex);
+                       total -= m - c;
+                       pthread_mutex_unlock(&count_mutex);
+                       CtdlIPC_delete_ptr(&ipc);
+                       return NULL;
+               }
+               gettimeofday(&tv, NULL);
+               tstart = tv.tv_sec * 1000 + tv.tv_usec / 1000; /* cvt to msec */
+               r = CtdlIPCGotoRoom(ipc, room, "", &rret, aaa);
+               if (r / 100 != 2) {
+                       fprintf(stderr, "Citadel refused room change: %s\n", aaa);
+                       pthread_mutex_lock(&count_mutex);
+                       total -= m - c;
+                       pthread_mutex_unlock(&count_mutex);
+                       CtdlIPC_delete_ptr(&ipc);
+                       return NULL;
+               }
+
+               /* Post the message */
+               r = CtdlIPCPostMessage(ipc, 1, NULL, &msg, aaa);
+               if (r / 100 != 4) {
+                       fprintf(stderr, "Citadel refused message entry: %s\n", aaa);
+                       pthread_mutex_lock(&count_mutex);
+                       total -= m - c;
+                       pthread_mutex_unlock(&count_mutex);
+                       CtdlIPC_delete_ptr(&ipc);
+                       return NULL;
+               }
+
+               /* Do a status update */
+               pthread_mutex_lock(&count_mutex);
+               count++;
+               pthread_mutex_unlock(&count_mutex);
+               fprintf(stderr, " %d/%d=%d%%             \r",
+                       count, total,
+                       (int)(100 * count / total));
+               gettimeofday(&tv, NULL);
+               tend = tv.tv_sec * 1000 + tv.tv_usec / 1000; /* cvt to msec */
+               tend -= tstart;
+               if (tend < tmin) tmin = tend;
+               if (tend > tmax) tmax = tend;
+               trun += tend;
+       }
+       end = time(NULL);
+       pthread_mutex_lock(&output_mutex);
+       fprintf(stderr, "               \r");
+       printf("%ld %ld %ld %ld\n", end - start, tmin, trun / c, tmax);
+       pthread_mutex_unlock(&output_mutex);
+       return (void*)(end - start);
+}
+
+
+/*
+ * Shift argument list
+ */
+int shift(int argc, char **argv, int start, int count)
+{
+       int i;
+
+       for (i = start; i < argc - count; ++i)
+               argv[i] = argv[i + count];
+       return argc - count;
+}
+
+
+/*
+ * Main loop.  Start a shitload of threads, all of which will attempt to
+ * kick a Citadel server square in the nuts.
+ */
+int main(int argc, char** argv)
+{
+       void* data[2];          /* pass args to worker thread */
+       pthread_t* threads;     /* A shitload of threads */
+       pthread_attr_t attr;    /* Thread attributes (we use defaults) */
+       int i;                  /* Counters */
+       long runtime;           /* Run time for each thread */
+
+       /* Read argument list */
+       for (i = 0; i < argc; i++) {
+               if (!strcmp(argv[i], "-n")) {
+                       n = atoi(argv[i + 1]);
+                       argc = shift(argc, argv, i, 2);
+               }
+               if (!strcmp(argv[i], "-w")) {
+                       w = atoi(argv[i + 1]);
+                       argc = shift(argc, argv, i, 2);
+               }
+               if (!strcmp(argv[i], "-m")) {
+                       m = atoi(argv[i + 1]);
+                       argc = shift(argc, argv, i, 2);
+               }
+               if (!strcmp(argv[i], "-h") || !strcmp(argv[i], "--help")) {
+                       fprintf(stderr, "Read stress.c for usage info\n");
+                       return 1;
+               }
+       }
+
+       data[0] = (void*)argc;  /* pass args to worker thread */
+       data[1] = (void*)argv;  /* pass args to worker thread */
+
+       /* This is how many total messages will be posted */
+       total = n * m;
+
+       /* Pick a randomized username */
+       pthread_mutex_lock(&rand_mutex);
+       /* See Numerical Recipes in C or Knuth vol. 2 ch. 3 */
+       i = (int)(100.0*rand()/(RAND_MAX+1.0)); /* range 0-99 */
+       pthread_mutex_unlock(&rand_mutex);
+       sprintf(username, "testuser%d", i);
+       strcpy(password, username);
+
+       /* First, memory for our shitload of threads */
+       threads = calloc(n, sizeof(pthread_t));
+       if (!threads) {
+               perror("Not enough memory");
+               return 1;
+       }
+
+       /* Then thread attributes (all defaults for now) */
+       pthread_attr_init(&attr);
+       pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_JOINABLE);
+
+       /* Then, create some threads */
+       fprintf(stderr, "Creating threads      \r");
+       for (i = 0; i < n; ++i) {
+               pthread_create(&threads[i], &attr, worker, (void*)data);
+               
+               /* Give thread #0 time to create the user account */
+               if (i == 0) sleep(3);
+       }
+
+       //fprintf(stderr, "Starting in %d seconds\r", n);
+       //sleep(n);
+       fprintf(stderr, "                      \r");
+
+       /* Then, signal the conditional they all are waiting on */
+       pthread_mutex_lock(&start_mutex);
+       pthread_cond_broadcast(&start_cond);
+       pthread_mutex_unlock(&start_mutex);
+
+       /* Then wait for them to exit */
+       for (i = 0; i < n; i++) {
+               pthread_join(threads[i], (void*)&runtime);
+               /* We're ignoring this value for now... TODO */
+       }
+       fprintf(stderr, "\r                                                                               \r");
+       return 0;
+}
diff --git a/citadel/utils/userlist.c b/citadel/utils/userlist.c
new file mode 100644 (file)
index 0000000..3f78cea
--- /dev/null
@@ -0,0 +1,115 @@
+/*
+ * $Id$
+ *
+ * Command-line user list utility.
+ *
+ */
+
+#include <stdlib.h>
+#include <unistd.h>
+#include <stdio.h>
+#include <string.h>
+
+#if TIME_WITH_SYS_TIME
+# include <sys/time.h>
+# include <time.h>
+#else
+# if HAVE_SYS_TIME_H
+#  include <sys/time.h>
+# else
+#  include <time.h>
+# endif
+#endif
+
+#include <libcitadel.h>
+#include "citadel.h"
+#include <unistd.h>
+#include "citadel_ipc.h"
+#include "citadel_dirs.h"
+
+void logoff(int code)
+{
+       exit(code);
+}
+
+void userlist(CtdlIPC *ipc) { 
+       char buf[SIZ];
+       char fl[SIZ];
+       struct tm tmbuf;
+       time_t lc;
+       char *listing = NULL;
+       int r;
+
+       r = CtdlIPCUserListing(ipc, "", &listing, buf);
+       if (r / 100 != 1) {
+               printf("%s\n", buf);
+               return;
+       }
+       printf("       User Name           Num  L Last Visit Logins Messages\n");
+       printf("------------------------- ----- - ---------- ------ --------\n");
+       while (strlen(listing) > 0) {
+               extract_token(buf, listing, 0, '\n', sizeof buf);
+               remove_token(listing, 0, '\n');
+               extract_token(fl, buf, 0, '|', sizeof fl);
+               printf("%-25s ",fl);
+               printf("%5ld %d ", extract_long(buf,2),
+                       extract_int(buf,1));
+               lc = extract_long(buf,3);
+               localtime_r(&lc, &tmbuf);
+               printf("%02d/%02d/%04d ",
+                       (tmbuf.tm_mon+1),
+                       tmbuf.tm_mday,
+                       (tmbuf.tm_year + 1900));
+               printf("%6ld %8ld\n",
+                       extract_long(buf,4),extract_long(buf,5));
+       }
+       printf("\n");
+}
+
+
+int main(int argc, char **argv)
+{
+       char buf[SIZ];
+       char hostbuf[SIZ], portbuf[SIZ];
+       CtdlIPC *ipc = NULL;
+       int relh=0;
+       int home=0;
+       char relhome[PATH_MAX]="";
+       char ctdldir[PATH_MAX]=CTDLDIR;
+
+       calc_dirs_n_files(relh, home, relhome, ctdldir, 0);
+
+       ipc = CtdlIPC_new(argc, argv, hostbuf, portbuf);
+       CtdlIPC_chat_recv(ipc, buf);
+       if ((buf[0]!='2')&&(strncmp(buf,"551",3))) {
+               fprintf(stderr,"%s: %s\n",argv[0],&buf[4]);
+               logoff(atoi(buf));
+       }
+
+       userlist(ipc);
+
+       CtdlIPCQuit(ipc);
+       exit(0);
+}
+
+
+#ifndef HAVE_STRERROR
+/*
+ * replacement strerror() for systems that don't have it
+ */
+char *strerror(int e)
+{
+       static char buf[32];
+
+       snprintf(buf, sizeof buf, "errno = %d",e);
+       return(buf);
+}
+#endif
+
+
+/*
+ * Stub function
+ */
+void stty_ctdl(int cmd) {
+}
+
diff --git a/citadel/utils/whobbs.c b/citadel/utils/whobbs.c
new file mode 100644 (file)
index 0000000..e3523aa
--- /dev/null
@@ -0,0 +1,203 @@
+/*
+ * $Id$
+ * 
+ * Command-line "who is online?" utility
+ *
+ */
+
+#include "sysdep.h"
+#include <stdlib.h>
+#include <unistd.h>
+#include <stdio.h>
+#include <string.h>
+#include <errno.h>
+#include <libcitadel.h>
+#include "citadel.h"
+#include "citadel_ipc.h"
+#include "citadel_dirs.h"
+
+void logoff(int code)
+{
+       exit(code);
+       }
+
+static void escapize(char *buf, size_t n) {
+       char hold[512];
+       int i, len;
+       size_t tmp;
+
+       strcpy(hold, buf);
+       strcpy(buf, "");
+       tmp = 0;
+       len = strlen(hold);
+       for (i=0; i<len; ++i) {
+               if (hold[i]=='<') {
+                       snprintf(&buf[tmp], n - tmp, "&lt;");
+                       tmp += 4;
+               }
+               else if (hold[i]=='>'){
+                       snprintf(&buf[tmp], n - tmp, "&gt;");
+                       tmp += 4;
+               }
+               else if (hold[i]==34){
+                       snprintf(&buf[tmp], n - tmp, "&quot;");
+                       tmp += 6;
+               }
+               else{
+                       snprintf(&buf[tmp], n - tmp, "%c", hold[i]);
+                       tmp ++;
+               }
+       }
+}
+
+
+
+
+int main(int argc, char **argv)
+{
+       char buf[512];
+       char nodetitle[SIZ];
+       int www = 0;
+       int s_pid = 0;
+       int my_pid = 0;
+       char hostbuf[SIZ];
+       char portbuf[SIZ];
+       char s_user[SIZ];
+       char s_room[SIZ];
+       char s_host[SIZ];
+       char s_client[SIZ];
+       int r;                  /* IPC response code */
+       time_t timenow;
+       char *listing = NULL;
+       CtdlIPC *ipc = NULL;
+       int relh=0;
+       int home=0;
+       char relhome[PATH_MAX]="";
+       char ctdldir[PATH_MAX]=CTDLDIR;
+
+       calc_dirs_n_files(relh, home, relhome, ctdldir, 0);
+
+       /* If this environment variable is set, we assume that the program
+        * is being called as a cgi-bin from a webserver and will output
+        * everything as HTML.
+        */     
+       if (getenv("REQUEST_METHOD") != NULL) www = 1;
+
+       ipc = CtdlIPC_new(argc, argv, hostbuf, portbuf);
+       if (!ipc) {
+               fprintf(stderr, "Server not available: %s\n", strerror(errno));
+               logoff(errno);
+       }
+       CtdlIPC_chat_recv(ipc, buf);
+       if ((buf[0]!='2')&&(strncmp(buf,"551",3))) {
+               fprintf(stderr,"%s: %s\n",argv[0],&buf[4]);
+               logoff(atoi(buf));
+               }
+       strcpy(nodetitle, "this Citadel site");
+       r = CtdlIPCServerInfo(ipc, buf);
+       if (r / 100 == 1) {
+               my_pid = ipc->ServInfo.pid;
+               strcpy(nodetitle, ipc->ServInfo.humannode);
+       }
+       
+       if (www) {
+               printf( "Content-type: text/html\n"
+                       "\n"
+                       "<HTML><HEAD>"
+                       "<META HTTP-EQUIV=\"Refresh\" CONTENT=\"60\">\n"
+                       "<TITLE>");
+               printf("%s: who is online", nodetitle);
+               printf( "</TITLE></HEAD><BODY><H1>");
+       } else {
+               printf("            ");
+       }
+
+       if (www) {
+               printf("<CENTER><H1>");
+       }
+
+       printf("Users currently logged on to %s\n", nodetitle);
+
+       if (www) {
+               printf("</H1>\n");
+       }
+
+       r = CtdlIPCOnlineUsers(ipc, &listing, &timenow, buf);
+       if (r / 100 != 1) {
+               fprintf(stderr,"%s: %s\n",argv[0], buf);
+               logoff(atoi(buf));
+       }
+
+       if (www) {
+               printf( "<TABLE BORDER=1 WIDTH=100%%>"
+                       "<TR><TH>Session</TH><TH>User name</TH>"
+                       "<TH>Room</TH><TH>From host</TH>"
+                       "<TH>Client software</TH></TR>\n");
+       } else {
+
+               printf( "Session         User name               "
+                       "Room                  From host\n");
+               printf( "------- ------------------------- "
+                       "------------------- ------------------------\n");
+       }
+
+
+       while (!IsEmptyStr(listing)) {
+               extract_token(buf, listing, 0, '\n', sizeof buf);
+               remove_token(listing, 0, '\n');
+
+               /* Escape some stuff if we're using www mode */
+               if (www) escapize(buf, sizeof buf);
+
+               s_pid = extract_int(buf,0);
+               extract_token(s_user, buf, 1, '|', sizeof s_user);
+               extract_token(s_room, buf, 2, '|', sizeof s_room);
+               extract_token(s_host, buf, 3, '|', sizeof s_host);
+               extract_token(s_client, buf, 4, '|', sizeof s_client);
+               if (s_pid != my_pid) {
+
+                       if (www) printf("<TR><TD>");
+                       printf("%-7d", s_pid);
+                       printf("%c", 
+                               ((s_pid == my_pid) ? '*' : ' '));
+                       if (www) printf("</TD><TD>");
+                       printf("%-26s", s_user);
+                       if (www) printf("</TD><TD>");
+                       printf("%-19s ", s_room);
+                       if (www) printf("</TD><TD>");
+                       printf("%-24s\n", s_host);
+                       if (www) printf("</TD><TD>%s</TD></TR>\n", s_client);
+                       }
+               }
+       free(listing);
+
+       if (www) printf("</TABLE></CENTER>\n"
+                       "<FONT SIZE=-1>"
+                       "(This display will automatically refresh "
+                       "once per minute)</FONT>\n"
+                       "</BODY></HTML>\n");
+
+       r = CtdlIPCQuit(ipc);
+       return (r / 100 == 2) ? 0 : r;
+}
+
+
+/*
+ * Stub function
+ */
+void stty_ctdl(int cmd) {
+}
+
+
+#ifndef HAVE_STRERROR
+/*
+ * replacement strerror() for systems that don't have it
+ */
+char *strerror(int e)
+{
+       static char buf[32];
+
+       snprintf(buf, sizeof buf, "errno = %d",e);
+       return(buf);
+       }
+#endif
diff --git a/citadel/whobbs.c b/citadel/whobbs.c
deleted file mode 100644 (file)
index e3523aa..0000000
+++ /dev/null
@@ -1,203 +0,0 @@
-/*
- * $Id$
- * 
- * Command-line "who is online?" utility
- *
- */
-
-#include "sysdep.h"
-#include <stdlib.h>
-#include <unistd.h>
-#include <stdio.h>
-#include <string.h>
-#include <errno.h>
-#include <libcitadel.h>
-#include "citadel.h"
-#include "citadel_ipc.h"
-#include "citadel_dirs.h"
-
-void logoff(int code)
-{
-       exit(code);
-       }
-
-static void escapize(char *buf, size_t n) {
-       char hold[512];
-       int i, len;
-       size_t tmp;
-
-       strcpy(hold, buf);
-       strcpy(buf, "");
-       tmp = 0;
-       len = strlen(hold);
-       for (i=0; i<len; ++i) {
-               if (hold[i]=='<') {
-                       snprintf(&buf[tmp], n - tmp, "&lt;");
-                       tmp += 4;
-               }
-               else if (hold[i]=='>'){
-                       snprintf(&buf[tmp], n - tmp, "&gt;");
-                       tmp += 4;
-               }
-               else if (hold[i]==34){
-                       snprintf(&buf[tmp], n - tmp, "&quot;");
-                       tmp += 6;
-               }
-               else{
-                       snprintf(&buf[tmp], n - tmp, "%c", hold[i]);
-                       tmp ++;
-               }
-       }
-}
-
-
-
-
-int main(int argc, char **argv)
-{
-       char buf[512];
-       char nodetitle[SIZ];
-       int www = 0;
-       int s_pid = 0;
-       int my_pid = 0;
-       char hostbuf[SIZ];
-       char portbuf[SIZ];
-       char s_user[SIZ];
-       char s_room[SIZ];
-       char s_host[SIZ];
-       char s_client[SIZ];
-       int r;                  /* IPC response code */
-       time_t timenow;
-       char *listing = NULL;
-       CtdlIPC *ipc = NULL;
-       int relh=0;
-       int home=0;
-       char relhome[PATH_MAX]="";
-       char ctdldir[PATH_MAX]=CTDLDIR;
-
-       calc_dirs_n_files(relh, home, relhome, ctdldir, 0);
-
-       /* If this environment variable is set, we assume that the program
-        * is being called as a cgi-bin from a webserver and will output
-        * everything as HTML.
-        */     
-       if (getenv("REQUEST_METHOD") != NULL) www = 1;
-
-       ipc = CtdlIPC_new(argc, argv, hostbuf, portbuf);
-       if (!ipc) {
-               fprintf(stderr, "Server not available: %s\n", strerror(errno));
-               logoff(errno);
-       }
-       CtdlIPC_chat_recv(ipc, buf);
-       if ((buf[0]!='2')&&(strncmp(buf,"551",3))) {
-               fprintf(stderr,"%s: %s\n",argv[0],&buf[4]);
-               logoff(atoi(buf));
-               }
-       strcpy(nodetitle, "this Citadel site");
-       r = CtdlIPCServerInfo(ipc, buf);
-       if (r / 100 == 1) {
-               my_pid = ipc->ServInfo.pid;
-               strcpy(nodetitle, ipc->ServInfo.humannode);
-       }
-       
-       if (www) {
-               printf( "Content-type: text/html\n"
-                       "\n"
-                       "<HTML><HEAD>"
-                       "<META HTTP-EQUIV=\"Refresh\" CONTENT=\"60\">\n"
-                       "<TITLE>");
-               printf("%s: who is online", nodetitle);
-               printf( "</TITLE></HEAD><BODY><H1>");
-       } else {
-               printf("            ");
-       }
-
-       if (www) {
-               printf("<CENTER><H1>");
-       }
-
-       printf("Users currently logged on to %s\n", nodetitle);
-
-       if (www) {
-               printf("</H1>\n");
-       }
-
-       r = CtdlIPCOnlineUsers(ipc, &listing, &timenow, buf);
-       if (r / 100 != 1) {
-               fprintf(stderr,"%s: %s\n",argv[0], buf);
-               logoff(atoi(buf));
-       }
-
-       if (www) {
-               printf( "<TABLE BORDER=1 WIDTH=100%%>"
-                       "<TR><TH>Session</TH><TH>User name</TH>"
-                       "<TH>Room</TH><TH>From host</TH>"
-                       "<TH>Client software</TH></TR>\n");
-       } else {
-
-               printf( "Session         User name               "
-                       "Room                  From host\n");
-               printf( "------- ------------------------- "
-                       "------------------- ------------------------\n");
-       }
-
-
-       while (!IsEmptyStr(listing)) {
-               extract_token(buf, listing, 0, '\n', sizeof buf);
-               remove_token(listing, 0, '\n');
-
-               /* Escape some stuff if we're using www mode */
-               if (www) escapize(buf, sizeof buf);
-
-               s_pid = extract_int(buf,0);
-               extract_token(s_user, buf, 1, '|', sizeof s_user);
-               extract_token(s_room, buf, 2, '|', sizeof s_room);
-               extract_token(s_host, buf, 3, '|', sizeof s_host);
-               extract_token(s_client, buf, 4, '|', sizeof s_client);
-               if (s_pid != my_pid) {
-
-                       if (www) printf("<TR><TD>");
-                       printf("%-7d", s_pid);
-                       printf("%c", 
-                               ((s_pid == my_pid) ? '*' : ' '));
-                       if (www) printf("</TD><TD>");
-                       printf("%-26s", s_user);
-                       if (www) printf("</TD><TD>");
-                       printf("%-19s ", s_room);
-                       if (www) printf("</TD><TD>");
-                       printf("%-24s\n", s_host);
-                       if (www) printf("</TD><TD>%s</TD></TR>\n", s_client);
-                       }
-               }
-       free(listing);
-
-       if (www) printf("</TABLE></CENTER>\n"
-                       "<FONT SIZE=-1>"
-                       "(This display will automatically refresh "
-                       "once per minute)</FONT>\n"
-                       "</BODY></HTML>\n");
-
-       r = CtdlIPCQuit(ipc);
-       return (r / 100 == 2) ? 0 : r;
-}
-
-
-/*
- * Stub function
- */
-void stty_ctdl(int cmd) {
-}
-
-
-#ifndef HAVE_STRERROR
-/*
- * replacement strerror() for systems that don't have it
- */
-char *strerror(int e)
-{
-       static char buf[32];
-
-       snprintf(buf, sizeof buf, "errno = %d",e);
-       return(buf);
-       }
-#endif