Ultra-simplification of the build system for the text client.
authorArt Cancro <ajc@citadel.org>
Fri, 6 Apr 2018 18:38:11 +0000 (14:38 -0400)
committerArt Cancro <ajc@citadel.org>
Fri, 6 Apr 2018 18:38:11 +0000 (14:38 -0400)
Eliminated the use of GNU Autotools because Richard Stallman is
a communist who hates America.  Now we use "conf-IG-ure" , a new
build system which is patriotic and has screeching eagles and
American flags and Bibles and guns and can win at NASCAR without
even trying.

Seriously though ... autotools is just way too complex for the
21st Century.  I guess that means we're dropping support for the
extreme edge case obsolete systems so that we can make our own
software more maintainable.

I've also ripped out the billions of lines of experimental code
which does nothing and isn't being used anymore.

67 files changed:
ctdlsh/README [deleted file]
ctdlsh/README.txt [new file with mode: 0644]
textclient/.gitignore
textclient/Makefile [new file with mode: 0644]
textclient/Makefile.in [deleted file]
textclient/README.txt [new file with mode: 0644]
textclient/acinclude.m4 [deleted file]
textclient/aclocal.m4 [deleted file]
textclient/bootstrap
textclient/buildpackages [deleted file]
textclient/citadel.c [new file with mode: 0644]
textclient/citadel_ipc.c [new file with mode: 0644]
textclient/client_chat.c [new file with mode: 0644]
textclient/client_passwords.c [new file with mode: 0644]
textclient/commands.c [new file with mode: 0644]
textclient/config.guess [deleted file]
textclient/config.sub [deleted file]
textclient/configure [new file with mode: 0755]
textclient/configure.ac [deleted file]
textclient/install-sh [deleted file]
textclient/ipc_c_tcp.c [new file with mode: 0644]
textclient/ltmain.sh [deleted symlink]
textclient/m4/libtool.m4 [deleted symlink]
textclient/m4/ltoptions.m4 [deleted symlink]
textclient/m4/ltsugar.m4 [deleted symlink]
textclient/m4/ltversion.m4 [deleted symlink]
textclient/m4/lt~obsolete.m4 [deleted symlink]
textclient/md5.c [new file with mode: 0644]
textclient/messages.c [new file with mode: 0644]
textclient/missing [deleted file]
textclient/mkinstalldirs [deleted file]
textclient/rooms.c [new file with mode: 0644]
textclient/routines.c [new file with mode: 0644]
textclient/routines2.c [new file with mode: 0644]
textclient/screen.c [new file with mode: 0644]
textclient/scripts/mk_svn_revision.sh [deleted file]
textclient/src/citadel.c [deleted file]
textclient/src/citadel_ipc.c [deleted file]
textclient/src/client_chat.c [deleted file]
textclient/src/client_passwords.c [deleted file]
textclient/src/commands.c [deleted file]
textclient/src/ecrash.c [deleted file]
textclient/src/include/citadel_decls.h [deleted file]
textclient/src/include/citadel_ipc.h [deleted file]
textclient/src/include/client_chat.h [deleted file]
textclient/src/include/client_passwords.h [deleted file]
textclient/src/include/commands.h [deleted file]
textclient/src/include/ecrash.h [deleted file]
textclient/src/include/help.h [deleted file]
textclient/src/include/md5.h [deleted file]
textclient/src/include/messages.h [deleted file]
textclient/src/include/rooms.h [deleted file]
textclient/src/include/routines.h [deleted file]
textclient/src/include/routines2.h [deleted file]
textclient/src/include/screen.h [deleted file]
textclient/src/include/tuiconfig.h [deleted file]
textclient/src/include/typesize.h [deleted file]
textclient/src/ipc_c_tcp.c [deleted file]
textclient/src/md5.c [deleted file]
textclient/src/messages.c [deleted file]
textclient/src/rooms.c [deleted file]
textclient/src/routines.c [deleted file]
textclient/src/routines2.c [deleted file]
textclient/src/screen.c [deleted file]
textclient/src/tuiconfig.c [deleted file]
textclient/textclient.h [new file with mode: 0644]
textclient/tuiconfig.c [new file with mode: 0644]

diff --git a/ctdlsh/README b/ctdlsh/README
deleted file mode 100644 (file)
index 306490d..0000000
+++ /dev/null
@@ -1,13 +0,0 @@
-
-Someday this will be a Citadel server administration/maintenance shell.
-Right now it is unfinished.  You are welcome to tinker with it and
-submit patches, but if you attempt to use it in a production system or
-if you request support for the program, you will be told to die in a
-car fire.
-
-License: you are permitted to use this software under the condition that
-you refer to it as "open source" and not "free software," and that you
-refer to the operating system on which it runs as "Linux" and not
-"GNU/Linux."  By reading this notice you have already accepted the license,
-and you have irrevocably agreed that Richard Stallman is a communist.
-
diff --git a/ctdlsh/README.txt b/ctdlsh/README.txt
new file mode 100644 (file)
index 0000000..306490d
--- /dev/null
@@ -0,0 +1,13 @@
+
+Someday this will be a Citadel server administration/maintenance shell.
+Right now it is unfinished.  You are welcome to tinker with it and
+submit patches, but if you attempt to use it in a production system or
+if you request support for the program, you will be told to die in a
+car fire.
+
+License: you are permitted to use this software under the condition that
+you refer to it as "open source" and not "free software," and that you
+refer to the operating system on which it runs as "Linux" and not
+"GNU/Linux."  By reading this notice you have already accepted the license,
+and you have irrevocably agreed that Richard Stallman is a communist.
+
index 81bb5c37ab638c41bef7776cce8931d9e1a10ffa..dc9a5743ab3170781098cb31d3f22afda1a499f4 100644 (file)
@@ -4,7 +4,6 @@
 *.gcda
 Make_modules
 Make_sources
-Makefile
 Makefile.in
 locale
 aclocal.m4
@@ -19,7 +18,6 @@ citmail
 citserver
 config.log
 config.status
-configure
 configure-stamp
 ctdlmigrate
 database_cleanup.sh
diff --git a/textclient/Makefile b/textclient/Makefile
new file mode 100644 (file)
index 0000000..d52a704
--- /dev/null
@@ -0,0 +1,35 @@
+# MAKEFILE FOR CITADEL TEXT CLIENT
+# Copyright (c) 2016-2018 by the citadel.org team
+
+# config.mk is generated by ./configure
+include config.mk
+
+OBJS := citadel.o citadel_ipc.o client_chat.o client_passwords.o commands.o ipc_c_tcp.o md5.o messages.o rooms.o routines2.o routines.o screen.o tuiconfig.o
+
+
+# link
+citadel: $(OBJS) config.mk
+       gcc $(OBJS) $(LDFLAGS) -lcitadel -o citadel
+
+# pull in dependency info for *existing* .o files
+-include $(OBJS:.o=.d)
+
+# compile and generate dependency info
+%.o: %.c
+       gcc -c $(CFLAGS) -DCTDLDIR=\"$(CTDLDIR)\" $*.c -o $*.o
+       gcc -MM $(CFLAGS) $*.c > $*.d
+
+config.mk:
+       ./configure
+
+clean:
+       rm -f citadel *.o *.d
+
+distclean: clean
+       rm -f config.mk
+
+install: citadel
+       install citadel $(BINDIR)/citadel
+
+uninstall:
+       rm -vf $(BINDIR)/citadel
diff --git a/textclient/Makefile.in b/textclient/Makefile.in
deleted file mode 100644 (file)
index 12a9372..0000000
+++ /dev/null
@@ -1,214 +0,0 @@
-# Makefile for Citadel
-#
-# NOTE: normally you should not have to modify the Makefile.  All
-# system-dependent configuration is in the "configure" script, which
-# uses "Makefile.in" to generate a "Makefile".  In the rare instance
-# that you have to modify something here, please take note:
-# 1. Edit Makefile.in, -not- Makefile.
-# 2. Send e-mail to ajc@uncensored.citadel.org and let me know what you
-#    did, so any necessary changes can be put into the next release.
-#
-########################################################################
-
-prefix=@prefix@
-srcdir=@srcdir@
-VPATH=$(srcdir)
-
-TARGETS=@TARGETS@
-RUN_DIR=@MAKE_RUN_DIR@
-SPOOL_DIR=@MAKE_SPOOL_DIR@
-ETC_DIR=@MAKE_ETC_DIR@
-DATA_DIR=@MAKE_DATA_DIR@
-STATICDATA_DIR=@MAKE_STATICDATA_DIR@
-HELP_DIR=@MAKE_HELP_DIR@
-DOC_DIR=@MAKE_DOC_DIR@
-UTILBIN_DIR=@MAKE_UTILBIN_DIR@
-DEPEND_FLAG=@DEPEND_FLAG@
-all: buildinfo $(TARGETS)
-
-.SUFFIXES: .o .d .c
-
-EXEEXT=@EXEEXT@
-
-CLIENT_TARGETS=citadel$(EXEEXT)
-
-ACLOCAL=@ACLOCAL@
-AUTOCONF=@AUTOCONF@
-chkpwd_LIBS=@chkpwd_LIBS@
-CC=@CC@
-CFLAGS=@CFLAGS@ -I ./include/
-CPPFLAGS=@CPPFLAGS@ -I. -I ./src/include/
-DATABASE=@DATABASE@
-DEFS=@DEFS@ -DDIFF=\"@DIFF@\" -DPATCH=\"@PATCH@\"
-LDFLAGS=@LDFLAGS@
-LIBS=@LIBS@
-LIBOBJS=@LIBOBJS@
-INSTALL=@INSTALL@
-INSTALL_DATA=@INSTALL_DATA@
-RESOLV=@RESOLV@
-SHELL=/bin/sh
-SERVER_LDFLAGS=@SERVER_LDFLAGS@
-SERVER_LIBS=@SERVER_LIBS@
-SETUP_LIBS=@SETUP_LIBS@
-YACC=@YACC@
-DIFF=@DIFF@
-PATCH=@PATCH@
-LOCALEDIR=@LOCALEDIR@
-
-# End configuration section
-
-.SILENT:
-
-
-SOURCES=src/client_chat.c \
-       src/client_passwords.c \
-       src/commands.c \
-       src/messages.c \
-       src/rooms.c \
-       src/routines.c \
-       src/routines2.c \
-       src/tuiconfig.c \
-       src/citadel.c \
-       src/citadel_ipc.c \
-       src/ecrash.c \
-       src/ipc_c_tcp.c \
-       src/getutline.c \
-       src/screen.c
-
-# for VPATH builds (invoked by configure)
-mkdir-init:
-       mkdir -p textclient
-       mkdir locale
-
-
-noinst: $(NOINST_TARGETS)
-client: $(CLIENT_TARGETS)
-
-citadel$(EXEEXT): src/citadel.o  \
-       src/client_chat.o \
-       src/client_passwords.o \
-       src/commands.o \
-       src/md5.o \
-       src/messages.o \
-       src/rooms.o \
-       src/routines.o \
-       src/routines2.o \
-       src/tuiconfig.o \
-       src/screen.o \
-       src/ecrash.o \
-       src/citadel_ipc.o \
-       src/ipc_c_tcp.o \
-       $(LIBOBJS)
-       $(CC) src/citadel.o \
-       src/client_chat.o \
-       src/client_passwords.o \
-       src/commands.o \
-       src/md5.o \
-       src/messages.o \
-       src/rooms.o \
-       src/routines.o \
-       src/routines2.o \
-       src/tuiconfig.o \
-       src/screen.o \
-       src/ecrash.o \
-       src/citadel_ipc.o \
-       src/ipc_c_tcp.o \
-       $(LIBOBJS) \
-       $(LDFLAGS) -o citadel $(LIBS)
-
-%.o: %.c ${HEADERS}
-       echo "CC $<"
-       $(CC) $(CFLAGS) $(CPPFLAGS) $(DEFS) -c $< -o $@
-
-.PHONY: install-data install-doc install-exec clean cleaner distclean
-
-install-locale:
-
-install: install-exec install-data install-doc install-locale
-       @echo 
-       @echo Installation is complete.
-       @echo 
-
-install-new: install-exec-new install-data-new install-doc-new install-locale
-       @echo 
-       @echo Installation is complete.
-       @echo 
-
-upgrade: install-exec install-doc
-       @echo
-       @echo Upgrade is complete.
-       @echo
-
-install-data:
-       @for i in citadel.rc \
-                `find $(srcdir)/help $(srcdir)/messages $(srcdir)/network -type f | grep -v .svn`; do \
-               echo $(INSTALL_DATA) $$i $(DESTDIR)$(prefix)/$$i; \
-               $(INSTALL_DATA) $$i $(DESTDIR)$(prefix)/$$i; \
-       done
-
-install-data-new:
-       $(srcdir)/mkinstalldirs $(DESTDIR)$(ETC_DIR)/
-       $(INSTALL_DATA) $(srcdir)/citadel.rc $(DESTDIR)$(ETC_DIR)/citadel.rc
-
-install-doc:
-       @$(srcdir)/mkinstalldirs $(DESTDIR)$(prefix)/docs
-
-install-doc-new:
-       @$(srcdir)/mkinstalldirs $(DESTDIR)$(DOC_DIR)/docs
-
-install-exec: all
-       @for i in $(CLIENT_TARGETS) ; do \
-               if test -f $$i; then \
-                       echo $(INSTALL) $$i $(DESTDIR)$(prefix)/$$i; \
-                       $(INSTALL) $$i $(DESTDIR)$(prefix)/$$i; \
-               fi \
-       done
-
-install-exec-new: all
-       $(srcdir)/mkinstalldirs $(DESTDIR)/usr/bin; 
-       @for i in $(CLIENT_TARGETS); do \
-               if test -f $$i; then \
-                       echo $(INSTALL) $$i $(DESTDIR)/usr/bin/$$i; \
-                       $(INSTALL) $$i $(DESTDIR)/usr/bin/$$i; \
-               fi \
-       done
-
-clean:
-       rm -fr locale/*
-       rm -f *.o 
-       rm -f $(CLIENT_TARGETS) 
-
-cleaner: clean
-       rm -rf $(CLIENT_TARGETS)
-
-distclean: cleaner
-       find . -name '*~' -o -name '.#*' | xargs rm -f
-       rm -f po/Makefile 
-       rm -f Makefile sysdep.h config.cache config.log config.status *.d 
-
-
-.c.d:
-       @echo Checking dependencies for $<
-       @$(CC) $(DEPEND_FLAG) $(CPPFLAGS) $< | sed -e 's!$*.o!$*.o $*/.o $@!' > $@
-       @test -s $@ || rm -f $@
-
-Makefile: $(srcdir)/Makefile.in config.status
-       CONFIG_FILES=Makefile CONFIG_HEADERS= $(SHELL) ./config.status
-
-config.status: $(srcdir)/configure
-       $(SHELL) ./config.status --recheck
-
-$(srcdir)/configure: $(srcdir)/configure.ac $(srcdir)/aclocal.m4
-       cd $(srcdir) && $(AUTOCONF)
-
-$(srcdir)/aclocal.m4: $(srcdir)/acinclude.m4
-       cd $(srcdir) && $(ACLOCAL)
-
-buildinfo:
-       echo
-       echo "Dependencies: $(CC) $(DEPEND_FLAG) $(CPPFLAGS) $< | sed -e 's!$*.o!$*.o $*/.o $@!' > $@"
-       echo "Complie: $(CC) $(CFLAGS) $(CPPFLAGS) $(DEFS) -c $< -o $@ "
-       echo "LDFLAGS: $(LDFLAGS)"
-       echo
-
--include $(DEP_FILES)
diff --git a/textclient/README.txt b/textclient/README.txt
new file mode 100644 (file)
index 0000000..a80489f
--- /dev/null
@@ -0,0 +1,10 @@
+
+This is a text mode user interface for the Citadel system.  It presents
+a Citadel site to users in the form of a traditional BBS.
+
+All code is Copyright (c) 1987-2018 by the citadel.org team, and is released
+under the terms of the GNU General Public License v3.  As a special exception,
+the Citadel team requires all users of this code to agree that our favorite
+software model is called "open source" and NOT "free software", and that our
+favorite operating system is called "Linux" and NOT "GNU/Linux".
+
diff --git a/textclient/acinclude.m4 b/textclient/acinclude.m4
deleted file mode 100644 (file)
index 0941acf..0000000
+++ /dev/null
@@ -1,50 +0,0 @@
-# CIT_STRUCT_TM
-# ------------------
-# Figure out how to get the current GMT offset.  If `struct tm' has a
-# `tm_gmtoff' member, define `HAVE_STRUCT_TM_TM_GMTOFF'.  Otherwise, if the
-# external variable `timezone' is found, define `HAVE_TIMEZONE'.
-AC_DEFUN([CIT_STRUCT_TM],
-[AC_REQUIRE([AC_STRUCT_TM])dnl
-AC_CHECK_MEMBERS([struct tm.tm_gmtoff],,,[#include <sys/types.h>
-#include <$ac_cv_struct_tm>
-])
-if test "$ac_cv_member_struct_tm_tm_gmtoff" != yes; then
-  AC_CACHE_CHECK(for timezone, ac_cv_var_timezone,
-[AC_TRY_LINK(
-[#include <time.h>],
-[printf("%ld", (long)timezone);], ac_cv_var_timezone=yes, ac_cv_var_timezone=no)])
-  if test $ac_cv_var_timezone = yes; then
-    AC_DEFINE(HAVE_TIMEZONE, 1,
-              [Define if you don't have `tm_gmtoff' but do have the external
-               variable `timezone'.])
-  fi
-fi
-])# CIT_STRUCT_TM
-
-AC_DEFUN([AC_CHECK_DB],[
-for lib in $1
-do
-   AS_VAR_PUSHDEF([ac_tr_db], [ac_cv_db_lib_${lib}])dnl
-   bogo_saved_LIBS="$LIBS"
-   LIBS="$LIBS -l$lib"
-   AC_CACHE_CHECK([for db_create in -l${lib}], ac_tr_db,
-      [AC_TRY_LINK([#include <db.h>], [int foo=db_create((void *)0, (void *) 0, 
-0 )],
-                   [AS_VAR_SET(ac_tr_db, yes)],
-                   [AS_VAR_SET(ac_tr_db, no)])
-      ])
-   AS_IF([test AS_VAR_GET(ac_tr_db) = yes],
-         [$2
-         LIBS="$bogo_saved_LIBS"
-         SERVER_LIBS="$SERVER_LIBS -l$lib"
-          db=yes],
-         [LIBS="$bogo_saved_LIBS"
-          db=no])
-   AS_VAR_POPDEF([ac_tr_db])dnl
-test "$db" = "yes" && break
-done
-if test "$db" = "no"; then
-$3
-fi
-])# AC_CHECK_DB
-
diff --git a/textclient/aclocal.m4 b/textclient/aclocal.m4
deleted file mode 100644 (file)
index 440da76..0000000
+++ /dev/null
@@ -1,106 +0,0 @@
-# generated automatically by aclocal 1.14.1 -*- Autoconf -*-
-
-# Copyright (C) 1996-2013 Free Software Foundation, Inc.
-
-# This file is free software; the Free Software Foundation
-# gives unlimited permission to copy and/or distribute it,
-# with or without modifications, as long as this notice is preserved.
-
-# This program is distributed in the hope that it will be useful,
-# but WITHOUT ANY WARRANTY, to the extent permitted by law; without
-# even the implied warranty of MERCHANTABILITY or FITNESS FOR A
-# PARTICULAR PURPOSE.
-
-m4_ifndef([AC_CONFIG_MACRO_DIRS], [m4_defun([_AM_CONFIG_MACRO_DIRS], [])m4_defun([AC_CONFIG_MACRO_DIRS], [_AM_CONFIG_MACRO_DIRS($@)])])
-# AM_AUX_DIR_EXPAND                                         -*- Autoconf -*-
-
-# Copyright (C) 2001-2013 Free Software Foundation, Inc.
-#
-# This file is free software; the Free Software Foundation
-# gives unlimited permission to copy and/or distribute it,
-# with or without modifications, as long as this notice is preserved.
-
-# For projects using AC_CONFIG_AUX_DIR([foo]), Autoconf sets
-# $ac_aux_dir to '$srcdir/foo'.  In other projects, it is set to
-# '$srcdir', '$srcdir/..', or '$srcdir/../..'.
-#
-# Of course, Automake must honor this variable whenever it calls a
-# tool from the auxiliary directory.  The problem is that $srcdir (and
-# therefore $ac_aux_dir as well) can be either absolute or relative,
-# depending on how configure is run.  This is pretty annoying, since
-# it makes $ac_aux_dir quite unusable in subdirectories: in the top
-# source directory, any form will work fine, but in subdirectories a
-# relative path needs to be adjusted first.
-#
-# $ac_aux_dir/missing
-#    fails when called from a subdirectory if $ac_aux_dir is relative
-# $top_srcdir/$ac_aux_dir/missing
-#    fails if $ac_aux_dir is absolute,
-#    fails when called from a subdirectory in a VPATH build with
-#          a relative $ac_aux_dir
-#
-# The reason of the latter failure is that $top_srcdir and $ac_aux_dir
-# are both prefixed by $srcdir.  In an in-source build this is usually
-# harmless because $srcdir is '.', but things will broke when you
-# start a VPATH build or use an absolute $srcdir.
-#
-# So we could use something similar to $top_srcdir/$ac_aux_dir/missing,
-# iff we strip the leading $srcdir from $ac_aux_dir.  That would be:
-#   am_aux_dir='\$(top_srcdir)/'`expr "$ac_aux_dir" : "$srcdir//*\(.*\)"`
-# and then we would define $MISSING as
-#   MISSING="\${SHELL} $am_aux_dir/missing"
-# This will work as long as MISSING is not called from configure, because
-# unfortunately $(top_srcdir) has no meaning in configure.
-# However there are other variables, like CC, which are often used in
-# configure, and could therefore not use this "fixed" $ac_aux_dir.
-#
-# Another solution, used here, is to always expand $ac_aux_dir to an
-# absolute PATH.  The drawback is that using absolute paths prevent a
-# configured tree to be moved without reconfiguration.
-
-AC_DEFUN([AM_AUX_DIR_EXPAND],
-[AC_REQUIRE([AC_CONFIG_AUX_DIR_DEFAULT])dnl
-# Expand $ac_aux_dir to an absolute path.
-am_aux_dir=`cd "$ac_aux_dir" && pwd`
-])
-
-# Fake the existence of programs that GNU maintainers use.  -*- Autoconf -*-
-
-# Copyright (C) 1997-2013 Free Software Foundation, Inc.
-#
-# This file is free software; the Free Software Foundation
-# gives unlimited permission to copy and/or distribute it,
-# with or without modifications, as long as this notice is preserved.
-
-# AM_MISSING_PROG(NAME, PROGRAM)
-# ------------------------------
-AC_DEFUN([AM_MISSING_PROG],
-[AC_REQUIRE([AM_MISSING_HAS_RUN])
-$1=${$1-"${am_missing_run}$2"}
-AC_SUBST($1)])
-
-# AM_MISSING_HAS_RUN
-# ------------------
-# Define MISSING if not defined so far and test if it is modern enough.
-# If it is, set am_missing_run to use it, otherwise, to nothing.
-AC_DEFUN([AM_MISSING_HAS_RUN],
-[AC_REQUIRE([AM_AUX_DIR_EXPAND])dnl
-AC_REQUIRE_AUX_FILE([missing])dnl
-if test x"${MISSING+set}" != xset; then
-  case $am_aux_dir in
-  *\ * | *\    *)
-    MISSING="\${SHELL} \"$am_aux_dir/missing\"" ;;
-  *)
-    MISSING="\${SHELL} $am_aux_dir/missing" ;;
-  esac
-fi
-# Use eval to expand $SHELL
-if eval "$MISSING --is-lightweight"; then
-  am_missing_run="$MISSING "
-else
-  am_missing_run=
-  AC_MSG_WARN(['missing' script is too old or missing])
-fi
-])
-
-m4_include([acinclude.m4])
index d4c23fcf2749d8804e3b0563a4eb55fd3b2f9448..c52d3c26b35fd40696a3bd45f3aab0663ed0d4d9 100755 (executable)
@@ -1,34 +1,3 @@
 #!/bin/sh
-#
-# run me after checking Citadel out of the source code repository.
 
-# Remove any vestiges of pre-6.05 build environments
-rm -f .libs modules *.so *.lo *.la 2>/dev/null
-
-echo ... running aclocal ...
-aclocal  -I m4
-
-echo ... running autoconf ...
-autoconf
-
-# If your autoconf version changes, the autom4te.cache stuff will mess you up.
-# Get rid of it.
-echo ... removing autoheader cache files ...
-rm -rf autom4te*.cache
-
-echo ... running autoheader ...
-autoheader
-
-echo ... running mk_svn_revision.sh ...
-./scripts/mk_svn_revision.sh
-
-echo
-echo This script has been tested with autoconf 2.53 and
-echo automake 1.5. Other versions may work, but we recommend
-echo the latest echo compatible versions of these.
-echo
-echo Also note that autoconf and automake should be configured
-echo with the same prefix.
-echo
-
-grep '^#define CLIENT_VERSION' src/include/citadel_ipc.h | sed 's/[^0-9]*//g' >package-version.txt
+exit 0
diff --git a/textclient/buildpackages b/textclient/buildpackages
deleted file mode 100755 (executable)
index c784a48..0000000
+++ /dev/null
@@ -1,122 +0,0 @@
-#!/bin/bash
-# find out the package version from conf
-if test -f Makefile; then
-    make distclean
-fi
-
-#./bootstrap
-
-export `grep PACKAGE_VERSION= configure |sed -e "s;';;g" -e "s;PACKAGE;CITADEL;"`
-
-PACKAGE_VERSION=`cat packageversion`
-DATE=`date '+%a, %d %b %Y %H:%I:00 %z'`
-ACTUAL_DIR=`pwd`
-
-
-rm -rf debian/textclient debian/citadel-common debian/citadel-doc debian/citadel-mta debian/citadel-server debian/citadel-suite debian/tmp
-if echo "$ACTUAL_DIR" |grep -q "$CITADEL_VERSION"; then
-       echo "directory ($ACTUAL_DIR) naming scheme seems right. nothing done."
-else
-       done=false
-       if test -L "$ACTUAL_DIR"; then 
-               SYMLINK_=`pwd`
-               SYMLINK=`ls -l $SYMLINK_|sed "s;.*-> ;;"`
-               if ls -l $SYMLINK_|grep -q "$CITADEL_VERSION"; then
-                       done=true
-               fi
-       else
-               SYMLINK=`pwd|sed "s;.*/;;"`
-       fi
-       if test "$done" = "false"; then 
-               cd ..
-               if test ! -L "textclient-$CITADEL_VERSION"; then
-                       ln -sf textclient "textclient-$CITADEL_VERSION"
-               fi
-               cd "textclient-$CITADEL_VERSION"
-       else
-               cd "../textclient-$CITADEL_VERSION"
-       fi
-       
-fi
-
-
-case $1 in
-    debian)
-       if grep -q "($CITADEL_VERSION" debian/changelog; then
-           echo rebuilding package.
-       else
-           echo "Upstream Version higher than local."
-           
-       fi
-       if test "$2" == "src"; then
-           cd ..
-           rm -rf tmp
-           mkdir tmp
-           cp -rL textclient-$CITADEL_VERSION tmp
-           cd tmp/textclient-$CITADEL_VERSION
-           rm -rf `find -name .svn ` svn*tmp config.log config.status
-           find -type f -exec chmod a-x {} \;
-           chmod a+x configure *.sh *.sh.in debian/rules debian/*inst* debian/rules mkinstalldirs
-           cd ..
-           tar -chzf textclient_${CITADEL_VERSION}.orig.tar.gz textclient-${CITADEL_VERSION}/ --exclude "debian/*"
-           pwd
-           cd  textclient-${CITADEL_VERSION}; debuild -S -sa -kw.goesgens@outgesourced.org
-       else
-           fakeroot dpkg-buildpackage
-       fi
-       ;;
-    csw)
-       if !test -d ~/pkgs/; then
-           mkdir ~/pkgs
-       fi
-       echo "
-PKG=CSWcitadel-textclient
-NAME=citadel - The groupware server for Web 2.0
-VERSION=${PACKAGE_VERSION}
-CATEGORY=application
-VENDOR=http://www.citadel.org/  packaged for CSW by Wilfried Goesgens
-HOTLINE=https://uncensored.citadel.org/ Room citadel support
-EMAIL=citadel@outgesourced.org
-" >~/pkgs/citadel
-       export LDFLAGS='-L/opt/csw/lib -L /usr/local/lib'
-       export CFLAGS='-I/opt/csw/include -I/usr/local/include  -DDISABLE_CURSES'
-       ./configure \
-           --with-db=/opt/csw/bdb44 \
-           --with-ical=/usr/local/ \
-           --with-prefix=/opt/csw/ \
-           --with-datadir=/opt/csw/var/lib/citadel \
-           --with-sysconfdir=/opt/csw/etc/citadel \
-           --with-ssldir=/opt/csw/etc/ssl/citadel/ \
-           --with-spooldir=/opt/csw/var/spool/citadel \
-           --with-rundir=/opt/csw/var/run/citadel \
-           --with-docdir=/opt/csw/share/doc/citadel-doc/ \
-           --with-pam \
-           --with-zlib \
-           --with-ldap \
-           --with-libsieve
-           
-       gmake citserver aidepost msgform citmail userlist sendcommand base64 whobbs citadel
-       gmake DESTDIR=$ACTUAL_DIR/cswstage install-new
-       
-
-
-       ;;
-    sourcedist)
-       cd ..; tar \
-           --exclude ".gitignore" \
-           --exclude "*.lo" \
-           --exclude "*.o" \
-           --exclude "*.d" \
-           --exclude "autom4te.cache/*" \
-           --exclude "debian/*" \
-           --exclude "sysdep.h" \
-           \
-           -chvzf textclient-$CITADEL_VERSION.tar.gz textclient-$CITADEL_VERSION/
-       ;;
-       version)
-               echo This would build textclient-$CITADEL_VERSION
-               ;;
-       *)
-               echo "Not yet implemented. we have: debian "
-               ;;
-esac
diff --git a/textclient/citadel.c b/textclient/citadel.c
new file mode 100644 (file)
index 0000000..d01f6fe
--- /dev/null
@@ -0,0 +1,2152 @@
+/*
+ * Main source module for the client program.
+ *
+ * Copyright (c) 1987-2018 by the citadel.org team
+ *
+ * This program is open source software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 3.
+ *
+ * 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.
+ */
+
+#include "textclient.h"
+
+#define IFEXPERT if (userflags&US_EXPERT)
+#define IFNEXPERT if ((userflags&US_EXPERT)==0)
+#define IFAIDE if (axlevel>=AxAideU)
+#define IFNAIDE if (axlevel<AxAideU)
+
+int rordercmp(struct ctdlroomlisting *r1, struct ctdlroomlisting *r2);
+march *marchptr = NULL;
+extern char *moreprompt;
+
+/* 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 printcmd[SIZ];            /* print command */
+int editor_pid = (-1);
+char fullname[USERNAME_SIZE];
+unsigned room_flags;
+unsigned room_flags2;
+int entmsg_ok = 0;
+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 = AxDeleted;              /* access level */
+char is_room_aide = 0;         /* boolean flag, 1 if room admin */
+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 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;
+
+
+/*
+ * 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
+ */
+       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, 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) {
+               scr_printf("%s\n", buf);
+               return;
+       }
+
+       scr_printf("       User Name           Num  L Last Visit Logins Messages\n");
+       scr_printf("------------------------- ----- - ---------- ------ --------\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) {
+                       scr_printf("%-25s ", fl);
+                       scr_printf("%5ld %d ", extract_long(buf, 2),
+                              extract_int(buf, 1));
+                       lc = extract_long(buf, 3);
+                       localtime_r(&lc, &tmbuf);
+                       scr_printf("%02d/%02d/%04d ",
+                              (tmbuf.tm_mon + 1),
+                              tmbuf.tm_mday,
+                              (tmbuf.tm_year + 1900));
+                       scr_printf("%6ld %8ld\n", extract_long(buf, 4), extract_long(buf, 5));
+                   }
+
+               }
+       }
+       free(listing);
+       scr_printf("\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 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);
+               }
+       }
+       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;
+
+       /* Determine, based on the room's default view, whether an <E>nter message
+        * command will be valid here.
+        */
+       switch(room->RRdefaultview) {
+               case VIEW_BBS:
+               case VIEW_MAILBOX:
+               case VIEW_BLOG:
+                                       entmsg_ok = 1;
+                                       break;
+               default:
+                                       entmsg_ok = 0;
+                                       break;
+       }
+
+       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);
+                       if (rv) 
+                               scr_printf("*** failed to check for mail calling %s Reason %d.\n",
+                                          rc_gotmail_cmd, rv);
+               }
+       }
+       free(room);
+
+       if (screenwidth>5) snprintf(&status_line[1], screenwidth-1, "%s  |  %s  |  %s  |  %s  |  %d new mail  |",
+               (secure ? "Encrypted" : "Unencrypted"),
+               ipc->ServInfo.humannode,
+               ipc->ServInfo.site_location,
+               room_name,
+               newmailcount
+       );
+}
+
+/* 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];
+
+       /* 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) {
+               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]);
+       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);
+       }
+
+       /* 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) {
+               scr_printf("%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("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_path)) {
+       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;
+
+       /* get #users, #active & server uptime */
+       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# */
+       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)CLIENT_VERSION/100);
+       scr_printf("server build %s,\n", ipc->ServInfo.svn_revision, (float)CLIENT_VERSION/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);
+}
+
+/*
+ * 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);
+}
+
+
+/*
+ * 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);
+       moreprompt = ipc->ServInfo.moreprompt;
+
+       /* be nice and identify ourself to the server */
+       CtdlIPCIdentifySoftware(ipc, CLIENT_TYPE, 0, CLIENT_VERSION,
+                (ipc->isLocal ? "local" : "Citadel text mode client"),
+                (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);
+       }
+}
+
+
+
+/*
+ * Session username compare function for SortOnlineUsers()
+ */
+int rwho_username_cmp(const void *rec1, const void *rec2) {
+       char *u1, *u2;
+
+       u1 = strchr(rec1, '|');
+       u2 = strchr(rec2, '|');
+
+       return strcasecmp( (u1?++u1:"") , (u2?++u2:"") );
+}
+
+
+/*
+ * Idle time 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.
+ *
+ * If 'condense' is nonzero, multiple sessions for the same user will be
+ * combined into one for brevity.
+ */
+char *SortOnlineUsers(char *listing, int condense) {
+       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);
+       }
+
+       /* Sort by idle time */
+       qsort(sortbuf, rows, SIZ, idlecmp);
+
+       /* Combine multiple sessions for the same user */
+       if (condense) {
+               qsort(sortbuf, rows, SIZ, rwho_username_cmp);
+               if (rows > 1) for (i=1; i<rows; ++i) if (i>0) {
+                       char u1[USERNAME_SIZE];
+                       char u2[USERNAME_SIZE];
+                       extract_token(u1, &sortbuf[(i-1)*SIZ], 1, '|', sizeof u1);
+                       extract_token(u2, &sortbuf[i*SIZ], 1, '|', sizeof u2);
+                       if (!strcasecmp(u1, u2)) {
+                               memcpy(&sortbuf[i*SIZ], &sortbuf[(i+1)*SIZ], (rows-i-1)*SIZ);
+                               --rows;
+                               --i;
+                       }
+               }
+
+               qsort(sortbuf, rows, SIZ, idlecmp);     /* idle sort again */
+       }
+
+       /* Copy back to a \n delimited list */
+       strcpy(retbuf, "");
+       for (i=0; i<rows; ++i) {
+               if (!IsEmptyStr(&sortbuf[i*SIZ])) {
+                       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);
+               scr_printf("           User Name               Room          ");
+               if (screenwidth >= 80) scr_printf(" Idle        From host");
+               scr_printf("\n");
+               color(DIM_WHITE);
+               scr_printf("   ------------------------- --------------------");
+               if (screenwidth >= 80) scr_printf(" ---- ------------------------");
+               scr_printf("\n");
+       }
+       r = CtdlIPCOnlineUsers(ipc, &listing, &timenow, buf);
+       listing = SortOnlineUsers(listing, (!longlist));
+       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);
+
+                               scr_printf("  Flags: %s\n", flags);
+                               scr_printf("Session: %d\n", extract_int(buf, 0));
+                               scr_printf("   Name: %s\n", username);
+                               scr_printf("In room: %s\n", roomname);
+                               scr_printf("   Host: %s\n", fromhost);
+                               scr_printf(" Client: %s\n", clientsoft);
+                               scr_printf("   Idle: %ld:%02ld:%02ld\n",
+                                       (long) idlehours,
+                                       (long) idlemins,
+                                       (long) idlesecs);
+
+                               if ( (!IsEmptyStr(actual_user)&&
+                                     !IsEmptyStr(actual_room)&&
+                                     !IsEmptyStr(actual_host))) {
+                                       scr_printf("(really ");
+                                       if (!IsEmptyStr(actual_user)) scr_printf("<%s> ", actual_user);
+                                       if (!IsEmptyStr(actual_room)) scr_printf("in <%s> ", actual_room);
+                                       if (!IsEmptyStr(actual_host)) scr_printf("from <%s> ", actual_host);
+                                       scr_printf(")\n");
+                               }
+                               scr_printf("\n");
+
+                       } else {
+                               if (isidle == 0) {
+                                       if (extract_int(buf, 0) == last_session) {
+                                               scr_printf("        ");
+                                       }
+                                       else {
+                                               color(BRIGHT_MAGENTA);
+                                               scr_printf("%-3s", flags);
+                                       }
+                                       last_session = extract_int(buf, 0);
+                                       color(BRIGHT_CYAN);
+                                       scr_printf("%-25s ", username);
+                                       color(BRIGHT_MAGENTA);
+                                       roomname[20] = 0;
+                                       scr_printf("%-20s", roomname);
+
+                                       if (screenwidth >= 80) {
+                                               scr_printf(" ");
+                                               if (idletime > rc_idle_threshold) {
+                                                       /* over 1000d, must be gone fishing */
+                                                       if (idlehours > 23999) {
+                                                               scr_printf("fish");
+                                                       /* over 10 days */
+                                                       } else if (idlehours > 239) {
+                                                               scr_printf("%3ldd", idlehours / 24);
+                                                       /* over 10 hours */
+                                                       } else if (idlehours > 9) {
+                                                               scr_printf("%1ldd%02ld",
+                                                                       idlehours / 24,
+                                                                       idlehours % 24);
+                                                       /* less than 10 hours */
+                                                       }
+                                                       else {
+                                                               scr_printf("%1ld:%02ld", idlehours, idlemins);
+                                                       }
+                                               }
+                                               else {
+                                                       scr_printf("    ");
+                                               }
+                                               scr_printf(" ");
+                                               color(BRIGHT_CYAN);
+                                               fromhost[24] = '\0';
+                                               scr_printf("%-24s", fromhost);
+                                       }
+                                       scr_printf("\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) {
+       scr_printf(s);
+}
+
+/*
+ * 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];
+       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; 
+       calc_dirs_n_files(relh, home, relhome, ctdldir, 0);
+
+#ifdef HAVE_BACKTRACE
+       bzero(&params, sizeof(params));
+       params.debugLevel = ECRASH_DEBUG_VERBOSE;
+       params.dumpAllThreads = TRUE;
+       params.useBacktraceSymbols = 1;
+       params.signals[0]=SIGSEGV;
+       params.signals[1]=SIGILL;
+       params.signals[2]=SIGBUS;
+       params.signals[3]=SIGABRT;
+#endif 
+       setIPCErrorPrintf(scr_printf);
+       setCryptoStatusHook(statusHook);
+       
+       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(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
+
+       /* 
+        * 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], "-p")) {
+                       // ignore this argument when called from telnetd
+                       argc = shift(argc, argv, a, 1);
+               }
+       }
+       
+       screen_new();
+       /* Get screen dimensions.  First we go to a default of 80x24.
+        * Then attempt to read the actual screen size from the terminal.
+        */
+       check_screen_dims();
+
+
+#ifdef __CYGWIN__
+       newprompt("Connect to (return for local server): ", hostbuf, 64);
+#endif
+
+       scr_printf("Attaching to server...\n");
+       ipc = CtdlIPC_new(argc, argv, hostbuf, portbuf);
+       if (!ipc) {
+               error_printf("Can't connect: %s\n", strerror(errno));
+               logoff(NULL, 3);
+       }
+
+       CtdlIPC_SetNetworkStatusCallback(ipc, scr_wait_indicator);
+
+       if (!(ipc->isLocal)) {
+               scr_printf("Connected to %s [%s].\n", ipc->ip_hostname, ipc->ip_address);
+       }
+
+       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_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);
+                               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;
+       }
+
+       /* FIXME this is a stupid way to do guest mode but it's a reasonable test harness FIXME */
+       if ( (ipc->ServInfo.guest_logins) && (!strcasecmp(fullname, "guest")) ) {
+               goto PWOK;
+       }
+
+       /* 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, -(SIZ-1));
+       }
+
+       if (*nonce) {
+               r = CtdlIPCTryApopPassword(ipc, make_apop_string(password, nonce, hexstring, sizeof hexstring), aaa);
+               if (r / 100 != 2) {
+                       strproc(password);
+                       r = CtdlIPCTryApopPassword(ipc, make_apop_string(password, nonce, hexstring, sizeof hexstring), aaa);
+               }
+       } else {
+               r = CtdlIPCTryPassword(ipc, password, aaa);
+               if (r / 100 != 2) {
+                       strproc(password);
+                       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 (rv)
+                                       scr_printf("*** failed to check for mail calling %s Reason %d.\n",
+                                                  rc_gotmail_cmd, rv);
+
+                       }
+               }
+               if ((axlevel >= AxAideU) && (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);
+
+       r = CtdlIPCGetConfig(ipc, &myself, aaa);
+       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();            /* get screen size */
+#endif
+
+               if (termn8 == 0)
+                       switch (mcmd) {
+                       case 1:
+                               display_help(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 */
+                               gotonext(ipc);
+                               break;
+                       case 90:                        /* <.A>bandon */
+                               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)) {
+                                       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:
+                               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 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(argbuf, "?");
+                               }
+                               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) {
+                                       updatels(ipc);
+                                       a = 0;
+                                       termn8 = 1;
+                               }
+                               break;
+
+                       case 85:
+                               scr_printf("All users will be disconnected!  "
+                                          "Really terminate the server? ");
+                               if (yesno() == 1) {
+                                       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 6:
+                               gotonext(ipc);
+                               break;
+
+                       case 3:
+                               chatmode(ipc);
+                               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 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:
+                               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) {
+               scr_printf("\n\nType 'off' to disconnect, or next user...\n");
+       }
+       CtdlIPCLogout(ipc);
+       if ((mcmd == 29) || (mcmd == 15)) {
+               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/textclient/citadel_ipc.c b/textclient/citadel_ipc.c
new file mode 100644 (file)
index 0000000..da0f7b9
--- /dev/null
@@ -0,0 +1,3351 @@
+/*
+ * Copyright (c) 1987-2018 by the citadel.org team
+ *
+ *  This program is open source software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License version 3.
+ *
+ *  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.
+ */
+
+#include "textclient.h"
+
+#ifdef HAVE_OPENSSL
+static SSL_CTX *ssl_ctx;
+char arg_encrypt;
+char rc_encrypt;
+
+#endif /* HAVE_OPENSSL */
+
+#ifndef INADDR_NONE
+#define INADDR_NONE 0xffffffff
+#endif
+
+static void (*status_hook)(char *s) = NULL;
+char ctdl_autoetc_dir[PATH_MAX]="";
+char file_citadel_rc[PATH_MAX]="";
+char ctdl_run_dir[PATH_MAX]="";
+char ctdl_etc_dir[PATH_MAX]="";
+char ctdl_home_directory[PATH_MAX] = "";
+char file_citadel_socket[PATH_MAX]="";
+
+char *viewdefs[]={
+        "Messages",
+        "Summary",
+        "Address book",
+        "Calendar",
+        "Tasks"
+};
+
+char *axdefs[]={
+        "Deleted",
+        "New User",
+        "Problem User",
+        "Local User",
+        "Network User",
+        "Preferred User",
+        "Admin",
+        "Admin"
+        };
+
+
+void CtdlIPC_lock(CtdlIPC *ipc)
+{
+       if (ipc->network_status_cb) ipc->network_status_cb(1);
+}
+
+
+void CtdlIPC_unlock(CtdlIPC *ipc)
+{
+       if (ipc->network_status_cb) ipc->network_status_cb(0);
+}
+
+#ifdef __cplusplus
+}
+#endif
+
+
+char *libcitadelclient_version_string(void) {
+        return "libcitadelclient(unnumbered)";
+}
+
+
+
+
+#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] = "";
+
+       StripSlashes(ctdldir, 1);
+
+#ifndef HAVE_RUN_DIR
+       basedir=ctdldir;
+#else
+       basedir=RUN_DIR;
+#endif
+       COMPUTE_DIRECTORY(ctdl_run_dir);
+       StripSlashes(ctdl_run_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_etc_dir);
+       StripSlashes(ctdl_etc_dir, 1);
+
+
+
+       snprintf(file_citadel_rc, 
+                        sizeof file_citadel_rc,
+                        "%scitadel.rc",
+                        ctdl_etc_dir);
+       StripSlashes(file_citadel_rc, 0);
+
+       snprintf(file_citadel_socket, 
+                        sizeof file_citadel_socket,
+                               "%scitadel.socket",
+                        ctdl_run_dir);
+       StripSlashes(file_citadel_socket, 0);
+
+       DBG_PRINT(ctdl_run_dir);
+       DBG_PRINT(file_citadel_socket);
+       DBG_PRINT(ctdl_etc_dir);
+       DBG_PRINT(file_citadel_rc);
+}
+
+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);
+#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)
+{
+       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)
+{
+       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)
+{
+       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)
+{
+       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)
+{
+       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)
+{
+       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)
+{
+       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)
+{
+       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)
+{
+       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)
+{
+       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]->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 80|24|%d",
+               uret->flags
+       );
+       return CtdlIPCGenericCommand(ipc, aaa, NULL, 0, NULL, NULL, cret);
+}
+
+
+/* RENU */
+int CtdlIPCRenameUser(CtdlIPC *ipc, char *oldname, char *newname, char *cret)
+{
+       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)
+{
+       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]->RRcurrentview = extract_int(cret, 11);
+               rret[0]->RRdefaultview = extract_int(cret, 12);
+               /* position 13 is a trash folder flag ... irrelevant in this client */
+               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)
+{
+       int ret;
+       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)
+{
+       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, "room=", 5))
+                                       safestrncpy(mret[0]->room, &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)
+{
+       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)
+{
+       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;
+                       case 24:        ipc->ServInfo.guest_logins = atoi(buf);
+                                       break;
+                       }
+               }
+
+       }
+       if (listing) free(listing);
+       return ret;
+}
+
+
+/* RDIR */
+int CtdlIPCReadDirectory(CtdlIPC *ipc, char **listing, char *cret)
+{
+       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)
+{
+       int ret;
+       char aaa[64];
+
+       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)
+{
+       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)
+{
+       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)
+{
+       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)
+{
+       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)
+{
+       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)
+{
+       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[64];
+
+       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)
+{
+       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[64];
+
+       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)
+{
+       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)
+{
+       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)
+{
+       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)
+{
+       int ret;
+       char *aaa;
+
+       if (!cret) return -2;
+       if (!username) return -2;
+       if (axlevel < AxDeleted || axlevel > AxAideU) 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[64];
+
+       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)
+{
+       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)
+{
+       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)
+{
+       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)
+{
+       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)
+{
+       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)
+{
+       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)
+{
+       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)
+{
+       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(MimeType) + 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)
+{
+       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)
+{
+       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)
+{
+       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)
+{
+       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)
+{
+       int ret;
+       char *aaa;
+
+       if (developerid < 0 || clientid < 0 || revision < 0 ||
+           !software_name) {
+               developerid = 8;
+               clientid = 0;
+               revision = CLIENT_VERSION - 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)
+{
+       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[64];
+
+       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)
+{
+       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[64];
+
+       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[64];
+
+       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)
+{
+       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)
+{
+       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)
+{
+       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)
+{
+       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)
+{
+       time_t tret;
+       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)
+{
+       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)
+{
+       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;
+}
+
+
+/* AGEA */
+int CtdlIPCAideGetEmailAddresses(CtdlIPC *ipc, const char *who, char *target_buf, char *cret)
+{
+       int ret;
+       char aaa[SIZ];
+       char *emailaddrs = NULL;
+       size_t emailaddrs_len = 0;
+
+       sprintf(aaa, "AGEA %s", who);
+       ret = CtdlIPCGenericCommand(ipc, aaa, NULL, 0, &emailaddrs, &emailaddrs_len, cret);
+
+       if (ret / 100 == 1) {
+               strcpy(target_buf, emailaddrs);
+       }
+
+       if (emailaddrs != NULL) {
+               free(emailaddrs);
+       }
+
+       return ret;
+}
+
+
+/* ASEA */
+int CtdlIPCAideSetEmailAddresses(CtdlIPC *ipc, const char *who, char *emailaddrs, char *cret)
+{
+       char aaa[SIZ];
+       int ret;
+
+       if (!who) return -2;
+       if (!emailaddrs) return -2;
+       if (!cret) return -2;
+
+       sprintf(aaa, "ASEA %s", who);
+       ret = CtdlIPCGenericCommand(ipc, aaa, emailaddrs, 0, NULL, NULL, cret);
+       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, GPEXWhichPolicy which,
+               struct ExpirePolicy **policy, char *cret)
+{
+       static char *proto[] = {
+               strof(roompolicy),
+               strof(floorpolicy),
+               strof(sitepolicy),
+               strof(mailboxespolicy)
+       };
+       char cmd[256];
+       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)
+{
+       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)
+{
+       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[64];
+
+       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, (const unsigned char*) "Citadel SID", 14);
+#endif
+
+       /* 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 */
+       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;
+
+       error_printf("Encrypting with %s cipher %s\n",
+               SSL_CIPHER_get_version(SSL_get_current_cipher(ipc->ssl)),
+               SSL_CIPHER_get_name(SSL_get_current_cipher(ipc->ssl))
+       );
+       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)
+{
+       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);
+}
+
+
+
+
+/* ************************************************************************** */
+/*          Stuff below this line is not for public consumption            */
+/* ************************************************************************** */
+
+
+/* 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)
+{
+       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)
+{
+       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) {
+       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)
+{
+       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) {
+               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)
+{
+       size_t len;
+       int calls;      /* How many calls in the pipeline */
+       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)
+{
+       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)
+{
+       int ret = -1;
+       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) {
+               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];
+       int ret;
+
+       if (!command) return -2;
+       if (!proto_response) return -2;
+
+       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;
+}
+
+
+/*
+ * Connect to a Citadel on a remote host using a TCP/IP socket
+ */
+static int tcp_connectsock(char *host, char *service)
+{
+       struct in6_addr serveraddr;
+       struct addrinfo hints;
+       struct addrinfo *res = NULL;
+       struct addrinfo *ai = NULL;
+       int rc = (-1);
+       int sock = (-1);
+
+       if ((host == NULL) || IsEmptyStr(host)) {
+               service = DEFAULT_HOST ;
+       }
+       if ((service == NULL) || IsEmptyStr(service)) {
+               service = DEFAULT_PORT ;
+       }
+
+       memset(&hints, 0x00, sizeof(hints));
+       hints.ai_flags = AI_NUMERICSERV;
+       hints.ai_family = AF_UNSPEC;
+       hints.ai_socktype = SOCK_STREAM;
+
+       /*
+        * Handle numeric IPv4 and IPv6 addresses
+        */
+       rc = inet_pton(AF_INET, host, &serveraddr);
+       if (rc == 1) {                                          /* dotted quad */
+               hints.ai_family = AF_INET;
+               hints.ai_flags |= AI_NUMERICHOST;
+       }
+       else {
+               rc = inet_pton(AF_INET6, host, &serveraddr);
+               if (rc == 1) {                                  /* IPv6 address */
+                       hints.ai_family = AF_INET6;
+                       hints.ai_flags |= AI_NUMERICHOST;
+               }
+       }
+
+       /* Begin the connection process */
+
+       rc = getaddrinfo(host, service, &hints, &res);
+       if (rc != 0) {
+               return(-1);
+       }
+
+       /*
+        * Try all available addresses until we connect to one or until we run out.
+        */
+       for (ai = res; ai != NULL; ai = ai->ai_next) {
+               sock = socket(ai->ai_family, ai->ai_socktype, ai->ai_protocol);
+               if (sock < 0) return(-1);
+
+               rc = connect(sock, ai->ai_addr, ai->ai_addrlen);
+               if (rc >= 0) {
+                       return(sock);           /* Connected! */
+               }
+               else {
+                       close(sock);            /* Failed.  Close the socket to avoid fd leak! */
+               }
+       }
+
+       return(-1);
+}
+
+
+
+
+
+/*
+ * Connect to a Citadel on the local host using a unix domain socket
+ */
+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;
+       }
+}
+
+
+
+
+static void CtdlIPC_init_OpenSSL(void)
+{
+       int a;
+       const 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 (!(DH_generate_parameters_ex(dh, 128, DH_GENERATOR_2, 0))) {
+               error_printf("Can't generate DH parameters: %s\n", ERR_reason_error_string(ERR_get_error()));
+               DH_free(dh);
+               return;
+       }
+
+       SSL_CTX_set_tmp_dh(ssl_ctx, dh);
+       DH_free(dh);
+}
+
+#endif /* HAVE_OPENSSL */
+
+
+int
+ReadNetworkChunk(CtdlIPC* ipc)
+{
+       fd_set read_fd;
+       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);
+               
+               if (ret > 0) {
+                       
+                       *(ipc->BufPtr) = '\0';
+                       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 = malloc(sizeof(struct _CtdlIPC));
+       if (!ipc) {
+               return 0;
+       }
+#if defined(HAVE_OPENSSL)
+       ipc->ssl = NULL;
+       CtdlIPC_init_OpenSSL();
+#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 */
+       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]);
+                       free(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, "%s", file_citadel_socket);
+               }
+               else {
+                       snprintf(sockpath, sizeof sockpath, "%s/%s", citport, "citadel.socket");
+               }
+               printf("[%s]\n", sockpath);
+               ipc->sock = uds_connectsock(&(ipc->isLocal), sockpath);
+               if (ipc->sock == -1) {
+                       free(ipc);
+                       return 0;
+               }
+               if (hostbuf != NULL) strcpy(hostbuf, cithost);
+               if (portbuf != NULL) strcpy(portbuf, sockpath);
+               strcpy(ipc->ip_hostname, "");
+               strcpy(ipc->ip_address, "");
+               return ipc;
+       }
+
+       printf("[%s:%s]\n", cithost, citport);
+       ipc->sock = tcp_connectsock(cithost, citport);
+       if (ipc->sock == -1) {
+               free(ipc);
+               return 0;
+       }
+
+
+       /* Learn the actual network identity of the host to which we are connected */
+
+       struct sockaddr_in6 clientaddr;
+       unsigned int addrlen = sizeof(clientaddr);
+
+       ipc->ip_hostname[0] = 0;
+       ipc->ip_address[0] = 0;
+
+       getpeername(ipc->sock, (struct sockaddr *)&clientaddr, &addrlen);
+       getnameinfo((struct sockaddr *)&clientaddr, addrlen,
+               ipc->ip_hostname, sizeof ipc->ip_hostname, NULL, 0, 0
+       );
+       getnameinfo((struct sockaddr *)&clientaddr, addrlen,
+               ipc->ip_address, sizeof ipc->ip_address, NULL, 0, NI_NUMERICHOST
+       );
+
+       /* stuff other things elsewhere */
+
+       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;
+       free(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/textclient/client_chat.c b/textclient/client_chat.c
new file mode 100644 (file)
index 0000000..e725f02
--- /dev/null
@@ -0,0 +1,270 @@
+/*
+ * front end for multiuser chat
+ *
+ * Copyright (c) 1987-2016 by the citadel.org team
+ *
+ * This program is open source software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 3.
+ *
+ * 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.
+ */
+
+#include "textclient.h"
+
+#define MIN(a, b) ((a) < (b) ? (a) : (b))
+
+extern char temp[];
+char last_paged[SIZ] = "";
+
+void chatmode(CtdlIPC *ipc)
+{
+       char wbuf[SIZ];
+       char buf[SIZ];
+       char response[SIZ];
+       char c_user[SIZ];
+       char c_text[SIZ];
+       char last_user[SIZ];
+       int send_complete_line;
+       char ch;
+       int a, pos;
+       int seq = 0;
+
+       fd_set rfds;
+       struct timeval tv;
+       int retval;
+
+       CtdlIPC_chat_send(ipc, "RCHT enter");
+       CtdlIPC_chat_recv(ipc, buf);
+       if (buf[0] != '2') {
+               scr_printf("%s\n", &buf[4]);
+               return;
+       }
+       scr_printf("Entering chat mode (type /quit to exit)\n");
+
+       strcpy(buf, "");
+       strcpy(wbuf, "");
+       strcpy(last_user, ""); 
+       color(BRIGHT_YELLOW);
+       scr_printf("\n");
+       scr_printf("> ");
+       send_complete_line = 0;
+
+       while (1) {
+               scr_flush();
+               FD_ZERO(&rfds);
+               FD_SET(0, &rfds);
+               tv.tv_sec = 1;
+               tv.tv_usec = 0;
+               retval = select(1, &rfds, NULL, NULL, &tv);
+
+               if (retval < 0) {
+                       color(BRIGHT_WHITE);
+                       scr_printf("Server gone Exiting chat mode\n");
+                       scr_flush();
+                       return;
+               }
+
+               /* 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;
+                                       scr_printf("%c %c", 8, 8);
+                               }
+                       } else {
+                               scr_putc(ch);
+                               wbuf[strlen(wbuf) + 1] = 0;
+                               wbuf[strlen(wbuf)] = ch;
+                       }
+               }
+
+               /* if the user hit return, send the line */
+               if (send_complete_line) {
+
+                       if (!strcasecmp(wbuf, "/quit")) {
+                               CtdlIPC_chat_send(ipc, "RCHT exit");
+                               CtdlIPC_chat_recv(ipc, response);       /* don't care about the result */
+                               color(BRIGHT_WHITE);
+                               scr_printf("\rExiting chat mode\n");
+                               scr_flush();
+                               return;
+                       }
+
+                       CtdlIPC_chat_send(ipc, "RCHT send");
+                       CtdlIPC_chat_recv(ipc, response);
+                       if (response[0] == '4') {
+                               CtdlIPC_chat_send(ipc, wbuf);
+                               CtdlIPC_chat_send(ipc, "000");
+                       }
+                       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, "RCHT send");
+                               CtdlIPC_chat_recv(ipc, response);
+                               if (response[0] == '4') {
+                                       CtdlIPC_chat_send(ipc, wbuf);
+                                       CtdlIPC_chat_send(ipc, "000");
+                               }
+                               strcpy(wbuf, "");
+                               send_complete_line = 0;
+                       } else {
+                               wbuf[pos] = 0;
+                               CtdlIPC_chat_send(ipc, "RCHT send");
+                               CtdlIPC_chat_recv(ipc, response);
+                               if (response[0] == '4') {
+                                       CtdlIPC_chat_send(ipc, wbuf);
+                                       CtdlIPC_chat_send(ipc, "000");
+                               }
+                               strcpy(wbuf, &wbuf[pos + 1]);
+                       }
+               }
+
+               /* poll for incoming chat messages */
+               snprintf(buf, sizeof buf, "RCHT poll|%d", seq);
+               CtdlIPC_chat_send(ipc, buf);
+               CtdlIPC_chat_recv(ipc, response);
+       
+               if (response[0] == '1') {
+                       seq = extract_int(&response[4], 0);
+                       extract_token(c_user, &response[4], 2, '|', sizeof c_user);
+                       while (CtdlIPC_chat_recv(ipc, c_text), strcmp(c_text, "000")) {
+                               scr_printf("\r%79s\r", "");
+                               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)) {
+                                       scr_printf("\r%79s\n", "");
+                                       strcpy(last_user, c_user);
+                               }
+                               scr_printf("\r%s\n", buf);
+                               scr_flush();
+                       }
+               }
+               color(BRIGHT_YELLOW);
+               scr_printf("\r> %s", wbuf);
+               scr_flush();
+               strcpy(buf, "");
+       }
+}
+
+
+/*
+ * 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/textclient/client_passwords.c b/textclient/client_passwords.c
new file mode 100644 (file)
index 0000000..2337736
--- /dev/null
@@ -0,0 +1,138 @@
+/*
+ * Functions which allow the client to remember usernames and passwords for
+ * various sites.
+ *
+ * Copyright (c) 1987-2016 by the citadel.org team
+ *
+ *  This program is open source software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License version 3.
+ *
+ *  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.
+ */
+
+#include "textclient.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/textclient/commands.c b/textclient/commands.c
new file mode 100644 (file)
index 0000000..9a68488
--- /dev/null
@@ -0,0 +1,1864 @@
+/*
+ * This file contains functions which implement parts of the
+ * text-mode user interface.
+ *
+ * Copyright (c) 1987-2018 by the citadel.org team
+ *
+ * This program is open source software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 3.
+ *
+ * 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.
+ */
+
+#include "textclient.h"
+
+char *helpnames[] = {
+       "help",
+       "admin",
+       "floors",
+       "intro",
+       "mail",
+       "network",
+       "software"
+};
+
+char *helptexts[] = {
+
+"                          Citadel Help Menu\n"
+"  \n"
+" ?         Help. (Typing a '?' will give you a menu almost anywhere)\n"
+" A         Abandon this room where you stopped reading, goto next room.\n"
+" C         Chat (multiuser chat, where available)\n"
+" D         Prints directory, if there is one in the current room.\n"
+" E         Enter a message.\n"
+" F         Read all messages in the room, forward.\n"
+" G         Goto next room which has UNREAD messages.\n"
+" H         Help. Same as '?'\n"
+" I         Reads the Information file for this room.\n"
+" K         List of Known rooms.\n"
+" L         Reads the last five messages in the room.\n"
+" N         Reads all new messages in the room.\n"
+" O         Reads all old messages, backwards.\n"
+" P         Page another user (send an instant message)\n"
+" R         Reads all messages in the room, in reverse order.\n"
+" S         Skips current room without making its messages old.\n"
+" T         Terminate (logout)\n"
+" U         Ungoto (returns to the last room you were in)\n"
+" W         Displays who is currently logged in.\n"
+" X         Toggle eXpert mode (menus and help blurbs on/off)\n"
+" Z         Zap (forget) room. (Removes the room from your list)\n"
+" + -       Goto next, previous room on current floor.\n"
+" > <       Goto next, previous floor.\n"
+" *         Enter any locally installed 'doors'.\n"
+"   \n"
+" In addition, there are dot commands. You hit the . (dot), then press the\n"
+"first letter of each word of the command. As you hit the letters, the words\n"
+"pop onto your screen. Exceptions: after you hit .Help or .Goto, the remainder\n"
+"of the command is a help file name or room name.\n"
+"    \n"
+"      *** USE  .<H>elp ?    for additional help *** \n"
+
+,
+
+"The following commands are available only to Admins.  A subset of these\n"
+"commands are available to room aides when they are currently in the room\n"
+"they are room aide for.\n"
+"\n"
+" <.> <A>dmin <K>ill this room   (Delete the current room)\n"
+" <.> <A>dmin <E>dit this room   (Edit the current room's parameters)\n"
+" <.> <A>dmin <W>ho knows room   (List users with access to this room)\n"
+" <.> <A>dmin edit <U>ser        (Change user's access level, password, etc.)\n"
+" <.> <A>dmin <V>alidate new users   (Process new user registrations)\n"
+" <.> <A>dmin enter <I>nfo file      (Create/change this room's banner)\n"
+" <.> <A>dmin <R>oom <I>nvite user   (Grant access to an invitation-only room)\n"
+" <.> <A>dmin <R>oom <K>ick out user (Revoke access to an invitation-only room)\n"
+" <.> <A>dmin <F>ile <D>elete        (Delete a file from the room's directory)\n"
+" <.> <A>dmin <F>ile <S>end over net (Transmit a file to another node)\n"
+" <.> <A>dmin <F>ile <M>ove          (Move a file to another room's directory)\n"
+" <.> <A>dmin <M>essage edit:        (Edit system banners)\n"
+" <.> <A>dmin <P>ost                 (Post a message on behalf of another user)\n"
+" <.> <A>dmin <S>ystem configuration <G>eneral   (Edit global site config)\n"
+" <.> <A>dmin <S>ystem configuration <I>nternet  (Edit Internet domains)\n"
+" <.> <A>dmin <S>ystem configuration check <M>essage base   (Internal checks)\n"
+" <.> <A>dmin <S>ystem configuration <N>etwork   (Netting with other Citadels)\n"
+" <.> <A>dmin <S>ystem configuration network <F>ilter list\n"
+" <.> <A>dmin <T>erminate server <N>ow          (Shut down Citadel server now)\n"
+" <.> <A>dmin <T>erminate server <S>cheduled    (Shut down Citadel server later)\n"
+" <.> <A>dmin mailing <L>ist recipients         (For mailing list rooms)\n"
+" <.> <A>dmin mailing list <D>igest recipients  (For mailing list rooms)\n"
+" <.> <A>dmin <N>etwork room sharing     (Replication with other Citadels)\n"
+" \n"
+" In addition, the <M>ove and <D>elete commands are available at the\n"
+"message prompt.\n"
+
+,
+
+" Floors\n"
+" ------\n"
+"   Floors in Citadel are used to group rooms into related subject areas,\n"
+"just as rooms are used to group messages into manageable groups.\n"
+" \n"
+"   You, as a user, do NOT have to use floors.  If you choose not to, you suffer\n"
+"no penalty; you will not lose access to any rooms.  You may use .EC or ;C (the\n"
+"latter is easier to use) to decide if you want to use floors.  Feel free to\n"
+"experiment.\n"
+" \n"
+"   Floor options are accessed two ways.  First, if you are in floor mode, the\n"
+"<G>oto and <S>kip commands take you to the next room with new messages on the\n"
+"current floor; if there are none left, then the system will automatically\n"
+"switch floors (and let you know) and put you in the first room with new messages\n"
+"on that level.  (Notice that your pattern of basic use of Citadel therefore\n"
+"doesn't really change.)\n"
+" \n"
+"   Direct access to floor options is via the use of a ';' command.\n"
+"The following commands are currently available (more can be\n"
+"added if needed):\n"
+" \n"
+" <;C>onfigure\n"
+" This command toggles your floor mode.\n"
+" \n"
+" <;G>oto FLOORNAME\n"
+" This command causes the system to take you to the named floor.\n"
+" \n"
+" <;K>nown rooms on floors\n"
+" List all rooms on all floors.  This is a very readable way to get a list of\n"
+"all rooms on the system.\n"
+" \n"
+" <;S>kip FLOORNAME\n"
+" This command causes the system to mark all rooms on the current floor as\n"
+"Skipped and takes you to the floor that you specify.\n"
+" \n"
+" <;Z>Forget floor\n"
+"   This command causes you to forget all the rooms currently on the current\n"
+"floor.  Unfortunately, it doesn't apply to rooms that are subsequently created\n"
+"or moved to this floor.  (Sorry.)\n"
+" \n"
+"   Feel free to experiment, you can't hurt yourself or the system with the\n"
+"floor stuff unless you ZForget a floor by accident.\n"
+
+,
+
+"                  New User's Introduction to the site\n"
+"  \n"
+" This is an introduction to the Citadel BBS concept.  It is intended\n"
+"for new users so that they can more easily become acquainted to using\n"
+"Citadel when accessing it in the form of a text-based BBS.  Of\n"
+"course, old users might learn something new each time they read\n"
+"through it.\n"
+" \n"
+" Full help for the BBS commands can be obtained by typing <.H>elp SUMMARY\n"
+"  \n"
+" The CITADEL BBS room concept\n"
+" ----------------------------\n"
+"   The term BBS stands for 'Bulletin Board System'.  The analogy is\n"
+"appropriate: one posts messages so that others may read them.  In\n"
+"order to organize the posts, people can post in different areas of the\n"
+"BBS, called rooms.\n"
+"   In order to post in a certain room, you need to be 'in' that room.\n"
+"Your current prompt is usually the room that you are in, followed the\n"
+"greater-than-sign, such as:\n"
+" \n"
+" Lobby>\n"
+" \n"
+" The easiest way to traverse the room structure is with the 'Goto'\n"
+"command, on the 'G' key.  Pressing 'G' will take you to the next room\n"
+"in the 'march list' (see below) that has new messages in it.  You can\n"
+"read these new messages with the 'N' key.\n"
+" Once you've 'Gotoed' every room in the system (or all of the ones\n"
+"you choose to read) you return to the 'Lobby,' the first and last room\n"
+"in the system.  If new messages get posted to rooms you've already\n"
+"read during your session you will be brought BACK to those rooms so\n"
+"you can read them.\n"
+" \n"
+" March List\n"
+" ----------\n"
+"   All the room names are stored in a march list, which is just a\n"
+"list containing all the room names.  When you <G>oto or <S>kip a\n"
+"room, you are placed in the next room in your march list THAT HAS NEW\n"
+"MESSAGES.  If you have no new messages in any of the rooms on your\n"
+"march list, you will keep going to the Lobby>.  You can choose not to\n"
+"read certain rooms (that don't interest you) by 'Z'apping them.  When\n"
+"you <Z>ap a room, you are merely deleting it from your march list (but\n"
+"not from anybody else's).\n"
+" \n"
+"   You can use the <.G>oto (note the period before the G.  You can also use\n"
+"<J>ump on some systems) to go to any room in the\n"
+"system.  You don't have to type in the complete name of a room to\n"
+"'jump' to it; you merely need to type in enough to distinguish it from\n"
+"the other rooms.  Left-aligned matches carry a heavier weight, so if you\n"
+"typed (for example) '.Goto TECH', you might be taken to a room called\n"
+"'Tech Area>' even if it found a room called 'Biotech/Ethics>' first.\n"
+" \n"
+"  To return to a room you have previously <Z>apped, use the <.G>oto command\n"
+"to enter it, and it will be re-inserted into your march list.  In the case\n"
+"of returning to Zapped rooms, you must type the room name in its entirety.\n"
+"REMEMBER, rooms with no new messages will not show on your\n"
+"march list!  You must <.G>oto to a room with no new messages.\n"
+"Incidentally, you cannot change the order of the rooms on your march list.\n"
+"It's the same for everybody.\n"
+" \n"
+" Special rooms\n"
+" -------------\n"
+"   There are two special rooms on a Citadel that you should know about.\n"
+"  \n"
+"   The first is the Lobby>.  It's used for system announcements and other\n"
+"such administrativia.  You cannot <Z>ap the Lobby>.  Each time you first\n"
+"login, you will be placed in the Lobby>.\n"
+" \n"
+"   The second is Mail>.  In Mail>, when you post a messages, you are\n"
+"prompted to enter the screen name of the person who you want to send the\n"
+"message to.  Only the person who you send the message to can read the\n"
+"message.  NO ONE else can read it, not even the admins.  Mail> is the\n"
+"first room on the march list, and is un-<Z>appable, so you can be sure\n"
+"that the person will get the message.\n"
+"   \n"
+" System admins\n"
+" -------------\n"
+"   These people, along with the room admins, keep the site running smoothly.\n"
+"\n"
+"   Among the many things that admins do are: create rooms, delete\n"
+"rooms, set access levels, invite users, check registration, grant\n"
+"room admin status, and countless other things.  They have access to the\n"
+"Aide> room, a special room only for admins.\n"
+" \n"
+"   If you enter a mail message to 'Sysop' it will be placed in the\n"
+"Aide> room so that the next admin online will read it and deal with it.\n"
+"Admins cannot <Z>ap rooms.  All the rooms are always on each admin's\n"
+"march list.  Admins can read *any* and *every* room, but they *CAN* *NOT*\n"
+"read other users' Mail!\n"
+"  \n"
+" Room admins\n"
+" -----------\n"
+"   Room admins are granted special privileges in specific rooms.\n"
+"They are *NOT* true system admins; their power extends only over the\n"
+"rooms that they control, and they answer to the system admins.\n"
+"  \n"
+"   A room admin's job is to keep the topic of the their room on track,\n"
+"with nudges in the right direction now and then.  A room admin can also\n"
+"move an off topic post to another room, or delete a post, if he/she\n"
+"feels it is necessary. \n"
+"  \n"
+"   Currently, very few rooms have room admins.  Most rooms do not need\n"
+"their own specific room admin.  Being a room admin requires a certain\n"
+"amount of trust, due to the additional privileges granted.\n"
+"  \n"
+" Citadel messages\n"
+" ----------------\n"
+"   Most of the time, the BBS code does not print a lot of messages\n"
+"to your screen.  This is a great benefit once you become familiar\n"
+"with the system, because you do not have endless menus and screens\n"
+"to navigate through.  nevertheless, there are some messages which you\n"
+"might see from time to time.\n"
+"  \n"
+"  'There were messages posted while you were entering.'\n"
+"  \n"
+"   This is also known as 'simulposting.'  When you start entering a \n"
+"message, the system knows where you last left off.  When you save\n"
+"your message, the system checks to see if any messages were entered\n"
+"while you were typing.  This is so that you know whether you need\n"
+"to go back and re-read the last few messages.  This message may appear\n"
+"in any room.\n"
+"   \n"
+" '*** You have new mail'\n"
+"  \n"
+"   This message is essentially the same as the above message, but can\n"
+"appear at any time.  It simply means that new mail has arrived for you while\n"
+"you are logged in.  Simply go to the Mail> room to read it.\n"
+"  \n"
+" Who list\n"
+" --------\n"
+"   The <W>ho command shows you the names of all users who are currently\n"
+"online.  It also shows you the name of the room they are currently in.  If\n"
+"they are in any type of private room, however, the room name will simply\n"
+"display as '<private room>'.  Along with this information is displayed the\n"
+"name of the host computer the user is logged in from.\n"
+
+,
+
+"To send mail on this system, go to the Mail> room (using the command .G Mail)\n"
+"and press E to enter a message.  You will be prompted with:\n"
+" \n"
+" Enter Recipient:\n"
+" \n"
+"   At this point you may enter the name of another user on the system.  Private\n"
+"mail is only readable by the sender and recipient.  There is no need to delete\n"
+"mail after it is read; it will scroll out automatically.\n"
+"  \n"
+"   To send mail to another user on the Citadel network, simply type the\n"
+"user's name, followed by @ and then the system name. For example,\n"
+"  \n"
+" Enter Recipient: Joe Schmoe @ citadrool\n"
+"  \n"
+"  If your account is enabled for Internet mail, you can also send email to\n"
+"anyone on the Internet here.  Simply enter their address at the prompt:\n"
+"  \n"
+" Enter Recipient: ajc@herring.fishnet.com\n"
+
+,
+
+"  Welcome to the network. Messages entered in a network room will appear in\n"
+"that room on all other systems carrying it (The name of the room, however,\n"
+"may be different on other systems).\n"
+
+,
+
+"   Citadel is the premier 'online community' (i.e. Bulletin Board System)\n"
+"software.  It runs on all POSIX-compliant systems, including Linux.  It is an\n"
+"advanced client/server application, and is being actively maintained.\n"
+" \n"
+"   For more info, visit UNCENSORED! BBS at uncensored.citadel.org\n"
+
+,
+
+"Extended commands are available using the period ( . ) key. To use\n"
+"a dot command, press the . key, and then enter the first letter of\n"
+"each word in the command. The words will appear as you enter the keys.\n"
+"You can also backspace over partially entered commands. The following\n"
+"commands are available:\n"
+"\n"
+" <.> <H>elp:    Displays help files.  Type .H followed by a help file\n"
+"                name.  You are now reading <.H>elp SUMMARY\n"
+" \n"
+" <.> <G>oto:    Jumps directly to the room you specify.  You can also\n"
+"                type a partial room name, just enough to make it unique,\n"
+"                and it'll find the room you're looking for.  As with the\n"
+"                regular <G>oto command, messages in the current room will\n"
+"                be marked as read.\n"
+" \n"
+" <.> <S>kip, goto:    This is similar to <.G>oto, except it doesn't mark\n"
+"                      messages in the current room as read.\n"
+" \n"
+" <.> list <Z>apped rooms      Shows all rooms you've <Z>apped (forgotten)\n"
+"\n"
+"  \n"
+" Terminate (logoff) commands:\n"
+" \n"
+" <.> <T>erminate and <Q>uit               Log off and disconnect.\n"
+" <.> <T>erminate and <S>tay online        Log in as a different user.\n"
+" \n"
+" \n"
+" Read commands:\n"
+"\n"
+" <.> <R>ead <N>ew messages                Same as <N>ew\n"
+" <.> <R>ead <O>ld msgs reverse            Same as <O>ld\n"
+" <.> <R>ead <L>ast five msgs              Same as <L>ast5\n"
+" <.> read <L>ast:                         Allows you to specify how many\n"
+"                                          messages you wish to read.\n"
+"\n"
+" <.> <R>ead <U>ser listing:               Lists all users on the system if\n"
+"                                          you just hit enter, otherwise\n"
+"                                          you can specify a partial match\n"
+"\n"
+" <.> <R>ead <T>extfile formatted          File 'download' commands.\n"
+" <.> <R>ead file using <X>modem   \n"
+" <.> <R>ead file using <Y>modem   \n"
+" <.> <R>ead file using <Z>modem   \n"
+" <.> <R>ead <F>ile unformatted   \n"
+" <.> <R>ead <D>irectory   \n"
+"\n"
+" <.> <R>ead <I>nfo file                   Read the room info file.\n"
+" <.> <R>ead <B>io                         Read other users' 'bio' files.\n"
+" <.> <R>ead <C>onfiguration               Display your 'preferences'.\n"
+" <.> <R>ead <S>ystem info                 Display system statistics.\n"
+"\n"
+" \n"
+" Enter commands:\n"
+"\n"
+" <.> <E>nter <M>essage                    Post a message in this room.\n"
+" <.> <E>nter message with <E>ditor        Post using a full-screen editor.\n"
+" <.> <E>nter <A>SCII message              Post 'raw' (use this when 'pasting'\n"
+"                                          a message from your clipboard).\n"
+"\n"
+" <.> <E>nter <P>assword                   Change your password.\n"
+" <.> <E>nter <C>onfiguration              Change your 'preferences'.\n"
+" <.> <E>nter a new <R>oom                 Create a new room.\n"
+" <.> <E>nter re<G>istration               Register (name, address, etc.)\n"
+" <.> <E>nter <B>io                        Enter/change your 'bio' file.\n"
+"\n"
+" <.> <E>nter <T>extfile                   File 'upload' commands.\n"
+" <.> <E>nter file using <X>modem   \n"
+" <.> <E>nter file using <Y>modem   \n"
+" <.> <E>nter file using <Z>modem   \n"
+"  \n"
+"  \n"
+"  Wholist commands:\n"
+" \n"
+" <.> <W>holist <L>ong             Same as <W>ho is online, but displays\n"
+"                                  more detailed information.\n"
+" <.> <W>holist <R>oomname         Masquerade your room name (other users\n"
+"                                  see the name you enter rather than the\n"
+"                                  actual name of the room you're in)\n"
+" <.> <W>holist <H>ostname         Masquerade your host name\n"
+" <.> <E>nter <U>sername           Masquerade your user name (Admins only)\n"
+" <.> <W>holist <S>tealth mode     Enter/exit 'stealth mode' (when in stealth\n"
+"                                  mode you are invisible on the wholist)\n"
+" \n"
+" \n"
+" Floor commands (if using floor mode)\n"
+" ;<C>onfigure floor mode            - turn floor mode on or off\n"
+" ;<G>oto floor:                     - jump to a specific floor\n"
+" ;<K>nown rooms                     - list all rooms on all floors\n"
+" ;<S>kip to floor:                  - skip current floor, jump to another\n"
+" ;<Z>ap floor                       - zap (forget) all rooms on this floor\n"
+" \n"
+" \n"
+" Administrative commands: \n"
+" \n"
+" <.> <A>dmin <K>ill this room   \n"
+" <.> <A>dmin <E>dit this room   \n"
+" <.> <A>dmin <W>ho knows room   \n"
+" <.> <A>dmin edit <U>ser   \n"
+" <.> <A>dmin <V>alidate new users   \n"
+" <.> <A>dmin enter <I>nfo file   \n"
+" <.> <A>dmin <R>oom <I>nvite user  \n"
+" <.> <A>dmin <R>oom <K>ick out user  \n"
+" <.> <A>dmin <F>ile <D>elete  \n"
+" <.> <A>dmin <F>ile <S>end over net  \n"
+" <.> <A>dmin <F>ile <M>ove  \n"
+" <.> <A>dmin <M>essage edit:   \n"
+" <.> <A>dmin <P>ost   \n"
+" <.> <A>dmin <S>ystem configuration   \n"
+" <.> <A>dmin <T>erminate server <N>ow\n"
+" <.> <A>dmin <T>erminate server <S>cheduled\n"
+
+};
+
+
+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];
+
+int next_lazy_cmd = 5;
+
+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 ((retval > 0) && FD_ISSET(0, &rfds)) {
+               set_keepalives(KA_NO);
+               the_character = inkey();
+               set_keepalives(KA_YES);
+       }
+       else {
+               the_character = 0;
+       }
+       return(the_character);
+}
+
+
+
+
+
+/*
+ * 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");
+
+               /* 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");
+               fmout(screenwidth, NULL, listing, NULL, 0);
+               free(listing);
+
+       }
+       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) {
+
+       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) {
+               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");
+       }
+}
+
+/* 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 terminal sessions from idling out */
+       scr_printf(" %c", 8);
+       scr_flush();
+
+       really_do_keepalive();
+}
+
+
+
+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();
+       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 {
+                       do_keepalive();
+
+                       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;
+               }
+       } 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);
+               }
+       }
+}
+
+
+
+
+/*
+ * Function to read a line of text from the terminal.
+ *
+ * string              Pointer to string buffer
+ * lim                 Maximum length
+ * noshow              Echo asterisks instead of keystrokes?
+ * bs                  Allow backspacing out of the prompt? (returns -1 if this happens)
+ *
+ * returns: string length
+ */
+int ctdl_getline(char *string, int lim, int noshow, int bs)
+{
+       int pos = strlen(string);
+       int ch;
+
+       if (noshow && !IsEmptyStr(string)) {
+               int num_stars = strlen(string);
+               while (num_stars--) {
+                       scr_putc('*');
+               }
+       }
+       else {
+               scr_printf("%s", string);
+       }
+
+       while(1) {
+               ch = inkey();
+
+               if ((ch == 8)  && (pos > 0)) {                          /* backspace */
+                       --pos;
+                       scr_putc(8); scr_putc(32); scr_putc(8);
+               }
+
+               else if ((ch == 8) && (pos == 0) && (bs)) {             /* backspace out of the prompt */
+                       return(-1);
+               }
+
+               else if ((ch == 23) && (pos > 0)) {                     /* Ctrl-W deletes a word */
+                       while ((pos > 0) && !isspace(string[pos])) {
+                               --pos;
+                               scr_putc(8); scr_putc(32); scr_putc(8);
+                       }
+                       while ((pos > 0) && !isspace(string[pos-1])) {
+                               --pos;
+                               scr_putc(8); scr_putc(32); scr_putc(8);
+                       }
+               }
+
+               else if (ch == 10) {                                    /* return */
+                       string[pos] = 0;
+                       scr_printf("\n");
+                       return(pos);
+               }
+
+               else if (isprint(ch)) {                                 /* payload characters */
+                       scr_putc((noshow ? '*' : ch));
+                       string[pos] = ch;
+                       ++pos;
+               }
+       }
+}
+
+
+/* 
+ * newprompt()         prompt for a string, print the existing value, and
+ *                     allow the user to press return to keep it...
+ *                     If len is negative, pass the "noshow" flag to ctdl_getline()
+ */
+void strprompt(char *prompt, char *str, int len)
+{
+       print_instant();
+       color(DIM_WHITE);
+       scr_printf("%s", prompt);
+       color(DIM_WHITE);
+       scr_printf(": ");
+       color(BRIGHT_CYAN);
+       ctdl_getline(str, abs(len), (len<0), 0);
+       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)
+ *                     If len is negative, pass the "noshow" flag to ctdl_getline()
+ */
+void newprompt(char *prompt, char *str, int len)
+{
+       str[0] = 0;
+       color(BRIGHT_MAGENTA);
+       scr_printf("%s", prompt);
+       color(DIM_MAGENTA);
+       ctdl_getline(str, abs(len), (len<0), 0);
+       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];
+       struct citcmd *cptr;
+       struct citcmd *lastcmd = NULL;
+       int a, d;
+       int b = 0;
+
+       /* first, set up some defaults for non-required variables */
+
+       strcpy(editor_path, "");
+       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
+
+       /* 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 (!strncasecmp(buf, "editor=", 7)) {
+                       strcpy(editor_path, &buf[7]);
+               }
+
+               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, "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, "status_line=", 12)) {
+                       if (!strncasecmp(&buf[12], "on", 2))
+                               enable_status_line = 1;
+               }
+               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, "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 {
+               scr_printf("-----------------------------------------------------------------------\n");
+               scr_printf("Room cmds:    <K>nown rooms, <G>oto next room, <.G>oto a specific room,\n");
+               scr_printf("              <S>kip this room, <A>bandon this room, <Z>ap this room,\n");
+               scr_printf("              <U>ngoto (move back)\n");
+               scr_printf("Message cmds: <N>ew msgs, <F>orward read, <R>everse read, <O>ld msgs,\n");
+               scr_printf("              <L>ast five msgs, <E>nter a message\n");
+               scr_printf("General cmds: <?> help, <T>erminate, <C>hat, <W>ho is online\n");
+               scr_printf("Misc:         <X> toggle eXpert mode, <D>irectory\n");
+               scr_printf("\n");
+               scr_printf(" (Type .Help SUMMARY for extended commands, <X> to hide this menu)\n");
+               scr_printf("-----------------------------------------------------------------------\n");
+       }
+
+       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));
+
+       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)) {
+                                       argbuf[0] = 0;
+                                       ctdl_getline(argbuf, 64, 0, 0);
+                               } 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 == '?') {
+                       scr_printf("\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));
+                                  scr_printf(" ");
+                                       }
+                                       scr_printf("\n");
+                               }
+                       }
+               sigcaught = 0;
+
+                       scr_printf("\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) {
+                                               scr_printf("%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
+ */
+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;
+
+               /* 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);
+       }
+
+}
+
+
+// this is the old version which uses sgtty.h instead of termios.h
+#if 0
+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 text viewer
+ */
+void display_help(CtdlIPC *ipc, char *name)
+{
+       int i;
+       int num_helps = sizeof(helpnames) / sizeof(char *) ;
+
+       for (i=0; i<num_helps; ++i) {
+               if (!strcasecmp(name, helpnames[i])) {
+                       fmout(screenwidth, NULL, helptexts[i], NULL, 0);
+                       return;
+               }
+       }
+
+       scr_printf("'%s' not found.  Enter one of:\n", name);
+       for (i=0; i<num_helps; ++i) {
+               scr_printf("  %s\n", helpnames[i]);
+       }
+}
+
+
+/*
+ * 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 */
+       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) {
+               scr_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) {
+                       scr_printf("Can't print message: %s!\n",
+                                       strerror(errno));
+                       logoff(NULL, 3);
+               }
+       } else {
+               buffer = text;
+       }
+       e = buffer;
+
+       /* 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");
+                               }
+                               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");
+                               }
+                               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");
+                       }
+                       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");
+       }
+
+       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) {
+               /* 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);
+
+       }
+}
+
+void cls(int colornum)
+{
+       if (enable_color) {
+               printf("\033[4%dm\033[2J\033[H\033[0m",
+                               colornum ? colornum : rc_color_use_bg);
+       }
+}
+
+
+/*
+ * 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);
+                               if (rv < 0) {
+                                       scr_printf("failed to read after select: %s", 
+                                                  strerror(errno));
+                                       break;
+                               }
+                       }
+               } 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]=='<') {
+                       scr_printf("%c", buf[i]);
+                       color(BRIGHT_MAGENTA);
+               } else {
+                       if (buf[i]=='>'&& buf[i+1] != '>') {
+                               color(DIM_WHITE);
+                       }
+                       scr_printf("%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/textclient/config.guess b/textclient/config.guess
deleted file mode 100755 (executable)
index 396482d..0000000
+++ /dev/null
@@ -1,1500 +0,0 @@
-#! /bin/sh
-# Attempt to guess a canonical system name.
-#   Copyright (C) 1992, 1993, 1994, 1995, 1996, 1997, 1998, 1999,
-#   2000, 2001, 2002, 2003, 2004, 2005, 2006 Free Software Foundation,
-#   Inc.
-
-timestamp='2006-07-02'
-
-# This file is free software; you can redistribute it and/or modify it
-# under the terms of the GNU General Public License as published by
-# the Free Software Foundation; either version 2 of the License, or
-# (at your option) any later version.
-#
-# This program is distributed in the hope that it will be useful, but
-# WITHOUT ANY WARRANTY; without even the implied warranty of
-# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
-# General Public License for more details.
-#
-# You should have received a copy of the GNU General Public License
-# along with this program; if not, write to the Free Software
-# Foundation, Inc., 51 Franklin Street - Fifth Floor, Boston, MA
-# 02110-1301, USA.
-#
-# As a special exception to the GNU General Public License, if you
-# distribute this file as part of a program that contains a
-# configuration script generated by Autoconf, you may include it under
-# the same distribution terms that you use for the rest of that program.
-
-
-# Originally written by Per Bothner <per@bothner.com>.
-# Please send patches to <config-patches@gnu.org>.  Submit a context
-# diff and a properly formatted ChangeLog entry.
-#
-# This script attempts to guess a canonical system name similar to
-# config.sub.  If it succeeds, it prints the system name on stdout, and
-# exits with 0.  Otherwise, it exits with 1.
-#
-# The plan is that this can be called by configure scripts if you
-# don't specify an explicit build system type.
-
-me=`echo "$0" | sed -e 's,.*/,,'`
-
-usage="\
-Usage: $0 [OPTION]
-
-Output the configuration name of the system \`$me' is run on.
-
-Operation modes:
-  -h, --help         print this help, then exit
-  -t, --time-stamp   print date of last modification, then exit
-  -v, --version      print version number, then exit
-
-Report bugs and patches to <config-patches@gnu.org>."
-
-version="\
-GNU config.guess ($timestamp)
-
-Originally written by Per Bothner.
-Copyright (C) 1992, 1993, 1994, 1995, 1996, 1997, 1998, 1999, 2000, 2001, 2002, 2003, 2004, 2005
-Free Software Foundation, Inc.
-
-This is free software; see the source for copying conditions.  There is NO
-warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE."
-
-help="
-Try \`$me --help' for more information."
-
-# Parse command line
-while test $# -gt 0 ; do
-  case $1 in
-    --time-stamp | --time* | -t )
-       echo "$timestamp" ; exit ;;
-    --version | -v )
-       echo "$version" ; exit ;;
-    --help | --h* | -h )
-       echo "$usage"; exit ;;
-    -- )     # Stop option processing
-       shift; break ;;
-    - )        # Use stdin as input.
-       break ;;
-    -* )
-       echo "$me: invalid option $1$help" >&2
-       exit 1 ;;
-    * )
-       break ;;
-  esac
-done
-
-if test $# != 0; then
-  echo "$me: too many arguments$help" >&2
-  exit 1
-fi
-
-trap 'exit 1' 1 2 15
-
-# CC_FOR_BUILD -- compiler used by this script. Note that the use of a
-# compiler to aid in system detection is discouraged as it requires
-# temporary files to be created and, as you can see below, it is a
-# headache to deal with in a portable fashion.
-
-# Historically, `CC_FOR_BUILD' used to be named `HOST_CC'. We still
-# use `HOST_CC' if defined, but it is deprecated.
-
-# Portable tmp directory creation inspired by the Autoconf team.
-
-set_cc_for_build='
-trap "exitcode=\$?; (rm -f \$tmpfiles 2>/dev/null; rmdir \$tmp 2>/dev/null) && exit \$exitcode" 0 ;
-trap "rm -f \$tmpfiles 2>/dev/null; rmdir \$tmp 2>/dev/null; exit 1" 1 2 13 15 ;
-: ${TMPDIR=/tmp} ;
- { tmp=`(umask 077 && mktemp -d "$TMPDIR/cgXXXXXX") 2>/dev/null` && test -n "$tmp" && test -d "$tmp" ; } ||
- { test -n "$RANDOM" && tmp=$TMPDIR/cg$$-$RANDOM && (umask 077 && mkdir $tmp) ; } ||
- { tmp=$TMPDIR/cg-$$ && (umask 077 && mkdir $tmp) && echo "Warning: creating insecure temp directory" >&2 ; } ||
- { echo "$me: cannot create a temporary directory in $TMPDIR" >&2 ; exit 1 ; } ;
-dummy=$tmp/dummy ;
-tmpfiles="$dummy.c $dummy.o $dummy.rel $dummy" ;
-case $CC_FOR_BUILD,$HOST_CC,$CC in
- ,,)    echo "int x;" > $dummy.c ;
-       for c in cc gcc c89 c99 ; do
-         if ($c -c -o $dummy.o $dummy.c) >/dev/null 2>&1 ; then
-            CC_FOR_BUILD="$c"; break ;
-         fi ;
-       done ;
-       if test x"$CC_FOR_BUILD" = x ; then
-         CC_FOR_BUILD=no_compiler_found ;
-       fi
-       ;;
- ,,*)   CC_FOR_BUILD=$CC ;;
- ,*,*)  CC_FOR_BUILD=$HOST_CC ;;
-esac ; set_cc_for_build= ;'
-
-# This is needed to find uname on a Pyramid OSx when run in the BSD universe.
-# (ghazi@noc.rutgers.edu 1994-08-24)
-if (test -f /.attbin/uname) >/dev/null 2>&1 ; then
-       PATH=$PATH:/.attbin ; export PATH
-fi
-
-UNAME_MACHINE=`(uname -m) 2>/dev/null` || UNAME_MACHINE=unknown
-UNAME_RELEASE=`(uname -r) 2>/dev/null` || UNAME_RELEASE=unknown
-UNAME_SYSTEM=`(uname -s) 2>/dev/null`  || UNAME_SYSTEM=unknown
-UNAME_VERSION=`(uname -v) 2>/dev/null` || UNAME_VERSION=unknown
-
-# Note: order is significant - the case branches are not exclusive.
-
-case "${UNAME_MACHINE}:${UNAME_SYSTEM}:${UNAME_RELEASE}:${UNAME_VERSION}" in
-    *:NetBSD:*:*)
-       # NetBSD (nbsd) targets should (where applicable) match one or
-       # more of the tupples: *-*-netbsdelf*, *-*-netbsdaout*,
-       # *-*-netbsdecoff* and *-*-netbsd*.  For targets that recently
-       # switched to ELF, *-*-netbsd* would select the old
-       # object file format.  This provides both forward
-       # compatibility and a consistent mechanism for selecting the
-       # object file format.
-       #
-       # Note: NetBSD doesn't particularly care about the vendor
-       # portion of the name.  We always set it to "unknown".
-       sysctl="sysctl -n hw.machine_arch"
-       UNAME_MACHINE_ARCH=`(/sbin/$sysctl 2>/dev/null || \
-           /usr/sbin/$sysctl 2>/dev/null || echo unknown)`
-       case "${UNAME_MACHINE_ARCH}" in
-           armeb) machine=armeb-unknown ;;
-           arm*) machine=arm-unknown ;;
-           sh3el) machine=shl-unknown ;;
-           sh3eb) machine=sh-unknown ;;
-           *) machine=${UNAME_MACHINE_ARCH}-unknown ;;
-       esac
-       # The Operating System including object format, if it has switched
-       # to ELF recently, or will in the future.
-       case "${UNAME_MACHINE_ARCH}" in
-           arm*|i386|m68k|ns32k|sh3*|sparc|vax)
-               eval $set_cc_for_build
-               if echo __ELF__ | $CC_FOR_BUILD -E - 2>/dev/null \
-                       | grep __ELF__ >/dev/null
-               then
-                   # Once all utilities can be ECOFF (netbsdecoff) or a.out (netbsdaout).
-                   # Return netbsd for either.  FIX?
-                   os=netbsd
-               else
-                   os=netbsdelf
-               fi
-               ;;
-           *)
-               os=netbsd
-               ;;
-       esac
-       # The OS release
-       # Debian GNU/NetBSD machines have a different userland, and
-       # thus, need a distinct triplet. However, they do not need
-       # kernel version information, so it can be replaced with a
-       # suitable tag, in the style of linux-gnu.
-       case "${UNAME_VERSION}" in
-           Debian*)
-               release='-gnu'
-               ;;
-           *)
-               release=`echo ${UNAME_RELEASE}|sed -e 's/[-_].*/\./'`
-               ;;
-       esac
-       # Since CPU_TYPE-MANUFACTURER-KERNEL-OPERATING_SYSTEM:
-       # contains redundant information, the shorter form:
-       # CPU_TYPE-MANUFACTURER-OPERATING_SYSTEM is used.
-       echo "${machine}-${os}${release}"
-       exit ;;
-    *:OpenBSD:*:*)
-       UNAME_MACHINE_ARCH=`arch | sed 's/OpenBSD.//'`
-       echo ${UNAME_MACHINE_ARCH}-unknown-openbsd${UNAME_RELEASE}
-       exit ;;
-    *:ekkoBSD:*:*)
-       echo ${UNAME_MACHINE}-unknown-ekkobsd${UNAME_RELEASE}
-       exit ;;
-    *:SolidBSD:*:*)
-       echo ${UNAME_MACHINE}-unknown-solidbsd${UNAME_RELEASE}
-       exit ;;
-    macppc:MirBSD:*:*)
-       echo powerpc-unknown-mirbsd${UNAME_RELEASE}
-       exit ;;
-    *:MirBSD:*:*)
-       echo ${UNAME_MACHINE}-unknown-mirbsd${UNAME_RELEASE}
-       exit ;;
-    alpha:OSF1:*:*)
-       case $UNAME_RELEASE in
-       *4.0)
-               UNAME_RELEASE=`/usr/sbin/sizer -v | awk '{print $3}'`
-               ;;
-       *5.*)
-               UNAME_RELEASE=`/usr/sbin/sizer -v | awk '{print $4}'`
-               ;;
-       esac
-       # According to Compaq, /usr/sbin/psrinfo has been available on
-       # OSF/1 and Tru64 systems produced since 1995.  I hope that
-       # covers most systems running today.  This code pipes the CPU
-       # types through head -n 1, so we only detect the type of CPU 0.
-       ALPHA_CPU_TYPE=`/usr/sbin/psrinfo -v | sed -n -e 's/^  The alpha \(.*\) processor.*$/\1/p' | head -n 1`
-       case "$ALPHA_CPU_TYPE" in
-           "EV4 (21064)")
-               UNAME_MACHINE="alpha" ;;
-           "EV4.5 (21064)")
-               UNAME_MACHINE="alpha" ;;
-           "LCA4 (21066/21068)")
-               UNAME_MACHINE="alpha" ;;
-           "EV5 (21164)")
-               UNAME_MACHINE="alphaev5" ;;
-           "EV5.6 (21164A)")
-               UNAME_MACHINE="alphaev56" ;;
-           "EV5.6 (21164PC)")
-               UNAME_MACHINE="alphapca56" ;;
-           "EV5.7 (21164PC)")
-               UNAME_MACHINE="alphapca57" ;;
-           "EV6 (21264)")
-               UNAME_MACHINE="alphaev6" ;;
-           "EV6.7 (21264A)")
-               UNAME_MACHINE="alphaev67" ;;
-           "EV6.8CB (21264C)")
-               UNAME_MACHINE="alphaev68" ;;
-           "EV6.8AL (21264B)")
-               UNAME_MACHINE="alphaev68" ;;
-           "EV6.8CX (21264D)")
-               UNAME_MACHINE="alphaev68" ;;
-           "EV6.9A (21264/EV69A)")
-               UNAME_MACHINE="alphaev69" ;;
-           "EV7 (21364)")
-               UNAME_MACHINE="alphaev7" ;;
-           "EV7.9 (21364A)")
-               UNAME_MACHINE="alphaev79" ;;
-       esac
-       # A Pn.n version is a patched version.
-       # A Vn.n version is a released version.
-       # A Tn.n version is a released field test version.
-       # A Xn.n version is an unreleased experimental baselevel.
-       # 1.2 uses "1.2" for uname -r.
-       echo ${UNAME_MACHINE}-dec-osf`echo ${UNAME_RELEASE} | sed -e 's/^[PVTX]//' | tr 'ABCDEFGHIJKLMNOPQRSTUVWXYZ' 'abcdefghijklmnopqrstuvwxyz'`
-       exit ;;
-    Alpha\ *:Windows_NT*:*)
-       # How do we know it's Interix rather than the generic POSIX subsystem?
-       # Should we change UNAME_MACHINE based on the output of uname instead
-       # of the specific Alpha model?
-       echo alpha-pc-interix
-       exit ;;
-    21064:Windows_NT:50:3)
-       echo alpha-dec-winnt3.5
-       exit ;;
-    Amiga*:UNIX_System_V:4.0:*)
-       echo m68k-unknown-sysv4
-       exit ;;
-    *:[Aa]miga[Oo][Ss]:*:*)
-       echo ${UNAME_MACHINE}-unknown-amigaos
-       exit ;;
-    *:[Mm]orph[Oo][Ss]:*:*)
-       echo ${UNAME_MACHINE}-unknown-morphos
-       exit ;;
-    *:OS/390:*:*)
-       echo i370-ibm-openedition
-       exit ;;
-    *:z/VM:*:*)
-       echo s390-ibm-zvmoe
-       exit ;;
-    *:OS400:*:*)
-        echo powerpc-ibm-os400
-       exit ;;
-    arm:RISC*:1.[012]*:*|arm:riscix:1.[012]*:*)
-       echo arm-acorn-riscix${UNAME_RELEASE}
-       exit ;;
-    arm:riscos:*:*|arm:RISCOS:*:*)
-       echo arm-unknown-riscos
-       exit ;;
-    SR2?01:HI-UX/MPP:*:* | SR8000:HI-UX/MPP:*:*)
-       echo hppa1.1-hitachi-hiuxmpp
-       exit ;;
-    Pyramid*:OSx*:*:* | MIS*:OSx*:*:* | MIS*:SMP_DC-OSx*:*:*)
-       # akee@wpdis03.wpafb.af.mil (Earle F. Ake) contributed MIS and NILE.
-       if test "`(/bin/universe) 2>/dev/null`" = att ; then
-               echo pyramid-pyramid-sysv3
-       else
-               echo pyramid-pyramid-bsd
-       fi
-       exit ;;
-    NILE*:*:*:dcosx)
-       echo pyramid-pyramid-svr4
-       exit ;;
-    DRS?6000:unix:4.0:6*)
-       echo sparc-icl-nx6
-       exit ;;
-    DRS?6000:UNIX_SV:4.2*:7* | DRS?6000:isis:4.2*:7*)
-       case `/usr/bin/uname -p` in
-           sparc) echo sparc-icl-nx7; exit ;;
-       esac ;;
-    sun4H:SunOS:5.*:*)
-       echo sparc-hal-solaris2`echo ${UNAME_RELEASE}|sed -e 's/[^.]*//'`
-       exit ;;
-    sun4*:SunOS:5.*:* | tadpole*:SunOS:5.*:*)
-       echo sparc-sun-solaris2`echo ${UNAME_RELEASE}|sed -e 's/[^.]*//'`
-       exit ;;
-    i86pc:SunOS:5.*:*)
-       echo i386-pc-solaris2`echo ${UNAME_RELEASE}|sed -e 's/[^.]*//'`
-       exit ;;
-    sun4*:SunOS:6*:*)
-       # According to config.sub, this is the proper way to canonicalize
-       # SunOS6.  Hard to guess exactly what SunOS6 will be like, but
-       # it's likely to be more like Solaris than SunOS4.
-       echo sparc-sun-solaris3`echo ${UNAME_RELEASE}|sed -e 's/[^.]*//'`
-       exit ;;
-    sun4*:SunOS:*:*)
-       case "`/usr/bin/arch -k`" in
-           Series*|S4*)
-               UNAME_RELEASE=`uname -v`
-               ;;
-       esac
-       # Japanese Language versions have a version number like `4.1.3-JL'.
-       echo sparc-sun-sunos`echo ${UNAME_RELEASE}|sed -e 's/-/_/'`
-       exit ;;
-    sun3*:SunOS:*:*)
-       echo m68k-sun-sunos${UNAME_RELEASE}
-       exit ;;
-    sun*:*:4.2BSD:*)
-       UNAME_RELEASE=`(sed 1q /etc/motd | awk '{print substr($5,1,3)}') 2>/dev/null`
-       test "x${UNAME_RELEASE}" = "x" && UNAME_RELEASE=3
-       case "`/bin/arch`" in
-           sun3)
-               echo m68k-sun-sunos${UNAME_RELEASE}
-               ;;
-           sun4)
-               echo sparc-sun-sunos${UNAME_RELEASE}
-               ;;
-       esac
-       exit ;;
-    aushp:SunOS:*:*)
-       echo sparc-auspex-sunos${UNAME_RELEASE}
-       exit ;;
-    # The situation for MiNT is a little confusing.  The machine name
-    # can be virtually everything (everything which is not
-    # "atarist" or "atariste" at least should have a processor
-    # > m68000).  The system name ranges from "MiNT" over "FreeMiNT"
-    # to the lowercase version "mint" (or "freemint").  Finally
-    # the system name "TOS" denotes a system which is actually not
-    # MiNT.  But MiNT is downward compatible to TOS, so this should
-    # be no problem.
-    atarist[e]:*MiNT:*:* | atarist[e]:*mint:*:* | atarist[e]:*TOS:*:*)
-        echo m68k-atari-mint${UNAME_RELEASE}
-       exit ;;
-    atari*:*MiNT:*:* | atari*:*mint:*:* | atarist[e]:*TOS:*:*)
-       echo m68k-atari-mint${UNAME_RELEASE}
-        exit ;;
-    *falcon*:*MiNT:*:* | *falcon*:*mint:*:* | *falcon*:*TOS:*:*)
-        echo m68k-atari-mint${UNAME_RELEASE}
-       exit ;;
-    milan*:*MiNT:*:* | milan*:*mint:*:* | *milan*:*TOS:*:*)
-        echo m68k-milan-mint${UNAME_RELEASE}
-        exit ;;
-    hades*:*MiNT:*:* | hades*:*mint:*:* | *hades*:*TOS:*:*)
-        echo m68k-hades-mint${UNAME_RELEASE}
-        exit ;;
-    *:*MiNT:*:* | *:*mint:*:* | *:*TOS:*:*)
-        echo m68k-unknown-mint${UNAME_RELEASE}
-        exit ;;
-    m68k:machten:*:*)
-       echo m68k-apple-machten${UNAME_RELEASE}
-       exit ;;
-    powerpc:machten:*:*)
-       echo powerpc-apple-machten${UNAME_RELEASE}
-       exit ;;
-    RISC*:Mach:*:*)
-       echo mips-dec-mach_bsd4.3
-       exit ;;
-    RISC*:ULTRIX:*:*)
-       echo mips-dec-ultrix${UNAME_RELEASE}
-       exit ;;
-    VAX*:ULTRIX*:*:*)
-       echo vax-dec-ultrix${UNAME_RELEASE}
-       exit ;;
-    2020:CLIX:*:* | 2430:CLIX:*:*)
-       echo clipper-intergraph-clix${UNAME_RELEASE}
-       exit ;;
-    mips:*:*:UMIPS | mips:*:*:RISCos)
-       eval $set_cc_for_build
-       sed 's/^        //' << EOF >$dummy.c
-#ifdef __cplusplus
-#include <stdio.h>  /* for printf() prototype */
-       int main (int argc, char *argv[]) {
-#else
-       int main (argc, argv) int argc; char *argv[]; {
-#endif
-       #if defined (host_mips) && defined (MIPSEB)
-       #if defined (SYSTYPE_SYSV)
-         printf ("mips-mips-riscos%ssysv\n", argv[1]); exit (0);
-       #endif
-       #if defined (SYSTYPE_SVR4)
-         printf ("mips-mips-riscos%ssvr4\n", argv[1]); exit (0);
-       #endif
-       #if defined (SYSTYPE_BSD43) || defined(SYSTYPE_BSD)
-         printf ("mips-mips-riscos%sbsd\n", argv[1]); exit (0);
-       #endif
-       #endif
-         exit (-1);
-       }
-EOF
-       $CC_FOR_BUILD -o $dummy $dummy.c &&
-         dummyarg=`echo "${UNAME_RELEASE}" | sed -n 's/\([0-9]*\).*/\1/p'` &&
-         SYSTEM_NAME=`$dummy $dummyarg` &&
-           { echo "$SYSTEM_NAME"; exit; }
-       echo mips-mips-riscos${UNAME_RELEASE}
-       exit ;;
-    Motorola:PowerMAX_OS:*:*)
-       echo powerpc-motorola-powermax
-       exit ;;
-    Motorola:*:4.3:PL8-*)
-       echo powerpc-harris-powermax
-       exit ;;
-    Night_Hawk:*:*:PowerMAX_OS | Synergy:PowerMAX_OS:*:*)
-       echo powerpc-harris-powermax
-       exit ;;
-    Night_Hawk:Power_UNIX:*:*)
-       echo powerpc-harris-powerunix
-       exit ;;
-    m88k:CX/UX:7*:*)
-       echo m88k-harris-cxux7
-       exit ;;
-    m88k:*:4*:R4*)
-       echo m88k-motorola-sysv4
-       exit ;;
-    m88k:*:3*:R3*)
-       echo m88k-motorola-sysv3
-       exit ;;
-    AViiON:dgux:*:*)
-        # DG/UX returns AViiON for all architectures
-        UNAME_PROCESSOR=`/usr/bin/uname -p`
-       if [ $UNAME_PROCESSOR = mc88100 ] || [ $UNAME_PROCESSOR = mc88110 ]
-       then
-           if [ ${TARGET_BINARY_INTERFACE}x = m88kdguxelfx ] || \
-              [ ${TARGET_BINARY_INTERFACE}x = x ]
-           then
-               echo m88k-dg-dgux${UNAME_RELEASE}
-           else
-               echo m88k-dg-dguxbcs${UNAME_RELEASE}
-           fi
-       else
-           echo i586-dg-dgux${UNAME_RELEASE}
-       fi
-       exit ;;
-    M88*:DolphinOS:*:*)        # DolphinOS (SVR3)
-       echo m88k-dolphin-sysv3
-       exit ;;
-    M88*:*:R3*:*)
-       # Delta 88k system running SVR3
-       echo m88k-motorola-sysv3
-       exit ;;
-    XD88*:*:*:*) # Tektronix XD88 system running UTekV (SVR3)
-       echo m88k-tektronix-sysv3
-       exit ;;
-    Tek43[0-9][0-9]:UTek:*:*) # Tektronix 4300 system running UTek (BSD)
-       echo m68k-tektronix-bsd
-       exit ;;
-    *:IRIX*:*:*)
-       echo mips-sgi-irix`echo ${UNAME_RELEASE}|sed -e 's/-/_/g'`
-       exit ;;
-    ????????:AIX?:[12].1:2)   # AIX 2.2.1 or AIX 2.1.1 is RT/PC AIX.
-       echo romp-ibm-aix     # uname -m gives an 8 hex-code CPU id
-       exit ;;               # Note that: echo "'`uname -s`'" gives 'AIX '
-    i*86:AIX:*:*)
-       echo i386-ibm-aix
-       exit ;;
-    ia64:AIX:*:*)
-       if [ -x /usr/bin/oslevel ] ; then
-               IBM_REV=`/usr/bin/oslevel`
-       else
-               IBM_REV=${UNAME_VERSION}.${UNAME_RELEASE}
-       fi
-       echo ${UNAME_MACHINE}-ibm-aix${IBM_REV}
-       exit ;;
-    *:AIX:2:3)
-       if grep bos325 /usr/include/stdio.h >/dev/null 2>&1; then
-               eval $set_cc_for_build
-               sed 's/^                //' << EOF >$dummy.c
-               #include <sys/systemcfg.h>
-
-               main()
-                       {
-                       if (!__power_pc())
-                               exit(1);
-                       puts("powerpc-ibm-aix3.2.5");
-                       exit(0);
-                       }
-EOF
-               if $CC_FOR_BUILD -o $dummy $dummy.c && SYSTEM_NAME=`$dummy`
-               then
-                       echo "$SYSTEM_NAME"
-               else
-                       echo rs6000-ibm-aix3.2.5
-               fi
-       elif grep bos324 /usr/include/stdio.h >/dev/null 2>&1; then
-               echo rs6000-ibm-aix3.2.4
-       else
-               echo rs6000-ibm-aix3.2
-       fi
-       exit ;;
-    *:AIX:*:[45])
-       IBM_CPU_ID=`/usr/sbin/lsdev -C -c processor -S available | sed 1q | awk '{ print $1 }'`
-       if /usr/sbin/lsattr -El ${IBM_CPU_ID} | grep ' POWER' >/dev/null 2>&1; then
-               IBM_ARCH=rs6000
-       else
-               IBM_ARCH=powerpc
-       fi
-       if [ -x /usr/bin/oslevel ] ; then
-               IBM_REV=`/usr/bin/oslevel`
-       else
-               IBM_REV=${UNAME_VERSION}.${UNAME_RELEASE}
-       fi
-       echo ${IBM_ARCH}-ibm-aix${IBM_REV}
-       exit ;;
-    *:AIX:*:*)
-       echo rs6000-ibm-aix
-       exit ;;
-    ibmrt:4.4BSD:*|romp-ibm:BSD:*)
-       echo romp-ibm-bsd4.4
-       exit ;;
-    ibmrt:*BSD:*|romp-ibm:BSD:*)            # covers RT/PC BSD and
-       echo romp-ibm-bsd${UNAME_RELEASE}   # 4.3 with uname added to
-       exit ;;                             # report: romp-ibm BSD 4.3
-    *:BOSX:*:*)
-       echo rs6000-bull-bosx
-       exit ;;
-    DPX/2?00:B.O.S.:*:*)
-       echo m68k-bull-sysv3
-       exit ;;
-    9000/[34]??:4.3bsd:1.*:*)
-       echo m68k-hp-bsd
-       exit ;;
-    hp300:4.4BSD:*:* | 9000/[34]??:4.3bsd:2.*:*)
-       echo m68k-hp-bsd4.4
-       exit ;;
-    9000/[34678]??:HP-UX:*:*)
-       HPUX_REV=`echo ${UNAME_RELEASE}|sed -e 's/[^.]*.[0B]*//'`
-       case "${UNAME_MACHINE}" in
-           9000/31? )            HP_ARCH=m68000 ;;
-           9000/[34]?? )         HP_ARCH=m68k ;;
-           9000/[678][0-9][0-9])
-               if [ -x /usr/bin/getconf ]; then
-                   sc_cpu_version=`/usr/bin/getconf SC_CPU_VERSION 2>/dev/null`
-                    sc_kernel_bits=`/usr/bin/getconf SC_KERNEL_BITS 2>/dev/null`
-                    case "${sc_cpu_version}" in
-                      523) HP_ARCH="hppa1.0" ;; # CPU_PA_RISC1_0
-                      528) HP_ARCH="hppa1.1" ;; # CPU_PA_RISC1_1
-                      532)                      # CPU_PA_RISC2_0
-                        case "${sc_kernel_bits}" in
-                          32) HP_ARCH="hppa2.0n" ;;
-                          64) HP_ARCH="hppa2.0w" ;;
-                         '') HP_ARCH="hppa2.0" ;;   # HP-UX 10.20
-                        esac ;;
-                    esac
-               fi
-               if [ "${HP_ARCH}" = "" ]; then
-                   eval $set_cc_for_build
-                   sed 's/^              //' << EOF >$dummy.c
-
-              #define _HPUX_SOURCE
-              #include <stdlib.h>
-              #include <unistd.h>
-
-              int main ()
-              {
-              #if defined(_SC_KERNEL_BITS)
-                  long bits = sysconf(_SC_KERNEL_BITS);
-              #endif
-                  long cpu  = sysconf (_SC_CPU_VERSION);
-
-                  switch (cpu)
-               {
-               case CPU_PA_RISC1_0: puts ("hppa1.0"); break;
-               case CPU_PA_RISC1_1: puts ("hppa1.1"); break;
-               case CPU_PA_RISC2_0:
-              #if defined(_SC_KERNEL_BITS)
-                   switch (bits)
-                       {
-                       case 64: puts ("hppa2.0w"); break;
-                       case 32: puts ("hppa2.0n"); break;
-                       default: puts ("hppa2.0"); break;
-                       } break;
-              #else  /* !defined(_SC_KERNEL_BITS) */
-                   puts ("hppa2.0"); break;
-              #endif
-               default: puts ("hppa1.0"); break;
-               }
-                  exit (0);
-              }
-EOF
-                   (CCOPTS= $CC_FOR_BUILD -o $dummy $dummy.c 2>/dev/null) && HP_ARCH=`$dummy`
-                   test -z "$HP_ARCH" && HP_ARCH=hppa
-               fi ;;
-       esac
-       if [ ${HP_ARCH} = "hppa2.0w" ]
-       then
-           eval $set_cc_for_build
-
-           # hppa2.0w-hp-hpux* has a 64-bit kernel and a compiler generating
-           # 32-bit code.  hppa64-hp-hpux* has the same kernel and a compiler
-           # generating 64-bit code.  GNU and HP use different nomenclature:
-           #
-           # $ CC_FOR_BUILD=cc ./config.guess
-           # => hppa2.0w-hp-hpux11.23
-           # $ CC_FOR_BUILD="cc +DA2.0w" ./config.guess
-           # => hppa64-hp-hpux11.23
-
-           if echo __LP64__ | (CCOPTS= $CC_FOR_BUILD -E - 2>/dev/null) |
-               grep __LP64__ >/dev/null
-           then
-               HP_ARCH="hppa2.0w"
-           else
-               HP_ARCH="hppa64"
-           fi
-       fi
-       echo ${HP_ARCH}-hp-hpux${HPUX_REV}
-       exit ;;
-    ia64:HP-UX:*:*)
-       HPUX_REV=`echo ${UNAME_RELEASE}|sed -e 's/[^.]*.[0B]*//'`
-       echo ia64-hp-hpux${HPUX_REV}
-       exit ;;
-    3050*:HI-UX:*:*)
-       eval $set_cc_for_build
-       sed 's/^        //' << EOF >$dummy.c
-       #include <unistd.h>
-       int
-       main ()
-       {
-         long cpu = sysconf (_SC_CPU_VERSION);
-         /* The order matters, because CPU_IS_HP_MC68K erroneously returns
-            true for CPU_PA_RISC1_0.  CPU_IS_PA_RISC returns correct
-            results, however.  */
-         if (CPU_IS_PA_RISC (cpu))
-           {
-             switch (cpu)
-               {
-                 case CPU_PA_RISC1_0: puts ("hppa1.0-hitachi-hiuxwe2"); break;
-                 case CPU_PA_RISC1_1: puts ("hppa1.1-hitachi-hiuxwe2"); break;
-                 case CPU_PA_RISC2_0: puts ("hppa2.0-hitachi-hiuxwe2"); break;
-                 default: puts ("hppa-hitachi-hiuxwe2"); break;
-               }
-           }
-         else if (CPU_IS_HP_MC68K (cpu))
-           puts ("m68k-hitachi-hiuxwe2");
-         else puts ("unknown-hitachi-hiuxwe2");
-         exit (0);
-       }
-EOF
-       $CC_FOR_BUILD -o $dummy $dummy.c && SYSTEM_NAME=`$dummy` &&
-               { echo "$SYSTEM_NAME"; exit; }
-       echo unknown-hitachi-hiuxwe2
-       exit ;;
-    9000/7??:4.3bsd:*:* | 9000/8?[79]:4.3bsd:*:* )
-       echo hppa1.1-hp-bsd
-       exit ;;
-    9000/8??:4.3bsd:*:*)
-       echo hppa1.0-hp-bsd
-       exit ;;
-    *9??*:MPE/iX:*:* | *3000*:MPE/iX:*:*)
-       echo hppa1.0-hp-mpeix
-       exit ;;
-    hp7??:OSF1:*:* | hp8?[79]:OSF1:*:* )
-       echo hppa1.1-hp-osf
-       exit ;;
-    hp8??:OSF1:*:*)
-       echo hppa1.0-hp-osf
-       exit ;;
-    i*86:OSF1:*:*)
-       if [ -x /usr/sbin/sysversion ] ; then
-           echo ${UNAME_MACHINE}-unknown-osf1mk
-       else
-           echo ${UNAME_MACHINE}-unknown-osf1
-       fi
-       exit ;;
-    parisc*:Lites*:*:*)
-       echo hppa1.1-hp-lites
-       exit ;;
-    C1*:ConvexOS:*:* | convex:ConvexOS:C1*:*)
-       echo c1-convex-bsd
-        exit ;;
-    C2*:ConvexOS:*:* | convex:ConvexOS:C2*:*)
-       if getsysinfo -f scalar_acc
-       then echo c32-convex-bsd
-       else echo c2-convex-bsd
-       fi
-        exit ;;
-    C34*:ConvexOS:*:* | convex:ConvexOS:C34*:*)
-       echo c34-convex-bsd
-        exit ;;
-    C38*:ConvexOS:*:* | convex:ConvexOS:C38*:*)
-       echo c38-convex-bsd
-        exit ;;
-    C4*:ConvexOS:*:* | convex:ConvexOS:C4*:*)
-       echo c4-convex-bsd
-        exit ;;
-    CRAY*Y-MP:*:*:*)
-       echo ymp-cray-unicos${UNAME_RELEASE} | sed -e 's/\.[^.]*$/.X/'
-       exit ;;
-    CRAY*[A-Z]90:*:*:*)
-       echo ${UNAME_MACHINE}-cray-unicos${UNAME_RELEASE} \
-       | sed -e 's/CRAY.*\([A-Z]90\)/\1/' \
-             -e y/ABCDEFGHIJKLMNOPQRSTUVWXYZ/abcdefghijklmnopqrstuvwxyz/ \
-             -e 's/\.[^.]*$/.X/'
-       exit ;;
-    CRAY*TS:*:*:*)
-       echo t90-cray-unicos${UNAME_RELEASE} | sed -e 's/\.[^.]*$/.X/'
-       exit ;;
-    CRAY*T3E:*:*:*)
-       echo alphaev5-cray-unicosmk${UNAME_RELEASE} | sed -e 's/\.[^.]*$/.X/'
-       exit ;;
-    CRAY*SV1:*:*:*)
-       echo sv1-cray-unicos${UNAME_RELEASE} | sed -e 's/\.[^.]*$/.X/'
-       exit ;;
-    *:UNICOS/mp:*:*)
-       echo craynv-cray-unicosmp${UNAME_RELEASE} | sed -e 's/\.[^.]*$/.X/'
-       exit ;;
-    F30[01]:UNIX_System_V:*:* | F700:UNIX_System_V:*:*)
-       FUJITSU_PROC=`uname -m | tr 'ABCDEFGHIJKLMNOPQRSTUVWXYZ' 'abcdefghijklmnopqrstuvwxyz'`
-        FUJITSU_SYS=`uname -p | tr 'ABCDEFGHIJKLMNOPQRSTUVWXYZ' 'abcdefghijklmnopqrstuvwxyz' | sed -e 's/\///'`
-        FUJITSU_REL=`echo ${UNAME_RELEASE} | sed -e 's/ /_/'`
-        echo "${FUJITSU_PROC}-fujitsu-${FUJITSU_SYS}${FUJITSU_REL}"
-        exit ;;
-    5000:UNIX_System_V:4.*:*)
-        FUJITSU_SYS=`uname -p | tr 'ABCDEFGHIJKLMNOPQRSTUVWXYZ' 'abcdefghijklmnopqrstuvwxyz' | sed -e 's/\///'`
-        FUJITSU_REL=`echo ${UNAME_RELEASE} | tr 'ABCDEFGHIJKLMNOPQRSTUVWXYZ' 'abcdefghijklmnopqrstuvwxyz' | sed -e 's/ /_/'`
-        echo "sparc-fujitsu-${FUJITSU_SYS}${FUJITSU_REL}"
-       exit ;;
-    i*86:BSD/386:*:* | i*86:BSD/OS:*:* | *:Ascend\ Embedded/OS:*:*)
-       echo ${UNAME_MACHINE}-pc-bsdi${UNAME_RELEASE}
-       exit ;;
-    sparc*:BSD/OS:*:*)
-       echo sparc-unknown-bsdi${UNAME_RELEASE}
-       exit ;;
-    *:BSD/OS:*:*)
-       echo ${UNAME_MACHINE}-unknown-bsdi${UNAME_RELEASE}
-       exit ;;
-    *:FreeBSD:*:*)
-       case ${UNAME_MACHINE} in
-           pc98)
-               echo i386-unknown-freebsd`echo ${UNAME_RELEASE}|sed -e 's/[-(].*//'` ;;
-           amd64)
-               echo x86_64-unknown-freebsd`echo ${UNAME_RELEASE}|sed -e 's/[-(].*//'` ;;
-           *)
-               echo ${UNAME_MACHINE}-unknown-freebsd`echo ${UNAME_RELEASE}|sed -e 's/[-(].*//'` ;;
-       esac
-       exit ;;
-    i*:CYGWIN*:*)
-       echo ${UNAME_MACHINE}-pc-cygwin
-       exit ;;
-    i*:MINGW*:*)
-       echo ${UNAME_MACHINE}-pc-mingw32
-       exit ;;
-    i*:windows32*:*)
-       # uname -m includes "-pc" on this system.
-       echo ${UNAME_MACHINE}-mingw32
-       exit ;;
-    i*:PW*:*)
-       echo ${UNAME_MACHINE}-pc-pw32
-       exit ;;
-    x86:Interix*:[3456]*)
-       echo i586-pc-interix${UNAME_RELEASE}
-       exit ;;
-    EM64T:Interix*:[3456]*)
-       echo x86_64-unknown-interix${UNAME_RELEASE}
-       exit ;;
-    [345]86:Windows_95:* | [345]86:Windows_98:* | [345]86:Windows_NT:*)
-       echo i${UNAME_MACHINE}-pc-mks
-       exit ;;
-    i*:Windows_NT*:* | Pentium*:Windows_NT*:*)
-       # How do we know it's Interix rather than the generic POSIX subsystem?
-       # It also conflicts with pre-2.0 versions of AT&T UWIN. Should we
-       # UNAME_MACHINE based on the output of uname instead of i386?
-       echo i586-pc-interix
-       exit ;;
-    i*:UWIN*:*)
-       echo ${UNAME_MACHINE}-pc-uwin
-       exit ;;
-    amd64:CYGWIN*:*:* | x86_64:CYGWIN*:*:*)
-       echo x86_64-unknown-cygwin
-       exit ;;
-    p*:CYGWIN*:*)
-       echo powerpcle-unknown-cygwin
-       exit ;;
-    prep*:SunOS:5.*:*)
-       echo powerpcle-unknown-solaris2`echo ${UNAME_RELEASE}|sed -e 's/[^.]*//'`
-       exit ;;
-    *:GNU:*:*)
-       # the GNU system
-       echo `echo ${UNAME_MACHINE}|sed -e 's,[-/].*$,,'`-unknown-gnu`echo ${UNAME_RELEASE}|sed -e 's,/.*$,,'`
-       exit ;;
-    *:GNU/*:*:*)
-       # other systems with GNU libc and userland
-       echo ${UNAME_MACHINE}-unknown-`echo ${UNAME_SYSTEM} | sed 's,^[^/]*/,,' | tr '[A-Z]' '[a-z]'``echo ${UNAME_RELEASE}|sed -e 's/[-(].*//'`-gnu
-       exit ;;
-    i*86:Minix:*:*)
-       echo ${UNAME_MACHINE}-pc-minix
-       exit ;;
-    arm*:Linux:*:*)
-       echo ${UNAME_MACHINE}-unknown-linux-gnu
-       exit ;;
-    avr32*:Linux:*:*)
-       echo ${UNAME_MACHINE}-unknown-linux-gnu
-       exit ;;
-    cris:Linux:*:*)
-       echo cris-axis-linux-gnu
-       exit ;;
-    crisv32:Linux:*:*)
-       echo crisv32-axis-linux-gnu
-       exit ;;
-    frv:Linux:*:*)
-       echo frv-unknown-linux-gnu
-       exit ;;
-    ia64:Linux:*:*)
-       echo ${UNAME_MACHINE}-unknown-linux-gnu
-       exit ;;
-    m32r*:Linux:*:*)
-       echo ${UNAME_MACHINE}-unknown-linux-gnu
-       exit ;;
-    m68*:Linux:*:*)
-       echo ${UNAME_MACHINE}-unknown-linux-gnu
-       exit ;;
-    mips:Linux:*:*)
-       eval $set_cc_for_build
-       sed 's/^        //' << EOF >$dummy.c
-       #undef CPU
-       #undef mips
-       #undef mipsel
-       #if defined(__MIPSEL__) || defined(__MIPSEL) || defined(_MIPSEL) || defined(MIPSEL)
-       CPU=mipsel
-       #else
-       #if defined(__MIPSEB__) || defined(__MIPSEB) || defined(_MIPSEB) || defined(MIPSEB)
-       CPU=mips
-       #else
-       CPU=
-       #endif
-       #endif
-EOF
-       eval "`$CC_FOR_BUILD -E $dummy.c 2>/dev/null | sed -n '
-           /^CPU/{
-               s: ::g
-               p
-           }'`"
-       test x"${CPU}" != x && { echo "${CPU}-unknown-linux-gnu"; exit; }
-       ;;
-    mips64:Linux:*:*)
-       eval $set_cc_for_build
-       sed 's/^        //' << EOF >$dummy.c
-       #undef CPU
-       #undef mips64
-       #undef mips64el
-       #if defined(__MIPSEL__) || defined(__MIPSEL) || defined(_MIPSEL) || defined(MIPSEL)
-       CPU=mips64el
-       #else
-       #if defined(__MIPSEB__) || defined(__MIPSEB) || defined(_MIPSEB) || defined(MIPSEB)
-       CPU=mips64
-       #else
-       CPU=
-       #endif
-       #endif
-EOF
-       eval "`$CC_FOR_BUILD -E $dummy.c 2>/dev/null | sed -n '
-           /^CPU/{
-               s: ::g
-               p
-           }'`"
-       test x"${CPU}" != x && { echo "${CPU}-unknown-linux-gnu"; exit; }
-       ;;
-    or32:Linux:*:*)
-       echo or32-unknown-linux-gnu
-       exit ;;
-    ppc:Linux:*:*)
-       echo powerpc-unknown-linux-gnu
-       exit ;;
-    ppc64:Linux:*:*)
-       echo powerpc64-unknown-linux-gnu
-       exit ;;
-    alpha:Linux:*:*)
-       case `sed -n '/^cpu model/s/^.*: \(.*\)/\1/p' < /proc/cpuinfo` in
-         EV5)   UNAME_MACHINE=alphaev5 ;;
-         EV56)  UNAME_MACHINE=alphaev56 ;;
-         PCA56) UNAME_MACHINE=alphapca56 ;;
-         PCA57) UNAME_MACHINE=alphapca56 ;;
-         EV6)   UNAME_MACHINE=alphaev6 ;;
-         EV67)  UNAME_MACHINE=alphaev67 ;;
-         EV68*) UNAME_MACHINE=alphaev68 ;;
-        esac
-       objdump --private-headers /bin/sh | grep ld.so.1 >/dev/null
-       if test "$?" = 0 ; then LIBC="libc1" ; else LIBC="" ; fi
-       echo ${UNAME_MACHINE}-unknown-linux-gnu${LIBC}
-       exit ;;
-    parisc:Linux:*:* | hppa:Linux:*:*)
-       # Look for CPU level
-       case `grep '^cpu[^a-z]*:' /proc/cpuinfo 2>/dev/null | cut -d' ' -f2` in
-         PA7*) echo hppa1.1-unknown-linux-gnu ;;
-         PA8*) echo hppa2.0-unknown-linux-gnu ;;
-         *)    echo hppa-unknown-linux-gnu ;;
-       esac
-       exit ;;
-    parisc64:Linux:*:* | hppa64:Linux:*:*)
-       echo hppa64-unknown-linux-gnu
-       exit ;;
-    s390:Linux:*:* | s390x:Linux:*:*)
-       echo ${UNAME_MACHINE}-ibm-linux
-       exit ;;
-    sh64*:Linux:*:*)
-       echo ${UNAME_MACHINE}-unknown-linux-gnu
-       exit ;;
-    sh*:Linux:*:*)
-       echo ${UNAME_MACHINE}-unknown-linux-gnu
-       exit ;;
-    sparc:Linux:*:* | sparc64:Linux:*:*)
-       echo ${UNAME_MACHINE}-unknown-linux-gnu
-       exit ;;
-    vax:Linux:*:*)
-       echo ${UNAME_MACHINE}-dec-linux-gnu
-       exit ;;
-    x86_64:Linux:*:*)
-       echo x86_64-unknown-linux-gnu
-       exit ;;
-    i*86:Linux:*:*)
-       # The BFD linker knows what the default object file format is, so
-       # first see if it will tell us. cd to the root directory to prevent
-       # problems with other programs or directories called `ld' in the path.
-       # Set LC_ALL=C to ensure ld outputs messages in English.
-       ld_supported_targets=`cd /; LC_ALL=C ld --help 2>&1 \
-                        | sed -ne '/supported targets:/!d
-                                   s/[         ][      ]*/ /g
-                                   s/.*supported targets: *//
-                                   s/ .*//
-                                   p'`
-        case "$ld_supported_targets" in
-         elf32-i386)
-               TENTATIVE="${UNAME_MACHINE}-pc-linux-gnu"
-               ;;
-         a.out-i386-linux)
-               echo "${UNAME_MACHINE}-pc-linux-gnuaout"
-               exit ;;
-         coff-i386)
-               echo "${UNAME_MACHINE}-pc-linux-gnucoff"
-               exit ;;
-         "")
-               # Either a pre-BFD a.out linker (linux-gnuoldld) or
-               # one that does not give us useful --help.
-               echo "${UNAME_MACHINE}-pc-linux-gnuoldld"
-               exit ;;
-       esac
-       # Determine whether the default compiler is a.out or elf
-       eval $set_cc_for_build
-       sed 's/^        //' << EOF >$dummy.c
-       #include <features.h>
-       #ifdef __ELF__
-       # ifdef __GLIBC__
-       #  if __GLIBC__ >= 2
-       LIBC=gnu
-       #  else
-       LIBC=gnulibc1
-       #  endif
-       # else
-       LIBC=gnulibc1
-       # endif
-       #else
-       #if defined(__INTEL_COMPILER) || defined(__PGI) || defined(__SUNPRO_C) || defined(__SUNPRO_CC)
-       LIBC=gnu
-       #else
-       LIBC=gnuaout
-       #endif
-       #endif
-       #ifdef __dietlibc__
-       LIBC=dietlibc
-       #endif
-EOF
-       eval "`$CC_FOR_BUILD -E $dummy.c 2>/dev/null | sed -n '
-           /^LIBC/{
-               s: ::g
-               p
-           }'`"
-       test x"${LIBC}" != x && {
-               echo "${UNAME_MACHINE}-pc-linux-${LIBC}"
-               exit
-       }
-       test x"${TENTATIVE}" != x && { echo "${TENTATIVE}"; exit; }
-       ;;
-    i*86:DYNIX/ptx:4*:*)
-       # ptx 4.0 does uname -s correctly, with DYNIX/ptx in there.
-       # earlier versions are messed up and put the nodename in both
-       # sysname and nodename.
-       echo i386-sequent-sysv4
-       exit ;;
-    i*86:UNIX_SV:4.2MP:2.*)
-        # Unixware is an offshoot of SVR4, but it has its own version
-        # number series starting with 2...
-        # I am not positive that other SVR4 systems won't match this,
-       # I just have to hope.  -- rms.
-        # Use sysv4.2uw... so that sysv4* matches it.
-       echo ${UNAME_MACHINE}-pc-sysv4.2uw${UNAME_VERSION}
-       exit ;;
-    i*86:OS/2:*:*)
-       # If we were able to find `uname', then EMX Unix compatibility
-       # is probably installed.
-       echo ${UNAME_MACHINE}-pc-os2-emx
-       exit ;;
-    i*86:XTS-300:*:STOP)
-       echo ${UNAME_MACHINE}-unknown-stop
-       exit ;;
-    i*86:atheos:*:*)
-       echo ${UNAME_MACHINE}-unknown-atheos
-       exit ;;
-    i*86:syllable:*:*)
-       echo ${UNAME_MACHINE}-pc-syllable
-       exit ;;
-    i*86:LynxOS:2.*:* | i*86:LynxOS:3.[01]*:* | i*86:LynxOS:4.0*:*)
-       echo i386-unknown-lynxos${UNAME_RELEASE}
-       exit ;;
-    i*86:*DOS:*:*)
-       echo ${UNAME_MACHINE}-pc-msdosdjgpp
-       exit ;;
-    i*86:*:4.*:* | i*86:SYSTEM_V:4.*:*)
-       UNAME_REL=`echo ${UNAME_RELEASE} | sed 's/\/MP$//'`
-       if grep Novell /usr/include/link.h >/dev/null 2>/dev/null; then
-               echo ${UNAME_MACHINE}-univel-sysv${UNAME_REL}
-       else
-               echo ${UNAME_MACHINE}-pc-sysv${UNAME_REL}
-       fi
-       exit ;;
-    i*86:*:5:[678]*)
-       # UnixWare 7.x, OpenUNIX and OpenServer 6.
-       case `/bin/uname -X | grep "^Machine"` in
-           *486*)           UNAME_MACHINE=i486 ;;
-           *Pentium)        UNAME_MACHINE=i586 ;;
-           *Pent*|*Celeron) UNAME_MACHINE=i686 ;;
-       esac
-       echo ${UNAME_MACHINE}-unknown-sysv${UNAME_RELEASE}${UNAME_SYSTEM}${UNAME_VERSION}
-       exit ;;
-    i*86:*:3.2:*)
-       if test -f /usr/options/cb.name; then
-               UNAME_REL=`sed -n 's/.*Version //p' </usr/options/cb.name`
-               echo ${UNAME_MACHINE}-pc-isc$UNAME_REL
-       elif /bin/uname -X 2>/dev/null >/dev/null ; then
-               UNAME_REL=`(/bin/uname -X|grep Release|sed -e 's/.*= //')`
-               (/bin/uname -X|grep i80486 >/dev/null) && UNAME_MACHINE=i486
-               (/bin/uname -X|grep '^Machine.*Pentium' >/dev/null) \
-                       && UNAME_MACHINE=i586
-               (/bin/uname -X|grep '^Machine.*Pent *II' >/dev/null) \
-                       && UNAME_MACHINE=i686
-               (/bin/uname -X|grep '^Machine.*Pentium Pro' >/dev/null) \
-                       && UNAME_MACHINE=i686
-               echo ${UNAME_MACHINE}-pc-sco$UNAME_REL
-       else
-               echo ${UNAME_MACHINE}-pc-sysv32
-       fi
-       exit ;;
-    pc:*:*:*)
-       # Left here for compatibility:
-        # uname -m prints for DJGPP always 'pc', but it prints nothing about
-        # the processor, so we play safe by assuming i386.
-       echo i386-pc-msdosdjgpp
-        exit ;;
-    Intel:Mach:3*:*)
-       echo i386-pc-mach3
-       exit ;;
-    paragon:*:*:*)
-       echo i860-intel-osf1
-       exit ;;
-    i860:*:4.*:*) # i860-SVR4
-       if grep Stardent /usr/include/sys/uadmin.h >/dev/null 2>&1 ; then
-         echo i860-stardent-sysv${UNAME_RELEASE} # Stardent Vistra i860-SVR4
-       else # Add other i860-SVR4 vendors below as they are discovered.
-         echo i860-unknown-sysv${UNAME_RELEASE}  # Unknown i860-SVR4
-       fi
-       exit ;;
-    mini*:CTIX:SYS*5:*)
-       # "miniframe"
-       echo m68010-convergent-sysv
-       exit ;;
-    mc68k:UNIX:SYSTEM5:3.51m)
-       echo m68k-convergent-sysv
-       exit ;;
-    M680?0:D-NIX:5.3:*)
-       echo m68k-diab-dnix
-       exit ;;
-    M68*:*:R3V[5678]*:*)
-       test -r /sysV68 && { echo 'm68k-motorola-sysv'; exit; } ;;
-    3[345]??:*:4.0:3.0 | 3[34]??A:*:4.0:3.0 | 3[34]??,*:*:4.0:3.0 | 3[34]??/*:*:4.0:3.0 | 4400:*:4.0:3.0 | 4850:*:4.0:3.0 | SKA40:*:4.0:3.0 | SDS2:*:4.0:3.0 | SHG2:*:4.0:3.0 | S7501*:*:4.0:3.0)
-       OS_REL=''
-       test -r /etc/.relid \
-       && OS_REL=.`sed -n 's/[^ ]* [^ ]* \([0-9][0-9]\).*/\1/p' < /etc/.relid`
-       /bin/uname -p 2>/dev/null | grep 86 >/dev/null \
-         && { echo i486-ncr-sysv4.3${OS_REL}; exit; }
-       /bin/uname -p 2>/dev/null | /bin/grep entium >/dev/null \
-         && { echo i586-ncr-sysv4.3${OS_REL}; exit; } ;;
-    3[34]??:*:4.0:* | 3[34]??,*:*:4.0:*)
-        /bin/uname -p 2>/dev/null | grep 86 >/dev/null \
-          && { echo i486-ncr-sysv4; exit; } ;;
-    m68*:LynxOS:2.*:* | m68*:LynxOS:3.0*:*)
-       echo m68k-unknown-lynxos${UNAME_RELEASE}
-       exit ;;
-    mc68030:UNIX_System_V:4.*:*)
-       echo m68k-atari-sysv4
-       exit ;;
-    TSUNAMI:LynxOS:2.*:*)
-       echo sparc-unknown-lynxos${UNAME_RELEASE}
-       exit ;;
-    rs6000:LynxOS:2.*:*)
-       echo rs6000-unknown-lynxos${UNAME_RELEASE}
-       exit ;;
-    PowerPC:LynxOS:2.*:* | PowerPC:LynxOS:3.[01]*:* | PowerPC:LynxOS:4.0*:*)
-       echo powerpc-unknown-lynxos${UNAME_RELEASE}
-       exit ;;
-    SM[BE]S:UNIX_SV:*:*)
-       echo mips-dde-sysv${UNAME_RELEASE}
-       exit ;;
-    RM*:ReliantUNIX-*:*:*)
-       echo mips-sni-sysv4
-       exit ;;
-    RM*:SINIX-*:*:*)
-       echo mips-sni-sysv4
-       exit ;;
-    *:SINIX-*:*:*)
-       if uname -p 2>/dev/null >/dev/null ; then
-               UNAME_MACHINE=`(uname -p) 2>/dev/null`
-               echo ${UNAME_MACHINE}-sni-sysv4
-       else
-               echo ns32k-sni-sysv
-       fi
-       exit ;;
-    PENTIUM:*:4.0*:*) # Unisys `ClearPath HMP IX 4000' SVR4/MP effort
-                      # says <Richard.M.Bartel@ccMail.Census.GOV>
-        echo i586-unisys-sysv4
-        exit ;;
-    *:UNIX_System_V:4*:FTX*)
-       # From Gerald Hewes <hewes@openmarket.com>.
-       # How about differentiating between stratus architectures? -djm
-       echo hppa1.1-stratus-sysv4
-       exit ;;
-    *:*:*:FTX*)
-       # From seanf@swdc.stratus.com.
-       echo i860-stratus-sysv4
-       exit ;;
-    i*86:VOS:*:*)
-       # From Paul.Green@stratus.com.
-       echo ${UNAME_MACHINE}-stratus-vos
-       exit ;;
-    *:VOS:*:*)
-       # From Paul.Green@stratus.com.
-       echo hppa1.1-stratus-vos
-       exit ;;
-    mc68*:A/UX:*:*)
-       echo m68k-apple-aux${UNAME_RELEASE}
-       exit ;;
-    news*:NEWS-OS:6*:*)
-       echo mips-sony-newsos6
-       exit ;;
-    R[34]000:*System_V*:*:* | R4000:UNIX_SYSV:*:* | R*000:UNIX_SV:*:*)
-       if [ -d /usr/nec ]; then
-               echo mips-nec-sysv${UNAME_RELEASE}
-       else
-               echo mips-unknown-sysv${UNAME_RELEASE}
-       fi
-        exit ;;
-    BeBox:BeOS:*:*)    # BeOS running on hardware made by Be, PPC only.
-       echo powerpc-be-beos
-       exit ;;
-    BeMac:BeOS:*:*)    # BeOS running on Mac or Mac clone, PPC only.
-       echo powerpc-apple-beos
-       exit ;;
-    BePC:BeOS:*:*)     # BeOS running on Intel PC compatible.
-       echo i586-pc-beos
-       exit ;;
-    SX-4:SUPER-UX:*:*)
-       echo sx4-nec-superux${UNAME_RELEASE}
-       exit ;;
-    SX-5:SUPER-UX:*:*)
-       echo sx5-nec-superux${UNAME_RELEASE}
-       exit ;;
-    SX-6:SUPER-UX:*:*)
-       echo sx6-nec-superux${UNAME_RELEASE}
-       exit ;;
-    Power*:Rhapsody:*:*)
-       echo powerpc-apple-rhapsody${UNAME_RELEASE}
-       exit ;;
-    *:Rhapsody:*:*)
-       echo ${UNAME_MACHINE}-apple-rhapsody${UNAME_RELEASE}
-       exit ;;
-    *:Darwin:*:*)
-       UNAME_PROCESSOR=`uname -p` || UNAME_PROCESSOR=unknown
-       case $UNAME_PROCESSOR in
-           unknown) UNAME_PROCESSOR=powerpc ;;
-       esac
-       echo ${UNAME_PROCESSOR}-apple-darwin${UNAME_RELEASE}
-       exit ;;
-    *:procnto*:*:* | *:QNX:[0123456789]*:*)
-       UNAME_PROCESSOR=`uname -p`
-       if test "$UNAME_PROCESSOR" = "x86"; then
-               UNAME_PROCESSOR=i386
-               UNAME_MACHINE=pc
-       fi
-       echo ${UNAME_PROCESSOR}-${UNAME_MACHINE}-nto-qnx${UNAME_RELEASE}
-       exit ;;
-    *:QNX:*:4*)
-       echo i386-pc-qnx
-       exit ;;
-    NSE-?:NONSTOP_KERNEL:*:*)
-       echo nse-tandem-nsk${UNAME_RELEASE}
-       exit ;;
-    NSR-?:NONSTOP_KERNEL:*:*)
-       echo nsr-tandem-nsk${UNAME_RELEASE}
-       exit ;;
-    *:NonStop-UX:*:*)
-       echo mips-compaq-nonstopux
-       exit ;;
-    BS2000:POSIX*:*:*)
-       echo bs2000-siemens-sysv
-       exit ;;
-    DS/*:UNIX_System_V:*:*)
-       echo ${UNAME_MACHINE}-${UNAME_SYSTEM}-${UNAME_RELEASE}
-       exit ;;
-    *:Plan9:*:*)
-       # "uname -m" is not consistent, so use $cputype instead. 386
-       # is converted to i386 for consistency with other x86
-       # operating systems.
-       if test "$cputype" = "386"; then
-           UNAME_MACHINE=i386
-       else
-           UNAME_MACHINE="$cputype"
-       fi
-       echo ${UNAME_MACHINE}-unknown-plan9
-       exit ;;
-    *:TOPS-10:*:*)
-       echo pdp10-unknown-tops10
-       exit ;;
-    *:TENEX:*:*)
-       echo pdp10-unknown-tenex
-       exit ;;
-    KS10:TOPS-20:*:* | KL10:TOPS-20:*:* | TYPE4:TOPS-20:*:*)
-       echo pdp10-dec-tops20
-       exit ;;
-    XKL-1:TOPS-20:*:* | TYPE5:TOPS-20:*:*)
-       echo pdp10-xkl-tops20
-       exit ;;
-    *:TOPS-20:*:*)
-       echo pdp10-unknown-tops20
-       exit ;;
-    *:ITS:*:*)
-       echo pdp10-unknown-its
-       exit ;;
-    SEI:*:*:SEIUX)
-        echo mips-sei-seiux${UNAME_RELEASE}
-       exit ;;
-    *:DragonFly:*:*)
-       echo ${UNAME_MACHINE}-unknown-dragonfly`echo ${UNAME_RELEASE}|sed -e 's/[-(].*//'`
-       exit ;;
-    *:*VMS:*:*)
-       UNAME_MACHINE=`(uname -p) 2>/dev/null`
-       case "${UNAME_MACHINE}" in
-           A*) echo alpha-dec-vms ; exit ;;
-           I*) echo ia64-dec-vms ; exit ;;
-           V*) echo vax-dec-vms ; exit ;;
-       esac ;;
-    *:XENIX:*:SysV)
-       echo i386-pc-xenix
-       exit ;;
-    i*86:skyos:*:*)
-       echo ${UNAME_MACHINE}-pc-skyos`echo ${UNAME_RELEASE}` | sed -e 's/ .*$//'
-       exit ;;
-    i*86:rdos:*:*)
-       echo ${UNAME_MACHINE}-pc-rdos
-       exit ;;
-esac
-
-#echo '(No uname command or uname output not recognized.)' 1>&2
-#echo "${UNAME_MACHINE}:${UNAME_SYSTEM}:${UNAME_RELEASE}:${UNAME_VERSION}" 1>&2
-
-eval $set_cc_for_build
-cat >$dummy.c <<EOF
-#ifdef _SEQUENT_
-# include <sys/types.h>
-# include <sys/utsname.h>
-#endif
-main ()
-{
-#if defined (sony)
-#if defined (MIPSEB)
-  /* BFD wants "bsd" instead of "newsos".  Perhaps BFD should be changed,
-     I don't know....  */
-  printf ("mips-sony-bsd\n"); exit (0);
-#else
-#include <sys/param.h>
-  printf ("m68k-sony-newsos%s\n",
-#ifdef NEWSOS4
-          "4"
-#else
-         ""
-#endif
-         ); exit (0);
-#endif
-#endif
-
-#if defined (__arm) && defined (__acorn) && defined (__unix)
-  printf ("arm-acorn-riscix\n"); exit (0);
-#endif
-
-#if defined (hp300) && !defined (hpux)
-  printf ("m68k-hp-bsd\n"); exit (0);
-#endif
-
-#if defined (NeXT)
-#if !defined (__ARCHITECTURE__)
-#define __ARCHITECTURE__ "m68k"
-#endif
-  int version;
-  version=`(hostinfo | sed -n 's/.*NeXT Mach \([0-9]*\).*/\1/p') 2>/dev/null`;
-  if (version < 4)
-    printf ("%s-next-nextstep%d\n", __ARCHITECTURE__, version);
-  else
-    printf ("%s-next-openstep%d\n", __ARCHITECTURE__, version);
-  exit (0);
-#endif
-
-#if defined (MULTIMAX) || defined (n16)
-#if defined (UMAXV)
-  printf ("ns32k-encore-sysv\n"); exit (0);
-#else
-#if defined (CMU)
-  printf ("ns32k-encore-mach\n"); exit (0);
-#else
-  printf ("ns32k-encore-bsd\n"); exit (0);
-#endif
-#endif
-#endif
-
-#if defined (__386BSD__)
-  printf ("i386-pc-bsd\n"); exit (0);
-#endif
-
-#if defined (sequent)
-#if defined (i386)
-  printf ("i386-sequent-dynix\n"); exit (0);
-#endif
-#if defined (ns32000)
-  printf ("ns32k-sequent-dynix\n"); exit (0);
-#endif
-#endif
-
-#if defined (_SEQUENT_)
-    struct utsname un;
-
-    uname(&un);
-
-    if (strncmp(un.version, "V2", 2) == 0) {
-       printf ("i386-sequent-ptx2\n"); exit (0);
-    }
-    if (strncmp(un.version, "V1", 2) == 0) { /* XXX is V1 correct? */
-       printf ("i386-sequent-ptx1\n"); exit (0);
-    }
-    printf ("i386-sequent-ptx\n"); exit (0);
-
-#endif
-
-#if defined (vax)
-# if !defined (ultrix)
-#  include <sys/param.h>
-#  if defined (BSD)
-#   if BSD == 43
-      printf ("vax-dec-bsd4.3\n"); exit (0);
-#   else
-#    if BSD == 199006
-      printf ("vax-dec-bsd4.3reno\n"); exit (0);
-#    else
-      printf ("vax-dec-bsd\n"); exit (0);
-#    endif
-#   endif
-#  else
-    printf ("vax-dec-bsd\n"); exit (0);
-#  endif
-# else
-    printf ("vax-dec-ultrix\n"); exit (0);
-# endif
-#endif
-
-#if defined (alliant) && defined (i860)
-  printf ("i860-alliant-bsd\n"); exit (0);
-#endif
-
-  exit (1);
-}
-EOF
-
-$CC_FOR_BUILD -o $dummy $dummy.c 2>/dev/null && SYSTEM_NAME=`$dummy` &&
-       { echo "$SYSTEM_NAME"; exit; }
-
-# Apollos put the system type in the environment.
-
-test -d /usr/apollo && { echo ${ISP}-apollo-${SYSTYPE}; exit; }
-
-# Convex versions that predate uname can use getsysinfo(1)
-
-if [ -x /usr/convex/getsysinfo ]
-then
-    case `getsysinfo -f cpu_type` in
-    c1*)
-       echo c1-convex-bsd
-       exit ;;
-    c2*)
-       if getsysinfo -f scalar_acc
-       then echo c32-convex-bsd
-       else echo c2-convex-bsd
-       fi
-       exit ;;
-    c34*)
-       echo c34-convex-bsd
-       exit ;;
-    c38*)
-       echo c38-convex-bsd
-       exit ;;
-    c4*)
-       echo c4-convex-bsd
-       exit ;;
-    esac
-fi
-
-cat >&2 <<EOF
-$0: unable to guess system type
-
-This script, last modified $timestamp, has failed to recognize
-the operating system you are using. It is advised that you
-download the most up to date version of the config scripts from
-
-  http://savannah.gnu.org/cgi-bin/viewcvs/*checkout*/config/config/config.guess
-and
-  http://savannah.gnu.org/cgi-bin/viewcvs/*checkout*/config/config/config.sub
-
-If the version you run ($0) is already up to date, please
-send the following data and any information you think might be
-pertinent to <config-patches@gnu.org> in order to provide the needed
-information to handle your system.
-
-config.guess timestamp = $timestamp
-
-uname -m = `(uname -m) 2>/dev/null || echo unknown`
-uname -r = `(uname -r) 2>/dev/null || echo unknown`
-uname -s = `(uname -s) 2>/dev/null || echo unknown`
-uname -v = `(uname -v) 2>/dev/null || echo unknown`
-
-/usr/bin/uname -p = `(/usr/bin/uname -p) 2>/dev/null`
-/bin/uname -X     = `(/bin/uname -X) 2>/dev/null`
-
-hostinfo               = `(hostinfo) 2>/dev/null`
-/bin/universe          = `(/bin/universe) 2>/dev/null`
-/usr/bin/arch -k       = `(/usr/bin/arch -k) 2>/dev/null`
-/bin/arch              = `(/bin/arch) 2>/dev/null`
-/usr/bin/oslevel       = `(/usr/bin/oslevel) 2>/dev/null`
-/usr/convex/getsysinfo = `(/usr/convex/getsysinfo) 2>/dev/null`
-
-UNAME_MACHINE = ${UNAME_MACHINE}
-UNAME_RELEASE = ${UNAME_RELEASE}
-UNAME_SYSTEM  = ${UNAME_SYSTEM}
-UNAME_VERSION = ${UNAME_VERSION}
-EOF
-
-exit 1
-
-# Local variables:
-# eval: (add-hook 'write-file-hooks 'time-stamp)
-# time-stamp-start: "timestamp='"
-# time-stamp-format: "%:y-%02m-%02d"
-# time-stamp-end: "'"
-# End:
diff --git a/textclient/config.sub b/textclient/config.sub
deleted file mode 100755 (executable)
index fab0aa3..0000000
+++ /dev/null
@@ -1,1616 +0,0 @@
-#! /bin/sh
-# Configuration validation subroutine script.
-#   Copyright (C) 1992, 1993, 1994, 1995, 1996, 1997, 1998, 1999,
-#   2000, 2001, 2002, 2003, 2004, 2005, 2006 Free Software Foundation,
-#   Inc.
-
-timestamp='2006-09-20'
-
-# This file is (in principle) common to ALL GNU software.
-# The presence of a machine in this file suggests that SOME GNU software
-# can handle that machine.  It does not imply ALL GNU software can.
-#
-# This file is free software; you can redistribute it and/or modify
-# it under the terms of the GNU General Public License as published by
-# the Free Software Foundation; either version 2 of the License, or
-# (at your option) any later version.
-#
-# This program is distributed in the hope that it will be useful,
-# but WITHOUT ANY WARRANTY; without even the implied warranty of
-# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
-# GNU General Public License for more details.
-#
-# You should have received a copy of the GNU General Public License
-# along with this program; if not, write to the Free Software
-# Foundation, Inc., 51 Franklin Street - Fifth Floor, Boston, MA
-# 02110-1301, USA.
-#
-# As a special exception to the GNU General Public License, if you
-# distribute this file as part of a program that contains a
-# configuration script generated by Autoconf, you may include it under
-# the same distribution terms that you use for the rest of that program.
-
-
-# Please send patches to <config-patches@gnu.org>.  Submit a context
-# diff and a properly formatted ChangeLog entry.
-#
-# Configuration subroutine to validate and canonicalize a configuration type.
-# Supply the specified configuration type as an argument.
-# If it is invalid, we print an error message on stderr and exit with code 1.
-# Otherwise, we print the canonical config type on stdout and succeed.
-
-# This file is supposed to be the same for all GNU packages
-# and recognize all the CPU types, system types and aliases
-# that are meaningful with *any* GNU software.
-# Each package is responsible for reporting which valid configurations
-# it does not support.  The user should be able to distinguish
-# a failure to support a valid configuration from a meaningless
-# configuration.
-
-# The goal of this file is to map all the various variations of a given
-# machine specification into a single specification in the form:
-#      CPU_TYPE-MANUFACTURER-OPERATING_SYSTEM
-# or in some cases, the newer four-part form:
-#      CPU_TYPE-MANUFACTURER-KERNEL-OPERATING_SYSTEM
-# It is wrong to echo any other type of specification.
-
-me=`echo "$0" | sed -e 's,.*/,,'`
-
-usage="\
-Usage: $0 [OPTION] CPU-MFR-OPSYS
-       $0 [OPTION] ALIAS
-
-Canonicalize a configuration name.
-
-Operation modes:
-  -h, --help         print this help, then exit
-  -t, --time-stamp   print date of last modification, then exit
-  -v, --version      print version number, then exit
-
-Report bugs and patches to <config-patches@gnu.org>."
-
-version="\
-GNU config.sub ($timestamp)
-
-Copyright (C) 1992, 1993, 1994, 1995, 1996, 1997, 1998, 1999, 2000, 2001, 2002, 2003, 2004, 2005
-Free Software Foundation, Inc.
-
-This is free software; see the source for copying conditions.  There is NO
-warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE."
-
-help="
-Try \`$me --help' for more information."
-
-# Parse command line
-while test $# -gt 0 ; do
-  case $1 in
-    --time-stamp | --time* | -t )
-       echo "$timestamp" ; exit ;;
-    --version | -v )
-       echo "$version" ; exit ;;
-    --help | --h* | -h )
-       echo "$usage"; exit ;;
-    -- )     # Stop option processing
-       shift; break ;;
-    - )        # Use stdin as input.
-       break ;;
-    -* )
-       echo "$me: invalid option $1$help"
-       exit 1 ;;
-
-    *local*)
-       # First pass through any local machine types.
-       echo $1
-       exit ;;
-
-    * )
-       break ;;
-  esac
-done
-
-case $# in
- 0) echo "$me: missing argument$help" >&2
-    exit 1;;
- 1) ;;
- *) echo "$me: too many arguments$help" >&2
-    exit 1;;
-esac
-
-# Separate what the user gave into CPU-COMPANY and OS or KERNEL-OS (if any).
-# Here we must recognize all the valid KERNEL-OS combinations.
-maybe_os=`echo $1 | sed 's/^\(.*\)-\([^-]*-[^-]*\)$/\2/'`
-case $maybe_os in
-  nto-qnx* | linux-gnu* | linux-dietlibc | linux-newlib* | linux-uclibc* | \
-  uclinux-uclibc* | uclinux-gnu* | kfreebsd*-gnu* | knetbsd*-gnu* | netbsd*-gnu* | \
-  storm-chaos* | os2-emx* | rtmk-nova*)
-    os=-$maybe_os
-    basic_machine=`echo $1 | sed 's/^\(.*\)-\([^-]*-[^-]*\)$/\1/'`
-    ;;
-  *)
-    basic_machine=`echo $1 | sed 's/-[^-]*$//'`
-    if [ $basic_machine != $1 ]
-    then os=`echo $1 | sed 's/.*-/-/'`
-    else os=; fi
-    ;;
-esac
-
-### Let's recognize common machines as not being operating systems so
-### that things like config.sub decstation-3100 work.  We also
-### recognize some manufacturers as not being operating systems, so we
-### can provide default operating systems below.
-case $os in
-       -sun*os*)
-               # Prevent following clause from handling this invalid input.
-               ;;
-       -dec* | -mips* | -sequent* | -encore* | -pc532* | -sgi* | -sony* | \
-       -att* | -7300* | -3300* | -delta* | -motorola* | -sun[234]* | \
-       -unicom* | -ibm* | -next | -hp | -isi* | -apollo | -altos* | \
-       -convergent* | -ncr* | -news | -32* | -3600* | -3100* | -hitachi* |\
-       -c[123]* | -convex* | -sun | -crds | -omron* | -dg | -ultra | -tti* | \
-       -harris | -dolphin | -highlevel | -gould | -cbm | -ns | -masscomp | \
-       -apple | -axis | -knuth | -cray)
-               os=
-               basic_machine=$1
-               ;;
-       -sim | -cisco | -oki | -wec | -winbond)
-               os=
-               basic_machine=$1
-               ;;
-       -scout)
-               ;;
-       -wrs)
-               os=-vxworks
-               basic_machine=$1
-               ;;
-       -chorusos*)
-               os=-chorusos
-               basic_machine=$1
-               ;;
-       -chorusrdb)
-               os=-chorusrdb
-               basic_machine=$1
-               ;;
-       -hiux*)
-               os=-hiuxwe2
-               ;;
-       -sco6)
-               os=-sco5v6
-               basic_machine=`echo $1 | sed -e 's/86-.*/86-pc/'`
-               ;;
-       -sco5)
-               os=-sco3.2v5
-               basic_machine=`echo $1 | sed -e 's/86-.*/86-pc/'`
-               ;;
-       -sco4)
-               os=-sco3.2v4
-               basic_machine=`echo $1 | sed -e 's/86-.*/86-pc/'`
-               ;;
-       -sco3.2.[4-9]*)
-               os=`echo $os | sed -e 's/sco3.2./sco3.2v/'`
-               basic_machine=`echo $1 | sed -e 's/86-.*/86-pc/'`
-               ;;
-       -sco3.2v[4-9]*)
-               # Don't forget version if it is 3.2v4 or newer.
-               basic_machine=`echo $1 | sed -e 's/86-.*/86-pc/'`
-               ;;
-       -sco5v6*)
-               # Don't forget version if it is 3.2v4 or newer.
-               basic_machine=`echo $1 | sed -e 's/86-.*/86-pc/'`
-               ;;
-       -sco*)
-               os=-sco3.2v2
-               basic_machine=`echo $1 | sed -e 's/86-.*/86-pc/'`
-               ;;
-       -udk*)
-               basic_machine=`echo $1 | sed -e 's/86-.*/86-pc/'`
-               ;;
-       -isc)
-               os=-isc2.2
-               basic_machine=`echo $1 | sed -e 's/86-.*/86-pc/'`
-               ;;
-       -clix*)
-               basic_machine=clipper-intergraph
-               ;;
-       -isc*)
-               basic_machine=`echo $1 | sed -e 's/86-.*/86-pc/'`
-               ;;
-       -lynx*)
-               os=-lynxos
-               ;;
-       -ptx*)
-               basic_machine=`echo $1 | sed -e 's/86-.*/86-sequent/'`
-               ;;
-       -windowsnt*)
-               os=`echo $os | sed -e 's/windowsnt/winnt/'`
-               ;;
-       -psos*)
-               os=-psos
-               ;;
-       -mint | -mint[0-9]*)
-               basic_machine=m68k-atari
-               os=-mint
-               ;;
-esac
-
-# Decode aliases for certain CPU-COMPANY combinations.
-case $basic_machine in
-       # Recognize the basic CPU types without company name.
-       # Some are omitted here because they have special meanings below.
-       1750a | 580 \
-       | a29k \
-       | alpha | alphaev[4-8] | alphaev56 | alphaev6[78] | alphapca5[67] \
-       | alpha64 | alpha64ev[4-8] | alpha64ev56 | alpha64ev6[78] | alpha64pca5[67] \
-       | am33_2.0 \
-       | arc | arm | arm[bl]e | arme[lb] | armv[2345] | armv[345][lb] | avr | avr32 \
-       | bfin \
-       | c4x | clipper \
-       | d10v | d30v | dlx | dsp16xx \
-       | fr30 | frv \
-       | h8300 | h8500 | hppa | hppa1.[01] | hppa2.0 | hppa2.0[nw] | hppa64 \
-       | i370 | i860 | i960 | ia64 \
-       | ip2k | iq2000 \
-       | m32c | m32r | m32rle | m68000 | m68k | m88k \
-       | maxq | mb | microblaze | mcore \
-       | mips | mipsbe | mipseb | mipsel | mipsle \
-       | mips16 \
-       | mips64 | mips64el \
-       | mips64vr | mips64vrel \
-       | mips64orion | mips64orionel \
-       | mips64vr4100 | mips64vr4100el \
-       | mips64vr4300 | mips64vr4300el \
-       | mips64vr5000 | mips64vr5000el \
-       | mips64vr5900 | mips64vr5900el \
-       | mipsisa32 | mipsisa32el \
-       | mipsisa32r2 | mipsisa32r2el \
-       | mipsisa64 | mipsisa64el \
-       | mipsisa64r2 | mipsisa64r2el \
-       | mipsisa64sb1 | mipsisa64sb1el \
-       | mipsisa64sr71k | mipsisa64sr71kel \
-       | mipstx39 | mipstx39el \
-       | mn10200 | mn10300 \
-       | mt \
-       | msp430 \
-       | nios | nios2 \
-       | ns16k | ns32k \
-       | or32 \
-       | pdp10 | pdp11 | pj | pjl \
-       | powerpc | powerpc64 | powerpc64le | powerpcle | ppcbe \
-       | pyramid \
-       | score \
-       | sh | sh[1234] | sh[24]a | sh[23]e | sh[34]eb | sheb | shbe | shle | sh[1234]le | sh3ele \
-       | sh64 | sh64le \
-       | sparc | sparc64 | sparc64b | sparc64v | sparc86x | sparclet | sparclite \
-       | sparcv8 | sparcv9 | sparcv9b | sparcv9v \
-       | spu | strongarm \
-       | tahoe | thumb | tic4x | tic80 | tron \
-       | v850 | v850e \
-       | we32k \
-       | x86 | xc16x | xscale | xscalee[bl] | xstormy16 | xtensa \
-       | z8k)
-               basic_machine=$basic_machine-unknown
-               ;;
-       m6811 | m68hc11 | m6812 | m68hc12)
-               # Motorola 68HC11/12.
-               basic_machine=$basic_machine-unknown
-               os=-none
-               ;;
-       m88110 | m680[12346]0 | m683?2 | m68360 | m5200 | v70 | w65 | z8k)
-               ;;
-       ms1)
-               basic_machine=mt-unknown
-               ;;
-
-       # We use `pc' rather than `unknown'
-       # because (1) that's what they normally are, and
-       # (2) the word "unknown" tends to confuse beginning users.
-       i*86 | x86_64)
-         basic_machine=$basic_machine-pc
-         ;;
-       # Object if more than one company name word.
-       *-*-*)
-               echo Invalid configuration \`$1\': machine \`$basic_machine\' not recognized 1>&2
-               exit 1
-               ;;
-       # Recognize the basic CPU types with company name.
-       580-* \
-       | a29k-* \
-       | alpha-* | alphaev[4-8]-* | alphaev56-* | alphaev6[78]-* \
-       | alpha64-* | alpha64ev[4-8]-* | alpha64ev56-* | alpha64ev6[78]-* \
-       | alphapca5[67]-* | alpha64pca5[67]-* | arc-* \
-       | arm-*  | armbe-* | armle-* | armeb-* | armv*-* \
-       | avr-* | avr32-* \
-       | bfin-* | bs2000-* \
-       | c[123]* | c30-* | [cjt]90-* | c4x-* | c54x-* | c55x-* | c6x-* \
-       | clipper-* | craynv-* | cydra-* \
-       | d10v-* | d30v-* | dlx-* \
-       | elxsi-* \
-       | f30[01]-* | f700-* | fr30-* | frv-* | fx80-* \
-       | h8300-* | h8500-* \
-       | hppa-* | hppa1.[01]-* | hppa2.0-* | hppa2.0[nw]-* | hppa64-* \
-       | i*86-* | i860-* | i960-* | ia64-* \
-       | ip2k-* | iq2000-* \
-       | m32c-* | m32r-* | m32rle-* \
-       | m68000-* | m680[012346]0-* | m68360-* | m683?2-* | m68k-* \
-       | m88110-* | m88k-* | maxq-* | mcore-* \
-       | mips-* | mipsbe-* | mipseb-* | mipsel-* | mipsle-* \
-       | mips16-* \
-       | mips64-* | mips64el-* \
-       | mips64vr-* | mips64vrel-* \
-       | mips64orion-* | mips64orionel-* \
-       | mips64vr4100-* | mips64vr4100el-* \
-       | mips64vr4300-* | mips64vr4300el-* \
-       | mips64vr5000-* | mips64vr5000el-* \
-       | mips64vr5900-* | mips64vr5900el-* \
-       | mipsisa32-* | mipsisa32el-* \
-       | mipsisa32r2-* | mipsisa32r2el-* \
-       | mipsisa64-* | mipsisa64el-* \
-       | mipsisa64r2-* | mipsisa64r2el-* \
-       | mipsisa64sb1-* | mipsisa64sb1el-* \
-       | mipsisa64sr71k-* | mipsisa64sr71kel-* \
-       | mipstx39-* | mipstx39el-* \
-       | mmix-* \
-       | mt-* \
-       | msp430-* \
-       | nios-* | nios2-* \
-       | none-* | np1-* | ns16k-* | ns32k-* \
-       | orion-* \
-       | pdp10-* | pdp11-* | pj-* | pjl-* | pn-* | power-* \
-       | powerpc-* | powerpc64-* | powerpc64le-* | powerpcle-* | ppcbe-* \
-       | pyramid-* \
-       | romp-* | rs6000-* \
-       | sh-* | sh[1234]-* | sh[24]a-* | sh[23]e-* | sh[34]eb-* | sheb-* | shbe-* \
-       | shle-* | sh[1234]le-* | sh3ele-* | sh64-* | sh64le-* \
-       | sparc-* | sparc64-* | sparc64b-* | sparc64v-* | sparc86x-* | sparclet-* \
-       | sparclite-* \
-       | sparcv8-* | sparcv9-* | sparcv9b-* | sparcv9v-* | strongarm-* | sv1-* | sx?-* \
-       | tahoe-* | thumb-* \
-       | tic30-* | tic4x-* | tic54x-* | tic55x-* | tic6x-* | tic80-* \
-       | tron-* \
-       | v850-* | v850e-* | vax-* \
-       | we32k-* \
-       | x86-* | x86_64-* | xc16x-* | xps100-* | xscale-* | xscalee[bl]-* \
-       | xstormy16-* | xtensa-* \
-       | ymp-* \
-       | z8k-*)
-               ;;
-       # Recognize the various machine names and aliases which stand
-       # for a CPU type and a company and sometimes even an OS.
-       386bsd)
-               basic_machine=i386-unknown
-               os=-bsd
-               ;;
-       3b1 | 7300 | 7300-att | att-7300 | pc7300 | safari | unixpc)
-               basic_machine=m68000-att
-               ;;
-       3b*)
-               basic_machine=we32k-att
-               ;;
-       a29khif)
-               basic_machine=a29k-amd
-               os=-udi
-               ;;
-       abacus)
-               basic_machine=abacus-unknown
-               ;;
-       adobe68k)
-               basic_machine=m68010-adobe
-               os=-scout
-               ;;
-       alliant | fx80)
-               basic_machine=fx80-alliant
-               ;;
-       altos | altos3068)
-               basic_machine=m68k-altos
-               ;;
-       am29k)
-               basic_machine=a29k-none
-               os=-bsd
-               ;;
-       amd64)
-               basic_machine=x86_64-pc
-               ;;
-       amd64-*)
-               basic_machine=x86_64-`echo $basic_machine | sed 's/^[^-]*-//'`
-               ;;
-       amdahl)
-               basic_machine=580-amdahl
-               os=-sysv
-               ;;
-       amiga | amiga-*)
-               basic_machine=m68k-unknown
-               ;;
-       amigaos | amigados)
-               basic_machine=m68k-unknown
-               os=-amigaos
-               ;;
-       amigaunix | amix)
-               basic_machine=m68k-unknown
-               os=-sysv4
-               ;;
-       apollo68)
-               basic_machine=m68k-apollo
-               os=-sysv
-               ;;
-       apollo68bsd)
-               basic_machine=m68k-apollo
-               os=-bsd
-               ;;
-       aux)
-               basic_machine=m68k-apple
-               os=-aux
-               ;;
-       balance)
-               basic_machine=ns32k-sequent
-               os=-dynix
-               ;;
-       c90)
-               basic_machine=c90-cray
-               os=-unicos
-               ;;
-       convex-c1)
-               basic_machine=c1-convex
-               os=-bsd
-               ;;
-       convex-c2)
-               basic_machine=c2-convex
-               os=-bsd
-               ;;
-       convex-c32)
-               basic_machine=c32-convex
-               os=-bsd
-               ;;
-       convex-c34)
-               basic_machine=c34-convex
-               os=-bsd
-               ;;
-       convex-c38)
-               basic_machine=c38-convex
-               os=-bsd
-               ;;
-       cray | j90)
-               basic_machine=j90-cray
-               os=-unicos
-               ;;
-       craynv)
-               basic_machine=craynv-cray
-               os=-unicosmp
-               ;;
-       cr16c)
-               basic_machine=cr16c-unknown
-               os=-elf
-               ;;
-       crds | unos)
-               basic_machine=m68k-crds
-               ;;
-       crisv32 | crisv32-* | etraxfs*)
-               basic_machine=crisv32-axis
-               ;;
-       cris | cris-* | etrax*)
-               basic_machine=cris-axis
-               ;;
-       crx)
-               basic_machine=crx-unknown
-               os=-elf
-               ;;
-       da30 | da30-*)
-               basic_machine=m68k-da30
-               ;;
-       decstation | decstation-3100 | pmax | pmax-* | pmin | dec3100 | decstatn)
-               basic_machine=mips-dec
-               ;;
-       decsystem10* | dec10*)
-               basic_machine=pdp10-dec
-               os=-tops10
-               ;;
-       decsystem20* | dec20*)
-               basic_machine=pdp10-dec
-               os=-tops20
-               ;;
-       delta | 3300 | motorola-3300 | motorola-delta \
-             | 3300-motorola | delta-motorola)
-               basic_machine=m68k-motorola
-               ;;
-       delta88)
-               basic_machine=m88k-motorola
-               os=-sysv3
-               ;;
-       djgpp)
-               basic_machine=i586-pc
-               os=-msdosdjgpp
-               ;;
-       dpx20 | dpx20-*)
-               basic_machine=rs6000-bull
-               os=-bosx
-               ;;
-       dpx2* | dpx2*-bull)
-               basic_machine=m68k-bull
-               os=-sysv3
-               ;;
-       ebmon29k)
-               basic_machine=a29k-amd
-               os=-ebmon
-               ;;
-       elxsi)
-               basic_machine=elxsi-elxsi
-               os=-bsd
-               ;;
-       encore | umax | mmax)
-               basic_machine=ns32k-encore
-               ;;
-       es1800 | OSE68k | ose68k | ose | OSE)
-               basic_machine=m68k-ericsson
-               os=-ose
-               ;;
-       fx2800)
-               basic_machine=i860-alliant
-               ;;
-       genix)
-               basic_machine=ns32k-ns
-               ;;
-       gmicro)
-               basic_machine=tron-gmicro
-               os=-sysv
-               ;;
-       go32)
-               basic_machine=i386-pc
-               os=-go32
-               ;;
-       h3050r* | hiux*)
-               basic_machine=hppa1.1-hitachi
-               os=-hiuxwe2
-               ;;
-       h8300hms)
-               basic_machine=h8300-hitachi
-               os=-hms
-               ;;
-       h8300xray)
-               basic_machine=h8300-hitachi
-               os=-xray
-               ;;
-       h8500hms)
-               basic_machine=h8500-hitachi
-               os=-hms
-               ;;
-       harris)
-               basic_machine=m88k-harris
-               os=-sysv3
-               ;;
-       hp300-*)
-               basic_machine=m68k-hp
-               ;;
-       hp300bsd)
-               basic_machine=m68k-hp
-               os=-bsd
-               ;;
-       hp300hpux)
-               basic_machine=m68k-hp
-               os=-hpux
-               ;;
-       hp3k9[0-9][0-9] | hp9[0-9][0-9])
-               basic_machine=hppa1.0-hp
-               ;;
-       hp9k2[0-9][0-9] | hp9k31[0-9])
-               basic_machine=m68000-hp
-               ;;
-       hp9k3[2-9][0-9])
-               basic_machine=m68k-hp
-               ;;
-       hp9k6[0-9][0-9] | hp6[0-9][0-9])
-               basic_machine=hppa1.0-hp
-               ;;
-       hp9k7[0-79][0-9] | hp7[0-79][0-9])
-               basic_machine=hppa1.1-hp
-               ;;
-       hp9k78[0-9] | hp78[0-9])
-               # FIXME: really hppa2.0-hp
-               basic_machine=hppa1.1-hp
-               ;;
-       hp9k8[67]1 | hp8[67]1 | hp9k80[24] | hp80[24] | hp9k8[78]9 | hp8[78]9 | hp9k893 | hp893)
-               # FIXME: really hppa2.0-hp
-               basic_machine=hppa1.1-hp
-               ;;
-       hp9k8[0-9][13679] | hp8[0-9][13679])
-               basic_machine=hppa1.1-hp
-               ;;
-       hp9k8[0-9][0-9] | hp8[0-9][0-9])
-               basic_machine=hppa1.0-hp
-               ;;
-       hppa-next)
-               os=-nextstep3
-               ;;
-       hppaosf)
-               basic_machine=hppa1.1-hp
-               os=-osf
-               ;;
-       hppro)
-               basic_machine=hppa1.1-hp
-               os=-proelf
-               ;;
-       i370-ibm* | ibm*)
-               basic_machine=i370-ibm
-               ;;
-# I'm not sure what "Sysv32" means.  Should this be sysv3.2?
-       i*86v32)
-               basic_machine=`echo $1 | sed -e 's/86.*/86-pc/'`
-               os=-sysv32
-               ;;
-       i*86v4*)
-               basic_machine=`echo $1 | sed -e 's/86.*/86-pc/'`
-               os=-sysv4
-               ;;
-       i*86v)
-               basic_machine=`echo $1 | sed -e 's/86.*/86-pc/'`
-               os=-sysv
-               ;;
-       i*86sol2)
-               basic_machine=`echo $1 | sed -e 's/86.*/86-pc/'`
-               os=-solaris2
-               ;;
-       i386mach)
-               basic_machine=i386-mach
-               os=-mach
-               ;;
-       i386-vsta | vsta)
-               basic_machine=i386-unknown
-               os=-vsta
-               ;;
-       iris | iris4d)
-               basic_machine=mips-sgi
-               case $os in
-                   -irix*)
-                       ;;
-                   *)
-                       os=-irix4
-                       ;;
-               esac
-               ;;
-       isi68 | isi)
-               basic_machine=m68k-isi
-               os=-sysv
-               ;;
-       m88k-omron*)
-               basic_machine=m88k-omron
-               ;;
-       magnum | m3230)
-               basic_machine=mips-mips
-               os=-sysv
-               ;;
-       merlin)
-               basic_machine=ns32k-utek
-               os=-sysv
-               ;;
-       mingw32)
-               basic_machine=i386-pc
-               os=-mingw32
-               ;;
-       miniframe)
-               basic_machine=m68000-convergent
-               ;;
-       *mint | -mint[0-9]* | *MiNT | *MiNT[0-9]*)
-               basic_machine=m68k-atari
-               os=-mint
-               ;;
-       mips3*-*)
-               basic_machine=`echo $basic_machine | sed -e 's/mips3/mips64/'`
-               ;;
-       mips3*)
-               basic_machine=`echo $basic_machine | sed -e 's/mips3/mips64/'`-unknown
-               ;;
-       monitor)
-               basic_machine=m68k-rom68k
-               os=-coff
-               ;;
-       morphos)
-               basic_machine=powerpc-unknown
-               os=-morphos
-               ;;
-       msdos)
-               basic_machine=i386-pc
-               os=-msdos
-               ;;
-       ms1-*)
-               basic_machine=`echo $basic_machine | sed -e 's/ms1-/mt-/'`
-               ;;
-       mvs)
-               basic_machine=i370-ibm
-               os=-mvs
-               ;;
-       ncr3000)
-               basic_machine=i486-ncr
-               os=-sysv4
-               ;;
-       netbsd386)
-               basic_machine=i386-unknown
-               os=-netbsd
-               ;;
-       netwinder)
-               basic_machine=armv4l-rebel
-               os=-linux
-               ;;
-       news | news700 | news800 | news900)
-               basic_machine=m68k-sony
-               os=-newsos
-               ;;
-       news1000)
-               basic_machine=m68030-sony
-               os=-newsos
-               ;;
-       news-3600 | risc-news)
-               basic_machine=mips-sony
-               os=-newsos
-               ;;
-       necv70)
-               basic_machine=v70-nec
-               os=-sysv
-               ;;
-       next | m*-next )
-               basic_machine=m68k-next
-               case $os in
-                   -nextstep* )
-                       ;;
-                   -ns2*)
-                     os=-nextstep2
-                       ;;
-                   *)
-                     os=-nextstep3
-                       ;;
-               esac
-               ;;
-       nh3000)
-               basic_machine=m68k-harris
-               os=-cxux
-               ;;
-       nh[45]000)
-               basic_machine=m88k-harris
-               os=-cxux
-               ;;
-       nindy960)
-               basic_machine=i960-intel
-               os=-nindy
-               ;;
-       mon960)
-               basic_machine=i960-intel
-               os=-mon960
-               ;;
-       nonstopux)
-               basic_machine=mips-compaq
-               os=-nonstopux
-               ;;
-       np1)
-               basic_machine=np1-gould
-               ;;
-       nsr-tandem)
-               basic_machine=nsr-tandem
-               ;;
-       op50n-* | op60c-*)
-               basic_machine=hppa1.1-oki
-               os=-proelf
-               ;;
-       openrisc | openrisc-*)
-               basic_machine=or32-unknown
-               ;;
-       os400)
-               basic_machine=powerpc-ibm
-               os=-os400
-               ;;
-       OSE68000 | ose68000)
-               basic_machine=m68000-ericsson
-               os=-ose
-               ;;
-       os68k)
-               basic_machine=m68k-none
-               os=-os68k
-               ;;
-       pa-hitachi)
-               basic_machine=hppa1.1-hitachi
-               os=-hiuxwe2
-               ;;
-       paragon)
-               basic_machine=i860-intel
-               os=-osf
-               ;;
-       pbd)
-               basic_machine=sparc-tti
-               ;;
-       pbb)
-               basic_machine=m68k-tti
-               ;;
-       pc532 | pc532-*)
-               basic_machine=ns32k-pc532
-               ;;
-       pc98)
-               basic_machine=i386-pc
-               ;;
-       pc98-*)
-               basic_machine=i386-`echo $basic_machine | sed 's/^[^-]*-//'`
-               ;;
-       pentium | p5 | k5 | k6 | nexgen | viac3)
-               basic_machine=i586-pc
-               ;;
-       pentiumpro | p6 | 6x86 | athlon | athlon_*)
-               basic_machine=i686-pc
-               ;;
-       pentiumii | pentium2 | pentiumiii | pentium3)
-               basic_machine=i686-pc
-               ;;
-       pentium4)
-               basic_machine=i786-pc
-               ;;
-       pentium-* | p5-* | k5-* | k6-* | nexgen-* | viac3-*)
-               basic_machine=i586-`echo $basic_machine | sed 's/^[^-]*-//'`
-               ;;
-       pentiumpro-* | p6-* | 6x86-* | athlon-*)
-               basic_machine=i686-`echo $basic_machine | sed 's/^[^-]*-//'`
-               ;;
-       pentiumii-* | pentium2-* | pentiumiii-* | pentium3-*)
-               basic_machine=i686-`echo $basic_machine | sed 's/^[^-]*-//'`
-               ;;
-       pentium4-*)
-               basic_machine=i786-`echo $basic_machine | sed 's/^[^-]*-//'`
-               ;;
-       pn)
-               basic_machine=pn-gould
-               ;;
-       power)  basic_machine=power-ibm
-               ;;
-       ppc)    basic_machine=powerpc-unknown
-               ;;
-       ppc-*)  basic_machine=powerpc-`echo $basic_machine | sed 's/^[^-]*-//'`
-               ;;
-       ppcle | powerpclittle | ppc-le | powerpc-little)
-               basic_machine=powerpcle-unknown
-               ;;
-       ppcle-* | powerpclittle-*)
-               basic_machine=powerpcle-`echo $basic_machine | sed 's/^[^-]*-//'`
-               ;;
-       ppc64)  basic_machine=powerpc64-unknown
-               ;;
-       ppc64-*) basic_machine=powerpc64-`echo $basic_machine | sed 's/^[^-]*-//'`
-               ;;
-       ppc64le | powerpc64little | ppc64-le | powerpc64-little)
-               basic_machine=powerpc64le-unknown
-               ;;
-       ppc64le-* | powerpc64little-*)
-               basic_machine=powerpc64le-`echo $basic_machine | sed 's/^[^-]*-//'`
-               ;;
-       ps2)
-               basic_machine=i386-ibm
-               ;;
-       pw32)
-               basic_machine=i586-unknown
-               os=-pw32
-               ;;
-       rdos)
-               basic_machine=i386-pc
-               os=-rdos
-               ;;
-       rom68k)
-               basic_machine=m68k-rom68k
-               os=-coff
-               ;;
-       rm[46]00)
-               basic_machine=mips-siemens
-               ;;
-       rtpc | rtpc-*)
-               basic_machine=romp-ibm
-               ;;
-       s390 | s390-*)
-               basic_machine=s390-ibm
-               ;;
-       s390x | s390x-*)
-               basic_machine=s390x-ibm
-               ;;
-       sa29200)
-               basic_machine=a29k-amd
-               os=-udi
-               ;;
-       sb1)
-               basic_machine=mipsisa64sb1-unknown
-               ;;
-       sb1el)
-               basic_machine=mipsisa64sb1el-unknown
-               ;;
-       sde)
-               basic_machine=mipsisa32-sde
-               os=-elf
-               ;;
-       sei)
-               basic_machine=mips-sei
-               os=-seiux
-               ;;
-       sequent)
-               basic_machine=i386-sequent
-               ;;
-       sh)
-               basic_machine=sh-hitachi
-               os=-hms
-               ;;
-       sh64)
-               basic_machine=sh64-unknown
-               ;;
-       sparclite-wrs | simso-wrs)
-               basic_machine=sparclite-wrs
-               os=-vxworks
-               ;;
-       sps7)
-               basic_machine=m68k-bull
-               os=-sysv2
-               ;;
-       spur)
-               basic_machine=spur-unknown
-               ;;
-       st2000)
-               basic_machine=m68k-tandem
-               ;;
-       stratus)
-               basic_machine=i860-stratus
-               os=-sysv4
-               ;;
-       sun2)
-               basic_machine=m68000-sun
-               ;;
-       sun2os3)
-               basic_machine=m68000-sun
-               os=-sunos3
-               ;;
-       sun2os4)
-               basic_machine=m68000-sun
-               os=-sunos4
-               ;;
-       sun3os3)
-               basic_machine=m68k-sun
-               os=-sunos3
-               ;;
-       sun3os4)
-               basic_machine=m68k-sun
-               os=-sunos4
-               ;;
-       sun4os3)
-               basic_machine=sparc-sun
-               os=-sunos3
-               ;;
-       sun4os4)
-               basic_machine=sparc-sun
-               os=-sunos4
-               ;;
-       sun4sol2)
-               basic_machine=sparc-sun
-               os=-solaris2
-               ;;
-       sun3 | sun3-*)
-               basic_machine=m68k-sun
-               ;;
-       sun4)
-               basic_machine=sparc-sun
-               ;;
-       sun386 | sun386i | roadrunner)
-               basic_machine=i386-sun
-               ;;
-       sv1)
-               basic_machine=sv1-cray
-               os=-unicos
-               ;;
-       symmetry)
-               basic_machine=i386-sequent
-               os=-dynix
-               ;;
-       t3e)
-               basic_machine=alphaev5-cray
-               os=-unicos
-               ;;
-       t90)
-               basic_machine=t90-cray
-               os=-unicos
-               ;;
-       tic54x | c54x*)
-               basic_machine=tic54x-unknown
-               os=-coff
-               ;;
-       tic55x | c55x*)
-               basic_machine=tic55x-unknown
-               os=-coff
-               ;;
-       tic6x | c6x*)
-               basic_machine=tic6x-unknown
-               os=-coff
-               ;;
-       tx39)
-               basic_machine=mipstx39-unknown
-               ;;
-       tx39el)
-               basic_machine=mipstx39el-unknown
-               ;;
-       toad1)
-               basic_machine=pdp10-xkl
-               os=-tops20
-               ;;
-       tower | tower-32)
-               basic_machine=m68k-ncr
-               ;;
-       tpf)
-               basic_machine=s390x-ibm
-               os=-tpf
-               ;;
-       udi29k)
-               basic_machine=a29k-amd
-               os=-udi
-               ;;
-       ultra3)
-               basic_machine=a29k-nyu
-               os=-sym1
-               ;;
-       v810 | necv810)
-               basic_machine=v810-nec
-               os=-none
-               ;;
-       vaxv)
-               basic_machine=vax-dec
-               os=-sysv
-               ;;
-       vms)
-               basic_machine=vax-dec
-               os=-vms
-               ;;
-       vpp*|vx|vx-*)
-               basic_machine=f301-fujitsu
-               ;;
-       vxworks960)
-               basic_machine=i960-wrs
-               os=-vxworks
-               ;;
-       vxworks68)
-               basic_machine=m68k-wrs
-               os=-vxworks
-               ;;
-       vxworks29k)
-               basic_machine=a29k-wrs
-               os=-vxworks
-               ;;
-       w65*)
-               basic_machine=w65-wdc
-               os=-none
-               ;;
-       w89k-*)
-               basic_machine=hppa1.1-winbond
-               os=-proelf
-               ;;
-       xbox)
-               basic_machine=i686-pc
-               os=-mingw32
-               ;;
-       xps | xps100)
-               basic_machine=xps100-honeywell
-               ;;
-       ymp)
-               basic_machine=ymp-cray
-               os=-unicos
-               ;;
-       z8k-*-coff)
-               basic_machine=z8k-unknown
-               os=-sim
-               ;;
-       none)
-               basic_machine=none-none
-               os=-none
-               ;;
-
-# Here we handle the default manufacturer of certain CPU types.  It is in
-# some cases the only manufacturer, in others, it is the most popular.
-       w89k)
-               basic_machine=hppa1.1-winbond
-               ;;
-       op50n)
-               basic_machine=hppa1.1-oki
-               ;;
-       op60c)
-               basic_machine=hppa1.1-oki
-               ;;
-       romp)
-               basic_machine=romp-ibm
-               ;;
-       mmix)
-               basic_machine=mmix-knuth
-               ;;
-       rs6000)
-               basic_machine=rs6000-ibm
-               ;;
-       vax)
-               basic_machine=vax-dec
-               ;;
-       pdp10)
-               # there are many clones, so DEC is not a safe bet
-               basic_machine=pdp10-unknown
-               ;;
-       pdp11)
-               basic_machine=pdp11-dec
-               ;;
-       we32k)
-               basic_machine=we32k-att
-               ;;
-       sh[1234] | sh[24]a | sh[34]eb | sh[1234]le | sh[23]ele)
-               basic_machine=sh-unknown
-               ;;
-       sparc | sparcv8 | sparcv9 | sparcv9b | sparcv9v)
-               basic_machine=sparc-sun
-               ;;
-       cydra)
-               basic_machine=cydra-cydrome
-               ;;
-       orion)
-               basic_machine=orion-highlevel
-               ;;
-       orion105)
-               basic_machine=clipper-highlevel
-               ;;
-       mac | mpw | mac-mpw)
-               basic_machine=m68k-apple
-               ;;
-       pmac | pmac-mpw)
-               basic_machine=powerpc-apple
-               ;;
-       *-unknown)
-               # Make sure to match an already-canonicalized machine name.
-               ;;
-       *)
-               echo Invalid configuration \`$1\': machine \`$basic_machine\' not recognized 1>&2
-               exit 1
-               ;;
-esac
-
-# Here we canonicalize certain aliases for manufacturers.
-case $basic_machine in
-       *-digital*)
-               basic_machine=`echo $basic_machine | sed 's/digital.*/dec/'`
-               ;;
-       *-commodore*)
-               basic_machine=`echo $basic_machine | sed 's/commodore.*/cbm/'`
-               ;;
-       *)
-               ;;
-esac
-
-# Decode manufacturer-specific aliases for certain operating systems.
-
-if [ x"$os" != x"" ]
-then
-case $os in
-        # First match some system type aliases
-        # that might get confused with valid system types.
-       # -solaris* is a basic system type, with this one exception.
-       -solaris1 | -solaris1.*)
-               os=`echo $os | sed -e 's|solaris1|sunos4|'`
-               ;;
-       -solaris)
-               os=-solaris2
-               ;;
-       -svr4*)
-               os=-sysv4
-               ;;
-       -unixware*)
-               os=-sysv4.2uw
-               ;;
-       -gnu/linux*)
-               os=`echo $os | sed -e 's|gnu/linux|linux-gnu|'`
-               ;;
-       # First accept the basic system types.
-       # The portable systems comes first.
-       # Each alternative MUST END IN A *, to match a version number.
-       # -sysv* is not here because it comes later, after sysvr4.
-       -gnu* | -bsd* | -mach* | -minix* | -genix* | -ultrix* | -irix* \
-             | -*vms* | -sco* | -esix* | -isc* | -aix* | -sunos | -sunos[34]*\
-             | -hpux* | -unos* | -osf* | -luna* | -dgux* | -solaris* | -sym* \
-             | -amigaos* | -amigados* | -msdos* | -newsos* | -unicos* | -aof* \
-             | -aos* \
-             | -nindy* | -vxsim* | -vxworks* | -ebmon* | -hms* | -mvs* \
-             | -clix* | -riscos* | -uniplus* | -iris* | -rtu* | -xenix* \
-             | -hiux* | -386bsd* | -knetbsd* | -mirbsd* | -netbsd* \
-             | -openbsd* | -solidbsd* \
-             | -ekkobsd* | -kfreebsd* | -freebsd* | -riscix* | -lynxos* \
-             | -bosx* | -nextstep* | -cxux* | -aout* | -elf* | -oabi* \
-             | -ptx* | -coff* | -ecoff* | -winnt* | -domain* | -vsta* \
-             | -udi* | -eabi* | -lites* | -ieee* | -go32* | -aux* \
-             | -chorusos* | -chorusrdb* \
-             | -cygwin* | -pe* | -psos* | -moss* | -proelf* | -rtems* \
-             | -mingw32* | -linux-gnu* | -linux-newlib* | -linux-uclibc* \
-             | -uxpv* | -beos* | -mpeix* | -udk* \
-             | -interix* | -uwin* | -mks* | -rhapsody* | -darwin* | -opened* \
-             | -openstep* | -oskit* | -conix* | -pw32* | -nonstopux* \
-             | -storm-chaos* | -tops10* | -tenex* | -tops20* | -its* \
-             | -os2* | -vos* | -palmos* | -uclinux* | -nucleus* \
-             | -morphos* | -superux* | -rtmk* | -rtmk-nova* | -windiss* \
-             | -powermax* | -dnix* | -nx6 | -nx7 | -sei* | -dragonfly* \
-             | -skyos* | -haiku* | -rdos* | -toppers*)
-       # Remember, each alternative MUST END IN *, to match a version number.
-               ;;
-       -qnx*)
-               case $basic_machine in
-                   x86-* | i*86-*)
-                       ;;
-                   *)
-                       os=-nto$os
-                       ;;
-               esac
-               ;;
-       -nto-qnx*)
-               ;;
-       -nto*)
-               os=`echo $os | sed -e 's|nto|nto-qnx|'`
-               ;;
-       -sim | -es1800* | -hms* | -xray | -os68k* | -none* | -v88r* \
-             | -windows* | -osx | -abug | -netware* | -os9* | -beos* | -haiku* \
-             | -macos* | -mpw* | -magic* | -mmixware* | -mon960* | -lnews*)
-               ;;
-       -mac*)
-               os=`echo $os | sed -e 's|mac|macos|'`
-               ;;
-       -linux-dietlibc)
-               os=-linux-dietlibc
-               ;;
-       -linux*)
-               os=`echo $os | sed -e 's|linux|linux-gnu|'`
-               ;;
-       -sunos5*)
-               os=`echo $os | sed -e 's|sunos5|solaris2|'`
-               ;;
-       -sunos6*)
-               os=`echo $os | sed -e 's|sunos6|solaris3|'`
-               ;;
-       -opened*)
-               os=-openedition
-               ;;
-        -os400*)
-               os=-os400
-               ;;
-       -wince*)
-               os=-wince
-               ;;
-       -osfrose*)
-               os=-osfrose
-               ;;
-       -osf*)
-               os=-osf
-               ;;
-       -utek*)
-               os=-bsd
-               ;;
-       -dynix*)
-               os=-bsd
-               ;;
-       -acis*)
-               os=-aos
-               ;;
-       -atheos*)
-               os=-atheos
-               ;;
-       -syllable*)
-               os=-syllable
-               ;;
-       -386bsd)
-               os=-bsd
-               ;;
-       -ctix* | -uts*)
-               os=-sysv
-               ;;
-       -nova*)
-               os=-rtmk-nova
-               ;;
-       -ns2 )
-               os=-nextstep2
-               ;;
-       -nsk*)
-               os=-nsk
-               ;;
-       # Preserve the version number of sinix5.
-       -sinix5.*)
-               os=`echo $os | sed -e 's|sinix|sysv|'`
-               ;;
-       -sinix*)
-               os=-sysv4
-               ;;
-        -tpf*)
-               os=-tpf
-               ;;
-       -triton*)
-               os=-sysv3
-               ;;
-       -oss*)
-               os=-sysv3
-               ;;
-       -svr4)
-               os=-sysv4
-               ;;
-       -svr3)
-               os=-sysv3
-               ;;
-       -sysvr4)
-               os=-sysv4
-               ;;
-       # This must come after -sysvr4.
-       -sysv*)
-               ;;
-       -ose*)
-               os=-ose
-               ;;
-       -es1800*)
-               os=-ose
-               ;;
-       -xenix)
-               os=-xenix
-               ;;
-       -*mint | -mint[0-9]* | -*MiNT | -MiNT[0-9]*)
-               os=-mint
-               ;;
-       -aros*)
-               os=-aros
-               ;;
-       -kaos*)
-               os=-kaos
-               ;;
-       -zvmoe)
-               os=-zvmoe
-               ;;
-       -none)
-               ;;
-       *)
-               # Get rid of the `-' at the beginning of $os.
-               os=`echo $os | sed 's/[^-]*-//'`
-               echo Invalid configuration \`$1\': system \`$os\' not recognized 1>&2
-               exit 1
-               ;;
-esac
-else
-
-# Here we handle the default operating systems that come with various machines.
-# The value should be what the vendor currently ships out the door with their
-# machine or put another way, the most popular os provided with the machine.
-
-# Note that if you're going to try to match "-MANUFACTURER" here (say,
-# "-sun"), then you have to tell the case statement up towards the top
-# that MANUFACTURER isn't an operating system.  Otherwise, code above
-# will signal an error saying that MANUFACTURER isn't an operating
-# system, and we'll never get to this point.
-
-case $basic_machine in
-        score-*)
-               os=-elf
-               ;;
-        spu-*)
-               os=-elf
-               ;;
-       *-acorn)
-               os=-riscix1.2
-               ;;
-       arm*-rebel)
-               os=-linux
-               ;;
-       arm*-semi)
-               os=-aout
-               ;;
-        c4x-* | tic4x-*)
-               os=-coff
-               ;;
-       # This must come before the *-dec entry.
-       pdp10-*)
-               os=-tops20
-               ;;
-       pdp11-*)
-               os=-none
-               ;;
-       *-dec | vax-*)
-               os=-ultrix4.2
-               ;;
-       m68*-apollo)
-               os=-domain
-               ;;
-       i386-sun)
-               os=-sunos4.0.2
-               ;;
-       m68000-sun)
-               os=-sunos3
-               # This also exists in the configure program, but was not the
-               # default.
-               # os=-sunos4
-               ;;
-       m68*-cisco)
-               os=-aout
-               ;;
-       mips*-cisco)
-               os=-elf
-               ;;
-       mips*-*)
-               os=-elf
-               ;;
-       or32-*)
-               os=-coff
-               ;;
-       *-tti)  # must be before sparc entry or we get the wrong os.
-               os=-sysv3
-               ;;
-       sparc-* | *-sun)
-               os=-sunos4.1.1
-               ;;
-       *-be)
-               os=-beos
-               ;;
-       *-haiku)
-               os=-haiku
-               ;;
-       *-ibm)
-               os=-aix
-               ;;
-       *-knuth)
-               os=-mmixware
-               ;;
-       *-wec)
-               os=-proelf
-               ;;
-       *-winbond)
-               os=-proelf
-               ;;
-       *-oki)
-               os=-proelf
-               ;;
-       *-hp)
-               os=-hpux
-               ;;
-       *-hitachi)
-               os=-hiux
-               ;;
-       i860-* | *-att | *-ncr | *-altos | *-motorola | *-convergent)
-               os=-sysv
-               ;;
-       *-cbm)
-               os=-amigaos
-               ;;
-       *-dg)
-               os=-dgux
-               ;;
-       *-dolphin)
-               os=-sysv3
-               ;;
-       m68k-ccur)
-               os=-rtu
-               ;;
-       m88k-omron*)
-               os=-luna
-               ;;
-       *-next )
-               os=-nextstep
-               ;;
-       *-sequent)
-               os=-ptx
-               ;;
-       *-crds)
-               os=-unos
-               ;;
-       *-ns)
-               os=-genix
-               ;;
-       i370-*)
-               os=-mvs
-               ;;
-       *-next)
-               os=-nextstep3
-               ;;
-       *-gould)
-               os=-sysv
-               ;;
-       *-highlevel)
-               os=-bsd
-               ;;
-       *-encore)
-               os=-bsd
-               ;;
-       *-sgi)
-               os=-irix
-               ;;
-       *-siemens)
-               os=-sysv4
-               ;;
-       *-masscomp)
-               os=-rtu
-               ;;
-       f30[01]-fujitsu | f700-fujitsu)
-               os=-uxpv
-               ;;
-       *-rom68k)
-               os=-coff
-               ;;
-       *-*bug)
-               os=-coff
-               ;;
-       *-apple)
-               os=-macos
-               ;;
-       *-atari*)
-               os=-mint
-               ;;
-       *)
-               os=-none
-               ;;
-esac
-fi
-
-# Here we handle the case where we know the os, and the CPU type, but not the
-# manufacturer.  We pick the logical manufacturer.
-vendor=unknown
-case $basic_machine in
-       *-unknown)
-               case $os in
-                       -riscix*)
-                               vendor=acorn
-                               ;;
-                       -sunos*)
-                               vendor=sun
-                               ;;
-                       -aix*)
-                               vendor=ibm
-                               ;;
-                       -beos*)
-                               vendor=be
-                               ;;
-                       -hpux*)
-                               vendor=hp
-                               ;;
-                       -mpeix*)
-                               vendor=hp
-                               ;;
-                       -hiux*)
-                               vendor=hitachi
-                               ;;
-                       -unos*)
-                               vendor=crds
-                               ;;
-                       -dgux*)
-                               vendor=dg
-                               ;;
-                       -luna*)
-                               vendor=omron
-                               ;;
-                       -genix*)
-                               vendor=ns
-                               ;;
-                       -mvs* | -opened*)
-                               vendor=ibm
-                               ;;
-                       -os400*)
-                               vendor=ibm
-                               ;;
-                       -ptx*)
-                               vendor=sequent
-                               ;;
-                       -tpf*)
-                               vendor=ibm
-                               ;;
-                       -vxsim* | -vxworks* | -windiss*)
-                               vendor=wrs
-                               ;;
-                       -aux*)
-                               vendor=apple
-                               ;;
-                       -hms*)
-                               vendor=hitachi
-                               ;;
-                       -mpw* | -macos*)
-                               vendor=apple
-                               ;;
-                       -*mint | -mint[0-9]* | -*MiNT | -MiNT[0-9]*)
-                               vendor=atari
-                               ;;
-                       -vos*)
-                               vendor=stratus
-                               ;;
-               esac
-               basic_machine=`echo $basic_machine | sed "s/unknown/$vendor/"`
-               ;;
-esac
-
-echo $basic_machine$os
-exit
-
-# Local variables:
-# eval: (add-hook 'write-file-hooks 'time-stamp)
-# time-stamp-start: "timestamp='"
-# time-stamp-format: "%:y-%02m-%02d"
-# time-stamp-end: "'"
-# End:
diff --git a/textclient/configure b/textclient/configure
new file mode 100755 (executable)
index 0000000..46ee9f4
--- /dev/null
@@ -0,0 +1,57 @@
+#!/bin/sh
+
+echo 
+echo 
+echo Running the configure script to create config.mk
+echo
+
+# Parse the command line arguments
+for x in $*
+do
+       a=$1
+       k=`echo $a | awk -F= ' { print $1 } '`
+       v=`echo $a | awk -F= ' { print $2 } '`
+
+       case $k in
+               --prefix)
+                       PREFIX=$v
+               ;;
+               --bindir)
+                       BINDIR=$v
+               ;;
+               --ctdldir)
+                       CTDLDIR=$v
+               ;;
+               *)
+                       echo $0 : unknown option $k
+                       echo
+                       echo Valid options are:
+                       echo '  --prefix=PREFIX         Install files in PREFIX [/usr/local]'
+                       echo '  --bindir=DIR            Install executables in DIR [PREFIX/bin]'
+                       echo '  --ctdldir=DIR           Look for Citadel server in DIR [/usr/local/citadel]'
+                       exit 1
+               ;;
+       esac
+       shift
+done
+
+# Set any missing values
+
+[ "$PREFIX" = "" ]     && PREFIX=/usr/local
+[ "$BINDIR" = "" ]     && BINDIR=${PREFIX}/bin
+[ "$CTDLDIR" = "" ]    && CTDLDIR=/usr/local/citadel
+[ "$CFLAGS" = "" ]     && CFLAGS='-ggdb'
+[ "$LDFLAGS" = "" ]    && LDFLAGS=''
+
+# Output the config.mk
+
+(
+       echo "CFLAGS := ${CFLAGS}"
+       echo "LDFLAGS := ${LDFLAGS}"
+       echo "PREFIX := ${PREFIX}"
+       echo "BINDIR := ${BINDIR}"
+       echo "CTDLDIR := ${CTDLDIR}"
+) >config.mk
+
+cat config.mk
+echo
diff --git a/textclient/configure.ac b/textclient/configure.ac
deleted file mode 100644 (file)
index 18b7d94..0000000
+++ /dev/null
@@ -1,862 +0,0 @@
-dnl Process this file with autoconf to produce a configure script.
-AC_PREREQ(2.52)
-AC_INIT([CitadelTextClient],m4_esyscmd_s([grep CLIENT_VERSION src/include/citadel_ipc.h | sed 's/[^0-9]*//g']),[http://uncensored.citadel.org])
-AC_REVISION([$Revision: 5108 $])
-
-AC_CONFIG_HEADER(sysdep.h)
-AC_CONFIG_MACRO_DIR([m4])
-AC_PREFIX_DEFAULT(/usr/local/citadel)
-if test "$prefix" = NONE; then
-       AC_DEFINE_UNQUOTED(CTDLDIR, "$ac_default_prefix", [define this to the Citadel home directory])
-       ssl_dir="$ac_default_prefix/keys"
-       localedir=$ac_default_prefix
-else
-       AC_DEFINE_UNQUOTED(CTDLDIR, "$prefix", [define this to the Citadel home directory])
-       ssl_dir="$prefix/keys"
-       localedir=$prefix
-fi
-
-dnl Make sure we see all GNU and Solaris extensions.
-AC_GNU_SOURCE
-
-
-dnl Checks for the Datadir
-AC_ARG_WITH(datadir, 
-                   [  --with-datadir          directory to store the databases under],
-                       [ if test "x$withval" != "xno" ; then
-                                             AC_DEFINE(HAVE_DATA_DIR,[],[use alternate database location?])
-                                             AC_DEFINE_UNQUOTED(DATA_DIR, "$withval",[define, if the user suplied a data-directory to use.])
-                                             MAKE_DATA_DIR=$withval
-                                             AC_SUBST(MAKE_DATA_DIR)
-                         fi
-                       ]
-)
-
-
-dnl Checks for the helpDatadir
-AC_ARG_WITH(helpdir, 
-                   [  --with-helpdir          directory to store the helpfiles under],
-                       [ if test "x$withval" != "xno" ; then
-                                             AC_DEFINE(HAVE_HELP_DIR,[],[use alternate database location?])
-                                             AC_DEFINE_UNQUOTED(HELP_DIR, "$withval",[define, if the user suplied a helpfile-directory to use.])
-                                             MAKE_HELP_DIR=$withval
-                                             AC_SUBST(MAKE_HELP_DIR)
-                         fi
-                       ]
-)
-
-dnl Checks for the Static Datadir
-AC_ARG_WITH(staticdatadir, 
-                   [  --with-staticdatadir    directory to store citadels system messages under],
-                       [ if test "x$withval" != "xno" ; then
-                                                 AC_DEFINE(HAVE_STATICDATA_DIR, [], [should we activate an alternate static text location?])
-                                                 AC_DEFINE_UNQUOTED(STATICDATA_DIR, "$withval", [where do we put our static text data?])
-                                                 MAKE_STATICDATA_DIR=$withval
-                                                 AC_SUBST(MAKE_STATICDATA_DIR)
-                         fi
-                       ]
-)
-
-
-dnl Checks for the SSLdir
-dnl this is a bit different than the rest, 
-dnl because of the citadel used to have a keys/ subdir.
-AC_ARG_WITH(ssldir, 
-                   [  --with-ssldir           directory to store the ssl certificates under],
-                       [ if test "x$withval" != "xno" ; then
-                                             
-                                                 ssl_dir="$withval"
-                         fi
-                       AC_SUBST(MAKE_SSL_DIR)
-                       ]
-)
-AC_DEFINE_UNQUOTED(SSL_DIR, "$ssl_dir", [were should we put our keys?])
-
-
-dnl Checks for the spooldir
-AC_ARG_WITH(spooldir, 
-                       [  --with-spooldir         directory to keep queues under],
-                       [ if test "x$withval" != "xno" ; then
-                                               AC_DEFINE(HAVE_SPOOL_DIR, [], [enable alternate spool dir?])
-                                               AC_DEFINE_UNQUOTED(SPOOL_DIR,"$withval", [where do we place our spool dirs?])
-                                               MAKE_SPOOL_DIR=$withval
-                                               AC_SUBST(MAKE_SPOOL_DIR)
-                          fi
-                       ]
-)
-
-
-dnl Checks for the Configdir
-AC_ARG_WITH(sysconfdir, 
-                       [  --with-sysconfdir       directory to store the configs under],
-                       [ if test "x$withval" != "xno" ; then
-                                           AC_DEFINE(HAVE_ETC_DIR, [], [should we search our system config in an alternate place?])
-                                               AC_DEFINE_UNQUOTED(ETC_DIR, "$withval", [where to search our config files])
-                                               MAKE_ETC_DIR=$withval
-                                               AC_SUBST(MAKE_ETC_DIR)
-                         fi
-                       ]
-)
-
-dnl Checks for the Configdir
-AC_ARG_WITH(autosysconfdir, 
-                       [  --with-autosysconfdir         directory to store the automaticaly maintained configs under],
-                       [ if test "x$withval" != "xno" ; then
-                                           AC_DEFINE(HAVE_AUTO_ETC_DIR, [], [should we search our automatic config in an alternate place?])
-                                               AC_DEFINE_UNQUOTED(AUTO_ETC_DIR, "$withval", [where to search our automatic config files])
-                                               MAKE_AUTO_ETC_DIR=$withval
-                                               AC_SUBST(MAKE_AUTO_ETC_DIR)
-                         fi
-                       ]
-)
-
-dnl Checks for where to put our utilities
-AC_ARG_WITH(utility-bindir, 
-                       [  --with-utility-bindir   directory where to find helper binaries],
-                       [ if test "x$withval" != "xno" ; then
-                                           AC_DEFINE(HAVE_UTILBIN_DIR,[],[should we put our helper binaries to another location?])
-                                               AC_DEFINE_UNQUOTED(UTILBIN_DIR, "$withval", [were to put our helper programs])
-                                               MAKE_UTILBIN_DIR=$withval
-                                               AC_SUBST(MAKE_UTILBIN_DIR)
-                         fi
-                       ]
-)
-
-
-dnl Checks for the run-dir for the sockets
-AC_ARG_WITH(rundir, 
-                       [  --with-rundir           directory to place runtime files (UDS) to?],
-                       [ if test "x$withval" != "xno" ; then
-                                           AC_DEFINE(HAVE_RUN_DIR, [], [should we put our non volatile files elsewhere?])
-                                               AC_DEFINE_UNQUOTED(RUN_DIR, "$withval", [define, where the config should go in unix style])
-                                               MAKE_RUN_DIR=$withval
-                                               AC_SUBST(MAKE_RUN_DIR)
-                         fi
-                       ]
-)
-
-
-dnl Checks for the Pseudo Random Generator sockets TODO: this keeps being default.
-AC_DEFINE_UNQUOTED(EGD_POOL, "/var/run/egd-pool", [place to keep our pseudo random generator file])
-AC_ARG_WITH(egdpool, 
-                       [  --with-egdpool          the socket from Pseudo Random Generator, defaults to /var/run/egd-pool],
-                       [ if test "x$withval" != "xno" ; then
-                                               AC_DEFINE_UNQUOTED(EGD_POOL, "$withval", [the socket from Pseudo Random Generator])
-                         fi
-                       ]
-)
-
-
-AC_ARG_WITH(docdir,
-                       [  --with-docdir           where to install the documentation. default: /usr/local/citadel/],
-                       [ if test "x$withval" != "xno" ; then
-                                       MAKE_DOC_DIR=$withval
-                                       AC_SUBST(MAKE_DOC_DIR)
-                         fi
-                       ]
-)
-
-dnl where to put the locale files
-AC_ARG_WITH(localedir, 
-                   [  --with-localedir          directory to put the locale files to],
-                       [ if test "x$withval" != "xno" ; then
-                           localedir=$withval
-                         fi
-                       ]
-)
-AC_DEFINE_UNQUOTED(LOCALEDIR, "$localedir",[where to find our pot files])
-LOCALEDIR=$localedir
-AC_SUBST(LOCALEDIR)
-
-
-
-dnl Checks for the zlib compression library.
-saved_CFLAGS="$CFLAGS"
-CFLAGS="$CFLAGS"
-AC_CHECK_HEADER(zlib.h,
-       [AC_CHECK_LIB(z, zlibVersion,
-               [
-                       LIBS="-lz $LIBS"
-               ],
-               [
-                       AC_MSG_ERROR(zlib was not found or is not usable.  Please install zlib.)
-               ]
-       ,
-       )],
-       [
-               AC_MSG_ERROR(zlib.h was not found or is not usable.  Please install zlib.)
-       ]
-)
-CFLAGS="$saved_CFLAGS"
-
-dnl Here is the check for a libc integrated iconv
-AC_ARG_ENABLE(iconv,
-       [  --disable-iconv         do not use iconv charset conversion],
-       ok_iconv=no, ok_iconv=yes)
-
-AC_MSG_CHECKING(Checking to see if your system supports iconv)
-AC_TRY_RUN([
-       #include <iconv.h>
-       main() {
-               iconv_t ic = (iconv_t)(-1) ;
-               ic = iconv_open("UTF-8", "us-ascii");
-               iconv_close(ic);
-               exit(0);
-       }
- ],
-               [
-                 ok_iconv=yes
-                 AC_MSG_RESULT([yes])
-               ],
-               [ 
-                 ok_iconv=no
-                 AC_MSG_RESULT([no])
-               ]
-)
-
-dnl Check for iconv in external libiconv
-if test "$ok_iconv" = no; then
-       AC_MSG_CHECKING(Checking for an external libiconv)
-       OLD_LDFLAGS="$LDFLAGS"
-       LDFLAGS="$LDFLAGS -liconv"
-       AC_TRY_RUN([
-                       #include <iconv.h>
-                       main() {
-                               iconv_t ic = (iconv_t)(-1) ;
-                               ic = iconv_open("UTF-8", "us-ascii");
-                               iconv_close(ic);
-                       }
-               ],
-                       [
-                         ok_iconv=yes
-                         AC_MSG_RESULT([yes])
-                       ],
-                       [ 
-                         ok_iconv=no
-                         LDFLAGS="$OLD_LDFLAGS"
-                         AC_MSG_RESULT([no])
-                       ]
-               )
-fi     
-if test "$ok_iconv" != "no"; then
-       AC_MSG_RESULT(Citadel will be built with character set conversion.)
-       AC_DEFINE(HAVE_ICONV,[],[whether we have iconv for charset conversion])
-else
-       AC_MSG_RESULT(Citadel will be built without character set conversion.)
-fi
-
-AC_CHECK_LIB(intl, libintl_bindtextdomain, [LDFLAGS="$LDFLAGS -lintl"])
-
-
-
-AC_ARG_ENABLE(threaded-client, [  --disable-threaded-client
-                         disable multithreaded client])
-
-AC_ARG_ENABLE(pie, [  --enable-pie            build position-independent executables])
-
-AC_ARG_WITH(pam, [  --with-pam              use PAM if present (see PAM.txt before you try this)])
-AC_ARG_WITH(kthread, [  --with-kthread          use kernel threads (on FreeBSD) (not recommended yet)])
-
-AC_ARG_WITH(ssl,
-       [  --with-ssl=PATH         Specify path to OpenSSL installation ],
-       [
-               if test "x$withval" != "xno" ; then
-                       tryssldir=$withval
-               fi
-       ]
-)
-
-
-dnl By default, we only build the client (citadel and whobbs) unless we can
-dnl figure out how to build with POSIX threads.
-TARGETS=client
-
-AC_CANONICAL_HOST
-PTHREAD_DEFS=-D_REENTRANT
-AC_MSG_CHECKING([how to compile with POSIX threads])
-case "$host" in
-       dnl BSDI 3.0 wants relocatable object modules instead of shared libs
-       dnl for dlopen(), and has a wrapper script to link with shared libs.
-       dnl Also has stupid non-reentrant gethostbyaddr() and friends.
-       *-*-bsdi[123]*)
-               test -z "$CC" -a -x /usr/bin/shlicc2 && CC=shlicc2
-               AC_DEFINE(HAVE_NONREENTRANT_NETDB,[], [define this if the OS has broken non-reentrant gethostby{name,addr}() ])
-               AC_MSG_RESULT([Old BSDI])
-       ;;
-       *-*-bsdi*)
-               AC_DEFINE(HAVE_NONREENTRANT_NETDB, [], [define this if the OS has broken non-reentrant gethostby{name,addr}() ])
-               AC_MSG_RESULT([BSD/OS])
-       ;;
-       dnl Curses support on Mac OS X is kind of screwed at the moment.
-       dnl TCP buffering isn't ideal under OS X. This define should also be
-       dnl checked in other cases of OS X-Linux differences.
-       *-*-darwin*)
-               AC_DEFINE(HAVE_DARWIN, [], [define if using OS X/Darwin])
-               AC_MSG_RESULT([Mac OS X])
-       ;;
-       dnl Digital Unix has an odd way to build for pthreads, and we can't
-       dnl build pthreads programs with gcc due to header problems.
-       alpha*-dec-osf*)
-               test -z "$CC" && CC=cc
-               LIBS="-lpthread -lexc $LIBS"
-               check_pthread=no
-               AC_MSG_RESULT([Tru64 or Digital UNIX])
-       ;;
-       dnl FreeBSD is similar to Digital UNIX with DEC C, which has a -pthread flag:
-       *-*-freebsd*)
-               if test "$with_kthread" = yes; then
-                       LIBS="-kthread $LIBS"
-               else
-                       LIBS="-pthread $LIBS"
-               fi
-               check_pthread=no
-               PTHREAD_DEFS=-D_THREAD_SAFE
-               AC_MSG_RESULT([FreeBSD])
-       ;;
-       *-*-openbsd*)
-               LIBS="-pthread $LIBS"
-               check_pthread=no
-               PTHREAD_DEFS=-pthread
-               AC_MSG_RESULT([OpenBSD])
-       ;;
-       *-*-linux*)
-               PTHREAD_DEFS="-D_REENTRANT -pthread"
-               AC_MSG_RESULT([Linux])
-       ;;
-       *-*-solaris*)
-               PTHREAD_DEFS="-D_REENTRANT -D_PTHREADS"
-               AC_MSG_RESULT([Solaris])
-       ;;
-       *-*-cygwin*)
-               SERVER_LDFLAGS="-Wl,-subsystem,windows"
-               AC_MSG_RESULT([Cygwin])
-       ;;
-       *)
-               AC_MSG_RESULT([default])
-       ;;
-esac
-dnl DEFS="$DEFS $PTHREAD_DEFS"
-
-
-dnl Checks for programs.
-AC_PROG_CC
-
-
-
-dnl Set up system-dependent compiler flags.
-if test "$GCC" = yes; then
-       if test "$CC" = icc; then
-               CFLAGS="$CFLAGS -w1"
-       else
-               case "$host" in
-                       *-*-solaris*|alpha*-dec-osf*)
-                               CFLAGS="$CFLAGS -Wall -Wcast-qual -Wcast-align -Wno-char-subscripts $PTHREAD_DEFS"
-                       ;;
-                       *)
-                       CFLAGS="$CFLAGS -Wall -Wcast-qual -Wcast-align -Wstrict-prototypes -Wno-strict-aliasing $PTHREAD_DEFS"
-                       ;;
-               esac
-       fi
-fi
-
-if test "x$enable_pie" = xyes; then
-       save_CFLAGS="$CFLAGS"
-       save_LDFLAGS="$LDFLAGS"
-       CFLAGS="$CFLAGS -fpie"
-       LDFLAGS="$LDFLAGS -pie -fpie"
-       AC_CACHE_CHECK([whether compiler accepts -pie -fpie], ac_cv_pie_fpie,
-       [AC_TRY_LINK([], [],
-       ac_cv_pie_fpie=yes, ac_cv_pie_fpie=no)])
-       if test $ac_cv_pie_fpie = no; then
-               CFLAGS="$save_CFLAGS"
-               LDFLAGS="$save_LDFLAGS"
-       fi
-fi
-
-AC_MSG_CHECKING([how to create dependancy checks])
-                if test -n "`$CC -V 2>&1 |grep Sun`"; then 
-                       DEPEND_FLAG=-xM;
-                else 
-                       DEPEND_FLAG=-M
-                fi
-AC_SUBST(DEPEND_FLAG)
-
-
-AC_PROG_INSTALL
-AC_PROG_YACC
-AC_PATH_PROG(DIFF,diff)
-AC_PATH_PROG(PATCH,patch)
-missing_dir=`cd $ac_aux_dir && pwd`
-AM_MISSING_PROG(AUTOCONF, autoconf, $missing_dir)
-AM_MISSING_PROG(ACLOCAL, aclocal, $missing_dir)
-
-dnl Checks for system services.
-
-dnl Check the size of various builtin types; see typesize.h (error)
-AC_CHECK_SIZEOF(char, 0)
-AC_CHECK_SIZEOF(short, 0)
-AC_CHECK_SIZEOF(int, 0)
-AC_CHECK_SIZEOF(long, 0)
-AC_CHECK_SIZEOF(size_t, 0)
-dnl AC_CHECK_SIZEOF(long long, 0)
-
-dnl Checks for libraries.
-
-dnl We want to test for the following in libc before checking for their
-dnl respective libraries, because some systems (like Irix) have both, and the
-dnl non-libc versions may be broken.
-AC_CHECK_FUNCS(crypt gethostbyname connect flock getpwnam_r getpwuid_r getloadavg)
-
-AC_CHECK_FUNCS(strftime_l uselocale gettext)
-
-if test "$ok_nls" != "no"; then
-       AC_CHECK_PROG(ok_xgettext, xgettext, yes, no)
-       ok_nls=$ok_xgettext
-fi
-
-if test "$ok_nls" != "no"; then
-       AC_CHECK_PROG(ok_msgmerge, msgmerge, yes, no)
-       ok_nls=$ok_msgmerge
-fi
-
-if test "$ok_nls" != "no"; then
-       AC_CHECK_PROG(ok_msgfmt, msgfmt, yes, no)
-       ok_nls=$ok_msgfmt
-fi
-
-if test "$ok_nls" != "no"; then
-       AC_MSG_RESULT(citadel will be built with national language support.)
-       AC_DEFINE(ENABLE_NLS, [], [whether we have NLS support])
-       PROG_SUBDIRS="$PROG_SUBDIRS po/citadel-setup"
-else
-       AC_MSG_RESULT(citadel will be built without national language support.)
-fi
-
-dnl disable backtrace if we don't want it.
-AC_ARG_WITH(backtrace, 
-                   [  --with-backtrace        enable backtrace dumps in the syslog],
-                       [ if test "x$withval" != "xno" ; then
-                            CFLAGS="$CFLAGS  -rdynamic "
-                            LDFLAGS="$LDFLAGS  -rdynamic "
-                             AC_CHECK_FUNCS(backtrace)
-                         fi
-                       ]
-)
-
-dnl disable backtrace if we don't want it.
-AC_ARG_WITH(gprof, 
-                   [  --with-gprof            enable profiling],
-                       [ if test "x$withval" != "xno" ; then
-                            CFLAGS="$CFLAGS  -pg "
-                            LDFLAGS="$LDFLAGS  -pg "
-                         fi
-                       ]
-)
-
-if test "$ac_cv_func_gethostbyname" = no; then
-       AC_CHECK_LIB(nsl, gethostbyname)
-fi
-
-if test "$ac_cv_func_connect" = no; then
-        AC_CHECK_LIB(socket, connect)
-fi
-
-dnl Check for Solaris realtime support
-AC_CHECK_LIB(rt, sched_yield)
-
-AC_CHECK_FUNCS(vw_printw wcolor_set resizeterm wresize)
-
-dnl Check for libpthread(s) if we're not using Digital UNIX or FreeBSD. (On
-dnl which the -pthread flag takes care of this.)
-if test "$check_pthread" != no; then
-       AC_CHECK_LIB(pthread, pthread_create)
-       AC_CHECK_LIB(pthreads, pthread_create)
-fi
-
-test -d /usr/kerberos/include && CPPFLAGS="$CPPFLAGS -I/usr/kerberos/include"
-
-
-saved_CFLAGS="$CFLAGS"
-CFLAGS="$CFLAGS"
-dnl Check for libcitadel
-AC_CHECK_HEADER(libcitadel.h,
-       [AC_CHECK_LIB(citadel, libcitadel_version_string,
-               [
-                       LIBS="-lcitadel $LIBS"
-               ],
-               [
-                       AC_MSG_ERROR(libcitadel was not found or is not usable.  Please install libcitadel.)
-               ]
-       ,
-       )],
-       [
-               AC_MSG_ERROR(libcitadel.h was not found or is not usable.  Please install libcitadel.)
-       ]
-)
-
-CFLAGS="$saved_CFLAGS"
-
-saved_CFLAGS="$CFLAGS"
-CFLAGS="$CFLAGS "
-dnl Check for libcitadelclient
-dnl AC_CHECK_HEADER(citadel_ipc.h,
-dnl    [AC_CHECK_LIB(citadelclient, libcitadelclient_version_string,
-dnl            [
-dnl                    LIBS="-lcitadelclient $LIBS "
-dnl            ],
-dnl            [
-dnl                    AC_MSG_ERROR(libcitadelclient was not found or is not usable.  Please install libcitadelclient.)
-dnl            ]
-dnl    ,
-dnl    )],
-dnl    [
-dnl            AC_MSG_ERROR(citadel_ipc.h was not found or is not usable.  Please install libcitadelclient.)
-dnl    ]
-dnl )
-dnl 
-dnl CFLAGS="$saved_CFLAGS"
-
-
-# The big search for OpenSSL
-if test "$with_ssl" != "no"; then
-       saved_LIBS="$LIBS"
-       saved_LDFLAGS="$LDFLAGS"
-       saved_CFLAGS="$CFLAGS"
-       if test "x$prefix" != "xNONE"; then
-               tryssldir="$tryssldir $prefix"
-       fi
-       AC_CACHE_CHECK([for OpenSSL], ac_cv_openssldir, [
-               for ssldir in $tryssldir "" /usr /usr/local/openssl /usr/lib/openssl /usr/local/ssl /usr/lib/ssl /usr/local /usr/pkg /opt /opt/openssl ; do
-                       CFLAGS="$saved_CFLAGS"
-                       LDFLAGS="$saved_LDFLAGS"
-                       LIBS="$saved_LIBS -lssl -lcrypto"
-       
-                       # Skip directories if they don't exist
-                       if test ! -z "$ssldir" -a ! -d "$ssldir" ; then
-                               continue;
-                       fi
-                       if test ! -z "$ssldir" -a "x$ssldir" != "x/usr"; then
-                               # Try to use $ssldir/lib if it exists, otherwise
-                               # $ssldir
-                               if test -d "$ssldir/lib" ; then
-                                       LDFLAGS="-L$ssldir/lib $saved_LDFLAGS"
-                                       if test ! -z "$need_dash_r" ; then
-                                               LDFLAGS="-R$ssldir/lib $LDFLAGS"
-                                       fi
-                               else
-                                       LDFLAGS="-L$ssldir $saved_LDFLAGS"
-                                       if test ! -z "$need_dash_r" ; then
-                                               LDFLAGS="-R$ssldir $LDFLAGS"
-                                       fi
-                               fi
-                               # Try to use $ssldir/include if it exists, otherwise
-                               # $ssldir
-                               if test -d "$ssldir/include" ; then
-                                       CFLAGS="-I$ssldir/include $saved_CFLAGS"
-                               else
-                                       CFLAGS="-I$ssldir $saved_CFLAGS"
-                               fi
-                       fi
-       
-                       # Basic test to check for compatible version and correct linking
-                       # *does not* test for RSA - that comes later.
-                       AC_TRY_RUN(
-                               [
-       #include <string.h>
-       #include <openssl/rand.h>
-       int main(void)
-       {
-               char a[2048];
-               memset(a, 0, sizeof(a));
-               RAND_add(a, sizeof(a), sizeof(a));
-               return(RAND_status() <= 0);
-       }
-                               ],
-                               [
-                                       found_crypto=1
-                                       break;
-                               ], []
-                       )
-       
-                       if test ! -z "$found_crypto" ; then
-                               break;
-                       fi
-               done
-       
-               if test -z "$ssldir" ; then
-                       ssldir="(system)"
-               fi
-       
-               if test ! -z "$found_crypto" ; then
-                       ac_cv_openssldir=$ssldir
-               else
-                       ac_cv_openssldir="no"
-               fi
-       ])
-       LIBS="$saved_LIBS"
-       LDFLAGS="$saved_LDFLAGS"
-       CFLAGS="$saved_CFLAGS"
-       
-       if test "x$ac_cv_openssldir" != "xno" ; then
-               AC_DEFINE(HAVE_OPENSSL, [], [Define if you have OpenSSL.])
-               LIBS="-lssl -lcrypto $LIBS"
-               dnl Need to recover ssldir - test above runs in subshell
-               ssldir=$ac_cv_openssldir
-               if test ! -z "$ssldir" -a "x$ssldir" != "x/usr" -a "x$ssldir" != "x(system)"; then
-                       # Try to use $ssldir/lib if it exists, otherwise
-                       # $ssldir
-                       if test -d "$ssldir/lib" ; then
-                               LDFLAGS="-L$ssldir/lib $saved_LDFLAGS"
-                               if test ! -z "$need_dash_r" ; then
-                                       LDFLAGS="-R$ssldir/lib $LDFLAGS"
-                               fi
-                       else
-                               LDFLAGS="-L$ssldir $saved_LDFLAGS"
-                               if test ! -z "$need_dash_r" ; then
-                                       LDFLAGS="-R$ssldir $LDFLAGS"
-                               fi
-                       fi
-                       # Try to use $ssldir/include if it exists, otherwise
-                       # $ssldir
-                       if test -d "$ssldir/include" ; then
-                               CFLAGS="-I$ssldir/include $saved_CFLAGS"
-                       else
-                               CFLAGS="-I$ssldir $saved_CFLAGS"
-                       fi
-               fi
-       fi
-fi
-
-
-
-
-
-
-
-dnl Checks for header files.
-AC_HEADER_DIRENT
-AC_HEADER_STDC
-AC_HEADER_SYS_WAIT
-
-dnl
-dnl TODO: for the DB header checks, we should check whether the headers
-dnl define db_env_create, somehow
-dnl
-AC_CHECK_HEADERS(dl.h fcntl.h limits.h malloc.h termios.h sys/ioctl.h sys/select.h sys/stat.h sys/time.h sys/prctl.h syslog.h unistd.h paths.h db.h db4/db.h pthread.h netinet/in.h arpa/nameser.h arpa/nameser_compat.h syscall.h sys/syscall.h)
-
-AC_CHECK_HEADER(resolv.h,AC_DEFINE(HAVE_RESOLV_H, [], [define this if you have the resolv.h header file.]),,
-[#ifdef HAVE_SYS_TYPES_H
-#include <sys/types.h>
-#endif
-#ifdef HAVE_NETINET_IN_H
-#include <netinet/in.h>
-#endif
-#ifdef HAVE_ARPA_NAMESER_H
-#include <arpa/nameser.h>
-#endif])
-
-
-dnl Checks for typedefs, structures, and compiler characteristics.
-AC_C_CONST
-AC_TYPE_PID_T
-AC_TYPE_SIZE_T
-AC_HEADER_TIME
-
-dnl defined in acinclude.m4:
-CIT_STRUCT_TM
-
-AC_CACHE_CHECK(
-       [for call semantics from getpwuid_r], 
-       ac_cv_call_getpwuid_r,
-       [AC_TRY_COMPILE([#include <sys/types.h>
-#include <pwd.h>], 
-                         [
-                               struct passwd pw, *pwp;
-                               char pwbuf[64];
-                               uid_t uid;
-
-                               getpwuid_r(uid, &pw, pwbuf, sizeof(pwbuf), &pwp);
-                       ],
-                       ac_cv_call_getpwuid_r=yes, 
-                       ac_cv_call_getpwuid_r=no)
-       ])
-
-if test $ac_cv_call_getpwuid_r = no; then
-       AC_DEFINE(SOLARIS_GETPWUID,[],[do we need to use solaris call syntax?])
-       AC_DEFINE(F_UID_T, "%ld", [whats the matching format string for uid_t?])
-       AC_DEFINE(F_PID_T, "%ld",  [whats the matching format string for pid_t?])
-       AC_DEFINE(F_XPID_T, "%lx",  [whats the matching format string for xpid_t?])
-else
-       AC_DEFINE(F_UID_T, "%d",  [whats the matching format string for uid_t?])
-       AC_DEFINE(F_PID_T, "%d",  [whats the matching format string for pid_t?])
-       AC_DEFINE(F_XPID_T, "%x",  [whats the matching format string for xpid_t?])
-fi
-
-
-dnl Our own happy little check for the resolver library.
-
-case "`uname -a`" in 
- OpenBSD*)
-       echo "we don't need to check for resolv on openbsd"  
-       ;;
- FreeBSD*)
-       echo "we don't need to check for resolv on freeBSD"  
-       ;;
- *)
-       test -f /usr/local/lib/libresolv.a && LDFLAGS="$LDFLAGS -L/usr/local/lib"
-       AC_CHECK_LIB(resolv, res_query,
-               RESOLV="$RESOLV -lresolv",
-               [dnl Have to include resolv.h as res_query is sometimes defined as a macro
-                       AC_MSG_CHECKING([for res_query in -lresolv (with resolv.h if present)])
-                       saved_libs="$LIBS"
-                       LIBS="-lresolv $LIBS"
-                       AC_TRY_LINK([
-                               #ifdef HAVE_RESOLV_H
-                               #include <resolv.h>
-                               #endif],
-                               [res_query(0,0,0,0,0)],
-                               [AC_MSG_RESULT(yes)
-                                               have_res_query=yes],
-                               [AC_MSG_RESULT(no)
-                                       AC_MSG_ERROR(libresolv was not found.  Citadel requires the resolver library.)
-                               ])
-         ]
-       )
-       ;;
-esac
-
-
-
-dnl Checks for library functions.
-AC_FUNC_GETPGRP
-AC_PROG_GCC_TRADITIONAL
-AC_TYPE_SIGNAL
-AC_FUNC_VPRINTF
-AC_CHECK_FUNCS(getspnam mkdir mkfifo mktime rmdir select socket strerror strcasecmp strncasecmp)
-
-dnl Now check for pthreads
-
-dnl On some platforms, AC_CHECK_FUNC[S] doesn't work for pthreads programs;
-dnl we need to include pthread.h
-
-AC_CACHE_CHECK([for pthread_cancel], ac_cv_func_pthread_cancel,
-[AC_TRY_LINK([#include <pthread.h>],
-[      pthread_t thread;
-
-       /* The GNU C library defines this for functions which it implements
-          to always fail with ENOSYS.  Some functions are actually named
-          something starting with __ and the normal name is an alias.  */
-#if defined (__stub_pthread_cancel) || defined (__stub___pthread_cancel)
-       choke me
-#else
-       pthread_cancel(thread);
-#endif],
-ac_cv_func_pthread_cancel=yes, ac_cv_func_pthread_cancel=no)])
-if test "$ac_cv_func_pthread_cancel" = yes; then
-       AC_DEFINE(HAVE_PTHREAD_CANCEL, [], [define this if you have the pthread_cancel() function])
-fi
-
-AC_CACHE_CHECK([for pthread_create], ac_cv_func_pthread_create,
-[AC_TRY_LINK([#include <pthread.h>],
-[      /* The GNU C library defines this for functions which it implements
-          to always fail with ENOSYS.  Some functions are actually named
-          something starting with __ and the normal name is an alias.  */
-#if defined (__stub_pthread_cancel) || defined (__stub___pthread_cancel)
-       choke me
-#else
-       pthread_create(NULL, NULL, NULL, NULL);
-#endif],
-ac_cv_func_pthread_create=yes, ac_cv_func_pthread_create=no)])
-if test "$ac_cv_func_pthread_create" = yes; then
-       test "$DATABASE" && TARGETS="client server utils"
-       if test "x$enable_threaded_client" != xno; then
-               AC_DEFINE(THREADED_CLIENT, [], [define this if you want to enable the multithreaded client])
-       fi
-fi
-
-AC_REPLACE_FUNCS(snprintf getutline)
-
-AC_CACHE_CHECK([the weather], ac_cv_weather, [
-       sleep 1
-       echo $ECHO_N "opening your window... $ECHO_C" >&6
-       sleep 2
-       month=`date | cut -f 2 -d ' '`
-       case $month in
-       Dec | Jan | Feb)
-               ac_cv_weather="it's cold!"
-               ;;
-       Mar | Apr)
-               ac_cv_weather="it's wet!"
-               ;;
-       Jul | Aug)
-               ac_cv_weather="it's hot!"
-               ;;
-       Oct | Nov)
-               ac_cv_weather="it's cool"
-               ;;
-       May | Jun | Sep | *)
-               ac_cv_weather="it's fine"
-               ;;
-       esac
-       ])
-
-
-
-
-AC_CACHE_CHECK([under the bed], ac_cv_under_the_bed, [
-       number=`date | cut -c 19`
-       case $number in
-       7)
-               ac_cv_under_the_bed="lunatics and monsters found"
-               ;;
-       *)
-               ac_cv_under_the_bed="dust bunnies found"
-               ;;
-       esac
-       ])
-
-
-dnl Done! Now write the Makefile and sysdep.h
-AC_SUBST(RESOLV)
-AC_SUBST(TARGETS)
-AC_SUBST(DIFF)
-AC_SUBST(PATCH)
-AC_CONFIG_FILES([Makefile])
-AC_OUTPUT()
-
-
-abs_srcdir="`cd $srcdir && pwd`"
-abs_builddir="`pwd`"
-if test "$abs_srcdir" != "$abs_builddir"; then
-   ln -sf $abs_srcdir/include $abs_builddir
-   ln -sf $abs_srcdir/Make_sources $abs_builddir
-   ln -sf $abs_srcdir/Make_modules $abs_builddir
-   for i in $abs_srcdir/*.h ; do
-      if test "$abs_srcdir/sysdep.h" != "$i"; then
-         ln -sf $i $abs_builddir
-      fi
-   done
-   for d in `/bin/ls $abs_srcdir/modules/`; do 
-       (mkdir -p $abs_builddir/modules/$d) 
-   done
-   if test -d "$abs_srcdir/user_modules/"; then
-      for d in `/bin/ls $abs_srcdir/user_modules/`; do 
-       (mkdir -p $abs_builddir/user_modules/$d) 
-       done
-   fi
-   mkdir -p $abs_builddir/utils
-   mkdir -p $abs_builddir/utillib
-   mkdir -p $abs_builddir/textclient
-fi
-
-if test -n "$srcdir"; then 
-   export srcdir=.
-fi
-
-
-echo ------------------------------------------------------------------------
-echo 'Character set conversion support:' $ok_iconv
-echo 
-echo 'Note: if you are not using Linux, make sure you are using GNU make'
-echo '(gmake) to compile Citadel.'
-echo
diff --git a/textclient/install-sh b/textclient/install-sh
deleted file mode 100755 (executable)
index ebc6691..0000000
+++ /dev/null
@@ -1,250 +0,0 @@
-#! /bin/sh
-#
-# install - install a program, script, or datafile
-# This comes from X11R5 (mit/util/scripts/install.sh).
-#
-# Copyright 1991 by the Massachusetts Institute of Technology
-#
-# Permission to use, copy, modify, distribute, and sell this software and its
-# documentation for any purpose is hereby granted without fee, provided that
-# the above copyright notice appear in all copies and that both that
-# copyright notice and this permission notice appear in supporting
-# documentation, and that the name of M.I.T. not be used in advertising or
-# publicity pertaining to distribution of the software without specific,
-# written prior permission.  M.I.T. makes no representations about the
-# suitability of this software for any purpose.  It is provided "as is"
-# without express or implied warranty.
-#
-# Calling this script install-sh is preferred over install.sh, to prevent
-# `make' implicit rules from creating a file called install from it
-# when there is no Makefile.
-#
-# This script is compatible with the BSD install script, but was written
-# from scratch.  It can only install one file at a time, a restriction
-# shared with many OS's install programs.
-
-
-# set DOITPROG to echo to test this script
-
-# Don't use :- since 4.3BSD and earlier shells don't like it.
-doit="${DOITPROG-}"
-
-
-# put in absolute paths if you don't have them in your path; or use env. vars.
-
-mvprog="${MVPROG-mv}"
-cpprog="${CPPROG-cp}"
-chmodprog="${CHMODPROG-chmod}"
-chownprog="${CHOWNPROG-chown}"
-chgrpprog="${CHGRPPROG-chgrp}"
-stripprog="${STRIPPROG-strip}"
-rmprog="${RMPROG-rm}"
-mkdirprog="${MKDIRPROG-mkdir}"
-
-transformbasename=""
-transform_arg=""
-instcmd="$mvprog"
-chmodcmd="$chmodprog 0755"
-chowncmd=""
-chgrpcmd=""
-stripcmd=""
-rmcmd="$rmprog -f"
-mvcmd="$mvprog"
-src=""
-dst=""
-dir_arg=""
-
-while [ x"$1" != x ]; do
-    case $1 in
-       -c) instcmd="$cpprog"
-           shift
-           continue;;
-
-       -d) dir_arg=true
-           shift
-           continue;;
-
-       -m) chmodcmd="$chmodprog $2"
-           shift
-           shift
-           continue;;
-
-       -o) chowncmd="$chownprog $2"
-           shift
-           shift
-           continue;;
-
-       -g) chgrpcmd="$chgrpprog $2"
-           shift
-           shift
-           continue;;
-
-       -s) stripcmd="$stripprog"
-           shift
-           continue;;
-
-       -t=*) transformarg=`echo $1 | sed 's/-t=//'`
-           shift
-           continue;;
-
-       -b=*) transformbasename=`echo $1 | sed 's/-b=//'`
-           shift
-           continue;;
-
-       *)  if [ x"$src" = x ]
-           then
-               src=$1
-           else
-               # this colon is to work around a 386BSD /bin/sh bug
-               :
-               dst=$1
-           fi
-           shift
-           continue;;
-    esac
-done
-
-if [ x"$src" = x ]
-then
-       echo "install:  no input file specified"
-       exit 1
-else
-       true
-fi
-
-if [ x"$dir_arg" != x ]; then
-       dst=$src
-       src=""
-       
-       if [ -d $dst ]; then
-               instcmd=:
-       else
-               instcmd=mkdir
-       fi
-else
-
-# Waiting for this to be detected by the "$instcmd $src $dsttmp" command
-# might cause directories to be created, which would be especially bad 
-# if $src (and thus $dsttmp) contains '*'.
-
-       if [ -f $src -o -d $src ]
-       then
-               true
-       else
-               echo "install:  $src does not exist"
-               exit 1
-       fi
-       
-       if [ x"$dst" = x ]
-       then
-               echo "install:  no destination specified"
-               exit 1
-       else
-               true
-       fi
-
-# If destination is a directory, append the input filename; if your system
-# does not like double slashes in filenames, you may need to add some logic
-
-       if [ -d $dst ]
-       then
-               dst="$dst"/`basename $src`
-       else
-               true
-       fi
-fi
-
-## this sed command emulates the dirname command
-dstdir=`echo $dst | sed -e 's,[^/]*$,,;s,/$,,;s,^$,.,'`
-
-# Make sure that the destination directory exists.
-#  this part is taken from Noah Friedman's mkinstalldirs script
-
-# Skip lots of stat calls in the usual case.
-if [ ! -d "$dstdir" ]; then
-defaultIFS='   
-'
-IFS="${IFS-${defaultIFS}}"
-
-oIFS="${IFS}"
-# Some sh's can't handle IFS=/ for some reason.
-IFS='%'
-set - `echo ${dstdir} | sed -e 's@/@%@g' -e 's@^%@/@'`
-IFS="${oIFS}"
-
-pathcomp=''
-
-while [ $# -ne 0 ] ; do
-       pathcomp="${pathcomp}${1}"
-       shift
-
-       if [ ! -d "${pathcomp}" ] ;
-        then
-               $mkdirprog "${pathcomp}"
-       else
-               true
-       fi
-
-       pathcomp="${pathcomp}/"
-done
-fi
-
-if [ x"$dir_arg" != x ]
-then
-       $doit $instcmd $dst &&
-
-       if [ x"$chowncmd" != x ]; then $doit $chowncmd $dst; else true ; fi &&
-       if [ x"$chgrpcmd" != x ]; then $doit $chgrpcmd $dst; else true ; fi &&
-       if [ x"$stripcmd" != x ]; then $doit $stripcmd $dst; else true ; fi &&
-       if [ x"$chmodcmd" != x ]; then $doit $chmodcmd $dst; else true ; fi
-else
-
-# If we're going to rename the final executable, determine the name now.
-
-       if [ x"$transformarg" = x ] 
-       then
-               dstfile=`basename $dst`
-       else
-               dstfile=`basename $dst $transformbasename | 
-                       sed $transformarg`$transformbasename
-       fi
-
-# don't allow the sed command to completely eliminate the filename
-
-       if [ x"$dstfile" = x ] 
-       then
-               dstfile=`basename $dst`
-       else
-               true
-       fi
-
-# Make a temp file name in the proper directory.
-
-       dsttmp=$dstdir/#inst.$$#
-
-# Move or copy the file name to the temp name
-
-       $doit $instcmd $src $dsttmp &&
-
-       trap "rm -f ${dsttmp}" 0 &&
-
-# and set any options; do chmod last to preserve setuid bits
-
-# If any of these fail, we abort the whole thing.  If we want to
-# ignore errors from any of these, just make sure not to ignore
-# errors from the above "$doit $instcmd $src $dsttmp" command.
-
-       if [ x"$chowncmd" != x ]; then $doit $chowncmd $dsttmp; else true;fi &&
-       if [ x"$chgrpcmd" != x ]; then $doit $chgrpcmd $dsttmp; else true;fi &&
-       if [ x"$stripcmd" != x ]; then $doit $stripcmd $dsttmp; else true;fi &&
-       if [ x"$chmodcmd" != x ]; then $doit $chmodcmd $dsttmp; else true;fi &&
-
-# Now rename the file to the real destination.
-
-       $doit $rmcmd -f $dstdir/$dstfile &&
-       $doit $mvcmd $dsttmp $dstdir/$dstfile 
-
-fi &&
-
-
-exit 0
diff --git a/textclient/ipc_c_tcp.c b/textclient/ipc_c_tcp.c
new file mode 100644 (file)
index 0000000..93edebc
--- /dev/null
@@ -0,0 +1,56 @@
+/*
+ * Client-side IPC functions
+ *
+ * Copyright (c) 1987-2018 by the citadel.org team
+ *
+ * This program is open source software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 3.
+ *
+ * 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.
+ */
+
+
+#include "textclient.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/textclient/ltmain.sh b/textclient/ltmain.sh
deleted file mode 120000 (symlink)
index 4159958..0000000
+++ /dev/null
@@ -1 +0,0 @@
-/usr/share/libtool/config/ltmain.sh
\ No newline at end of file
diff --git a/textclient/m4/libtool.m4 b/textclient/m4/libtool.m4
deleted file mode 120000 (symlink)
index f3504e9..0000000
+++ /dev/null
@@ -1 +0,0 @@
-/usr/share/aclocal/libtool.m4
\ No newline at end of file
diff --git a/textclient/m4/ltoptions.m4 b/textclient/m4/ltoptions.m4
deleted file mode 120000 (symlink)
index b81279e..0000000
+++ /dev/null
@@ -1 +0,0 @@
-/usr/share/aclocal/ltoptions.m4
\ No newline at end of file
diff --git a/textclient/m4/ltsugar.m4 b/textclient/m4/ltsugar.m4
deleted file mode 120000 (symlink)
index 4d76cc7..0000000
+++ /dev/null
@@ -1 +0,0 @@
-/usr/share/aclocal/ltsugar.m4
\ No newline at end of file
diff --git a/textclient/m4/ltversion.m4 b/textclient/m4/ltversion.m4
deleted file mode 120000 (symlink)
index 5eb474f..0000000
+++ /dev/null
@@ -1 +0,0 @@
-/usr/share/aclocal/ltversion.m4
\ No newline at end of file
diff --git a/textclient/m4/lt~obsolete.m4 b/textclient/m4/lt~obsolete.m4
deleted file mode 120000 (symlink)
index de0b9f9..0000000
+++ /dev/null
@@ -1 +0,0 @@
-/usr/share/aclocal/lt~obsolete.m4
\ No newline at end of file
diff --git a/textclient/md5.c b/textclient/md5.c
new file mode 100644 (file)
index 0000000..2f33199
--- /dev/null
@@ -0,0 +1,355 @@
+/*
+ * This code implements the MD5 message-digest algorithm.
+ * The algorithm is due to Ron Rivest.  This code was
+ * written by Colin Plumb in 1993, no copyright is claimed.
+ * This code is in the public domain; do with it what you wish.
+ *
+ * Equivalent code is available from RSA Data Security, Inc.
+ * This code has been tested against that, and is equivalent,
+ * except that you don't need to include two pages of legalese
+ * with every copy.
+ *
+ * To compute the message digest of a chunk of bytes, declare an
+ * MD5Context structure, pass it to MD5Init, call MD5Update as
+ * needed on buffers full of bytes, and then call MD5Final, which
+ * will fill a supplied 16-byte array with the digest.
+ */
+
+#include "textclient.h"
+
+#ifndef HIGHFIRST
+#define byteReverse(buf, len)  /* Nothing */
+#else
+void byteReverse(unsigned char *buf, unsigned longs);
+
+#ifndef ASM_MD5
+/*
+ * Note: this code is harmless on little-endian machines.
+ */
+void byteReverse(unsigned char *buf, unsigned longs)
+{
+    uint32_t t;
+    do {
+       t = (uint32_t) ((unsigned) buf[3] << 8 | buf[2]) << 16 |
+           ((unsigned) buf[1] << 8 | buf[0]);
+       *(uint32_t *) buf = t;
+       buf += 4;
+    } while (--longs);
+}
+#endif
+#endif
+
+
+
+/*
+ * Start MD5 accumulation.  Set bit count to 0 and buffer to mysterious
+ * initialization constants.
+ */
+void MD5Init(struct MD5Context *ctx)
+{
+    ctx->buf[0] = 0x67452301;
+    ctx->buf[1] = 0xefcdab89;
+    ctx->buf[2] = 0x98badcfe;
+    ctx->buf[3] = 0x10325476;
+
+    ctx->bits[0] = 0;
+    ctx->bits[1] = 0;
+}
+
+/*
+ * Update context to reflect the concatenation of another buffer full
+ * of bytes.
+ */
+void MD5Update(struct MD5Context *ctx, unsigned char const *buf, unsigned len)
+{
+    uint32_t t;
+
+    /* Update bitcount */
+
+    t = ctx->bits[0];
+    if ((ctx->bits[0] = t + ((uint32_t) len << 3)) < t)
+       ctx->bits[1]++;         /* Carry from low to high */
+    ctx->bits[1] += len >> 29;
+
+    t = (t >> 3) & 0x3f;       /* Bytes already in shsInfo->data */
+
+    /* Handle any leading odd-sized chunks */
+
+    if (t) {
+       unsigned char *p = (unsigned char *) ctx->in + t;
+
+       t = 64 - t;
+       if (len < t) {
+           memcpy(p, buf, len);
+           return;
+       }
+       memcpy(p, buf, t);
+       byteReverse(ctx->in, 16);
+       MD5Transform(ctx->buf, ctx->in);
+       buf += t;
+       len -= t;
+    }
+    /* Process data in 64-byte chunks */
+
+    while (len >= 64) {
+       memcpy(ctx->in, buf, 64);
+       byteReverse(ctx->in, 16);
+       MD5Transform(ctx->buf, ctx->in);
+       buf += 64;
+       len -= 64;
+    }
+
+    /* Handle any remaining bytes of data. */
+
+    memcpy(ctx->in, buf, len);
+}
+
+/*
+ * Final wrapup - pad to 64-byte boundary with the bit pattern 
+ * 1 0* (64-bit count of bits processed, MSB-first)
+ */
+void MD5Final(unsigned char digest[16], struct MD5Context *ctx)
+{
+    unsigned count;
+    unsigned char *p;
+
+    /* Compute number of bytes mod 64 */
+    count = (ctx->bits[0] >> 3) & 0x3F;
+
+    /* Set the first char of padding to 0x80.  This is safe since there is
+       always at least one byte free */
+    p = ((unsigned char*)ctx->in) + count;
+    *p++ = 0x80;
+
+    /* Bytes of padding needed to make 64 bytes */
+    count = 64 - 1 - count;
+
+    /* Pad out to 56 mod 64 */
+    if (count < 8) {
+       /* Two lots of padding:  Pad the first block to 64 bytes */
+       memset(p, 0, count);
+       byteReverse(ctx->in, 16);
+       MD5Transform(ctx->buf, ctx->in);
+
+       /* Now fill the next block with 56 bytes */
+       memset(ctx->in, 0, 56);
+    } else {
+       /* Pad block to 56 bytes */
+       memset(p, 0, count - 8);
+    }
+    byteReverse(ctx->in, 14);
+
+    /* Append length in bits and transform */
+    ((uint32_t *) ctx->in)[14] = ctx->bits[0];
+    ((uint32_t *) ctx->in)[15] = ctx->bits[1];
+
+    MD5Transform(ctx->buf, (uint32_t *) ctx->in);
+    byteReverse((unsigned char *) ctx->buf, 4);
+    memcpy(digest, ctx->buf, 16);
+    memset(ctx, 0, sizeof(ctx));       /* In case it's sensitive */
+}
+
+#ifndef ASM_MD5
+
+/* The four core functions - F1 is optimized somewhat */
+
+/* #define F1(x, y, z) (x & y | ~x & z) */
+#define F1(x, y, z) (z ^ (x & (y ^ z)))
+#define F2(x, y, z) F1(z, x, y)
+#define F3(x, y, z) (x ^ y ^ z)
+#define F4(x, y, z) (y ^ (x | ~z))
+
+/* This is the central step in the MD5 algorithm. */
+#ifdef __PUREC__
+#define MD5STEP(f, w, x, y, z, data, s) \
+       ( w += f /*(x, y, z)*/ + data,  w = w<<s | w>>(32-s),  w += x )
+#else
+#define MD5STEP(f, w, x, y, z, data, s) \
+       ( w += f(x, y, z) + data,  w = w<<s | w>>(32-s),  w += x )
+#endif
+
+/*
+ * The core of the MD5 algorithm, this alters an existing MD5 hash to
+ * reflect the addition of 16 longwords of new data.  MD5Update blocks
+ * the data and converts bytes into longwords for this routine.
+ */
+void MD5Transform(uint32_t buf[4], uint32_t const in[16])
+{
+    register uint32_t a, b, c, d;
+
+    a = buf[0];
+    b = buf[1];
+    c = buf[2];
+    d = buf[3];
+
+#ifdef __PUREC__       /* PureC Weirdness... (GG) */
+    MD5STEP(F1(b,c,d), a, b, c, d, in[0] + 0xd76aa478L, 7);
+    MD5STEP(F1(a,b,c), d, a, b, c, in[1] + 0xe8c7b756L, 12);
+    MD5STEP(F1(d,a,b), c, d, a, b, in[2] + 0x242070dbL, 17);
+    MD5STEP(F1(c,d,a), b, c, d, a, in[3] + 0xc1bdceeeL, 22);
+    MD5STEP(F1(b,c,d), a, b, c, d, in[4] + 0xf57c0fafL, 7);
+    MD5STEP(F1(a,b,c), d, a, b, c, in[5] + 0x4787c62aL, 12);
+    MD5STEP(F1(d,a,b), c, d, a, b, in[6] + 0xa8304613L, 17);
+    MD5STEP(F1(c,d,a), b, c, d, a, in[7] + 0xfd469501L, 22);
+    MD5STEP(F1(b,c,d), a, b, c, d, in[8] + 0x698098d8L, 7);
+    MD5STEP(F1(a,b,c), d, a, b, c, in[9] + 0x8b44f7afL, 12);
+    MD5STEP(F1(d,a,b), c, d, a, b, in[10] + 0xffff5bb1L, 17);
+    MD5STEP(F1(c,d,a), b, c, d, a, in[11] + 0x895cd7beL, 22);
+    MD5STEP(F1(b,c,d), a, b, c, d, in[12] + 0x6b901122L, 7);
+    MD5STEP(F1(a,b,c), d, a, b, c, in[13] + 0xfd987193L, 12);
+    MD5STEP(F1(d,a,b), c, d, a, b, in[14] + 0xa679438eL, 17);
+    MD5STEP(F1(c,d,a), b, c, d, a, in[15] + 0x49b40821L, 22);
+
+    MD5STEP(F2(b,c,d), a, b, c, d, in[1] + 0xf61e2562L, 5);
+    MD5STEP(F2(a,b,c), d, a, b, c, in[6] + 0xc040b340L, 9);
+    MD5STEP(F2(d,a,b), c, d, a, b, in[11] + 0x265e5a51L, 14);
+    MD5STEP(F2(c,d,a), b, c, d, a, in[0] + 0xe9b6c7aaL, 20);
+    MD5STEP(F2(b,c,d), a, b, c, d, in[5] + 0xd62f105dL, 5);
+    MD5STEP(F2(a,b,c), d, a, b, c, in[10] + 0x02441453L, 9);
+    MD5STEP(F2(d,a,b), c, d, a, b, in[15] + 0xd8a1e681L, 14);
+    MD5STEP(F2(c,d,a), b, c, d, a, in[4] + 0xe7d3fbc8L, 20);
+    MD5STEP(F2(b,c,d), a, b, c, d, in[9] + 0x21e1cde6L, 5);
+    MD5STEP(F2(a,b,c), d, a, b, c, in[14] + 0xc33707d6L, 9);
+    MD5STEP(F2(d,a,b), c, d, a, b, in[3] + 0xf4d50d87L, 14);
+    MD5STEP(F2(c,d,a), b, c, d, a, in[8] + 0x455a14edL, 20);
+    MD5STEP(F2(b,c,d), a, b, c, d, in[13] + 0xa9e3e905L, 5);
+    MD5STEP(F2(a,b,c), d, a, b, c, in[2] + 0xfcefa3f8L, 9);
+    MD5STEP(F2(d,a,b), c, d, a, b, in[7] + 0x676f02d9L, 14);
+    MD5STEP(F2(c,d,a), b, c, d, a, in[12] + 0x8d2a4c8aL, 20);
+
+    MD5STEP(F3(b,c,d), a, b, c, d, in[5] + 0xfffa3942L, 4);
+    MD5STEP(F3(a,b,c), d, a, b, c, in[8] + 0x8771f681L, 11);
+    MD5STEP(F3(d,a,b), c, d, a, b, in[11] + 0x6d9d6122L, 16);
+    MD5STEP(F3(c,d,a), b, c, d, a, in[14] + 0xfde5380cL, 23);
+    MD5STEP(F3(b,c,d), a, b, c, d, in[1] + 0xa4beea44L, 4);
+    MD5STEP(F3(a,b,c), d, a, b, c, in[4] + 0x4bdecfa9L, 11);
+    MD5STEP(F3(d,a,b), c, d, a, b, in[7] + 0xf6bb4b60L, 16);
+    MD5STEP(F3(c,d,a), b, c, d, a, in[10] + 0xbebfbc70L, 23);
+    MD5STEP(F3(b,c,d), a, b, c, d, in[13] + 0x289b7ec6L, 4);
+    MD5STEP(F3(a,b,c), d, a, b, c, in[0] + 0xeaa127faL, 11);
+    MD5STEP(F3(d,a,b), c, d, a, b, in[3] + 0xd4ef3085L, 16);
+    MD5STEP(F3(c,d,a), b, c, d, a, in[6] + 0x04881d05L, 23);
+    MD5STEP(F3(b,c,d), a, b, c, d, in[9] + 0xd9d4d039L, 4);
+    MD5STEP(F3(a,b,c), d, a, b, c, in[12] + 0xe6db99e5L, 11);
+    MD5STEP(F3(d,a,b), c, d, a, b, in[15] + 0x1fa27cf8L, 16);
+    MD5STEP(F3(c,d,a), b, c, d, a, in[2] + 0xc4ac5665L, 23);
+
+    MD5STEP(F4(b,c,d), a, b, c, d, in[0] + 0xf4292244L, 6);
+    MD5STEP(F4(a,b,c), d, a, b, c, in[7] + 0x432aff97L, 10);
+    MD5STEP(F4(d,a,b), c, d, a, b, in[14] + 0xab9423a7L, 15);
+    MD5STEP(F4(c,d,a), b, c, d, a, in[5] + 0xfc93a039L, 21);
+    MD5STEP(F4(b,c,d), a, b, c, d, in[12] + 0x655b59c3L, 6);
+    MD5STEP(F4(a,b,c), d, a, b, c, in[3] + 0x8f0ccc92L, 10);
+    MD5STEP(F4(d,a,b), c, d, a, b, in[10] + 0xffeff47dL, 15);
+    MD5STEP(F4(c,d,a), b, c, d, a, in[1] + 0x85845dd1L, 21);
+    MD5STEP(F4(b,c,d), a, b, c, d, in[8] + 0x6fa87e4fL, 6);
+    MD5STEP(F4(a,b,c), d, a, b, c, in[15] + 0xfe2ce6e0L, 10);
+    MD5STEP(F4(d,a,b), c, d, a, b, in[6] + 0xa3014314L, 15);
+    MD5STEP(F4(c,d,a), b, c, d, a, in[13] + 0x4e0811a1L, 21);
+    MD5STEP(F4(b,c,d), a, b, c, d, in[4] + 0xf7537e82L, 6);
+    MD5STEP(F4(a,b,c), d, a, b, c, in[11] + 0xbd3af235L, 10);
+    MD5STEP(F4(d,a,b), c, d, a, b, in[2] + 0x2ad7d2bbL, 15);
+    MD5STEP(F4(c,d,a), b, c, d, a, in[9] + 0xeb86d391L, 21);
+#else
+    MD5STEP(F1, a, b, c, d, in[0] + 0xd76aa478, 7);
+    MD5STEP(F1, d, a, b, c, in[1] + 0xe8c7b756, 12);
+    MD5STEP(F1, c, d, a, b, in[2] + 0x242070db, 17);
+    MD5STEP(F1, b, c, d, a, in[3] + 0xc1bdceee, 22);
+    MD5STEP(F1, a, b, c, d, in[4] + 0xf57c0faf, 7);
+    MD5STEP(F1, d, a, b, c, in[5] + 0x4787c62a, 12);
+    MD5STEP(F1, c, d, a, b, in[6] + 0xa8304613, 17);
+    MD5STEP(F1, b, c, d, a, in[7] + 0xfd469501, 22);
+    MD5STEP(F1, a, b, c, d, in[8] + 0x698098d8, 7);
+    MD5STEP(F1, d, a, b, c, in[9] + 0x8b44f7af, 12);
+    MD5STEP(F1, c, d, a, b, in[10] + 0xffff5bb1, 17);
+    MD5STEP(F1, b, c, d, a, in[11] + 0x895cd7be, 22);
+    MD5STEP(F1, a, b, c, d, in[12] + 0x6b901122, 7);
+    MD5STEP(F1, d, a, b, c, in[13] + 0xfd987193, 12);
+    MD5STEP(F1, c, d, a, b, in[14] + 0xa679438e, 17);
+    MD5STEP(F1, b, c, d, a, in[15] + 0x49b40821, 22);
+
+    MD5STEP(F2, a, b, c, d, in[1] + 0xf61e2562, 5);
+    MD5STEP(F2, d, a, b, c, in[6] + 0xc040b340, 9);
+    MD5STEP(F2, c, d, a, b, in[11] + 0x265e5a51, 14);
+    MD5STEP(F2, b, c, d, a, in[0] + 0xe9b6c7aa, 20);
+    MD5STEP(F2, a, b, c, d, in[5] + 0xd62f105d, 5);
+    MD5STEP(F2, d, a, b, c, in[10] + 0x02441453, 9);
+    MD5STEP(F2, c, d, a, b, in[15] + 0xd8a1e681, 14);
+    MD5STEP(F2, b, c, d, a, in[4] + 0xe7d3fbc8, 20);
+    MD5STEP(F2, a, b, c, d, in[9] + 0x21e1cde6, 5);
+    MD5STEP(F2, d, a, b, c, in[14] + 0xc33707d6, 9);
+    MD5STEP(F2, c, d, a, b, in[3] + 0xf4d50d87, 14);
+    MD5STEP(F2, b, c, d, a, in[8] + 0x455a14ed, 20);
+    MD5STEP(F2, a, b, c, d, in[13] + 0xa9e3e905, 5);
+    MD5STEP(F2, d, a, b, c, in[2] + 0xfcefa3f8, 9);
+    MD5STEP(F2, c, d, a, b, in[7] + 0x676f02d9, 14);
+    MD5STEP(F2, b, c, d, a, in[12] + 0x8d2a4c8a, 20);
+
+    MD5STEP(F3, a, b, c, d, in[5] + 0xfffa3942, 4);
+    MD5STEP(F3, d, a, b, c, in[8] + 0x8771f681, 11);
+    MD5STEP(F3, c, d, a, b, in[11] + 0x6d9d6122, 16);
+    MD5STEP(F3, b, c, d, a, in[14] + 0xfde5380c, 23);
+    MD5STEP(F3, a, b, c, d, in[1] + 0xa4beea44, 4);
+    MD5STEP(F3, d, a, b, c, in[4] + 0x4bdecfa9, 11);
+    MD5STEP(F3, c, d, a, b, in[7] + 0xf6bb4b60, 16);
+    MD5STEP(F3, b, c, d, a, in[10] + 0xbebfbc70, 23);
+    MD5STEP(F3, a, b, c, d, in[13] + 0x289b7ec6, 4);
+    MD5STEP(F3, d, a, b, c, in[0] + 0xeaa127fa, 11);
+    MD5STEP(F3, c, d, a, b, in[3] + 0xd4ef3085, 16);
+    MD5STEP(F3, b, c, d, a, in[6] + 0x04881d05, 23);
+    MD5STEP(F3, a, b, c, d, in[9] + 0xd9d4d039, 4);
+    MD5STEP(F3, d, a, b, c, in[12] + 0xe6db99e5, 11);
+    MD5STEP(F3, c, d, a, b, in[15] + 0x1fa27cf8, 16);
+    MD5STEP(F3, b, c, d, a, in[2] + 0xc4ac5665, 23);
+
+    MD5STEP(F4, a, b, c, d, in[0] + 0xf4292244, 6);
+    MD5STEP(F4, d, a, b, c, in[7] + 0x432aff97, 10);
+    MD5STEP(F4, c, d, a, b, in[14] + 0xab9423a7, 15);
+    MD5STEP(F4, b, c, d, a, in[5] + 0xfc93a039, 21);
+    MD5STEP(F4, a, b, c, d, in[12] + 0x655b59c3, 6);
+    MD5STEP(F4, d, a, b, c, in[3] + 0x8f0ccc92, 10);
+    MD5STEP(F4, c, d, a, b, in[10] + 0xffeff47d, 15);
+    MD5STEP(F4, b, c, d, a, in[1] + 0x85845dd1, 21);
+    MD5STEP(F4, a, b, c, d, in[8] + 0x6fa87e4f, 6);
+    MD5STEP(F4, d, a, b, c, in[15] + 0xfe2ce6e0, 10);
+    MD5STEP(F4, c, d, a, b, in[6] + 0xa3014314, 15);
+    MD5STEP(F4, b, c, d, a, in[13] + 0x4e0811a1, 21);
+    MD5STEP(F4, a, b, c, d, in[4] + 0xf7537e82, 6);
+    MD5STEP(F4, d, a, b, c, in[11] + 0xbd3af235, 10);
+    MD5STEP(F4, c, d, a, b, in[2] + 0x2ad7d2bb, 15);
+    MD5STEP(F4, b, c, d, a, in[9] + 0xeb86d391, 21);
+#endif
+
+    buf[0] += a;
+    buf[1] += b;
+    buf[2] += c;
+    buf[3] += d;
+}
+
+/*
+ * This part was added by Brian Costello <btx@calyx.net>
+ * For citadel's APOP auth - makes a lower case (as per APOP RFC)
+ * md5 string
+ */
+
+char *make_apop_string(char *realpass, char *nonce, char *buffer, size_t n)
+{
+   struct MD5Context ctx;
+   u_char rawdigest[MD5_DIGEST_LEN];
+   int i;
+   
+   MD5Init(&ctx);
+   MD5Update(&ctx, (u_char*)nonce, strlen(nonce));
+   MD5Update(&ctx, (u_char*)realpass, strlen(realpass));
+   MD5Final(rawdigest, &ctx);
+   for (i=0; i<MD5_DIGEST_LEN; i++)
+   {
+      snprintf(&buffer[i*2], n - i*2, "%02X", (unsigned char) (rawdigest[i] & 0xff));
+      buffer[i*2] = tolower(buffer[i*2]);
+      buffer[(i*2)+1] = tolower(buffer[(i*2)+1]);
+   }
+   return buffer;
+}
+
+
+#endif
diff --git a/textclient/messages.c b/textclient/messages.c
new file mode 100644 (file)
index 0000000..fe975c5
--- /dev/null
@@ -0,0 +1,1928 @@
+/*
+ * Text client functions for reading and writing of messages
+ *
+ * Copyright (c) 1987-2018 by the citadel.org team
+ *
+ * This program is open source software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 3.
+ *
+ * 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.
+ */
+
+#include "textclient.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);
+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;
+extern char room_name[];
+extern char tempdir[];
+extern unsigned room_flags;
+extern unsigned room_flags2;
+extern int entmsg_ok;
+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(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, "");
+       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);
+
+       /* 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));
+       if (rv < 0)
+               scr_printf("failed to set message buffer: %s\n", strerror(errno));
+
+       
+       /* 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);
+       }
+}
+
+
+/*
+ * This is a mini RFC2047 decoder.
+ * It only handles strings encoded from UTF-8 as Quoted-printable.
+ */
+void mini_2047_decode(char *s) {
+       if (!s) return;
+
+       char *qstart = strstr(s, "=?UTF-8?Q?");
+       if (!qstart) return;
+
+       char *qend = strstr(s, "?=");
+       if (!qend) return;
+
+       if (qend <= qstart) return;
+
+       strcpy(qstart, &qstart[10]);
+       qend -= 10;
+
+       char *p = qstart;
+       while (p < qend) {
+
+               if (p[0] == '=') {
+
+                       char ch[3];
+                       ch[0] = p[1];
+                       ch[1] = p[2];
+                       ch[2] = p[3];
+                       int c;
+                       sscanf(ch, "%02x", &c);
+                       p[0] = c;
+                       strcpy(&p[1], &p[3]);
+                       qend -= 2;
+               }
+
+               if (p[0] == '_') {
+                       p[0] = ' ';
+               }
+               
+               ++p;
+       }
+
+       strcpy(qend, &qend[2]);
+}
+
+/*
+ * 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[256];
+       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) {
+               scr_printf("*** msg #%ld: %d %s\n", num, r, buf);
+               stty_ctdl(0);
+               free(message->text);
+               free_parts(message->attachments);
+               free(message);
+               return (0);
+       }
+
+       if (dest) {
+               fprintf(dest, "\n ");
+       } else {
+               scr_printf("\n");
+               if (pagin != 2)
+                       scr_printf(" ");
+       }
+       if (pagin == 1 && !dest) {
+               color(BRIGHT_CYAN);
+       }
+
+       /* View headers only */
+       if (pagin == 2) {
+               scr_printf("nhdr=%s\nfrom=%s\ntype=%d\nmsgn=%s\n",
+                               message->nhdr ? "yes" : "no",
+                               message->author, message->type,
+                               message->msgid);
+               if (!IsEmptyStr(message->subject)) {
+                       scr_printf("subj=%s\n", message->subject);
+               }
+               if (!IsEmptyStr(message->email)) {
+                       scr_printf("rfca=%s\n", message->email);
+               }
+               scr_printf("room=%s\ntime=%s",
+                       message->room,
+                       asctime(localtime(&message->time))
+               );
+               if (!IsEmptyStr(message->recipient)) {
+                       scr_printf("rcpt=%s\n", message->recipient);
+               }
+               if (message->attachments) {
+                       struct parts *ptr;
+
+                       for (ptr = message->attachments; ptr; ptr = ptr->next) {
+                               scr_printf("part=%s|%s|%s|%s|%s|%ld\n",
+                                       ptr->name, ptr->filename, ptr->number,
+                                       ptr->disposition, ptr->mimetype,
+                                       ptr->length
+                               );
+                       }
+               }
+               scr_printf("\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 {
+               struct tm thetime;
+               localtime_r(&message->time, &thetime);
+               strftime(now, sizeof now, "%F %R", &thetime);
+               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 (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)) {
+               safestrncpy(reply_to, message->author, sizeof(reply_to));
+       }
+
+       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);
+                               mini_2047_decode(message->subject);
+                               scr_printf("%s\n", message->subject);
+                       }
+               }
+       }
+
+       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;
+       }
+
+       /* Render text/x-markdown as plain text */
+       if (!strcasecmp(message->content_type, "text/x-markdown")) {
+               format_type = 1;
+       }
+
+       /* Extract URL's */
+       static char *urlprefixes[] = {
+               "http://",
+               "https://",
+               "ftp://"
+       };
+       int p = 0;
+       num_urls = 0;   /* Start with a clean slate */
+       for (p=0; p<(sizeof urlprefixes / sizeof(char *)); ++p) {
+               searchptr = message->text;
+               while ( (searchptr != NULL) && (num_urls < MAXURLS) ) {
+                       searchptr = strstr(searchptr, urlprefixes[p]);
+                       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, 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);
+                               }
+                       }
+                       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");
+                       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);
+                                       scr_printf("Part ");
+                                       color(BRIGHT_MAGENTA);
+                                       scr_printf("%s", ptr->number);
+                                       color(DIM_WHITE);
+                                       scr_printf(": ");
+                                       color(BRIGHT_CYAN);
+                                       scr_printf("%s", ptr->filename);
+                                       color(DIM_WHITE);
+                                       scr_printf(" (%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;
+
+       newprompt("Enter text to be replaced: ", srch_str, (sizeof(srch_str)-1) );
+       if (IsEmptyStr(srch_str)) {
+               return;
+       }
+
+       newprompt("Enter text to replace it with: ", 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);
+                       if (rv < 0) {
+                               scr_printf("failed to replace string: %s\n", strerror(errno));
+                               break; /*whoopsi! */
+                       }
+                       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[256];
+       char header[SIZ];
+       int cksum = 0;
+
+       if ( (mode == 2) && (IsEmptyStr(editor_path)) ) {
+               scr_printf("*** No editor available; using built-in editor.\n");
+               mode = 0;
+       }
+
+       struct tm thetime;
+       time_t now = time(NULL);
+       localtime_r(&now, &thetime);
+       strftime(datestr, sizeof datestr, "%F %R", &thetime);
+       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);
+       }
+
+       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);
+                       beg = ftell(fp);
+                       if (beg < 0)
+                               scr_printf("failed to get stream position %s\n", 
+                                          strerror(errno));
+                       fclose(fp);
+               } else {
+                       fp = fopen(filename, "w");
+                       if (fp == NULL) {
+                               scr_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) {
+                       scr_printf("*** Error opening temp file!\n    %s: %s\n",
+                               filename, strerror(errno)
+                       );
+                       return(1);
+               }
+               citedit(fp);
+               fclose(fp);
+               goto MECR;
+
+       case 1:
+               fp = fopen(filename, "a");
+               if (fp == NULL) {
+                       scr_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 */
+               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);
+               break;
+       }
+
+MECR:  if (mode >= 2) {
+               if (file_checksum(filename) == cksum) {
+                       scr_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, 0);
+                       beg = ftell(fp);
+                       if (beg < 0)
+                               scr_printf("failed to get stream position %s\n", 
+                                          strerror(errno));
+                       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 (!entmsg_ok) {
+               scr_printf("You may not enter messages in this type of room.\n");
+               return(1);
+       }
+
+       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);
+       }
+
+       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
+               );
+       }
+
+       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) {
+               scr_printf("Entering message.  Word wrap will give you soft linebreaks.  Pressing the\n");
+               scr_printf("'enter' key will give you a hard linebreak and an indent.  Press 'enter' twice\n");
+               scr_printf("when finished.\n");
+       }
+
+       /* Handle the selection of a recipient, if necessary. */
+       strcpy(buf, "");
+       if (need_recp == 1) {
+               if (axlevel >= AxProbU) {
+                       if (is_reply) {
+                               strcpy(buf, reply_to);
+                       } else {
+                               newprompt("Enter recipient: ", buf, SIZ-100);
+                               if (IsEmptyStr(buf)) {
+                                       return (1);
+                               }
+                       }
+               } else
+                       strcpy(buf, "sysop");
+       }
+       strcpy(message.recipient, buf);
+
+       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))) {
+               scr_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);
+       }
+
+       qstart = intprompt("Begin quoting at", 1, 1, line);
+       qend = intprompt("  End quoting at", line, qstart, line);
+
+       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);
+       if (rv != 0) 
+               scr_printf("failed to '%s' by %d\n", cmd, rv);
+       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, 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 */
+
+       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;
+       }
+
+       /* 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:
+               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 */
+                               }
+                               stty_ctdl(SB_RESTORE);
+                               ka_system(printcmd);
+                               stty_ctdl(SB_NO_INTR);
+                               unlink(prtfile);
+                               exit(0);
+                       }
+                       if (f > 0)
+                               do {
+                                       g = wait(NULL);
+                               } while ((g != f) && (g >= 0));
+                       scr_printf("Message printed.\n");
+               }
+               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 {
+                               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");
+               }
+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);
+                                       if (rv != 0)
+                                               scr_printf("failed to save %s Reason %d\n", cmd, rv);
+                               }
+                               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);
+}
+
+
+
+
+/*
+ * 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/textclient/missing b/textclient/missing
deleted file mode 100755 (executable)
index 0a7fb5a..0000000
+++ /dev/null
@@ -1,283 +0,0 @@
-#! /bin/sh
-# Common stub for a few missing GNU programs while installing.
-# Copyright 1996, 1997, 1999, 2000 Free Software Foundation, Inc.
-# Originally by Fran,cois Pinard <pinard@iro.umontreal.ca>, 1996.
-
-# This program is free software; you can redistribute it and/or modify
-# it under the terms of the GNU General Public License as published by
-# the Free Software Foundation; either version 2, 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.
-
-# As a special exception to the GNU General Public License, if you
-# distribute this file as part of a program that contains a
-# configuration script generated by Autoconf, you may include it under
-# the same distribution terms that you use for the rest of that program.
-
-if test $# -eq 0; then
-  echo 1>&2 "Try \`$0 --help' for more information"
-  exit 1
-fi
-
-run=:
-
-# In the cases where this matters, `missing' is being run in the
-# srcdir already.
-if test -f configure.ac; then
-  configure_ac=configure.ac
-else
-  configure_ac=configure.in
-fi
-
-case "$1" in
---run)
-  # Try to run requested program, and just exit if it succeeds.
-  run=
-  shift
-  "$@" && exit 0
-  ;;
-esac
-
-# If it does not exist, or fails to run (possibly an outdated version),
-# try to emulate it.
-case "$1" in
-
-  -h|--h|--he|--hel|--help)
-    echo "\
-$0 [OPTION]... PROGRAM [ARGUMENT]...
-
-Handle \`PROGRAM [ARGUMENT]...' for when PROGRAM is missing, or return an
-error status if there is no known handling for PROGRAM.
-
-Options:
-  -h, --help      display this help and exit
-  -v, --version   output version information and exit
-  --run           try to run the given command, and emulate it if it fails
-
-Supported PROGRAM values:
-  aclocal      touch file \`aclocal.m4'
-  autoconf     touch file \`configure'
-  autoheader   touch file \`config.h.in'
-  automake     touch all \`Makefile.in' files
-  bison        create \`y.tab.[ch]', if possible, from existing .[ch]
-  flex         create \`lex.yy.c', if possible, from existing .c
-  help2man     touch the output file
-  lex          create \`lex.yy.c', if possible, from existing .c
-  makeinfo     touch the output file
-  tar          try tar, gnutar, gtar, then tar without non-portable flags
-  yacc         create \`y.tab.[ch]', if possible, from existing .[ch]"
-    ;;
-
-  -v|--v|--ve|--ver|--vers|--versi|--versio|--version)
-    echo "missing 0.3 - GNU automake"
-    ;;
-
-  -*)
-    echo 1>&2 "$0: Unknown \`$1' option"
-    echo 1>&2 "Try \`$0 --help' for more information"
-    exit 1
-    ;;
-
-  aclocal)
-    echo 1>&2 "\
-WARNING: \`$1' is missing on your system.  You should only need it if
-         you modified \`acinclude.m4' or \`${configure_ac}'.  You might want
-         to install the \`Automake' and \`Perl' packages.  Grab them from
-         any GNU archive site."
-    touch aclocal.m4
-    ;;
-
-  autoconf)
-    echo 1>&2 "\
-WARNING: \`$1' is missing on your system.  You should only need it if
-         you modified \`${configure_ac}'.  You might want to install the
-         \`Autoconf' and \`GNU m4' packages.  Grab them from any GNU
-         archive site."
-    touch configure
-    ;;
-
-  autoheader)
-    echo 1>&2 "\
-WARNING: \`$1' is missing on your system.  You should only need it if
-         you modified \`acconfig.h' or \`${configure_ac}'.  You might want
-         to install the \`Autoconf' and \`GNU m4' packages.  Grab them
-         from any GNU archive site."
-    files=`sed -n 's/^[ ]*A[CM]_CONFIG_HEADER(\([^)]*\)).*/\1/p' ${configure_ac}`
-    test -z "$files" && files="config.h"
-    touch_files=
-    for f in $files; do
-      case "$f" in
-      *:*) touch_files="$touch_files "`echo "$f" |
-                                      sed -e 's/^[^:]*://' -e 's/:.*//'`;;
-      *) touch_files="$touch_files $f.in";;
-      esac
-    done
-    touch $touch_files
-    ;;
-
-  automake)
-    echo 1>&2 "\
-WARNING: \`$1' is missing on your system.  You should only need it if
-         you modified \`Makefile.am', \`acinclude.m4' or \`${configure_ac}'.
-         You might want to install the \`Automake' and \`Perl' packages.
-         Grab them from any GNU archive site."
-    find . -type f -name Makefile.am -print |
-          sed 's/\.am$/.in/' |
-          while read f; do touch "$f"; done
-    ;;
-
-  bison|yacc)
-    echo 1>&2 "\
-WARNING: \`$1' is missing on your system.  You should only need it if
-         you modified a \`.y' file.  You may need the \`Bison' package
-         in order for those modifications to take effect.  You can get
-         \`Bison' from any GNU archive site."
-    rm -f y.tab.c y.tab.h
-    if [ $# -ne 1 ]; then
-        eval LASTARG="\${$#}"
-       case "$LASTARG" in
-       *.y)
-           SRCFILE=`echo "$LASTARG" | sed 's/y$/c/'`
-           if [ -f "$SRCFILE" ]; then
-                cp "$SRCFILE" y.tab.c
-           fi
-           SRCFILE=`echo "$LASTARG" | sed 's/y$/h/'`
-           if [ -f "$SRCFILE" ]; then
-                cp "$SRCFILE" y.tab.h
-           fi
-         ;;
-       esac
-    fi
-    if [ ! -f y.tab.h ]; then
-       echo >y.tab.h
-    fi
-    if [ ! -f y.tab.c ]; then
-       echo 'main() { return 0; }' >y.tab.c
-    fi
-    ;;
-
-  lex|flex)
-    echo 1>&2 "\
-WARNING: \`$1' is missing on your system.  You should only need it if
-         you modified a \`.l' file.  You may need the \`Flex' package
-         in order for those modifications to take effect.  You can get
-         \`Flex' from any GNU archive site."
-    rm -f lex.yy.c
-    if [ $# -ne 1 ]; then
-        eval LASTARG="\${$#}"
-       case "$LASTARG" in
-       *.l)
-           SRCFILE=`echo "$LASTARG" | sed 's/l$/c/'`
-           if [ -f "$SRCFILE" ]; then
-                cp "$SRCFILE" lex.yy.c
-           fi
-         ;;
-       esac
-    fi
-    if [ ! -f lex.yy.c ]; then
-       echo 'main() { return 0; }' >lex.yy.c
-    fi
-    ;;
-
-  help2man)
-    echo 1>&2 "\
-WARNING: \`$1' is missing on your system.  You should only need it if
-        you modified a dependency of a manual page.  You may need the
-        \`Help2man' package in order for those modifications to take
-        effect.  You can get \`Help2man' from any GNU archive site."
-
-    file=`echo "$*" | sed -n 's/.*-o \([^ ]*\).*/\1/p'`
-    if test -z "$file"; then
-       file=`echo "$*" | sed -n 's/.*--output=\([^ ]*\).*/\1/p'`
-    fi
-    if [ -f "$file" ]; then
-       touch $file
-    else
-       test -z "$file" || exec >$file
-       echo ".ab help2man is required to generate this page"
-       exit 1
-    fi
-    ;;
-
-  makeinfo)
-    if test -z "$run" && (makeinfo --version) > /dev/null 2>&1; then
-       # We have makeinfo, but it failed.
-       exit 1
-    fi
-
-    echo 1>&2 "\
-WARNING: \`$1' is missing on your system.  You should only need it if
-         you modified a \`.texi' or \`.texinfo' file, or any other file
-         indirectly affecting the aspect of the manual.  The spurious
-         call might also be the consequence of using a buggy \`make' (AIX,
-         DU, IRIX).  You might want to install the \`Texinfo' package or
-         the \`GNU make' package.  Grab either from any GNU archive site."
-    file=`echo "$*" | sed -n 's/.*-o \([^ ]*\).*/\1/p'`
-    if test -z "$file"; then
-      file=`echo "$*" | sed 's/.* \([^ ]*\) *$/\1/'`
-      file=`sed -n '/^@setfilename/ { s/.* \([^ ]*\) *$/\1/; p; q; }' $file`
-    fi
-    touch $file
-    ;;
-
-  tar)
-    shift
-    if test -n "$run"; then
-      echo 1>&2 "ERROR: \`tar' requires --run"
-      exit 1
-    fi
-
-    # We have already tried tar in the generic part.
-    # Look for gnutar/gtar before invocation to avoid ugly error
-    # messages.
-    if (gnutar --version > /dev/null 2>&1); then
-       gnutar ${1+"$@"} && exit 0
-    fi
-    if (gtar --version > /dev/null 2>&1); then
-       gtar ${1+"$@"} && exit 0
-    fi
-    firstarg="$1"
-    if shift; then
-       case "$firstarg" in
-       *o*)
-           firstarg=`echo "$firstarg" | sed s/o//`
-           tar "$firstarg" ${1+"$@"} && exit 0
-           ;;
-       esac
-       case "$firstarg" in
-       *h*)
-           firstarg=`echo "$firstarg" | sed s/h//`
-           tar "$firstarg" ${1+"$@"} && exit 0
-           ;;
-       esac
-    fi
-
-    echo 1>&2 "\
-WARNING: I can't seem to be able to run \`tar' with the given arguments.
-         You may want to install GNU tar or Free paxutils, or check the
-         command line arguments."
-    exit 1
-    ;;
-
-  *)
-    echo 1>&2 "\
-WARNING: \`$1' is needed, and you do not seem to have it handy on your
-         system.  You might have modified some files without having the
-         proper tools for further handling them.  Check the \`README' file,
-         it often tells you about the needed prerequirements for installing
-         this package.  You may also peek at any GNU archive site, in case
-         some other package would contain this missing \`$1' program."
-    exit 1
-    ;;
-esac
-
-exit 0
diff --git a/textclient/mkinstalldirs b/textclient/mkinstalldirs
deleted file mode 100755 (executable)
index f945dbf..0000000
+++ /dev/null
@@ -1,38 +0,0 @@
-#! /bin/sh
-# mkinstalldirs --- make directory hierarchy
-# Author: Noah Friedman <friedman@prep.ai.mit.edu>
-# Created: 1993-05-16
-# Public domain
-
-errstatus=0
-
-for file
-do
-   set fnord `echo ":$file" | sed -ne 's/^:\//#/;s/^://;s/\// /g;s/^#/\//;p'`
-   shift
-
-   pathcomp=
-   for d
-   do
-     pathcomp="$pathcomp$d"
-     case "$pathcomp" in
-       -* ) pathcomp=./$pathcomp ;;
-     esac
-
-     if test ! -d "$pathcomp"; then
-        echo "mkdir $pathcomp" 1>&2
-
-        mkdir "$pathcomp" || lasterr=$?
-
-        if test ! -d "$pathcomp"; then
-         errstatus=$lasterr
-        fi
-     fi
-
-     pathcomp="$pathcomp/"
-   done
-done
-
-exit $errstatus
-
-# mkinstalldirs ends here
diff --git a/textclient/rooms.c b/textclient/rooms.c
new file mode 100644 (file)
index 0000000..0bd1a5b
--- /dev/null
@@ -0,0 +1,1368 @@
+/*
+ * Client-side functions which perform room operations
+ *
+ * Copyright (c) 1987-2018 by the citadel.org team
+ *
+ * This program is open source software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 3.
+ *
+ * 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.
+ */
+
+#include "textclient.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 */
+                       scr_printf("\n");
+                       c = 1;
+               }
+               if (f & QR_MAILBOX) {
+                       color(BRIGHT_YELLOW);
+               } else if (f & QR_PRIVATE) {
+                       color(BRIGHT_RED);
+               } else {
+                       color(DIM_WHITE);
+               }
+               scr_printf("%s", rmname);
+               if (f & QR_DIRECTORY) {
+                       scr_printf("]  ");
+               }
+               else {
+                       scr_printf(">  ");
+               }
+               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) {
+                               scr_printf("\n");
+                               c = 1;
+                       }
+                       scr_printf("%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);
+               scr_printf("\n   Rooms with unread messages:\n");
+               listrms(listing, LISTRMS_NEW_ONLY, -1, 0, NULL);
+               color(BRIGHT_CYAN);
+               scr_printf("\n\n   No unseen messages in:\n");
+               listrms(listing, LISTRMS_OLD_ONLY, -1, 0, NULL);
+               scr_printf("\n");
+       }
+
+       if (kn_floor_mode == 1) {
+               color(BRIGHT_CYAN);
+               scr_printf("\n   Rooms with unread messages on %s:\n",
+                       floorlist[(int) curr_floor]);
+               listrms(listing, LISTRMS_NEW_ONLY, curr_floor, 0, NULL);
+               color(BRIGHT_CYAN);
+               scr_printf("\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);
+               scr_printf("\n\n   Other floors:\n");
+               list_other_floors();
+               scr_printf("\n");
+       }
+
+       if (kn_floor_mode == 2) {
+               for (a = 0; a < 128; ++a) {
+                       if (floorlist[a][0] != 0) {
+                               color(BRIGHT_CYAN);
+                               scr_printf("\n   Rooms on %s:\n",
+                                       floorlist[a]);
+                               listrms(listing, LISTRMS_ALL, a, 0, NULL);
+                               scr_printf("\n");
+                       }
+               }
+       }
+
+       /* Free the room list */
+       while (listing) {
+               mptr = listing->next;
+               free(listing);
+               listing = mptr;
+       };
+
+       color(DIM_WHITE);
+}
+
+
+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);
+       scr_printf("\n   Forgotten public rooms:\n");
+       listrms(listing, LISTRMS_ALL, -1, 0, NULL);
+       scr_printf("\n");
+
+       /* Free the room list */
+       while (listing) {
+               mptr = listing->next;
+               free(listing);
+               listing = mptr;
+       };
+
+       color(DIM_WHITE);
+}
+
+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:
+       scr_printf("\n   Anonymous rooms:\n");
+           listrms(listing, LISTRMS_ALL, -1, QR_ANONONLY|QR_ANONOPT, NULL);
+       scr_printf("\n");
+               break;
+    case 1:
+       scr_printf("\n   Directory rooms:\n");
+           listrms(listing, LISTRMS_ALL, -1, QR_DIRECTORY, NULL);
+       scr_printf("\n");
+               break;
+    case 2:
+       scr_printf("\n   Matching \"%s\" rooms:\n", match);
+           listrms(listing, LISTRMS_ALL, -1, 0, match);
+       scr_printf("\n");
+               break;
+    case 3:
+       scr_printf("\n   Preferred only rooms:\n");
+           listrms(listing, LISTRMS_ALL, -1, QR_PREFONLY, NULL);
+       scr_printf("\n");
+               break;
+    case 4:
+       scr_printf("\n   Private rooms:\n");
+           listrms(listing, LISTRMS_ALL, -1, QR_PRIVATE, NULL);
+       scr_printf("\n");
+               break;
+    case 5:
+       scr_printf("\n   Read only rooms:\n");
+           listrms(listing, LISTRMS_ALL, -1, QR_READONLY, NULL);
+       scr_printf("\n");
+               break;
+       }
+
+       /* Free the room list */
+       while (listing) {
+               mptr = listing->next;
+               free(listing);
+               listing = mptr;
+       };
+
+       color(DIM_WHITE);
+}
+
+
+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 room_admin_name[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 admin */
+       r = CtdlIPCGetRoomAide(ipc, buf);
+       if (r / 100 == 2) {
+               safestrncpy(room_admin_name, buf, sizeof room_admin_name);
+       } else {
+               strcpy(room_admin_name, "");
+       }
+       if (IsEmptyStr(room_admin_name)) {
+               strcpy(room_admin_name, "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->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 admin */
+       do {
+               strprompt("Room admin (or 'none')", room_admin_name, 29);
+               if (!strcasecmp(room_admin_name, "none")) {
+                       strcpy(room_admin_name, "");
+                       break;
+               } else {
+                       r = CtdlIPCQueryUsername(ipc, room_admin_name, 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, room_admin_name, 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) {
+               scr_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) {
+               scr_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);
+
+       stty_ctdl(SB_RESTORE);
+       rv = system(transmit_cmd);
+       if (rv != 0)
+               scr_printf("failed to download '%s': %d\n", transmit_cmd, rv);
+       stty_ctdl(SB_NO_INTR);
+
+       /* 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) {
+               scr_printf("%s\n", buf);
+               return;
+       }
+
+       extract_token(comment, buf, 0, '|', sizeof comment);
+       extract_token(flnm, buf, 1, '|', sizeof flnm);
+       scr_printf("\nDirectory of %s on %s\n", flnm, comment);
+       scr_printf("-----------------------\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)
+                       scr_printf("%-14s %8s %s [%s]\n", flnm, flsz, comment, mimetype);
+               else
+                       scr_printf("%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];
+
+       newprompt("Name of user? ", username, USERNAME_SIZE);
+       if (username[0] == 0)
+               return;
+
+       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];
+
+       newprompt("Name of user? ", username, USERNAME_SIZE);
+       if (username[0] == 0)
+               return;
+
+       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 Admin)\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 room_admin_name[64];
+       int r;                  /* IPC response code */
+       char *text = NULL;
+
+       /* Name of currernt room admin */
+       r = CtdlIPCGetRoomAide(ipc, buf);
+       if (r / 100 == 2)
+               safestrncpy(room_admin_name, buf, sizeof room_admin_name);
+       else
+               strcpy(room_admin_name, "");
+
+       if (!IsEmptyStr(room_admin_name))
+               scr_printf("Room admin is %s.\n\n", room_admin_name);
+
+       r = CtdlIPCRoomInfo(ipc, &text, buf);
+       if (r / 100 != 1)
+               return;
+
+       if (text) {
+               fmout(screenwidth, NULL, text, NULL, 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) {
+               scr_printf("%s\n", buf);
+               return;
+       }
+       while (!IsEmptyStr(listing)) {
+               extract_token(buf, listing, 0, '\n', sizeof buf);
+               remove_token(listing, 0, '\n');
+               if (sigcaught == 0)
+                       scr_printf("%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_path)) {
+               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_path)) {
+               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_path)) {
+               char tmp[SIZ];
+
+               snprintf(tmp, sizeof tmp, "WINDOW_TITLE=%s", desc);
+               putenv(tmp);
+               stty_ctdl(SB_RESTORE);
+               editor_pid = fork();
+               if (editor_pid == 0) {
+                       chmod(temp, 0600);
+                       execlp(editor_path, editor_path, 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_path);
+               stty_ctdl(0);
+       } else {
+               scr_printf("Entering %s.  Press return twice when finished.\n", desc);
+               fp = fopen(temp, "r+");
+               citedit(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;
+
+       load_floorlist(ipc);
+
+       /* Fetch the expire policy (this will silently fail on old servers,
+        * resulting in "default" policy)
+        */
+       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 */
+       CtdlIPCSetMessageExpirationPolicy(ipc, 1, ep, buf);
+       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/textclient/routines.c b/textclient/routines.c
new file mode 100644 (file)
index 0000000..f82836c
--- /dev/null
@@ -0,0 +1,611 @@
+/*
+ * Client-side support functions.
+ *
+ * Copyright (c) 1987-2016 by the citadel.org team
+ *
+ *  This program is open source software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License version 3.
+ *
+ *  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.
+ */
+
+#include "textclient.h"
+
+#define IFAIDE if(axlevel>=AxAideU)
+#define IFNAIDE if (axlevel<AxAideU)
+
+extern unsigned userflags;
+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);
+       }
+}
+
+/*
+ * Edit a user's Internet email addresses
+ */
+void edit_user_internet_email_addresses(CtdlIPC *ipc, char *who)
+{
+       char buf[SIZ];
+       char *resp = NULL;
+       int num_recs = 0;
+       char **recs = NULL;
+       char ch;
+       int i, j;
+       int quitting = 0;
+       int modified = 0;
+       int r;
+       char emailaddrs[512];
+
+       r = CtdlIPCAideGetEmailAddresses(ipc, who, emailaddrs, buf);
+       if (r / 100 == 1) {
+               while (!IsEmptyStr(emailaddrs)) {
+                       extract_token(buf, emailaddrs, 0, '\n', sizeof buf);
+                       remove_token(emailaddrs, 0, '\n');
+                       if (!IsEmptyStr(buf)) {
+                               ++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);
+                       }
+               }
+       }
+
+       do {
+               scr_printf("\n");
+               color(BRIGHT_WHITE);
+               scr_printf("    Internet email addresses for %s\n", who);
+               color(DIM_WHITE);
+               scr_printf("--- --------------------------------------------------\n");
+               for (i=0; i<num_recs; ++i) {
+                       color(DIM_WHITE);
+                       scr_printf("%3d ", i+1);
+                       color(BRIGHT_CYAN);
+                       scr_printf("%s\n", recs[i]);
+                       color(DIM_WHITE);
+               }
+
+               ch = keymenu("", "<A>dd|<D>elete|<S>ave|<Q>uit");
+               switch(ch) {
+                       case 'a':
+                               newprompt("Enter new email address: ", buf, 50);
+                               striplt(buf);
+                               if (!IsEmptyStr(buf)) {
+                                       // FIXME validate the email address (format, our own domain, addr does not belong to another user)
+                                       ++num_recs;
+                                       if (num_recs == 1) {
+                                               recs = malloc(sizeof(char *));
+                                       }
+                                       else {
+                                               recs = realloc(recs, (sizeof(char *)) * num_recs);
+                                       }
+                                       recs[num_recs-1] = strdup(buf);
+                               }
+                               modified = 1;
+                               break;
+                       case 'd':
+                               i = intprompt("Delete which address", 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) {
+                                       scr_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 = CtdlIPCAideSetEmailAddresses(ipc, who, resp, buf);
+                               if (r / 100 != 4) {
+                                       scr_printf("%s\n", buf);
+                               } else {
+                                       scr_printf("Saved %d addresses.\n", num_recs);
+                                       modified = 0;
+                                       quitting = 1;
+                               }
+                               free(resp);
+                               break;
+                       case 'q':
+                               quitting = !modified || boolprompt("Quit without saving", 0);
+                               break;
+                       default:
+                               break;
+               }
+       } while (!quitting);
+
+       if (recs != NULL) {
+               for (i=0; i<num_recs; ++i) free(recs[i]);
+               free(recs);
+       }
+}
+
+
+/*
+ * 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) {                // user edit
+
+               /* val_user(ipc, user->fullname, 0); we used to display the vCard here but there's really no need */
+
+               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 login 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 = AxDeleted;
+       }
+
+       r = CtdlIPCAideSetUserParameters(ipc, user, buf);
+       if (r / 100 != 2) {
+               scr_printf("%s\n", buf);
+       }
+       free(user);
+
+       if (boolprompt("Edit this user's Internet email addresses", 0)) {
+               edit_user_internet_email_addresses(ipc, who);
+       }
+
+}
+
+
+/* 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) {
+
+               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_path)) {
+                       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]);
+       }
+
+}
+
+
+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) {
+               scr_printf("\r%79s\r","");
+       } 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;
+               scr_printf(fmt, dots_printed, "",
+                               curr * 100 / cmax, curr, cmax);
+               scr_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)
+{
+       FILE *who = (FILE *)popen("who am i","r");
+       if (who==NULL) {
+               strcpy(hbuf, ipc->ServInfo.fqdn);
+               return; 
+       }
+       fgets(hbuf, SIZ, who);
+       pclose(who);
+       stripallbut(hbuf, '(' , ')' );
+}
+
+/*
+ * 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/textclient/routines2.c b/textclient/routines2.c
new file mode 100644 (file)
index 0000000..961ef28
--- /dev/null
@@ -0,0 +1,581 @@
+/*
+ * More client-side support functions.
+ *
+ * Copyright (c) 1987-2017 by the citadel.org team
+ *
+ * This program is open source software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 3.
+ *
+ * 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.
+ */
+
+#include "textclient.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;
+
+
+/*
+ * return proper room prompt character
+ */
+int room_prompt(unsigned int qrflags)
+{
+       int a;
+       a = '>';
+       if (qrflags & QR_DIRECTORY)
+               a = ']';
+       return (a);
+}
+
+
+/*
+ * Register with name and address
+ */
+void entregis(CtdlIPC *ipc)
+{
+
+       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");
+}
+
+/*
+ * make all messages old in current room
+ */
+void updatels(CtdlIPC *ipc)
+{
+       char buf[256];
+       int r;                          /* IPC response code */
+
+       r = CtdlIPCSetLastRead(ipc, (maxmsgnum > highest_msg_read) ?  maxmsgnum : highest_msg_read, buf);
+
+       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) {
+               scr_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) {
+               scr_printf("%s\n", buf);
+       } else if (r < 0) {
+               scr_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 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);
+               if (rv < 0) {
+                       scr_printf("failed to change into %s Reason %s\nAborting now.\n", 
+                                  tempdir, 
+                                  strerror(errno));
+                       nukedir(tempdir);
+                       return;
+               }
+               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:
+                       stty_ctdl(3);
+                       execlp("rx", "rx", flnm, NULL);
+                       exit(1);
+               case 2:
+                       stty_ctdl(3);
+                       execlp("rb", "rb", NULL);
+                       exit(1);
+               case 3:
+                       stty_ctdl(3);
+                       execlp("rz", "rz", NULL);
+                       exit(1);
+               }
+       } else
+               do {
+                       b = ka_wait(&a);
+               } while ((b != xfer_pid) && (b != (-1)));
+       stty_ctdl(0);
+
+       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);
+                       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);
+}
+
+
+/*
+ * Validate new users
+ */
+void validate(CtdlIPC *ipc)
+{
+       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;
+
+       stty_ctdl(SB_RESTORE);
+       a = fork();
+       if (a == 0) {
+               signal(SIGINT, SIG_DFL);
+               signal(SIGQUIT, SIG_DFL);
+               execlp(getenv("SHELL"), getenv("SHELL"), NULL);
+               scr_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);
+}
+
+/*
+ * <.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);
+       scr_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);
+       scr_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) {
+               scr_printf("%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) {
+                       scr_printf("\n");
+                       pos = 1;
+               }
+               scr_printf("%s, ", buf);
+               pos = pos + strlen(buf) + 2;
+       }
+       scr_printf("%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);
+               scr_printf("\n");
+               if (!strcmp(who, "?"))
+                       list_bio(ipc);
+       } while (!strcmp(who, "?"));
+
+       r = CtdlIPCGetBio(ipc, who, &resp, buf);
+       if (r / 100 != 1) {
+               scr_printf("%s\n", buf);
+               return;
+       }
+       while (!IsEmptyStr(resp)) {
+               extract_token(buf, resp, 0, '\n', sizeof buf);
+               remove_token(resp, 0, '\n');
+               scr_printf("%s\n", buf);
+       }
+       if (resp) free(resp);
+}
diff --git a/textclient/screen.c b/textclient/screen.c
new file mode 100644 (file)
index 0000000..cd1d526
--- /dev/null
@@ -0,0 +1,258 @@
+/*
+ * Screen output handling
+ *
+ * Copyright (c) 1987-2018 by the citadel.org team
+ *
+ * This program is open source software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 3.
+ *
+ * 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.
+ */
+
+#include "textclient.h"
+
+
+int enable_status_line = 0;
+char status_line[1024] = "     ";
+
+/* the default paginator prompt will be replaced by the server's prompt when we learn it */
+char *moreprompt = " -- more -- ";
+
+int screenheight = 24;
+int screenwidth = 80;
+int lines_printed = 0;
+int cols_printed = 0;
+
+extern int rc_ansi_color;
+extern int rc_prompt_control;
+void do_keepalive(void);
+
+/*
+ * Attempt to discover the screen dimensions. 
+ * WARNING: This is sometimes 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 (ioctl(0, TIOCGWINSZ, &xwinsz) == 0) {
+               if (xwinsz.height)
+                       screenheight = (int) xwinsz.height;
+               if (xwinsz.width)
+                       screenwidth = (int) xwinsz.width;
+       }
+#endif
+}
+
+
+/*
+ * Initialize the screen
+ */
+void screen_new(void)
+{
+       send_ansi_detect();
+       look_for_ansi();
+       cls(0);
+       color(DIM_WHITE);
+}
+
+
+
+/*
+ * Beep.
+ */
+void ctdl_beep(void) {
+       putc(7, stdout);
+}
+       
+
+
+
+/*
+ * scr_printf() outputs to the terminal
+ */
+int scr_printf(char *fmt, ...)
+{
+       static char outbuf[4096];       /* static for performance -- not re-entrant -- change if needed */
+       va_list ap;
+       int retval;
+       int i, len;
+
+       va_start(ap, fmt);
+       retval = vsnprintf(outbuf, sizeof outbuf, fmt, ap);
+       va_end(ap);
+
+       len = strlen(outbuf);
+       for (i=0; i<len; ++i) {
+               scr_putc(outbuf[i]);
+       }
+       return retval;
+}
+
+
+/*
+ * Read one character from the terminal
+ */
+int scr_getc(int delay)
+{
+       unsigned char buf;
+
+       scr_flush();
+
+       buf = '\0';
+       if (!read (0, &buf, 1))
+               logoff(NULL, 3);
+
+       lines_printed = 0;
+       return buf;
+}
+
+/*
+ * Issue the paginator prompt (more / hit any key to continue)
+ */
+void hit_any_key(void) {
+       int a, b;
+
+       color(COLOR_PUSH);
+       color(DIM_RED);
+       scr_printf("%s\r", moreprompt);
+       color(COLOR_POP);
+       b=inkey();
+       for (a=0; a<screenwidth; ++a) {
+               scr_putc(' ');
+       }
+       scr_printf("\r");
+
+       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;
+}
+
+
+/*
+ * Output one character to the terminal
+ */
+int scr_putc(int c)
+{
+       /* handle tabs normally */
+       if (c == '\t') {
+               do {
+                       scr_putc(' ');
+               } while ((cols_printed % 8) != 0);
+               return(c);
+       }
+
+       /* Output the character... */
+       if (putc(c, stdout) == EOF) {
+               logoff(NULL, 3);
+       }
+
+       if (c == '\n') {
+               ++lines_printed;
+               cols_printed = 0;
+       }
+       else if (c == '\r') {
+               cols_printed = 0;
+       }
+       else if (isprint(c)) {
+               ++cols_printed;
+               if ((screenwidth > 0) && (cols_printed > screenwidth)) {
+                       ++lines_printed;
+                       cols_printed = 0;
+               }
+       }
+
+       /* How many lines output before stopping for the paginator?
+        * Depends on whether we are displaying a status line.
+        */
+       int height_offset = ( ((enable_color) && (screenwidth > 0) && (enable_status_line)) ? (3) : (2) ) ;
+
+       /* Ok, go check it.  Stop and display the paginator prompt if necessary. */
+       if ((screenheight > 0) && (lines_printed > (screenheight-height_offset))) {
+               lines_printed = 0;
+               hit_any_key();
+               lines_printed = 0;
+               cols_printed = 0;
+       }
+
+       return c;
+}
+
+void scr_flush(void)
+{
+       if ((enable_color) && (screenwidth > 0) && (enable_status_line)) {
+               if (strlen(status_line) < screenwidth) {
+                       memset(&status_line[strlen(status_line)], 32, screenwidth - strlen(status_line));
+               }
+               printf("\033[s\033[1;1H\033[K\033[7m");
+               fwrite(status_line, screenwidth, 1, stdout);
+               printf("\033[27m\033[u");
+       }
+       fflush(stdout);
+}
+
+
+static volatile int caught_sigwinch = 0;
+
+
+/*
+ * scr_winch() handles window size changes from SIGWINCH
+ * resizes all our windows for us
+ */
+sighandler_t scr_winch(int signum)
+{
+       /* if we receive this signal, we must be running
+        * in a terminal that supports resizing.
+        */
+       caught_sigwinch = 1;
+       check_screen_dims();
+       signal(SIGWINCH, scr_winch);
+}
+
+
+
+/*
+ * Display a 3270-style "wait" indicator at the bottom of the screen
+ */
+void scr_wait_indicator(int state) {
+       int sp = (screenwidth - 2);
+
+       if (!enable_status_line) return;
+
+       if (screenwidth > 0) {
+               switch (state) {
+                       default:
+                       case 0:  /* Idle */
+                               status_line[sp] = ' ';
+                               break;
+                       case 1:  /* Waiting */
+                               status_line[sp] = 'X';
+                               break;
+                       case 2:  /* Receiving */
+                               status_line[sp] = '<';
+                               break;
+                       case 3:  /* Sending */
+                               status_line[sp] = '>';
+                               break;
+               }
+               scr_flush();
+       }
+}
+
diff --git a/textclient/scripts/mk_svn_revision.sh b/textclient/scripts/mk_svn_revision.sh
deleted file mode 100755 (executable)
index 144ffa4..0000000
+++ /dev/null
@@ -1,78 +0,0 @@
-#!/bin/sh
-#
-# Script to generate svn_revision.c
-#
-
-ECHO=/usr/bin/printf
-
-
-SCRIPT_DIR=`dirname $0`
-SRC_DIR=`dirname $SCRIPT_DIR`
-CUR_DIR=`pwd`
-C_FILE="$CUR_DIR/svn_revision.c"
-H_FILE="$CUR_DIR/svn_revision.h"
-
-# determine if this code base came from subversion.
-if test -d $SRC_DIR/.svn  ; then
-       echo "have subversion repository"
-       SVNVERSION=`which svnversion`
-       if test -x $SVNVERSION  ; then
-               echo "have svnversion at $SVNVERSION"
-               BUILD=`svnversion -n .`
-               echo "This code base svn-revision: $BUILD"
-               CAN_BUILD_SVN_REVISION="yes"
-       fi
-else 
-    if test -d $SRC_DIR/../.git  ; then
-       echo "have Git repository."
-       BUILD=`/usr/bin/git log -1 --pretty=%h . `
-       echo "This code base git-revision: $BUILD"
-       CAN_BUILD_SVN_REVISION="yes"
-    else
-       if test -f $C_FILE; then
-           exit
-       fi
-    fi
-fi
-
-if [ "$CAN_BUILD_SVN_REVISION" = "yes" ] ; then
-
-cat <<EOF > $C_FILE
-/*
- * Subversion / GIT revision functions
- *
- * Autogenerated at make/release time
- *
- * Do not modify this file
- *
- */
-const char *svn_revision (void)
-{
-       const char *SVN_Version = "$BUILD";
-       return SVN_Version;
-}
-EOF
-
-elif test ! -f $C_FILE  ; then
-
-cat <<EOF > $C_FILE
-/*
- * Subversion / GIT revision functions
- *
- * Autogenerated at make time
- *
- * There should have been one with your source distribution
- *
- * Do not modify this file
- *
- */
-const char *svn_revision (void)
-{
-       const char *SVN_Version = "(unknown)";
-       return SVN_Version;
-}
-EOF
-
-fi
diff --git a/textclient/src/citadel.c b/textclient/src/citadel.c
deleted file mode 100644 (file)
index 0385c37..0000000
+++ /dev/null
@@ -1,2202 +0,0 @@
-/*
- * Main source module for the client program.
- *
- * Copyright (c) 1987-2018 by the citadel.org team
- *
- * This program is open source software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License version 3.
- *
- * 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.
- */
-
-#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_ipc.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 "ecrash.h"
-#include "md5.h"
-
-#define IFEXPERT if (userflags&US_EXPERT)
-#define IFNEXPERT if ((userflags&US_EXPERT)==0)
-#define IFAIDE if (axlevel>=AxAideU)
-#define IFNAIDE if (axlevel<AxAideU)
-
-int rordercmp(struct ctdlroomlisting *r1, struct ctdlroomlisting *r2);
-march *marchptr = NULL;
-extern char *moreprompt;
-
-/* 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 printcmd[SIZ];            /* print command */
-int editor_pid = (-1);
-char fullname[USERNAME_SIZE];
-unsigned room_flags;
-unsigned room_flags2;
-int entmsg_ok = 0;
-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 = AxDeleted;              /* access level */
-char is_room_aide = 0;         /* boolean flag, 1 if room admin */
-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 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;
-
-
-/*
- * 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
- */
-       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, 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) {
-               scr_printf("%s\n", buf);
-               return;
-       }
-
-       scr_printf("       User Name           Num  L Last Visit Logins Messages\n");
-       scr_printf("------------------------- ----- - ---------- ------ --------\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) {
-                       scr_printf("%-25s ", fl);
-                       scr_printf("%5ld %d ", extract_long(buf, 2),
-                              extract_int(buf, 1));
-                       lc = extract_long(buf, 3);
-                       localtime_r(&lc, &tmbuf);
-                       scr_printf("%02d/%02d/%04d ",
-                              (tmbuf.tm_mon + 1),
-                              tmbuf.tm_mday,
-                              (tmbuf.tm_year + 1900));
-                       scr_printf("%6ld %8ld\n", extract_long(buf, 4), extract_long(buf, 5));
-                   }
-
-               }
-       }
-       free(listing);
-       scr_printf("\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 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);
-               }
-       }
-       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;
-
-       /* Determine, based on the room's default view, whether an <E>nter message
-        * command will be valid here.
-        */
-       switch(room->RRdefaultview) {
-               case VIEW_BBS:
-               case VIEW_MAILBOX:
-               case VIEW_BLOG:
-                                       entmsg_ok = 1;
-                                       break;
-               default:
-                                       entmsg_ok = 0;
-                                       break;
-       }
-
-       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);
-                       if (rv) 
-                               scr_printf("*** failed to check for mail calling %s Reason %d.\n",
-                                          rc_gotmail_cmd, rv);
-               }
-       }
-       free(room);
-
-       if (screenwidth>5) snprintf(&status_line[1], screenwidth-1, "%s  |  %s  |  %s  |  %s  |  %d new mail  |",
-               (secure ? "Encrypted" : "Unencrypted"),
-               ipc->ServInfo.humannode,
-               ipc->ServInfo.site_location,
-               room_name,
-               newmailcount
-       );
-}
-
-/* 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];
-
-       /* 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) {
-               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]);
-       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);
-       }
-
-       /* 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) {
-               scr_printf("%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("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_path)) {
-       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;
-
-       /* get #users, #active & server uptime */
-       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# */
-       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)CLIENT_VERSION/100);
-       scr_printf("server build %s,\n", ipc->ServInfo.svn_revision, (float)CLIENT_VERSION/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);
-}
-
-/*
- * 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);
-}
-
-
-/*
- * 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);
-       moreprompt = ipc->ServInfo.moreprompt;
-
-       /* be nice and identify ourself to the server */
-       CtdlIPCIdentifySoftware(ipc, CLIENT_TYPE, 0, CLIENT_VERSION,
-                (ipc->isLocal ? "local" : PACKAGE_STRING),
-                (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);
-       }
-}
-
-
-
-/*
- * Session username compare function for SortOnlineUsers()
- */
-int rwho_username_cmp(const void *rec1, const void *rec2) {
-       char *u1, *u2;
-
-       u1 = strchr(rec1, '|');
-       u2 = strchr(rec2, '|');
-
-       return strcasecmp( (u1?++u1:"") , (u2?++u2:"") );
-}
-
-
-/*
- * Idle time 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.
- *
- * If 'condense' is nonzero, multiple sessions for the same user will be
- * combined into one for brevity.
- */
-char *SortOnlineUsers(char *listing, int condense) {
-       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);
-       }
-
-       /* Sort by idle time */
-       qsort(sortbuf, rows, SIZ, idlecmp);
-
-       /* Combine multiple sessions for the same user */
-       if (condense) {
-               qsort(sortbuf, rows, SIZ, rwho_username_cmp);
-               if (rows > 1) for (i=1; i<rows; ++i) if (i>0) {
-                       char u1[USERNAME_SIZE];
-                       char u2[USERNAME_SIZE];
-                       extract_token(u1, &sortbuf[(i-1)*SIZ], 1, '|', sizeof u1);
-                       extract_token(u2, &sortbuf[i*SIZ], 1, '|', sizeof u2);
-                       if (!strcasecmp(u1, u2)) {
-                               memcpy(&sortbuf[i*SIZ], &sortbuf[(i+1)*SIZ], (rows-i-1)*SIZ);
-                               --rows;
-                               --i;
-                       }
-               }
-
-               qsort(sortbuf, rows, SIZ, idlecmp);     /* idle sort again */
-       }
-
-       /* Copy back to a \n delimited list */
-       strcpy(retbuf, "");
-       for (i=0; i<rows; ++i) {
-               if (!IsEmptyStr(&sortbuf[i*SIZ])) {
-                       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);
-               scr_printf("           User Name               Room          ");
-               if (screenwidth >= 80) scr_printf(" Idle        From host");
-               scr_printf("\n");
-               color(DIM_WHITE);
-               scr_printf("   ------------------------- --------------------");
-               if (screenwidth >= 80) scr_printf(" ---- ------------------------");
-               scr_printf("\n");
-       }
-       r = CtdlIPCOnlineUsers(ipc, &listing, &timenow, buf);
-       listing = SortOnlineUsers(listing, (!longlist));
-       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);
-
-                               scr_printf("  Flags: %s\n", flags);
-                               scr_printf("Session: %d\n", extract_int(buf, 0));
-                               scr_printf("   Name: %s\n", username);
-                               scr_printf("In room: %s\n", roomname);
-                               scr_printf("   Host: %s\n", fromhost);
-                               scr_printf(" Client: %s\n", clientsoft);
-                               scr_printf("   Idle: %ld:%02ld:%02ld\n",
-                                       (long) idlehours,
-                                       (long) idlemins,
-                                       (long) idlesecs);
-
-                               if ( (!IsEmptyStr(actual_user)&&
-                                     !IsEmptyStr(actual_room)&&
-                                     !IsEmptyStr(actual_host))) {
-                                       scr_printf("(really ");
-                                       if (!IsEmptyStr(actual_user)) scr_printf("<%s> ", actual_user);
-                                       if (!IsEmptyStr(actual_room)) scr_printf("in <%s> ", actual_room);
-                                       if (!IsEmptyStr(actual_host)) scr_printf("from <%s> ", actual_host);
-                                       scr_printf(")\n");
-                               }
-                               scr_printf("\n");
-
-                       } else {
-                               if (isidle == 0) {
-                                       if (extract_int(buf, 0) == last_session) {
-                                               scr_printf("        ");
-                                       }
-                                       else {
-                                               color(BRIGHT_MAGENTA);
-                                               scr_printf("%-3s", flags);
-                                       }
-                                       last_session = extract_int(buf, 0);
-                                       color(BRIGHT_CYAN);
-                                       scr_printf("%-25s ", username);
-                                       color(BRIGHT_MAGENTA);
-                                       roomname[20] = 0;
-                                       scr_printf("%-20s", roomname);
-
-                                       if (screenwidth >= 80) {
-                                               scr_printf(" ");
-                                               if (idletime > rc_idle_threshold) {
-                                                       /* over 1000d, must be gone fishing */
-                                                       if (idlehours > 23999) {
-                                                               scr_printf("fish");
-                                                       /* over 10 days */
-                                                       } else if (idlehours > 239) {
-                                                               scr_printf("%3ldd", idlehours / 24);
-                                                       /* over 10 hours */
-                                                       } else if (idlehours > 9) {
-                                                               scr_printf("%1ldd%02ld",
-                                                                       idlehours / 24,
-                                                                       idlehours % 24);
-                                                       /* less than 10 hours */
-                                                       }
-                                                       else {
-                                                               scr_printf("%1ld:%02ld", idlehours, idlemins);
-                                                       }
-                                               }
-                                               else {
-                                                       scr_printf("    ");
-                                               }
-                                               scr_printf(" ");
-                                               color(BRIGHT_CYAN);
-                                               fromhost[24] = '\0';
-                                               scr_printf("%-24s", fromhost);
-                                       }
-                                       scr_printf("\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) {
-       scr_printf(s);
-}
-
-/*
- * 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];
-       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.debugLevel = ECRASH_DEBUG_VERBOSE;
-       params.dumpAllThreads = TRUE;
-       params.useBacktraceSymbols = 1;
-       params.signals[0]=SIGSEGV;
-       params.signals[1]=SIGILL;
-       params.signals[2]=SIGBUS;
-       params.signals[3]=SIGABRT;
-#endif 
-       setIPCErrorPrintf(scr_printf);
-       setCryptoStatusHook(statusHook);
-       
-       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(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
-
-       /* 
-        * 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], "-p")) {
-                       // ignore this argument when called from telnetd
-                       argc = shift(argc, argv, a, 1);
-               }
-       }
-       
-       screen_new();
-       /* Get screen dimensions.  First we go to a default of 80x24.
-        * Then attempt to read the actual screen size from the terminal.
-        */
-       check_screen_dims();
-
-
-#ifdef __CYGWIN__
-       newprompt("Connect to (return for local server): ", hostbuf, 64);
-#endif
-
-       scr_printf("Attaching to server...\n");
-       ipc = CtdlIPC_new(argc, argv, hostbuf, portbuf);
-       if (!ipc) {
-               error_printf("Can't connect: %s\n", strerror(errno));
-               logoff(NULL, 3);
-       }
-
-       CtdlIPC_SetNetworkStatusCallback(ipc, scr_wait_indicator);
-
-       if (!(ipc->isLocal)) {
-               scr_printf("Connected to %s [%s].\n", ipc->ip_hostname, ipc->ip_address);
-       }
-
-       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_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);
-                               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;
-       }
-
-       /* FIXME this is a stupid way to do guest mode but it's a reasonable test harness FIXME */
-       if ( (ipc->ServInfo.guest_logins) && (!strcasecmp(fullname, "guest")) ) {
-               goto PWOK;
-       }
-
-       /* 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, -(SIZ-1));
-       }
-
-       if (*nonce) {
-               r = CtdlIPCTryApopPassword(ipc, make_apop_string(password, nonce, hexstring, sizeof hexstring), aaa);
-               if (r / 100 != 2) {
-                       strproc(password);
-                       r = CtdlIPCTryApopPassword(ipc, make_apop_string(password, nonce, hexstring, sizeof hexstring), aaa);
-               }
-       } else {
-               r = CtdlIPCTryPassword(ipc, password, aaa);
-               if (r / 100 != 2) {
-                       strproc(password);
-                       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 (rv)
-                                       scr_printf("*** failed to check for mail calling %s Reason %d.\n",
-                                                  rc_gotmail_cmd, rv);
-
-                       }
-               }
-               if ((axlevel >= AxAideU) && (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);
-
-       r = CtdlIPCGetConfig(ipc, &myself, aaa);
-       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();            /* get screen size */
-#endif
-
-               if (termn8 == 0)
-                       switch (mcmd) {
-                       case 1:
-                               display_help(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 */
-                               gotonext(ipc);
-                               break;
-                       case 90:                        /* <.A>bandon */
-                               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)) {
-                                       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:
-                               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 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(argbuf, "?");
-                               }
-                               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) {
-                                       updatels(ipc);
-                                       a = 0;
-                                       termn8 = 1;
-                               }
-                               break;
-
-                       case 85:
-                               scr_printf("All users will be disconnected!  "
-                                          "Really terminate the server? ");
-                               if (yesno() == 1) {
-                                       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 6:
-                               gotonext(ipc);
-                               break;
-
-                       case 3:
-                               chatmode(ipc);
-                               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 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:
-                               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) {
-               scr_printf("\n\nType 'off' to disconnect, or next user...\n");
-       }
-       CtdlIPCLogout(ipc);
-       if ((mcmd == 29) || (mcmd == 15)) {
-               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/textclient/src/citadel_ipc.c b/textclient/src/citadel_ipc.c
deleted file mode 100644 (file)
index e0e299a..0000000
+++ /dev/null
@@ -1,3437 +0,0 @@
-/*
- * Copyright (c) 1987-2018 by the citadel.org team
- *
- *  This program is open source software; you can redistribute it and/or modify
- *  it under the terms of the GNU General Public License version 3.
- *
- *  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.
- */
-
-#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_ipc.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;
-char ctdl_autoetc_dir[PATH_MAX]="";
-char file_citadel_rc[PATH_MAX]="";
-char ctdl_run_dir[PATH_MAX]="";
-char ctdl_etc_dir[PATH_MAX]="";
-char ctdl_home_directory[PATH_MAX] = "";
-char file_citadel_socket[PATH_MAX]="";
-
-char *viewdefs[]={
-        "Messages",
-        "Summary",
-        "Address book",
-        "Calendar",
-        "Tasks"
-};
-
-char *axdefs[]={
-        "Deleted",
-        "New User",
-        "Problem User",
-        "Local User",
-        "Network User",
-        "Preferred User",
-        "Admin",
-        "Admin"
-        };
-
-
-void CtdlIPC_lock(CtdlIPC *ipc)
-{
-       if (ipc->network_status_cb) ipc->network_status_cb(1);
-#ifdef THREADED_CLIENT
-       pthread_mutex_lock(&(ipc->mutex));
-#endif
-}
-
-
-void CtdlIPC_unlock(CtdlIPC *ipc)
-{
-#ifdef THREADED_CLIENT
-       pthread_mutex_unlock(&(ipc->mutex));
-#endif
-       if (ipc->network_status_cb) ipc->network_status_cb(0);
-}
-
-#ifdef __cplusplus
-}
-#endif
-
-
-char *libcitadelclient_version_string(void) {
-        return "libcitadelclient(unnumbered)";
-}
-
-
-
-
-#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] = "";
-
-       StripSlashes(ctdldir, 1);
-
-#ifndef HAVE_RUN_DIR
-       basedir=ctdldir;
-#else
-       basedir=RUN_DIR;
-#endif
-       COMPUTE_DIRECTORY(ctdl_run_dir);
-       StripSlashes(ctdl_run_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_etc_dir);
-       StripSlashes(ctdl_etc_dir, 1);
-
-
-
-       snprintf(file_citadel_rc, 
-                        sizeof file_citadel_rc,
-                        "%scitadel.rc",
-                        ctdl_etc_dir);
-       StripSlashes(file_citadel_rc, 0);
-
-       snprintf(file_citadel_socket, 
-                        sizeof file_citadel_socket,
-                               "%scitadel.socket",
-                        ctdl_run_dir);
-       StripSlashes(file_citadel_socket, 0);
-
-       DBG_PRINT(ctdl_run_dir);
-       DBG_PRINT(file_citadel_socket);
-       DBG_PRINT(ctdl_etc_dir);
-       DBG_PRINT(file_citadel_rc);
-}
-
-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)
-{
-       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)
-{
-       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)
-{
-       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)
-{
-       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)
-{
-       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)
-{
-       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)
-{
-       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)
-{
-       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)
-{
-       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)
-{
-       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]->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 80|24|%d",
-               uret->flags
-       );
-       return CtdlIPCGenericCommand(ipc, aaa, NULL, 0, NULL, NULL, cret);
-}
-
-
-/* RENU */
-int CtdlIPCRenameUser(CtdlIPC *ipc, char *oldname, char *newname, char *cret)
-{
-       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)
-{
-       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]->RRcurrentview = extract_int(cret, 11);
-               rret[0]->RRdefaultview = extract_int(cret, 12);
-               /* position 13 is a trash folder flag ... irrelevant in this client */
-               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)
-{
-       int ret;
-       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)
-{
-       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, "room=", 5))
-                                       safestrncpy(mret[0]->room, &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)
-{
-       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)
-{
-       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;
-                       case 24:        ipc->ServInfo.guest_logins = atoi(buf);
-                                       break;
-                       }
-               }
-
-       }
-       if (listing) free(listing);
-       return ret;
-}
-
-
-/* RDIR */
-int CtdlIPCReadDirectory(CtdlIPC *ipc, char **listing, char *cret)
-{
-       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)
-{
-       int ret;
-       char aaa[64];
-
-       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)
-{
-       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)
-{
-       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)
-{
-       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)
-{
-       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)
-{
-       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)
-{
-       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[64];
-
-       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)
-{
-       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[64];
-
-       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)
-{
-       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)
-{
-       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)
-{
-       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)
-{
-       int ret;
-       char *aaa;
-
-       if (!cret) return -2;
-       if (!username) return -2;
-       if (axlevel < AxDeleted || axlevel > AxAideU) 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[64];
-
-       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)
-{
-       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)
-{
-       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)
-{
-       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)
-{
-       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)
-{
-       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)
-{
-       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)
-{
-       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)
-{
-       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(MimeType) + 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)
-{
-       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)
-{
-       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)
-{
-       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)
-{
-       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)
-{
-       int ret;
-       char *aaa;
-
-       if (developerid < 0 || clientid < 0 || revision < 0 ||
-           !software_name) {
-               developerid = 8;
-               clientid = 0;
-               revision = CLIENT_VERSION - 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)
-{
-       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[64];
-
-       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)
-{
-       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[64];
-
-       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[64];
-
-       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)
-{
-       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)
-{
-       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)
-{
-       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)
-{
-       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)
-{
-       time_t tret;
-       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)
-{
-       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)
-{
-       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;
-}
-
-
-/* AGEA */
-int CtdlIPCAideGetEmailAddresses(CtdlIPC *ipc, const char *who, char *target_buf, char *cret)
-{
-       int ret;
-       char aaa[SIZ];
-       char *emailaddrs = NULL;
-       size_t emailaddrs_len = 0;
-
-       sprintf(aaa, "AGEA %s", who);
-       ret = CtdlIPCGenericCommand(ipc, aaa, NULL, 0, &emailaddrs, &emailaddrs_len, cret);
-
-       if (ret / 100 == 1) {
-               strcpy(target_buf, emailaddrs);
-       }
-
-       if (emailaddrs != NULL) {
-               free(emailaddrs);
-       }
-
-       return ret;
-}
-
-
-/* ASEA */
-int CtdlIPCAideSetEmailAddresses(CtdlIPC *ipc, const char *who, char *emailaddrs, char *cret)
-{
-       char aaa[SIZ];
-       int ret;
-
-       if (!who) return -2;
-       if (!emailaddrs) return -2;
-       if (!cret) return -2;
-
-       sprintf(aaa, "ASEA %s", who);
-       ret = CtdlIPCGenericCommand(ipc, aaa, emailaddrs, 0, NULL, NULL, cret);
-       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, GPEXWhichPolicy which,
-               struct ExpirePolicy **policy, char *cret)
-{
-       static char *proto[] = {
-               strof(roompolicy),
-               strof(floorpolicy),
-               strof(sitepolicy),
-               strof(mailboxespolicy)
-       };
-       char cmd[256];
-       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)
-{
-       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)
-{
-       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[64];
-
-       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, (const unsigned char*) "Citadel SID", 14);
-#endif
-
-       /* 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 */
-       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;
-
-       error_printf("Encrypting with %s cipher %s\n",
-               SSL_CIPHER_get_version(SSL_get_current_cipher(ipc->ssl)),
-               SSL_CIPHER_get_name(SSL_get_current_cipher(ipc->ssl))
-       );
-       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)
-{
-       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);
-}
-
-
-
-
-/* ************************************************************************** */
-/*          Stuff below this line is not for public consumption            */
-/* ************************************************************************** */
-
-
-/* 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)
-{
-       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)
-{
-       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) {
-       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)
-{
-       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) {
-               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)
-{
-       size_t len;
-       int calls;      /* How many calls in the pipeline */
-       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)
-{
-       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)
-{
-       int ret = -1;
-       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) {
-               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];
-       int ret;
-
-       if (!command) return -2;
-       if (!proto_response) return -2;
-
-       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;
-}
-
-
-/*
- * Connect to a Citadel on a remote host using a TCP/IP socket
- */
-static int tcp_connectsock(char *host, char *service)
-{
-       struct in6_addr serveraddr;
-       struct addrinfo hints;
-       struct addrinfo *res = NULL;
-       struct addrinfo *ai = NULL;
-       int rc = (-1);
-       int sock = (-1);
-
-       if ((host == NULL) || IsEmptyStr(host)) {
-               service = DEFAULT_HOST ;
-       }
-       if ((service == NULL) || IsEmptyStr(service)) {
-               service = DEFAULT_PORT ;
-       }
-
-       memset(&hints, 0x00, sizeof(hints));
-       hints.ai_flags = AI_NUMERICSERV;
-       hints.ai_family = AF_UNSPEC;
-       hints.ai_socktype = SOCK_STREAM;
-
-       /*
-        * Handle numeric IPv4 and IPv6 addresses
-        */
-       rc = inet_pton(AF_INET, host, &serveraddr);
-       if (rc == 1) {                                          /* dotted quad */
-               hints.ai_family = AF_INET;
-               hints.ai_flags |= AI_NUMERICHOST;
-       }
-       else {
-               rc = inet_pton(AF_INET6, host, &serveraddr);
-               if (rc == 1) {                                  /* IPv6 address */
-                       hints.ai_family = AF_INET6;
-                       hints.ai_flags |= AI_NUMERICHOST;
-               }
-       }
-
-       /* Begin the connection process */
-
-       rc = getaddrinfo(host, service, &hints, &res);
-       if (rc != 0) {
-               return(-1);
-       }
-
-       /*
-        * Try all available addresses until we connect to one or until we run out.
-        */
-       for (ai = res; ai != NULL; ai = ai->ai_next) {
-               sock = socket(ai->ai_family, ai->ai_socktype, ai->ai_protocol);
-               if (sock < 0) return(-1);
-
-               rc = connect(sock, ai->ai_addr, ai->ai_addrlen);
-               if (rc >= 0) {
-                       return(sock);           /* Connected! */
-               }
-               else {
-                       close(sock);            /* Failed.  Close the socket to avoid fd leak! */
-               }
-       }
-
-       return(-1);
-}
-
-
-
-
-
-/*
- * Connect to a Citadel on the local host using a unix domain socket
- */
-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;
-       const 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 (!(DH_generate_parameters_ex(dh, 128, DH_GENERATOR_2, 0))) {
-               error_printf("Can't generate DH parameters: %s\n", ERR_reason_error_string(ERR_get_error()));
-               DH_free(dh);
-               return;
-       }
-
-       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 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);
-               
-               if (ret > 0) {
-                       
-                       *(ipc->BufPtr) = '\0';
-                       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 */
-       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, "%s", file_citadel_socket);
-               }
-               else {
-                       snprintf(sockpath, sizeof sockpath, "%s/%s", citport, "citadel.socket");
-               }
-               printf("[%s]\n", sockpath);
-               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);
-               strcpy(ipc->ip_hostname, "");
-               strcpy(ipc->ip_address, "");
-               return ipc;
-       }
-
-       printf("[%s:%s]\n", cithost, citport);
-       ipc->sock = tcp_connectsock(cithost, citport);
-       if (ipc->sock == -1) {
-               ifree(ipc);
-               return 0;
-       }
-
-
-       /* Learn the actual network identity of the host to which we are connected */
-
-       struct sockaddr_in6 clientaddr;
-       unsigned int addrlen = sizeof(clientaddr);
-
-       ipc->ip_hostname[0] = 0;
-       ipc->ip_address[0] = 0;
-
-       getpeername(ipc->sock, (struct sockaddr *)&clientaddr, &addrlen);
-       getnameinfo((struct sockaddr *)&clientaddr, addrlen,
-               ipc->ip_hostname, sizeof ipc->ip_hostname, NULL, 0, 0
-       );
-       getnameinfo((struct sockaddr *)&clientaddr, addrlen,
-               ipc->ip_address, sizeof ipc->ip_address, NULL, 0, NI_NUMERICHOST
-       );
-
-       /* stuff other things elsewhere */
-
-       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/textclient/src/client_chat.c b/textclient/src/client_chat.c
deleted file mode 100644 (file)
index 8933687..0000000
+++ /dev/null
@@ -1,304 +0,0 @@
-/*
- * front end for multiuser chat
- *
- * Copyright (c) 1987-2016 by the citadel.org team
- *
- * This program is open source software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License version 3.
- *
- * 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.
- */
-
-#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_ipc.h"
-#include "client_chat.h"
-#include "commands.h"
-#include "routines.h"
-#include "citadel_decls.h"
-#include "rooms.h"
-#include "messages.h"
-#include "screen.h"
-
-#define MIN(a, b) ((a) < (b) ? (a) : (b))
-
-extern char temp[];
-char last_paged[SIZ] = "";
-
-void chatmode(CtdlIPC *ipc)
-{
-       char wbuf[SIZ];
-       char buf[SIZ];
-       char response[SIZ];
-       char c_user[SIZ];
-       char c_text[SIZ];
-       char last_user[SIZ];
-       int send_complete_line;
-       char ch;
-       int a, pos;
-       int seq = 0;
-
-       fd_set rfds;
-       struct timeval tv;
-       int retval;
-
-       CtdlIPC_chat_send(ipc, "RCHT enter");
-       CtdlIPC_chat_recv(ipc, buf);
-       if (buf[0] != '2') {
-               scr_printf("%s\n", &buf[4]);
-               return;
-       }
-       scr_printf("Entering chat mode (type /quit to exit)\n");
-
-       strcpy(buf, "");
-       strcpy(wbuf, "");
-       strcpy(last_user, ""); 
-       color(BRIGHT_YELLOW);
-       scr_printf("\n");
-       scr_printf("> ");
-       send_complete_line = 0;
-
-       while (1) {
-               scr_flush();
-               FD_ZERO(&rfds);
-               FD_SET(0, &rfds);
-               tv.tv_sec = 1;
-               tv.tv_usec = 0;
-               retval = select(1, &rfds, NULL, NULL, &tv);
-
-               if (retval < 0) {
-                       color(BRIGHT_WHITE);
-                       scr_printf("Server gone Exiting chat mode\n");
-                       scr_flush();
-                       return;
-               }
-
-               /* 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;
-                                       scr_printf("%c %c", 8, 8);
-                               }
-                       } else {
-                               scr_putc(ch);
-                               wbuf[strlen(wbuf) + 1] = 0;
-                               wbuf[strlen(wbuf)] = ch;
-                       }
-               }
-
-               /* if the user hit return, send the line */
-               if (send_complete_line) {
-
-                       if (!strcasecmp(wbuf, "/quit")) {
-                               CtdlIPC_chat_send(ipc, "RCHT exit");
-                               CtdlIPC_chat_recv(ipc, response);       /* don't care about the result */
-                               color(BRIGHT_WHITE);
-                               scr_printf("\rExiting chat mode\n");
-                               scr_flush();
-                               return;
-                       }
-
-                       CtdlIPC_chat_send(ipc, "RCHT send");
-                       CtdlIPC_chat_recv(ipc, response);
-                       if (response[0] == '4') {
-                               CtdlIPC_chat_send(ipc, wbuf);
-                               CtdlIPC_chat_send(ipc, "000");
-                       }
-                       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, "RCHT send");
-                               CtdlIPC_chat_recv(ipc, response);
-                               if (response[0] == '4') {
-                                       CtdlIPC_chat_send(ipc, wbuf);
-                                       CtdlIPC_chat_send(ipc, "000");
-                               }
-                               strcpy(wbuf, "");
-                               send_complete_line = 0;
-                       } else {
-                               wbuf[pos] = 0;
-                               CtdlIPC_chat_send(ipc, "RCHT send");
-                               CtdlIPC_chat_recv(ipc, response);
-                               if (response[0] == '4') {
-                                       CtdlIPC_chat_send(ipc, wbuf);
-                                       CtdlIPC_chat_send(ipc, "000");
-                               }
-                               strcpy(wbuf, &wbuf[pos + 1]);
-                       }
-               }
-
-               /* poll for incoming chat messages */
-               snprintf(buf, sizeof buf, "RCHT poll|%d", seq);
-               CtdlIPC_chat_send(ipc, buf);
-               CtdlIPC_chat_recv(ipc, response);
-       
-               if (response[0] == '1') {
-                       seq = extract_int(&response[4], 0);
-                       extract_token(c_user, &response[4], 2, '|', sizeof c_user);
-                       while (CtdlIPC_chat_recv(ipc, c_text), strcmp(c_text, "000")) {
-                               scr_printf("\r%79s\r", "");
-                               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)) {
-                                       scr_printf("\r%79s\n", "");
-                                       strcpy(last_user, c_user);
-                               }
-                               scr_printf("\r%s\n", buf);
-                               scr_flush();
-                       }
-               }
-               color(BRIGHT_YELLOW);
-               scr_printf("\r> %s", wbuf);
-               scr_flush();
-               strcpy(buf, "");
-       }
-}
-
-
-/*
- * 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/textclient/src/client_passwords.c b/textclient/src/client_passwords.c
deleted file mode 100644 (file)
index 2de2579..0000000
+++ /dev/null
@@ -1,151 +0,0 @@
-/*
- * Functions which allow the client to remember usernames and passwords for
- * various sites.
- *
- * Copyright (c) 1987-2016 by the citadel.org team
- *
- *  This program is open source software; you can redistribute it and/or modify
- *  it under the terms of the GNU General Public License version 3.
- *
- *  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.
- */
-
-#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_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/textclient/src/commands.c b/textclient/src/commands.c
deleted file mode 100644 (file)
index 1af9e8f..0000000
+++ /dev/null
@@ -1,1581 +0,0 @@
-/*
- * This file contains functions which implement parts of the
- * text-mode user interface.
- *
- * Copyright (c) 1987-2017 by the citadel.org team
- *
- * This program is open source software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License version 3.
- *
- * 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.
- */
-
-#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_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 "help.h"
-#include "tuiconfig.h"
-#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];
-
-int next_lazy_cmd = 5;
-
-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 ((retval > 0) && FD_ISSET(0, &rfds)) {
-               set_keepalives(KA_NO);
-               the_character = inkey();
-               set_keepalives(KA_YES);
-       }
-       else {
-               the_character = 0;
-       }
-       return(the_character);
-}
-
-
-
-
-
-/*
- * 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");
-
-               /* 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");
-               fmout(screenwidth, NULL, listing, NULL, 0);
-               free(listing);
-
-       }
-       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) {
-
-       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) {
-               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();
-       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 {
-                       do_keepalive();
-
-                       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;
-               }
-       } 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);
-               }
-       }
-}
-
-
-
-
-/*
- * Function to read a line of text from the terminal.
- *
- * string              Pointer to string buffer
- * lim                 Maximum length
- * noshow              Echo asterisks instead of keystrokes?
- * bs                  Allow backspacing out of the prompt? (returns -1 if this happens)
- *
- * returns: string length
- */
-int ctdl_getline(char *string, int lim, int noshow, int bs)
-{
-       int pos = strlen(string);
-       int ch;
-
-       async_ka_start();
-       if (noshow && !IsEmptyStr(string)) {
-               int num_stars = strlen(string);
-               while (num_stars--) {
-                       scr_putc('*');
-               }
-       }
-       else {
-               scr_printf("%s", string);
-       }
-
-       while(1) {
-               ch = inkey();
-
-               if ((ch == 8)  && (pos > 0)) {                          /* backspace */
-                       --pos;
-                       scr_putc(8); scr_putc(32); scr_putc(8);
-               }
-
-               else if ((ch == 8) && (pos == 0) && (bs)) {             /* backspace out of the prompt */
-                       async_ka_end();
-                       return(-1);
-               }
-
-               else if ((ch == 23) && (pos > 0)) {                     /* Ctrl-W deletes a word */
-                       while ((pos > 0) && !isspace(string[pos])) {
-                               --pos;
-                               scr_putc(8); scr_putc(32); scr_putc(8);
-                       }
-                       while ((pos > 0) && !isspace(string[pos-1])) {
-                               --pos;
-                               scr_putc(8); scr_putc(32); scr_putc(8);
-                       }
-               }
-
-               else if (ch == 10) {                                    /* return */
-                       string[pos] = 0;
-                       scr_printf("\n");
-                       async_ka_end();
-                       return(pos);
-               }
-
-               else if (isprint(ch)) {                                 /* payload characters */
-                       scr_putc((noshow ? '*' : ch));
-                       string[pos] = ch;
-                       ++pos;
-               }
-       }
-}
-
-
-/* 
- * newprompt()         prompt for a string, print the existing value, and
- *                     allow the user to press return to keep it...
- *                     If len is negative, pass the "noshow" flag to ctdl_getline()
- */
-void strprompt(char *prompt, char *str, int len)
-{
-       print_instant();
-       color(DIM_WHITE);
-       scr_printf("%s", prompt);
-       color(DIM_WHITE);
-       scr_printf(": ");
-       color(BRIGHT_CYAN);
-       ctdl_getline(str, abs(len), (len<0), 0);
-       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)
- *                     If len is negative, pass the "noshow" flag to ctdl_getline()
- */
-void newprompt(char *prompt, char *str, int len)
-{
-       str[0] = 0;
-       color(BRIGHT_MAGENTA);
-       scr_printf("%s", prompt);
-       color(DIM_MAGENTA);
-       ctdl_getline(str, abs(len), (len<0), 0);
-       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];
-       struct citcmd *cptr;
-       struct citcmd *lastcmd = NULL;
-       int a, d;
-       int b = 0;
-
-       /* first, set up some defaults for non-required variables */
-
-       strcpy(editor_path, "");
-       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
-
-       /* 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 (!strncasecmp(buf, "editor=", 7)) {
-                       strcpy(editor_path, &buf[7]);
-               }
-
-               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, "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, "status_line=", 12)) {
-                       if (!strncasecmp(&buf[12], "on", 2))
-                               enable_status_line = 1;
-               }
-               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, "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 {
-               scr_printf("-----------------------------------------------------------------------\n");
-               scr_printf("Room cmds:    <K>nown rooms, <G>oto next room, <.G>oto a specific room,\n");
-               scr_printf("              <S>kip this room, <A>bandon this room, <Z>ap this room,\n");
-               scr_printf("              <U>ngoto (move back)\n");
-               scr_printf("Message cmds: <N>ew msgs, <F>orward read, <R>everse read, <O>ld msgs,\n");
-               scr_printf("              <L>ast five msgs, <E>nter a message\n");
-               scr_printf("General cmds: <?> help, <T>erminate, <C>hat, <W>ho is online\n");
-               scr_printf("Misc:         <X> toggle eXpert mode, <D>irectory\n");
-               scr_printf("\n");
-               scr_printf(" (Type .Help SUMMARY for extended commands, <X> to hide this menu)\n");
-               scr_printf("-----------------------------------------------------------------------\n");
-       }
-
-       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));
-
-       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)) {
-                                       argbuf[0] = 0;
-                                       ctdl_getline(argbuf, 64, 0, 0);
-                               } 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 == '?') {
-                       scr_printf("\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));
-                                  scr_printf(" ");
-                                       }
-                                       scr_printf("\n");
-                               }
-                       }
-               sigcaught = 0;
-
-                       scr_printf("\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) {
-                                               scr_printf("%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 text viewer
- */
-void display_help(CtdlIPC *ipc, char *name)
-{
-       int i;
-       int num_helps = sizeof(helpnames) / sizeof(char *) ;
-
-       for (i=0; i<num_helps; ++i) {
-               if (!strcasecmp(name, helpnames[i])) {
-                       fmout(screenwidth, NULL, helptexts[i], NULL, 0);
-                       return;
-               }
-       }
-
-       scr_printf("'%s' not found.  Enter one of:\n", name);
-       for (i=0; i<num_helps; ++i) {
-               scr_printf("  %s\n", helpnames[i]);
-       }
-}
-
-
-/*
- * 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 */
-       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) {
-               scr_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) {
-                       scr_printf("Can't print message: %s!\n",
-                                       strerror(errno));
-                       logoff(NULL, 3);
-               }
-       } else {
-               buffer = text;
-       }
-       e = buffer;
-
-       /* 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");
-                               }
-                               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");
-                               }
-                               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");
-                       }
-                       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");
-       }
-
-       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) {
-               /* 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);
-
-       }
-}
-
-void cls(int colornum)
-{
-       if (enable_color) {
-               printf("\033[4%dm\033[2J\033[H\033[0m",
-                               colornum ? colornum : rc_color_use_bg);
-       }
-}
-
-
-/*
- * 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);
-                               if (rv < 0) {
-                                       scr_printf("failed to read after select: %s", 
-                                                  strerror(errno));
-                                       break;
-                               }
-                       }
-               } 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]=='<') {
-                       scr_printf("%c", buf[i]);
-                       color(BRIGHT_MAGENTA);
-               } else {
-                       if (buf[i]=='>'&& buf[i+1] != '>') {
-                               color(DIM_WHITE);
-                       }
-                       scr_printf("%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/textclient/src/ecrash.c b/textclient/src/ecrash.c
deleted file mode 100644 (file)
index a43f1b6..0000000
+++ /dev/null
@@ -1,516 +0,0 @@
-/*
- * author: David Frascone
- * 
- * eCrash Implementation
- *
- * eCrash will allow you to capture stack traces in the
- * event of a crash, and write those traces to disk, stdout,
- * or any other file handle.
- *
- * modified to integrate closer into citadel by Wilfried Goesgens
- *
- * vim: ts=4
- *
- * This program is open source 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 <stdio.h>
-#include <unistd.h>
-#include <stdlib.h>
-#include <stdarg.h>
-#include <string.h>
-#include <fcntl.h>
-#include <syslog.h>
-#include <sys/types.h>
-#include <sys/stat.h>
-#include <pthread.h>
-#include <libcitadel.h>
-#include "ecrash.h"
-
-#define NIY()  printf("function not implemented yet!\n");
-#ifdef HAVE_BACKTRACE
-#include <execinfo.h>
-static eCrashParameters gbl_params;
-
-static int    gbl_backtraceEntries;
-static void **gbl_backtraceBuffer;
-static char **gbl_backtraceSymbols;
-static int    gbl_backtraceDoneFlag = 0;
-
-static void *stack_frames[50];
-static size_t size, NThread;
-static char **strings;
-
-/* 
- * Private structures for our thread list
- */
-typedef struct thread_list_node{
-       char *threadName;
-       pthread_t thread;
-       int backtraceSignal;
-       sighandler_t oldHandler;
-       struct thread_list_node *Next;
-} ThreadListNode;
-
-static pthread_mutex_t ThreadListMutex = PTHREAD_MUTEX_INITIALIZER;
-static ThreadListNode *ThreadList = NULL;
-
-/*********************************************************************
- *********************************************************************
- **     P  R  I  V  A  T  E      F  U  N  C  T  I  O  N  S
- *********************************************************************
- ********************************************************************/
-
-
-/*!
- * Insert a node into our threadList
- *
- * @param name   Text string indicating our thread
- * @param thread Our Thread Id
- * @param signo  Signal to create backtrace with
- * @param old_handler Our old handler for signo
- *
- * @returns zero on success
- */
-static int addThreadToList(char *name, pthread_t thread,int signo,
-                                          sighandler_t old_handler)
-{
-       ThreadListNode *node;
-
-       node = malloc(sizeof(ThreadListNode));
-       if (!node) return -1;
-
-       DPRINTF(ECRASH_DEBUG_VERBOSE,
-                                       "Adding thread 0x%08x (%s)\n", (unsigned int)thread, name);
-       node->threadName = strdup(name);
-       node->thread = thread;
-       node->backtraceSignal = signo;
-       node->oldHandler = old_handler;
-
-       /* And, add it to the list */
-       pthread_mutex_lock(&ThreadListMutex);
-       node->Next = ThreadList;
-       ThreadList = node;
-       pthread_mutex_unlock(&ThreadListMutex);
-       
-       return 0;
-
-} // addThreadToList
-
-/*!
- * Remove a node from our threadList
- *
- * @param thread Our Thread Id
- *
- * @returns zero on success
- */
-static int removeThreadFromList(pthread_t thread)
-{
-       ThreadListNode *Probe, *Prev=NULL;
-       ThreadListNode *Removed = NULL;
-
-       DPRINTF(ECRASH_DEBUG_VERBOSE,
-                                       "Removing thread 0x%08x from list . . .\n", (unsigned int)thread);
-       pthread_mutex_lock(&ThreadListMutex);
-       for (Probe=ThreadList;Probe != NULL; Probe = Probe->Next) {
-               if (Probe->thread == thread) {
-                       // We found it!  Unlink it and move on!
-                       Removed = Probe;
-                       if (Prev == NULL) { // head of list
-                               ThreadList = Probe->Next;
-                       } else {
-                               // Prev != null, so we need to link around ourselves.
-                               Prev->Next = Probe->Next;
-                       }
-                       Removed->Next = NULL;
-                       break;
-               }
-
-               Prev = Probe;
-       }
-       pthread_mutex_unlock(&ThreadListMutex);
-
-       // Now, if something is in Removed, free it, and return success
-       if (Removed) {
-           DPRINTF(ECRASH_DEBUG_VERBOSE,
-                                               "   Found %s -- removing\n", Removed->threadName);
-               // Reset the signal handler
-               signal(Removed->backtraceSignal, Removed->oldHandler);
-
-               // And free the allocated memory
-               free (Removed->threadName);
-               free (Removed);
-
-               return 0;
-       } else {
-           DPRINTF(ECRASH_DEBUG_VERBOSE,
-                                               "   Not Found\n");
-               return -1; // Not Found
-       }
-} // removeThreadFromList
-
-/*!
- * Print out a line of output to all our destinations
- *
- * One by one, output a line of text to all of our output destinations.
- *
- * Return failure if we fail to output to any of them.
- *
- * @param format   Normal printf style vararg format
- *
- * @returns nothing// bytes written, or error on failure.
- */
-static void outputPrintf(char *format, ...)
-{
-       va_list ap;
-
-       va_start(ap, format);
-
-       vsyslog(LOG_CRIT|LOG_NDELAY|LOG_MAIL, format, ap);
-} // outputPrintf
-
-
-
-/*!
- * Dump our backtrace into a global location
- *
- * This function will dump out our backtrace into our
- * global holding area.
- *
- */
-static void createGlobalBacktrace( void )
-{
-
-       size = backtrace(stack_frames, sizeof(stack_frames) / sizeof(void*));
-       for (NThread = 0; NThread < size; NThread++) 
-       {
-               syslog(LOG_CRIT|LOG_NDELAY|LOG_MAIL, "RAW: %p  ", stack_frames[NThread]);
-       }
-       strings = backtrace_symbols(stack_frames, size);
-       for (NThread = 0; NThread < size; NThread++) {
-               if (strings != NULL) {
-                       syslog(LOG_CRIT|LOG_NDELAY|LOG_MAIL, "RAW: %p  ", strings[NThread]);
-               }
-       }
-} /* createGlobalBacktrace */
-static void outputRawtrace( void )
-{
-
-       size = backtrace(stack_frames, sizeof(stack_frames) / sizeof(void*));
-       for (NThread = 0; NThread < size; NThread++) 
-       {
-               syslog(LOG_CRIT|LOG_NDELAY|LOG_MAIL, "RAW: %p  ", stack_frames[NThread]);
-       }
-} /* createGlobalBacktrace */
-
-/*!
- * Print out (to all the fds, etc), or global backtrace
- */
-static void outputGlobalBacktrace ( void )
-{
-       int i;
-
-       for (i=0; i < gbl_backtraceEntries; i++) {
-               if (gbl_backtraceSymbols != FALSE) {
-                       outputPrintf("*      Frame %02x: %s\n",
-                                    i, gbl_backtraceSymbols[i]);
-               } else {
-                       outputPrintf("*      Frame %02x: %p\n", i,
-                                    gbl_backtraceBuffer[i]);
-               }
-       }
-} // outputGlobalBacktrace
-
-/*!
- * Output our current stack's backtrace
- */
-static void outputBacktrace( void )
-{
-       createGlobalBacktrace();
-       outputGlobalBacktrace();
-} /* outputBacktrace */
-
-static void outputBacktraceThreads( void )
-{
-       ThreadListNode *probe;
-       int i;
-
-       // When we're backtracing, don't worry about the mutex . . hopefully
-       // we're in a safe place.
-
-       for (probe=ThreadList; probe; probe=probe->Next) {
-               gbl_backtraceDoneFlag = 0;
-               pthread_kill(probe->thread, probe->backtraceSignal);
-               for (i=0; i < gbl_params.threadWaitTime; i++) {
-                       if (gbl_backtraceDoneFlag)
-                               break;
-                       sleep(1);
-               }
-               if (gbl_backtraceDoneFlag) {
-                       outputPrintf("*  Backtrace of \"%s\" (0x%08x)\n", 
-                                                probe->threadName, (unsigned int)probe->thread);
-                       outputGlobalBacktrace();
-               } else {
-                       outputPrintf("*  Error: unable to get backtrace of \"%s\" (0x%08x)\n", 
-                                                probe->threadName, (unsigned int)probe->thread);
-               }
-               outputPrintf("*\n");
-       }
-} // outputBacktraceThreads
-
-
-/*!
- * Handle signals (crash signals)
- *
- * This function will catch all crash signals, and will output the
- * crash dump.  
- *
- * It will physically write (and sync) the current thread's information
- * before it attempts to send signals to other threads.
- * 
- * @param signum Signal received.
- */
-static void crash_handler(int signo)
-{
-       outputRawtrace();
-       outputPrintf("*********************************************************\n");
-       outputPrintf("*               eCrash Crash Handler\n");
-       outputPrintf("*********************************************************\n");
-       outputPrintf("*\n");
-       outputPrintf("*  Got a crash! signo=%d\n", signo);
-       outputPrintf("*\n");
-       outputPrintf("*  Offending Thread's Backtrace:\n");
-       outputPrintf("*\n");
-       outputBacktrace();
-       outputPrintf("*\n");
-
-       if (gbl_params.dumpAllThreads != FALSE) {
-               outputBacktraceThreads();
-       }
-
-       outputPrintf("*\n");
-       outputPrintf("*********************************************************\n");
-       outputPrintf("*               eCrash Crash Handler\n");
-       outputPrintf("*********************************************************\n");
-
-       exit(signo);
-} // crash_handler
-
-/*!
- * Handle signals (bt signals)
- *
- * This function shoudl be called to generate a crashdump into our
- * global area.  Once the dump has been completed, this function will
- * return after tickling a global.  Since mutexes are not async
- * signal safe, the main thread, after signaling us to generate our
- * own backtrace, will sleep for a few seconds waiting for us to complete.
- *
- * @param signum Signal received.
- */
-static void bt_handler(int signo)
-{
-       createGlobalBacktrace();
-       gbl_backtraceDoneFlag=1;
-} // bt_handler
-
-/*!
- * Validate a passed-in symbol table
- *
- * For now, just print it out (if verbose), and make sure it's
- * sorted and none of the pointers are zero.
- */
-static int ValidateSymbolTable( void )
-{
-       int i;
-       int rc=0;
-       unsigned long lastAddress =0;
-
-       // Get out of here if the table is empty
-       if (!gbl_params.symbolTable) return 0;
-
-       // Dump it in verbose mode
-       DPRINTF(ECRASH_DEBUG_VERBOSE,
-                                       "Symbol Table Provided with %d symbols\n",
-                                       gbl_params.symbolTable->numSymbols);
-       for (i=0; i < gbl_params.symbolTable->numSymbols; i++){
-               // Dump it in verbose mode
-               DPRINTF(ECRASH_DEBUG_VERBOSE, 
-                               "%-30s %p\n",
-                               gbl_params.symbolTable->symbols[i].function,
-                               gbl_params.symbolTable->symbols[i].address);
-               if (lastAddress >
-                   (unsigned long)gbl_params.symbolTable->symbols[i].address) {
-                       DPRINTF(ECRASH_DEBUG_ERROR,
-                                       "Error: symbol table is not sorted (last=%p, current=%p)\n",
-                                       (void *)lastAddress,
-                                       gbl_params.symbolTable->symbols[i].address);
-                       rc = -1;
-               }
-
-       } // for
-
-       return rc;
-       
-} // ValidateSymbolTable
-
-/*********************************************************************
- *********************************************************************
- **      P  U  B  L  I  C      F  U  N  C  T  I  O  N  S
- *********************************************************************
- ********************************************************************/
-
-/*!
- * Initialize eCrash.
- * 
- * This function must be called before calling any other eCrash
- * functions.  It sets up the global behavior of the system, and
- * registers the calling thread for crash dumps.
- *
- * @param params Our input parameters.  The passed in structure will be copied.
- *
- * @return Zero on success.
- */
-int eCrash_Init(eCrashParameters *params)
-{
-       int sigIndex;
-       int ret = 0;
-#ifdef DO_SIGNALS_RIGHT
-       sigset_t blocked;
-       struct sigaction act;
-#endif
-
-       DPRINTF(ECRASH_DEBUG_VERY_VERBOSE,"Init Starting params = %p\n", params);
-
-       // Allocate our backtrace area
-       gbl_backtraceBuffer = malloc(sizeof(void *) * (params->maxStackDepth+5));
-
-#ifdef DO_SIGNALS_RIGHT
-       sigemptyset(&blocked);
-       act.sa_sigaction = crash_handler;
-       act.sa_mask = blocked;
-       act.sa_flags = SA_SIGINFO;
-#endif
-
-       if (params != NULL) {
-               // Make ourselves a global copy of params.
-               gbl_params = *params;
-               gbl_params.filename = strdup(params->filename);
-
-               // Set our defaults, if they weren't specified
-               if (gbl_params.maxStackDepth == 0 )
-                       gbl_params.maxStackDepth = ECRASH_DEFAULT_STACK_DEPTH;
-
-               if (gbl_params.defaultBacktraceSignal == 0 )
-                       gbl_params.defaultBacktraceSignal = ECRASH_DEFAULT_BACKTRACE_SIGNAL;
-
-               if (gbl_params.threadWaitTime == 0 )
-                       gbl_params.threadWaitTime = ECRASH_DEFAULT_THREAD_WAIT_TIME;
-
-               if (gbl_params.debugLevel == 0 )
-                       gbl_params.debugLevel = ECRASH_DEBUG_DEFAULT;
-
-               // Copy our symbol table
-               if (gbl_params.symbolTable) {
-                   DPRINTF(ECRASH_DEBUG_VERBOSE,
-                                                       "symbolTable @ %p -- %d symbols\n", gbl_params.symbolTable,
-                                               gbl_params.symbolTable->numSymbols);
-                       // Make a copy of our symbol table
-                       gbl_params.symbolTable = malloc(sizeof(eCrashSymbolTable));
-                       memcpy(gbl_params.symbolTable, params->symbolTable,
-                                  sizeof(eCrashSymbolTable));
-
-                       // Now allocate / copy the actual table.
-                       gbl_params.symbolTable->symbols = malloc(sizeof(eCrashSymbol) *
-                                                                    gbl_params.symbolTable->numSymbols);
-                       memcpy(gbl_params.symbolTable->symbols,
-                                  params->symbolTable->symbols,
-                                  sizeof(eCrashSymbol) * gbl_params.symbolTable->numSymbols);
-
-                       ValidateSymbolTable();
-               }
-       
-               // And, finally, register for our signals
-               for (sigIndex=0; gbl_params.signals[sigIndex] != 0; sigIndex++) {
-                       DPRINTF(ECRASH_DEBUG_VERY_VERBOSE,
-                                                       "   Catching signal[%d] %d\n", sigIndex,
-                                       gbl_params.signals[sigIndex]);
-
-                       // I know there's a better way to catch signals with pthreads.
-                       // I'll do it later TODO
-                       signal(gbl_params.signals[sigIndex], crash_handler);
-               }
-       } else {
-               DPRINTF(ECRASH_DEBUG_ERROR, "   Error:  Null Params!\n");
-               ret = -1;
-       }
-       DPRINTF(ECRASH_DEBUG_VERY_VERBOSE, "Init Complete ret=%d\n", ret);
-       return ret;
-} /* eCrash_Init */
-
-/*!
- * UnInitialize eCrash.
- * 
- * This function may be called to de-activate eCrash, release the
- * signal handlers, and free any memory allocated by eCrash.
- *
- * @return Zero on success.
- */
-int eCrash_Uninit( void )
-{
-       NIY();
-
-       return 0;
-} /* eCrash_Uninit */
-
-/*!
- * Register a thread for backtracing on crash.
- * 
- * This function must be called by any thread wanting it's stack
- * dumped in the event of a crash.  The thread my specify what 
- * signal should be used, or the default, SIGUSR1 will be used.
- *
- * @param signo Signal to use to generate dump (default: SIGUSR1)
- *
- * @return Zero on success.
- */
-int eCrash_RegisterThread(char *name, int signo)
-{
-       sighandler_t old_handler;
-
-       // Register for our signal
-       if (signo == 0) {
-               signo = gbl_params.defaultBacktraceSignal;
-       }
-
-       old_handler = signal(signo, bt_handler);
-       return addThreadToList(name, pthread_self(), signo, old_handler);
-
-} /* eCrash_RegisterThread */
-
-/*!
- * Un-register a thread for stack dumps.
- * 
- * This function may be called to un-register any previously 
- * registered thread.
- *
- * @return Zero on success.
- */
-int eCrash_UnregisterThread( void )
-{
-       return removeThreadFromList(pthread_self());
-} /* eCrash_UnregisterThread */
-
-#endif
diff --git a/textclient/src/include/citadel_decls.h b/textclient/src/include/citadel_decls.h
deleted file mode 100644 (file)
index 19895e6..0000000
+++ /dev/null
@@ -1,32 +0,0 @@
-extern char fullname[USERNAME_SIZE];
-extern unsigned room_flags;
-extern char room_name[ROOMNAMELEN];
-extern struct CtdlServInfo serv_info;
-extern char axlevel;
-extern char is_room_aide;
-extern unsigned userflags;
-extern char sigcaught;
-extern char editor_paths[MAX_EDITORS][SIZ];
-extern char printcmd[SIZ];
-extern char imagecmd[SIZ];
-extern char have_xterm;
-extern char rc_username[USERNAME_SIZE];
-extern char rc_password[32];
-extern char rc_floor_mode;
-extern time_t rc_idle_threshold;
-#ifdef HAVE_OPENSSL
-extern char rc_encrypt;                        /* from the citadel.rc file */
-extern char arg_encrypt;               /* from the command line */
-#endif
-#if defined(HAVE_CURSES_H) && !defined(DISABLE_CURSES)
-extern char rc_screen;
-extern char arg_screen;
-#endif
-extern char rc_alt_semantics;
-extern char instant_msgs;
-void ctdl_logoff(char *file, int line, CtdlIPC *ipc, int code);
-#define logoff(ipc, code)      ctdl_logoff(__FILE__, __LINE__, (ipc), (code))
-void formout(CtdlIPC *ipc, char *name);
-void sighandler(int which_sig);
-extern int secure;
-void remove_march(char *roomname, int floornum);
diff --git a/textclient/src/include/citadel_ipc.h b/textclient/src/include/citadel_ipc.h
deleted file mode 100644 (file)
index ab054dd..0000000
+++ /dev/null
@@ -1,514 +0,0 @@
-
-#define        UDS                     "_UDS_"
-#ifdef __CYGWIN__
-#define DEFAULT_HOST           "localhost"
-#else
-#define DEFAULT_HOST           UDS
-#endif
-#define DEFAULT_PORT           "504"
-
-#include <libcitadel.h>
-#include <limits.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
-
-#define CLIENT_VERSION          918
-#define CLIENT_TYPE               0
-//copycat of: /#include "server.h"
-
-#define ROOMNAMELEN    128     /* The size of a roomname string */
-
-// copycat of citadel_dirs.h
-void calc_dirs_n_files(int relh, int home, const char *relhome, char  *ctdldir, int dbg);
-
-//copycat of: /#include "citadel.h"
-/* commands we can send to the stty_ctdl() routine */
-#define SB_NO_INTR      0               /* set to Citadel client mode, i/q disabled */
-#define SB_YES_INTR     1               /* set to Citadel client mode, i/q enabled */
-#define SB_SAVE         2               /* save settings */
-#define SB_RESTORE      3               /* restore settings */
-#define SB_LAST         4               /* redo the last command sent */
-
-#define UGLISTLEN      100     /* you get a ungoto list of this size */
-
-#define USERNAME_SIZE   64      /* The size of a username string */
-#define MAX_EDITORS     5       /* # of external editors supported */
-                                /* MUST be at least 1 */
-
-#define NONCE_SIZE     128     /* Added by <bc> to allow for APOP auth 
-                                * it is BIG becuase there is a hostname
-                                * in the nonce, as per the APOP RFC.
-                                */
-
-/* 
- * S_KEEPALIVE is a watchdog timer.  It is used to send "keep
- * alive" messages to the server to prevent the server from assuming the
- * client is dead and terminating the session.  30 seconds is the recommended
- * value; I can't think of any good reason to change it.
- */
-#define S_KEEPALIVE    30
-
-#define READ_HEADER    2
-#define READ_MSGBODY   3
-
-#define NUM_CONFIGS    72
-
-
-
-/*
- * This struct stores a list of rooms with new messages which the client
- * fetches from the server.  This allows the client to "march" through
- * relevant rooms without having to ask the server each time where to go next.
- */
-typedef struct ExpirePolicy ExpirePolicy;
-struct ExpirePolicy {
-       int expire_mode;
-       int expire_value;
-};
-
-typedef struct march march;
-struct march {
-       struct march *next;
-       char march_name[ROOMNAMELEN];
-       unsigned int march_flags;
-       char march_floor;
-       char march_order;
-       unsigned int march_flags2;
-       int march_access;
-};
-
-/*
- * This is NOT the same 'struct ctdluser' from the server.
- */
-typedef struct ctdluser ctdluser;
-struct ctdluser {                      // User record
-       int version;                    // Cit vers. which created this rec
-       uid_t uid;                      // Associate with a unix account?
-       char password[32];              // password
-       unsigned flags;                 // See US_ flags below
-       long timescalled;               // Total number of logins
-       long posted;                    // Number of messages ever submitted
-       uint8_t axlevel;                // Access level
-       long usernum;                   // User number (never recycled)
-       time_t lastcall;                // Date/time of most recent login
-       int USuserpurge;                // Purge time (in days) for user
-       char fullname[64];              // Display name (primary identifier)
-       char emailaddrs[512];           // Internet email addresses
-};
-
-typedef struct ctdlroom ctdlroom;
-struct ctdlroom {
-       char QRname[ROOMNAMELEN];       /* Name of room                     */
-       char QRpasswd[10];              /* Only valid if it's a private rm  */
-       long QRroomaide;                /* User number of room aide         */
-       long QRhighest;                 /* Highest message NUMBER in room   */
-       time_t QRgen;                   /* Generation number of room        */
-       unsigned QRflags;               /* See flag values below            */
-       char QRdirname[15];             /* Directory name, if applicable    */
-       long QRinfo;                    /* Info file update relative to msgs*/
-       char QRfloor;                   /* Which floor this room is on      */
-       time_t QRmtime;                 /* Date/time of last post           */
-       struct ExpirePolicy QRep;       /* Message expiration policy        */
-       long QRnumber;                  /* Globally unique room number      */
-       char QRorder;                   /* Sort key for room listing order  */
-       unsigned QRflags2;              /* Additional flags                 */
-       int QRdefaultview;              /* How to display the contents      */
-};
-
-
-/////////////
-
-#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];
-       int guest_logins;
-};
-
-/*
- * This class is responsible for the server connection
- */
-typedef struct _CtdlIPC {
-       struct CtdlServInfo ServInfo;   /* The server info for this connection */
-
-#if defined(HAVE_OPENSSL)
-       SSL *ssl;                       /* NULL if not encrypted, non-NULL otherwise */
-#endif
-
-#if defined(HAVE_PTHREAD_H)
-       pthread_mutex_t mutex;          /* Fast mutex, call CtdlIPC_lock() or CtdlIPC_unlock() to use */
-#endif
-
-       int sock;                       /* Socket for connection to server, or -1 if not connected */
-       int isLocal;                    /* 1 if server is local, 0 otherwise or if not connected */
-       int downloading;                /* 1 if a download is open on the server, 0 otherwise */
-       int uploading;                  /* 1 if an upload is open on the server, 0 otherwise */
-       time_t last_command_sent;       /* Time the last command was sent to the server */
-
-       char *Buf;                      /* Our buffer for linebuffered read. */
-       size_t BufSize;
-       size_t BufUsed;
-       char *BufPtr;
-
-       void (*network_status_cb)(int state);   /* Callback for update on whether the IPC is locked */
-       char ip_hostname[256];          /* host name of server to which we are connected (if network) */
-       char ip_address[64];            /* IP address of server to which we are connected (if network) */
-
-} 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 */
-       char RRcurrentview;             /* The user's current view for this room */
-       char RRdefaultview;             /* The default view for this room */
-};
-
-
-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 */
-       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
-};
-extern char file_citadel_rc[PATH_MAX];
-extern char file_citadel_config[PATH_MAX];
-
-/* Shared Diffie-Hellman parameters */
-#define DH_P           "F6E33BD70D475906ABCFB368DA2D1E5611D57DFDAC6A10CD78F406D6952519C74E21FFDCC5A780B9359722AACC8036E4CD24D5F5165EAC9EF226DBD9BBCF678F8DDEE86386F1BC20E291A9854A513A2CA326B411DC92E38F2ED2FEB6A3B792F13DB6550371FDBAC5ECA373BE5050CA4905431CA86088737D52B36C8D13CE9CB4EEF4C910285035E8329DD07551A80B87775676DD1067395CCEE9040C9B8BF998C528B3772B4C590A2CF18C5E58929BFCB538A62638B7437A9C68572D15287E97692B0B1EC5444D9DAB6EB062D20B79CA005EC5035065567AFD1FEF9B251D74747C6065D8C8B6B0862D1EE03F3A244C429EADE0CCC5C3A4196F5CBF5AA01A9026EFB20AA90E462BD64620278F271905EB604F38E6CFAE412EAA6C468E3B58170909BC18662FE2053224F30BE4FDB93BF9FBF969D91A5427A0665AC7BD1C43701B991094C92F7A935063055617142164F02973EB4ED86DD74D2BBAB3CD3B28F7BBD8D9F925B0FE92F7F7D0568D783F9ECE7AF96FB5AF274B586924B64639733A73ACA8F2BD1E970DF51ADDD983F7F6361A2B0DC4F086DE26D8656EC8813DE4B74D6D57BC1E690AC2FF1682B7E16938565A41D1DC64C75ADB81DA4582613FC68C0FDD327D35E2CDF20D009465303773EF3870FBDB0985EE7002A95D7912BBCC78187C29DB046763B7FABFF44EABE820F8ED0D7230AA0AF24F428F82448345BC099B"
-#define DH_G           "2"
-#define DH_L           4096
-#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 CtdlIPCAideGetEmailAddresses(CtdlIPC *ipc, const char *who, char *, char *cret);
-int CtdlIPCAideSetUserParameters(CtdlIPC *ipc, const struct ctdluser *uret, char *cret);
-int CtdlIPCAideSetEmailAddresses(CtdlIPC *ipc, const char *who, char *emailaddrs, char *cret);
-int CtdlIPCRenameUser(CtdlIPC *ipc, char *oldname, char *newname, char *cret);
-int CtdlIPCGetMessageExpirationPolicy(CtdlIPC *ipc, GPEXWhichPolicy 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);
-
-/* ************************************************************************** */
-/*             Stuff below this line is not for public consumption            */
-/* ************************************************************************** */
-
-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);
-
-
-
-void CtdlIPC_lock(CtdlIPC *ipc);
-
-void CtdlIPC_unlock(CtdlIPC *ipc);
-
-char *libcitadelclient_version_string(void);
-
-/* commands we can send to the stty_ctdl() routine */
-#define SB_NO_INTR     0               /* set to Citadel client mode, i/q disabled */
-#define SB_YES_INTR    1               /* set to Citadel client mode, i/q enabled */
-#define SB_SAVE                2               /* save settings */
-#define SB_RESTORE     3               /* restore settings */
-#define SB_LAST                4               /* redo the last command sent */
-
-#define        NEXT_KEY        15
-#define STOP_KEY       3
-
-/* citadel.rc stuff */
-#define RC_NO          0               /* always no */
-#define RC_YES         1               /* always yes */
-#define RC_DEFAULT     2               /* setting depends on user config */
-
-/* keepalives */
-enum {
-       KA_NO,                          /* no keepalives */
-       KA_YES,                         /* full keepalives */
-       KA_HALF                         /* half keepalives */
-};
-
-/* for <;G>oto and <;S>kip commands */
-#define GF_GOTO                0               /* <;G>oto floor mode */
-#define GF_SKIP                1               /* <;S>kip floor mode */
-#define GF_ZAP         2               /* <;Z>ap floor mode */
-
-
-#ifndef AXDEFS
-
-extern char *axdefs[];
-
-extern char *viewdefs[];
-#endif
diff --git a/textclient/src/include/client_chat.h b/textclient/src/include/client_chat.h
deleted file mode 100644 (file)
index 3ea65fc..0000000
+++ /dev/null
@@ -1,18 +0,0 @@
-/*
- * Copyright (c) 1987-2012 by the citadel.org team
- *
- *  This program is open source software; you can redistribute it and/or modify
- *  it under the terms of the GNU General Public License version 3.
- *
- *  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.
- */
-
-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/textclient/src/include/client_passwords.h b/textclient/src/include/client_passwords.h
deleted file mode 100644 (file)
index 2b0d0d6..0000000
+++ /dev/null
@@ -1,29 +0,0 @@
-/* 
- * Copyright (c) 1987-2012 by the citadel.org team
- *
- *  This program is open source software; you can redistribute it and/or modify
- *  it under the terms of the GNU General Public License version 3.
- *
- *  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.
- */
-
-
-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/textclient/src/include/commands.h b/textclient/src/include/commands.h
deleted file mode 100644 (file)
index 9ec98a9..0000000
+++ /dev/null
@@ -1,77 +0,0 @@
-/*
- *
- * Copyright (c) 1987-2012 by the citadel.org team
- *
- *  This program is open source software; you can redistribute it and/or modify
- *  it under the terms of the GNU General Public License version 3.
- *
- *  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.
- */
-
-/*
- * 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, 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/textclient/src/include/ecrash.h b/textclient/src/include/ecrash.h
deleted file mode 100644 (file)
index e9fae96..0000000
+++ /dev/null
@@ -1,185 +0,0 @@
-/*
- * eCrash.h
- * David Frascone
- * 
- * eCrash types and prototypes.
- *
- * vim: ts=4
- */
-
-#ifndef _ECRASH_H_
-#define _ECRASH_H_
-
-#include <stdio.h>
-#include <signal.h>
-
-typedef void (*sighandler_t)(int);
-
-typedef long BOOL;
-
-#define MAX_LINE_LEN 256
-
-#ifndef TRUE
-#define TRUE 1
-#define FALSE 0
-#define BOOL int
-#endif
-
-#define ECRASH_DEFAULT_STACK_DEPTH 10
-#define ECRASH_DEFAULT_BACKTRACE_SIGNAL SIGUSR2
-#define ECRASH_DEFAULT_THREAD_WAIT_TIME 10
-#define ECRASH_MAX_NUM_SIGNALS 30
-
-/** \struct eCrashSymbol
- *  \brief Function Name / Address pair
- *
- *  This is used in conjunction with eCrashSymbolTable to
- *  provide an alternative to backtrace_symbols.
- */
-typedef struct {
-       char          *function;
-       void          *address;
-} eCrashSymbol;
-/** \struct eCrashSymbolTable
- *  \brief Symbol table used to avoid backtrace_symbols()
- *
- *  This structure is used to provide a alternative to 
- *  backtrace_symbols which is not async signal safe.
- *
- *  The symbol table should be sorted by address (it will 
- *  be either binary or linearly searched)
- */
-typedef struct {
-       int           numSymbols;
-       eCrashSymbol *symbols;
-} eCrashSymbolTable;
-
-
-
-#define ECRASH_DEBUG_ENABLE  /* undef to turn off debug */
-
-#ifdef ECRASH_DEBUG_ENABLE
-# define ECRASH_DEBUG_VERY_VERBOSE 1
-# define ECRASH_DEBUG_VERBOSE      2
-# define ECRASH_DEBUG_INFO         3
-# define ECRASH_DEBUG_WARN         4
-# define ECRASH_DEBUG_ERROR        5
-# define ECRASH_DEBUG_OFF          6
-# define ECRASH_DEBUG_DEFAULT    (ECRASH_DEBUG_ERROR)
-# define DPRINTF(level, fmt...) \
-               if (level >= gbl_params.debugLevel) { printf(fmt); fflush(stdout); }
-#else /* ECRASH_DEBUG_ENABLE */
-# define DPRINTF(level, fmt...) 
-#endif /* ECRASH_DEBUG_ENABLE */
-
-
-/** \struct eCrashParameters
- *  \brief eCrash Initialization Parameters
- *  
- *  This structure contains all the global initialization functions
- *  for eCrash.
- * 
- *  @see eCrash_Init
- */
-typedef struct {
-
-
-       /*  OUTPUT OPTIONS */
-               /** Filename to output to, or NULL */
-       char *filename;                 
-               /** FILE * to output to or NULL */
-       FILE *filep;
-               /** fd to output to or -1 */
-       int     fd;
-
-       int debugLevel;
-
-               /** If true, all registered threads will
-                *   be dumped
-         */
-       BOOL dumpAllThreads;
-
-               /** How far to backtrace each stack */
-       unsigned int maxStackDepth;
-
-               /** Default signal to use to tell a thread to drop it's
-                *  stack.
-                */
-       int defaultBacktraceSignal;
-
-               /** How long to wait for a threads
-                *  dump
-                */     
-       unsigned int threadWaitTime;
-
-               /** If this is non-zero, the dangerous function, backtrace_symbols
-                 * will be used.  That function does a malloc(), so is not async
-                 * signal safe, and could cause deadlocks.
-                 */
-       BOOL useBacktraceSymbols; 
-
-               /** To avoid the issues with backtrace_symbols (see comments above)
-                 * the caller can supply it's own symbol table, containing function
-                 * names and start addresses.  This table can be created using 
-                 * a script, or a static table.
-                 *
-                 * If this variable is not null, it will be used, instead of 
-                 * backtrace_symbols, reguardless of the setting
-                 * of useBacktraceSymbols.
-                 */
-       eCrashSymbolTable *symbolTable;
-
-               /** Array of crash signals to catch,
-                *  ending in 0  I would have left it a [] array, but I
-                *  do a static copy, so I needed a set size.
-         */
-       int signals[ECRASH_MAX_NUM_SIGNALS];
-
-} eCrashParameters;
-
-/*!
- * Initialize eCrash.
- * 
- * This function must be called before calling any other eCrash
- * functions.  It sets up the global behavior of the system.
- *
- * @param params Our input parameters.  The passed in structure will be copied.
- *
- * @return Zero on success.
- */
-int eCrash_Init(eCrashParameters *params);
-/*!
- * UnInitialize eCrash.
- * 
- * This function may be called to de-activate eCrash, release the
- * signal handlers, and free any memory allocated by eCrash.
- *
- * @return Zero on success.
- */
-int eCrash_Uninit( void );
-
-/*!
- * Register a thread for backtracing on crash.
- * 
- * This function must be called by any thread wanting it's stack
- * dumped in the event of a crash.  The thread my specify what 
- * signal should be used, or the default, SIGUSR1 will be used.
- *
- * @param name String used to refer to us in crash dumps
- * @param signo Signal to use to generate dump (default: SIGUSR1)
- *
- * @return Zero on success.
- */
-int eCrash_RegisterThread(char *name, int signo);
-
-/*!
- * Un-register a thread for stack dumps.
- * 
- * This function may be called to un-register any previously 
- * registered thread.
- *
- * @return Zero on success.
- */
-int eCrash_UnregisterThread( void );
-
-#endif /* _E_CRASH_H_ */
diff --git a/textclient/src/include/help.h b/textclient/src/include/help.h
deleted file mode 100644 (file)
index c373dc3..0000000
+++ /dev/null
@@ -1,420 +0,0 @@
-//
-// Help files for the text client are now embedded directly into the client.
-// Copyright (c) 1988-2017 by Art Cancro and the citadel.org developers.
-// This program is gleefully made available to the world under the terms of
-// the GNU General Public License v3, with the following three modifications:
-// 1. The name of our favorite operating system is 'Linux,' not 'GNU/Linux.'
-// 2. The name of our favority type of software is 'open source,' not 'free software.'
-// 3. Richard Stallman is a fan of Bernie Sanders and therefore a complete idiot.
-//
-
-char *helpnames[] = {
-       "help",
-       "admin",
-       "floors",
-       "intro",
-       "mail",
-       "network",
-       "software"
-};
-
-char *helptexts[] = {
-
-"                          Citadel Help Menu\n"
-"  \n"
-" ?         Help. (Typing a '?' will give you a menu almost anywhere)\n"
-" A         Abandon this room where you stopped reading, goto next room.\n"
-" C         Chat (multiuser chat, where available)\n"
-" D         Prints directory, if there is one in the current room.\n"
-" E         Enter a message.\n"
-" F         Read all messages in the room, forward.\n"
-" G         Goto next room which has UNREAD messages.\n"
-" H         Help. Same as '?'\n"
-" I         Reads the Information file for this room.\n"
-" K         List of Known rooms.\n"
-" L         Reads the last five messages in the room.\n"
-" N         Reads all new messages in the room.\n"
-" O         Reads all old messages, backwards.\n"
-" P         Page another user (send an instant message)\n"
-" R         Reads all messages in the room, in reverse order.\n"
-" S         Skips current room without making its messages old.\n"
-" T         Terminate (logout)\n"
-" U         Ungoto (returns to the last room you were in)\n"
-" W         Displays who is currently logged in.\n"
-" X         Toggle eXpert mode (menus and help blurbs on/off)\n"
-" Z         Zap (forget) room. (Removes the room from your list)\n"
-" + -       Goto next, previous room on current floor.\n"
-" > <       Goto next, previous floor.\n"
-" *         Enter any locally installed 'doors'.\n"
-"   \n"
-" In addition, there are dot commands. You hit the . (dot), then press the\n"
-"first letter of each word of the command. As you hit the letters, the words\n"
-"pop onto your screen. Exceptions: after you hit .Help or .Goto, the remainder\n"
-"of the command is a help file name or room name.\n"
-"    \n"
-"      *** USE  .<H>elp ?    for additional help *** \n"
-
-,
-
-"The following commands are available only to Admins.  A subset of these\n"
-"commands are available to room aides when they are currently in the room\n"
-"they are room aide for.\n"
-"\n"
-" <.> <A>dmin <K>ill this room   (Delete the current room)\n"
-" <.> <A>dmin <E>dit this room   (Edit the current room's parameters)\n"
-" <.> <A>dmin <W>ho knows room   (List users with access to this room)\n"
-" <.> <A>dmin edit <U>ser        (Change user's access level, password, etc.)\n"
-" <.> <A>dmin <V>alidate new users   (Process new user registrations)\n"
-" <.> <A>dmin enter <I>nfo file      (Create/change this room's banner)\n"
-" <.> <A>dmin <R>oom <I>nvite user   (Grant access to an invitation-only room)\n"
-" <.> <A>dmin <R>oom <K>ick out user (Revoke access to an invitation-only room)\n"
-" <.> <A>dmin <F>ile <D>elete        (Delete a file from the room's directory)\n"
-" <.> <A>dmin <F>ile <S>end over net (Transmit a file to another node)\n"
-" <.> <A>dmin <F>ile <M>ove          (Move a file to another room's directory)\n"
-" <.> <A>dmin <M>essage edit:        (Edit system banners)\n"
-" <.> <A>dmin <P>ost                 (Post a message on behalf of another user)\n"
-" <.> <A>dmin <S>ystem configuration <G>eneral   (Edit global site config)\n"
-" <.> <A>dmin <S>ystem configuration <I>nternet  (Edit Internet domains)\n"
-" <.> <A>dmin <S>ystem configuration check <M>essage base   (Internal checks)\n"
-" <.> <A>dmin <S>ystem configuration <N>etwork   (Netting with other Citadels)\n"
-" <.> <A>dmin <S>ystem configuration network <F>ilter list\n"
-" <.> <A>dmin <T>erminate server <N>ow          (Shut down Citadel server now)\n"
-" <.> <A>dmin <T>erminate server <S>cheduled    (Shut down Citadel server later)\n"
-" <.> <A>dmin mailing <L>ist recipients         (For mailing list rooms)\n"
-" <.> <A>dmin mailing list <D>igest recipients  (For mailing list rooms)\n"
-" <.> <A>dmin <N>etwork room sharing     (Replication with other Citadels)\n"
-" \n"
-" In addition, the <M>ove and <D>elete commands are available at the\n"
-"message prompt.\n"
-
-,
-
-" Floors\n"
-" ------\n"
-"   Floors in Citadel are used to group rooms into related subject areas,\n"
-"just as rooms are used to group messages into manageable groups.\n"
-" \n"
-"   You, as a user, do NOT have to use floors.  If you choose not to, you suffer\n"
-"no penalty; you will not lose access to any rooms.  You may use .EC or ;C (the\n"
-"latter is easier to use) to decide if you want to use floors.  Feel free to\n"
-"experiment.\n"
-" \n"
-"   Floor options are accessed two ways.  First, if you are in floor mode, the\n"
-"<G>oto and <S>kip commands take you to the next room with new messages on the\n"
-"current floor; if there are none left, then the system will automatically\n"
-"switch floors (and let you know) and put you in the first room with new messages\n"
-"on that level.  (Notice that your pattern of basic use of Citadel therefore\n"
-"doesn't really change.)\n"
-" \n"
-"   Direct access to floor options is via the use of a ';' command.\n"
-"The following commands are currently available (more can be\n"
-"added if needed):\n"
-" \n"
-" <;C>onfigure\n"
-" This command toggles your floor mode.\n"
-" \n"
-" <;G>oto FLOORNAME\n"
-" This command causes the system to take you to the named floor.\n"
-" \n"
-" <;K>nown rooms on floors\n"
-" List all rooms on all floors.  This is a very readable way to get a list of\n"
-"all rooms on the system.\n"
-" \n"
-" <;S>kip FLOORNAME\n"
-" This command causes the system to mark all rooms on the current floor as\n"
-"Skipped and takes you to the floor that you specify.\n"
-" \n"
-" <;Z>Forget floor\n"
-"   This command causes you to forget all the rooms currently on the current\n"
-"floor.  Unfortunately, it doesn't apply to rooms that are subsequently created\n"
-"or moved to this floor.  (Sorry.)\n"
-" \n"
-"   Feel free to experiment, you can't hurt yourself or the system with the\n"
-"floor stuff unless you ZForget a floor by accident.\n"
-
-,
-
-"                  New User's Introduction to the site\n"
-"  \n"
-" This is an introduction to the Citadel BBS concept.  It is intended\n"
-"for new users so that they can more easily become acquainted to using\n"
-"Citadel when accessing it in the form of a text-based BBS.  Of\n"
-"course, old users might learn something new each time they read\n"
-"through it.\n"
-" \n"
-" Full help for the BBS commands can be obtained by typing <.H>elp SUMMARY\n"
-"  \n"
-" The CITADEL BBS room concept\n"
-" ----------------------------\n"
-"   The term BBS stands for 'Bulletin Board System'.  The analogy is\n"
-"appropriate: one posts messages so that others may read them.  In\n"
-"order to organize the posts, people can post in different areas of the\n"
-"BBS, called rooms.\n"
-"   In order to post in a certain room, you need to be 'in' that room.\n"
-"Your current prompt is usually the room that you are in, followed the\n"
-"greater-than-sign, such as:\n"
-" \n"
-" Lobby>\n"
-" \n"
-" The easiest way to traverse the room structure is with the 'Goto'\n"
-"command, on the 'G' key.  Pressing 'G' will take you to the next room\n"
-"in the 'march list' (see below) that has new messages in it.  You can\n"
-"read these new messages with the 'N' key.\n"
-" Once you've 'Gotoed' every room in the system (or all of the ones\n"
-"you choose to read) you return to the 'Lobby,' the first and last room\n"
-"in the system.  If new messages get posted to rooms you've already\n"
-"read during your session you will be brought BACK to those rooms so\n"
-"you can read them.\n"
-" \n"
-" March List\n"
-" ----------\n"
-"   All the room names are stored in a march list, which is just a\n"
-"list containing all the room names.  When you <G>oto or <S>kip a\n"
-"room, you are placed in the next room in your march list THAT HAS NEW\n"
-"MESSAGES.  If you have no new messages in any of the rooms on your\n"
-"march list, you will keep going to the Lobby>.  You can choose not to\n"
-"read certain rooms (that don't interest you) by 'Z'apping them.  When\n"
-"you <Z>ap a room, you are merely deleting it from your march list (but\n"
-"not from anybody else's).\n"
-" \n"
-"   You can use the <.G>oto (note the period before the G.  You can also use\n"
-"<J>ump on some systems) to go to any room in the\n"
-"system.  You don't have to type in the complete name of a room to\n"
-"'jump' to it; you merely need to type in enough to distinguish it from\n"
-"the other rooms.  Left-aligned matches carry a heavier weight, so if you\n"
-"typed (for example) '.Goto TECH', you might be taken to a room called\n"
-"'Tech Area>' even if it found a room called 'Biotech/Ethics>' first.\n"
-" \n"
-"  To return to a room you have previously <Z>apped, use the <.G>oto command\n"
-"to enter it, and it will be re-inserted into your march list.  In the case\n"
-"of returning to Zapped rooms, you must type the room name in its entirety.\n"
-"REMEMBER, rooms with no new messages will not show on your\n"
-"march list!  You must <.G>oto to a room with no new messages.\n"
-"Incidentally, you cannot change the order of the rooms on your march list.\n"
-"It's the same for everybody.\n"
-" \n"
-" Special rooms\n"
-" -------------\n"
-"   There are two special rooms on a Citadel that you should know about.\n"
-"  \n"
-"   The first is the Lobby>.  It's used for system announcements and other\n"
-"such administrativia.  You cannot <Z>ap the Lobby>.  Each time you first\n"
-"login, you will be placed in the Lobby>.\n"
-" \n"
-"   The second is Mail>.  In Mail>, when you post a messages, you are\n"
-"prompted to enter the screen name of the person who you want to send the\n"
-"message to.  Only the person who you send the message to can read the\n"
-"message.  NO ONE else can read it, not even the admins.  Mail> is the\n"
-"first room on the march list, and is un-<Z>appable, so you can be sure\n"
-"that the person will get the message.\n"
-"   \n"
-" System admins\n"
-" -------------\n"
-"   These people, along with the room admins, keep the site running smoothly.\n"
-"\n"
-"   Among the many things that admins do are: create rooms, delete\n"
-"rooms, set access levels, invite users, check registration, grant\n"
-"room admin status, and countless other things.  They have access to the\n"
-"Aide> room, a special room only for admins.\n"
-" \n"
-"   If you enter a mail message to 'Sysop' it will be placed in the\n"
-"Aide> room so that the next admin online will read it and deal with it.\n"
-"Admins cannot <Z>ap rooms.  All the rooms are always on each admin's\n"
-"march list.  Admins can read *any* and *every* room, but they *CAN* *NOT*\n"
-"read other users' Mail!\n"
-"  \n"
-" Room admins\n"
-" -----------\n"
-"   Room admins are granted special privileges in specific rooms.\n"
-"They are *NOT* true system admins; their power extends only over the\n"
-"rooms that they control, and they answer to the system admins.\n"
-"  \n"
-"   A room admin's job is to keep the topic of the their room on track,\n"
-"with nudges in the right direction now and then.  A room admin can also\n"
-"move an off topic post to another room, or delete a post, if he/she\n"
-"feels it is necessary. \n"
-"  \n"
-"   Currently, very few rooms have room admins.  Most rooms do not need\n"
-"their own specific room admin.  Being a room admin requires a certain\n"
-"amount of trust, due to the additional privileges granted.\n"
-"  \n"
-" Citadel messages\n"
-" ----------------\n"
-"   Most of the time, the BBS code does not print a lot of messages\n"
-"to your screen.  This is a great benefit once you become familiar\n"
-"with the system, because you do not have endless menus and screens\n"
-"to navigate through.  nevertheless, there are some messages which you\n"
-"might see from time to time.\n"
-"  \n"
-"  'There were messages posted while you were entering.'\n"
-"  \n"
-"   This is also known as 'simulposting.'  When you start entering a \n"
-"message, the system knows where you last left off.  When you save\n"
-"your message, the system checks to see if any messages were entered\n"
-"while you were typing.  This is so that you know whether you need\n"
-"to go back and re-read the last few messages.  This message may appear\n"
-"in any room.\n"
-"   \n"
-" '*** You have new mail'\n"
-"  \n"
-"   This message is essentially the same as the above message, but can\n"
-"appear at any time.  It simply means that new mail has arrived for you while\n"
-"you are logged in.  Simply go to the Mail> room to read it.\n"
-"  \n"
-" Who list\n"
-" --------\n"
-"   The <W>ho command shows you the names of all users who are currently\n"
-"online.  It also shows you the name of the room they are currently in.  If\n"
-"they are in any type of private room, however, the room name will simply\n"
-"display as '<private room>'.  Along with this information is displayed the\n"
-"name of the host computer the user is logged in from.\n"
-
-,
-
-"To send mail on this system, go to the Mail> room (using the command .G Mail)\n"
-"and press E to enter a message.  You will be prompted with:\n"
-" \n"
-" Enter Recipient:\n"
-" \n"
-"   At this point you may enter the name of another user on the system.  Private\n"
-"mail is only readable by the sender and recipient.  There is no need to delete\n"
-"mail after it is read; it will scroll out automatically.\n"
-"  \n"
-"   To send mail to another user on the Citadel network, simply type the\n"
-"user's name, followed by @ and then the system name. For example,\n"
-"  \n"
-" Enter Recipient: Joe Schmoe @ citadrool\n"
-"  \n"
-"  If your account is enabled for Internet mail, you can also send email to\n"
-"anyone on the Internet here.  Simply enter their address at the prompt:\n"
-"  \n"
-" Enter Recipient: ajc@herring.fishnet.com\n"
-
-,
-
-"  Welcome to the network. Messages entered in a network room will appear in\n"
-"that room on all other systems carrying it (The name of the room, however,\n"
-"may be different on other systems).\n"
-
-,
-
-"   Citadel is the premier 'online community' (i.e. Bulletin Board System)\n"
-"software.  It runs on all POSIX-compliant systems, including Linux.  It is an\n"
-"advanced client/server application, and is being actively maintained.\n"
-" \n"
-"   For more info, visit UNCENSORED! BBS at uncensored.citadel.org\n"
-
-,
-
-"Extended commands are available using the period ( . ) key. To use\n"
-"a dot command, press the . key, and then enter the first letter of\n"
-"each word in the command. The words will appear as you enter the keys.\n"
-"You can also backspace over partially entered commands. The following\n"
-"commands are available:\n"
-"\n"
-" <.> <H>elp:    Displays help files.  Type .H followed by a help file\n"
-"                name.  You are now reading <.H>elp SUMMARY\n"
-" \n"
-" <.> <G>oto:    Jumps directly to the room you specify.  You can also\n"
-"                type a partial room name, just enough to make it unique,\n"
-"                and it'll find the room you're looking for.  As with the\n"
-"                regular <G>oto command, messages in the current room will\n"
-"                be marked as read.\n"
-" \n"
-" <.> <S>kip, goto:    This is similar to <.G>oto, except it doesn't mark\n"
-"                      messages in the current room as read.\n"
-" \n"
-" <.> list <Z>apped rooms      Shows all rooms you've <Z>apped (forgotten)\n"
-"\n"
-"  \n"
-" Terminate (logoff) commands:\n"
-" \n"
-" <.> <T>erminate and <Q>uit               Log off and disconnect.\n"
-" <.> <T>erminate and <S>tay online        Log in as a different user.\n"
-" \n"
-" \n"
-" Read commands:\n"
-"\n"
-" <.> <R>ead <N>ew messages                Same as <N>ew\n"
-" <.> <R>ead <O>ld msgs reverse            Same as <O>ld\n"
-" <.> <R>ead <L>ast five msgs              Same as <L>ast5\n"
-" <.> read <L>ast:                         Allows you to specify how many\n"
-"                                          messages you wish to read.\n"
-"\n"
-" <.> <R>ead <U>ser listing:               Lists all users on the system if\n"
-"                                          you just hit enter, otherwise\n"
-"                                          you can specify a partial match\n"
-"\n"
-" <.> <R>ead <T>extfile formatted          File 'download' commands.\n"
-" <.> <R>ead file using <X>modem   \n"
-" <.> <R>ead file using <Y>modem   \n"
-" <.> <R>ead file using <Z>modem   \n"
-" <.> <R>ead <F>ile unformatted   \n"
-" <.> <R>ead <D>irectory   \n"
-"\n"
-" <.> <R>ead <I>nfo file                   Read the room info file.\n"
-" <.> <R>ead <B>io                         Read other users' 'bio' files.\n"
-" <.> <R>ead <C>onfiguration               Display your 'preferences'.\n"
-" <.> <R>ead <S>ystem info                 Display system statistics.\n"
-"\n"
-" \n"
-" Enter commands:\n"
-"\n"
-" <.> <E>nter <M>essage                    Post a message in this room.\n"
-" <.> <E>nter message with <E>ditor        Post using a full-screen editor.\n"
-" <.> <E>nter <A>SCII message              Post 'raw' (use this when 'pasting'\n"
-"                                          a message from your clipboard).\n"
-"\n"
-" <.> <E>nter <P>assword                   Change your password.\n"
-" <.> <E>nter <C>onfiguration              Change your 'preferences'.\n"
-" <.> <E>nter a new <R>oom                 Create a new room.\n"
-" <.> <E>nter re<G>istration               Register (name, address, etc.)\n"
-" <.> <E>nter <B>io                        Enter/change your 'bio' file.\n"
-"\n"
-" <.> <E>nter <T>extfile                   File 'upload' commands.\n"
-" <.> <E>nter file using <X>modem   \n"
-" <.> <E>nter file using <Y>modem   \n"
-" <.> <E>nter file using <Z>modem   \n"
-"  \n"
-"  \n"
-"  Wholist commands:\n"
-" \n"
-" <.> <W>holist <L>ong             Same as <W>ho is online, but displays\n"
-"                                  more detailed information.\n"
-" <.> <W>holist <R>oomname         Masquerade your room name (other users\n"
-"                                  see the name you enter rather than the\n"
-"                                  actual name of the room you're in)\n"
-" <.> <W>holist <H>ostname         Masquerade your host name\n"
-" <.> <E>nter <U>sername           Masquerade your user name (Admins only)\n"
-" <.> <W>holist <S>tealth mode     Enter/exit 'stealth mode' (when in stealth\n"
-"                                  mode you are invisible on the wholist)\n"
-" \n"
-" \n"
-" Floor commands (if using floor mode)\n"
-" ;<C>onfigure floor mode            - turn floor mode on or off\n"
-" ;<G>oto floor:                     - jump to a specific floor\n"
-" ;<K>nown rooms                     - list all rooms on all floors\n"
-" ;<S>kip to floor:                  - skip current floor, jump to another\n"
-" ;<Z>ap floor                       - zap (forget) all rooms on this floor\n"
-" \n"
-" \n"
-" Administrative commands: \n"
-" \n"
-" <.> <A>dmin <K>ill this room   \n"
-" <.> <A>dmin <E>dit this room   \n"
-" <.> <A>dmin <W>ho knows room   \n"
-" <.> <A>dmin edit <U>ser   \n"
-" <.> <A>dmin <V>alidate new users   \n"
-" <.> <A>dmin enter <I>nfo file   \n"
-" <.> <A>dmin <R>oom <I>nvite user  \n"
-" <.> <A>dmin <R>oom <K>ick out user  \n"
-" <.> <A>dmin <F>ile <D>elete  \n"
-" <.> <A>dmin <F>ile <S>end over net  \n"
-" <.> <A>dmin <F>ile <M>ove  \n"
-" <.> <A>dmin <M>essage edit:   \n"
-" <.> <A>dmin <P>ost   \n"
-" <.> <A>dmin <S>ystem configuration   \n"
-" <.> <A>dmin <T>erminate server <N>ow\n"
-" <.> <A>dmin <T>erminate server <S>cheduled\n"
-
-};
diff --git a/textclient/src/include/md5.h b/textclient/src/include/md5.h
deleted file mode 100644 (file)
index 8fc2922..0000000
+++ /dev/null
@@ -1,31 +0,0 @@
-
-#ifndef MD5_H
-#define MD5_H
-
-#include "sysdep.h"
-#include "typesize.h"
-
-struct MD5Context {
-       cit_uint32_t buf[4];
-       cit_uint32_t bits[2];
-       cit_uint32_t in[16];
-};
-
-void MD5Init(struct MD5Context *context);
-void MD5Update(struct MD5Context *context, unsigned char const *buf,
-              unsigned len);
-void MD5Final(unsigned char digest[16], struct MD5Context *context);
-void MD5Transform(cit_uint32_t buf[4], cit_uint32_t const in[16]);
-char *make_apop_string(char *realpass, char *nonce, char *buffer, size_t n);
-
-/*
- * This is needed to make RSAREF happy on some MS-DOS compilers.
- */
-#ifndef HAVE_OPENSSL
-typedef struct MD5Context MD5_CTX;
-#endif
-
-#define MD5_DIGEST_LEN         16
-#define MD5_HEXSTRING_SIZE     33
-
-#endif /* !MD5_H */
diff --git a/textclient/src/include/messages.h b/textclient/src/include/messages.h
deleted file mode 100644 (file)
index e959acd..0000000
+++ /dev/null
@@ -1,35 +0,0 @@
-/*
- * Copyright (c) 1987-2012 by the citadel.org team
- *
- *  This program is open source software; you can redistribute it and/or modify
- *  it under the terms of the GNU General Public License version 3.
- *
- * 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.
- */
-
-
-#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);
-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(FILE *);
-char *load_message_from_file(FILE *src);
-int file_checksum(char *filename);
diff --git a/textclient/src/include/rooms.h b/textclient/src/include/rooms.h
deleted file mode 100644 (file)
index 71edc80..0000000
+++ /dev/null
@@ -1,61 +0,0 @@
-/*
- * Copyright (c) 1987-2012 by the citadel.org team
- *
- *  This program is open source software; you can redistribute it and/or modify
- *  it under the terms of the GNU General Public License version 3.
- *
- *  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.
- */
-
-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);
-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/textclient/src/include/routines.h b/textclient/src/include/routines.h
deleted file mode 100644 (file)
index 499400a..0000000
+++ /dev/null
@@ -1,24 +0,0 @@
-/*
- * Copyright (c) 1987-2012 by the citadel.org team
- *
- *  This program is open source software; you can redistribute it and/or modify
- *  it under the terms of the GNU General Public License version 3.
- *
- *  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.
- */
-
-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/textclient/src/include/routines2.h b/textclient/src/include/routines2.h
deleted file mode 100644 (file)
index 6ee9599..0000000
+++ /dev/null
@@ -1,25 +0,0 @@
-/*
- * Copyright (c) 1987-2012 by the citadel.org team
- *
- *  This program is open source software; you can redistribute it and/or modify
- *  it under the terms of the GNU General Public License version 3.
- *
- *  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.
- */
-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/textclient/src/include/screen.h b/textclient/src/include/screen.h
deleted file mode 100644 (file)
index 8e05796..0000000
+++ /dev/null
@@ -1,31 +0,0 @@
-/*
- * client code may need the ERR define
- *
- * Copyright (c) 1987-2012 by the citadel.org team
- *
- *  This program is open source software; you can redistribute it and/or modify
- *  it under the terms of the GNU General Public License version 3.
- *
- *  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.
- */
-
-void screen_new(void);
-int scr_printf(char *fmt, ...);
-#define SCR_NOBLOCK 0
-#define SCR_BLOCK -1
-int scr_getc(int delay);
-int scr_putc(int c);
-void scr_flush(void);
-int scr_blockread(void);
-RETSIGTYPE scr_winch(int signum);
-void wait_indicator(int state);
-void ctdl_beep(void);
-void scr_wait_indicator(int);
-extern char status_line[];
-extern void check_screen_dims(void);
-
-extern int screenwidth;
-extern int screenheight;
diff --git a/textclient/src/include/tuiconfig.h b/textclient/src/include/tuiconfig.h
deleted file mode 100644 (file)
index 6aa77d6..0000000
+++ /dev/null
@@ -1,19 +0,0 @@
-/*
- * Copyright (c) 1987-2018 by the citadel.org team
- *
- *  This program is open source software; you can redistribute it and/or modify
- *  it under the terms of the GNU General Public License version 3.
- *
- *  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.
- */
-
-void do_internet_configuration(CtdlIPC *ipc);
-void network_config_management(CtdlIPC *ipc, char *entrytype, char *comment);
-void do_pop3client_configuration(CtdlIPC *ipc);
-void do_rssclient_configuration(CtdlIPC *ipc);
-void do_system_configuration(CtdlIPC *ipc);
-extern char editor_path[PATH_MAX];
-extern int enable_status_line;
diff --git a/textclient/src/include/typesize.h b/textclient/src/include/typesize.h
deleted file mode 100644 (file)
index 3732c39..0000000
+++ /dev/null
@@ -1,69 +0,0 @@
-
-/*
-   This file defines typedefs for 8, 16, and 32 bit integers.  They are:
-   cit_int8_t  default 8-bit int
-   cit_int16_t default 16-bit int
-   cit_int32_t default 32-bit int
-   cit_int64_t default 64-bit int (not implemented yet)
-   cit_sint8_t signed 8-bit int
-   cit_sint16_t        signed 16-bit int
-   cit_sint32_t        signed 32-bit int
-   cit_sint64_t        signed 64-bit int (not implemented yet)
-   cit_uint8_t unsigned 8-bit int
-   cit_uint16_t        unsigned 16-bit int
-   cit_uint32_t        unsigned 32-bit int
-   cit_uint64_t        unsigned 64-bit int (not implemented yet)
-
-   The sizes are determined during the configure process; see the 
-   AC_CHECK_SIZEOF macros in configure.in.  In no way do we assume that any
-   given datatype is any particular width, e.g. we don't assume short is two
-   bytes; we check for it specifically.
-
-   This might seem excessively paranoid, but I've seen some WEIRD systems
-   and some bizarre compilers (Domain/OS for instance) in my time.
-*/
-
-#ifndef _CITADEL_UX_TYPESIZE_H
-#define _CITADEL_UX_TYPESIZE_H
-
-/* Include sysdep.h if not already included */
-#ifndef CTDLDIR
-# include "sysdep.h"
-#endif
-
-/* 8-bit - If this fails, your compiler is broken */
-#if SIZEOF_CHAR == 1
-typedef char cit_int8_t;
-typedef signed char cit_sint8_t;
-typedef unsigned char cit_uint8_t;
-#else
-# error Unable to find an 8-bit integer datatype
-#endif
-
-/* 16-bit - If this fails, your compiler is broken */
-#if SIZEOF_SHORT == 2
-typedef short cit_int16_t;
-typedef signed short cit_sint16_t;
-typedef unsigned short cit_uint16_t;
-#elif SIZEOF_INT == 2
-typedef int cit_int16_t;
-typedef signed int cit_sint16_t;
-typedef unsigned int cit_uint16_t;
-#else
-# error Unable to find a 16-bit integer datatype
-#endif
-
-/* 32-bit - If this fails, your compiler is broken */
-#if SIZEOF_INT == 4
-typedef int cit_int32_t;
-typedef signed int cit_sint32_t;
-typedef unsigned int cit_uint32_t;
-#elif SIZEOF_LONG == 4
-typedef long cit_int32_t;
-typedef signed long cit_sint32_t;
-typedef unsigned long cit_uint32_t;
-#else
-# error Unable to find a 32-bit integer datatype
-#endif
-
-#endif /* _CITADEL_UX_TYPESIZE_H */
diff --git a/textclient/src/ipc_c_tcp.c b/textclient/src/ipc_c_tcp.c
deleted file mode 100644 (file)
index 53b2093..0000000
+++ /dev/null
@@ -1,92 +0,0 @@
-/*
- * 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_ipc.h"
-#include "commands.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
-
-/* 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/textclient/src/md5.c b/textclient/src/md5.c
deleted file mode 100644 (file)
index 6e50570..0000000
+++ /dev/null
@@ -1,359 +0,0 @@
-/*
- * This code implements the MD5 message-digest algorithm.
- * The algorithm is due to Ron Rivest.  This code was
- * written by Colin Plumb in 1993, no copyright is claimed.
- * This code is in the public domain; do with it what you wish.
- *
- * Equivalent code is available from RSA Data Security, Inc.
- * This code has been tested against that, and is equivalent,
- * except that you don't need to include two pages of legalese
- * with every copy.
- *
- * To compute the message digest of a chunk of bytes, declare an
- * MD5Context structure, pass it to MD5Init, call MD5Update as
- * needed on buffers full of bytes, and then call MD5Final, which
- * will fill a supplied 16-byte array with the digest.
- */
-
-#include <stdio.h>
-#include <sys/types.h>
-#include <ctype.h>
-#include <string.h>            /* for memcpy() */
-#include "md5.h"
-
-#ifndef HIGHFIRST
-#define byteReverse(buf, len)  /* Nothing */
-#else
-void byteReverse(unsigned char *buf, unsigned longs);
-
-#ifndef ASM_MD5
-/*
- * Note: this code is harmless on little-endian machines.
- */
-void byteReverse(unsigned char *buf, unsigned longs)
-{
-    cit_uint32_t t;
-    do {
-       t = (cit_uint32_t) ((unsigned) buf[3] << 8 | buf[2]) << 16 |
-           ((unsigned) buf[1] << 8 | buf[0]);
-       *(cit_uint32_t *) buf = t;
-       buf += 4;
-    } while (--longs);
-}
-#endif
-#endif
-
-
-
-/*
- * Start MD5 accumulation.  Set bit count to 0 and buffer to mysterious
- * initialization constants.
- */
-void MD5Init(struct MD5Context *ctx)
-{
-    ctx->buf[0] = 0x67452301;
-    ctx->buf[1] = 0xefcdab89;
-    ctx->buf[2] = 0x98badcfe;
-    ctx->buf[3] = 0x10325476;
-
-    ctx->bits[0] = 0;
-    ctx->bits[1] = 0;
-}
-
-/*
- * Update context to reflect the concatenation of another buffer full
- * of bytes.
- */
-void MD5Update(struct MD5Context *ctx, unsigned char const *buf, unsigned len)
-{
-    cit_uint32_t t;
-
-    /* Update bitcount */
-
-    t = ctx->bits[0];
-    if ((ctx->bits[0] = t + ((cit_uint32_t) len << 3)) < t)
-       ctx->bits[1]++;         /* Carry from low to high */
-    ctx->bits[1] += len >> 29;
-
-    t = (t >> 3) & 0x3f;       /* Bytes already in shsInfo->data */
-
-    /* Handle any leading odd-sized chunks */
-
-    if (t) {
-       unsigned char *p = (unsigned char *) ctx->in + t;
-
-       t = 64 - t;
-       if (len < t) {
-           memcpy(p, buf, len);
-           return;
-       }
-       memcpy(p, buf, t);
-       byteReverse(ctx->in, 16);
-       MD5Transform(ctx->buf, ctx->in);
-       buf += t;
-       len -= t;
-    }
-    /* Process data in 64-byte chunks */
-
-    while (len >= 64) {
-       memcpy(ctx->in, buf, 64);
-       byteReverse(ctx->in, 16);
-       MD5Transform(ctx->buf, ctx->in);
-       buf += 64;
-       len -= 64;
-    }
-
-    /* Handle any remaining bytes of data. */
-
-    memcpy(ctx->in, buf, len);
-}
-
-/*
- * Final wrapup - pad to 64-byte boundary with the bit pattern 
- * 1 0* (64-bit count of bits processed, MSB-first)
- */
-void MD5Final(unsigned char digest[16], struct MD5Context *ctx)
-{
-    unsigned count;
-    unsigned char *p;
-
-    /* Compute number of bytes mod 64 */
-    count = (ctx->bits[0] >> 3) & 0x3F;
-
-    /* Set the first char of padding to 0x80.  This is safe since there is
-       always at least one byte free */
-    p = ((unsigned char*)ctx->in) + count;
-    *p++ = 0x80;
-
-    /* Bytes of padding needed to make 64 bytes */
-    count = 64 - 1 - count;
-
-    /* Pad out to 56 mod 64 */
-    if (count < 8) {
-       /* Two lots of padding:  Pad the first block to 64 bytes */
-       memset(p, 0, count);
-       byteReverse(ctx->in, 16);
-       MD5Transform(ctx->buf, ctx->in);
-
-       /* Now fill the next block with 56 bytes */
-       memset(ctx->in, 0, 56);
-    } else {
-       /* Pad block to 56 bytes */
-       memset(p, 0, count - 8);
-    }
-    byteReverse(ctx->in, 14);
-
-    /* Append length in bits and transform */
-    ((cit_uint32_t *) ctx->in)[14] = ctx->bits[0];
-    ((cit_uint32_t *) ctx->in)[15] = ctx->bits[1];
-
-    MD5Transform(ctx->buf, (cit_uint32_t *) ctx->in);
-    byteReverse((unsigned char *) ctx->buf, 4);
-    memcpy(digest, ctx->buf, 16);
-    memset(ctx, 0, sizeof(ctx));       /* In case it's sensitive */
-}
-
-#ifndef ASM_MD5
-
-/* The four core functions - F1 is optimized somewhat */
-
-/* #define F1(x, y, z) (x & y | ~x & z) */
-#define F1(x, y, z) (z ^ (x & (y ^ z)))
-#define F2(x, y, z) F1(z, x, y)
-#define F3(x, y, z) (x ^ y ^ z)
-#define F4(x, y, z) (y ^ (x | ~z))
-
-/* This is the central step in the MD5 algorithm. */
-#ifdef __PUREC__
-#define MD5STEP(f, w, x, y, z, data, s) \
-       ( w += f /*(x, y, z)*/ + data,  w = w<<s | w>>(32-s),  w += x )
-#else
-#define MD5STEP(f, w, x, y, z, data, s) \
-       ( w += f(x, y, z) + data,  w = w<<s | w>>(32-s),  w += x )
-#endif
-
-/*
- * The core of the MD5 algorithm, this alters an existing MD5 hash to
- * reflect the addition of 16 longwords of new data.  MD5Update blocks
- * the data and converts bytes into longwords for this routine.
- */
-void MD5Transform(cit_uint32_t buf[4], cit_uint32_t const in[16])
-{
-    register cit_uint32_t a, b, c, d;
-
-    a = buf[0];
-    b = buf[1];
-    c = buf[2];
-    d = buf[3];
-
-#ifdef __PUREC__       /* PureC Weirdness... (GG) */
-    MD5STEP(F1(b,c,d), a, b, c, d, in[0] + 0xd76aa478L, 7);
-    MD5STEP(F1(a,b,c), d, a, b, c, in[1] + 0xe8c7b756L, 12);
-    MD5STEP(F1(d,a,b), c, d, a, b, in[2] + 0x242070dbL, 17);
-    MD5STEP(F1(c,d,a), b, c, d, a, in[3] + 0xc1bdceeeL, 22);
-    MD5STEP(F1(b,c,d), a, b, c, d, in[4] + 0xf57c0fafL, 7);
-    MD5STEP(F1(a,b,c), d, a, b, c, in[5] + 0x4787c62aL, 12);
-    MD5STEP(F1(d,a,b), c, d, a, b, in[6] + 0xa8304613L, 17);
-    MD5STEP(F1(c,d,a), b, c, d, a, in[7] + 0xfd469501L, 22);
-    MD5STEP(F1(b,c,d), a, b, c, d, in[8] + 0x698098d8L, 7);
-    MD5STEP(F1(a,b,c), d, a, b, c, in[9] + 0x8b44f7afL, 12);
-    MD5STEP(F1(d,a,b), c, d, a, b, in[10] + 0xffff5bb1L, 17);
-    MD5STEP(F1(c,d,a), b, c, d, a, in[11] + 0x895cd7beL, 22);
-    MD5STEP(F1(b,c,d), a, b, c, d, in[12] + 0x6b901122L, 7);
-    MD5STEP(F1(a,b,c), d, a, b, c, in[13] + 0xfd987193L, 12);
-    MD5STEP(F1(d,a,b), c, d, a, b, in[14] + 0xa679438eL, 17);
-    MD5STEP(F1(c,d,a), b, c, d, a, in[15] + 0x49b40821L, 22);
-
-    MD5STEP(F2(b,c,d), a, b, c, d, in[1] + 0xf61e2562L, 5);
-    MD5STEP(F2(a,b,c), d, a, b, c, in[6] + 0xc040b340L, 9);
-    MD5STEP(F2(d,a,b), c, d, a, b, in[11] + 0x265e5a51L, 14);
-    MD5STEP(F2(c,d,a), b, c, d, a, in[0] + 0xe9b6c7aaL, 20);
-    MD5STEP(F2(b,c,d), a, b, c, d, in[5] + 0xd62f105dL, 5);
-    MD5STEP(F2(a,b,c), d, a, b, c, in[10] + 0x02441453L, 9);
-    MD5STEP(F2(d,a,b), c, d, a, b, in[15] + 0xd8a1e681L, 14);
-    MD5STEP(F2(c,d,a), b, c, d, a, in[4] + 0xe7d3fbc8L, 20);
-    MD5STEP(F2(b,c,d), a, b, c, d, in[9] + 0x21e1cde6L, 5);
-    MD5STEP(F2(a,b,c), d, a, b, c, in[14] + 0xc33707d6L, 9);
-    MD5STEP(F2(d,a,b), c, d, a, b, in[3] + 0xf4d50d87L, 14);
-    MD5STEP(F2(c,d,a), b, c, d, a, in[8] + 0x455a14edL, 20);
-    MD5STEP(F2(b,c,d), a, b, c, d, in[13] + 0xa9e3e905L, 5);
-    MD5STEP(F2(a,b,c), d, a, b, c, in[2] + 0xfcefa3f8L, 9);
-    MD5STEP(F2(d,a,b), c, d, a, b, in[7] + 0x676f02d9L, 14);
-    MD5STEP(F2(c,d,a), b, c, d, a, in[12] + 0x8d2a4c8aL, 20);
-
-    MD5STEP(F3(b,c,d), a, b, c, d, in[5] + 0xfffa3942L, 4);
-    MD5STEP(F3(a,b,c), d, a, b, c, in[8] + 0x8771f681L, 11);
-    MD5STEP(F3(d,a,b), c, d, a, b, in[11] + 0x6d9d6122L, 16);
-    MD5STEP(F3(c,d,a), b, c, d, a, in[14] + 0xfde5380cL, 23);
-    MD5STEP(F3(b,c,d), a, b, c, d, in[1] + 0xa4beea44L, 4);
-    MD5STEP(F3(a,b,c), d, a, b, c, in[4] + 0x4bdecfa9L, 11);
-    MD5STEP(F3(d,a,b), c, d, a, b, in[7] + 0xf6bb4b60L, 16);
-    MD5STEP(F3(c,d,a), b, c, d, a, in[10] + 0xbebfbc70L, 23);
-    MD5STEP(F3(b,c,d), a, b, c, d, in[13] + 0x289b7ec6L, 4);
-    MD5STEP(F3(a,b,c), d, a, b, c, in[0] + 0xeaa127faL, 11);
-    MD5STEP(F3(d,a,b), c, d, a, b, in[3] + 0xd4ef3085L, 16);
-    MD5STEP(F3(c,d,a), b, c, d, a, in[6] + 0x04881d05L, 23);
-    MD5STEP(F3(b,c,d), a, b, c, d, in[9] + 0xd9d4d039L, 4);
-    MD5STEP(F3(a,b,c), d, a, b, c, in[12] + 0xe6db99e5L, 11);
-    MD5STEP(F3(d,a,b), c, d, a, b, in[15] + 0x1fa27cf8L, 16);
-    MD5STEP(F3(c,d,a), b, c, d, a, in[2] + 0xc4ac5665L, 23);
-
-    MD5STEP(F4(b,c,d), a, b, c, d, in[0] + 0xf4292244L, 6);
-    MD5STEP(F4(a,b,c), d, a, b, c, in[7] + 0x432aff97L, 10);
-    MD5STEP(F4(d,a,b), c, d, a, b, in[14] + 0xab9423a7L, 15);
-    MD5STEP(F4(c,d,a), b, c, d, a, in[5] + 0xfc93a039L, 21);
-    MD5STEP(F4(b,c,d), a, b, c, d, in[12] + 0x655b59c3L, 6);
-    MD5STEP(F4(a,b,c), d, a, b, c, in[3] + 0x8f0ccc92L, 10);
-    MD5STEP(F4(d,a,b), c, d, a, b, in[10] + 0xffeff47dL, 15);
-    MD5STEP(F4(c,d,a), b, c, d, a, in[1] + 0x85845dd1L, 21);
-    MD5STEP(F4(b,c,d), a, b, c, d, in[8] + 0x6fa87e4fL, 6);
-    MD5STEP(F4(a,b,c), d, a, b, c, in[15] + 0xfe2ce6e0L, 10);
-    MD5STEP(F4(d,a,b), c, d, a, b, in[6] + 0xa3014314L, 15);
-    MD5STEP(F4(c,d,a), b, c, d, a, in[13] + 0x4e0811a1L, 21);
-    MD5STEP(F4(b,c,d), a, b, c, d, in[4] + 0xf7537e82L, 6);
-    MD5STEP(F4(a,b,c), d, a, b, c, in[11] + 0xbd3af235L, 10);
-    MD5STEP(F4(d,a,b), c, d, a, b, in[2] + 0x2ad7d2bbL, 15);
-    MD5STEP(F4(c,d,a), b, c, d, a, in[9] + 0xeb86d391L, 21);
-#else
-    MD5STEP(F1, a, b, c, d, in[0] + 0xd76aa478, 7);
-    MD5STEP(F1, d, a, b, c, in[1] + 0xe8c7b756, 12);
-    MD5STEP(F1, c, d, a, b, in[2] + 0x242070db, 17);
-    MD5STEP(F1, b, c, d, a, in[3] + 0xc1bdceee, 22);
-    MD5STEP(F1, a, b, c, d, in[4] + 0xf57c0faf, 7);
-    MD5STEP(F1, d, a, b, c, in[5] + 0x4787c62a, 12);
-    MD5STEP(F1, c, d, a, b, in[6] + 0xa8304613, 17);
-    MD5STEP(F1, b, c, d, a, in[7] + 0xfd469501, 22);
-    MD5STEP(F1, a, b, c, d, in[8] + 0x698098d8, 7);
-    MD5STEP(F1, d, a, b, c, in[9] + 0x8b44f7af, 12);
-    MD5STEP(F1, c, d, a, b, in[10] + 0xffff5bb1, 17);
-    MD5STEP(F1, b, c, d, a, in[11] + 0x895cd7be, 22);
-    MD5STEP(F1, a, b, c, d, in[12] + 0x6b901122, 7);
-    MD5STEP(F1, d, a, b, c, in[13] + 0xfd987193, 12);
-    MD5STEP(F1, c, d, a, b, in[14] + 0xa679438e, 17);
-    MD5STEP(F1, b, c, d, a, in[15] + 0x49b40821, 22);
-
-    MD5STEP(F2, a, b, c, d, in[1] + 0xf61e2562, 5);
-    MD5STEP(F2, d, a, b, c, in[6] + 0xc040b340, 9);
-    MD5STEP(F2, c, d, a, b, in[11] + 0x265e5a51, 14);
-    MD5STEP(F2, b, c, d, a, in[0] + 0xe9b6c7aa, 20);
-    MD5STEP(F2, a, b, c, d, in[5] + 0xd62f105d, 5);
-    MD5STEP(F2, d, a, b, c, in[10] + 0x02441453, 9);
-    MD5STEP(F2, c, d, a, b, in[15] + 0xd8a1e681, 14);
-    MD5STEP(F2, b, c, d, a, in[4] + 0xe7d3fbc8, 20);
-    MD5STEP(F2, a, b, c, d, in[9] + 0x21e1cde6, 5);
-    MD5STEP(F2, d, a, b, c, in[14] + 0xc33707d6, 9);
-    MD5STEP(F2, c, d, a, b, in[3] + 0xf4d50d87, 14);
-    MD5STEP(F2, b, c, d, a, in[8] + 0x455a14ed, 20);
-    MD5STEP(F2, a, b, c, d, in[13] + 0xa9e3e905, 5);
-    MD5STEP(F2, d, a, b, c, in[2] + 0xfcefa3f8, 9);
-    MD5STEP(F2, c, d, a, b, in[7] + 0x676f02d9, 14);
-    MD5STEP(F2, b, c, d, a, in[12] + 0x8d2a4c8a, 20);
-
-    MD5STEP(F3, a, b, c, d, in[5] + 0xfffa3942, 4);
-    MD5STEP(F3, d, a, b, c, in[8] + 0x8771f681, 11);
-    MD5STEP(F3, c, d, a, b, in[11] + 0x6d9d6122, 16);
-    MD5STEP(F3, b, c, d, a, in[14] + 0xfde5380c, 23);
-    MD5STEP(F3, a, b, c, d, in[1] + 0xa4beea44, 4);
-    MD5STEP(F3, d, a, b, c, in[4] + 0x4bdecfa9, 11);
-    MD5STEP(F3, c, d, a, b, in[7] + 0xf6bb4b60, 16);
-    MD5STEP(F3, b, c, d, a, in[10] + 0xbebfbc70, 23);
-    MD5STEP(F3, a, b, c, d, in[13] + 0x289b7ec6, 4);
-    MD5STEP(F3, d, a, b, c, in[0] + 0xeaa127fa, 11);
-    MD5STEP(F3, c, d, a, b, in[3] + 0xd4ef3085, 16);
-    MD5STEP(F3, b, c, d, a, in[6] + 0x04881d05, 23);
-    MD5STEP(F3, a, b, c, d, in[9] + 0xd9d4d039, 4);
-    MD5STEP(F3, d, a, b, c, in[12] + 0xe6db99e5, 11);
-    MD5STEP(F3, c, d, a, b, in[15] + 0x1fa27cf8, 16);
-    MD5STEP(F3, b, c, d, a, in[2] + 0xc4ac5665, 23);
-
-    MD5STEP(F4, a, b, c, d, in[0] + 0xf4292244, 6);
-    MD5STEP(F4, d, a, b, c, in[7] + 0x432aff97, 10);
-    MD5STEP(F4, c, d, a, b, in[14] + 0xab9423a7, 15);
-    MD5STEP(F4, b, c, d, a, in[5] + 0xfc93a039, 21);
-    MD5STEP(F4, a, b, c, d, in[12] + 0x655b59c3, 6);
-    MD5STEP(F4, d, a, b, c, in[3] + 0x8f0ccc92, 10);
-    MD5STEP(F4, c, d, a, b, in[10] + 0xffeff47d, 15);
-    MD5STEP(F4, b, c, d, a, in[1] + 0x85845dd1, 21);
-    MD5STEP(F4, a, b, c, d, in[8] + 0x6fa87e4f, 6);
-    MD5STEP(F4, d, a, b, c, in[15] + 0xfe2ce6e0, 10);
-    MD5STEP(F4, c, d, a, b, in[6] + 0xa3014314, 15);
-    MD5STEP(F4, b, c, d, a, in[13] + 0x4e0811a1, 21);
-    MD5STEP(F4, a, b, c, d, in[4] + 0xf7537e82, 6);
-    MD5STEP(F4, d, a, b, c, in[11] + 0xbd3af235, 10);
-    MD5STEP(F4, c, d, a, b, in[2] + 0x2ad7d2bb, 15);
-    MD5STEP(F4, b, c, d, a, in[9] + 0xeb86d391, 21);
-#endif
-
-    buf[0] += a;
-    buf[1] += b;
-    buf[2] += c;
-    buf[3] += d;
-}
-
-/*
- * This part was added by Brian Costello <btx@calyx.net>
- * For citadel's APOP auth - makes a lower case (as per APOP RFC)
- * md5 string
- */
-
-char *make_apop_string(char *realpass, char *nonce, char *buffer, size_t n)
-{
-   struct MD5Context ctx;
-   u_char rawdigest[MD5_DIGEST_LEN];
-   int i;
-   
-   MD5Init(&ctx);
-   MD5Update(&ctx, (u_char*)nonce, strlen(nonce));
-   MD5Update(&ctx, (u_char*)realpass, strlen(realpass));
-   MD5Final(rawdigest, &ctx);
-   for (i=0; i<MD5_DIGEST_LEN; i++)
-   {
-      snprintf(&buffer[i*2], n - i*2, "%02X", (unsigned char) (rawdigest[i] & 0xff));
-      buffer[i*2] = tolower(buffer[i*2]);
-      buffer[(i*2)+1] = tolower(buffer[(i*2)+1]);
-   }
-   return buffer;
-}
-
-
-#endif
diff --git a/textclient/src/messages.c b/textclient/src/messages.c
deleted file mode 100644 (file)
index aeb5a4e..0000000
+++ /dev/null
@@ -1,1962 +0,0 @@
-/*
- * Text client functions for reading and writing of messages
- *
- * Copyright (c) 1987-2018 by the citadel.org team
- *
- * This program is open source software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License version 3.
- *
- * 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.
- */
-
-#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_ipc.h"
-#include "citadel_decls.h"
-#include "messages.h"
-#include "commands.h"
-#include "tuiconfig.h"
-#include "rooms.h"
-#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);
-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;
-extern char room_name[];
-extern char tempdir[];
-extern unsigned room_flags;
-extern unsigned room_flags2;
-extern int entmsg_ok;
-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(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));
-       if (rv < 0)
-               scr_printf("failed to set message buffer: %s\n", strerror(errno));
-
-       
-       /* 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);
-       }
-}
-
-
-/*
- * This is a mini RFC2047 decoder.
- * It only handles strings encoded from UTF-8 as Quoted-printable.
- */
-void mini_2047_decode(char *s) {
-       if (!s) return;
-
-       char *qstart = strstr(s, "=?UTF-8?Q?");
-       if (!qstart) return;
-
-       char *qend = strstr(s, "?=");
-       if (!qend) return;
-
-       if (qend <= qstart) return;
-
-       strcpy(qstart, &qstart[10]);
-       qend -= 10;
-
-       char *p = qstart;
-       while (p < qend) {
-
-               if (p[0] == '=') {
-
-                       char ch[3];
-                       ch[0] = p[1];
-                       ch[1] = p[2];
-                       ch[2] = p[3];
-                       int c;
-                       sscanf(ch, "%02x", &c);
-                       p[0] = c;
-                       strcpy(&p[1], &p[3]);
-                       qend -= 2;
-               }
-
-               if (p[0] == '_') {
-                       p[0] = ' ';
-               }
-               
-               ++p;
-       }
-
-       strcpy(qend, &qend[2]);
-}
-
-/*
- * 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[256];
-       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) {
-               scr_printf("*** msg #%ld: %d %s\n", num, r, buf);
-               stty_ctdl(0);
-               free(message->text);
-               free_parts(message->attachments);
-               free(message);
-               return (0);
-       }
-
-       if (dest) {
-               fprintf(dest, "\n ");
-       } else {
-               scr_printf("\n");
-               if (pagin != 2)
-                       scr_printf(" ");
-       }
-       if (pagin == 1 && !dest) {
-               color(BRIGHT_CYAN);
-       }
-
-       /* View headers only */
-       if (pagin == 2) {
-               scr_printf("nhdr=%s\nfrom=%s\ntype=%d\nmsgn=%s\n",
-                               message->nhdr ? "yes" : "no",
-                               message->author, message->type,
-                               message->msgid);
-               if (!IsEmptyStr(message->subject)) {
-                       scr_printf("subj=%s\n", message->subject);
-               }
-               if (!IsEmptyStr(message->email)) {
-                       scr_printf("rfca=%s\n", message->email);
-               }
-               scr_printf("room=%s\ntime=%s",
-                       message->room,
-                       asctime(localtime(&message->time))
-               );
-               if (!IsEmptyStr(message->recipient)) {
-                       scr_printf("rcpt=%s\n", message->recipient);
-               }
-               if (message->attachments) {
-                       struct parts *ptr;
-
-                       for (ptr = message->attachments; ptr; ptr = ptr->next) {
-                               scr_printf("part=%s|%s|%s|%s|%s|%ld\n",
-                                       ptr->name, ptr->filename, ptr->number,
-                                       ptr->disposition, ptr->mimetype,
-                                       ptr->length
-                               );
-                       }
-               }
-               scr_printf("\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 {
-               struct tm thetime;
-               localtime_r(&message->time, &thetime);
-               strftime(now, sizeof now, "%F %R", &thetime);
-               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 (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)) {
-               safestrncpy(reply_to, message->author, sizeof(reply_to));
-       }
-
-       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);
-                               mini_2047_decode(message->subject);
-                               scr_printf("%s\n", message->subject);
-                       }
-               }
-       }
-
-       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 */
-       static char *urlprefixes[] = {
-               "http://",
-               "https://",
-               "ftp://"
-       };
-       int p = 0;
-       num_urls = 0;   /* Start with a clean slate */
-       for (p=0; p<(sizeof urlprefixes / sizeof(char *)); ++p) {
-               searchptr = message->text;
-               while ( (searchptr != NULL) && (num_urls < MAXURLS) ) {
-                       searchptr = strstr(searchptr, urlprefixes[p]);
-                       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, 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);
-                               }
-                       }
-                       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");
-                       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);
-                                       scr_printf("Part ");
-                                       color(BRIGHT_MAGENTA);
-                                       scr_printf("%s", ptr->number);
-                                       color(DIM_WHITE);
-                                       scr_printf(": ");
-                                       color(BRIGHT_CYAN);
-                                       scr_printf("%s", ptr->filename);
-                                       color(DIM_WHITE);
-                                       scr_printf(" (%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;
-
-       newprompt("Enter text to be replaced: ", srch_str, (sizeof(srch_str)-1) );
-       if (IsEmptyStr(srch_str)) {
-               return;
-       }
-
-       newprompt("Enter text to replace it with: ", 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);
-                       if (rv < 0) {
-                               scr_printf("failed to replace string: %s\n", strerror(errno));
-                               break; /*whoopsi! */
-                       }
-                       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[256];
-       char header[SIZ];
-       int cksum = 0;
-
-       if ( (mode == 2) && (IsEmptyStr(editor_path)) ) {
-               scr_printf("*** No editor available; using built-in editor.\n");
-               mode = 0;
-       }
-
-       struct tm thetime;
-       time_t now = time(NULL);
-       localtime_r(&now, &thetime);
-       strftime(datestr, sizeof datestr, "%F %R", &thetime);
-       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);
-       }
-
-       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);
-                       beg = ftell(fp);
-                       if (beg < 0)
-                               scr_printf("failed to get stream position %s\n", 
-                                          strerror(errno));
-                       fclose(fp);
-               } else {
-                       fp = fopen(filename, "w");
-                       if (fp == NULL) {
-                               scr_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) {
-                       scr_printf("*** Error opening temp file!\n    %s: %s\n",
-                               filename, strerror(errno)
-                       );
-                       return(1);
-               }
-               citedit(fp);
-               fclose(fp);
-               goto MECR;
-
-       case 1:
-               fp = fopen(filename, "a");
-               if (fp == NULL) {
-                       scr_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 */
-               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);
-               break;
-       }
-
-MECR:  if (mode >= 2) {
-               if (file_checksum(filename) == cksum) {
-                       scr_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, 0);
-                       beg = ftell(fp);
-                       if (beg < 0)
-                               scr_printf("failed to get stream position %s\n", 
-                                          strerror(errno));
-                       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 (!entmsg_ok) {
-               scr_printf("You may not enter messages in this type of room.\n");
-               return(1);
-       }
-
-       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);
-       }
-
-       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
-               );
-       }
-
-       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) {
-               scr_printf("Entering message.  Word wrap will give you soft linebreaks.  Pressing the\n");
-               scr_printf("'enter' key will give you a hard linebreak and an indent.  Press 'enter' twice\n");
-               scr_printf("when finished.\n");
-       }
-
-       /* Handle the selection of a recipient, if necessary. */
-       strcpy(buf, "");
-       if (need_recp == 1) {
-               if (axlevel >= AxProbU) {
-                       if (is_reply) {
-                               strcpy(buf, reply_to);
-                       } else {
-                               newprompt("Enter recipient: ", buf, SIZ-100);
-                               if (IsEmptyStr(buf)) {
-                                       return (1);
-                               }
-                       }
-               } else
-                       strcpy(buf, "sysop");
-       }
-       strcpy(message.recipient, buf);
-
-       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))) {
-               scr_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);
-       }
-
-       qstart = intprompt("Begin quoting at", 1, 1, line);
-       qend = intprompt("  End quoting at", line, qstart, line);
-
-       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);
-       if (rv != 0) 
-               scr_printf("failed to '%s' by %d\n", cmd, rv);
-       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, 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 */
-
-       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;
-       }
-
-       /* 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:
-               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 */
-                               }
-                               stty_ctdl(SB_RESTORE);
-                               ka_system(printcmd);
-                               stty_ctdl(SB_NO_INTR);
-                               unlink(prtfile);
-                               exit(0);
-                       }
-                       if (f > 0)
-                               do {
-                                       g = wait(NULL);
-                               } while ((g != f) && (g >= 0));
-                       scr_printf("Message printed.\n");
-               }
-               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 {
-                               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");
-               }
-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);
-                                       if (rv != 0)
-                                               scr_printf("failed to save %s Reason %d\n", cmd, rv);
-                               }
-                               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);
-}
-
-
-
-
-/*
- * 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/textclient/src/rooms.c b/textclient/src/rooms.c
deleted file mode 100644 (file)
index 19d321b..0000000
+++ /dev/null
@@ -1,1388 +0,0 @@
-/*
- * Client-side functions which perform room operations
- *
- * Copyright (c) 1987-2018 by the citadel.org team
- *
- * This program is open source software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License version 3.
- *
- * 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.
- */
-
-#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_ipc.h"
-#include "citadel_decls.h"
-#include "rooms.h"
-#include "commands.h"
-#include "messages.h"
-#include "tuiconfig.h"
-#include "screen.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 */
-                       scr_printf("\n");
-                       c = 1;
-               }
-               if (f & QR_MAILBOX) {
-                       color(BRIGHT_YELLOW);
-               } else if (f & QR_PRIVATE) {
-                       color(BRIGHT_RED);
-               } else {
-                       color(DIM_WHITE);
-               }
-               scr_printf("%s", rmname);
-               if (f & QR_DIRECTORY) {
-                       scr_printf("]  ");
-               }
-               else {
-                       scr_printf(">  ");
-               }
-               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) {
-                               scr_printf("\n");
-                               c = 1;
-                       }
-                       scr_printf("%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);
-               scr_printf("\n   Rooms with unread messages:\n");
-               listrms(listing, LISTRMS_NEW_ONLY, -1, 0, NULL);
-               color(BRIGHT_CYAN);
-               scr_printf("\n\n   No unseen messages in:\n");
-               listrms(listing, LISTRMS_OLD_ONLY, -1, 0, NULL);
-               scr_printf("\n");
-       }
-
-       if (kn_floor_mode == 1) {
-               color(BRIGHT_CYAN);
-               scr_printf("\n   Rooms with unread messages on %s:\n",
-                       floorlist[(int) curr_floor]);
-               listrms(listing, LISTRMS_NEW_ONLY, curr_floor, 0, NULL);
-               color(BRIGHT_CYAN);
-               scr_printf("\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);
-               scr_printf("\n\n   Other floors:\n");
-               list_other_floors();
-               scr_printf("\n");
-       }
-
-       if (kn_floor_mode == 2) {
-               for (a = 0; a < 128; ++a) {
-                       if (floorlist[a][0] != 0) {
-                               color(BRIGHT_CYAN);
-                               scr_printf("\n   Rooms on %s:\n",
-                                       floorlist[a]);
-                               listrms(listing, LISTRMS_ALL, a, 0, NULL);
-                               scr_printf("\n");
-                       }
-               }
-       }
-
-       /* Free the room list */
-       while (listing) {
-               mptr = listing->next;
-               free(listing);
-               listing = mptr;
-       };
-
-       color(DIM_WHITE);
-}
-
-
-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);
-       scr_printf("\n   Forgotten public rooms:\n");
-       listrms(listing, LISTRMS_ALL, -1, 0, NULL);
-       scr_printf("\n");
-
-       /* Free the room list */
-       while (listing) {
-               mptr = listing->next;
-               free(listing);
-               listing = mptr;
-       };
-
-       color(DIM_WHITE);
-}
-
-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:
-       scr_printf("\n   Anonymous rooms:\n");
-           listrms(listing, LISTRMS_ALL, -1, QR_ANONONLY|QR_ANONOPT, NULL);
-       scr_printf("\n");
-               break;
-    case 1:
-       scr_printf("\n   Directory rooms:\n");
-           listrms(listing, LISTRMS_ALL, -1, QR_DIRECTORY, NULL);
-       scr_printf("\n");
-               break;
-    case 2:
-       scr_printf("\n   Matching \"%s\" rooms:\n", match);
-           listrms(listing, LISTRMS_ALL, -1, 0, match);
-       scr_printf("\n");
-               break;
-    case 3:
-       scr_printf("\n   Preferred only rooms:\n");
-           listrms(listing, LISTRMS_ALL, -1, QR_PREFONLY, NULL);
-       scr_printf("\n");
-               break;
-    case 4:
-       scr_printf("\n   Private rooms:\n");
-           listrms(listing, LISTRMS_ALL, -1, QR_PRIVATE, NULL);
-       scr_printf("\n");
-               break;
-    case 5:
-       scr_printf("\n   Read only rooms:\n");
-           listrms(listing, LISTRMS_ALL, -1, QR_READONLY, NULL);
-       scr_printf("\n");
-               break;
-       }
-
-       /* Free the room list */
-       while (listing) {
-               mptr = listing->next;
-               free(listing);
-               listing = mptr;
-       };
-
-       color(DIM_WHITE);
-}
-
-
-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 room_admin_name[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 admin */
-       r = CtdlIPCGetRoomAide(ipc, buf);
-       if (r / 100 == 2) {
-               safestrncpy(room_admin_name, buf, sizeof room_admin_name);
-       } else {
-               strcpy(room_admin_name, "");
-       }
-       if (IsEmptyStr(room_admin_name)) {
-               strcpy(room_admin_name, "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->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 admin */
-       do {
-               strprompt("Room admin (or 'none')", room_admin_name, 29);
-               if (!strcasecmp(room_admin_name, "none")) {
-                       strcpy(room_admin_name, "");
-                       break;
-               } else {
-                       r = CtdlIPCQueryUsername(ipc, room_admin_name, 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, room_admin_name, 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) {
-               scr_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) {
-               scr_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);
-
-       stty_ctdl(SB_RESTORE);
-       rv = system(transmit_cmd);
-       if (rv != 0)
-               scr_printf("failed to download '%s': %d\n", transmit_cmd, rv);
-       stty_ctdl(SB_NO_INTR);
-
-       /* 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) {
-               scr_printf("%s\n", buf);
-               return;
-       }
-
-       extract_token(comment, buf, 0, '|', sizeof comment);
-       extract_token(flnm, buf, 1, '|', sizeof flnm);
-       scr_printf("\nDirectory of %s on %s\n", flnm, comment);
-       scr_printf("-----------------------\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)
-                       scr_printf("%-14s %8s %s [%s]\n", flnm, flsz, comment, mimetype);
-               else
-                       scr_printf("%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];
-
-       newprompt("Name of user? ", username, USERNAME_SIZE);
-       if (username[0] == 0)
-               return;
-
-       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];
-
-       newprompt("Name of user? ", username, USERNAME_SIZE);
-       if (username[0] == 0)
-               return;
-
-       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 Admin)\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 room_admin_name[64];
-       int r;                  /* IPC response code */
-       char *text = NULL;
-
-       /* Name of currernt room admin */
-       r = CtdlIPCGetRoomAide(ipc, buf);
-       if (r / 100 == 2)
-               safestrncpy(room_admin_name, buf, sizeof room_admin_name);
-       else
-               strcpy(room_admin_name, "");
-
-       if (!IsEmptyStr(room_admin_name))
-               scr_printf("Room admin is %s.\n\n", room_admin_name);
-
-       r = CtdlIPCRoomInfo(ipc, &text, buf);
-       if (r / 100 != 1)
-               return;
-
-       if (text) {
-               fmout(screenwidth, NULL, text, NULL, 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) {
-               scr_printf("%s\n", buf);
-               return;
-       }
-       while (!IsEmptyStr(listing)) {
-               extract_token(buf, listing, 0, '\n', sizeof buf);
-               remove_token(listing, 0, '\n');
-               if (sigcaught == 0)
-                       scr_printf("%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_path)) {
-               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_path)) {
-               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_path)) {
-               char tmp[SIZ];
-
-               snprintf(tmp, sizeof tmp, "WINDOW_TITLE=%s", desc);
-               putenv(tmp);
-               stty_ctdl(SB_RESTORE);
-               editor_pid = fork();
-               if (editor_pid == 0) {
-                       chmod(temp, 0600);
-                       execlp(editor_path, editor_path, 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_path);
-               stty_ctdl(0);
-       } else {
-               scr_printf("Entering %s.  Press return twice when finished.\n", desc);
-               fp = fopen(temp, "r+");
-               citedit(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;
-
-       load_floorlist(ipc);
-
-       /* Fetch the expire policy (this will silently fail on old servers,
-        * resulting in "default" policy)
-        */
-       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 */
-       CtdlIPCSetMessageExpirationPolicy(ipc, 1, ep, buf);
-       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/textclient/src/routines.c b/textclient/src/routines.c
deleted file mode 100644 (file)
index 5e6e22b..0000000
+++ /dev/null
@@ -1,650 +0,0 @@
-/*
- * Client-side support functions.
- *
- * Copyright (c) 1987-2016 by the citadel.org team
- *
- *  This program is open source software; you can redistribute it and/or modify
- *  it under the terms of the GNU General Public License version 3.
- *
- *  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.
- */
-
-#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
-
-#include <libcitadel.h>
-#include "citadel_ipc.h"
-#include "screen.h"
-
-#define ROUTINES_C
-
-#include "routines.h"
-#include "commands.h"
-#include "citadel_decls.h"
-#include "routines2.h"
-#include "tuiconfig.h"
-
-#define IFAIDE if(axlevel>=AxAideU)
-#define IFNAIDE if (axlevel<AxAideU)
-
-extern unsigned userflags;
-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);
-       }
-}
-
-/*
- * Edit a user's Internet email addresses
- */
-void edit_user_internet_email_addresses(CtdlIPC *ipc, char *who)
-{
-       char buf[SIZ];
-       char *resp = NULL;
-       int num_recs = 0;
-       char **recs = NULL;
-       char ch;
-       int i, j;
-       int quitting = 0;
-       int modified = 0;
-       int r;
-       char emailaddrs[512];
-
-       r = CtdlIPCAideGetEmailAddresses(ipc, who, emailaddrs, buf);
-       if (r / 100 == 1) {
-               while (!IsEmptyStr(emailaddrs)) {
-                       extract_token(buf, emailaddrs, 0, '\n', sizeof buf);
-                       remove_token(emailaddrs, 0, '\n');
-                       if (!IsEmptyStr(buf)) {
-                               ++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);
-                       }
-               }
-       }
-
-       do {
-               scr_printf("\n");
-               color(BRIGHT_WHITE);
-               scr_printf("    Internet email addresses for %s\n", who);
-               color(DIM_WHITE);
-               scr_printf("--- --------------------------------------------------\n");
-               for (i=0; i<num_recs; ++i) {
-                       color(DIM_WHITE);
-                       scr_printf("%3d ", i+1);
-                       color(BRIGHT_CYAN);
-                       scr_printf("%s\n", recs[i]);
-                       color(DIM_WHITE);
-               }
-
-               ch = keymenu("", "<A>dd|<D>elete|<S>ave|<Q>uit");
-               switch(ch) {
-                       case 'a':
-                               newprompt("Enter new email address: ", buf, 50);
-                               striplt(buf);
-                               if (!IsEmptyStr(buf)) {
-                                       // FIXME validate the email address (format, our own domain, addr does not belong to another user)
-                                       ++num_recs;
-                                       if (num_recs == 1) {
-                                               recs = malloc(sizeof(char *));
-                                       }
-                                       else {
-                                               recs = realloc(recs, (sizeof(char *)) * num_recs);
-                                       }
-                                       recs[num_recs-1] = strdup(buf);
-                               }
-                               modified = 1;
-                               break;
-                       case 'd':
-                               i = intprompt("Delete which address", 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) {
-                                       scr_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 = CtdlIPCAideSetEmailAddresses(ipc, who, resp, buf);
-                               if (r / 100 != 4) {
-                                       scr_printf("%s\n", buf);
-                               } else {
-                                       scr_printf("Saved %d addresses.\n", num_recs);
-                                       modified = 0;
-                                       quitting = 1;
-                               }
-                               free(resp);
-                               break;
-                       case 'q':
-                               quitting = !modified || boolprompt("Quit without saving", 0);
-                               break;
-                       default:
-                               break;
-               }
-       } while (!quitting);
-
-       if (recs != NULL) {
-               for (i=0; i<num_recs; ++i) free(recs[i]);
-               free(recs);
-       }
-}
-
-
-/*
- * 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) {                // user edit
-
-               /* val_user(ipc, user->fullname, 0); we used to display the vCard here but there's really no need */
-
-               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 login 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 = AxDeleted;
-       }
-
-       r = CtdlIPCAideSetUserParameters(ipc, user, buf);
-       if (r / 100 != 2) {
-               scr_printf("%s\n", buf);
-       }
-       free(user);
-
-       if (boolprompt("Edit this user's Internet email addresses", 0)) {
-               edit_user_internet_email_addresses(ipc, who);
-       }
-
-}
-
-
-/* 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) {
-
-               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_path)) {
-                       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]);
-       }
-
-}
-
-
-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) {
-               scr_printf("\r%79s\r","");
-       } 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;
-               scr_printf(fmt, dots_printed, "",
-                               curr * 100 / cmax, curr, cmax);
-               scr_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)
-{
-       FILE *who = (FILE *)popen("who am i","r");
-       if (who==NULL) {
-               strcpy(hbuf, ipc->ServInfo.fqdn);
-               return; 
-       }
-       fgets(hbuf, SIZ, who);
-       pclose(who);
-       stripallbut(hbuf, '(' , ')' );
-}
-
-/*
- * 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/textclient/src/routines2.c b/textclient/src/routines2.c
deleted file mode 100644 (file)
index 0862f01..0000000
+++ /dev/null
@@ -1,613 +0,0 @@
-/*
- * More client-side support functions.
- *
- * Copyright (c) 1987-2017 by the citadel.org team
- *
- * This program is open source software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License version 3.
- *
- * 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.
- */
-
-#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_ipc.h"
-#include "citadel_decls.h"
-#include "routines2.h"
-#include "routines.h"
-#include "commands.h"
-#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;
-
-
-/*
- * return proper room prompt character
- */
-int room_prompt(unsigned int qrflags)
-{
-       int a;
-       a = '>';
-       if (qrflags & QR_DIRECTORY)
-               a = ']';
-       return (a);
-}
-
-
-/*
- * Register with name and address
- */
-void entregis(CtdlIPC *ipc)
-{
-
-       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");
-}
-
-/*
- * make all messages old in current room
- */
-void updatels(CtdlIPC *ipc)
-{
-       char buf[256];
-       int r;                          /* IPC response code */
-
-       r = CtdlIPCSetLastRead(ipc, (maxmsgnum > highest_msg_read) ?  maxmsgnum : highest_msg_read, buf);
-
-       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) {
-               scr_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) {
-               scr_printf("%s\n", buf);
-       } else if (r < 0) {
-               scr_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 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);
-               if (rv < 0) {
-                       scr_printf("failed to change into %s Reason %s\nAborting now.\n", 
-                                  tempdir, 
-                                  strerror(errno));
-                       nukedir(tempdir);
-                       return;
-               }
-               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:
-                       stty_ctdl(3);
-                       execlp("rx", "rx", flnm, NULL);
-                       exit(1);
-               case 2:
-                       stty_ctdl(3);
-                       execlp("rb", "rb", NULL);
-                       exit(1);
-               case 3:
-                       stty_ctdl(3);
-                       execlp("rz", "rz", NULL);
-                       exit(1);
-               }
-       } else
-               do {
-                       b = ka_wait(&a);
-               } while ((b != xfer_pid) && (b != (-1)));
-       stty_ctdl(0);
-
-       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);
-                       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);
-}
-
-
-/*
- * Validate new users
- */
-void validate(CtdlIPC *ipc)
-{
-       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;
-
-       stty_ctdl(SB_RESTORE);
-       a = fork();
-       if (a == 0) {
-               signal(SIGINT, SIG_DFL);
-               signal(SIGQUIT, SIG_DFL);
-               execlp(getenv("SHELL"), getenv("SHELL"), NULL);
-               scr_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);
-}
-
-/*
- * <.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);
-       scr_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);
-       scr_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) {
-               scr_printf("%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) {
-                       scr_printf("\n");
-                       pos = 1;
-               }
-               scr_printf("%s, ", buf);
-               pos = pos + strlen(buf) + 2;
-       }
-       scr_printf("%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);
-               scr_printf("\n");
-               if (!strcmp(who, "?"))
-                       list_bio(ipc);
-       } while (!strcmp(who, "?"));
-
-       r = CtdlIPCGetBio(ipc, who, &resp, buf);
-       if (r / 100 != 1) {
-               scr_printf("%s\n", buf);
-               return;
-       }
-       while (!IsEmptyStr(resp)) {
-               extract_token(buf, resp, 0, '\n', sizeof buf);
-               remove_token(resp, 0, '\n');
-               scr_printf("%s\n", buf);
-       }
-       if (resp) free(resp);
-}
diff --git a/textclient/src/screen.c b/textclient/src/screen.c
deleted file mode 100644 (file)
index f4cfc94..0000000
+++ /dev/null
@@ -1,272 +0,0 @@
-/*
- * Screen output handling
- *
- * Copyright (c) 1987-2016 by the citadel.org team
- *
- * This program is open source software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License version 3.
- *
- * 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.
- */
-
-
-#include <stdlib.h>
-#include <unistd.h>
-#include <stdio.h>
-#include <signal.h>
-#include <string.h>
-#include <stdarg.h>
-#include <ctype.h>
-#include <sys/types.h>
-#include <sys/ioctl.h>
-#include "sysdep.h"
-#include <libcitadel.h>
-#include "citadel_ipc.h"
-#include "citadel_decls.h"
-#include "commands.h"
-#include "screen.h"
-
-int enable_status_line = 0;
-char status_line[1024] = "     ";
-
-/* the default paginator prompt will be replaced by the server's prompt when we learn it */
-char *moreprompt = " -- more -- ";
-
-int screenheight = 24;
-int screenwidth = 80;
-int lines_printed = 0;
-int cols_printed = 0;
-
-extern int rc_ansi_color;
-extern int rc_prompt_control;
-void do_keepalive(void);
-
-/*
- * Attempt to discover the screen dimensions. 
- * WARNING: This is sometimes 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 (ioctl(0, TIOCGWINSZ, &xwinsz) == 0) {
-               if (xwinsz.height)
-                       screenheight = (int) xwinsz.height;
-               if (xwinsz.width)
-                       screenwidth = (int) xwinsz.width;
-       }
-#endif
-}
-
-
-/*
- * Initialize the screen
- */
-void screen_new(void)
-{
-       send_ansi_detect();
-       look_for_ansi();
-       cls(0);
-       color(DIM_WHITE);
-}
-
-
-
-/*
- * Beep.
- */
-void ctdl_beep(void) {
-       putc(7, stdout);
-}
-       
-
-
-
-/*
- * scr_printf() outputs to the terminal
- */
-int scr_printf(char *fmt, ...)
-{
-       static char outbuf[4096];       /* static for performance -- not re-entrant -- change if needed */
-       va_list ap;
-       int retval;
-       int i, len;
-
-       va_start(ap, fmt);
-       retval = vsnprintf(outbuf, sizeof outbuf, fmt, ap);
-       va_end(ap);
-
-       len = strlen(outbuf);
-       for (i=0; i<len; ++i) {
-               scr_putc(outbuf[i]);
-       }
-       return retval;
-}
-
-
-/*
- * Read one character from the terminal
- */
-int scr_getc(int delay)
-{
-       unsigned char buf;
-
-       scr_flush();
-
-       buf = '\0';
-       if (!read (0, &buf, 1))
-               logoff(NULL, 3);
-
-       lines_printed = 0;
-       return buf;
-}
-
-/*
- * Issue the paginator prompt (more / hit any key to continue)
- */
-void hit_any_key(void) {
-       int a, b;
-
-       color(COLOR_PUSH);
-       color(DIM_RED);
-       scr_printf("%s\r", moreprompt);
-       color(COLOR_POP);
-       b=inkey();
-       for (a=0; a<screenwidth; ++a) {
-               scr_putc(' ');
-       }
-       scr_printf("\r");
-
-       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;
-}
-
-
-/*
- * Output one character to the terminal
- */
-int scr_putc(int c)
-{
-       /* handle tabs normally */
-       if (c == '\t') {
-               do {
-                       scr_putc(' ');
-               } while ((cols_printed % 8) != 0);
-               return(c);
-       }
-
-       /* Output the character... */
-       if (putc(c, stdout) == EOF) {
-               logoff(NULL, 3);
-       }
-
-       if (c == '\n') {
-               ++lines_printed;
-               cols_printed = 0;
-       }
-       else if (c == '\r') {
-               cols_printed = 0;
-       }
-       else if (isprint(c)) {
-               ++cols_printed;
-               if ((screenwidth > 0) && (cols_printed > screenwidth)) {
-                       ++lines_printed;
-                       cols_printed = 0;
-               }
-       }
-
-       /* How many lines output before stopping for the paginator?
-        * Depends on whether we are displaying a status line.
-        */
-       int height_offset = ( ((enable_color) && (screenwidth > 0) && (enable_status_line)) ? (3) : (2) ) ;
-
-       /* Ok, go check it.  Stop and display the paginator prompt if necessary. */
-       if ((screenheight > 0) && (lines_printed > (screenheight-height_offset))) {
-               lines_printed = 0;
-               hit_any_key();
-               lines_printed = 0;
-               cols_printed = 0;
-       }
-
-       return c;
-}
-
-void scr_flush(void)
-{
-       if ((enable_color) && (screenwidth > 0) && (enable_status_line)) {
-               if (strlen(status_line) < screenwidth) {
-                       memset(&status_line[strlen(status_line)], 32, screenwidth - strlen(status_line));
-               }
-               printf("\033[s\033[1;1H\033[K\033[7m");
-               fwrite(status_line, screenwidth, 1, stdout);
-               printf("\033[27m\033[u");
-       }
-       fflush(stdout);
-}
-
-
-static volatile int caught_sigwinch = 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.
-        */
-       caught_sigwinch = 1;
-       check_screen_dims();
-       signal(SIGWINCH, scr_winch);
-}
-
-
-
-/*
- * Display a 3270-style "wait" indicator at the bottom of the screen
- */
-void scr_wait_indicator(int state) {
-       int sp = (screenwidth - 2);
-
-       if (!enable_status_line) return;
-
-       if (screenwidth > 0) {
-               switch (state) {
-                       default:
-                       case 0:  /* Idle */
-                               status_line[sp] = ' ';
-                               break;
-                       case 1:  /* Waiting */
-                               status_line[sp] = 'X';
-                               break;
-                       case 2:  /* Receiving */
-                               status_line[sp] = '<';
-                               break;
-                       case 3:  /* Sending */
-                               status_line[sp] = '>';
-                               break;
-               }
-               scr_flush();
-       }
-}
-
diff --git a/textclient/src/tuiconfig.c b/textclient/src/tuiconfig.c
deleted file mode 100644 (file)
index 0f6d64a..0000000
+++ /dev/null
@@ -1,972 +0,0 @@
-/*
- * Configuration screens that are part of the text mode client.
- *
- * Copyright (c) 1987-2018 by the citadel.org team
- *
- * This program is open source software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License version 3.
- *
- * 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.
- */
-
-#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_ipc.h"
-#include "citadel_decls.h"
-#include "tuiconfig.h"
-#include "messages.h"
-#include "routines.h"
-#include "commands.h"
-#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;
-char editor_path[PATH_MAX];
-
-
-/* 
- * General system configuration command
- */
-void do_system_configuration(CtdlIPC *ipc)
-{
-
-       /* NUM_CONFIGS is now defined in citadel.h */
-
-       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[67], sizeof sc[67], "%d", (boolprompt("Allow anonymous guest logins", atoi(&sc[67][0]))));
-       snprintf(sc[4], sizeof sc[4], "%d", (boolprompt(
-               "Automatically give room admin 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 admins 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 (* 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("NNTP server port (-1 to disable)", &sc[70][0], 5);
-       strprompt("NNTPS server port (-1 to disable)", &sc[71][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]))));
-
-       /* 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);
-       }
-
-       /* No more Funambol */
-       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) {
-                       scr_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) {
-                       scr_printf("%s\n", buf);
-               }
-               free(resp);
-
-               r = CtdlIPCSetMessageExpirationPolicy(ipc, 2, site_expirepolicy, buf);
-               if (r / 100 != 2) {
-                       scr_printf("%s\n", buf);
-               }
-
-               r = CtdlIPCSetMessageExpirationPolicy(ipc, 3, mbx_expirepolicy, buf);
-               if (r / 100 != 2) {
-                       scr_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> fallback host  (Send mail to this host only if direct delivery fails)\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, 8);
-       switch(sel) {
-               case 1: strcpy(buf, "localhost");
-                       return;
-               case 2: strcpy(buf, "smarthost");
-                       return;
-               case 3: strcpy(buf, "fallbackhost");
-                       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 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');
-
-                       // VILE SLEAZY HACK: replace obsolete "directory" domains with "localhost"
-                       char *d = strstr(buf, "|directory");
-                       if (d != NULL) {
-                               strcpy(d, "|localhost");
-                       }
-
-                       ++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) {
-                                       scr_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) {
-                                       scr_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:
-                               break;
-               }
-       } 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_path)) {
-               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) {
-               scr_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 */
-       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_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);
-       }
-
-       if (file_checksum(filename) == cksum) {
-               scr_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);
-}
-
-
-/*
- * POP3 aggregation client configuration
- */
-void do_pop3client_configuration(CtdlIPC *ipc)
-{
-       char buf[SIZ];
-       int num_recs = 0;
-       char **recs = NULL;
-       char ch;
-       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? "
-                       "\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\n", (extract_int(recs[i], 4) ? "Yes" : "No") );
-               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, "|");
-                               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) {
-                                       scr_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:
-                               break;
-               }
-       } 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 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) {
-                                       scr_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:
-                               break;
-               }
-       } while (!quitting);
-
-       if (recs != NULL) {
-               for (i=0; i<num_recs; ++i) free(recs[i]);
-               free(recs);
-       }
-}
-
-
diff --git a/textclient/textclient.h b/textclient/textclient.h
new file mode 100644 (file)
index 0000000..4b0aa53
--- /dev/null
@@ -0,0 +1,752 @@
+/*
+ * Copyright (c) 1987-2018 by the citadel.org team
+ *
+ *  This program is open source software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License version 3.
+ *
+ *  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.
+ */
+
+#define        UDS                     "_UDS_"
+#define DEFAULT_HOST           "localhost"
+#define DEFAULT_PORT           "504"
+#define CLIENT_VERSION          918
+#define CLIENT_TYPE            0
+
+/* commands we can send to the stty_ctdl() routine */
+#define SB_NO_INTR                     0               // set to Citadel client mode, i/q disabled
+#define SB_YES_INTR            1               // set to Citadel client mode, i/q enabled
+#define SB_SAVE                        2               // save settings
+#define SB_RESTORE             3               // restore settings
+#define SB_LAST                        4               // redo the last command sent
+
+#define UGLISTLEN              100             // you get a ungoto list of this size */
+#define ROOMNAMELEN            128             // The size of a roomname string
+#define USERNAME_SIZE          64              // The size of a username string
+#define MAX_EDITORS            5               // number of external editors supported, must be at least 1
+#define NONCE_SIZE             128             // Added by <bc> to allow for APOP auth 
+
+#define S_KEEPALIVE            30              // How often (in seconds) to send keepalives to the server
+
+#define READ_HEADER            2
+#define READ_MSGBODY           3
+
+#define NUM_CONFIGS            72
+
+#define        NEXT_KEY                15
+#define STOP_KEY               3
+
+/* citadel.rc stuff */
+#define RC_NO                  0               /* always no */
+#define RC_YES                 1               /* always yes */
+#define RC_DEFAULT             2               /* setting depends on user config */
+
+/* keepalives */
+enum {
+       KA_NO,                          /* no keepalives */
+       KA_YES,                         /* full keepalives */
+       KA_HALF                         /* half keepalives */
+};
+
+/* for <;G>oto and <;S>kip commands */
+#define GF_GOTO                0               /* <;G>oto floor mode */
+#define GF_SKIP                1               /* <;S>kip floor mode */
+#define GF_ZAP         2               /* <;Z>ap floor mode */
+
+
+/*
+ * 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 */
+
+typedef void (*sighandler_t)(int);
+
+
+
+#include <stdlib.h>
+#include <unistd.h>
+#include <fcntl.h>
+#include <stdio.h>
+#include <ctype.h>
+#include <string.h>
+#include <time.h>
+#include <limits.h>
+#include <sys/types.h>
+#include <sys/wait.h>
+#include <sys/stat.h>
+#include <sys/ioctl.h>
+#include <termios.h>
+//     #include <sgtty.h>      not needed if we have termios.h
+#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>
+#include <signal.h>
+#include <pwd.h>
+#include <stdarg.h>
+#include <errno.h>
+#include <signal.h>
+#include <malloc.h>
+#include <stdarg.h>
+#include <sys/select.h>
+#include <dirent.h>
+#include <libcitadel.h>
+
+#include <limits.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
+
+
+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];
+       int guest_logins;
+};
+
+
+/*
+ * This class is responsible for the server connection
+ */
+typedef struct _CtdlIPC {
+       struct CtdlServInfo ServInfo;   /* The server info for this connection */
+#if defined(HAVE_OPENSSL)
+       SSL *ssl;                       /* NULL if not encrypted, non-NULL otherwise */
+#endif
+       int sock;                       /* Socket for connection to server, or -1 if not connected */
+       int isLocal;                    /* 1 if server is local, 0 otherwise or if not connected */
+       int downloading;                /* 1 if a download is open on the server, 0 otherwise */
+       int uploading;                  /* 1 if an upload is open on the server, 0 otherwise */
+       time_t last_command_sent;       /* Time the last command was sent to the server */
+       char *Buf;                      /* Our buffer for linebuffered read. */
+       size_t BufSize;
+       size_t BufUsed;
+       char *BufPtr;
+       void (*network_status_cb)(int state);   /* Callback for update on whether the IPC is locked */
+       char ip_hostname[256];          /* host name of server to which we are connected (if network) */
+       char ip_address[64];            /* IP address of server to which we are connected (if network) */
+} CtdlIPC;
+
+extern char *axdefs[];
+extern char *viewdefs[];
+extern char fullname[USERNAME_SIZE];
+extern unsigned room_flags;
+extern char room_name[ROOMNAMELEN];
+extern struct CtdlServInfo serv_info;
+extern char axlevel;
+extern char is_room_aide;
+extern unsigned userflags;
+extern char sigcaught;
+extern char editor_paths[MAX_EDITORS][SIZ];
+extern char printcmd[SIZ];
+extern char imagecmd[SIZ];
+extern char have_xterm;
+extern char rc_username[USERNAME_SIZE];
+extern char rc_password[32];
+extern char rc_floor_mode;
+extern time_t rc_idle_threshold;
+#ifdef HAVE_OPENSSL
+extern char rc_encrypt;                        /* from the citadel.rc file */
+extern char arg_encrypt;               /* from the command line */
+#endif
+#if defined(HAVE_CURSES_H) && !defined(DISABLE_CURSES)
+extern char rc_screen;
+extern char arg_screen;
+#endif
+extern char rc_alt_semantics;
+extern char instant_msgs;
+void ctdl_logoff(char *file, int line, CtdlIPC *ipc, int code);
+#define logoff(ipc, code)      ctdl_logoff(__FILE__, __LINE__, (ipc), (code))
+void formout(CtdlIPC *ipc, char *name);
+void sighandler(int which_sig);
+extern int secure;
+void remove_march(char *roomname, int floornum);
+void calc_dirs_n_files(int relh, int home, const char *relhome, char  *ctdldir, int dbg);
+
+/*
+ * This struct stores a list of rooms with new messages which the client
+ * fetches from the server.  This allows the client to "march" through
+ * relevant rooms without having to ask the server each time where to go next.
+ */
+typedef struct ExpirePolicy ExpirePolicy;
+struct ExpirePolicy {
+       int expire_mode;
+       int expire_value;
+};
+
+typedef struct march march;
+struct march {
+       struct march *next;
+       char march_name[ROOMNAMELEN];
+       unsigned int march_flags;
+       char march_floor;
+       char march_order;
+       unsigned int march_flags2;
+       int march_access;
+};
+
+/*
+ * This is NOT the same 'struct ctdluser' from the server.
+ */
+typedef struct ctdluser ctdluser;
+struct ctdluser {                      // User record
+       int version;                    // Cit vers. which created this rec
+       uid_t uid;                      // Associate with a unix account?
+       char password[32];              // password
+       unsigned flags;                 // See US_ flags below
+       long timescalled;               // Total number of logins
+       long posted;                    // Number of messages ever submitted
+       uint8_t axlevel;                // Access level
+       long usernum;                   // User number (never recycled)
+       time_t lastcall;                // Date/time of most recent login
+       int USuserpurge;                // Purge time (in days) for user
+       char fullname[64];              // Display name (primary identifier)
+       char emailaddrs[512];           // Internet email addresses
+};
+
+typedef struct ctdlroom ctdlroom;
+struct ctdlroom {
+       char QRname[ROOMNAMELEN];       /* Name of room                     */
+       char QRpasswd[10];              /* Only valid if it's a private rm  */
+       long QRroomaide;                /* User number of room aide         */
+       long QRhighest;                 /* Highest message NUMBER in room   */
+       time_t QRgen;                   /* Generation number of room        */
+       unsigned QRflags;               /* See flag values below            */
+       char QRdirname[15];             /* Directory name, if applicable    */
+       long QRinfo;                    /* Info file update relative to msgs*/
+       char QRfloor;                   /* Which floor this room is on      */
+       time_t QRmtime;                 /* Date/time of last post           */
+       struct ExpirePolicy QRep;       /* Message expiration policy        */
+       long QRnumber;                  /* Globally unique room number      */
+       char QRorder;                   /* Sort key for room listing order  */
+       unsigned QRflags2;              /* Additional flags                 */
+       int QRdefaultview;              /* How to display the contents      */
+};
+
+
+/* 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 */
+       char RRcurrentview;             /* The user's current view for this room */
+       char RRdefaultview;             /* The default view for this room */
+};
+
+
+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 */
+       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
+};
+extern char file_citadel_rc[PATH_MAX];
+extern char file_citadel_config[PATH_MAX];
+
+/* Shared Diffie-Hellman parameters */
+#define DH_P           "F6E33BD70D475906ABCFB368DA2D1E5611D57DFDAC6A10CD78F406D6952519C74E21FFDCC5A780B9359722AACC8036E4CD24D5F5165EAC9EF226DBD9BBCF678F8DDEE86386F1BC20E291A9854A513A2CA326B411DC92E38F2ED2FEB6A3B792F13DB6550371FDBAC5ECA373BE5050CA4905431CA86088737D52B36C8D13CE9CB4EEF4C910285035E8329DD07551A80B87775676DD1067395CCEE9040C9B8BF998C528B3772B4C590A2CF18C5E58929BFCB538A62638B7437A9C68572D15287E97692B0B1EC5444D9DAB6EB062D20B79CA005EC5035065567AFD1FEF9B251D74747C6065D8C8B6B0862D1EE03F3A244C429EADE0CCC5C3A4196F5CBF5AA01A9026EFB20AA90E462BD64620278F271905EB604F38E6CFAE412EAA6C468E3B58170909BC18662FE2053224F30BE4FDB93BF9FBF969D91A5427A0665AC7BD1C43701B991094C92F7A935063055617142164F02973EB4ED86DD74D2BBAB3CD3B28F7BBD8D9F925B0FE92F7F7D0568D783F9ECE7AF96FB5AF274B586924B64639733A73ACA8F2BD1E970DF51ADDD983F7F6361A2B0DC4F086DE26D8656EC8813DE4B74D6D57BC1E690AC2FF1682B7E16938565A41D1DC64C75ADB81DA4582613FC68C0FDD327D35E2CDF20D009465303773EF3870FBDB0985EE7002A95D7912BBCC78187C29DB046763B7FABFF44EABE820F8ED0D7230AA0AF24F428F82448345BC099B"
+#define DH_G           "2"
+#define DH_L           4096
+#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 CtdlIPCAideGetEmailAddresses(CtdlIPC *ipc, const char *who, char *, char *cret);
+int CtdlIPCAideSetUserParameters(CtdlIPC *ipc, const struct ctdluser *uret, char *cret);
+int CtdlIPCAideSetEmailAddresses(CtdlIPC *ipc, const char *who, char *emailaddrs, char *cret);
+int CtdlIPCRenameUser(CtdlIPC *ipc, char *oldname, char *newname, char *cret);
+int CtdlIPCGetMessageExpirationPolicy(CtdlIPC *ipc, GPEXWhichPolicy 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);
+
+/* ************************************************************************** */
+/*             Stuff below this line is not for public consumption            */
+/* ************************************************************************** */
+
+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);
+void CtdlIPC_lock(CtdlIPC *ipc);
+void CtdlIPC_unlock(CtdlIPC *ipc);
+char *libcitadelclient_version_string(void);
+void chatmode(CtdlIPC *ipc);
+void page_user(CtdlIPC *ipc);
+void quiet_mode(CtdlIPC *ipc);
+void stealth_mode(CtdlIPC *ipc);
+extern char last_paged[];
+
+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);
+
+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, 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;
+
+#ifndef MD5_H
+#define MD5_H
+
+struct MD5Context {
+       uint32_t buf[4];
+       uint32_t bits[2];
+       uint32_t in[16];
+};
+
+void MD5Init(struct MD5Context *context);
+void MD5Update(struct MD5Context *context, unsigned char const *buf,
+              unsigned len);
+void MD5Final(unsigned char digest[16], struct MD5Context *context);
+void MD5Transform(uint32_t buf[4], uint32_t const in[16]);
+char *make_apop_string(char *realpass, char *nonce, char *buffer, size_t n);
+
+/*
+ * This is needed to make RSAREF happy on some MS-DOS compilers.
+ */
+#ifndef HAVE_OPENSSL
+typedef struct MD5Context MD5_CTX;
+#endif
+
+#define MD5_DIGEST_LEN         16
+#define MD5_HEXSTRING_SIZE     33
+
+#endif /* !MD5_H */
+
+
+#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);
+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(FILE *);
+char *load_message_from_file(FILE *src);
+int file_checksum(char *filename);
+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);
+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
+};
+
+
+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);
+
+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);
+
+void screen_new(void);
+int scr_printf(char *fmt, ...);
+#define SCR_NOBLOCK 0
+#define SCR_BLOCK -1
+int scr_getc(int delay);
+int scr_putc(int c);
+void scr_flush(void);
+int scr_blockread(void);
+sighandler_t scr_winch(int signum);
+void wait_indicator(int state);
+void ctdl_beep(void);
+void scr_wait_indicator(int);
+extern char status_line[];
+extern void check_screen_dims(void);
+extern int screenwidth;
+extern int screenheight;
+void do_internet_configuration(CtdlIPC *ipc);
+void network_config_management(CtdlIPC *ipc, char *entrytype, char *comment);
+void do_pop3client_configuration(CtdlIPC *ipc);
+void do_rssclient_configuration(CtdlIPC *ipc);
+void do_system_configuration(CtdlIPC *ipc);
+extern char editor_path[PATH_MAX];
+extern int enable_status_line;
diff --git a/textclient/tuiconfig.c b/textclient/tuiconfig.c
new file mode 100644 (file)
index 0000000..efc7aff
--- /dev/null
@@ -0,0 +1,934 @@
+/*
+ * Configuration screens that are part of the text mode client.
+ *
+ * Copyright (c) 1987-2018 by the citadel.org team
+ *
+ * This program is open source software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 3.
+ *
+ * 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.
+ */
+
+#include "textclient.h"
+
+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;
+char editor_path[PATH_MAX];
+
+
+/* 
+ * General system configuration command
+ */
+void do_system_configuration(CtdlIPC *ipc)
+{
+
+       /* NUM_CONFIGS is now defined in citadel.h */
+
+       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[67], sizeof sc[67], "%d", (boolprompt("Allow anonymous guest logins", atoi(&sc[67][0]))));
+       snprintf(sc[4], sizeof sc[4], "%d", (boolprompt(
+               "Automatically give room admin 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 admins 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 (* 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("NNTP server port (-1 to disable)", &sc[70][0], 5);
+       strprompt("NNTPS server port (-1 to disable)", &sc[71][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]))));
+
+       /* 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);
+       }
+
+       /* No more Funambol */
+       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) {
+                       scr_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) {
+                       scr_printf("%s\n", buf);
+               }
+               free(resp);
+
+               r = CtdlIPCSetMessageExpirationPolicy(ipc, 2, site_expirepolicy, buf);
+               if (r / 100 != 2) {
+                       scr_printf("%s\n", buf);
+               }
+
+               r = CtdlIPCSetMessageExpirationPolicy(ipc, 3, mbx_expirepolicy, buf);
+               if (r / 100 != 2) {
+                       scr_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> fallback host  (Send mail to this host only if direct delivery fails)\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, 8);
+       switch(sel) {
+               case 1: strcpy(buf, "localhost");
+                       return;
+               case 2: strcpy(buf, "smarthost");
+                       return;
+               case 3: strcpy(buf, "fallbackhost");
+                       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 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');
+
+                       // VILE SLEAZY HACK: replace obsolete "directory" domains with "localhost"
+                       char *d = strstr(buf, "|directory");
+                       if (d != NULL) {
+                               strcpy(d, "|localhost");
+                       }
+
+                       ++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) {
+                                       scr_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) {
+                                       scr_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:
+                               break;
+               }
+       } 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_path)) {
+               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) {
+               scr_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 */
+       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_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);
+       }
+
+       if (file_checksum(filename) == cksum) {
+               scr_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);
+}
+
+
+/*
+ * POP3 aggregation client configuration
+ */
+void do_pop3client_configuration(CtdlIPC *ipc)
+{
+       char buf[SIZ];
+       int num_recs = 0;
+       char **recs = NULL;
+       char ch;
+       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? "
+                       "\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\n", (extract_int(recs[i], 4) ? "Yes" : "No") );
+               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, "|");
+                               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) {
+                                       scr_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:
+                               break;
+               }
+       } 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 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) {
+                                       scr_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:
+                               break;
+               }
+       } while (!quitting);
+
+       if (recs != NULL) {
+               for (i=0; i<num_recs; ++i) free(recs[i]);
+               free(recs);
+       }
+}
+
+