Merge branch 'master' of ssh://git.citadel.org/appl/gitroot/citadel
authorthe_mgt <themgt@uncensored.citadel.org>
Wed, 18 Jul 2012 15:24:35 +0000 (17:24 +0200)
committerthe_mgt <themgt@uncensored.citadel.org>
Wed, 18 Jul 2012 15:24:35 +0000 (17:24 +0200)
Conflicts:
webcit/static/t/view_blog/post.html

113 files changed:
citadel/citadel.h
citadel/citadel.rc [deleted file]
citadel/citadel_decls.h [deleted file]
citadel/configure.ac
citadel/debian/citadel-client.install [deleted file]
citadel/debian/citadel-client.postinst [deleted file]
citadel/include/citadel_dirs.h
citadel/include/citadel_ipc.h [deleted file]
citadel/include/commands.h [deleted file]
citadel/sysconfig.h
citadel/textclient/citadel.c [deleted file]
citadel/textclient/client_chat.c [deleted file]
citadel/textclient/client_chat.h [deleted file]
citadel/textclient/client_passwords.c [deleted file]
citadel/textclient/client_passwords.h [deleted file]
citadel/textclient/commands.c [deleted file]
citadel/textclient/messages.c [deleted file]
citadel/textclient/messages.h [deleted file]
citadel/textclient/rooms.c [deleted file]
citadel/textclient/rooms.h [deleted file]
citadel/textclient/routines.c [deleted file]
citadel/textclient/routines.h [deleted file]
citadel/textclient/routines2.c [deleted file]
citadel/textclient/routines2.h [deleted file]
citadel/textclient/screen.c [deleted file]
citadel/textclient/screen.h [deleted file]
citadel/textclient/tuiconfig.c [deleted file]
citadel/textclient/tuiconfig.h [deleted file]
citadel/utillib/citadel_dirs.c
citadel/utillib/citadel_ipc.c [deleted file]
citadel/utillib/ipc_c_tcp.c [deleted file]
libcitadel/lib/libcitadel.h
textclient/.gitignore [new file with mode: 0644]
textclient/Makefile.in [new file with mode: 0644]
textclient/acinclude.m4 [new file with mode: 0644]
textclient/aclocal.m4 [new file with mode: 0644]
textclient/bootstrap [new file with mode: 0755]
textclient/citadel.rc [new file with mode: 0644]
textclient/config.guess [new file with mode: 0755]
textclient/config.sub [new file with mode: 0755]
textclient/configure.ac [new file with mode: 0644]
textclient/debian/changelog [new file with mode: 0644]
textclient/debian/control [new file with mode: 0644]
textclient/debian/copyright [new file with mode: 0644]
textclient/debian/rules [new file with mode: 0755]
textclient/install-sh [new file with mode: 0755]
textclient/ltmain.sh [new symlink]
textclient/m4/libtool.m4 [new symlink]
textclient/m4/ltoptions.m4 [new symlink]
textclient/m4/ltsugar.m4 [new symlink]
textclient/m4/ltversion.m4 [new symlink]
textclient/m4/lt~obsolete.m4 [new symlink]
textclient/missing [new file with mode: 0755]
textclient/mkinstalldirs [new file with mode: 0755]
textclient/scripts/mk_svn_revision.sh [new file with mode: 0755]
textclient/src/citadel.c [new file with mode: 0644]
textclient/src/citadel_ipc.c [new file with mode: 0644]
textclient/src/client_chat.c [new file with mode: 0644]
textclient/src/client_passwords.c [new file with mode: 0644]
textclient/src/commands.c [new file with mode: 0644]
textclient/src/ecrash.c [new file with mode: 0644]
textclient/src/include/citadel_decls.h [new file with mode: 0644]
textclient/src/include/citadel_ipc.h [new file with mode: 0644]
textclient/src/include/client_chat.h [new file with mode: 0644]
textclient/src/include/client_passwords.h [new file with mode: 0644]
textclient/src/include/commands.h [new file with mode: 0644]
textclient/src/include/ecrash.h [new file with mode: 0644]
textclient/src/include/md5.h [new file with mode: 0644]
textclient/src/include/messages.h [new file with mode: 0644]
textclient/src/include/rooms.h [new file with mode: 0644]
textclient/src/include/routines.h [new file with mode: 0644]
textclient/src/include/routines2.h [new file with mode: 0644]
textclient/src/include/screen.h [new file with mode: 0644]
textclient/src/include/tuiconfig.h [new file with mode: 0644]
textclient/src/include/typesize.h [new file with mode: 0644]
textclient/src/ipc_c_tcp.c [new file with mode: 0644]
textclient/src/md5.c [new file with mode: 0644]
textclient/src/messages.c [new file with mode: 0644]
textclient/src/rooms.c [new file with mode: 0644]
textclient/src/routines.c [new file with mode: 0644]
textclient/src/routines2.c [new file with mode: 0644]
textclient/src/screen.c [new file with mode: 0644]
textclient/src/tuiconfig.c [new file with mode: 0644]
webcit/dav_main.c
webcit/downloads.c
webcit/mainmenu.c
webcit/messages.c
webcit/messages.h
webcit/msg_renderers.c
webcit/netconf.c
webcit/notes.c
webcit/pushemail.c
webcit/roomlist.c
webcit/roomops.c
webcit/roomtokens.c
webcit/roomviews.c
webcit/sieve.c
webcit/siteconfig.c
webcit/smtpqueue.c
webcit/static/t/user/list_section.html
webcit/static/t/view_blog/comment.html
webcit/static/t/view_blog/post.html
webcit/static/t/view_message.html
webcit/static/t/who/bio.html
webcit/static/t/who/iconbar_section.html
webcit/static/t/who/section.html
webcit/static/t/who/summary_section.html
webcit/subst.c
webcit/subst.h
webcit/useredit.c
webcit/vcard_edit.c
webcit/webcit.h
webcit/who.c

index 0d18972200d745a0fec0b9bdd47faa567dee8dd0..87224168af6d7d86cacb5ea62ea0e17f5c44c36b 100644 (file)
@@ -61,12 +61,7 @@ extern "C" {
 
 /* Various length constants */
 
-#define UGLISTLEN      100     /* you get a ungoto list of this size */
 #define ROOMNAMELEN    128     /* The size of a roomname string */
-#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.
-                                */
                                         
 #define USERNAME_SIZE  64      /* The size of a username string */
 #define MAX_EDITORS    5       /* # of external editors supported */
@@ -202,50 +197,9 @@ struct floor {
 #define NEWREGISTER    0               /* new user to register */
 #define REREGISTER     1               /* existing user reregistering */
 
-#define READ_HEADER    2
-#define READ_MSGBODY   3
-
-/* 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 */
-
 /* number of items which may be handled by the CONF command */
 #define NUM_CONFIGS 70
 
-/*
- * MIME types used in Citadel for configuration stuff
- */
-#define SPOOLMIME      "application/x-citadel-delivery-list"
-#define        INTERNETCFG     "application/x-citadel-internet-config"
-#define IGNETCFG       "application/x-citadel-ignet-config"
-#define IGNETMAP       "application/x-citadel-ignet-map"
-#define FILTERLIST     "application/x-citadel-filter-list"
-#define SIEVECONFIG    "application/x-citadel-sieve-config"
-#define XMPPMORTUARY   "application/x-citadel-xmpp-mortuary"
-
 #define TRACE  syslog(LOG_DEBUG, "Checkpoint: %s, %d\n", __FILE__, __LINE__)
 
 #ifndef LONG_MAX
diff --git a/citadel/citadel.rc b/citadel/citadel.rc
deleted file mode 100644 (file)
index 351f5b5..0000000
+++ /dev/null
@@ -1,354 +0,0 @@
-#
-# This file allows full customization of the user interface.
-#
-# The standard client looks for this file in:
-# 1. $HOME/.citadelrc
-# 2. <compiled CTDLDIR>/citadel.rc
-# 3. /etc/citadel.rc
-# 4. <current directory>/citadel.rc
-
-# Set ENCRYPT to yes to force SSL/TLS encryption when connecting to a
-# Citadel server, even if the server is on the same machine as the
-# client.  Set it to no to disable SSL/TLS encryption.  The default is to
-# enable encryption for remote systems and to disable encryption for
-# systems on the same machine as the client.
-encrypt=default
-
-# Set EDITOR to the name of an external editor to be used for entering
-# messages.  If you want the external editor to be used by default, be sure
-# to reflect this in the command set below.
-#
-# editor=vi
-
-# If you define PRINTCMD, it will be a pipe through which messages are
-# printed when the user hits the <P>rint key after a message.
-#
-#printcmd=lpr
-
-# If you define EXPCMD, it will be a pipe through which any incoming
-# instant messages will be printed.
-#expcmd=xmessage -title "Instant message" -center -buttons OK -file -
-
-# ANSI_COLOR should be set to on/off/auto/user to control the use of
-# color on the screen.  If it is set to "auto" then an autodetect will be
-# attempted.  If it is set to "user" then it's a user-configurable option.
-#
-ansi_color=user
-
-# USE_BACKGROUND controls Citadel's use of the background.  If it is turned
-# off, then Citadel will set the background to black.  When it is turned on,
-# the background will be unchanged.  This is most useful with "transparent"
-# terminals.  Color must be on (see above) or this option has no effect.
-#
-use_background=off
-
-# STATUS_LINE will keep an informative status line on the top of the screen,
-# but it doesn't work on all terminals (Mac OS is particularly bad at this).
-status_line=off
-
-# PROMPT_CONTROL should be set to on/off/user to control whether the <N>ext
-# and <S>top keys are active when displaying the paginator prompt.  If it
-# is set to "user" then it's a user-configurable option.
-#
-prompt_control=user
-
-# If DISPLAY_MESSAGE_NUMBERS is set to 1, then messages will be displayed
-# with their message numbers in the header.  This is ugly but some
-# people seem to like it anyway...
-#
-display_message_numbers=0
-
-# The FORCE_MAIL_PROMPTS directive causes mail rooms to always display a
-# prompt after each message, even if the user has prompts turned off.
-#
-force_mail_prompts=1
-
-# USE_FLOORS determines whether the user sees floors, or a flat room space.
-# Set it to YES to always use floors, NO to never use floors, or DEFAULT
-# to use the setting in the user's configuration (which is normally the case).
-#
-use_floors=DEFAULT
-
-# BEEP should be set to 1 if you wish the terminal to beep when an express
-# message (page) comes in, otherwise set it to 0.
-#
-beep=1
-
-# ALLOW_ATTACHMENTS should be set to 1 if you wish the user to be able to
-# attach files to messages and/or save attachments to disk while reading
-# messages.  (DANGER: do NOT enable this for "safe" public clients!)
-#
-allow_attachments=0
-
-# IDLE_THRESHOLD is the amount of time a user does nothing before being
-# listed as "idle" on the who list.  The default is 900 seconds (15 minutes).
-#
-idle_threshold=900
-
-# If you set REMEMBER_PASSWORDS to 1, the Citadel client will offer to
-# remember your user name and password for every Citadel server you access,
-# automatically logging in with them on subsequent visits. 
-# (DANGER: do NOT enable this for "safe" public clients!)
-#
-remember_passwords=0
-
-# Optionally, you can force the same username and password everywhere.  This
-# obviously isn't as robust as the password manager.
-#
-#username=My User Name
-#password=mypassword
-
-# If URLCMD is defined, users can hit 'U' after reading a message which
-# contains embedded URL's, and the command will be executed.  Usually this
-# will be used to remote-control a web browser.  (Do not enable this command
-# for 'safe' public clients.)
-#
-# This one ought to work on any Linux that has the "Portland" API's installed.
-# If properly configured, xdg-open will open the URL in the user's preferred
-# web browser.
-#urlcmd=xdg-open "%s"
-#
-# This one works really well on a Macintosh -- it opens URL's in whatever
-# browser you have configured as the system default.
-#urlcmd=open "%s"
-
-# If OPENCMD is defined, users can hit 'O' after reading a message which
-# contains attachments, to open the attachments using that command.  This
-# allows attachments to be opened directly from the Citadel client without
-# having to first save them in a file.  The "allow_attachments" option (see
-# above) must be enabled in order for this to work.
-# 
-# xdg-open works on most newer Linux systems
-opencmd=xdg-open "%s"
-#
-# This probably will work on a Macintosh
-#opencmd=open "%s"
-
-# If GOTMAILCMD is defined, the specified command will be executed.  This
-# might be nice for playing sounds or providing any other type of notification.
-#
-#gotmailcmd=play gotmail.wav
-
-# If IMAGECMD is defined, users can hit 'I' to view images attached to a
-# message.  (Do not enable this command for public clients.)
-#
-# Possible image viewers follow
-
-# xdg-open - on Linux systems with the Portland API's installed, this ought
-# to open the image in the user's preferred viewer.
-#imagecmd=xdg-open "%s"
-#
-# Preview on Mac OS X
-#imagecmd=/Applications/Preview.app/Contents/MacOS/Preview "%s"
-
-
-# COMMAND SET CONFIGURATION
-#
-# All lines starting with "cmd=" are considered to be commands.  This allows
-# mapping of keytstrokes to various functions of the client.
-#
-# Format of each line:
-# cmd_num,access,keystrokes
-#
-# Keep a copy of the original version of this file around as a reference
-# for the command numbers.  They are not documented anywhere else.
-#
-# Access is:  0 (all users), 1 (aides or room aides), 2 (aides only).
-# Please be aware that it is futile to attempt to gain unauthorized access to
-# the administrative functions of the system by changing all the access levels
-# to 0.  If you do this, you'll simply be able to enter a lot of commands that
-# will fail at the server ... so don't bother trying. :-)
-#
-# The actual key to be pressed should be prefaced with an & (ampersand)
-# character.  Ampersands are interesting and useful characters and you should
-# use them as much as possible.  Commands requiring more than one keystroke
-# should be entered as multiple fields.
-#
-# If the last keystroke string ends with a : (colon), then the command
-# will finish by allowing the user to enter a string.
-#
-# In keystroke names, the string ^r will be replaced by the name of the
-# current room.  The string ^c will be replaced by a comma.
-#
-# Commands may contain no more than five keystrokes.
-#
-# Note that the following characters are illegal in commands:
-#  , (comma)    : (colon)     ^ (caret)     & (ampersand)
-#
-#
-cmd=1,0,&? (Help)
-cmd=1,0,&Help
-cmd=3,0,&Chat
-#
-# If you want to use an external editor by default, set <E>nter message
-# to command #46 (external editor) instead of #4 (built-in editor).
-cmd=4,0,&Enter message
-#
-cmd=5,0,&Goto
-cmd=6,0,&Skip ^r
-cmd=7,0,&Zap (forget) room
-cmd=8,0,&Known rooms
-cmd=9,0,&Last five msgs
-cmd=10,0,read &Forward
-cmd=11,0,read &Reverse
-cmd=12,0,read &Old
-cmd=13,0,read &New
-cmd=14,0,read &Directory
-cmd=15,0,&Terminate
-cmd=16,0,&Ungoto
-cmd=95,0,&.,&Ungoto:
-cmd=17,0,&Who is online
-cmd=47,0,&Abandon ^r^c goto...
-#cmd=90,0,&Abandon ^r^c goto:
-cmd=50,0,toggle e&Xpert mode
-cmd=49,0,read &Info file
-cmd=18,2,&! <shell>
-cmd=19,0,&.,list &Zapped rooms
-cmd=52,0,&.,&Skip ^r^c goto:
-cmd=56,0,&Page a user
-cmd=58,0,&Mail
-cmd=84,0,&Quiet mode
-#
-# We implement both <.G>oto and <J>ump commands which do the same thing, in
-# order to please a wider audience of users.  Remove one if you want to.
-#
-cmd=20,0,&Jump:
-cmd=20,0,&.,&Goto:
-#
-cmd=21,0,&.,&Help:
-cmd=22,1,&.,&Aide,&Kill this room
-cmd=23,1,&.,&Aide,&Edit this room
-cmd=24,0,&.,&Aide,&Who knows room
-cmd=25,2,&.,&Aide,&User,&Edit
-cmd=96,2,&.,&Aide,&User,&Delete
-cmd=26,2,&.,&Aide,&Validate new users
-cmd=48,1,&.,&Aide,enter &Info file
-cmd=27,0,&.,&Aide,&Room,&Invite user
-cmd=28,0,&.,&Aide,&Room,&Kick out user
-cmd=51,1,&.,&Aide,&File,&Delete
-cmd=54,1,&.,&Aide,&File,&Move
-cmd=70,2,&.,&Aide,&Message edit:
-cmd=78,1,&.,&Aide,&Post
-cmd=80,2,&.,&Aide,&System configuration,&General
-cmd=82,2,&.,&Aide,&System configuration,&Internet
-cmd=88,2,&.,&Aide,&System configuration,&Network
-cmd=92,2,&.,&Aide,&System configuration,network &Filter list
-cmd=85,2,&.,&Aide,&Terminate server,&Now
-cmd=86,2,&.,&Aide,&Terminate server,&Scheduled
-cmd=87,1,&.,&Aide,mailing &List recipients
-cmd=94,1,&.,&Aide,mailing list &Digest recipients
-cmd=89,1,&.,&Aide,&Network room sharing
-
-cmd=29,0,&.,&Terminate,and &Quit
-cmd=30,0,&.,&Terminate,and &Stay online
-
-# The 'read user listing' command accepts two different formats.  The one with
-# the trailing colon allows the user to enter a few characters to search for
-# users with a partial match.
-#
-#cmd=32,0,&.,&Read,&User listing
-cmd=32,0,&.,&Read,&User listing:
-
-cmd=33,0,&.,&Read,&Textfile formatted
-#
-# Command 55 allows the user to save a downloaded file directly to the
-# computer running the client software.  It is appropriate for a copy of
-# this client running on the user's own computer.  It is NOT appropriate for
-# public copies of the client that people will be dialing into.
-#
-#cmd=55,0,&.,&Read,&File
-#
-# Commands 34, 43, and 45 are appropriate for public copies of the client for
-# dialup use.  They transfer downloaded files to a temporary file and then
-# send them along to a dialup user using the popular protocols.
-#
-# cmd=34,0,&.,&Read,file using &Xmodem
-# cmd=43,0,&.,&Read,file using &Ymodem
-# cmd=45,0,&.,&Read,file using &Zmodem
-cmd=31,0,&.,&Read,&File unformatted
-#
-cmd=13,0,&.,&Read,&New messages
-cmd=12,0,&.,&Read,&Old msgs reverse
-cmd=71,0,&.,read &Last:
-cmd=9,0,&.,&Read,&Last five msgs
-cmd=14,0,&.,&Read,&Directory
-cmd=49,0,&.,&Read,&Info file
-cmd=114,0,&.,&Read,&Configuration
-cmd=115,0,&.,&Read,&System info
-cmd=35,0,&.,&Enter,&Password
-cmd=36,0,&.,&Enter,&ASCII message
-cmd=37,0,&.,&Enter,&Configuration
-cmd=38,0,&.,&Enter,a new &Room
-cmd=39,0,&.,&Enter,&Textfile
-cmd=77,0,&.,&Enter,&Username
-# cmd=40,0,&.,&Enter,file using &Xmodem
-# cmd=42,0,&.,&Enter,file using &Ymodem
-# cmd=44,0,&.,&Enter,file using &Zmodem
-#
-# Command 57 is the local-file-upload command for users with their own
-# copy of the clientware.  Commands 72-74 are for image uploads.
-#
-#cmd=57,0,&.,&Enter,&File
-#cmd=72,0,&.,&Enter,&Image,user &Picture
-#cmd=73,0,&.,&Enter,&Image,&Room banner
-#cmd=74,0,&.,&Enter,&Image,&Floor label
-#
-cmd=41,0,&.,&Enter,re&Gistration
-cmd=4,0,&.,&Enter,&Message
-cmd=127,0,&.,&Enter,remote POP&3 accounts
-cmd=128,0,&.,&Enter,&XML/RSS feed retrieval
-
-# If you have an external editor defined, it will appear on command 46.
-cmd=46,0,&.,&Enter,message with &Editor
-
-# If you have multiple editors defined, they will appear on
-# commands 46, 101, 102, 103... up to MAX_EDITORS (5 by default)
-#cmd=101,0,&.,&Enter,message with &VI
-#cmd=102,0,&.,&Enter,message with &Nano
-
-cmd=59,0,&;,&Configure floor mode
-cmd=60,0,&;,&Goto floor:
-cmd=60,0,&.,&Enter,&Floor:
-cmd=61,0,&;,&Skip to floor:
-cmd=62,0,&;,&Zap (forget) floor
-cmd=63,2,&;,&Aide,&Create floor
-cmd=64,2,&;,&Aide,&Edit this floor
-cmd=65,2,&;,&Aide,&Kill this floor
-cmd=68,0,&;,&Known rooms
-cmd=66,0,&.,&Enter,&Bio
-cmd=67,0,&.,&Read,&Bio
-
-cmd=79,0,&.,&Wholist,&Long
-cmd=75,0,&.,&Wholist,&Roomname
-cmd=76,0,&.,&Wholist,&Hostname
-cmd=91,0,&.,&Wholist,&Active
-cmd=93,0,&.,&Wholist,&Stealth mode
-
-cmd=110,0,&+Next room
-cmd=111,0,&-Previous room
-cmd=112,0,&>Next floor
-cmd=113,0,&<Previous floor
-cmd=116,0,&.,skip to &+Next room
-cmd=117,0,&.,skip to &-Previous room
-cmd=118,0,&.,skip to &>Next floor
-cmd=119,0,&.,skip to &<Previous floor
-
-cmd=120,0,&.,&Known,&Anonymous rooms
-cmd=121,0,&.,&Known,&Directory rooms
-cmd=68,0,&.,&Known,&Floors
-cmd=49,0,&.,&Known,room &Info
-cmd=122,0,&.,&Known,&Match rooms:
-cmd=123,0,&.,&Known,preferred &Only rooms
-cmd=124,0,&.,&Known,&Private rooms
-cmd=125,0,&.,&Known,&Read only rooms
-cmd=126,0,&.,&Known,&Shared rooms
-cmd=19,0,&.,&Known,&Zapped rooms
-
-#
-# Command 69 allows the user to enter a server command directly.  It is
-# primarily for testing and not intended for general use.  Usually there
-# is no need to enable it.
-cmd=69,0,&@Server command:
-#
-# end of command set configuration
-#
diff --git a/citadel/citadel_decls.h b/citadel/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);
index 943a8457126c660a59878de6cf197153a6b59da2..41c00e0d9d44e212d5c0e1dae7ddd6d4d16b296f 100644 (file)
@@ -247,11 +247,6 @@ 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)])
@@ -284,10 +279,6 @@ else
        test -d /usr/local/BerkeleyDB.4.5 && db_dir=/usr/local/BerkeleyDB.4.5
 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])
@@ -996,10 +987,7 @@ AC_CACHE_CHECK([for pthread_create], ac_cv_func_pthread_create,
 #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
+       test "$DATABASE" && TARGETS="server utils"
 fi
 
 AC_REPLACE_FUNCS(snprintf getutline)
@@ -1084,7 +1072,6 @@ if test "$abs_srcdir" != "$abs_builddir"; then
    fi
    mkdir -p $abs_builddir/utils
    mkdir -p $abs_builddir/utillib
-   mkdir -p $abs_builddir/textclient
 fi
 
 if test -n "$srcdir"; then 
diff --git a/citadel/debian/citadel-client.install b/citadel/debian/citadel-client.install
deleted file mode 100644 (file)
index 30b8f8a..0000000
+++ /dev/null
@@ -1,2 +0,0 @@
-etc/citadel/citadel.rc
-usr/bin/citadel
diff --git a/citadel/debian/citadel-client.postinst b/citadel/debian/citadel-client.postinst
deleted file mode 100644 (file)
index 190f16a..0000000
+++ /dev/null
@@ -1,24 +0,0 @@
-#!/bin/sh
-# postinst script for citadel-client
-
-set -e
-
-case "$1" in
-    configure)
-        if test -f /etc/citadel/citadel.rc; then
-           chown citadel:citadel /etc/citadel/citadel.rc
-       fi
-    ;;
-
-    abort-upgrade|abort-remove|abort-deconfigure)
-    ;;
-
-    *)
-        echo "postinst called with unknown argument \`$1'" >&2
-        exit 1
-    ;;
-esac
-
-#DEBHELPER#
-
-exit 0
index 51929cc6376810fe4d11b161d1a0ef36700d66d8..3a0b6d86e38573f630970387cd37c7c2d6417281 100644 (file)
@@ -37,7 +37,6 @@ extern char ctdl_utilbin_dir[PATH_MAX];
 
 /* some of the frequently used files */
 extern char file_citadel_control[PATH_MAX];
-extern char file_citadel_rc[PATH_MAX];
 extern char file_citadel_config[PATH_MAX];
 extern char file_citadel_urlshorteners[PATH_MAX];
 extern char file_lmtp_socket[PATH_MAX];
diff --git a/citadel/include/citadel_ipc.h b/citadel/include/citadel_ipc.h
deleted file mode 100644 (file)
index c67cfd3..0000000
+++ /dev/null
@@ -1,388 +0,0 @@
-
-#define        UDS                     "_UDS_"
-#ifdef __CYGWIN__
-#define DEFAULT_HOST           "localhost"
-#else
-#define DEFAULT_HOST           UDS
-#endif
-#define DEFAULT_PORT           "504"
-
-#include "sysdep.h"
-#ifdef HAVE_PTHREAD_H
-#include <pthread.h>
-#endif
-#ifdef HAVE_OPENSSL
-#include <openssl/ssl.h>
-#include <openssl/err.h>
-#include <openssl/rand.h>
-#endif
-
-#include "server.h"
-
-#ifdef __cplusplus
-extern "C" {
-#endif
-
-/* Quick and dirty hack; we don't want to use malloc() in C++ */
-#ifdef __cplusplus
-#define ialloc(t)      new t()
-#define ifree(o)       delete o
-#else
-#define ialloc(t)      malloc(sizeof(t))
-#define ifree(o)       free(o);
-#endif
-
-struct CtdlServInfo {
-       int pid;
-       char nodename[32];
-       char humannode[64];
-       char fqdn[64];
-       char software[64];
-       int rev_level;
-       char site_location[64];
-       char sysadm[64];
-       char moreprompt[256];
-       int ok_floors;
-       int paging_level;
-       int supports_qnop;
-       int supports_ldap;
-       int newuser_disabled;
-       char default_cal_zone[256];
-       double load_avg;
-       double worker_avg;
-       int thread_count;
-       int has_sieve;
-       int fulltext_enabled;
-       char svn_revision[256];
-       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 */
-       char node[SIZ];                 /* Short nodename of origin system */
-       char hnod[SIZ];                 /* Humannode of origin system */
-       struct parts *attachments;      /* Available attachments */
-       char *text;                     /* Message text */
-       int type;                       /* Message type */
-       time_t time;                    /* Time message was posted */
-       char nhdr;                      /* Suppress message header? */
-       char anonymous;                 /* An anonymous message */
-       char mime_chosen[SIZ];          /* Chosen MIME part to output */
-       char content_type[SIZ];         /* How would you like that? */
-       char references[SIZ];           /* Thread references */
-};
-
-
-struct ctdlipcfile {
-       char remote_name[PATH_MAX];     /* Filename on server */
-       char local_name[PATH_MAX];      /* Filename on client */
-       char description[80];           /* Description on server */
-       FILE *local_fd;                 /* Open file on client */
-       size_t size;                    /* Size of file in octets */
-       unsigned int upload:1;          /* uploading? 0 if downloading */
-       unsigned int complete:1;        /* Transfer has finished? */
-};
-
-
-struct ctdlipcmisc {
-       long newmail;                   /* Number of new Mail messages */
-       char needregis;                 /* Nonzero if user needs to register */
-       char needvalid;                 /* Nonzero if users need validation */
-};
-
-enum RoomList {
-       SubscribedRooms,
-       SubscribedRoomsWithNewMessages,
-       SubscribedRoomsWithNoNewMessages,
-       UnsubscribedRooms,
-       AllAccessibleRooms,
-       AllPublicRooms
-};
-#define AllFloors -1
-enum MessageList {
-       AllMessages,
-       OldMessages,
-       NewMessages,
-       LastMessages,
-       FirstMessages,
-       MessagesGreaterThan,
-       MessagesLessThan
-};
-enum MessageDirection {
-       ReadReverse = -1,
-       ReadForward = 1
-};
-
-/* Shared Diffie-Hellman parameters */
-#define DH_P           "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 CtdlIPCAideSetUserParameters(CtdlIPC *ipc, const struct ctdluser *uret, 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);
-
-
-
-static INLINE void CtdlIPC_lock(CtdlIPC *ipc)
-{
-       if (ipc->network_status_cb) ipc->network_status_cb(1);
-#ifdef THREADED_CLIENT
-       pthread_mutex_lock(&(ipc->mutex));
-#endif
-}
-
-
-static INLINE void CtdlIPC_unlock(CtdlIPC *ipc)
-{
-#ifdef THREADED_CLIENT
-       pthread_mutex_unlock(&(ipc->mutex));
-#endif
-       if (ipc->network_status_cb) ipc->network_status_cb(0);
-}
-
-#ifdef __cplusplus
-}
-#endif
diff --git a/citadel/include/commands.h b/citadel/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;
index 97ecc1dff8e55e3415f1baf08919a485e85e6ff4..a2a00f52b238d9b0e2fdf8f3f5f174b4be7e4e8a 100644 (file)
  */
 #define CHATLOG                "/dev/null"
 
-/* 
- * 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
-
 /*
  * Logging level to use if none is specified on the command line.
  * Note that this will suppress messages before they even get to syslog().
diff --git a/citadel/textclient/citadel.c b/citadel/textclient/citadel.c
deleted file mode 100644 (file)
index 5821df4..0000000
+++ /dev/null
@@ -1,2273 +0,0 @@
-/*
- * Main source module for the client program.
- *
- * 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.
- */
-
-#include "sysdep.h"
-#include <stdlib.h>
-#include <unistd.h>
-#include <fcntl.h>
-#include <stdio.h>
-#include <ctype.h>
-#include <string.h>
-
-#if TIME_WITH_SYS_TIME
-# include <sys/time.h>
-# include <time.h>
-#else
-# if HAVE_SYS_TIME_H
-#  include <sys/time.h>
-# else
-#  include <time.h>
-# endif
-#endif
-
-#include <limits.h>
-#include <sys/types.h>
-#include <sys/wait.h>
-#include <sys/stat.h>
-#include <sys/ioctl.h>
-#include <signal.h>
-#include <pwd.h>
-#include <stdarg.h>
-#include <errno.h>
-#include <libcitadel.h>
-#include "citadel.h"
-#include "citadel_ipc.h"
-#include "axdefs.h"
-#include "routines.h"
-#include "routines2.h"
-#include "tuiconfig.h"
-#include "rooms.h"
-#include "messages.h"
-#include "commands.h"
-#include "client_chat.h"
-#include "client_passwords.h"
-#include "citadel_decls.h"
-#include "sysdep.h"
-#ifndef HAVE_SNPRINTF
-#include "snprintf.h"
-#endif
-#include "screen.h"
-#include "citadel_dirs.h"
-
-#include "ecrash.h"
-#include "md5.h"
-
-#define IFEXPERT if (userflags&US_EXPERT)
-#define IFNEXPERT if ((userflags&US_EXPERT)==0)
-#define IFAIDE if (axlevel>=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 aide */
-int timescalled;
-int posted;
-unsigned userflags;
-long usernum = 0L;             /* user number */
-time_t lastcall = 0L;          /* Date/time of previous login */
-char newnow;
-long highest_msg_read;         /* used for <A>bandon room cmd */
-long maxmsgnum;                        /* used for <G>oto */
-char sigcaught = 0;
-char 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)REV_LEVEL/100);
-       scr_printf("server build %s,\n", ipc->ServInfo.svn_revision, (float)REV_LEVEL/100);
-    scr_printf("and located in %s.\n", ipc->ServInfo.site_location);
-    scr_printf("Connected users %d / Active users %d / Highest message #%ld\n", mrtg_users, mrtg_active_users, mrtg_himessage);
-    scr_printf("Server uptime: %s\n", mrtg_server_uptime);
-    scr_printf("Your system administrator is %s.\n", ipc->ServInfo.sysadm);
-    scr_printf("Copyright (C)1987-2009 by the Citadel development team\n");
-}
-
-/*
- * forget all rooms on current floor
- */
-void forget_this_floor(CtdlIPC *ipc)
-{
-       if (curr_floor == 0) {
-               scr_printf("Can't forget this floor.\n");
-               return;
-       }
-       if (floorlist[0][0] == 0) {
-               load_floorlist(ipc);
-       }
-       scr_printf("Are you sure you want to forget all rooms on %s? ",
-              &floorlist[(int) curr_floor][0]);
-       if (yesno() == 0) {
-               return;
-       }
-
-       gf_toroom(ipc, "_BASEROOM_", GF_ZAP);
-}
-
-
-/*
- * 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, SERVER_TYPE, 0, REV_LEVEL,
-                (ipc->isLocal ? "local" : CITADEL),
-                (supplied_hostname) ? supplied_hostname : 
-                /* Look up the , in the bible if you're confused */
-                (locate_host(ipc, buf), buf), buf);
-
-       /* Indicate to the server that we prefer to decode Base64 and
-        * quoted-printable on the client side.
-        */
-       if ((CtdlIPCSpecifyPreferredFormats(ipc, buf, "dont_decode") / 100 ) != 2) {
-               scr_printf("ERROR: Extremely old server; MSG4 framework not supported.\n");
-               logoff(ipc, 0);
-       }
-
-       /*
-        * Tell the server what our preferred content formats are.
-        *
-        * Originally we preferred HTML over plain text because we can format
-        * it to the reader's screen width, but since our HTML-to-text parser
-        * isn't really all that great, it's probably better to just go with
-        * the plain text when we have it available.
-        */
-       if ((CtdlIPCSpecifyPreferredFormats(ipc, buf, "text/plain|text/html") / 100 ) != 2) {
-               scr_printf("ERROR: Extremely old server; MSG4 framework not supported.\n");
-               logoff(ipc, 0);
-       }
-}
-
-
-
-/*
- * 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.filename = file_pid_paniclog;
-//     panic_fd=open(file_pid_paniclog, O_APPEND|O_CREAT|O_DIRECT);
-       params.filep = fopen(file_pid_paniclog, "a+");
-       params.debugLevel = ECRASH_DEBUG_VERBOSE;
-       params.dumpAllThreads = TRUE;
-       params.useBacktraceSymbols = 1;
-///    BuildSymbolTable(&symbol_table);
-//     params.symbolTable = &symbol_table;
-       params.signals[0]=SIGSEGV;
-       params.signals[1]=SIGILL;
-       params.signals[2]=SIGBUS;
-       params.signals[3]=SIGABRT;
-
-       eCrash_Init(&params);
-#endif 
-       setIPCErrorPrintf(scr_printf);
-       setCryptoStatusHook(statusHook);
-       
-       /* Permissions sanity check - don't run citadel setuid/setgid */
-       if (getuid() != geteuid()) {
-               scr_printf("Please do not run citadel setuid!\n");
-               logoff(NULL, 3);
-       } else if (getgid() != getegid()) {
-               scr_printf("Please do not run citadel setgid!\n");
-               logoff(NULL, 3);
-       }
-
-       stty_ctdl(SB_SAVE);     /* Store the old terminal parameters */
-       load_command_set();     /* parse the citadel.rc file */
-       stty_ctdl(SB_NO_INTR);  /* Install the new ones */
-       /* signal(SIGHUP, dropcarr);FIXME */    /* Cleanup gracefully if carrier is dropped */
-       signal(SIGPIPE, dropcarr);      /* Cleanup gracefully if local conn. dropped */
-       signal(SIGTERM, dropcarr);      /* Cleanup gracefully if terminated */
-       signal(SIGCONT, catch_sigcont); /* Catch SIGCONT so we can reset terminal */
-#ifdef SIGWINCH
-       signal(SIGWINCH, scr_winch);    /* Window resize signal */
-#endif
-
-#ifdef HAVE_OPENSSL
-       arg_encrypt = RC_DEFAULT;
-#endif
-
-       /* 
-        * 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")) {
-                       struct stat st;
-               
-                       if (chdir(CTDLDIR) < 0) {
-                               perror("can't change to " CTDLDIR);
-                               logoff(NULL, 3);
-                       }
-
-                       /*
-                        * Drop privileges if necessary. We stat
-                        * citadel.config to get the uid/gid since it's
-                        * guaranteed to have the uid/gid we want.
-                        */
-                       if (!getuid() || !getgid()) {
-                               if (stat(file_citadel_config, &st) < 0) {
-                                       perror("couldn't stat citadel.config");
-                                       logoff(NULL, 3);
-                               }
-                               if (!getgid() && (setgid(st.st_gid) < 0)) {
-                                       perror("couldn't change gid");
-                                       logoff(NULL, 3);
-                               }
-                               if (!getuid() && (setuid(st.st_uid) < 0)) {
-                                       perror("couldn't change uid");
-                                       logoff(NULL, 3);
-                               }
-                               /*
-                                 scr_printf("Privileges changed to uid %d gid %d\n",
-                                 getuid(), getgid());
-                               */
-                       }
-                       argc = shift(argc, argv, a, 1);
-               }
-       }
-       
-
-       screen_new();
-       /* 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:
-                               formout(ipc, "help");
-                               break;
-                       case 4:
-                               entmsg(ipc, 0, ((userflags & US_EXTEDIT) ? 2 : 0), 0);
-                               break;
-                       case 36:
-                               entmsg(ipc, 0, 1, 0);
-                               break;
-                       case 46:
-                               entmsg(ipc, 0, 2, 0);
-                               break;
-                       case 78:
-                               entmsg(ipc, 0, ((userflags & US_EXTEDIT) ? 2 : 0), 1);
-                               break;
-                       case 5:                         /* <G>oto */
-                               updatels(ipc);
-                               gotonext(ipc);
-                               break;
-                       case 47:                        /* <A>bandon */
-                               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 74:
-                               snprintf(aaa, sizeof aaa, "_floorpic_|%d", curr_floor);
-                               cli_image_upload(ipc, aaa);
-                               break;
-
-                       case 75:
-                               enternew(ipc, "roomname", aaa, 20);
-                               r = CtdlIPCChangeRoomname(ipc, aaa, bbb);
-                               if (r / 100 != 2)
-                                       scr_printf("\n%s\n", bbb);
-                               break;
-                       case 76:
-                               enternew(ipc, "hostname", aaa, 25);
-                               r = CtdlIPCChangeHostname(ipc, aaa, bbb);
-                               if (r / 100 != 2)
-                                       scr_printf("\n%s\n", bbb);
-                               break;
-                       case 77:
-                               enternew(ipc, "username", aaa, 32);
-                               r = CtdlIPCChangeUsername(ipc, aaa, bbb);
-                               if (r / 100 != 2)
-                                       scr_printf("\n%s\n", bbb);
-                               break;
-
-                       case 35:
-                               set_password(ipc);
-                               break;
-
-                       case 21:
-                               if (argbuf[0] == 0)
-                                       strcpy(aaa, "?");
-                               display_help(ipc, argbuf);
-                               break;
-
-                       case 41:
-                               formout(ipc, "register");
-                               entregis(ipc);
-                               break;
-
-                       case 15:
-                               scr_printf("Are you sure (y/n)? ");
-                               if (yesno() == 1) {
-                                       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 89:
-                               network_config_management(ipc, "ignet_push_share",
-                                                         "Nodes with which we share this room");
-                               break;
-
-                       case 88:
-                               do_ignet_configuration(ipc);
-                               break;
-
-                       case 92:
-                               do_filterlist_configuration(ipc);
-                               break;
-
-                       case 6:
-                               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/citadel/textclient/client_chat.c b/citadel/textclient/client_chat.c
deleted file mode 100644 (file)
index 760ce59..0000000
+++ /dev/null
@@ -1,308 +0,0 @@
-/*
- * front end for multiuser chat
- *
- * 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.
- */
-
-#include "sysdep.h"
-#include <stdlib.h>
-#include <unistd.h>
-#include <stdio.h>
-#include <fcntl.h>
-#include <ctype.h>
-#include <string.h>
-#include <signal.h>
-#include <errno.h>
-
-#if TIME_WITH_SYS_TIME
-# include <sys/time.h>
-# include <time.h>
-#else
-# if HAVE_SYS_TIME_H
-#  include <sys/time.h>
-# else
-#  include <time.h>
-# endif
-#endif
-
-#include <sys/types.h>
-#ifdef HAVE_SYS_SELECT_H
-#include <sys/select.h>
-#endif
-#include <stdarg.h>
-#include <libcitadel.h>
-#include "citadel.h"
-#include "citadel_ipc.h"
-#include "client_chat.h"
-#include "commands.h"
-#include "routines.h"
-#include "citadel_decls.h"
-#include "rooms.h"
-#include "messages.h"
-#ifndef HAVE_SNPRINTF
-#include "snprintf.h"
-#endif
-#include "screen.h"
-
-#define MIN(a, b) ((a) < (b) ? (a) : (b))
-
-extern char temp[];
-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/citadel/textclient/client_chat.h b/citadel/textclient/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/citadel/textclient/client_passwords.c b/citadel/textclient/client_passwords.c
deleted file mode 100644 (file)
index aabb7d9..0000000
+++ /dev/null
@@ -1,152 +0,0 @@
-/*
- * Functions which allow the client to remember usernames and passwords for
- * various sites.
- *
- * 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.
- */
-
-#include "sysdep.h"
-#include <stdlib.h>
-#include <unistd.h>
-#include <string.h>
-#include <ctype.h>
-#include <pwd.h>
-#include <sys/types.h>
-#include <sys/stat.h>
-#include <limits.h>
-#include <stdio.h>
-#include <libcitadel.h>
-#include "citadel.h"
-#include "citadel_ipc.h"
-#include "commands.h"
-#include "client_passwords.h"
-
-#define PWFILENAME "%s/.citadel.passwords"
-
-void determine_pwfilename(char *pwfile, size_t n) {
-       struct passwd *p;
-
-       p = getpwuid(getuid());
-       if (p == NULL) strcpy(pwfile, "");
-       snprintf(pwfile, n, PWFILENAME, p->pw_dir);
-}
-
-
-/*
- * Check the password file for a host/port match; if found, stuff the user
- * name and password into the user/pass buffers
- */
-void get_stored_password(
-               char *host,
-               char *port,
-               char *username,
-               char *password) {
-
-       char pwfile[PATH_MAX];
-       FILE *fp;
-       char buf[SIZ];
-       char buf64[SIZ];
-       char hostbuf[256], portbuf[256], ubuf[256], pbuf[256];
-
-       strcpy(username, "");
-       strcpy(password, "");
-
-       determine_pwfilename(pwfile, sizeof pwfile);
-       if (IsEmptyStr(pwfile)) return;
-
-       fp = fopen(pwfile, "r");
-       if (fp == NULL) return;
-       while (fgets(buf64, sizeof buf64, fp) != NULL) {
-               CtdlDecodeBase64(buf, buf64, sizeof(buf64));
-               extract_token(hostbuf, buf, 0, '|', sizeof hostbuf);
-               extract_token(portbuf, buf, 1, '|', sizeof portbuf);
-               extract_token(ubuf, buf, 2, '|', sizeof ubuf);
-               extract_token(pbuf, buf, 3, '|', sizeof pbuf);
-
-               if (!strcasecmp(hostbuf, host)) {
-                       if (!strcasecmp(portbuf, port)) {
-                               strcpy(username, ubuf);
-                               strcpy(password, pbuf);
-                       }
-               }
-       }
-       fclose(fp);
-}
-
-
-/*
- * Set (or clear) stored passwords.
- */
-void set_stored_password(
-               char *host,
-               char *port,
-               char *username,
-               char *password) {
-
-       char pwfile[PATH_MAX];
-       FILE *fp, *oldfp;
-       char buf[SIZ];
-       char buf64[SIZ];
-       char hostbuf[256], portbuf[256], ubuf[256], pbuf[256];
-
-       determine_pwfilename(pwfile, sizeof pwfile);
-       if (IsEmptyStr(pwfile)) return;
-
-       oldfp = fopen(pwfile, "r");
-       if (oldfp == NULL) oldfp = fopen("/dev/null", "r");
-       unlink(pwfile);
-       fp = fopen(pwfile, "w");
-       if (fp == NULL) fp = fopen("/dev/null", "w");
-       while (fgets(buf64, sizeof buf64, oldfp) != NULL) {
-               CtdlDecodeBase64(buf, buf64, sizeof(buf64));
-               extract_token(hostbuf, buf, 0, '|', sizeof hostbuf);
-               extract_token(portbuf, buf, 1, '|', sizeof portbuf);
-               extract_token(ubuf, buf, 2, '|', sizeof ubuf);
-               extract_token(pbuf, buf, 3, '|', sizeof pbuf);
-
-               if ( (strcasecmp(hostbuf, host)) 
-                  || (strcasecmp(portbuf, port)) ) {
-                       snprintf(buf, sizeof buf, "%s|%s|%s|%s|",
-                               hostbuf, portbuf, ubuf, pbuf);
-                       CtdlEncodeBase64(buf64, buf, strlen(buf), 0);
-                       fprintf(fp, "%s\n", buf64);
-               }
-       }
-       if (!IsEmptyStr(username)) {
-               snprintf(buf, sizeof buf, "%s|%s|%s|%s|",
-                       host, port, username, password);
-               CtdlEncodeBase64(buf64, buf, strlen(buf), 0);
-               fprintf(fp, "%s\n", buf64);
-       }
-       fclose(oldfp);
-       fclose(fp);
-       chmod(pwfile, 0600);
-}
-
-
-/*
- * Set the password if the user wants to, clear it otherwise 
- */
-void offer_to_remember_password(CtdlIPC *ipc,
-               char *host,
-               char *port,
-               char *username,
-               char *password) {
-
-       if (rc_remember_passwords) {
-               if (boolprompt("Remember username/password for this site", 0)) {
-                       set_stored_password(host, port, username, password);
-               }
-               else {
-                       set_stored_password(host, port, "", "");
-               }
-       }
-}
diff --git a/citadel/textclient/client_passwords.h b/citadel/textclient/client_passwords.h
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/citadel/textclient/commands.c b/citadel/textclient/commands.c
deleted file mode 100644 (file)
index 426849a..0000000
+++ /dev/null
@@ -1,1555 +0,0 @@
-/*
- * This file contains functions which implement parts of the
- * text-mode user interface.
- *
- * 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.
- */
-
-#include "sysdep.h"
-#include <stdlib.h>
-#include <unistd.h>
-#include <stdio.h>
-#include <fcntl.h>
-#include <ctype.h>
-#include <string.h>
-#include <sys/types.h>
-
-#if TIME_WITH_SYS_TIME
-# include <sys/time.h>
-# include <time.h>
-#else
-# if HAVE_SYS_TIME_H
-#  include <sys/time.h>
-# else
-#  include <time.h>
-# endif
-#endif
-
-#ifdef HAVE_TERMIOS_H
-#include <termios.h>
-#else
-#include <sgtty.h>
-#endif
-
-#ifdef HAVE_SYS_SELECT_H
-#include <sys/select.h>
-#endif
-
-#ifdef THREADED_CLIENT
-#include <pthread.h>
-#endif
-
-#include <signal.h>
-#include <errno.h>
-#include <stdarg.h>
-#include <libcitadel.h>
-#include "citadel.h"
-#include "citadel_ipc.h"
-#include "commands.h"
-#include "messages.h"
-#include "citadel_decls.h"
-#include "routines.h"
-#include "routines2.h"
-#include "rooms.h"
-#include "client_chat.h"
-#include "citadel_dirs.h"
-#include "tuiconfig.h"
-#ifndef HAVE_SNPRINTF
-#include "snprintf.h"
-#endif
-#include "screen.h"
-#include "ecrash.h"
-
-struct citcmd {
-       struct citcmd *next;
-       int c_cmdnum;
-       int c_axlevel;
-       char c_keys[5][64];
-};
-
-#define IFNEXPERT if ((userflags&US_EXPERT)==0)
-
-
-int rc_exp_beep;
-char rc_exp_cmd[1024];
-int rc_allow_attachments;
-int rc_display_message_numbers;
-int rc_force_mail_prompts;
-int rc_remember_passwords;
-int rc_ansi_color;
-int rc_color_use_bg;
-int rc_prompt_control = 0;
-time_t rc_idle_threshold = (time_t)900;
-char rc_url_cmd[SIZ];
-char rc_open_cmd[SIZ];
-char rc_gotmail_cmd[SIZ];
-
-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 formout(ipc, "mainmenu");
-
-       print_instant();
-       strcpy(argbuf, "");
-       cmdpos = 0;
-       for (a = 0; a < 5; ++a)
-               cmdbuf[a] = 0;
-       /* now the room prompt... */
-       ok_to_interrupt = 1;
-       color(BRIGHT_WHITE);
-       scr_printf("\n%s", room_name);
-       color(DIM_WHITE);
-       scr_printf("%c ", room_prompt(room_flags));
-
-       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 file viewer
- */
-void display_help(CtdlIPC *ipc, char *name)
-{
-       formout(ipc, name);
-}
-
-
-/*
- * fmout() - Citadel text formatter and paginator
- */
-int fmout(
-       int width,      /* screen width to use */
-       FILE *fpin,     /* file to read from, or NULL to format given text */
-       char *text,     /* text to be formatted (when fpin is NULL */
-       FILE *fpout,    /* file to write to, or NULL to write to screen */
-       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/citadel/textclient/messages.c b/citadel/textclient/messages.c
deleted file mode 100644 (file)
index 6cc8f6a..0000000
+++ /dev/null
@@ -1,1981 +0,0 @@
-/*
- * Text client functions for reading and writing of messages
- *
- * 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.
- */
-
-#include "sysdep.h"
-#include <stdlib.h>
-#include <unistd.h>
-#include <sys/types.h>
-#include <fcntl.h>
-#include <stdio.h>
-#include <ctype.h>
-#include <string.h>
-#include <signal.h>
-#include <errno.h>
-#include <limits.h>
-#include <sys/wait.h>
-#include <sys/stat.h>
-
-#if TIME_WITH_SYS_TIME
-# include <sys/time.h>
-# include <time.h>
-#else
-# if HAVE_SYS_TIME_H
-#  include <sys/time.h>
-# else
-#  include <time.h>
-# endif
-#endif
-
-#ifdef HAVE_PTHREAD_H
-#include <pthread.h>
-#endif
-
-#include <stdarg.h>
-#include <libcitadel.h>
-#include "citadel.h"
-#include "citadel_ipc.h"
-#include "citadel_decls.h"
-#include "messages.h"
-#include "commands.h"
-#include "tuiconfig.h"
-#include "rooms.h"
-#ifndef HAVE_SNPRINTF
-#include "snprintf.h"
-#endif
-#include "screen.h"
-
-#define MAXWORDBUF SIZ
-#define NO_REPLY_TO    "nobody ... xxxxxx"
-
-char reply_to[SIZ];
-char reply_subject[SIZ];
-char reply_references[SIZ];
-char reply_inreplyto[SIZ];
-
-struct cittext {
-       struct cittext *next;
-       char text[MAXWORDBUF];
-};
-
-void stty_ctdl(int cmd);
-int haschar(const char *st, int ch);
-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[SIZ];
-       int format_type = 0;
-       int fr = 0;
-       int nhdr = 0;
-       struct ctdlipcmessage *message = NULL;
-       int r;                          /* IPC response code */
-       char *converted_text = NULL;
-       char *lineptr;
-       char *nextline;
-       char *searchptr;
-       int i;
-       char ch;
-       int linelen;
-       int final_line_is_blank = 0;
-
-       has_images = 0;
-
-       sigcaught = 0;
-       stty_ctdl(1);
-
-       strcpy(reply_to, NO_REPLY_TO);
-       strcpy(reply_subject, "");
-       strcpy(reply_references, "");
-       strcpy(reply_inreplyto, "");
-
-       r = CtdlIPCGetSingleMessage(ipc, num, (pagin == READ_HEADER ? 1 : 0), 4, &message, buf);
-       if (r / 100 != 1) {
-               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("hnod=%s\nroom=%s\nnode=%s\ntime=%s",
-                               message->hnod, message->room,
-                               message->node, 
-                               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 {
-               fmt_date(now, sizeof now, message->time, 0);
-               if (dest) {
-                       fprintf(dest, "%s from %s ", now, message->author);
-                       if (!IsEmptyStr(message->email)) {
-                               fprintf(dest, "<%s> ", message->email);
-                       }
-               } else {
-                       color(BRIGHT_CYAN);
-                       scr_printf("%s ", now);
-                       color(DIM_WHITE);
-                       scr_printf("from ");
-                       color(BRIGHT_CYAN);
-                       scr_printf("%s ", message->author);
-                       if (!IsEmptyStr(message->email)) {
-                               color(DIM_WHITE);
-                               scr_printf("<");
-                               color(BRIGHT_BLUE);
-                               scr_printf("%s", message->email);
-                                       color(DIM_WHITE);
-                               scr_printf("> ");
-                       }
-               }
-               if (!IsEmptyStr(message->node)) {
-                       if ((room_flags & QR_NETWORK)
-                           || ((strcasecmp(message->node, ipc->ServInfo.nodename)
-                            && (strcasecmp(message->node, ipc->ServInfo.fqdn))))) {
-                               if (IsEmptyStr(message->email)) {
-                                       if (dest) {
-                                               fprintf(dest, "@%s ", message->node);
-                                       } else {
-                                               color(DIM_WHITE);
-                                               scr_printf("@");
-                                               color(BRIGHT_YELLOW);
-                                               scr_printf("%s ", message->node);
-                                       }
-                               }
-                       }
-               }
-               if (strcasecmp(message->hnod, ipc->ServInfo.humannode)
-                   && (!IsEmptyStr(message->hnod)) && (IsEmptyStr(message->email))) {
-                       if (dest) {
-                               fprintf(dest, "(%s) ", message->hnod);
-                       } else {
-                               color(DIM_WHITE);
-                               scr_printf("(");
-                               color(BRIGHT_WHITE);
-                               scr_printf("%s", message->hnod);
-                               color(DIM_WHITE);
-                               scr_printf(") ");
-                       }
-               }
-               if (strcasecmp(message->room, room_name) && (IsEmptyStr(message->email))) {
-                       if (dest) {
-                               fprintf(dest, "in %s> ", message->room);
-                       } else {
-                               color(DIM_WHITE);
-                               scr_printf("in ");
-                               color(BRIGHT_MAGENTA);
-                               scr_printf("%s> ", message->room);
-                       }
-               }
-               if (!IsEmptyStr(message->recipient)) {
-                       if (dest) {
-                               fprintf(dest, "to %s ", message->recipient);
-                       } else {
-                               color(DIM_WHITE);
-                               scr_printf("to ");
-                               color(BRIGHT_CYAN);
-                               scr_printf("%s ", message->recipient);
-                       }
-               }
-       }
-       
-       if (dest) {
-               fprintf(dest, "\n");
-       } else {
-               scr_printf("\n");
-       }
-
-       /* Set the reply-to address to an Internet e-mail address if possible
-        */
-       if ((message->email != NULL) && (!IsEmptyStr(message->email))) {
-               if (!IsEmptyStr(message->author)) {
-                       snprintf(reply_to, sizeof reply_to, "%s <%s>", message->author, message->email);
-               }
-               else {
-                       safestrncpy(reply_to, message->email, sizeof reply_to);
-               }
-       }
-
-       /* But if we can't do that, set it to a Citadel address.
-        */
-       if (!strcmp(reply_to, NO_REPLY_TO)) {
-               snprintf(reply_to, sizeof(reply_to), "%s @ %s",
-                        message->author, message->node);
-       }
-
-       if (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 */
-       num_urls = 0;   /* Start with a clean slate */
-       searchptr = message->text;
-       while ( (searchptr != NULL) && (num_urls < MAXURLS) ) {
-               searchptr = strstr(searchptr, "http://");
-               if (searchptr != NULL) {
-                       safestrncpy(urls[num_urls], searchptr, sizeof(urls[num_urls]));
-                       for (i = 0; i < strlen(urls[num_urls]); i++) {
-                               ch = urls[num_urls][i];
-                               if (ch == '>' || ch == '\"' || ch == ')' ||
-                                   ch == ' ' || ch == '\n') {
-                                       urls[num_urls][i] = 0;
-                                       break;
-                               }
-                       }
-                       num_urls++;
-                       ++searchptr;
-               }
-       }
-
-       /*
-        * Here we go
-        */
-       if (format_type == 0) {
-               fr = fmout(screenwidth, NULL, message->text, dest, 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 test 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[SIZ];
-       char header[SIZ];
-       int cksum = 0;
-
-       if ( (mode == 2) && (IsEmptyStr(editor_path)) ) {
-               scr_printf("*** No editor available; using built-in editor.\n");
-               mode = 0;
-       }
-
-       fmt_date(datestr, sizeof datestr, time(NULL), 0);
-       header[0] = 0;
-
-       if (room_flags & QR_ANONONLY && !recipient) {
-               snprintf(header, sizeof header, " ****");
-       }
-       else {
-               snprintf(header, sizeof header,
-                       " %s from %s",
-                       datestr,
-                       (is_anonymous ? "[anonymous]" : fullname)
-                       );
-               if (!IsEmptyStr(recipient)) {
-                       size_t tmp = strlen(header);
-                       snprintf(&header[tmp], sizeof header - tmp,
-                               " to %s", recipient);
-               }
-       }
-       scr_printf("%s\n", header);
-       if (subject != NULL) if (!IsEmptyStr(subject)) {
-               scr_printf("Subject: %s\n", subject);
-       }
-       
-       if ( (subject_required) && (IsEmptyStr(subject)) ) {
-               newprompt("Subject: ", subject, 70);
-       }
-
-       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) {
-               formout(ipc, "entermsg");
-       }
-
-       /* 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/citadel/textclient/messages.h b/citadel/textclient/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/citadel/textclient/rooms.c b/citadel/textclient/rooms.c
deleted file mode 100644 (file)
index 6dcc99b..0000000
+++ /dev/null
@@ -1,1402 +0,0 @@
-/*
- * Client-side functions which perform room operations
- *
- * 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.
- */
-
-#include "sysdep.h"
-#include <stdlib.h>
-#include <unistd.h>
-#include <fcntl.h>
-#include <stdio.h>
-#include <ctype.h>
-#include <string.h>
-#include <signal.h>
-#include <sys/types.h>
-#include <sys/stat.h>
-#include <sys/wait.h>
-#include <errno.h>
-#include <stdarg.h>
-#include <libcitadel.h>
-#include "citadel.h"
-#include "citadel_ipc.h"
-#include "citadel_decls.h"
-#include "rooms.h"
-#include "commands.h"
-#include "messages.h"
-#include "tuiconfig.h"
-#ifndef HAVE_SNPRINTF
-#include "snprintf.h"
-#endif
-#include "screen.h"
-#include "citadel_dirs.h"
-
-#define IFNEXPERT if ((userflags&US_EXPERT)==0)
-
-
-void stty_ctdl(int cmd);
-void dotgoto(CtdlIPC *ipc, char *towhere, int display_name, int fromungoto);
-void progress(CtdlIPC* ipc, unsigned long curr, unsigned long cmax);
-int pattern(char *search, char *patn);
-int file_checksum(char *filename);
-int nukedir(char *dirname);
-
-extern unsigned room_flags;
-extern char room_name[];
-extern char temp[];
-extern char tempdir[];
-extern int editor_pid;
-extern int screenwidth;
-extern int screenheight;
-extern char fullname[];
-extern char sigcaught;
-extern char floor_mode;
-extern char curr_floor;
-
-
-extern int ugnum;
-extern long uglsn;
-extern char *uglist[];
-extern long uglistlsn[];
-extern int uglistsize;
-
-extern char floorlist[128][SIZ];
-
-
-void load_floorlist(CtdlIPC *ipc)
-{
-       int a;
-       char buf[SIZ];
-       char *listing = NULL;
-       int r;                  /* IPC response code */
-
-       for (a = 0; a < 128; ++a)
-               floorlist[a][0] = 0;
-
-       r = CtdlIPCFloorListing(ipc, &listing, buf);
-       if (r / 100 != 1) {
-               strcpy(floorlist[0], "Main Floor");
-               return;
-       }
-       while (*listing && !IsEmptyStr(listing)) {
-               extract_token(buf, listing, 0, '\n', sizeof buf);
-               remove_token(listing, 0, '\n');
-               extract_token(floorlist[extract_int(buf, 0)], buf, 1, '|', SIZ);
-       }
-       free(listing);
-}
-
-
-void room_tree_list(struct ctdlroomlisting *rp)
-{
-       static int c = 0;
-       char rmname[ROOMNAMELEN];
-       int f;
-
-       if (rp == NULL) {
-               c = 1;
-               return;
-       }
-
-       if (rp->lnext != NULL) {
-               room_tree_list(rp->lnext);
-       }
-
-       if (sigcaught == 0) {
-               strcpy(rmname, rp->rlname);
-               f = rp->rlflags;
-               if ((c + strlen(rmname) + 4) > screenwidth) {
-
-                       /* line break, check the paginator */
-                       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) && (f & QR_NETWORK))
-                       scr_printf("}  ");
-               else if (f & QR_DIRECTORY)
-                       scr_printf("]  ");
-               else if (f & QR_NETWORK)
-                       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;
-    case 6:
-       scr_printf("\n   Shared rooms:\n");
-           listrms(listing, LISTRMS_ALL, -1, QR_NETWORK, 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 raide[USERNAME_SIZE];
-       char buf[SIZ];
-       struct ctdlroom *attr = NULL;
-       struct ExpirePolicy *eptr = NULL;
-       int r;                          /* IPC response code */
-
-       /* Fetch the existing room config */
-       r = CtdlIPCGetRoomAttributes(ipc, &attr, buf);
-       if (r / 100 != 2) {
-               scr_printf("%s\n", buf);
-               return;
-       }
-       eptr = &(attr->QRep);
-
-       /* Fetch the name of the current room aide */
-       r = CtdlIPCGetRoomAide(ipc, buf);
-       if (r / 100 == 2) {
-               safestrncpy(raide, buf, sizeof raide);
-       } else {
-               strcpy(raide, "");
-       }
-       if (IsEmptyStr(raide)) {
-               strcpy(raide, "none");
-       }
-
-       /* Fetch the expire policy (this will silently fail on old servers,
-        * resulting in "default" policy)
-        */
-       r = CtdlIPCGetMessageExpirationPolicy(ipc, 0, &eptr, buf);
-
-       /* Now interact with the user. */
-
-       strprompt("Room name", attr->QRname, ROOMNAMELEN-1);
-       attr->QRfloor = select_floor(ipc, attr->QRfloor);
-       attr->QRflags = set_room_attr(ipc, attr->QRflags, "Private room", QR_PRIVATE);
-       if (attr->QRflags & QR_PRIVATE) {
-               attr->QRflags = set_room_attr(ipc, attr->QRflags,
-                                      "Hidden room (accessible to anyone who knows the room name)",
-                                      QR_GUESSNAME);
-       }
-
-       /* if it's public, clear the privacy classes */
-       if ((attr->QRflags & QR_PRIVATE) == 0) {
-               if (attr->QRflags & QR_GUESSNAME) {
-                       attr->QRflags = attr->QRflags - QR_GUESSNAME;
-               }
-               if (attr->QRflags & QR_PASSWORDED) {
-                       attr->QRflags = attr->QRflags - QR_PASSWORDED;
-               }
-       }
-
-       /* if it's private, choose the privacy classes */
-       if ((attr->QRflags & QR_PRIVATE)
-           && ((attr->QRflags & QR_GUESSNAME) == 0)) {
-               attr->QRflags = set_room_attr(ipc, attr->QRflags,
-                                      "Accessible by entering a password",
-                                      QR_PASSWORDED);
-       }
-       if ((attr->QRflags & QR_PRIVATE)
-           && ((attr->QRflags & QR_PASSWORDED) == QR_PASSWORDED)) {
-               strprompt("Room password", attr->QRpasswd, 9);
-       }
-
-       if ((attr->QRflags & QR_PRIVATE) == QR_PRIVATE) {
-               rbump = boolprompt("Cause current users to forget room", 0);
-       }
-
-       attr->QRflags = set_room_attr(ipc, attr->QRflags,
-                                       "Preferred users only", QR_PREFONLY);
-       attr->QRflags = set_room_attr(ipc, attr->QRflags,
-                                       "Read-only room", QR_READONLY);
-       attr->QRflags2 = set_room_attr(ipc, attr->QRflags2,
-                               "Allow message deletion by anyone who can post",
-                               QR2_COLLABDEL);
-       attr->QRflags = set_room_attr(ipc, attr->QRflags,
-                                       "Permanent room", QR_PERMANENT);
-       attr->QRflags2 = set_room_attr(ipc, attr->QRflags2,
-                                                                  "Subject Required (Force "
-                                                                  "users to specify a message "
-                                   "subject)", QR2_SUBJECTREQ);
-       attr->QRflags = set_room_attr(ipc, attr->QRflags,
-                                       "Directory room", QR_DIRECTORY);
-       if (attr->QRflags & QR_DIRECTORY) {
-               strprompt("Directory name", attr->QRdirname, 14);
-               attr->QRflags =
-                   set_room_attr(ipc, attr->QRflags,
-                                               "Uploading allowed", QR_UPLOAD);
-               attr->QRflags =
-                   set_room_attr(ipc, attr->QRflags, "Downloading allowed",
-                                 QR_DOWNLOAD);
-               attr->QRflags =
-                   set_room_attr(ipc, attr->QRflags,
-                                               "Visible directory", QR_VISDIR);
-       }
-       attr->QRflags = set_room_attr(ipc, attr->QRflags,
-                                       "Network shared room", QR_NETWORK);
-       attr->QRflags2 = set_room_attr(ipc, attr->QRflags2,
-                               "Self-service list subscribe/unsubscribe",
-                               QR2_SELFLIST);
-       attr->QRflags2 = set_room_attr(ipc, attr->QRflags2,
-                               "public posting to this room via room_roomname@yourcitadel.org",
-                               QR2_SMTP_PUBLIC);
-       attr->QRflags2 = set_room_attr(ipc, attr->QRflags2,
-                               "moderated mailinglist",
-                               QR2_MODERATED);
-       attr->QRflags = set_room_attr(ipc, attr->QRflags,
-                              "Automatically make all messages anonymous",
-                              QR_ANONONLY);
-       if ((attr->QRflags & QR_ANONONLY) == 0) {
-               attr->QRflags = set_room_attr(ipc, attr->QRflags,
-                                      "Ask users whether to make messages anonymous",
-                                      QR_ANONOPT);
-       }
-       attr->QRorder = intprompt("Listing order", attr->QRorder, 0, 127);
-
-       /* Ask about the room aide */
-       do {
-               strprompt("Room aide (or 'none')", raide, 29);
-               if (!strcasecmp(raide, "none")) {
-                       strcpy(raide, "");
-                       break;
-               } else {
-                       r = CtdlIPCQueryUsername(ipc, raide, buf);
-                       if (r / 100 != 2)
-                               scr_printf("%s\n", buf);
-               }
-       } while (r / 100 != 2);
-
-       /* Angels and demons dancing in my head... */
-       do {
-               snprintf(buf, sizeof buf, "%d", attr->QRep.expire_mode);
-               strprompt("Message expire policy (? for list)", buf, 1);
-               if (buf[0] == '?') {
-                       scr_printf("\n"
-                               "0. Use the default for this floor\n"
-                               "1. Never automatically expire messages\n"
-                               "2. Expire by message count\n"
-                               "3. Expire by message age\n");
-               }
-       } while ((buf[0] < 48) || (buf[0] > 51));
-       attr->QRep.expire_mode = buf[0] - 48;
-
-       /* ...lunatics and monsters underneath my bed */
-       if (attr->QRep.expire_mode == 2) {
-               snprintf(buf, sizeof buf, "%d", attr->QRep.expire_value);
-               strprompt("Keep how many messages online?", buf, 10);
-               attr->QRep.expire_value = atol(buf);
-       }
-
-       if (attr->QRep.expire_mode == 3) {
-               snprintf(buf, sizeof buf, "%d", attr->QRep.expire_value);
-               strprompt("Keep messages for how many days?", buf, 10);
-               attr->QRep.expire_value = atol(buf);
-       }
-
-       /* Give 'em a chance to change their minds */
-       scr_printf("Save changes (y/n)? ");
-
-       if (yesno() == 1) {
-               r = CtdlIPCSetRoomAide(ipc, raide, buf);
-               if (r / 100 != 2) {
-                       scr_printf("%s\n", buf);
-               }
-
-               r = CtdlIPCSetMessageExpirationPolicy(ipc, 0, eptr, buf);
-               if (r / 100 != 2) {
-                       scr_printf("%s\n", buf);
-               }
-
-               r = CtdlIPCSetRoomAttributes(ipc, rbump, attr, buf);
-               scr_printf("%s\n", buf);
-               strncpy(buf, attr->QRname, ROOMNAMELEN);
-               free(attr);
-               if (r / 100 == 2)
-                       dotgoto(ipc, buf, 2, 0);
-       }
-       else free(attr);
-}
-
-
-/*
- * un-goto the previous room, or a specified room
- */
-void dotungoto(CtdlIPC *ipc, char *towhere)
-  {
-    /* Find this 'towhere' room in the list ungoto from this room to
-       that at the messagepointer position in that room in our ungoto list.
-       I suppose I could be a real dick and just ungoto that many places
-       in our list. */
-    int found = -1;
-    int lp;
-       char buf[SIZ];
-       struct ctdlipcroom *rret = NULL;        /* ignored */
-       int r;
-
-       if (uglistsize == 0)
-      {
-               scr_printf("No rooms to ungoto.\n");
-               return;
-      }
-       if (towhere == NULL)
-      {
-               scr_printf("Must specify a room to ungoto.\n");
-               return;
-      }
-       if (IsEmptyStr(towhere))
-      {
-               scr_printf("Must specify a room to ungoto.\n");
-               return;
-      }
-    for (lp = uglistsize-1; lp >= 0; lp--)
-      {
-        if (strcasecmp(towhere, uglist[lp]) == 0)
-          {
-            found = lp;
-            break;
-          }
-      }
-    if (found == -1)
-      {
-               scr_printf("Room: %s not in ungoto list.\n", towhere);
-       return;
-      }
-
-       r = CtdlIPCGotoRoom(ipc, uglist[found], "", &rret, buf);
-       if (rret) free(rret);   /* ignored */
-       if (r / 100 != 2) {
-               scr_printf("%s\n", buf);
-               return;
-       }
-       r = CtdlIPCSetLastRead(ipc, uglistlsn[found] ? uglistlsn[found] : 1, buf);
-       if (r / 100 != 2) {
-               scr_printf("%s\n", buf);
-       }
-       safestrncpy(buf, uglist[found], sizeof(buf));
-    /* we queue ungoto information here, because we're not really
-       ungotoing, we're really going to a random spot in some arbitrary
-       room list. */
-       dotgoto(ipc, buf, 0, 0);
-  }
-
-void ungoto(CtdlIPC *ipc)
-{
-       char buf[SIZ];
-       struct ctdlipcroom *rret = NULL;        /* ignored */
-       int r;
-
-       if (uglistsize == 0)
-               return;
-
-       r = CtdlIPCGotoRoom(ipc, uglist[uglistsize-1], "", &rret, buf);
-       if (rret) free(rret);   /* ignored */
-       if (r / 100 != 2) {
-               scr_printf("%s\n", buf);
-               return;
-       }
-       r = CtdlIPCSetLastRead(ipc, uglistlsn[uglistsize-1] ? uglistlsn[uglistsize-1] : 1, buf);
-       if (r / 100 != 2) {
-               scr_printf("%s\n", buf);
-       }
-       safestrncpy(buf, uglist[uglistsize-1], sizeof(buf));
-       uglistsize--;
-       free(uglist[uglistsize]);
-       /* Don't queue ungoto info or we end up in a loop */
-       dotgoto(ipc, buf, 0, 1);
-}
-
-
-/*
- * saves filelen bytes from file at pathname
- */
-int save_buffer(void *file, size_t filelen, const char *pathname)
-{
-       size_t block = 0;
-       size_t bytes_written = 0;
-       FILE *fp;
-
-       fp = fopen(pathname, "w");
-       if (!fp) {
-               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 Aide)\n"
-                       "<5>Personal room (accessible to you only)\n"
-                       "Enter room type: "
-               );
-               do {
-                       b = inkey();
-               } while (((b < '1') || (b > '5')) && (b != '?'));
-               if (b == '?') {
-                       scr_printf("?\n");
-                       formout(ipc, "roomaccess");
-               }
-       } while ((b < '1') || (b > '5'));
-       b -= '0';                       /* Portable */
-       scr_printf("%d\n", b);
-       new_room_type = b - 1;
-       if (new_room_type == 2) {
-               newprompt("Enter a room password: ", new_room_pass, 9);
-               for (a = 0; !IsEmptyStr(&new_room_pass[a]); ++a)
-                       if (new_room_pass[a] == '|')
-                               new_room_pass[a] = '_';
-       } else {
-               strcpy(new_room_pass, "");
-       }
-
-       scr_printf("\042%s\042, a", new_room_name);
-       if (b == 1)
-               scr_printf(" public room.");
-       if (b == 2)
-               scr_printf(" hidden room.");
-       if (b == 3)
-               scr_printf(" passworded room, password: %s", new_room_pass);
-       if (b == 4)
-               scr_printf("n invitation-only room.");
-       if (b == 5)
-               scr_printf(" personal room.");
-       scr_printf("\nInstall it? (y/n) : ");
-       if (yesno() == 0) {
-               return;
-       }
-
-       r = CtdlIPCCreateRoom(ipc, 1, new_room_name, new_room_type,
-                             new_room_pass, new_room_floor, buf);
-       if (r / 100 != 2) {
-               scr_printf("%s\n", buf);
-               return;
-       }
-
-       /* command succeeded... now GO to the new room! */
-       dotgoto(ipc, new_room_name, 0, 0);
-}
-
-
-
-void readinfo(CtdlIPC *ipc)
-{                              /* read info file for current room */
-       char buf[SIZ];
-       char raide[64];
-       int r;                  /* IPC response code */
-       char *text = NULL;
-
-       /* Name of currernt room aide */
-       r = CtdlIPCGetRoomAide(ipc, buf);
-       if (r / 100 == 2)
-               safestrncpy(raide, buf, sizeof raide);
-       else
-               strcpy(raide, "");
-
-       if (!IsEmptyStr(raide))
-               scr_printf("Room aide is %s.\n\n", raide);
-
-       r = CtdlIPCRoomInfo(ipc, &text, buf);
-       if (r / 100 != 1)
-               return;
-
-       if (text) {
-               fmout(screenwidth, NULL, text, NULL, 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/citadel/textclient/rooms.h b/citadel/textclient/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/citadel/textclient/routines.c b/citadel/textclient/routines.c
deleted file mode 100644 (file)
index f7261ee..0000000
+++ /dev/null
@@ -1,626 +0,0 @@
-/*
- * Client-side support functions.
- *
- * 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.
- */
-
-#include "sysdep.h"
-#include <stdlib.h>
-#include <unistd.h>
-#include <fcntl.h>
-#include <stdio.h>
-#include <ctype.h>
-#include <string.h>
-#include <sys/types.h>
-#include <sys/ioctl.h>
-#include <pwd.h>
-#include <signal.h>
-#include <dirent.h>
-#include <errno.h>
-
-#if TIME_WITH_SYS_TIME
-# include <sys/time.h>
-# include <time.h>
-#else
-# if HAVE_SYS_TIME_H
-#  include <sys/time.h>
-# else
-#  include <time.h>
-# endif
-#endif
-
-#ifdef HAVE_LIMITS_H
-#include <limits.h>
-#endif
-#ifdef HAVE_UTMP_H
-#include <utmp.h>
-#endif
-#ifdef HAVE_UTMPX_H
-#include <utmpx.h>
-#endif
-
-#include <libcitadel.h>
-#include "citadel.h"
-#include "citadel_ipc.h"
-#include "screen.h"
-
-#ifndef HAVE_GETUTLINE
-struct utmp *getutline(struct utmp *ut);
-#endif
-
-#define ROUTINES_C
-
-#include "citadel.h"
-#include "routines.h"
-#include "commands.h"
-#include "citadel_decls.h"
-#include "routines2.h"
-#include "tuiconfig.h"
-
-#define IFAIDE if(axlevel>=AxAideU)
-#define IFNAIDE if (axlevel<AxAideU)
-
-extern unsigned userflags;
-//extern char *axdefs[8];
-extern char sigcaught;
-extern char rc_floor_mode;
-extern int rc_ansi_color;
-extern int rc_prompt_control;
-
-/* Destructive backspace */
-void back(int spaces) {
-       int a;
-       for (a=0; a<spaces; ++a) {
-               scr_putc(8);
-               scr_putc(32);
-               scr_putc(8);
-       }
-}
-
-/*
- * Edit or delete a user (cmd=25 to edit/create, 96 to delete)
- */
-void edituser(CtdlIPC *ipc, int cmd)
-{
-       char buf[SIZ];
-       char who[USERNAME_SIZE];
-       char newname[USERNAME_SIZE];
-       struct ctdluser *user = NULL;
-       int newnow = 0;
-       int r;                          /* IPC response code */
-       int change_name = 0;
-
-       strcpy(newname, "");
-
-       newprompt("User name: ", who, 29);
-       while ((r = CtdlIPCAideGetUserParameters(ipc, who, &user, buf)) / 100 != 2) {
-               scr_printf("%s\n", buf);
-               if (cmd == 25) {
-                       scr_printf("Do you want to create this user? ");
-                       if (yesno()) {
-                               r = CtdlIPCCreateUser(ipc, who, 0, buf);
-                               if (r / 100 == 2) {
-                                       newnow = 1;
-                                       continue;
-                               }
-                               scr_printf("%s\n", buf);
-                       }
-               }
-               free(user);
-               return;
-       }
-
-       if (cmd == 25) {
-               val_user(ipc, user->fullname, 0); /* Display registration */
-
-               if (!newnow) {
-                       change_name = 1;
-                       while (change_name == 1) {
-                               if (boolprompt("Change name", 0)) {
-                                       strprompt("New name", newname, USERNAME_SIZE-1);
-                                       r = CtdlIPCRenameUser(ipc, user->fullname, newname, buf);
-                                       if (r / 100 != 2) {
-                                               scr_printf("%s\n", buf);
-                                       }
-                                       else {
-                                               strcpy(user->fullname, newname);
-                                               change_name = 0;
-                                       }
-                               }
-                               else {
-                                       change_name = 0;
-                               }
-                       }
-               }
-
-               if (newnow || boolprompt("Change password", 0)) {
-                       strprompt("Password", user->password, -19);
-               }
-       
-               user->axlevel = intprompt("Access level", user->axlevel, 0, 6);
-               if (boolprompt("Permission to send Internet mail", (user->flags & US_INTERNET)))
-                       user->flags |= US_INTERNET;
-               else
-                       user->flags &= ~US_INTERNET;
-               if (boolprompt("Ask user to register again", !(user->flags & US_REGIS)))
-                       user->flags &= ~US_REGIS;
-               else
-                       user->flags |= US_REGIS;
-               user->timescalled = intprompt("Times called",
-                               user->timescalled, 0, INT_MAX);
-               user->posted = intprompt("Messages posted",
-                                       user->posted, 0, INT_MAX);
-               user->lastcall = boolprompt("Set last call to now", 0) ?
-                                       time(NULL) : user->lastcall;
-               user->USuserpurge = intprompt("Purge time (in days, 0 for system default",
-                               user->USuserpurge, 0, INT_MAX);
-       }
-
-       if (cmd == 96) {
-               scr_printf("Do you want to delete this user? ");
-               if (!yesno()) {
-                       free(user);
-                       return;
-               }
-               user->axlevel = AxDeleted;
-       }
-
-       r = CtdlIPCAideSetUserParameters(ipc, user, buf);
-       if (r / 100 != 2) {
-               scr_printf("%s\n", buf);
-       }
-       free(user);
-}
-
-
-/* Display a prompt and flip a bit based on whether the user answers
- * yes or no.  Yes=1 and No=0, unless 'backwards' is set to a nonzero value
- * in which case No=1 and Yes=0.
- */
-int set_attr(CtdlIPC *ipc, unsigned int sval, char *prompt, unsigned int sbit, int backwards)
-{
-       int a;
-       int temp;
-
-       temp = sval;
-       color(DIM_WHITE);
-       scr_printf("%50s ", prompt);
-       color(DIM_MAGENTA);
-       scr_printf("[");
-       color(BRIGHT_MAGENTA);
-
-       if (backwards) {
-               scr_printf("%3s", ((temp&sbit) ? "No":"Yes"));
-       }
-       else {
-               scr_printf("%3s", ((temp&sbit) ? "Yes":"No"));
-       }
-
-       color(DIM_MAGENTA);
-       scr_printf("]? ");
-       color(BRIGHT_CYAN);
-       a = (temp & sbit);
-       if (a != 0) a = 1;
-       if (backwards) a = 1 - a;
-       a = yesno_d(a);
-       if (backwards) a = 1 - a;
-       color(DIM_WHITE);
-       temp = (temp|sbit);
-       if (!a) temp = (temp^sbit);
-       return(temp);
-}
-
-/*
- * modes are:  0 - .EC command, 1 - .EC for new user,
- *             2 - toggle Xpert mode  3 - toggle floor mode
- */
-void enter_config(CtdlIPC *ipc, int mode)
-{
-       char buf[SIZ];
-       struct ctdluser *user = NULL;
-       int r;                          /* IPC response code */
-
-       r = CtdlIPCGetConfig(ipc, &user, buf);
-       if (r / 100 != 2) {
-               scr_printf("%s\n", buf);
-               free(user);
-               return;
-       }
-
-       if (mode == 0 || mode == 1) {
-
-               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]);
-       }
-
-}
-
-
-#ifndef HAVE_STRERROR
-/*
- * replacement strerror() for systems that don't have it
- */
-char *strerror(int e)
-{
-       static char buf[128];
-
-       snprintf(buf, sizeof buf, "errno = %d",e);
-       return(buf);
-}
-#endif
-
-
-void progress(CtdlIPC* ipc, unsigned long curr, unsigned long cmax)
-{
-       static char dots[] =
-               "**************************************************";
-       char dots_printed[51];
-       char fmt[42];
-       unsigned long a;
-
-       if (curr >= cmax) {
-               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)
-{
-#ifndef HAVE_UTMP_H
-       char buf[SIZ];
-       FILE *who;
-       int a,b;
-
-       who = (FILE *)popen("who am i","r");
-       if (who==NULL) {
-               strcpy(hbuf, ipc->ServInfo.fqdn);
-               return; 
-       }
-       fgets(buf,sizeof buf,who);
-       pclose(who);
-
-       b = 0;
-       for (a=0; !IsEmptyStr(&buf[a]); ++a) {
-               if ((buf[a]=='(')||(buf[a]==')')) ++b;
-       }
-       if (b<2) {
-               strcpy(hbuf, ipc->ServInfo.fqdn);
-               return;
-       }
-
-       for (a=0; a<strlen(buf); ++a) {
-               if (buf[a]=='(') {
-                       strcpy(buf,&buf[a+1]);
-               }
-       }
-       for (a=0; a<strlen(buf); ++a) {
-               if (buf[a]==')') buf[a] = 0;
-       }
-
-       if (IsEmptyStr(buf)) strcpy(hbuf, ipc->ServInfo.fqdn);
-       else strncpy(hbuf,buf,24);
-#else
-       char *tty = ttyname(0);
-#ifdef HAVE_GETUTXLINE
-       struct utmpx ut, *put;
-#else
-       struct utmp ut, *put;
-#endif
-
-       if (tty == NULL) {
-           fail:
-               safestrncpy(hbuf, ipc->ServInfo.fqdn, 24);
-               return;
-       }
-
-       if (strncmp(tty, "/dev/", 5))
-               goto fail;
-
-       safestrncpy(ut.ut_line, &tty[5], sizeof ut.ut_line);
-
-#ifdef HAVE_GETUTXLINE /* Solaris uses this */
-       if ((put = getutxline(&ut)) == NULL)
-#else
-       if ((put = getutline(&ut)) == NULL)
-#endif
-               goto fail;
-
-#if defined(HAVE_UT_TYPE) || defined(HAVE_GETUTXLINE)
-       if (put->ut_type == USER_PROCESS) {
-#endif
-#if defined(HAVE_UT_HOST) || defined(HAVE_GETUTXLINE)
-               if (*put->ut_host)
-                       safestrncpy(hbuf, put->ut_host, 24);
-               else
-#endif
-                       safestrncpy(hbuf, put->ut_line, 24);
-#if defined(HAVE_UT_TYPE) || defined(HAVE_GETUTXLINE)
-       }
-       else goto fail;
-#endif
-#endif /* HAVE_UTMP_H */
-}
-
-/*
- * miscellaneous server commands (testing, etc.)
- */
-void misc_server_cmd(CtdlIPC *ipc, char *cmd) {
-       char buf[SIZ];
-
-       CtdlIPC_chat_send(ipc, cmd);
-       CtdlIPC_chat_recv(ipc, buf);
-       scr_printf("%s\n",buf);
-       if (buf[0]=='1') {
-               set_keepalives(KA_HALF);
-               while (CtdlIPC_chat_recv(ipc, buf), strcmp(buf,"000")) {
-                       scr_printf("%s\n",buf);
-               }
-               set_keepalives(KA_YES);
-               return;
-       }
-       if (buf[0]=='4') {
-               do {
-                       newprompt("> ",buf,255);
-                       CtdlIPC_chat_send(ipc, buf);
-               } while(strcmp(buf,"000"));
-               return;
-       }
-}
-
-
-/*
- * compute the checksum of a file
- */
-int file_checksum(char *filename)
-{
-       int cksum = 0;
-       int ch;
-       FILE *fp;
-
-       fp = fopen(filename,"r");
-       if (fp == NULL) return(0);
-
-       /* yes, this algorithm may allow cksum to overflow, but that's ok
-        * as long as it overflows consistently, which it will.
-        */
-       while (ch=getc(fp), ch>=0) {
-               cksum = (cksum + ch);
-       }
-
-       fclose(fp);
-       return(cksum);
-}
-
-/*
- * nuke a directory and its contents
- */
-int nukedir(char *dirname)
-{
-       DIR *dp;
-       struct dirent *d;
-       char filename[SIZ];
-
-       dp = opendir(dirname);
-       if (dp == NULL) {
-               return(errno);
-       }
-
-       while (d = readdir(dp), d != NULL) {
-               snprintf(filename, sizeof filename, "%s/%s",
-                       dirname, d->d_name);
-               unlink(filename);
-       }
-
-       closedir(dp);
-       return(rmdir(dirname));
-}
diff --git a/citadel/textclient/routines.h b/citadel/textclient/routines.h
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/citadel/textclient/routines2.c b/citadel/textclient/routines2.c
deleted file mode 100644 (file)
index afe38db..0000000
+++ /dev/null
@@ -1,628 +0,0 @@
-/*
- * More client-side support functions.
- * Unlike routines.c, some of these DO use global variables.
- *
- * 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.
- */
-
-#include <stdlib.h>
-#include <unistd.h>
-#include <sys/types.h>
-#include <sys/stat.h>
-#include <fcntl.h>
-#include <stdio.h>
-#include <ctype.h>
-#include <string.h>
-#include <limits.h>
-
-#if TIME_WITH_SYS_TIME
-# include <sys/time.h>
-# include <time.h>
-#else
-# if HAVE_SYS_TIME_H
-#  include <sys/time.h>
-# else
-#  include <time.h>
-# endif
-#endif
-
-#include <signal.h>
-#include <pwd.h>
-#include <errno.h>
-#include <stdarg.h>
-#include <libcitadel.h>
-#include "sysdep.h"
-#include "citadel.h"
-#include "citadel_ipc.h"
-#include "citadel_decls.h"
-#include "routines2.h"
-#include "routines.h"
-#include "commands.h"
-#include "messages.h"
-#ifndef HAVE_SNPRINTF
-#include "snprintf.h"
-#endif
-#include "screen.h"
-
-/* work around solaris include files */
-#ifdef reg
-#undef reg
-#endif
-
-extern char temp[];
-extern char tempdir[];
-extern char *axdefs[8];
-extern long highest_msg_read;
-extern long maxmsgnum;
-extern unsigned room_flags;
-extern int screenwidth;
-
-
-/*
-int eopen(char *name, int mode)
-{
-       int ret;
-       ret = open(name, mode);
-       if (ret < 0) {
-               scr_printf("Cannot open file '%s', mode=%d, errno=%d\n",
-                       name, mode, errno);
-               interr(errno);
-       }
-       return (ret);
-}
-*/
-
-
-int room_prompt(unsigned int qrflags)
-{                              /* return proper room prompt character */
-       int a;
-       a = '>';
-       if (qrflags & QR_DIRECTORY)
-               a = ']';
-       if ((a == ']') && (qrflags & QR_NETWORK))
-               a = '}';
-       if ((a == '>') && (qrflags & QR_NETWORK))
-               a = ')';
-       return (a);
-}
-
-void entregis(CtdlIPC *ipc)
-{                              /* register with name and address */
-
-       char buf[SIZ];
-       char tmpname[30];
-       char tmpaddr[25];
-       char tmpcity[15];
-       char tmpstate[3];
-       char tmpzip[11];
-       char tmpphone[15];
-       char tmpemail[SIZ];
-       char tmpcountry[32];
-       char diruser[256];
-       char dirnode[256];
-       char holdemail[SIZ];
-       char *reg = NULL;
-       int ok = 0;
-       int r;                          /* IPC response code */
-
-       strcpy(tmpname, "");
-       strcpy(tmpaddr, "");
-       strcpy(tmpcity, "");
-       strcpy(tmpstate, "");
-       strcpy(tmpzip, "");
-       strcpy(tmpphone, "");
-       strcpy(tmpemail, "");
-       strcpy(tmpcountry, "");
-
-       r = CtdlIPCGetUserRegistration(ipc, NULL, &reg, buf);
-       if (r / 100 == 1) {
-               int a = 0;
-
-               while (reg && !IsEmptyStr(reg)) {
-
-                       extract_token(buf, reg, 0, '\n', sizeof buf);
-                       remove_token(reg, 0, '\n');
-
-                       if (a == 2)
-                               safestrncpy(tmpname, buf, sizeof tmpname);
-                       else if (a == 3)
-                               safestrncpy(tmpaddr, buf, sizeof tmpaddr);
-                       else if (a == 4)
-                               safestrncpy(tmpcity, buf, sizeof tmpcity);
-                       else if (a == 5)
-                               safestrncpy(tmpstate, buf, sizeof tmpstate);
-                       else if (a == 6)
-                               safestrncpy(tmpzip, buf, sizeof tmpzip);
-                       else if (a == 7)
-                               safestrncpy(tmpphone, buf, sizeof tmpphone);
-                       else if (a == 9)
-                               safestrncpy(tmpemail, buf, sizeof tmpemail);
-                       else if (a == 10)
-                               safestrncpy(tmpcountry, buf, sizeof tmpcountry);
-                       ++a;
-               }
-       }
-       strprompt("REAL name", tmpname, 29);
-       strprompt("Address", tmpaddr, 24);
-       strprompt("City/town", tmpcity, 14);
-       strprompt("State/province", tmpstate, 2);
-       strprompt("ZIP/Postal Code", tmpzip, 10);
-       strprompt("Country", tmpcountry, 31);
-       strprompt("Telephone number", tmpphone, 14);
-
-       do {
-               ok = 1;
-               safestrncpy(holdemail, tmpemail, sizeof holdemail);
-               strprompt("Email address", tmpemail, 31);
-               r = CtdlIPCDirectoryLookup(ipc, tmpemail, buf);
-               if (r / 100 == 2) {
-                       extract_token(diruser, buf, 0, '@', sizeof diruser);
-                       extract_token(dirnode, buf, 1, '@', sizeof dirnode);
-                       striplt(diruser);
-                       striplt(dirnode);
-                       if ((strcasecmp(diruser, fullname))
-                          || (strcasecmp(dirnode, ipc->ServInfo.nodename))) {
-                               scr_printf(
-                                       "\nYou can't use %s as your address.\n",
-                                       tmpemail);
-                               scr_printf(
-                                       "It is already in use by %s @ %s.\n",
-                                       diruser, dirnode);
-                               ok = 0;
-                               safestrncpy(tmpemail, holdemail, sizeof tmpemail);
-                       }
-               }
-       } while (ok == 0);
-
-       /* now send the registration info back to the server */
-       reg = (char *)realloc(reg, SIZ);
-       if (reg) {
-               sprintf(reg, "%s\n%s\n%s\n%s\n%s\n%s\n%s\n%s\n",
-                       tmpname, tmpaddr, tmpcity, tmpstate,
-                       tmpzip, tmpphone, tmpemail, tmpcountry);
-               r = CtdlIPCSetRegistration(ipc, reg, buf);
-               if (r / 100 != 4)
-                       scr_printf("%s\n", buf);
-               free(reg);
-       }
-       scr_printf("\n");
-}
-
-void updatels(CtdlIPC *ipc)
-{                              /* make all messages old in current room */
-       char buf[256];
-       int r;                          /* IPC response code */
-
-       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);
-}
-
-
-void validate(CtdlIPC *ipc)
-{                              /* validate new users */
-       char cmd[256];
-       char buf[256];
-       int finished = 0;
-       int r;                          /* IPC response code */
-
-       do {
-               r = CtdlIPCNextUnvalidatedUser(ipc, cmd);
-               if (r / 100 != 3)
-                       finished = 1;
-               if (r / 100 == 2)
-                       scr_printf("%s\n", cmd);
-               if (r / 100 == 3) {
-                       extract_token(buf, cmd, 0, '|', sizeof buf);
-                       if (val_user(ipc, buf, 1) != 0) finished = 1;
-               }
-       } while (finished == 0);
-}
-
-void subshell(void)
-{
-       int a, b;
-
-       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/citadel/textclient/routines2.h b/citadel/textclient/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/citadel/textclient/screen.c b/citadel/textclient/screen.c
deleted file mode 100644 (file)
index 6bcc4b5..0000000
+++ /dev/null
@@ -1,276 +0,0 @@
-/*
- * Screen output handling
- *
- * 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.
- */
-
-
-#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"
-#ifndef HAVE_SNPRINTF
-#include "snprintf.h"
-#endif
-#include <libcitadel.h>
-#include "citadel.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;
-       register 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/citadel/textclient/screen.h b/citadel/textclient/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/citadel/textclient/tuiconfig.c b/citadel/textclient/tuiconfig.c
deleted file mode 100644 (file)
index 3ec7783..0000000
+++ /dev/null
@@ -1,1254 +0,0 @@
-/*
- * Configuration screens that are part of the text mode client.
- *
- * 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.
- */
-
-#include <stdlib.h>
-#include <unistd.h>
-#include <sys/types.h>
-#include <sys/stat.h>
-#include <fcntl.h>
-#include <stdio.h>
-#include <ctype.h>
-#include <string.h>
-#include <limits.h>
-
-#if TIME_WITH_SYS_TIME
-# include <sys/time.h>
-# include <time.h>
-#else
-# if HAVE_SYS_TIME_H
-#  include <sys/time.h>
-# else
-#  include <time.h>
-# endif
-#endif
-
-#include <signal.h>
-#include <pwd.h>
-#include <errno.h>
-#include <stdarg.h>
-#include <libcitadel.h>
-#include "sysdep.h"
-#include "citadel.h"
-#include "citadel_ipc.h"
-#include "citadel_decls.h"
-#include "tuiconfig.h"
-#include "messages.h"
-#include "routines.h"
-#include "commands.h"
-#ifndef HAVE_SNPRINTF
-#include "snprintf.h"
-#endif
-#include "screen.h"
-
-/* work around solaris include files */
-#ifdef reg
-#undef reg
-#endif
-
-extern char temp[];
-extern char tempdir[];
-extern char *axdefs[8];
-extern long highest_msg_read;
-extern long maxmsgnum;
-extern unsigned room_flags;
-extern int screenwidth;
-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 aide privs to a user who creates a private room",
-               atoi(&sc[4][0]))));
-
-       snprintf(sc[8], sizeof sc[8], "%d", (boolprompt(
-               "Automatically move problem user messages to twit room",
-               atoi(&sc[8][0]))));
-
-       strprompt("Name of twit room", &sc[9][0], ROOMNAMELEN);
-       snprintf(sc[11], sizeof sc[11], "%d", (boolprompt(
-               "Restrict Internet mail to only those with that privilege",
-               atoi(&sc[11][0]))));
-       snprintf(sc[26], sizeof sc[26], "%d", (boolprompt(
-               "Allow Aides to Zap (forget) rooms",
-               atoi(&sc[26][0]))));
-
-       if (!IsEmptyStr(&sc[18][0])) {
-               logpages = 1;
-       }
-       else {
-               logpages = 0;
-       }
-       logpages = boolprompt("Log all instant messages", logpages);
-       if (logpages) {
-               strprompt("Name of logging room", &sc[18][0], ROOMNAMELEN);
-       }
-       else {
-               sc[18][0] = 0;
-       }
-
-       /* Commented out because this setting isn't really appropriate to
-        * change while the server is running.
-        *
-        * snprintf(sc[52], sizeof sc[52], "%d", (boolprompt(
-        *      "Use system authentication",
-        *      atoi(&sc[52][0]))));
-        */
-
-       /* Server tuning */
-
-       strprompt("Server connection idle timeout (in seconds)", &sc[5][0], 4);
-       strprompt("Maximum concurrent sessions", &sc[14][0], 4);
-       strprompt("Maximum message length", &sc[20][0], 20);
-       strprompt("Minimum number of worker threads", &sc[21][0], 3);
-       strprompt("Maximum number of worker threads", &sc[22][0], 3);
-       snprintf(sc[43], sizeof sc[43], "%d", (boolprompt(
-               "Automatically delete committed database logs",
-               atoi(&sc[43][0]))));
-
-       strprompt("Server IP address (* for 'any')", &sc[37][0], 15);
-       strprompt("POP3 server port (-1 to disable)", &sc[23][0], 5);
-       strprompt("POP3S server port (-1 to disable)", &sc[40][0], 5);
-       strprompt("IMAP server port (-1 to disable)", &sc[27][0], 5);
-       strprompt("IMAPS server port (-1 to disable)", &sc[39][0], 5);
-       strprompt("SMTP MTA server port (-1 to disable)", &sc[24][0], 5);
-       strprompt("SMTP MSA server port (-1 to disable)", &sc[38][0], 5);
-       strprompt("SMTPS server port (-1 to disable)", &sc[41][0], 5);
-       strprompt("Postfix TCP Dictionary Port server port (-1 to disable)", &sc[50][0], 5);
-       strprompt("ManageSieve server port (-1 to disable)", &sc[51][0], 5);
-
-       strprompt("XMPP (Jabber) client to server port (-1 to disable)", &sc[62][0], 5);
-       /* No prompt because we don't implement this service yet, it's just a placeholder */
-       /* strprompt("XMPP (Jabber) server to server port (-1 to disable)", &sc[63][0], 5); */
-
-       /* This logic flips the question around, because it's one of those
-        * situations where 0=yes and 1=no
-        */
-       a = atoi(sc[25]);
-       a = (a ? 0 : 1);
-       a = boolprompt("Correct forged From: lines during authenticated SMTP",
-               a);
-       a = (a ? 0 : 1);
-       snprintf(sc[25], sizeof sc[25], "%d", a);
-
-       snprintf(sc[66], sizeof sc[66], "%d", (boolprompt(
-               "Flag messages as spam instead of rejecting",
-               atoi(&sc[66][0]))));
-
-       /* This logic flips the question around, because it's one of those
-        * situations where 0=yes and 1=no
-        */
-       a = atoi(sc[61]);
-       a = (a ? 0 : 1);
-       a = boolprompt("Force IMAP posts in public rooms to be from the user who submitted them", a);
-       a = (a ? 0 : 1);
-       snprintf(sc[61], sizeof sc[61], "%d", a);
-
-       snprintf(sc[45], sizeof sc[45], "%d", (boolprompt(
-               "Allow unauthenticated SMTP clients to spoof my domains",
-               atoi(&sc[45][0]))));
-       snprintf(sc[57], sizeof sc[57], "%d", (boolprompt(
-               "Perform RBL checks at greeting instead of after RCPT",
-               atoi(&sc[57][0]))));
-       snprintf(sc[44], sizeof sc[44], "%d", (boolprompt(
-               "Instantly expunge deleted IMAP messages",
-               atoi(&sc[44][0]))));
-
-       /* LDAP settings */
-       if (ipc->ServInfo.supports_ldap) {
-               a = strlen(&sc[32][0]);
-               a = (a ? 1 : 0);        /* Set only to 1 or 0 */
-               a = boolprompt("Do you want to configure LDAP authentication?", a);
-               if (a) {
-                       strprompt("Host name of LDAP server",
-                               &sc[32][0], 127);
-                       strprompt("Port number of LDAP service",
-                               &sc[33][0], 5);
-                       strprompt("Base DN", &sc[34][0], 255);
-                       strprompt("Bind DN (or blank for anonymous bind)", &sc[35][0], 255);
-                       strprompt("Password for bind DN (or blank for anonymous bind)", &sc[36][0], 255);
-               }
-               else {
-                       strcpy(&sc[32][0], "");
-               }
-       }
-
-       /* Expiry settings */
-       strprompt("Default user purge time (days)", &sc[16][0], 5);
-       strprompt("Default room purge time (days)", &sc[17][0], 5);
-
-       /* Angels and demons dancing in my head... */
-       do {
-               snprintf(buf, sizeof buf, "%d", site_expirepolicy->expire_mode);
-               strprompt("System default message expire policy (? for list)",
-                         buf, 1);
-               if (buf[0] == '?') {
-                       scr_printf("\n"
-                               "1. Never automatically expire messages\n"
-                               "2. Expire by message count\n"
-                               "3. Expire by message age\n");
-               }
-       } while ((buf[0] < '1') || (buf[0] > '3'));
-       site_expirepolicy->expire_mode = buf[0] - '0';
-
-       /* ...lunatics and monsters underneath my bed */
-       if (site_expirepolicy->expire_mode == 2) {
-               snprintf(buf, sizeof buf, "%d", site_expirepolicy->expire_value);
-               strprompt("Keep how many messages online?", buf, 10);
-               site_expirepolicy->expire_value = atol(buf);
-       }
-       if (site_expirepolicy->expire_mode == 3) {
-               snprintf(buf, sizeof buf, "%d", site_expirepolicy->expire_value);
-               strprompt("Keep messages for how many days?", buf, 10);
-               site_expirepolicy->expire_value = atol(buf);
-       }
-
-       /* Media messiahs preying on my fears... */
-       do {
-               snprintf(buf, sizeof buf, "%d", mbx_expirepolicy->expire_mode);
-               strprompt("Mailbox default message expire policy (? for list)",
-                         buf, 1);
-               if (buf[0] == '?') {
-                       scr_printf("\n"
-                               "0. Go with the system default\n"
-                               "1. Never automatically expire messages\n"
-                               "2. Expire by message count\n"
-                               "3. Expire by message age\n");
-               }
-       } while ((buf[0] < '0') || (buf[0] > '3'));
-       mbx_expirepolicy->expire_mode = buf[0] - '0';
-
-       /* ...Pop culture prophets playing in my ears */
-       if (mbx_expirepolicy->expire_mode == 2) {
-               snprintf(buf, sizeof buf, "%d", mbx_expirepolicy->expire_value);
-               strprompt("Keep how many messages online?", buf, 10);
-               mbx_expirepolicy->expire_value = atol(buf);
-       }
-       if (mbx_expirepolicy->expire_mode == 3) {
-               snprintf(buf, sizeof buf, "%d", mbx_expirepolicy->expire_value);
-               strprompt("Keep messages for how many days?", buf, 10);
-               mbx_expirepolicy->expire_value = atol(buf);
-       }
-
-       strprompt("How often to run network jobs (in seconds)", &sc[28][0], 5);
-       strprompt("Default frequency to run POP3 collection (in seconds)", &sc[64][0], 5);
-       strprompt("Fastest frequency to run POP3 collection (in seconds)", &sc[65][0], 5);
-       strprompt("Hour to run purges (0-23)", &sc[31][0], 2);
-       snprintf(sc[42], sizeof sc[42], "%d", (boolprompt(
-               "Enable full text search index (warning: resource intensive)",
-               atoi(&sc[42][0]))));
-
-       snprintf(sc[46], sizeof sc[46], "%d", (boolprompt(
-               "Perform journaling of email messages",
-               atoi(&sc[46][0]))));
-       snprintf(sc[47], sizeof sc[47], "%d", (boolprompt(
-               "Perform journaling of non-email messages",
-               atoi(&sc[47][0]))));
-       if ( (atoi(&sc[46][0])) || (atoi(&sc[47][0])) ) {
-               strprompt("Email destination of journalized messages",
-                       &sc[48][0], 127);
-       }
-
-       /* Funambol push stuff */
-       int yes_funambol = 0;
-       if (strlen(sc[53]) > 0) yes_funambol = 1;
-       yes_funambol = boolprompt("Connect to an external Funambol sync server", yes_funambol);
-       if (yes_funambol) {
-               strprompt("Funambol server (blank to disable)", &sc[53][0], 63);
-               strprompt("Funambol server port", &sc[54][0], 5);
-               strprompt("Funambol sync source", &sc[55][0], 63);
-               strprompt("Funambol authentication details (user:pass in Base64)", &sc[56][0],63);
-       }
-       else {
-               sc[53][0] = 0;
-               sc[54][0] = 0;
-               sc[55][0] = 0;
-               sc[56][0] = 0;
-       }
-
-       /* External pager stuff */
-       int yes_pager = 0;
-       if (strlen(sc[60]) > 0) yes_pager = 1;
-       yes_pager = boolprompt("Configure an external pager tool", yes_pager);
-       if (yes_pager) {
-               strprompt("External pager tool", &sc[60][0], 255);
-       }
-       else {
-               sc[60][0] = 0;
-       }
-
-       /* Master user account */
-       int yes_muacct = 0;
-       if (strlen(sc[58]) > 0) yes_muacct = 1;
-       yes_muacct = boolprompt("Enable a 'master user' account", yes_muacct);
-       if (yes_muacct) {
-               strprompt("Master user name", &sc[58][0], 31);
-               strprompt("Master user password", &sc[59][0], -31);
-       }
-       else {
-               strcpy(&sc[58][0], "");
-               strcpy(&sc[59][0], "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx");
-       }
-
-       /* Save it */
-       scr_printf("Save this configuration? ");
-       if (yesno()) {
-               r = 1;
-               for (a = 0; a < NUM_CONFIGS; a++) {
-                       r += 1 + strlen(sc[a]);
-               }
-               resp = (char *)calloc(1, r);
-               if (!resp) {
-                       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> directory      (Consult the Global Address Book)\n");
-       keyopt(" <5> SpamAssassin   (Address of SpamAssassin server)\n");
-       keyopt(" <6> RBL            (domain suffix of spam hunting RBL)\n");
-       keyopt(" <7> masq domains   (Domains as which users are allowed to masquerade)\n");
-       keyopt(" <8> 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, "directory");
-                       return;
-               case 5: strcpy(buf, "spamassassin");
-                       return;
-               case 6: strcpy(buf, "rbl");
-                       return;
-               case 7: strcpy(buf, "masqdomain");
-                       return;
-               case 8: 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');
-                       ++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);
-}
-
-
-/*
- * IGnet node configuration
- */
-void do_ignet_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;
-       int r;
-
-       r = CtdlIPCGetSystemConfigByType(ipc, IGNETCFG, &listing, buf);
-       if (r / 100 == 1) while (*listing && !IsEmptyStr(listing)) {
-               extract_token(buf, listing, 0, '\n', sizeof buf);
-               remove_token(listing, 0, '\n');
-
-               ++num_recs;
-               if (num_recs == 1) recs = malloc(sizeof(char *));
-               else recs = realloc(recs, (sizeof(char *)) * num_recs);
-               recs[num_recs-1] = malloc(SIZ);
-               strcpy(recs[num_recs-1], buf);
-       }
-       if (listing) free(listing);
-
-       do {
-               scr_printf("\n");
-               color(BRIGHT_WHITE);
-               scr_printf(     "### "
-                       "   Node          "
-                       "  Secret           "
-                       "          Host or IP             "
-                       "Port#\n");
-               color(DIM_WHITE);
-               scr_printf(     "--- "
-                       "---------------- "
-                       "------------------ "
-                       "-------------------------------- "
-                       "-----\n");
-               for (i=0; i<num_recs; ++i) {
-               color(DIM_WHITE);
-               scr_printf("%3d ", i+1);
-               extract_token(buf, recs[i], 0, '|', sizeof buf);
-               color(BRIGHT_CYAN);
-               scr_printf("%-16s ", buf);
-               extract_token(buf, recs[i], 1, '|', sizeof buf);
-               color(BRIGHT_MAGENTA);
-               scr_printf("%-18s ", buf);
-               extract_token(buf, recs[i], 2, '|', sizeof buf);
-               color(BRIGHT_CYAN);
-               scr_printf("%-32s ", buf);
-               extract_token(buf, recs[i], 3, '|', sizeof buf);
-               color(BRIGHT_MAGENTA);
-               scr_printf("%-3s\n", buf);
-               color(DIM_WHITE);
-               }
-               scr_printf("\n");
-
-               ch = keymenu("", "<A>dd|<D>elete|<S>ave|<Q>uit");
-               switch(ch) {
-                       case 'a':
-                               ++num_recs;
-                               if (num_recs == 1)
-                                       recs = malloc(sizeof(char *));
-                               else recs = realloc(recs,
-                                       (sizeof(char *)) * num_recs);
-                               newprompt("Enter node name    : ", buf, 16);
-                               strcat(buf, "|");
-                               newprompt("Enter shared secret: ",
-                                       &buf[strlen(buf)], 18);
-                               strcat(buf, "|");
-                               newprompt("Enter host or IP   : ",
-                                       &buf[strlen(buf)], 32);
-                               strcat(buf, "|504");
-                               strprompt("Enter port number  : ",
-                                       &buf[strlen(buf)-3], 5);
-                               recs[num_recs-1] = strdup(buf);
-                               modified = 1;
-                               break;
-                       case 'd':
-                               i = intprompt("Delete which one",
-                                       1, 1, num_recs) - 1;
-                               free(recs[i]);
-                               --num_recs;
-                               for (j=i; j<num_recs; ++j)
-                                       recs[j] = recs[j+1];
-                               modified = 1;
-                               break;
-                       case 's':
-                               r = 1;
-                               for (i = 0; i < num_recs; ++i)
-                                       r += 1 + strlen(recs[i]);
-                               listing = (char*) calloc(1, r);
-                               if (!listing) {
-                                       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");
-                               }
-                               r = CtdlIPCSetSystemConfigByType(ipc, IGNETCFG, listing, buf);
-                               if (r / 100 != 4) {
-                                       scr_printf("%s\n", buf);
-                               } else {
-                                       scr_printf("Wrote %d records.\n", num_recs);
-                                       modified = 0;
-                               }
-                free(listing);
-                               break;
-                       case 'q':
-                               quitting = !modified || boolprompt(
-                                       "Quit without saving", 0);
-                               break;
-                       default:
-                               break;
-               }
-       } while (!quitting);
-
-       if (recs != NULL) {
-               for (i=0; i<num_recs; ++i) free(recs[i]);
-               free(recs);
-       }
-}
-
-
-/*
- * Filter list configuration
- */
-void do_filterlist_configuration(CtdlIPC *ipc)
-{
-       char buf[SIZ];
-       int num_recs = 0;
-       char **recs = NULL;
-       char ch;
-       int i, j;
-       int quitting = 0;
-       int modified = 0;
-       char *listing = NULL;
-       int r;
-
-       r = CtdlIPCGetSystemConfigByType(ipc, FILTERLIST, &listing, buf);
-       if (r / 100 == 1) while (*listing && !IsEmptyStr(listing)) {
-               extract_token(buf, listing, 0, '\n', sizeof buf);
-               remove_token(listing, 0, '\n');
-
-               ++num_recs;
-               if (num_recs == 1) recs = malloc(sizeof(char *));
-               else recs = realloc(recs, (sizeof(char *)) * num_recs);
-               recs[num_recs-1] = malloc(SIZ);
-               strcpy(recs[num_recs-1], buf);
-       }
-       if (listing) free(listing);
-
-       do {
-               scr_printf("\n");
-               color(BRIGHT_WHITE);
-               scr_printf(     "### "
-                       "         User name           "
-                       "         Room name           "
-                       "    Node name    "
-                       "\n");
-               color(DIM_WHITE);
-               scr_printf(     "--- "
-                       "---------------------------- "
-                       "---------------------------- "
-                       "---------------- "
-                       "\n");
-               for (i=0; i<num_recs; ++i) {
-               color(DIM_WHITE);
-               scr_printf("%3d ", i+1);
-               extract_token(buf, recs[i], 0, '|', sizeof buf);
-               color(BRIGHT_CYAN);
-               scr_printf("%-28s ", buf);
-               extract_token(buf, recs[i], 1, '|', sizeof buf);
-               color(BRIGHT_MAGENTA);
-               scr_printf("%-28s ", buf);
-               extract_token(buf, recs[i], 2, '|', sizeof buf);
-               color(BRIGHT_CYAN);
-               scr_printf("%-16s\n", buf);
-               extract_token(buf, recs[i], 3, '|', sizeof buf);
-               color(DIM_WHITE);
-               }
-
-               ch = keymenu("", "<A>dd|<D>elete|<S>ave|<Q>uit");
-               switch(ch) {
-                       case 'a':
-                               ++num_recs;
-                               if (num_recs == 1)
-                                       recs = malloc(sizeof(char *));
-                               else recs = realloc(recs,
-                                       (sizeof(char *)) * num_recs);
-                               newprompt("Enter user name: ", buf, 28);
-                               strcat(buf, "|");
-                               newprompt("Enter room name: ",
-                                       &buf[strlen(buf)], 28);
-                               strcat(buf, "|");
-                               newprompt("Enter node name: ",
-                                       &buf[strlen(buf)], 16);
-                               strcat(buf, "|");
-                               recs[num_recs-1] = strdup(buf);
-                               modified = 1;
-                               break;
-                       case 'd':
-                               i = intprompt("Delete which one",
-                                       1, 1, num_recs) - 1;
-                               free(recs[i]);
-                               --num_recs;
-                               for (j=i; j<num_recs; ++j)
-                                       recs[j] = recs[j+1];
-                               modified = 1;
-                               break;
-                       case 's':
-                               r = 1;
-                               for (i = 0; i < num_recs; ++i)
-                                       r += 1 + strlen(recs[i]);
-                               listing = (char*) calloc(1, r);
-                               if (!listing) {
-                                       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");
-                               }
-                               r = CtdlIPCSetSystemConfigByType(ipc, FILTERLIST, listing, buf);
-                               if (r / 100 != 4) {
-                                       scr_printf("%s\n", buf);
-                               } else {
-                                       scr_printf("Wrote %d records.\n", num_recs);
-                                       modified = 0;
-                               }
-                free(listing);
-                               break;
-                       case 'q':
-                               quitting = !modified || boolprompt(
-                                       "Quit without saving", 0);
-                               break;
-                       default:
-                               break;
-               }
-       } while (!quitting);
-
-       if (recs != NULL) {
-               for (i=0; i<num_recs; ++i) free(recs[i]);
-               free(recs);
-       }
-}
-
-
-
-
-/*
- * POP3 aggregation client configuration
- */
-void do_pop3client_configuration(CtdlIPC *ipc)
-{
-       char buf[SIZ];
-       int num_recs = 0;
-       char **recs = NULL;
-       char ch;
-       int i, j;
-       int quitting = 0;
-       int modified = 0;
-       char *listing = NULL;
-       char *other_listing = NULL;
-       int r;
-       char instr[SIZ];
-
-       r = CtdlIPCGetRoomNetworkConfig(ipc, &listing, buf);
-       if (r / 100 == 1) {
-               while(listing && !IsEmptyStr(listing)) {
-                       extract_token(buf, listing, 0, '\n', sizeof buf);
-                       remove_token(listing, 0, '\n');
-                       extract_token(instr, buf, 0, '|', sizeof instr);
-                       if (!strcasecmp(instr, "pop3client")) {
-
-                               ++num_recs;
-                               if (num_recs == 1) recs = malloc(sizeof(char *));
-                               else recs = realloc(recs, (sizeof(char *)) * num_recs);
-                               recs[num_recs-1] = malloc(SIZ);
-                               strcpy(recs[num_recs-1], buf);
-
-                       }
-               }
-       }
-       if (listing) {
-               free(listing);
-               listing = NULL;
-       }
-
-       do {
-               scr_printf("\n");
-               color(BRIGHT_WHITE);
-               scr_printf(     "### "
-                       "      Remote POP3 host       "
-                       "         User name           "
-                       "Keep on server? "
-                       "Fetching inteval"
-                       "\n");
-               color(DIM_WHITE);
-               scr_printf(     "--- "
-                       "---------------------------- "
-                       "---------------------------- "
-                       "--------------- "
-                       "---------------- "
-                       "\n");
-               for (i=0; i<num_recs; ++i) {
-               color(DIM_WHITE);
-               scr_printf("%3d ", i+1);
-
-               extract_token(buf, recs[i], 1, '|', sizeof buf);
-               color(BRIGHT_CYAN);
-               scr_printf("%-28s ", buf);
-
-               extract_token(buf, recs[i], 2, '|', sizeof buf);
-               color(BRIGHT_MAGENTA);
-               scr_printf("%-28s ", buf);
-
-               color(BRIGHT_CYAN);
-               scr_printf("%-15s ", (extract_int(recs[i], 4) ? "Yes" : "No") );
-               color(BRIGHT_MAGENTA);
-               scr_printf("%ld\n", extract_long(recs[i], 5) );
-               color(DIM_WHITE);
-               }
-
-               ch = keymenu("", "<A>dd|<D>elete|<S>ave|<Q>uit");
-               switch(ch) {
-                       case 'a':
-                               ++num_recs;
-                               if (num_recs == 1) {
-                                       recs = malloc(sizeof(char *));
-                               }
-                               else {
-                                       recs = realloc(recs, (sizeof(char *)) * num_recs);
-                               }
-                               strcpy(buf, "pop3client|");
-                               newprompt("Enter host name: ", &buf[strlen(buf)], 28);
-                               strcat(buf, "|");
-                               newprompt("Enter user name: ", &buf[strlen(buf)], 28);
-                               strcat(buf, "|");
-                               newprompt("Enter password : ", &buf[strlen(buf)], 16);
-                               strcat(buf, "|");
-                               scr_printf("Keep messages on server instead of deleting them? ");
-                               sprintf(&buf[strlen(buf)], "%d", yesno());
-                               strcat(buf, "|");
-                               newprompt("Enter interval : ", &buf[strlen(buf)], 5);
-                               strcat(buf, "|");
-                               recs[num_recs-1] = strdup(buf);
-                               modified = 1;
-                               break;
-                       case 'd':
-                               i = intprompt("Delete which one",
-                                       1, 1, num_recs) - 1;
-                               free(recs[i]);
-                               --num_recs;
-                               for (j=i; j<num_recs; ++j)
-                                       recs[j] = recs[j+1];
-                               modified = 1;
-                               break;
-                       case 's':
-                               r = 1;
-                               for (i = 0; i < num_recs; ++i) {
-                                       r += 1 + strlen(recs[i]);
-                               }
-                               listing = (char*) calloc(1, r);
-                               if (!listing) {
-                                       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/citadel/textclient/tuiconfig.h b/citadel/textclient/tuiconfig.h
deleted file mode 100644 (file)
index 7e65bc4..0000000
+++ /dev/null
@@ -1,22 +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 do_internet_configuration(CtdlIPC *ipc);
-void do_ignet_configuration(CtdlIPC *ipc);
-void network_config_management(CtdlIPC *ipc, char *entrytype, char *comment);
-void do_filterlist_configuration(CtdlIPC *ipc);
-void do_pop3client_configuration(CtdlIPC *ipc);
-void do_rssclient_configuration(CtdlIPC *ipc);
-void do_system_configuration(CtdlIPC *ipc);
-
-extern char editor_path[PATH_MAX];
-extern int enable_status_line;
index a884179541db2ed2bed371adb2b8f3f31314821a..ff624707408c15484dd1051169705489c714323c 100644 (file)
@@ -65,7 +65,6 @@ char ctdl_bin_dir[PATH_MAX]="";
 
 /* some of our files, that are needed in several places */
 char file_citadel_control[PATH_MAX]="";
-char file_citadel_rc[PATH_MAX]="";
 char file_citadel_config[PATH_MAX]="";
 char file_citadel_urlshorteners[PATH_MAX]="";
 char file_lmtp_socket[PATH_MAX]="";
@@ -233,11 +232,6 @@ void calc_dirs_n_files(int relh, int home, const char *relhome, char  *ctdldir,
                         "%scitadel.config",
                         ctdl_autoetc_dir);
        StripSlashes(file_citadel_config, 0);
-       snprintf(file_citadel_rc, 
-                        sizeof file_citadel_rc,
-                        "%scitadel.rc",
-                        ctdl_etc_dir);
-       StripSlashes(file_citadel_rc, 0);
        snprintf(file_citadel_urlshorteners, 
                         sizeof file_citadel_urlshorteners,
                         "%scitadel_urlshorteners.rc",
@@ -356,7 +350,6 @@ void calc_dirs_n_files(int relh, int home, const char *relhome, char  *ctdldir,
        DBG_PRINT(ctdl_bin_dir);
        DBG_PRINT(ctdl_utilbin_dir);
        DBG_PRINT(file_citadel_control);
-       DBG_PRINT(file_citadel_rc);
        DBG_PRINT(file_citadel_config);
        DBG_PRINT(file_lmtp_socket);
        DBG_PRINT(file_lmtp_unfiltered_socket);
diff --git a/citadel/utillib/citadel_ipc.c b/citadel/utillib/citadel_ipc.c
deleted file mode 100644 (file)
index 15e860a..0000000
+++ /dev/null
@@ -1,3341 +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.
- */
-
-#include "sysdep.h"
-#if TIME_WITH_SYS_TIME
-# include <sys/time.h>
-# include <time.h>
-#else
-# if HAVE_SYS_TIME_H
-#  include <sys/time.h>
-# else
-#  include <time.h>
-# endif
-#endif
-#include <unistd.h>
-#include <stdio.h>
-#include <sys/types.h>
-#include <string.h>
-#ifdef HAVE_MALLOC_H
-#include <malloc.h>
-#endif
-#include <stdlib.h>
-#include <ctype.h>
-#include <sys/socket.h>
-#include <arpa/inet.h>
-#include <netinet/in.h>
-#include <netdb.h>
-#include <sys/un.h>
-#include <errno.h>
-#ifdef THREADED_CLIENT
-#include <pthread.h>
-#endif
-#include <libcitadel.h>
-#include "citadel.h"
-#include "citadel_ipc.h"
-#include "citadel_decls.h"
-#include "citadel_dirs.h"
-#ifdef THREADED_CLIENT
-pthread_mutex_t rwlock;
-#endif
-
-#ifdef HAVE_OPENSSL
-static SSL_CTX *ssl_ctx;
-char arg_encrypt;
-char rc_encrypt;
-#ifdef THREADED_CLIENT
-pthread_mutex_t **Critters;                    /* Things that need locking */
-#endif /* THREADED_CLIENT */
-
-#endif /* HAVE_OPENSSL */
-
-#ifndef INADDR_NONE
-#define INADDR_NONE 0xffffffff
-#endif
-
-static void (*status_hook)(char *s) = NULL;
-
-void setCryptoStatusHook(void (*hook)(char *s)) {
-       status_hook = hook;
-}
-
-void CtdlIPC_SetNetworkStatusCallback(CtdlIPC *ipc, void (*hook)(int state)) {
-       ipc->network_status_cb = hook;
-}
-
-
-char instant_msgs = 0;
-
-
-static void serv_read(CtdlIPC *ipc, char *buf, unsigned int bytes);
-static void serv_write(CtdlIPC *ipc, const char *buf, unsigned int nbytes);
-#ifdef HAVE_OPENSSL
-static void serv_read_ssl(CtdlIPC *ipc, char *buf, unsigned int bytes);
-static void serv_write_ssl(CtdlIPC *ipc, const char *buf, unsigned int nbytes);
-static void endtls(SSL *ssl);
-#ifdef THREADED_CLIENT
-static unsigned long id_callback(void);
-#endif /* THREADED_CLIENT */
-#endif /* HAVE_OPENSSL */
-static void CtdlIPC_getline(CtdlIPC* ipc, char *buf);
-static void CtdlIPC_putline(CtdlIPC *ipc, const char *buf);
-
-
-
-const char *svn_revision(void);
-
-/*
- * Does nothing.  The server should always return 200.
- */
-int CtdlIPCNoop(CtdlIPC *ipc)
-{
-       char aaa[128];
-
-       return CtdlIPCGenericCommand(ipc, "NOOP", NULL, 0, NULL, NULL, aaa);
-}
-
-
-/*
- * Does nothing interesting.  The server should always return 200
- * along with your string.
- */
-int CtdlIPCEcho(CtdlIPC *ipc, const char *arg, char *cret)
-{
-       register int ret;
-       char *aaa;
-       
-       if (!arg) return -2;
-       if (!cret) return -2;
-
-       aaa = (char *)malloc((size_t)(strlen(arg) + 6));
-       if (!aaa) return -1;
-
-       sprintf(aaa, "ECHO %s", arg);
-       ret = CtdlIPCGenericCommand(ipc, aaa, NULL, 0, NULL, NULL, cret);
-       free(aaa);
-       return ret;
-}
-
-
-/*
- * Asks the server to close the connecction.
- * Should always return 200.
- */
-int CtdlIPCQuit(CtdlIPC *ipc)
-{
-       register int ret = 221;         /* Default to successful quit */
-       char aaa[SIZ]; 
-
-       CtdlIPC_lock(ipc);
-       if (ipc->sock > -1) {
-               CtdlIPC_putline(ipc, "QUIT");
-               CtdlIPC_getline(ipc, aaa);
-               ret = atoi(aaa);
-       }
-#ifdef HAVE_OPENSSL
-       if (ipc->ssl)
-               SSL_shutdown(ipc->ssl);
-       ipc->ssl = NULL;
-#endif
-       if (ipc->sock)
-               shutdown(ipc->sock, 2); /* Close connection; we're dead */
-       ipc->sock = -1;
-       CtdlIPC_unlock(ipc);
-       return ret;
-}
-
-
-/*
- * Asks the server to log out.  Should always return 200, even if no user
- * was logged in.  The user will not be logged in after this!
- */
-int CtdlIPCLogout(CtdlIPC *ipc)
-{
-       register int ret;
-       char aaa[SIZ];
-
-       CtdlIPC_lock(ipc);
-       CtdlIPC_putline(ipc, "LOUT");
-       CtdlIPC_getline(ipc, aaa);
-       ret = atoi(aaa);
-       CtdlIPC_unlock(ipc);
-       return ret;
-}
-
-
-/*
- * First stage of authentication - pass the username.  Returns 300 if the
- * username is able to log in, with the username correctly spelled in cret.
- * Returns various 500 error codes if the user doesn't exist, etc.
- */
-int CtdlIPCTryLogin(CtdlIPC *ipc, const char *username, char *cret)
-{
-       register int ret;
-       char *aaa;
-
-       if (!username) return -2;
-       if (!cret) return -2;
-
-       aaa = (char *)malloc((size_t)(strlen(username) + 6));
-       if (!aaa) return -1;
-
-       sprintf(aaa, "USER %s", username);
-       ret = CtdlIPCGenericCommand(ipc, aaa, NULL, 0, NULL, NULL, cret);
-       free(aaa);
-       return ret;
-}
-
-
-/*
- * Second stage of authentication - provide password.  The server returns
- * 200 and several arguments in cret relating to the user's account.
- */
-int CtdlIPCTryPassword(CtdlIPC *ipc, const char *passwd, char *cret)
-{
-       register int ret;
-       char *aaa;
-
-       if (!passwd) return -2;
-       if (!cret) return -2;
-
-       aaa = (char *)malloc((size_t)(strlen(passwd) + 6));
-       if (!aaa) return -1;
-
-       sprintf(aaa, "PASS %s", passwd);
-       ret = CtdlIPCGenericCommand(ipc, aaa, NULL, 0, NULL, NULL, cret);
-       free(aaa);
-       return ret;
-}
-
-
-/*
- * Second stage of authentication - provide password.  The server returns
- * 200 and several arguments in cret relating to the user's account.
- */
-int CtdlIPCTryApopPassword(CtdlIPC *ipc, const char *response, char *cret)
-{
-       register int ret;
-       char *aaa;
-
-       if (!response) return -2;
-       if (!cret) return -2;
-
-       aaa = (char *)malloc((size_t)(strlen(response) + 6));
-       if (!aaa) return -1;
-
-       sprintf(aaa, "PAS2 %s", response);
-       ret = CtdlIPCGenericCommand(ipc, aaa, NULL, 0, NULL, NULL, cret);
-       free(aaa);
-       return ret;
-}
-
-
-/*
- * Create a new user.  This returns 200 plus the same arguments as TryPassword
- * if selfservice is nonzero, unless there was a problem creating the account.
- * If selfservice is zero, creates a new user but does not log out the existing
- * user - intended for use by system administrators to create accounts on
- * behalf of other users.
- */
-int CtdlIPCCreateUser(CtdlIPC *ipc, const char *username, int selfservice, char *cret)
-{
-       register int ret;
-       char *aaa;
-
-       if (!username) return -2;
-       if (!cret) return -2;
-
-       aaa = (char *)malloc((size_t)(strlen(username) + 6));
-       if (!aaa) return -1;
-
-       sprintf(aaa, "%s %s", selfservice ? "NEWU" : "CREU",  username);
-       ret = CtdlIPCGenericCommand(ipc, aaa, NULL, 0, NULL, NULL, cret);
-       free(aaa);
-       return ret;
-}
-
-
-/*
- * Changes the user's password.  Returns 200 if changed, errors otherwise.
- */
-int CtdlIPCChangePassword(CtdlIPC *ipc, const char *passwd, char *cret)
-{
-       register int ret;
-       char *aaa;
-
-       if (!passwd) return -2;
-       if (!cret) return -2;
-
-       aaa = (char *)malloc((size_t)(strlen(passwd) + 6));
-       if (!aaa) return -1;
-
-       sprintf(aaa, "SETP %s", passwd);
-       ret = CtdlIPCGenericCommand(ipc, aaa, NULL, 0, NULL, NULL, cret);
-       free(aaa);
-       return ret;
-}
-
-
-/* LKRN */
-/* Caller must free the march list */
-/* Room types are defined in enum RoomList; keep these in sync! */
-/* floor is -1 for all, or floornum */
-int CtdlIPCKnownRooms(CtdlIPC *ipc, enum RoomList which, int floor, struct march **listing, char *cret)
-{
-       register int ret;
-       struct march *march = NULL;
-       static char *proto[] =
-               {"LKRA", "LKRN", "LKRO", "LZRM", "LRMS", "LPRM" };
-       char aaa[SIZ];
-       char *bbb = NULL;
-       size_t bbb_len;
-
-       if (!listing) return -2;
-       if (*listing) return -2;        /* Free the listing first */
-       if (!cret) return -2;
-       /* if (which < 0 || which > 4) return -2; */
-       if (floor < -1) return -2;      /* Can't validate upper bound, sorry */
-
-       sprintf(aaa, "%s %d", proto[which], floor);
-       ret = CtdlIPCGenericCommand(ipc, aaa, NULL, 0, &bbb, &bbb_len, cret);
-       if (ret / 100 == 1) {
-               struct march *mptr;
-
-               while (bbb && strlen(bbb)) {
-                       int a;
-
-                       extract_token(aaa, bbb, 0, '\n', sizeof aaa);
-                       a = strlen(aaa);
-                       memmove(bbb, bbb + a + 1, strlen(bbb) - a);
-                       mptr = (struct march *) malloc(sizeof (struct march));
-                       if (mptr) {
-                               mptr->next = NULL;
-                               extract_token(mptr->march_name, aaa, 0, '|', sizeof mptr->march_name);
-                               mptr->march_flags = (unsigned int) extract_int(aaa, 1);
-                               mptr->march_floor = (char) extract_int(aaa, 2);
-                               mptr->march_order = (char) extract_int(aaa, 3);
-                               mptr->march_flags2 = (unsigned int) extract_int(aaa, 4);
-                               mptr->march_access = (char) extract_int(aaa, 5);
-                               if (march == NULL)
-                                       march = mptr;
-                               else {
-                                       struct march *mptr2;
-
-                                       mptr2 = march;
-                                       while (mptr2->next != NULL)
-                                               mptr2 = mptr2->next;
-                                       mptr2->next = mptr;
-                               }
-                       }
-               }
-       }
-       *listing = march;
-       if (bbb) free(bbb);
-       return ret;
-}
-
-
-/* GETU */
-/* Caller must free the struct ctdluser; caller may pass an existing one */
-int CtdlIPCGetConfig(CtdlIPC *ipc, struct ctdluser **uret, char *cret)
-{
-       register int ret;
-
-       if (!cret) return -2;
-       if (!uret) return -2;
-       if (!*uret) *uret = (struct ctdluser *)calloc(1, sizeof (struct ctdluser));
-       if (!*uret) return -1;
-
-       ret = CtdlIPCGenericCommand(ipc, "GETU", NULL, 0, NULL, NULL, cret);
-       if (ret / 100 == 2) {
-               uret[0]->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)
-{
-       register int ret;
-       char cmd[256];
-
-       if (!oldname) return -2;
-       if (!newname) return -2;
-       if (!cret) return -2;
-
-       snprintf(cmd, sizeof cmd, "RENU %s|%s", oldname, newname);
-       ret = CtdlIPCGenericCommand(ipc, cmd, NULL, 0, NULL, NULL, cret);
-       return ret;
-}
-
-
-/* GOTO */
-int CtdlIPCGotoRoom(CtdlIPC *ipc, const char *room, const char *passwd,
-               struct ctdlipcroom **rret, char *cret)
-{
-       register int ret;
-       char *aaa;
-
-       if (!cret) return -2;
-       if (!rret) return -2;
-       if (!*rret) *rret = (struct ctdlipcroom *)calloc(1, sizeof (struct ctdlipcroom));
-       if (!*rret) return -1;
-
-       if (passwd) {
-               aaa = (char *)malloc(strlen(room) + strlen(passwd) + 7);
-               if (!aaa) {
-                       free(*rret);
-                       return -1;
-               }
-               sprintf(aaa, "GOTO %s|%s", room, passwd);
-       } else {
-               aaa = (char *)malloc(strlen(room) + 6);
-               if (!aaa) {
-                       free(*rret);
-                       return -1;
-               }
-               sprintf(aaa, "GOTO %s", room);
-       }
-       ret = CtdlIPCGenericCommand(ipc, aaa, NULL, 0, NULL, NULL, cret);
-       if (ret / 100 == 2) {
-               extract_token(rret[0]->RRname, cret, 0, '|', sizeof rret[0]->RRname);
-               rret[0]->RRunread = extract_long(cret, 1);
-               rret[0]->RRtotal = extract_long(cret, 2);
-               rret[0]->RRinfoupdated = extract_int(cret, 3);
-               rret[0]->RRflags = extract_int(cret, 4);
-               rret[0]->RRhighest = extract_long(cret, 5);
-               rret[0]->RRlastread = extract_long(cret, 6);
-               rret[0]->RRismailbox = extract_int(cret, 7);
-               rret[0]->RRaide = extract_int(cret, 8);
-               rret[0]->RRnewmail = extract_long(cret, 9);
-               rret[0]->RRfloor = extract_int(cret, 10);
-               rret[0]->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)
-{
-       register int ret;
-       register unsigned long count = 0;
-       static char *proto[] =
-               { "ALL", "OLD", "NEW", "LAST", "FIRST", "GT", "LT" };
-       char aaa[33];
-       char *bbb = NULL;
-       size_t bbb_len;
-
-       if (!cret) return -2;
-       if (!mret) return -2;
-       if (*mret) return -2;
-       if (which < 0 || which > 6) return -2;
-
-       if (which <= 2)
-               sprintf(aaa, "MSGS %s||%d", proto[which],
-                               (mtemplate) ? 1 : 0);
-       else
-               sprintf(aaa, "MSGS %s|%d|%d", proto[which], whicharg,
-                               (mtemplate) ? 1 : 0);
-       if (mtemplate) count = strlen(mtemplate);
-       ret = CtdlIPCGenericCommand(ipc, aaa, mtemplate, count, &bbb, &bbb_len, cret);
-       if (ret / 100 != 1)
-               return ret;
-       count = 0;
-       *mret = (unsigned long *)calloc(1, sizeof(unsigned long));
-       if (!*mret)
-               return -1;
-       while (bbb && strlen(bbb)) {
-               extract_token(aaa, bbb, 0, '\n', sizeof aaa);
-               remove_token(bbb, 0, '\n');
-               *mret = (unsigned long *)realloc(*mret, (size_t)((count + 2) *
-                                       sizeof (unsigned long)));
-               if (*mret) {
-                       (*mret)[count++] = atol(aaa);
-                       (*mret)[count] = 0L;
-               } else {
-                       break;
-               }
-       }
-       if (bbb) free(bbb);
-       return ret;
-}
-
-
-/* MSG0, MSG2 */
-int CtdlIPCGetSingleMessage(CtdlIPC *ipc, long msgnum, int headers, int as_mime,
-               struct ctdlipcmessage **mret, char *cret)
-{
-       register int ret;
-       char aaa[SIZ];
-       char *bbb = NULL;
-       size_t bbb_len;
-       int multipart_hunting = 0;
-       char multipart_prefix[128];
-       char encoding[256];
-
-       if (!cret) return -1;
-       if (!mret) return -1;
-       if (!*mret) *mret = (struct ctdlipcmessage *)calloc(1, sizeof (struct ctdlipcmessage));
-       if (!*mret) return -1;
-       if (!msgnum) return -1;
-
-       strcpy(encoding, "");
-       strcpy(mret[0]->content_type, "");
-       sprintf(aaa, "MSG%d %ld|%d", as_mime, msgnum, headers);
-       ret = CtdlIPCGenericCommand(ipc, aaa, NULL, 0, &bbb, &bbb_len, cret);
-       if (ret / 100 == 1) {
-               if (as_mime != 2) {
-                       strcpy(mret[0]->mime_chosen, "1");      /* Default chosen-part is "1" */
-                       while (strlen(bbb) > 4 && bbb[4] == '=') {
-                               extract_token(aaa, bbb, 0, '\n', sizeof aaa);
-                               remove_token(bbb, 0, '\n');
-
-                               if (!strncasecmp(aaa, "nhdr=yes", 8))
-                                       mret[0]->nhdr = 1;
-                               else if (!strncasecmp(aaa, "from=", 5))
-                                       safestrncpy(mret[0]->author, &aaa[5], SIZ);
-                               else if (!strncasecmp(aaa, "type=", 5))
-                                       mret[0]->type = atoi(&aaa[5]);
-                               else if (!strncasecmp(aaa, "msgn=", 5))
-                                       safestrncpy(mret[0]->msgid, &aaa[5], SIZ);
-                               else if (!strncasecmp(aaa, "subj=", 5))
-                                       safestrncpy(mret[0]->subject, &aaa[5], SIZ);
-                               else if (!strncasecmp(aaa, "rfca=", 5))
-                                       safestrncpy(mret[0]->email, &aaa[5], SIZ);
-                               else if (!strncasecmp(aaa, "hnod=", 5))
-                                       safestrncpy(mret[0]->hnod, &aaa[5], SIZ);
-                               else if (!strncasecmp(aaa, "room=", 5))
-                                       safestrncpy(mret[0]->room, &aaa[5], SIZ);
-                               else if (!strncasecmp(aaa, "node=", 5))
-                                       safestrncpy(mret[0]->node, &aaa[5], SIZ);
-                               else if (!strncasecmp(aaa, "rcpt=", 5))
-                                       safestrncpy(mret[0]->recipient, &aaa[5], SIZ);
-                               else if (!strncasecmp(aaa, "wefw=", 5))
-                                       safestrncpy(mret[0]->references, &aaa[5], SIZ);
-                               else if (!strncasecmp(aaa, "time=", 5))
-                                       mret[0]->time = atol(&aaa[5]);
-
-                               /* Multipart/alternative prefix & suffix strings help
-                                * us to determine which part we want to download.
-                                */
-                               else if (!strncasecmp(aaa, "pref=", 5)) {
-                                       extract_token(multipart_prefix, &aaa[5], 1, '|', sizeof multipart_prefix);
-                                       if (!strcasecmp(multipart_prefix,
-                                          "multipart/alternative")) {
-                                               ++multipart_hunting;
-                                       }
-                               }
-                               else if (!strncasecmp(aaa, "suff=", 5)) {
-                                       extract_token(multipart_prefix, &aaa[5], 1, '|', sizeof multipart_prefix);
-                                       if (!strcasecmp(multipart_prefix,
-                                          "multipart/alternative")) {
-                                               ++multipart_hunting;
-                                       }
-                               }
-
-                               else if (!strncasecmp(aaa, "part=", 5)) {
-                                       struct parts *ptr, *chain;
-       
-                                       ptr = (struct parts *)calloc(1, sizeof (struct parts));
-                                       if (ptr) {
-
-                                               /* Fill the buffers for the caller */
-                                               extract_token(ptr->name, &aaa[5], 0, '|', sizeof ptr->name);
-                                               extract_token(ptr->filename, &aaa[5], 1, '|', sizeof ptr->filename);
-                                               extract_token(ptr->number, &aaa[5], 2, '|', sizeof ptr->number);
-                                               extract_token(ptr->disposition, &aaa[5], 3, '|', sizeof ptr->disposition);
-                                               extract_token(ptr->mimetype, &aaa[5], 4, '|', sizeof ptr->mimetype);
-                                               ptr->length = extract_long(&aaa[5], 5);
-                                               if (!mret[0]->attachments)
-                                                       mret[0]->attachments = ptr;
-                                               else {
-                                                       chain = mret[0]->attachments;
-                                                       while (chain->next)
-                                                               chain = chain->next;
-                                                       chain->next = ptr;
-                                               }
-
-                                               /* Now handle multipart/alternative */
-                                               if (multipart_hunting > 0) {
-                                                       if ( (!strcasecmp(ptr->mimetype,
-                                                            "text/plain"))
-                                                          || (!strcasecmp(ptr->mimetype,
-                                                             "text/html")) ) {
-                                                               strcpy(mret[0]->mime_chosen,
-                                                                       ptr->number);
-                                                       }
-                                               }
-
-                                       }
-                               }
-                       }
-                       /* Eliminate "text\n" */
-                       remove_token(bbb, 0, '\n');
-
-                       /* If doing a MIME thing, pull out the extra headers */
-                       if (as_mime == 4) {
-                               do {
-                                       if (!strncasecmp(bbb, "Content-type:", 13)) {
-                                               extract_token(mret[0]->content_type, bbb, 0, '\n', sizeof mret[0]->content_type);
-                                               strcpy(mret[0]->content_type, &mret[0]->content_type[13]);
-                                               striplt(mret[0]->content_type);
-
-                                               /* strip out ";charset=" portion.  FIXME do something with
-                                                * the charset (like... convert it) instead of just throwing
-                                                * it away
-                                                */
-                                               if (strstr(mret[0]->content_type, ";") != NULL) {
-                                                       strcpy(strstr(mret[0]->content_type, ";"), "");
-                                               }
-
-                                       }
-                                       if (!strncasecmp(bbb, "X-Citadel-MSG4-Partnum:", 23)) {
-                                               extract_token(mret[0]->mime_chosen, bbb, 0, '\n', sizeof mret[0]->mime_chosen);
-                                               strcpy(mret[0]->mime_chosen, &mret[0]->mime_chosen[23]);
-                                               striplt(mret[0]->mime_chosen);
-                                       }
-                                       if (!strncasecmp(bbb, "Content-transfer-encoding:", 26)) {
-                                               extract_token(encoding, bbb, 0, '\n', sizeof encoding);
-                                               strcpy(encoding, &encoding[26]);
-                                               striplt(encoding);
-                                       }
-                                       remove_token(bbb, 0, '\n');
-                               } while ((bbb[0] != 0) && (bbb[0] != '\n'));
-                               remove_token(bbb, 0, '\n');
-                       }
-
-
-               }
-               if (strlen(bbb)) {
-
-                       if ( (!strcasecmp(encoding, "base64")) || (!strcasecmp(encoding, "quoted-printable")) ) {
-                               char *ccc = NULL;
-                               int bytes_decoded = 0;
-                               ccc = malloc(strlen(bbb) + 32768);
-                               if (!strcasecmp(encoding, "base64")) {
-                                       bytes_decoded = CtdlDecodeBase64(ccc, bbb, strlen(bbb));
-                               }
-                               else if (!strcasecmp(encoding, "quoted-printable")) {
-                                       bytes_decoded = CtdlDecodeQuotedPrintable(ccc, bbb, strlen(bbb));
-                               }
-                               ccc[bytes_decoded] = 0;
-                               free(bbb);
-                               bbb = ccc;
-                       }
-
-                       /* FIXME: Strip trailing whitespace */
-                       bbb = (char *)realloc(bbb, (size_t)(strlen(bbb) + 1));
-
-               } else {
-                       bbb = (char *)realloc(bbb, 1);
-                       *bbb = '\0';
-               }
-               mret[0]->text = bbb;
-       }
-       return ret;
-}
-
-
-/* WHOK */
-int CtdlIPCWhoKnowsRoom(CtdlIPC *ipc, char **listing, char *cret)
-{
-       register int ret;
-       size_t bytes;
-
-       if (!cret) return -2;
-       if (!listing) return -2;
-       if (*listing) return -2;
-
-       ret = CtdlIPCGenericCommand(ipc, "WHOK", NULL, 0, listing, &bytes, cret);
-       return ret;
-}
-
-
-/* INFO */
-int CtdlIPCServerInfo(CtdlIPC *ipc, char *cret)
-{
-       register int ret;
-       size_t bytes;
-       char *listing = NULL;
-       char buf[SIZ];
-
-       if (!cret) return -2;
-
-       ret = CtdlIPCGenericCommand(ipc, "INFO", NULL, 0, &listing, &bytes, cret);
-       if (ret / 100 == 1) {
-               int line = 0;
-
-               while (*listing && strlen(listing)) {
-                       extract_token(buf, listing, 0, '\n', sizeof buf);
-                       remove_token(listing, 0, '\n');
-                       switch (line++) {
-                       case 0:         ipc->ServInfo.pid = atoi(buf);
-                                       break;
-                       case 1:         strcpy(ipc->ServInfo.nodename,buf);
-                                       break;
-                       case 2:         strcpy(ipc->ServInfo.humannode,buf);
-                                       break;
-                       case 3:         strcpy(ipc->ServInfo.fqdn,buf);
-                                       break;
-                       case 4:         strcpy(ipc->ServInfo.software,buf);
-                                       break;
-                       case 5:         ipc->ServInfo.rev_level = atoi(buf);
-                                       break;
-                       case 6:         strcpy(ipc->ServInfo.site_location,buf);
-                                       break;
-                       case 7:         strcpy(ipc->ServInfo.sysadm,buf);
-                                       break;
-                       case 9:         strcpy(ipc->ServInfo.moreprompt,buf);
-                                       break;
-                       case 10:        ipc->ServInfo.ok_floors = atoi(buf);
-                                       break;
-                       case 11:        ipc->ServInfo.paging_level = atoi(buf);
-                                       break;
-                       case 13:        ipc->ServInfo.supports_qnop = atoi(buf);
-                                       break;
-                       case 14:        ipc->ServInfo.supports_ldap = atoi(buf);
-                                       break;
-                       case 15:        ipc->ServInfo.newuser_disabled = atoi(buf);
-                                       break;
-                       case 16:        strcpy(ipc->ServInfo.default_cal_zone, buf);
-                                       break;
-                       case 17:        ipc->ServInfo.load_avg = atof(buf);
-                                       break;
-                       case 18:        ipc->ServInfo.worker_avg = atof(buf);
-                                       break;
-                       case 19:        ipc->ServInfo.thread_count = atoi(buf);
-                                       break;
-                       case 20:        ipc->ServInfo.has_sieve = atoi(buf);
-                                       break;
-                       case 21:        ipc->ServInfo.fulltext_enabled = atoi(buf);
-                                       break;
-                       case 22:        strcpy(ipc->ServInfo.svn_revision, buf);
-                                       break;
-                       case 24:        ipc->ServInfo.guest_logins = atoi(buf);
-                                       break;
-                       }
-               }
-
-       }
-       if (listing) free(listing);
-       return ret;
-}
-
-
-/* RDIR */
-int CtdlIPCReadDirectory(CtdlIPC *ipc, char **listing, char *cret)
-{
-       register int ret;
-       size_t bytes;
-
-       if (!cret) return -2;
-       if (!listing) return -2;
-       if (*listing) return -2;
-
-       ret = CtdlIPCGenericCommand(ipc, "RDIR", NULL, 0, listing, &bytes, cret);
-       return ret;
-}
-
-
-/*
- * Set last-read pointer in this room to msgnum, or 0 for HIGHEST.
- */
-int CtdlIPCSetLastRead(CtdlIPC *ipc, long msgnum, char *cret)
-{
-       register int ret;
-       char aaa[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)
-{
-       register int ret;
-       char *aaa;
-
-       if (!cret) return -2;
-       if (!username) return -2;
-
-       aaa = (char *)malloc(strlen(username) + 6);
-       if (!aaa) return -1;
-
-       sprintf(aaa, "INVT %s", username);
-       ret = CtdlIPCGenericCommand(ipc, aaa, NULL, 0, NULL, NULL, cret);
-       free(aaa);
-       return ret;
-}
-
-
-/* KICK */
-int CtdlIPCKickoutUserFromRoom(CtdlIPC *ipc, const char *username, char *cret)
-{
-       register int ret;
-       char *aaa;
-
-       if (!cret) return -1;
-       if (!username) return -1;
-
-       aaa = (char *)malloc(strlen(username) + 6);
-
-       sprintf(aaa, "KICK %s", username);
-       ret = CtdlIPCGenericCommand(ipc, aaa, NULL, 0, NULL, NULL, cret);
-       free(aaa);
-       return ret;
-}
-
-
-/* GETR */
-int CtdlIPCGetRoomAttributes(CtdlIPC *ipc, struct ctdlroom **qret, char *cret)
-{
-       register int ret;
-
-       if (!cret) return -2;
-       if (!qret) return -2;
-       if (!*qret) *qret = (struct ctdlroom *)calloc(1, sizeof (struct ctdlroom));
-       if (!*qret) return -1;
-
-       ret = CtdlIPCGenericCommand(ipc, "GETR", NULL, 0, NULL, NULL, cret);
-       if (ret / 100 == 2) {
-               extract_token(qret[0]->QRname, cret, 0, '|', sizeof qret[0]->QRname);
-               extract_token(qret[0]->QRpasswd, cret, 1, '|', sizeof qret[0]->QRpasswd);
-               extract_token(qret[0]->QRdirname, cret, 2, '|', sizeof qret[0]->QRdirname);
-               qret[0]->QRflags = extract_int(cret, 3);
-               qret[0]->QRfloor = extract_int(cret, 4);
-               qret[0]->QRorder = extract_int(cret, 5);
-               qret[0]->QRdefaultview = extract_int(cret, 6);
-               qret[0]->QRflags2 = extract_int(cret, 7);
-       }
-       return ret;
-}
-
-
-/* SETR */
-/* set forget to kick all users out of room */
-int CtdlIPCSetRoomAttributes(CtdlIPC *ipc, int forget, struct ctdlroom *qret, char *cret)
-{
-       register int ret;
-       char *aaa;
-
-       if (!cret) return -2;
-       if (!qret) return -2;
-
-       aaa = (char *)malloc(strlen(qret->QRname) + strlen(qret->QRpasswd) +
-                       strlen(qret->QRdirname) + 64);
-       if (!aaa) return -1;
-
-       sprintf(aaa, "SETR %s|%s|%s|%d|%d|%d|%d|%d|%d",
-                       qret->QRname, qret->QRpasswd, qret->QRdirname,
-                       qret->QRflags, forget, qret->QRfloor, qret->QRorder,
-                       qret->QRdefaultview, qret->QRflags2);
-       ret = CtdlIPCGenericCommand(ipc, aaa, NULL, 0, NULL, NULL, cret);
-       free(aaa);
-       return ret;
-}
-
-
-/* GETA */
-int CtdlIPCGetRoomAide(CtdlIPC *ipc, char *cret)
-{
-       if (!cret) return -1;
-
-       return CtdlIPCGenericCommand(ipc, "GETA", NULL, 0, NULL, NULL, cret);
-}
-
-
-/* SETA */
-int CtdlIPCSetRoomAide(CtdlIPC *ipc, const char *username, char *cret)
-{
-       register int ret;
-       char *aaa;
-
-       if (!cret) return -2;
-       if (!username) return -2;
-
-       aaa = (char *)malloc(strlen(username) + 6);
-       if (!aaa) return -1;
-
-       sprintf(aaa, "SETA %s", username);
-       ret = CtdlIPCGenericCommand(ipc, aaa, NULL, 0, NULL, NULL, cret);
-       free(aaa);
-       return ret;
-}
-
-
-/* ENT0 */
-int CtdlIPCPostMessage(CtdlIPC *ipc, int flag, int *subject_required,  struct ctdlipcmessage *mr, char *cret)
-{
-       register int ret;
-       char cmd[SIZ];
-       char *ptr;
-
-       if (!cret) return -2;
-       if (!mr) return -2;
-
-       if (mr->references) {
-               for (ptr=mr->references; *ptr != 0; ++ptr) {
-                       if (*ptr == '|') *ptr = '!';
-               }
-       }
-
-       snprintf(cmd, sizeof cmd,
-                       "ENT0 %d|%s|%d|%d|%s|%s||||||%s|", flag, mr->recipient,
-                       mr->anonymous, mr->type, mr->subject, mr->author, mr->references);
-       ret = CtdlIPCGenericCommand(ipc, cmd, mr->text, strlen(mr->text), NULL,
-                       NULL, cret);
-       if ((flag == 0) && (subject_required != NULL)) {
-               /* Is the server strongly recommending that the user enter a message subject? */
-               if ((cret[3] != '\0') && (cret[4] != '\0')) {
-                       *subject_required = extract_int(&cret[4], 1);
-               }
-
-               
-       }
-       return ret;
-}
-
-
-/* RINF */
-int CtdlIPCRoomInfo(CtdlIPC *ipc, char **iret, char *cret)
-{
-       size_t bytes;
-
-       if (!cret) return -2;
-       if (!iret) return -2;
-       if (*iret) return -2;
-
-       return CtdlIPCGenericCommand(ipc, "RINF", NULL, 0, iret, &bytes, cret);
-}
-
-
-/* DELE */
-int CtdlIPCDeleteMessage(CtdlIPC *ipc, long msgnum, char *cret)
-{
-       char aaa[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)
-{
-       register int ret;
-       char *aaa;
-
-       if (!cret) return -2;
-       if (!destroom) return -2;
-       if (!msgnum) return -2;
-
-       aaa = (char *)malloc(strlen(destroom) + 28);
-       if (!aaa) return -1;
-
-       sprintf(aaa, "MOVE %ld|%s|%d", msgnum, destroom, copy);
-       ret = CtdlIPCGenericCommand(ipc, aaa, NULL, 0, NULL, NULL, cret);
-       free(aaa);
-       return ret;
-}
-
-
-/* KILL */
-int CtdlIPCDeleteRoom(CtdlIPC *ipc, int for_real, char *cret)
-{
-       char aaa[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)
-{
-       register int ret;
-       char *aaa;
-
-       if (!cret) return -2;
-       if (!roomname) return -2;
-
-       if (password) {
-               aaa = (char *)malloc(strlen(roomname) + strlen(password) + 40);
-               if (!aaa) return -1;
-               sprintf(aaa, "CRE8 %d|%s|%d|%s|%d", for_real, roomname, type,
-                               password, floor);
-       } else {
-               aaa = (char *)malloc(strlen(roomname) + 40);
-               if (!aaa) return -1;
-               sprintf(aaa, "CRE8 %d|%s|%d||%d", for_real, roomname, type,
-                               floor);
-       }
-       ret = CtdlIPCGenericCommand(ipc, aaa, NULL, 0, NULL, NULL, cret);
-       free(aaa);
-       return ret;
-}
-
-
-/* FORG */
-int CtdlIPCForgetRoom(CtdlIPC *ipc, char *cret)
-{
-       if (!cret) return -2;
-
-       return CtdlIPCGenericCommand(ipc, "FORG", NULL, 0, NULL, NULL, cret);
-}
-
-
-/* MESG */
-int CtdlIPCSystemMessage(CtdlIPC *ipc, const char *message, char **mret, char *cret)
-{
-       register int ret;
-       char *aaa;
-       size_t bytes;
-
-       if (!cret) return -2;
-       if (!mret) return -2;
-       if (*mret) return -2;
-       if (!message) return -2;
-
-       aaa = (char *)malloc(strlen(message) + 6);
-       if (!aaa) return -1;
-
-       sprintf(aaa, "MESG %s", message);
-       ret = CtdlIPCGenericCommand(ipc, aaa, NULL, 0, mret, &bytes, cret);
-       free(aaa);
-       return ret;
-}
-
-
-/* GNUR */
-int CtdlIPCNextUnvalidatedUser(CtdlIPC *ipc, char *cret)
-{
-       if (!cret) return -2;
-
-       return CtdlIPCGenericCommand(ipc, "GNUR", NULL, 0, NULL, NULL, cret);
-}
-
-
-/* GREG */
-int CtdlIPCGetUserRegistration(CtdlIPC *ipc, const char *username, char **rret, char *cret)
-{
-       register int ret;
-       char *aaa;
-       size_t bytes;
-
-       if (!cret) return -2;
-       if (!rret) return -2;
-       if (*rret) return -2;
-
-       if (username)
-               aaa = (char *)malloc(strlen(username) + 6);
-       else
-               aaa = (char *)malloc(12);
-       if (!aaa) return -1;
-
-       if (username)
-               sprintf(aaa, "GREG %s", username);
-       else
-               sprintf(aaa, "GREG _SELF_");
-       ret = CtdlIPCGenericCommand(ipc, aaa, NULL, 0, rret, &bytes, cret);
-       free(aaa);
-       return ret;
-}
-
-
-/* VALI */
-int CtdlIPCValidateUser(CtdlIPC *ipc, const char *username, int axlevel, char *cret)
-{
-       register int ret;
-       char *aaa;
-
-       if (!cret) return -2;
-       if (!username) return -2;
-       if (axlevel < 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)
-{
-       register int ret;
-
-       if (!cret) return -1;
-       if (!chek) return -1;
-
-       ret = CtdlIPCGenericCommand(ipc, "CHEK", NULL, 0, NULL, NULL, cret);
-       if (ret / 100 == 2) {
-               chek->newmail = extract_long(cret, 0);
-               chek->needregis = extract_int(cret, 1);
-               chek->needvalid = extract_int(cret, 2);
-       }
-       return ret;
-}
-
-
-/* DELF */
-int CtdlIPCDeleteFile(CtdlIPC *ipc, const char *filename, char *cret)
-{
-       register int ret;
-       char *aaa;
-
-       if (!cret) return -2;
-       if (!filename) return -2;
-       
-       aaa = (char *)malloc(strlen(filename) + 6);
-       if (!aaa) return -1;
-
-       sprintf(aaa, "DELF %s", filename);
-       ret = CtdlIPCGenericCommand(ipc, aaa, NULL, 0, NULL, NULL, cret);
-       free(aaa);
-       return ret;
-}
-
-
-/* MOVF */
-int CtdlIPCMoveFile(CtdlIPC *ipc, const char *filename, const char *destroom, char *cret)
-{
-       register int ret;
-       char *aaa;
-
-       if (!cret) return -2;
-       if (!filename) return -2;
-       if (!destroom) return -2;
-
-       aaa = (char *)malloc(strlen(filename) + strlen(destroom) + 7);
-       if (!aaa) return -1;
-
-       sprintf(aaa, "MOVF %s|%s", filename, destroom);
-       ret = CtdlIPCGenericCommand(ipc, aaa, NULL, 0, NULL, NULL, cret);
-       free(aaa);
-       return ret;
-}
-
-
-/* RWHO */
-int CtdlIPCOnlineUsers(CtdlIPC *ipc, char **listing, time_t *stamp, char *cret)
-{
-       register int ret;
-       size_t bytes;
-
-       if (!cret) return -1;
-       if (!listing) return -1;
-       if (*listing) return -1;
-
-       *stamp = CtdlIPCServerTime(ipc, cret);
-       if (!*stamp)
-               *stamp = time(NULL);
-       ret = CtdlIPCGenericCommand(ipc, "RWHO", NULL, 0, listing, &bytes, cret);
-       return ret;
-}
-
-
-/* OPEN */
-int CtdlIPCFileDownload(CtdlIPC *ipc, const char *filename, void **buf,
-               size_t resume,
-               void (*progress_gauge_callback)
-                       (CtdlIPC*, unsigned long, unsigned long),
-               char *cret)
-{
-       register int ret;
-       size_t bytes;
-       time_t last_mod;
-       char mimetype[SIZ];
-       char *aaa;
-
-       if (!cret) return -2;
-       if (!filename) return -2;
-       if (!buf) return -2;
-       if (*buf) return -2;
-       if (ipc->downloading) return -2;
-
-       aaa = (char *)malloc(strlen(filename) + 6);
-       if (!aaa) return -1;
-
-       sprintf(aaa, "OPEN %s", filename);
-       ret = CtdlIPCGenericCommand(ipc, aaa, NULL, 0, NULL, NULL, cret);
-       free(aaa);
-       if (ret / 100 == 2) {
-               ipc->downloading = 1;
-               bytes = extract_long(cret, 0);
-               last_mod = extract_int(cret, 1);
-               extract_token(mimetype, cret, 2, '|', sizeof mimetype);
-
-               ret = CtdlIPCReadDownload(ipc, buf, bytes, resume,
-                                       progress_gauge_callback, cret);
-               /*
-               ret = CtdlIPCHighSpeedReadDownload(ipc, buf, bytes, resume,
-                                       progress_gauge_callback, cret);
-               */
-
-               ret = CtdlIPCEndDownload(ipc, cret);
-               if (ret / 100 == 2)
-                       sprintf(cret, "%d|%ld|%s|%s", (int)bytes, last_mod,
-                                       filename, mimetype);
-       }
-       return ret;
-}
-
-
-/* OPNA */
-int CtdlIPCAttachmentDownload(CtdlIPC *ipc, long msgnum, const char *part,
-               void **buf,
-               void (*progress_gauge_callback)
-                       (CtdlIPC*, unsigned long, unsigned long),
-               char *cret)
-{
-       register int ret;
-       size_t bytes;
-       time_t last_mod;
-       char filename[SIZ];
-       char mimetype[SIZ];
-       char aaa[SIZ];
-
-       if (!cret) return -2;
-       if (!buf) return -2;
-       if (*buf) return -2;
-       if (!part) return -2;
-       if (!msgnum) return -2;
-       if (ipc->downloading) return -2;
-
-       sprintf(aaa, "OPNA %ld|%s", msgnum, part);
-       ret = CtdlIPCGenericCommand(ipc, aaa, NULL, 0, NULL, NULL, cret);
-       if (ret / 100 == 2) {
-               ipc->downloading = 1;
-               bytes = extract_long(cret, 0);
-               last_mod = extract_int(cret, 1);
-               extract_token(filename, cret, 2, '|', sizeof filename);
-               extract_token(mimetype, cret, 3, '|', sizeof mimetype);
-               /* ret = CtdlIPCReadDownload(ipc, buf, bytes, 0, progress_gauge_callback, cret); */
-               ret = CtdlIPCHighSpeedReadDownload(ipc, buf, bytes, 0, progress_gauge_callback, cret);
-               ret = CtdlIPCEndDownload(ipc, cret);
-               if (ret / 100 == 2)
-                       sprintf(cret, "%d|%ld|%s|%s", (int)bytes, last_mod,
-                                       filename, mimetype);
-       }
-       return ret;
-}
-
-
-/* OIMG */
-int CtdlIPCImageDownload(CtdlIPC *ipc, const char *filename, void **buf,
-               void (*progress_gauge_callback)
-                       (CtdlIPC*, unsigned long, unsigned long),
-               char *cret)
-{
-       register int ret;
-       size_t bytes;
-       time_t last_mod;
-       char mimetype[SIZ];
-       char *aaa;
-
-       if (!cret) return -1;
-       if (!buf) return -1;
-       if (*buf) return -1;
-       if (!filename) return -1;
-       if (ipc->downloading) return -1;
-
-       aaa = (char *)malloc(strlen(filename) + 6);
-       if (!aaa) return -1;
-
-       sprintf(aaa, "OIMG %s", filename);
-       ret = CtdlIPCGenericCommand(ipc, aaa, NULL, 0, NULL, NULL, cret);
-       free(aaa);
-       if (ret / 100 == 2) {
-               ipc->downloading = 1;
-               bytes = extract_long(cret, 0);
-               last_mod = extract_int(cret, 1);
-               extract_token(mimetype, cret, 2, '|', sizeof mimetype);
-/*             ret = CtdlIPCReadDownload(ipc, buf, bytes, 0, progress_gauge_callback, cret); */
-               ret = CtdlIPCHighSpeedReadDownload(ipc, buf, bytes, 0, progress_gauge_callback, cret);
-               ret = CtdlIPCEndDownload(ipc, cret);
-               if (ret / 100 == 2)
-                       sprintf(cret, "%d|%ld|%s|%s", (int)bytes, last_mod,
-                                       filename, mimetype);
-       }
-       return ret;
-}
-
-
-/* UOPN */
-int CtdlIPCFileUpload(CtdlIPC *ipc, const char *save_as, const char *comment, 
-               const char *path, 
-               void (*progress_gauge_callback)
-                       (CtdlIPC*, unsigned long, unsigned long),
-               char *cret)
-{
-       register int ret;
-       char *aaa;
-       FILE *uploadFP;
-       char MimeTestBuf[64];
-       const char *MimeType;
-       long len;
-
-       if (!cret) return -1;
-       if (!save_as) return -1;
-       if (!comment) return -1;
-       if (!path) return -1;
-       if (!*path) return -1;
-       if (ipc->uploading) return -1;
-
-       uploadFP = fopen(path, "r");
-       if (!uploadFP) return -2;
-
-       len = fread(&MimeTestBuf[0], 1, 64, uploadFP);
-       rewind (uploadFP);
-       if (len < 0) 
-               return -3;
-
-       MimeType = GuessMimeType(&MimeTestBuf[0], len);
-       aaa = (char *)malloc(strlen(save_as) + strlen(comment) + 7);
-       if (!aaa) return -1;
-
-       sprintf(aaa, "UOPN %s|%s|%s", save_as, MimeType,  comment);
-       ret = CtdlIPCGenericCommand(ipc, aaa, NULL, 0, NULL, NULL, cret);
-       free(aaa);
-       if (ret / 100 == 2) {
-               ipc->uploading = 1;
-               ret = CtdlIPCWriteUpload(ipc, uploadFP, progress_gauge_callback, cret);
-               ret = CtdlIPCEndUpload(ipc, (ret == -2 ? 1 : 0), cret);
-               ipc->uploading = 0;
-       }
-       return ret;
-}
-
-
-/* UIMG */
-int CtdlIPCImageUpload(CtdlIPC *ipc, int for_real, const char *path,
-               const char *save_as,
-               void (*progress_gauge_callback)
-                       (CtdlIPC*, unsigned long, unsigned long),
-               char *cret)
-{
-       register int ret;
-       FILE *uploadFP;
-       char *aaa;
-       char MimeTestBuf[64];
-       const char *MimeType;
-       long len;
-
-       if (!cret) return -1;
-       if (!save_as) return -1;
-       if (!path && for_real) return -1;
-       if (!*path && for_real) return -1;
-       if (ipc->uploading) return -1;
-
-       aaa = (char *)malloc(strlen(save_as) + 17);
-       if (!aaa) return -1;
-
-       uploadFP = fopen(path, "r");
-       if (!uploadFP) return -2;
-
-       len = fread(&MimeTestBuf[0], 1, 64, uploadFP);
-       rewind (uploadFP);
-       if (len < 0) 
-               return -3;
-       MimeType = GuessMimeType(&MimeTestBuf[0], 64);
-
-       sprintf(aaa, "UIMG %d|%s|%s", for_real, MimeType, save_as);
-       ret = CtdlIPCGenericCommand(ipc, aaa, NULL, 0, NULL, NULL, cret);
-       free(aaa);
-       if (ret / 100 == 2 && for_real) {
-               ipc->uploading = 1;
-               ret = CtdlIPCWriteUpload(ipc, uploadFP, progress_gauge_callback, cret);
-               ret = CtdlIPCEndUpload(ipc, (ret == -2 ? 1 : 0), cret);
-               ipc->uploading = 0;
-       }
-       return ret;
-}
-
-
-/* QUSR */
-int CtdlIPCQueryUsername(CtdlIPC *ipc, const char *username, char *cret)
-{
-       register int ret;
-       char *aaa;
-
-       if (!cret) return -2;
-       if (!username) return -2;
-
-       aaa = (char *)malloc(strlen(username) + 6);
-       if (!aaa) return -1;
-
-       sprintf(aaa, "QUSR %s", username);
-       ret = CtdlIPCGenericCommand(ipc, aaa, NULL, 0, NULL, NULL, cret);
-       free(aaa);
-       return ret;
-}
-
-
-/* LFLR */
-int CtdlIPCFloorListing(CtdlIPC *ipc, char **listing, char *cret)
-{
-       size_t bytes;
-
-       if (!cret) return -2;
-       if (!listing) return -2;
-       if (*listing) return -2;
-
-       return CtdlIPCGenericCommand(ipc, "LFLR", NULL, 0, listing, &bytes, cret);
-}
-
-
-/* CFLR */
-int CtdlIPCCreateFloor(CtdlIPC *ipc, int for_real, const char *name, char *cret)
-{
-       register int ret;
-       char aaa[SIZ];
-
-       if (!cret) return -2;
-       if (!name) return -2;
-
-       sprintf(aaa, "CFLR %s|%d", name, for_real);
-       ret = CtdlIPCGenericCommand(ipc, aaa, NULL, 0, NULL, NULL, cret);
-       return ret;
-}
-
-
-/* KFLR */
-int CtdlIPCDeleteFloor(CtdlIPC *ipc, int for_real, int floornum, char *cret)
-{
-       char aaa[SIZ];
-
-       if (!cret) return -1;
-       if (floornum < 0) return -1;
-
-       sprintf(aaa, "KFLR %d|%d", floornum, for_real);
-       return CtdlIPCGenericCommand(ipc, aaa, NULL, 0, NULL, NULL, cret);
-}
-
-
-/* EFLR */
-int CtdlIPCEditFloor(CtdlIPC *ipc, int floornum, const char *floorname, char *cret)
-{
-       register int ret;
-       char aaa[SIZ];
-
-       if (!cret) return -2;
-       if (!floorname) return -2;
-       if (floornum < 0) return -2;
-
-       sprintf(aaa, "EFLR %d|%s", floornum, floorname);
-       ret = CtdlIPCGenericCommand(ipc, aaa, NULL, 0, NULL, NULL, cret);
-       return ret;
-}
-
-
-/*
- * IDEN 
- *
- * You only need to fill out hostname, the defaults will be used if any of the
- * other fields are not set properly.
- */
-int CtdlIPCIdentifySoftware(CtdlIPC *ipc, int developerid, int clientid,
-               int revision, const char *software_name, const char *hostname,
-               char *cret)
-{
-       register int ret;
-       char *aaa;
-
-       if (developerid < 0 || clientid < 0 || revision < 0 ||
-           !software_name) {
-               developerid = 8;
-               clientid = 0;
-               revision = REV_LEVEL - 600;
-               software_name = "Citadel (libcitadel)";
-       }
-       if (!hostname) return -2;
-
-       aaa = (char *)malloc(strlen(software_name) + strlen(hostname) + 29);
-       if (!aaa) return -1;
-
-       sprintf(aaa, "IDEN %d|%d|%d|%s|%s", developerid, clientid,
-                       revision, software_name, hostname);
-       ret = CtdlIPCGenericCommand(ipc, aaa, NULL, 0, NULL, NULL, cret);
-       free(aaa);
-       return ret;
-}
-
-
-/* SEXP */
-int CtdlIPCSendInstantMessage(CtdlIPC *ipc, const char *username, const char *text,
-               char *cret)
-{
-       register int ret;
-       char *aaa;
-
-       if (!cret) return -2;
-       if (!username) return -2;
-
-       aaa = (char *)malloc(strlen(username) + 8);
-       if (!aaa) return -1;
-
-       if (text) {
-               sprintf(aaa, "SEXP %s|-", username);
-               ret = CtdlIPCGenericCommand(ipc, aaa, text, strlen(text),
-                               NULL, NULL, cret);
-       } else {
-               sprintf(aaa, "SEXP %s||", username);
-               ret = CtdlIPCGenericCommand(ipc, aaa, NULL, 0, NULL, NULL, cret);
-       }
-       free(aaa);
-       return ret;
-}
-
-
-/* GEXP */
-int CtdlIPCGetInstantMessage(CtdlIPC *ipc, char **listing, char *cret)
-{
-       size_t bytes;
-
-       if (!cret) return -2;
-       if (!listing) return -2;
-       if (*listing) return -2;
-
-       return CtdlIPCGenericCommand(ipc, "GEXP", NULL, 0, listing, &bytes, cret);
-}
-
-
-/* DEXP */
-/* mode is 0 = enable, 1 = disable, 2 = status */
-int CtdlIPCEnableInstantMessageReceipt(CtdlIPC *ipc, int mode, char *cret)
-{
-       char aaa[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)
-{
-       register int ret;
-       size_t bytes;
-       char *aaa;
-
-       if (!cret) return -2;
-       if (!username) return -2;
-       if (!listing) return -2;
-       if (*listing) return -2;
-
-       aaa = (char *)malloc(strlen(username) + 6);
-       if (!aaa) return -1;
-
-       sprintf(aaa, "RBIO %s", username);
-       ret = CtdlIPCGenericCommand(ipc, aaa, NULL, 0, listing, &bytes, cret);
-       free(aaa);
-       return ret;
-}
-
-
-/* LBIO */
-int CtdlIPCListUsersWithBios(CtdlIPC *ipc, char **listing, char *cret)
-{
-       size_t bytes;
-
-       if (!cret) return -2;
-       if (!listing) return -2;
-       if (*listing) return -2;
-
-       return CtdlIPCGenericCommand(ipc, "LBIO", NULL, 0, listing, &bytes, cret);
-}
-
-
-/* STEL */
-int CtdlIPCStealthMode(CtdlIPC *ipc, int mode, char *cret)
-{
-       char aaa[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)
-{
-       register int ret;
-       char *aaa;
-
-       if (!cret) return -2;
-       if (!text) return -2;
-       if (!filename) return -2;
-
-       aaa = (char *)malloc(strlen(filename) + 6);
-       if (!aaa) return -1;
-
-       sprintf(aaa, "EMSG %s", filename);
-       ret = CtdlIPCGenericCommand(ipc, aaa, text, strlen(text), NULL, NULL, cret);
-       free(aaa);
-       return ret;
-}
-
-
-/* HCHG */
-int CtdlIPCChangeHostname(CtdlIPC *ipc, const char *hostname, char *cret)
-{
-       register int ret;
-       char *aaa;
-
-       if (!cret) return -2;
-       if (!hostname) return -2;
-
-       aaa = (char *)malloc(strlen(hostname) + 6);
-       if (!aaa) return -1;
-
-       sprintf(aaa, "HCHG %s", hostname);
-       ret = CtdlIPCGenericCommand(ipc, aaa, NULL, 0, NULL, NULL, cret);
-       free(aaa);
-       return ret;
-}
-
-
-/* RCHG */
-int CtdlIPCChangeRoomname(CtdlIPC *ipc, const char *roomname, char *cret)
-{
-       register int ret;
-       char *aaa;
-
-       if (!cret) return -2;
-       if (!roomname) return -2;
-
-       aaa = (char *)malloc(strlen(roomname) + 6);
-       if (!aaa) return -1;
-
-       sprintf(aaa, "RCHG %s", roomname);
-       ret = CtdlIPCGenericCommand(ipc, aaa, NULL, 0, NULL, NULL, cret);
-       free(aaa);
-       return ret;
-}
-
-
-/* UCHG */
-int CtdlIPCChangeUsername(CtdlIPC *ipc, const char *username, char *cret)
-{
-       register int ret;
-       char *aaa;
-
-       if (!cret) return -2;
-       if (!username) return -2;
-
-       aaa = (char *)malloc(strlen(username) + 6);
-       if (!aaa) return -1;
-
-       sprintf(aaa, "UCHG %s", username);
-       ret = CtdlIPCGenericCommand(ipc, aaa, NULL, 0, NULL, NULL, cret);
-       free(aaa);
-       return ret;
-}
-
-
-/* TIME */
-/* This function returns the actual server time reported, or 0 if error */
-time_t CtdlIPCServerTime(CtdlIPC *ipc, char *cret)
-{
-       register time_t tret;
-       register int ret;
-
-       ret = CtdlIPCGenericCommand(ipc, "TIME", NULL, 0, NULL, NULL, cret);
-       if (ret / 100 == 2) {
-               tret = extract_long(cret, 0);
-       } else {
-               tret = 0L;
-       }
-       return tret;
-}
-
-
-/* AGUP */
-int CtdlIPCAideGetUserParameters(CtdlIPC *ipc, const char *who,
-                                struct ctdluser **uret, char *cret)
-{
-       register int ret;
-       char aaa[SIZ];
-
-       if (!cret) return -2;
-       if (!uret) return -2;
-       if (!*uret) *uret = (struct ctdluser *)calloc(1, sizeof(struct ctdluser));
-       if (!*uret) return -1;
-
-       sprintf(aaa, "AGUP %s", who);
-       ret = CtdlIPCGenericCommand(ipc, aaa, NULL, 0, NULL, NULL, cret);
-
-       if (ret / 100 == 2) {
-               extract_token(uret[0]->fullname, cret, 0, '|', sizeof uret[0]->fullname);
-               extract_token(uret[0]->password, cret, 1, '|', sizeof uret[0]->password);
-               uret[0]->flags = extract_int(cret, 2);
-               uret[0]->timescalled = extract_long(cret, 3);
-               uret[0]->posted = extract_long(cret, 4);
-               uret[0]->axlevel = extract_int(cret, 5);
-               uret[0]->usernum = extract_long(cret, 6);
-               uret[0]->lastcall = extract_long(cret, 7);
-               uret[0]->USuserpurge = extract_int(cret, 8);
-       }
-       return ret;
-}
-
-
-/* ASUP */
-int CtdlIPCAideSetUserParameters(CtdlIPC *ipc, const struct ctdluser *uret, char *cret)
-{
-       register int ret;
-       char *aaa;
-
-       if (!cret) return -2;
-       if (!uret) return -2;
-
-       aaa = (char *)malloc(strlen(uret->fullname) + strlen(uret->password) + 84);
-       if (!aaa) return -1;
-
-       sprintf(aaa, "ASUP %s|%s|%d|%ld|%ld|%d|%ld|%ld|%d",
-                       uret->fullname, uret->password, uret->flags,
-                       uret->timescalled, uret->posted, uret->axlevel,
-                       uret->usernum, uret->lastcall, uret->USuserpurge);
-       ret = CtdlIPCGenericCommand(ipc, aaa, NULL, 0, NULL, NULL, cret);
-       free(aaa);
-       return ret;
-}
-
-
-/* GPEX */
-/* which is 0 = room, 1 = floor, 2 = site, 3 = default for mailboxes */
-/* caller must free the struct ExpirePolicy */
-int CtdlIPCGetMessageExpirationPolicy(CtdlIPC *ipc, GPEXWhichPolicy which,
-               struct ExpirePolicy **policy, char *cret)
-{
-       static char *proto[] = {
-               strof(roompolicy),
-               strof(floorpolicy),
-               strof(sitepolicy),
-               strof(mailboxespolicy)
-       };
-       char cmd[256];
-       register int ret;
-
-       if (!cret) return -2;
-       if (!policy) return -2;
-       if (!*policy) *policy = (struct ExpirePolicy *)calloc(1, sizeof(struct ExpirePolicy));
-       if (!*policy) return -1;
-       if (which < 0 || which > 3) return -2;
-       
-       sprintf(cmd, "GPEX %s", proto[which]);
-       ret = CtdlIPCGenericCommand(ipc, cmd, NULL, 0, NULL, NULL, cret);
-       if (ret / 100 == 2) {
-               policy[0]->expire_mode = extract_int(cret, 0);
-               policy[0]->expire_value = extract_int(cret, 1);
-       }
-       return ret;
-}
-
-
-/* SPEX */
-/* which is 0 = room, 1 = floor, 2 = site, 3 = default for mailboxes */
-/* policy is 0 = inherit, 1 = no purge, 2 = by count, 3 = by age (days) */
-int CtdlIPCSetMessageExpirationPolicy(CtdlIPC *ipc, int which,
-               struct ExpirePolicy *policy, char *cret)
-{
-       char aaa[38];
-       char *whichvals[] = { "room", "floor", "site", "mailboxes" };
-
-       if (!cret) return -2;
-       if (which < 0 || which > 3) return -2;
-       if (!policy) return -2;
-       if (policy->expire_mode < 0 || policy->expire_mode > 3) return -2;
-       if (policy->expire_mode >= 2 && policy->expire_value < 1) return -2;
-
-       sprintf(aaa, "SPEX %s|%d|%d", whichvals[which],
-                       policy->expire_mode, policy->expire_value);
-       return CtdlIPCGenericCommand(ipc, aaa, NULL, 0, NULL, NULL, cret);
-}
-
-
-/* CONF GET */
-int CtdlIPCGetSystemConfig(CtdlIPC *ipc, char **listing, char *cret)
-{
-       size_t bytes;
-
-       if (!cret) return -2;
-       if (!listing) return -2;
-       if (*listing) return -2;
-
-       return CtdlIPCGenericCommand(ipc, "CONF GET", NULL, 0,
-                       listing, &bytes, cret);
-}
-
-
-/* CONF SET */
-int CtdlIPCSetSystemConfig(CtdlIPC *ipc, const char *listing, char *cret)
-{
-       if (!cret) return -2;
-       if (!listing) return -2;
-
-       return CtdlIPCGenericCommand(ipc, "CONF SET", listing, strlen(listing),
-                       NULL, NULL, cret);
-}
-
-
-/* CONF GETSYS */
-int CtdlIPCGetSystemConfigByType(CtdlIPC *ipc, const char *mimetype,
-               char **listing, char *cret)
-{
-       register int ret;
-       char *aaa;
-       size_t bytes;
-
-       if (!cret) return -2;
-       if (!mimetype) return -2;
-       if (!listing) return -2;
-       if (*listing) return -2;
-
-       aaa = malloc(strlen(mimetype) + 13);
-       if (!aaa) return -1;
-       sprintf(aaa, "CONF GETSYS|%s", mimetype);
-       ret = CtdlIPCGenericCommand(ipc, aaa, NULL, 0,
-                       listing, &bytes, cret);
-    free(aaa);
-    return ret;
-}
-
-
-/* CONF PUTSYS */
-int CtdlIPCSetSystemConfigByType(CtdlIPC *ipc, const char *mimetype,
-              const char *listing, char *cret)
-{
-    register int ret;
-       char *aaa;
-
-       if (!cret) return -2;
-       if (!mimetype) return -2;
-       if (!listing) return -2;
-
-       aaa = malloc(strlen(mimetype) + 13);
-       if (!aaa) return -1;
-       sprintf(aaa, "CONF PUTSYS|%s", mimetype);
-       ret = CtdlIPCGenericCommand(ipc, aaa, listing, strlen(listing),
-                       NULL, NULL, cret);
-    free(aaa);
-    return ret;
-}
-
-
-/* GNET */
-int CtdlIPCGetRoomNetworkConfig(CtdlIPC *ipc, char **listing, char *cret)
-{
-       size_t bytes;
-
-       if (!cret) return -2;
-       if (!listing) return -2;
-       if (*listing) return -2;
-
-       return CtdlIPCGenericCommand(ipc, "GNET", NULL, 0,
-                       listing, &bytes, cret);
-}
-
-
-/* SNET */
-int CtdlIPCSetRoomNetworkConfig(CtdlIPC *ipc, const char *listing, char *cret)
-{
-       if (!cret) return -2;
-       if (!listing) return -2;
-
-       return CtdlIPCGenericCommand(ipc, "SNET", listing, strlen(listing),
-                       NULL, NULL, cret);
-}
-
-
-/* REQT */
-int CtdlIPCRequestClientLogout(CtdlIPC *ipc, int session, char *cret)
-{
-       char aaa[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
-
-       if (!access(EGD_POOL, F_OK))
-               RAND_egd(EGD_POOL);
-
-       if (!RAND_status()) {
-               error_printf("PRNG not properly seeded\n");
-               return -2;
-       }
-
-       /* Associate network connection with SSL object */
-       if (SSL_set_fd(temp_ssl, ipc->sock) < 1) {
-               error_printf("SSL_set_fd failed: %s\n",
-                               ERR_reason_error_string(ERR_get_error()));
-               return -2;
-       }
-
-       if (status_hook != NULL)
-               status_hook("Requesting encryption...\r");
-
-       /* Ready to start SSL/TLS */
-       /* Old code
-       CtdlIPC_putline(ipc, "STLS");
-       CtdlIPC_getline(ipc, buf);
-       if (buf[0] != '2') {
-               error_printf("Server can't start TLS: %s\n", buf);
-               return 0;
-       }
-       */
-       r = CtdlIPCGenericCommand(ipc,
-                                 "STLS", NULL, 0, NULL, NULL, cret);
-       if (r / 100 != 2) {
-               error_printf("Server can't start TLS: %s\n", buf);
-               endtls(temp_ssl);
-               return r;
-       }
-
-       /* Do SSL/TLS handshake */
-       if ((a = SSL_connect(temp_ssl)) < 1) {
-               error_printf("SSL_connect failed: %s\n",
-                               ERR_reason_error_string(ERR_get_error()));
-               endtls(temp_ssl);
-               return -2;
-       }
-       ipc->ssl = temp_ssl;
-
-       if (BIO_set_close(ipc->ssl->rbio, BIO_NOCLOSE))
-       {
-               int bits, alg_bits;
-
-               bits = SSL_CIPHER_get_bits(SSL_get_current_cipher(ipc->ssl), &alg_bits);
-               error_printf("Encrypting with %s cipher %s (%d of %d bits)\n",
-                               SSL_CIPHER_get_version(SSL_get_current_cipher(ipc->ssl)),
-                               SSL_CIPHER_get_name(SSL_get_current_cipher(ipc->ssl)),
-                               bits, alg_bits);
-       }
-       return r;
-#else
-       return 0;
-#endif /* HAVE_OPENSSL */
-}
-
-
-#ifdef HAVE_OPENSSL
-static void endtls(SSL *ssl)
-{
-       if (ssl) {
-               SSL_shutdown(ssl);
-               SSL_free(ssl);
-       }
-}
-#endif
-
-
-/* QDIR */
-int CtdlIPCDirectoryLookup(CtdlIPC *ipc, const char *address, char *cret)
-{
-    register int ret;
-       char *aaa;
-
-       if (!address) return -2;
-       if (!cret) return -2;
-
-       aaa = (char *)malloc(strlen(address) + 6);
-       if (!aaa) return -1;
-
-       sprintf(aaa, "QDIR %s", address);
-       ret = CtdlIPCGenericCommand(ipc, aaa, NULL, 0, NULL, NULL, cret);
-    free(aaa);
-    return ret;
-}
-
-
-/* IPGM */
-int CtdlIPCInternalProgram(CtdlIPC *ipc, int secret, char *cret)
-{
-       char aaa[30];
-
-       if (!cret) return -2;
-       sprintf(aaa, "IPGM %d", secret);
-       return CtdlIPCGenericCommand(ipc, aaa, NULL, 0, NULL, NULL, cret);
-}
-
-
-
-/*
- * Not implemented:
- * 
- * CHAT
- * ETLS
- * EXPI
- * GTLS
- * IGAB
- * MSG3
- * MSG4
- * NDOP
- * NETP
- * NUOP
- * SMTP
- */
-
-
-/* ************************************************************************** */
-/*          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)
-{
-       register size_t len = 0;
-       char aaa[SIZ];
-
-       if (!buf) return 0;
-       if (!cret) return 0;
-       if (bytes < 1) return 0;
-
-       CtdlIPC_lock(ipc);
-       sprintf(aaa, "READ %d|%d", (int)offset, (int)bytes);
-       CtdlIPC_putline(ipc, aaa);
-       CtdlIPC_getline(ipc, aaa);
-       if (aaa[0] != '6')
-               strcpy(cret, &aaa[4]);
-       else {
-               len = extract_long(&aaa[4], 0);
-               *buf = (void *)realloc(*buf, (size_t)(offset + len));
-               if (*buf) {
-                       /* I know what I'm doing */
-                       serv_read(ipc, ((char *)(*buf) + offset), len);
-               } else {
-                       /* We have to read regardless */
-                       serv_read(ipc, aaa, len);
-                       len = 0;
-               }
-       }
-       CtdlIPC_unlock(ipc);
-       return len;
-}
-
-
-/* CLOS */
-int CtdlIPCEndDownload(CtdlIPC *ipc, char *cret)
-{
-       register int ret;
-
-       if (!cret) return -2;
-       if (!ipc->downloading) return -2;
-
-       ret = CtdlIPCGenericCommand(ipc, "CLOS", NULL, 0, NULL, NULL, cret);
-       if (ret / 100 == 2)
-               ipc->downloading = 0;
-       return ret;
-}
-
-
-/* MSGP */
-int CtdlIPCSpecifyPreferredFormats(CtdlIPC *ipc, char *cret, char *formats) {
-       register int ret;
-       char cmd[SIZ];
-       
-       snprintf(cmd, sizeof cmd, "MSGP %s", formats);
-       ret = CtdlIPCGenericCommand(ipc, cmd, NULL, 0, NULL, NULL, cret);
-       return ret;
-}
-
-
-
-/* READ */
-int CtdlIPCReadDownload(CtdlIPC *ipc, void **buf, size_t bytes, size_t resume,
-               void (*progress_gauge_callback)
-                       (CtdlIPC*, unsigned long, unsigned long),
-              char *cret)
-{
-       register size_t len;
-
-       if (!cret) return -1;
-       if (!buf) return -1;
-       if (*buf) return -1;
-       if (!ipc->downloading) return -1;
-
-       len = resume;
-       if (progress_gauge_callback)
-               progress_gauge_callback(ipc, len, bytes);
-       while (len < bytes) {
-               register size_t block;
-
-               block = CtdlIPCPartialRead(ipc, buf, len, 4096, cret);
-               if (block == 0) {
-                       free(*buf);
-                       return 0;
-               }
-               len += block;
-               if (progress_gauge_callback)
-                       progress_gauge_callback(ipc, len, bytes);
-       }
-       return len;
-}
-
-/* READ - pipelined */
-int CtdlIPCHighSpeedReadDownload(CtdlIPC *ipc, void **buf, size_t bytes,
-              size_t resume,
-               void (*progress_gauge_callback)
-                       (CtdlIPC*, unsigned long, unsigned long),
-              char *cret)
-{
-       register size_t len;
-       register int calls;     /* How many calls in the pipeline */
-       register int i;         /* iterator */
-       char aaa[4096];
-
-       if (!cret) return -1;
-       if (!buf) return -1;
-       if (*buf) return -1;
-       if (!ipc->downloading) return -1;
-
-       *buf = (void *)realloc(*buf, bytes - resume);
-       if (!*buf) return -1;
-
-       len = 0;
-       CtdlIPC_lock(ipc);
-       if (progress_gauge_callback)
-               progress_gauge_callback(ipc, len, bytes);
-
-       /* How many calls will be in the pipeline? */
-       calls = (bytes - resume) / 4096;
-       if ((bytes - resume) % 4096) calls++;
-
-       /* Send all requests at once */
-       for (i = 0; i < calls; i++) {
-               sprintf(aaa, "READ %d|4096", (int)(i * 4096 + resume) );
-               CtdlIPC_putline(ipc, aaa);
-       }
-
-       /* Receive all responses at once */
-       for (i = 0; i < calls; i++) {
-               CtdlIPC_getline(ipc, aaa);
-               if (aaa[0] != '6')
-                       strcpy(cret, &aaa[4]);
-               else {
-                       len = extract_long(&aaa[4], 0);
-                       /* I know what I'm doing */
-                       serv_read(ipc, ((char *)(*buf) + (i * 4096)), len);
-               }
-               if (progress_gauge_callback)
-                       progress_gauge_callback(ipc, i * 4096 + len, bytes);
-       }
-       CtdlIPC_unlock(ipc);
-       return len;
-}
-
-
-/* UCLS */
-int CtdlIPCEndUpload(CtdlIPC *ipc, int discard, char *cret)
-{
-       register int ret;
-       char cmd[8];
-
-       if (!cret) return -1;
-       if (!ipc->uploading) return -1;
-
-       sprintf(cmd, "UCLS %d", discard ? 0 : 1);
-       ret = CtdlIPCGenericCommand(ipc, cmd, NULL, 0, NULL, NULL, cret);
-       ipc->uploading = 0;
-       return ret;
-}
-
-
-/* WRIT */
-int CtdlIPCWriteUpload(CtdlIPC *ipc, FILE *uploadFP,
-               void (*progress_gauge_callback)
-                       (CtdlIPC*, unsigned long, unsigned long),
-               char *cret)
-{
-       register int ret = -1;
-       register size_t offset = 0;
-       size_t bytes;
-       char aaa[SIZ];
-       char buf[4096];
-       FILE *fd = uploadFP;
-       int ferr;
-
-       if (!cret) return -1;
-
-       fseek(fd, 0L, SEEK_END);
-       bytes = ftell(fd);
-       rewind(fd);
-
-       if (progress_gauge_callback)
-               progress_gauge_callback(ipc, 0, bytes);
-
-       while (offset < bytes) {
-               register size_t to_write;
-
-               /* Read some data in */
-               to_write = fread(buf, 1, 4096, fd);
-               if (!to_write) {
-                       if (feof(fd) || ferror(fd)) break;
-               }
-               sprintf(aaa, "WRIT %d", (int)to_write);
-               CtdlIPC_putline(ipc, aaa);
-               CtdlIPC_getline(ipc, aaa);
-               strcpy(cret, &aaa[4]);
-               ret = atoi(aaa);
-               if (aaa[0] == '7') {
-                       to_write = extract_long(&aaa[4], 0);
-                       
-                       serv_write(ipc, buf, to_write);
-                       offset += to_write;
-                       if (progress_gauge_callback)
-                               progress_gauge_callback(ipc, offset, bytes);
-                       /* Detect short reads and back up if needed */
-                       /* offset will never be negative anyway */
-                       fseek(fd, (signed)offset, SEEK_SET);
-               } else {
-                       break;
-               }
-       }
-       if (progress_gauge_callback)
-               progress_gauge_callback(ipc, 1, 1);
-       ferr = ferror(fd);
-       fclose(fd);
-       return (!ferr ? ret : -2);
-}
-
-
-/*
- * Generic command method.  This method should handle any server command
- * except for CHAT.  It takes the following arguments:
- *
- * ipc                 The server to speak with
- * command             Preformatted command to send to server
- * to_send             A text or binary file to send to server
- *                     (only sent if server requests it)
- * bytes_to_send       The number of bytes in to_send (required if
- *                     sending binary, optional if sending listing)
- * to_receive          Pointer to a NULL pointer, if the server
- *                     sends text or binary we will allocate memory
- *                     for the file and stuff it here
- * bytes_to_receive    If a file is received, we will store its
- *                     byte count here
- * proto_response      The protocol response.  Caller must provide
- *                     this buffer and ensure that it is at least
- *                     128 bytes in length.
- *
- * This function returns a number equal to the protocol response number,
- * -1 if an internal error occurred, -2 if caller provided bad values,
- * or 0 - the protocol response number if bad values were found during
- * the protocol exchange.
- * It stores the protocol response string (minus the number) in 
- * protocol_response as described above.  Some commands send additional
- * data in this string.
- */
-int CtdlIPCGenericCommand(CtdlIPC *ipc,
-               const char *command, const char *to_send,
-               size_t bytes_to_send, char **to_receive, 
-               size_t *bytes_to_receive, char *proto_response)
-{
-       char buf[SIZ];
-       register int ret;
-
-       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 (!(BN_hex2bn(&(dh->p), DH_P))) {
-               error_printf("Can't assign DH_P: %s\n",
-                               ERR_reason_error_string(ERR_get_error()));
-               DH_free(dh);
-               return;
-       }
-       if (!(BN_hex2bn(&(dh->g), DH_G))) {
-               error_printf("Can't assign DH_G: %s\n",
-                               ERR_reason_error_string(ERR_get_error()));
-               DH_free(dh);
-               return;
-       }
-       dh->length = DH_L;
-       SSL_CTX_set_tmp_dh(ssl_ctx, dh);
-       DH_free(dh);
-
-#ifdef THREADED_CLIENT
-       /* OpenSSL requires callbacks for threaded clients */
-       CRYPTO_set_locking_callback(ssl_lock);
-       CRYPTO_set_id_callback(id_callback);
-
-       /* OpenSSL requires us to do semaphores for threaded clients */
-       Critters = malloc(CRYPTO_num_locks() * sizeof (pthread_mutex_t *));
-       if (!Critters) {
-               perror("malloc failed");
-               exit(1);
-       } else {
-               for (a = 0; a < CRYPTO_num_locks(); a++) {
-                       Critters[a] = malloc(sizeof (pthread_mutex_t));
-                       if (!Critters[a]) {
-                               perror("malloc failed");
-                               exit(1);
-                       }
-                       pthread_mutex_init(Critters[a], NULL);
-               }
-       }
-#endif /* THREADED_CLIENT */       
-}
-
-
-
-#ifdef THREADED_CLIENT
-static unsigned long id_callback(void) {
-       return (unsigned long)pthread_self();
-}
-#endif /* THREADED_CLIENT */
-#endif /* HAVE_OPENSSL */
-
-
-int
-ReadNetworkChunk(CtdlIPC* ipc)
-{
-       fd_set read_fd;
-/*     int tries;*/
-       int ret = 0;
-       int err = 0;
-       struct timeval tv;
-       size_t n;
-
-       tv.tv_sec = 1;
-       tv.tv_usec = 1000;
-       /*tries = 0; */
-       n = 0;
-       while (1)
-       {
-               errno=0;
-               FD_ZERO(&read_fd);
-               FD_SET(ipc->sock, &read_fd);
-               ret = select(ipc->sock+1, &read_fd, NULL, NULL,  &tv);
-               
-//             fprintf(stderr, "\nselect failed: %d %d %s\n", ret,  err, strerror(err));
-               
-               if (ret > 0) {
-                       
-                       *(ipc->BufPtr) = '\0';
-//                     n = read(ipc->sock, ipc->BufPtr, ipc->BufSize  - (ipc->BufPtr - ipc->Buf) - 1);
-                       n = recv(ipc->sock, ipc->BufPtr, ipc->BufSize  - (ipc->BufPtr - ipc->Buf) - 1, 0);
-                       if (n > 0) {
-                               ipc->BufPtr[n]='\0';
-                               ipc->BufUsed += n;
-                               return n;
-                       }
-                       else 
-                               return n;
-               }
-               else if (ret < 0) {
-                       if (!(errno == EINTR || errno == EAGAIN))
-                               error_printf( "\nselect failed: %d %s\n", err, strerror(err));
-                       return -1;
-               }/*
-               else {
-                       tries ++;
-                       if (tries >= 10)
-                       n = read(ipc->sock, ipc->BufPtr, ipc->BufSize  - (ipc->BufPtr - ipc->Buf) - 1);
-                       if (n > 0) {
-                               ipc->BufPtr[n]='\0';
-                               ipc->BufUsed += n;
-                               return n;
-                       }
-                       else {
-                               connection_died(ipc, 0);
-                               return -1;
-                       }
-                       }*/
-       }
-}
-
-/*
- * input string from socket - implemented in terms of serv_read()
- */
-#ifdef CHUNKED_READ
-
-static void CtdlIPC_getline(CtdlIPC* ipc, char *buf)
-{
-       int i, ntries;
-       char *aptr, *bptr, *aeptr, *beptr;
-
-//     error_printf("---\n");
-
-       beptr = buf + SIZ;
-#if defined(HAVE_OPENSSL)
-       if (ipc->ssl) {
-               
-               /* Read one character at a time. */
-               for (i = 0;; i++) {
-                       serv_read(ipc, &buf[i], 1);
-                       if (buf[i] == '\n' || i == (SIZ-1))
-                               break;
-               }
-               
-               /* If we got a long line, discard characters until the newline. */
-               if (i == (SIZ-1))
-                       while (buf[i] != '\n')
-                               serv_read(ipc, &buf[i], 1);
-               
-               /* Strip the trailing newline (and carriage return, if present) */
-               if (i>=0 && buf[i] == 10) buf[i--] = 0;
-               if (i>=0 && buf[i] == 13) buf[i--] = 0;
-       }
-       else
-#endif
-       {
-               if (ipc->Buf == NULL)
-               {
-                       ipc->BufSize = SIZ;
-                       ipc->Buf = (char*) malloc(ipc->BufSize + 10);
-                       *(ipc->Buf) = '\0';
-                       ipc->BufPtr = ipc->Buf;
-               }
-
-               ntries = 0;
-//             while ((ipc->BufUsed == 0)||(ntries++ > 10))
-               if (ipc->BufUsed == 0)
-                       ReadNetworkChunk(ipc);
-
-////           if (ipc->BufUsed != 0) while (1)
-               bptr = buf;
-
-               while (1)
-               {
-                       aptr = ipc->BufPtr;
-                       aeptr = ipc->Buf + ipc->BufSize;
-                       while ((aptr < aeptr) && 
-                              (bptr < beptr) &&
-                              (*aptr != '\0') && 
-                              (*aptr != '\n'))
-                               *(bptr++) = *(aptr++);
-                       if ((*aptr == '\n') && (aptr < aeptr))
-                       {
-                               /* Terminate it right, remove the line breaks */
-                               while ((aptr < aeptr) && ((*aptr == '\n') || (*aptr == '\r')))
-                                       aptr ++;
-                               while ((aptr < aeptr ) && (*(aptr + 1) == '\0') )
-                                       aptr ++;
-                               *(bptr++) = '\0';
-//                             fprintf(stderr, "parsing %d %d %d - %d %d %d %s\n", ipc->BufPtr - ipc->Buf, aptr - ipc->BufPtr, ipc->BufUsed , *aptr, *(aptr-1), *(aptr+1), buf);
-                               if ((bptr > buf + 1) && (*(bptr-1) == '\r'))
-                                       *(--bptr) = '\0';
-                               
-                               /* is there more in the buffer we need to read later? */
-                               if (ipc->Buf + ipc->BufUsed > aptr)
-                               {
-                                       ipc->BufPtr = aptr;
-                               }
-                               else
-                               {
-                                       ipc->BufUsed = 0;
-                                       ipc->BufPtr = ipc->Buf;
-                               }
-//                             error_printf("----bla6\n");
-                               return;
-                               
-                       }/* should we move our read stuf to the bufferstart so we have more space at the end? */
-                       else if ((ipc->BufPtr != ipc->Buf) && 
-                                (ipc->BufUsed > (ipc->BufSize  - (ipc->BufSize / 4))))
-                       {
-                               size_t NewBufSize = ipc->BufSize * 2;
-                               int delta = (ipc->BufPtr - ipc->Buf);
-                               char *NewBuf;
-
-                               /* if the line would end after our buffer, we should use a bigger buffer. */
-                               NewBuf = (char *)malloc (NewBufSize + 10);
-                               memcpy (NewBuf, ipc->BufPtr, ipc->BufUsed - delta);
-                               free(ipc->Buf);
-                               ipc->Buf = ipc->BufPtr = NewBuf;
-                               ipc->BufUsed -= delta;
-                               ipc->BufSize = NewBufSize;
-                       }
-                       if (ReadNetworkChunk(ipc) <0)
-                       {
-//                             error_printf("----bla\n");
-                               return;
-                       }
-               }
-///            error_printf("----bl45761%s\nipc->BufUsed");
-       }
-//     error_printf("----bla1\n");
-}
-
-#else  /* CHUNKED_READ */
-
-static void CtdlIPC_getline(CtdlIPC* ipc, char *buf)
-{
-       int i;
-
-       /* Read one character at a time. */
-       for (i = 0;; i++) {
-               serv_read(ipc, &buf[i], 1);
-               if (buf[i] == '\n' || i == (SIZ-1))
-                       break;
-       }
-
-       /* If we got a long line, discard characters until the newline. */
-       if (i == (SIZ-1))
-               while (buf[i] != '\n')
-                       serv_read(ipc, &buf[i], 1);
-
-       /* Strip the trailing newline (and carriage return, if present) */
-       if (i>=0 && buf[i] == 10) buf[i--] = 0;
-       if (i>=0 && buf[i] == 13) buf[i--] = 0;
-}
-
-
-#endif /* CHUNKED_READ */
-
-
-void CtdlIPC_chat_recv(CtdlIPC* ipc, char* buf)
-{
-       CtdlIPC_getline(ipc, buf);
-}
-
-/*
- * send line to server - implemented in terms of serv_write()
- */
-static void CtdlIPC_putline(CtdlIPC *ipc, const char *buf)
-{
-       char *cmd = NULL;
-       int len;
-
-       len = strlen(buf);
-       cmd = malloc(len + 2);
-       if (!cmd) {
-               /* This requires no extra memory */
-               serv_write(ipc, buf, len);
-               serv_write(ipc, "\n", 1);
-       } else {
-               /* This is network-optimized */
-               strncpy(cmd, buf, len);
-               strcpy(cmd + len, "\n");
-               serv_write(ipc, cmd, len + 1);
-               free(cmd);
-       }
-
-       ipc->last_command_sent = time(NULL);
-}
-
-void CtdlIPC_chat_send(CtdlIPC* ipc, const char* buf)
-{
-       CtdlIPC_putline(ipc, buf);
-}
-
-
-/*
- * attach to server
- */
-CtdlIPC* CtdlIPC_new(int argc, char **argv, char *hostbuf, char *portbuf)
-{
-       int a;
-       char cithost[SIZ];
-       char citport[SIZ];
-       char sockpath[SIZ];
-       CtdlIPC* ipc;
-
-       ipc = ialloc(CtdlIPC);
-       if (!ipc) {
-               return 0;
-       }
-#if defined(HAVE_OPENSSL)
-       ipc->ssl = NULL;
-       CtdlIPC_init_OpenSSL();
-#endif
-#if defined(HAVE_PTHREAD_H)
-       pthread_mutex_init(&(ipc->mutex), NULL); /* Default fast mutex */
-#endif
-       ipc->sock = -1;                 /* Not connected */
-       ipc->isLocal = 0;               /* Not local, of course! */
-       ipc->downloading = 0;
-       ipc->uploading = 0;
-       ipc->last_command_sent = 0L;
-       ipc->network_status_cb = NULL;
-       ipc->Buf = NULL;
-       ipc->BufUsed = 0;
-       ipc->BufPtr = NULL;
-
-       strcpy(cithost, DEFAULT_HOST);  /* default host */
-       strcpy(citport, DEFAULT_PORT);  /* default port */
-
-       /* Allow caller to supply our values (Windows) */
-       if (hostbuf && strlen(hostbuf) > 0)
-               strcpy(cithost, hostbuf);
-       if (portbuf && strlen(portbuf) > 0)
-               strcpy(citport, portbuf);
-
-       /* Read host/port from command line if present */
-       for (a = 0; a < argc; ++a) {
-               if (a == 0) {
-                       /* do nothing */
-               } else if (a == 1) {
-                       strcpy(cithost, argv[a]);
-               } else if (a == 2) {
-                       strcpy(citport, argv[a]);
-               } else {
-                       error_printf("%s: usage: ",argv[0]);
-                       error_printf("%s [host] [port] ",argv[0]);
-                       ifree(ipc);
-                       errno = EINVAL;
-                       return 0;
-               }
-       }
-
-       if ((!strcmp(cithost, "localhost"))
-          || (!strcmp(cithost, "127.0.0.1"))) {
-               ipc->isLocal = 1;
-       }
-
-       /* If we're using a unix domain socket we can do a bunch of stuff */
-       if (!strcmp(cithost, UDS)) {
-               if (!strcasecmp(citport, DEFAULT_PORT)) {
-                       snprintf(sockpath, sizeof sockpath, "%s", file_citadel_socket);
-               }
-               else {
-                       snprintf(sockpath, sizeof sockpath, "%s/%s", citport, "citadel.socket");
-               }
-               ipc->sock = uds_connectsock(&(ipc->isLocal), sockpath);
-               if (ipc->sock == -1) {
-                       ifree(ipc);
-                       return 0;
-               }
-               if (hostbuf != NULL) strcpy(hostbuf, cithost);
-               if (portbuf != NULL) strcpy(portbuf, sockpath);
-               strcpy(ipc->ip_hostname, "");
-               strcpy(ipc->ip_address, "");
-               return ipc;
-       }
-
-       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/citadel/utillib/ipc_c_tcp.c b/citadel/utillib/ipc_c_tcp.c
deleted file mode 100644 (file)
index a6cae96..0000000
+++ /dev/null
@@ -1,69 +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.h"
-#include "citadel_ipc.h"
-#include "citadel_decls.h"
-#ifndef HAVE_SNPRINTF
-#include "snprintf.h"
-#endif
-#include "commands.h"
-
-/* Note that some of these functions may not work with multiple instances. */
-
-static void (*deathHook)(void) = NULL;
-int (*error_printf)(char *s, ...) = (int (*)(char *, ...))printf;
-
-void setIPCDeathHook(void (*hook)(void)) {
-       deathHook = hook;
-}
-
-void setIPCErrorPrintf(int (*func)(char *s, ...)) {
-       error_printf = func;
-}
-
-void connection_died(CtdlIPC* ipc, int using_ssl) {
-       if (deathHook != NULL) {
-               deathHook();
-       }
-
-       stty_ctdl(SB_RESTORE);
-       fprintf(stderr, "\r\n\n\n");
-       fprintf(stderr, "Your connection to %s is broken.\n", ipc->ServInfo.humannode);
-
-#ifdef HAVE_OPENSSL
-       if (using_ssl) {
-               fprintf(stderr, "Last error: %s\n", ERR_reason_error_string(ERR_get_error()));
-               SSL_free(ipc->ssl);
-               ipc->ssl = NULL;
-       } else
-#endif
-               fprintf(stderr, "Last error: %s\n", strerror(errno));
-
-       fprintf(stderr, "Please re-connect and log in again.\n");
-       fflush(stderr);
-       fflush(stdout);
-       shutdown(ipc->sock, 2);
-       ipc->sock = -1;
-        exit(1);
-}
index 296d1cee50a0de60f1a7bef41184198ce4524931..9b58553b10a3650c8a8988a5f4187a597bd5d6e8 100644 (file)
@@ -655,5 +655,110 @@ extern ConstStr RoomNetCfgStrs[maxRoomNetCfg];
 /* a nice consistent place to define how we turn a message id into a thread id hash */
 #define ThreadIdHash(Buf) abs(HashLittle(ChrPtr(Buf), StrLength(Buf)))
 
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/*
+ * MIME types used in Citadel for configuration stuff
+ */
+#define SPOOLMIME      "application/x-citadel-delivery-list"
+#define        INTERNETCFG     "application/x-citadel-internet-config"
+#define IGNETCFG       "application/x-citadel-ignet-config"
+#define IGNETMAP       "application/x-citadel-ignet-map"
+#define FILTERLIST     "application/x-citadel-filter-list"
+#define SIEVECONFIG    "application/x-citadel-sieve-config"
+#define XMPPMORTUARY   "application/x-citadel-xmpp-mortuary"
+#define INTERNETCFG     "application/x-citadel-internet-config"
+
+#define LISTING_FOLLOWS                100
+#define CIT_OK                 200
+#define MORE_DATA              300
+#define SEND_LISTING           400
+#define ERROR                  500
+#define BINARY_FOLLOWS         600
+#define SEND_BINARY            700
+#define START_CHAT_MODE                800
+
+#define INTERNAL_ERROR         10
+#define TOO_BIG                        11
+#define ILLEGAL_VALUE          12
+#define NOT_LOGGED_IN          20
+#define CMD_NOT_SUPPORTED      30
+#define SERVER_SHUTTING_DOWN   31
+#define PASSWORD_REQUIRED      40
+#define ALREADY_LOGGED_IN      41
+#define USERNAME_REQUIRED      42
+#define HIGHER_ACCESS_REQUIRED 50
+#define MAX_SESSIONS_EXCEEDED  51
+#define RESOURCE_BUSY          52
+#define RESOURCE_NOT_OPEN      53
+#define NOT_HERE               60
+#define INVALID_FLOOR_OPERATION        61
+#define NO_SUCH_USER           70
+#define FILE_NOT_FOUND         71
+#define ROOM_NOT_FOUND         72
+#define NO_SUCH_SYSTEM         73
+#define ALREADY_EXISTS         74
+#define MESSAGE_NOT_FOUND      75
+
+#define ASYNC_MSG              900
+#define ASYNC_GEXP             02
+
+#define QR_PERMANENT   1               /* Room does not purge              */
+#define QR_INUSE       2               /* Set if in use, clear if avail    */
+#define QR_PRIVATE     4               /* Set for any type of private room */
+#define QR_PASSWORDED  8               /* Set if there's a password too    */
+#define QR_GUESSNAME   16              /* Set if it's a guessname room     */
+#define QR_DIRECTORY   32              /* Directory room                   */
+#define QR_UPLOAD      64              /* Allowed to upload                */
+#define QR_DOWNLOAD    128             /* Allowed to download              */
+#define QR_VISDIR      256             /* Visible directory                */
+#define QR_ANONONLY    512             /* Anonymous-Only room              */
+#define QR_ANONOPT     1024            /* Anonymous-Option room            */
+#define QR_NETWORK     2048            /* Shared network room              */
+#define QR_PREFONLY    4096            /* Preferred status needed to enter */
+#define QR_READONLY    8192            /* Aide status required to post     */
+#define QR_MAILBOX     16384           /* Set if this is a private mailbox */
+
+#define QR2_SYSTEM     1               /* System room; hide by default     */
+#define QR2_SELFLIST   2               /* Self-service mailing list mgmt   */
+#define QR2_COLLABDEL  4               /* Anyone who can post can delete   */
+#define QR2_SUBJECTREQ 8               /* Subject strongly recommended */
+#define QR2_SMTP_PUBLIC        16              /* Listservice Subscribers may post */
+#define QR2_MODERATED  32              /* Listservice aide has to permit posts  */
+
+#define US_NEEDVALID   1               /* User needs to be validated       */
+#define US_EXTEDIT     2               /* Always use external editor       */
+#define US_PERM                4               /* Permanent user                   */
+#define US_LASTOLD     16              /* Print last old message with new  */
+#define US_EXPERT      32              /* Experienced user                 */
+#define US_UNLISTED    64              /* Unlisted userlog entry           */
+#define US_NOPROMPT    128             /* Don't prompt after each message  */
+#define US_PROMPTCTL   256             /* <N>ext & <S>top work at prompt   */
+#define US_DISAPPEAR   512             /* Use "disappearing msg prompts"   */
+#define US_REGIS       1024            /* Registered user                  */
+#define US_PAGINATOR   2048            /* Pause after each screen of text  */
+#define US_INTERNET    4096            /* Internet mail privileges         */
+#define US_FLOORS      8192            /* User wants to see floors         */
+#define US_COLOR       16384           /* User wants ANSI color support    */
+#define US_USER_SET    (US_LASTOLD | US_EXPERT | US_UNLISTED | \
+                       US_NOPROMPT | US_DISAPPEAR | US_PAGINATOR | \
+                       US_FLOORS | US_COLOR | US_PROMPTCTL | US_EXTEDIT)
+
+#define UA_KNOWN                2      /* Room appears in a 'known rooms' list */
+#define UA_GOTOALLOWED          4      /* User may goto this room if specified by exact name */
+#define UA_HASNEWMSGS           8      /* Unread messages exist in this room */
+#define UA_ZAPPED               16     /* User has forgotten (zapped) this room */
+#define UA_POSTALLOWED         32      /* User may post top-level messages here */
+#define UA_ADMINALLOWED                64      /* Aide or Room Aide rights exist here */
+#define UA_DELETEALLOWED       128     /* User is allowed to delete messages from this room */
+#define UA_REPLYALLOWED                256     /* User is allowed to reply to existing messages here */
+/* runtime flag extracted from goto reply; not db persistant, should be moved if new flags added */
+#define UA_ISTRASH              512    /* Only available in room view... */
+
+#ifdef __cplusplus
+}
+#endif
 
 #endif // LIBCITADEL_H
diff --git a/textclient/.gitignore b/textclient/.gitignore
new file mode 100644 (file)
index 0000000..3a3798b
--- /dev/null
@@ -0,0 +1,47 @@
+*.o
+*.d
+*.gcno
+*.gcda
+Make_modules
+Make_sources
+Makefile
+Makefile.in
+locale
+aclocal.m4
+aidepost
+base64
+build-arch-stamp
+build-indep-stamp
+chkpw
+chkpwd
+citadel
+citmail
+citserver
+config.log
+config.status
+configure
+configure-stamp
+ctdlmigrate
+database_cleanup.sh
+getmail
+modules_init.c
+modules_init.h
+modules_upgrade.c
+msgform
+panic.log
+sendcommand
+setup
+svn_revision.c
+sysdep.h
+sysdep.h.in
+userlist
+whobbs
+gmon.out
+autom4te.cache
+citadel.config
+citadel.control
+citadel.pid
+refcount_adjustments.dat
+keys
+netconfigs
+data
diff --git a/textclient/Makefile.in b/textclient/Makefile.in
new file mode 100644 (file)
index 0000000..a10ed3f
--- /dev/null
@@ -0,0 +1,213 @@
+# 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/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/acinclude.m4 b/textclient/acinclude.m4
new file mode 100644 (file)
index 0000000..0941acf
--- /dev/null
@@ -0,0 +1,50 @@
+# 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
new file mode 100644 (file)
index 0000000..899da07
--- /dev/null
@@ -0,0 +1,113 @@
+# generated automatically by aclocal 1.11.5 -*- Autoconf -*-
+
+# Copyright (C) 1996, 1997, 1998, 1999, 2000, 2001, 2002, 2003, 2004,
+# 2005, 2006, 2007, 2008, 2009, 2010, 2011 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.
+
+# AM_AUX_DIR_EXPAND                                         -*- Autoconf -*-
+
+# Copyright (C) 2001, 2003, 2005, 2011 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.
+
+# serial 1
+
+# 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],
+[dnl Rely on autoconf to set up CDPATH properly.
+AC_PREREQ([2.50])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, 1999, 2000, 2001, 2003, 2004, 2005, 2008
+# 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.
+
+# serial 6
+
+# 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 supports --run.
+# If it does, 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 --run true"; then
+  am_missing_run="$MISSING --run "
+else
+  am_missing_run=
+  AC_MSG_WARN([`missing' script is too old or missing])
+fi
+])
+
+m4_include([acinclude.m4])
diff --git a/textclient/bootstrap b/textclient/bootstrap
new file mode 100755 (executable)
index 0000000..9f4f846
--- /dev/null
@@ -0,0 +1,32 @@
+#!/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
diff --git a/textclient/citadel.rc b/textclient/citadel.rc
new file mode 100644 (file)
index 0000000..351f5b5
--- /dev/null
@@ -0,0 +1,354 @@
+#
+# This file allows full customization of the user interface.
+#
+# The standard client looks for this file in:
+# 1. $HOME/.citadelrc
+# 2. <compiled CTDLDIR>/citadel.rc
+# 3. /etc/citadel.rc
+# 4. <current directory>/citadel.rc
+
+# Set ENCRYPT to yes to force SSL/TLS encryption when connecting to a
+# Citadel server, even if the server is on the same machine as the
+# client.  Set it to no to disable SSL/TLS encryption.  The default is to
+# enable encryption for remote systems and to disable encryption for
+# systems on the same machine as the client.
+encrypt=default
+
+# Set EDITOR to the name of an external editor to be used for entering
+# messages.  If you want the external editor to be used by default, be sure
+# to reflect this in the command set below.
+#
+# editor=vi
+
+# If you define PRINTCMD, it will be a pipe through which messages are
+# printed when the user hits the <P>rint key after a message.
+#
+#printcmd=lpr
+
+# If you define EXPCMD, it will be a pipe through which any incoming
+# instant messages will be printed.
+#expcmd=xmessage -title "Instant message" -center -buttons OK -file -
+
+# ANSI_COLOR should be set to on/off/auto/user to control the use of
+# color on the screen.  If it is set to "auto" then an autodetect will be
+# attempted.  If it is set to "user" then it's a user-configurable option.
+#
+ansi_color=user
+
+# USE_BACKGROUND controls Citadel's use of the background.  If it is turned
+# off, then Citadel will set the background to black.  When it is turned on,
+# the background will be unchanged.  This is most useful with "transparent"
+# terminals.  Color must be on (see above) or this option has no effect.
+#
+use_background=off
+
+# STATUS_LINE will keep an informative status line on the top of the screen,
+# but it doesn't work on all terminals (Mac OS is particularly bad at this).
+status_line=off
+
+# PROMPT_CONTROL should be set to on/off/user to control whether the <N>ext
+# and <S>top keys are active when displaying the paginator prompt.  If it
+# is set to "user" then it's a user-configurable option.
+#
+prompt_control=user
+
+# If DISPLAY_MESSAGE_NUMBERS is set to 1, then messages will be displayed
+# with their message numbers in the header.  This is ugly but some
+# people seem to like it anyway...
+#
+display_message_numbers=0
+
+# The FORCE_MAIL_PROMPTS directive causes mail rooms to always display a
+# prompt after each message, even if the user has prompts turned off.
+#
+force_mail_prompts=1
+
+# USE_FLOORS determines whether the user sees floors, or a flat room space.
+# Set it to YES to always use floors, NO to never use floors, or DEFAULT
+# to use the setting in the user's configuration (which is normally the case).
+#
+use_floors=DEFAULT
+
+# BEEP should be set to 1 if you wish the terminal to beep when an express
+# message (page) comes in, otherwise set it to 0.
+#
+beep=1
+
+# ALLOW_ATTACHMENTS should be set to 1 if you wish the user to be able to
+# attach files to messages and/or save attachments to disk while reading
+# messages.  (DANGER: do NOT enable this for "safe" public clients!)
+#
+allow_attachments=0
+
+# IDLE_THRESHOLD is the amount of time a user does nothing before being
+# listed as "idle" on the who list.  The default is 900 seconds (15 minutes).
+#
+idle_threshold=900
+
+# If you set REMEMBER_PASSWORDS to 1, the Citadel client will offer to
+# remember your user name and password for every Citadel server you access,
+# automatically logging in with them on subsequent visits. 
+# (DANGER: do NOT enable this for "safe" public clients!)
+#
+remember_passwords=0
+
+# Optionally, you can force the same username and password everywhere.  This
+# obviously isn't as robust as the password manager.
+#
+#username=My User Name
+#password=mypassword
+
+# If URLCMD is defined, users can hit 'U' after reading a message which
+# contains embedded URL's, and the command will be executed.  Usually this
+# will be used to remote-control a web browser.  (Do not enable this command
+# for 'safe' public clients.)
+#
+# This one ought to work on any Linux that has the "Portland" API's installed.
+# If properly configured, xdg-open will open the URL in the user's preferred
+# web browser.
+#urlcmd=xdg-open "%s"
+#
+# This one works really well on a Macintosh -- it opens URL's in whatever
+# browser you have configured as the system default.
+#urlcmd=open "%s"
+
+# If OPENCMD is defined, users can hit 'O' after reading a message which
+# contains attachments, to open the attachments using that command.  This
+# allows attachments to be opened directly from the Citadel client without
+# having to first save them in a file.  The "allow_attachments" option (see
+# above) must be enabled in order for this to work.
+# 
+# xdg-open works on most newer Linux systems
+opencmd=xdg-open "%s"
+#
+# This probably will work on a Macintosh
+#opencmd=open "%s"
+
+# If GOTMAILCMD is defined, the specified command will be executed.  This
+# might be nice for playing sounds or providing any other type of notification.
+#
+#gotmailcmd=play gotmail.wav
+
+# If IMAGECMD is defined, users can hit 'I' to view images attached to a
+# message.  (Do not enable this command for public clients.)
+#
+# Possible image viewers follow
+
+# xdg-open - on Linux systems with the Portland API's installed, this ought
+# to open the image in the user's preferred viewer.
+#imagecmd=xdg-open "%s"
+#
+# Preview on Mac OS X
+#imagecmd=/Applications/Preview.app/Contents/MacOS/Preview "%s"
+
+
+# COMMAND SET CONFIGURATION
+#
+# All lines starting with "cmd=" are considered to be commands.  This allows
+# mapping of keytstrokes to various functions of the client.
+#
+# Format of each line:
+# cmd_num,access,keystrokes
+#
+# Keep a copy of the original version of this file around as a reference
+# for the command numbers.  They are not documented anywhere else.
+#
+# Access is:  0 (all users), 1 (aides or room aides), 2 (aides only).
+# Please be aware that it is futile to attempt to gain unauthorized access to
+# the administrative functions of the system by changing all the access levels
+# to 0.  If you do this, you'll simply be able to enter a lot of commands that
+# will fail at the server ... so don't bother trying. :-)
+#
+# The actual key to be pressed should be prefaced with an & (ampersand)
+# character.  Ampersands are interesting and useful characters and you should
+# use them as much as possible.  Commands requiring more than one keystroke
+# should be entered as multiple fields.
+#
+# If the last keystroke string ends with a : (colon), then the command
+# will finish by allowing the user to enter a string.
+#
+# In keystroke names, the string ^r will be replaced by the name of the
+# current room.  The string ^c will be replaced by a comma.
+#
+# Commands may contain no more than five keystrokes.
+#
+# Note that the following characters are illegal in commands:
+#  , (comma)    : (colon)     ^ (caret)     & (ampersand)
+#
+#
+cmd=1,0,&? (Help)
+cmd=1,0,&Help
+cmd=3,0,&Chat
+#
+# If you want to use an external editor by default, set <E>nter message
+# to command #46 (external editor) instead of #4 (built-in editor).
+cmd=4,0,&Enter message
+#
+cmd=5,0,&Goto
+cmd=6,0,&Skip ^r
+cmd=7,0,&Zap (forget) room
+cmd=8,0,&Known rooms
+cmd=9,0,&Last five msgs
+cmd=10,0,read &Forward
+cmd=11,0,read &Reverse
+cmd=12,0,read &Old
+cmd=13,0,read &New
+cmd=14,0,read &Directory
+cmd=15,0,&Terminate
+cmd=16,0,&Ungoto
+cmd=95,0,&.,&Ungoto:
+cmd=17,0,&Who is online
+cmd=47,0,&Abandon ^r^c goto...
+#cmd=90,0,&Abandon ^r^c goto:
+cmd=50,0,toggle e&Xpert mode
+cmd=49,0,read &Info file
+cmd=18,2,&! <shell>
+cmd=19,0,&.,list &Zapped rooms
+cmd=52,0,&.,&Skip ^r^c goto:
+cmd=56,0,&Page a user
+cmd=58,0,&Mail
+cmd=84,0,&Quiet mode
+#
+# We implement both <.G>oto and <J>ump commands which do the same thing, in
+# order to please a wider audience of users.  Remove one if you want to.
+#
+cmd=20,0,&Jump:
+cmd=20,0,&.,&Goto:
+#
+cmd=21,0,&.,&Help:
+cmd=22,1,&.,&Aide,&Kill this room
+cmd=23,1,&.,&Aide,&Edit this room
+cmd=24,0,&.,&Aide,&Who knows room
+cmd=25,2,&.,&Aide,&User,&Edit
+cmd=96,2,&.,&Aide,&User,&Delete
+cmd=26,2,&.,&Aide,&Validate new users
+cmd=48,1,&.,&Aide,enter &Info file
+cmd=27,0,&.,&Aide,&Room,&Invite user
+cmd=28,0,&.,&Aide,&Room,&Kick out user
+cmd=51,1,&.,&Aide,&File,&Delete
+cmd=54,1,&.,&Aide,&File,&Move
+cmd=70,2,&.,&Aide,&Message edit:
+cmd=78,1,&.,&Aide,&Post
+cmd=80,2,&.,&Aide,&System configuration,&General
+cmd=82,2,&.,&Aide,&System configuration,&Internet
+cmd=88,2,&.,&Aide,&System configuration,&Network
+cmd=92,2,&.,&Aide,&System configuration,network &Filter list
+cmd=85,2,&.,&Aide,&Terminate server,&Now
+cmd=86,2,&.,&Aide,&Terminate server,&Scheduled
+cmd=87,1,&.,&Aide,mailing &List recipients
+cmd=94,1,&.,&Aide,mailing list &Digest recipients
+cmd=89,1,&.,&Aide,&Network room sharing
+
+cmd=29,0,&.,&Terminate,and &Quit
+cmd=30,0,&.,&Terminate,and &Stay online
+
+# The 'read user listing' command accepts two different formats.  The one with
+# the trailing colon allows the user to enter a few characters to search for
+# users with a partial match.
+#
+#cmd=32,0,&.,&Read,&User listing
+cmd=32,0,&.,&Read,&User listing:
+
+cmd=33,0,&.,&Read,&Textfile formatted
+#
+# Command 55 allows the user to save a downloaded file directly to the
+# computer running the client software.  It is appropriate for a copy of
+# this client running on the user's own computer.  It is NOT appropriate for
+# public copies of the client that people will be dialing into.
+#
+#cmd=55,0,&.,&Read,&File
+#
+# Commands 34, 43, and 45 are appropriate for public copies of the client for
+# dialup use.  They transfer downloaded files to a temporary file and then
+# send them along to a dialup user using the popular protocols.
+#
+# cmd=34,0,&.,&Read,file using &Xmodem
+# cmd=43,0,&.,&Read,file using &Ymodem
+# cmd=45,0,&.,&Read,file using &Zmodem
+cmd=31,0,&.,&Read,&File unformatted
+#
+cmd=13,0,&.,&Read,&New messages
+cmd=12,0,&.,&Read,&Old msgs reverse
+cmd=71,0,&.,read &Last:
+cmd=9,0,&.,&Read,&Last five msgs
+cmd=14,0,&.,&Read,&Directory
+cmd=49,0,&.,&Read,&Info file
+cmd=114,0,&.,&Read,&Configuration
+cmd=115,0,&.,&Read,&System info
+cmd=35,0,&.,&Enter,&Password
+cmd=36,0,&.,&Enter,&ASCII message
+cmd=37,0,&.,&Enter,&Configuration
+cmd=38,0,&.,&Enter,a new &Room
+cmd=39,0,&.,&Enter,&Textfile
+cmd=77,0,&.,&Enter,&Username
+# cmd=40,0,&.,&Enter,file using &Xmodem
+# cmd=42,0,&.,&Enter,file using &Ymodem
+# cmd=44,0,&.,&Enter,file using &Zmodem
+#
+# Command 57 is the local-file-upload command for users with their own
+# copy of the clientware.  Commands 72-74 are for image uploads.
+#
+#cmd=57,0,&.,&Enter,&File
+#cmd=72,0,&.,&Enter,&Image,user &Picture
+#cmd=73,0,&.,&Enter,&Image,&Room banner
+#cmd=74,0,&.,&Enter,&Image,&Floor label
+#
+cmd=41,0,&.,&Enter,re&Gistration
+cmd=4,0,&.,&Enter,&Message
+cmd=127,0,&.,&Enter,remote POP&3 accounts
+cmd=128,0,&.,&Enter,&XML/RSS feed retrieval
+
+# If you have an external editor defined, it will appear on command 46.
+cmd=46,0,&.,&Enter,message with &Editor
+
+# If you have multiple editors defined, they will appear on
+# commands 46, 101, 102, 103... up to MAX_EDITORS (5 by default)
+#cmd=101,0,&.,&Enter,message with &VI
+#cmd=102,0,&.,&Enter,message with &Nano
+
+cmd=59,0,&;,&Configure floor mode
+cmd=60,0,&;,&Goto floor:
+cmd=60,0,&.,&Enter,&Floor:
+cmd=61,0,&;,&Skip to floor:
+cmd=62,0,&;,&Zap (forget) floor
+cmd=63,2,&;,&Aide,&Create floor
+cmd=64,2,&;,&Aide,&Edit this floor
+cmd=65,2,&;,&Aide,&Kill this floor
+cmd=68,0,&;,&Known rooms
+cmd=66,0,&.,&Enter,&Bio
+cmd=67,0,&.,&Read,&Bio
+
+cmd=79,0,&.,&Wholist,&Long
+cmd=75,0,&.,&Wholist,&Roomname
+cmd=76,0,&.,&Wholist,&Hostname
+cmd=91,0,&.,&Wholist,&Active
+cmd=93,0,&.,&Wholist,&Stealth mode
+
+cmd=110,0,&+Next room
+cmd=111,0,&-Previous room
+cmd=112,0,&>Next floor
+cmd=113,0,&<Previous floor
+cmd=116,0,&.,skip to &+Next room
+cmd=117,0,&.,skip to &-Previous room
+cmd=118,0,&.,skip to &>Next floor
+cmd=119,0,&.,skip to &<Previous floor
+
+cmd=120,0,&.,&Known,&Anonymous rooms
+cmd=121,0,&.,&Known,&Directory rooms
+cmd=68,0,&.,&Known,&Floors
+cmd=49,0,&.,&Known,room &Info
+cmd=122,0,&.,&Known,&Match rooms:
+cmd=123,0,&.,&Known,preferred &Only rooms
+cmd=124,0,&.,&Known,&Private rooms
+cmd=125,0,&.,&Known,&Read only rooms
+cmd=126,0,&.,&Known,&Shared rooms
+cmd=19,0,&.,&Known,&Zapped rooms
+
+#
+# Command 69 allows the user to enter a server command directly.  It is
+# primarily for testing and not intended for general use.  Usually there
+# is no need to enable it.
+cmd=69,0,&@Server command:
+#
+# end of command set configuration
+#
diff --git a/textclient/config.guess b/textclient/config.guess
new file mode 100755 (executable)
index 0000000..396482d
--- /dev/null
@@ -0,0 +1,1500 @@
+#! /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
new file mode 100755 (executable)
index 0000000..fab0aa3
--- /dev/null
@@ -0,0 +1,1616 @@
+#! /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.ac b/textclient/configure.ac
new file mode 100644 (file)
index 0000000..44f7633
--- /dev/null
@@ -0,0 +1,880 @@
+dnl Process this file with autoconf to produce a configure script.
+AC_PREREQ(2.52)
+AC_INIT([Citadel], [8.11], [http://www.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 utmp.h utmpx.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 ut_type in struct utmp], ac_cv_struct_ut_type,
+[AC_TRY_COMPILE([#include <sys/types.h>
+#include <utmp.h>], [struct utmp ut; ut.ut_type;],
+ac_cv_struct_ut_type=yes, ac_cv_struct_ut_type=no)])
+if test $ac_cv_struct_ut_type = yes; then
+       AC_DEFINE(HAVE_UT_TYPE, [], [define this if struct utmp has an ut_type member])
+fi
+
+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
+
+
+
+AC_CACHE_CHECK([for ut_host in struct utmp], ac_cv_struct_ut_host,
+[AC_TRY_COMPILE([#include <sys/types.h>
+#include <utmp.h>], [struct utmp ut; ut.ut_host;],
+ac_cv_struct_ut_host=yes, ac_cv_struct_ut_host=no)])
+if test $ac_cv_struct_ut_host = yes; then
+       AC_DEFINE(HAVE_UT_HOST, [], [define this if struct utmp has an ut_host member])
+fi
+
+dnl Checks for library functions.
+AC_FUNC_GETPGRP
+AC_PROG_GCC_TRADITIONAL
+AC_TYPE_SIGNAL
+AC_FUNC_VPRINTF
+AC_CHECK_FUNCS(getspnam getutxline 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
+       ])
+
+
+STRUCT_UCRED
+
+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/debian/changelog b/textclient/debian/changelog
new file mode 100644 (file)
index 0000000..02b9d6a
--- /dev/null
@@ -0,0 +1,6 @@
+citadel (8.20-1) stable; urgency=low
+
+  * new upstream version
+
+ -- Wilfried Goesgens <w.goesgens@outgesourced.org>  Wed, 16 May 2012 22:00:00 +0001
+
diff --git a/textclient/debian/control b/textclient/debian/control
new file mode 100644 (file)
index 0000000..3f6fc7d
--- /dev/null
@@ -0,0 +1,17 @@
+Source: citadel
+Section: mail
+Priority: extra
+Maintainer: Wilfried Goesgens <w.goesgens@outgesourced.org>
+Build-Depends: debhelper (>= 4), po-debconf, autotools-dev,
+ gettext, locales, libcitadel-dev (>= 8.11),
+Standards-Version: 3.8.0
+
+Package: citadel-client
+Architecture: any
+Recommends: shared-mime-info
+Depends: ${shlibs:Depends}, ${misc:Depends}, adduser, citadel-common
+Description: complete and feature-rich groupware server (command line client)
+ This is package contains the command line client for Citadel, a complete and
+ feature-rich open source groupware platform.
+ .
+ See the 'citadel-server' package for more information.
diff --git a/textclient/debian/copyright b/textclient/debian/copyright
new file mode 100644 (file)
index 0000000..4a103de
--- /dev/null
@@ -0,0 +1,131 @@
+This package was debianized by Fathi Boudra <fboudra@free.fr> on
+Wed, 27 Dec 2006 17:30:35 +0100.
+
+It was downloaded from http://www.citadel.org
+
+Upstream Authors (the citadel development team):
+   Clint Adams
+   Steven M. Bellovin
+   Nathan Bryant
+   Art Cancro
+   Brian Costello
+   Nick Georbit
+   David Given
+   Wilfried Goesgens
+   Michael Hampton
+   Andru Luvisi
+   Daniel Malament
+   Stu Mark
+   Edward S. Marshall
+   Ben Mehlman
+   Matt Pfleger
+   Ari Samson
+   Trey Van Riper
+   John Walker
+   Steve Williams
+   Ethan Young
+
+Copyright: (C) 1987-2012 Citadel development team
+
+Copyright for CRC16: (C) 1996-2003 Indigo Systems Corporation
+
+Copyright for MD5 implementation: (C) 1993 Colin Plumb
+
+Copyright for RFC 2739 openldap schema:
+   (C) 2000 The Internet Society
+   (C) 2004 Martin Konold <martin.konold@erfrakon.de>
+   (C) 2006 Art Cancro <ajc@uncensored.citadel.org>
+
+CRC16 license:
+
+   Redistribution and use in source and binary forms, with or without
+   modification, are permitted provided that the following conditions are met:
+
+   Redistributions of source code must retain the above copyright notice, this
+   list of conditions and the following disclaimer.
+
+   Redistributions in binary form must reproduce the above copyright notice,
+   this list of conditions and the following disclaimer in the documentation
+   and/or other materials provided with the distribution.
+
+   Neither the name of the Indigo Systems Corporation nor the names of its
+   contributors may be used to endorse or promote products derived from this
+   software without specific prior written permission.
+
+   THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+   AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+   IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+   ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
+   LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+   CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+   SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+   INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+   CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+   ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
+   THE POSSIBILITY OF SUCH DAMAGE.
+
+MD5 license:
+
+   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.
+
+RFC 2739 openldap schema license:
+
+   This document and translations of it may be copied and furnished to
+   others, and derivative works that comment on or otherwise explain it
+   or assist in its implementation may be prepared, copied, published
+   and distributed, in whole or in part, without restriction of any
+   kind, provided that the above copyright notice and this paragraph are
+   included on all such copies and derivative works.  However, this
+   document itself may not be modified in any way, such as by removing
+   the copyright notice or references to the Internet Society or other
+   Internet organizations, except as needed for the purpose of
+   developing Internet standards in which case the procedures for
+   copyrights defined in the Internet Standards process must be
+   followed, or as required to translate it into languages other than
+   English.
+
+   The limited permissions granted above are perpetual and will not be
+   revoked by the Internet Society or its successors or assigns.
+
+   This document and the information contained herein is provided on an
+   "AS IS" basis and THE INTERNET SOCIETY AND THE INTERNET ENGINEERING
+   TASK FORCE DISCLAIMS ALL WARRANTIES, EXPRESS OR IMPLIED, INCLUDING
+   BUT NOT LIMITED TO ANY WARRANTY THAT THE USE OF THE INFORMATION
+   HEREIN WILL NOT INFRINGE ANY RIGHTS OR ANY IMPLIED WARRANTIES OF
+   MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE.
+
+citadel license:
+
+   This package 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; version 3 of the License.
+
+   This package 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 package; if not, write to the Free Software
+   Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301 USA
+
+* In addition, as a special exception, the copyright holders give
+* permission to link the code of portions of this program with the
+* OpenSSL library under certain conditions as described in each
+* individual source file, and distribute linked combinations
+* including the two.
+* You must obey the GNU General Public License in all respects
+* for all of the code used other than OpenSSL. If you modify
+* file(s) with this exception, you may extend this exception to your
+* version of the file(s), but you are not obligated to do so. If you
+* do not wish to do so, delete this exception statement from your
+* version. If you delete this exception statement from all source
+* files in the program, then also delete it here.
+
+On Debian systems, the complete text of the GNU General
+Public License can be found in `/usr/share/common-licenses/GPL-3'.
+
+The Debian packaging is Copyright (C) 2006-2012, Debian Citadel Team
+<pkg-citadel-devel@lists.alioth.debian.org> and is licensed under the GPL, see
+above.
diff --git a/textclient/debian/rules b/textclient/debian/rules
new file mode 100755 (executable)
index 0000000..7cb8f65
--- /dev/null
@@ -0,0 +1,158 @@
+#!/usr/bin/make -f
+
+# Uncomment this to turn on verbose mode.
+#export DH_VERBOSE=1
+
+# This has to be exported to make some magic below work.
+DEB_HOST_GNU_TYPE   ?= $(shell dpkg-architecture -qDEB_HOST_GNU_TYPE)
+DEB_BUILD_GNU_TYPE  ?= $(shell dpkg-architecture -qDEB_BUILD_GNU_TYPE)
+
+export DH_OPTIONS
+
+DEB_DESTDIR = $(CURDIR)/debian/tmp
+
+CFLAGS = -Wall -g -Werror=format-security 
+
+COMPILER=
+EXTRA_ARGS=
+PROFILE_ARGS=
+#to enable debugging: export DEB_BUILD_OPTIONS="debug profiling threadoff rss gcov clang cares"
+ifneq (,$(findstring clang,$(DEB_BUILD_OPTIONS)))
+        COMPILER="clang" 
+endif
+ifneq (,$(findstring profiling,$(DEB_BUILD_OPTIONS)))
+       PROFILE_ARGS= --with-gprof
+endif
+
+ifneq (,$(findstring debug,$(DEB_BUILD_OPTIONS)))
+       CFLAGS += -O0 -ggdb -rdynamic -D_GNU_SOURCE -MD -MP -D TECH_PREVIEW
+       LDFLAGS+=-Wl,--no-undefined -Wl,--as-needed
+       EXTRA_ARGS = --with-backtrace
+       ifneq (,$(findstring event,$(DEB_BUILD_OPTIONS)))
+               EXTRA_ARGS = --with-backtrace
+       endif
+else
+       LDFLAGS+=-Wl,--no-undefined -Wl,--as-needed
+       CFLAGS += -O2
+endif
+
+ifneq (,$(findstring backtrace,$(DEB_BUILD_OPTIONS)))
+       CFLAGS+= -rdynamic -D_GNU_SOURCE -MD -MP
+       LDFLAGS+=-Wl,--no-undefined -Wl,--as-needed
+       EXTRA_ARGS += --with-backtrace
+endif
+
+ifneq (,$(findstring threadoff,$(DEB_BUILD_OPTIONS)))
+       THREAD_ARGS=--without-threaded-client
+       CFLAGS += -D WITH_THREADLOG
+else
+       THREAD_ARGS=
+endif
+
+ifneq (,$(findstring iodbg,$(DEB_BUILD_OPTIONS)))
+       CFLAGS += -D BIGBAD_IODBG
+endif
+
+ifneq (,$(findstring gcov,$(DEB_BUILD_OPTIONS)))
+       CFLAGS += -fprofile-arcs -ftest-coverage
+       LDFLAGS += -fprofile-arcs -ftest-coverage
+endif
+
+configure: configure-stamp
+configure-stamp:
+       dh_testdir
+
+       export CC=$(COMPILER); export LDFLAGS="$(LDFLAGS)"; export CFLAGS="$(CFLAGS)"; ./configure \
+               --prefix=/var/lib/citadel/ \
+               --with-datadir=/var/lib/citadel/ \
+               --with-helpdir=/usr/share/citadel-server/ \
+               --with-staticdatadir=/etc/citadel/ \
+               --with-spooldir=/var/spool/citadel/ \
+               --with-sysconfdir=/etc/citadel/ \
+               --with-rundir=/var/run/citadel/ \
+               --with-docdir=/usr/share/doc/citadel-doc/ \
+               --with-ssldir=/etc/ssl/citadel/ \
+               --with-utility-bindir=/usr/lib/citadel-server/ \
+               --with-autosysconfdir=/var/lib/citadel/data/ \
+                --with-localedir=/usr/share/ \
+               --with-pam \
+               --with-db \
+               --enable-debug $(EXTRA_ARGS) $(PROFILE_ARGS) $(THREAD_ARGS)
+
+       touch configure-stamp
+
+#Architecture 
+build: build-arch build-indep
+
+build-arch: build-arch-stamp
+build-arch-stamp: configure-stamp
+
+       $(MAKE)
+       touch $@
+
+build-indep: build-indep-stamp
+build-indep-stamp: configure-stamp
+
+       touch $@
+
+clean:
+       dh_testdir
+       dh_testroot
+       rm -f build-arch-stamp build-indep-stamp configure-stamp
+
+
+       dh_clean 
+       rm -f config.status config.log
+#[ ! -f Makefile ] || $(MAKE) distclean
+
+install: install-indep install-arch
+install-indep: build-indep
+       dh_testdir
+       dh_testroot
+       dh_clean -k -i 
+       dh_installdirs -i
+
+       $(MAKE) DESTDIR=$(DEB_DESTDIR) install-doc-new
+
+       dh_install -i --sourcedir=debian/tmp
+
+install-arch: build-arch
+       dh_testdir
+       dh_testroot
+       dh_clean -k -s 
+       dh_installdirs -s
+
+       $(MAKE) install-locale DESTDIR=$(DEB_DESTDIR)
+       $(MAKE) DESTDIR=$(DEB_DESTDIR) install-exec-new install-data-new
+
+       dh_install -s --sourcedir=debian/tmp
+
+binary-common:
+       dh_testdir
+       dh_testroot
+       dh_installchangelogs
+       dh_installdocs
+       dh_installdebconf       
+       dh_installinit --name=citadel
+       dh_installman
+       dh_strip --dbg-package=citadel-dbg
+       dh_link
+       dh_compress 
+       dh_fixperms
+       dh_makeshlibs
+       dh_installdeb
+       dh_shlibdeps
+       dh_gencontrol
+       dh_md5sums
+       dh_builddeb
+
+# Build architecture independant packages using the common target.
+binary-indep: build-indep install-indep
+       $(MAKE) -f debian/rules DH_OPTIONS=-i binary-common
+
+# Build architecture dependant packages using the common target.
+binary-arch: build-arch install-arch
+       $(MAKE) -f debian/rules DH_OPTIONS=-s binary-common
+
+binary: binary-arch binary-indep
+.PHONY: build clean binary-indep binary-arch binary install install-indep install-arch configure
diff --git a/textclient/install-sh b/textclient/install-sh
new file mode 100755 (executable)
index 0000000..ebc6691
--- /dev/null
@@ -0,0 +1,250 @@
+#! /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/ltmain.sh b/textclient/ltmain.sh
new file mode 120000 (symlink)
index 0000000..4159958
--- /dev/null
@@ -0,0 +1 @@
+/usr/share/libtool/config/ltmain.sh
\ No newline at end of file
diff --git a/textclient/m4/libtool.m4 b/textclient/m4/libtool.m4
new file mode 120000 (symlink)
index 0000000..f3504e9
--- /dev/null
@@ -0,0 +1 @@
+/usr/share/aclocal/libtool.m4
\ No newline at end of file
diff --git a/textclient/m4/ltoptions.m4 b/textclient/m4/ltoptions.m4
new file mode 120000 (symlink)
index 0000000..b81279e
--- /dev/null
@@ -0,0 +1 @@
+/usr/share/aclocal/ltoptions.m4
\ No newline at end of file
diff --git a/textclient/m4/ltsugar.m4 b/textclient/m4/ltsugar.m4
new file mode 120000 (symlink)
index 0000000..4d76cc7
--- /dev/null
@@ -0,0 +1 @@
+/usr/share/aclocal/ltsugar.m4
\ No newline at end of file
diff --git a/textclient/m4/ltversion.m4 b/textclient/m4/ltversion.m4
new file mode 120000 (symlink)
index 0000000..5eb474f
--- /dev/null
@@ -0,0 +1 @@
+/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
new file mode 120000 (symlink)
index 0000000..de0b9f9
--- /dev/null
@@ -0,0 +1 @@
+/usr/share/aclocal/lt~obsolete.m4
\ No newline at end of file
diff --git a/textclient/missing b/textclient/missing
new file mode 100755 (executable)
index 0000000..0a7fb5a
--- /dev/null
@@ -0,0 +1,283 @@
+#! /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
new file mode 100755 (executable)
index 0000000..f945dbf
--- /dev/null
@@ -0,0 +1,38 @@
+#! /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/scripts/mk_svn_revision.sh b/textclient/scripts/mk_svn_revision.sh
new file mode 100755 (executable)
index 0000000..144ffa4
--- /dev/null
@@ -0,0 +1,78 @@
+#!/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
new file mode 100644 (file)
index 0000000..3b95889
--- /dev/null
@@ -0,0 +1,2273 @@
+/*
+ * Main source module for the client program.
+ *
+ * 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.
+ */
+
+#include "sysdep.h"
+#include <stdlib.h>
+#include <unistd.h>
+#include <fcntl.h>
+#include <stdio.h>
+#include <ctype.h>
+#include <string.h>
+
+#if TIME_WITH_SYS_TIME
+# include <sys/time.h>
+# include <time.h>
+#else
+# if HAVE_SYS_TIME_H
+#  include <sys/time.h>
+# else
+#  include <time.h>
+# endif
+#endif
+
+#include <limits.h>
+#include <sys/types.h>
+#include <sys/wait.h>
+#include <sys/stat.h>
+#include <sys/ioctl.h>
+#include <signal.h>
+#include <pwd.h>
+#include <stdarg.h>
+#include <errno.h>
+#include <libcitadel.h>
+///#include "citadel.h"
+#include "citadel_ipc.h"
+//#include "axdefs.h"
+#include "routines.h"
+#include "routines2.h"
+#include "tuiconfig.h"
+#include "rooms.h"
+#include "messages.h"
+#include "commands.h"
+#include "client_chat.h"
+#include "client_passwords.h"
+#include "citadel_decls.h"
+#include "sysdep.h"
+#ifndef HAVE_SNPRINTF
+#include "snprintf.h"
+#endif
+#include "screen.h"
+///#include "citadel_dirs.h"
+
+#include "ecrash.h"
+#include "md5.h"
+
+#define IFEXPERT if (userflags&US_EXPERT)
+#define IFNEXPERT if ((userflags&US_EXPERT)==0)
+#define IFAIDE if (axlevel>=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 aide */
+int timescalled;
+int posted;
+unsigned userflags;
+long usernum = 0L;             /* user number */
+time_t lastcall = 0L;          /* Date/time of previous login */
+char newnow;
+long highest_msg_read;         /* used for <A>bandon room cmd */
+long maxmsgnum;                        /* used for <G>oto */
+char sigcaught = 0;
+char 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);
+    scr_printf("Copyright (C)1987-2009 by the Citadel development team\n");
+}
+
+/*
+ * forget all rooms on current floor
+ */
+void forget_this_floor(CtdlIPC *ipc)
+{
+       if (curr_floor == 0) {
+               scr_printf("Can't forget this floor.\n");
+               return;
+       }
+       if (floorlist[0][0] == 0) {
+               load_floorlist(ipc);
+       }
+       scr_printf("Are you sure you want to forget all rooms on %s? ",
+              &floorlist[(int) curr_floor][0]);
+       if (yesno() == 0) {
+               return;
+       }
+
+       gf_toroom(ipc, "_BASEROOM_", GF_ZAP);
+}
+
+
+/*
+ * 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.filename = file_pid_paniclog;
+//     panic_fd=open(file_pid_paniclog, O_APPEND|O_CREAT|O_DIRECT);
+///    params.filep = fopen(file_pid_paniclog, "a+");
+       params.debugLevel = ECRASH_DEBUG_VERBOSE;
+       params.dumpAllThreads = TRUE;
+       params.useBacktraceSymbols = 1;
+///    BuildSymbolTable(&symbol_table);
+//     params.symbolTable = &symbol_table;
+       params.signals[0]=SIGSEGV;
+       params.signals[1]=SIGILL;
+       params.signals[2]=SIGBUS;
+       params.signals[3]=SIGABRT;
+
+///    eCrash_Init(&params);
+#endif 
+       setIPCErrorPrintf(scr_printf);
+       setCryptoStatusHook(statusHook);
+       
+       /* Permissions sanity check - don't run citadel setuid/setgid */
+       if (getuid() != geteuid()) {
+               scr_printf("Please do not run citadel setuid!\n");
+               logoff(NULL, 3);
+       } else if (getgid() != getegid()) {
+               scr_printf("Please do not run citadel setgid!\n");
+               logoff(NULL, 3);
+       }
+
+       stty_ctdl(SB_SAVE);     /* Store the old terminal parameters */
+       load_command_set();     /* parse the citadel.rc file */
+       stty_ctdl(SB_NO_INTR);  /* Install the new ones */
+       /* signal(SIGHUP, dropcarr);FIXME */    /* Cleanup gracefully if carrier is dropped */
+       signal(SIGPIPE, dropcarr);      /* Cleanup gracefully if local conn. dropped */
+       signal(SIGTERM, dropcarr);      /* Cleanup gracefully if terminated */
+       signal(SIGCONT, catch_sigcont); /* Catch SIGCONT so we can reset terminal */
+#ifdef SIGWINCH
+       signal(SIGWINCH, scr_winch);    /* Window resize signal */
+#endif
+
+#ifdef HAVE_OPENSSL
+       arg_encrypt = RC_DEFAULT;
+#endif
+
+       /* 
+        * 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")) {
+                       struct stat st;
+               
+                       if (chdir(CTDLDIR) < 0) {
+                               perror("can't change to " CTDLDIR);
+                               logoff(NULL, 3);
+                       }
+
+                       /*
+                        * Drop privileges if necessary. We stat
+                        * citadel.config to get the uid/gid since it's
+                        * guaranteed to have the uid/gid we want.
+                        */
+                       if (!getuid() || !getgid()) {
+                               if (stat(file_citadel_config, &st) < 0) {
+                                       perror("couldn't stat citadel.config");
+                                       logoff(NULL, 3);
+                               }
+                               if (!getgid() && (setgid(st.st_gid) < 0)) {
+                                       perror("couldn't change gid");
+                                       logoff(NULL, 3);
+                               }
+                               if (!getuid() && (setuid(st.st_uid) < 0)) {
+                                       perror("couldn't change uid");
+                                       logoff(NULL, 3);
+                               }
+                               /*
+                                 scr_printf("Privileges changed to uid %d gid %d\n",
+                                 getuid(), getgid());
+                               */
+                       }
+                       argc = shift(argc, argv, a, 1);
+               }
+       }
+       
+
+       screen_new();
+       /* 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:
+                               formout(ipc, "help");
+                               break;
+                       case 4:
+                               entmsg(ipc, 0, ((userflags & US_EXTEDIT) ? 2 : 0), 0);
+                               break;
+                       case 36:
+                               entmsg(ipc, 0, 1, 0);
+                               break;
+                       case 46:
+                               entmsg(ipc, 0, 2, 0);
+                               break;
+                       case 78:
+                               entmsg(ipc, 0, ((userflags & US_EXTEDIT) ? 2 : 0), 1);
+                               break;
+                       case 5:                         /* <G>oto */
+                               updatels(ipc);
+                               gotonext(ipc);
+                               break;
+                       case 47:                        /* <A>bandon */
+                               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 74:
+                               snprintf(aaa, sizeof aaa, "_floorpic_|%d", curr_floor);
+                               cli_image_upload(ipc, aaa);
+                               break;
+
+                       case 75:
+                               enternew(ipc, "roomname", aaa, 20);
+                               r = CtdlIPCChangeRoomname(ipc, aaa, bbb);
+                               if (r / 100 != 2)
+                                       scr_printf("\n%s\n", bbb);
+                               break;
+                       case 76:
+                               enternew(ipc, "hostname", aaa, 25);
+                               r = CtdlIPCChangeHostname(ipc, aaa, bbb);
+                               if (r / 100 != 2)
+                                       scr_printf("\n%s\n", bbb);
+                               break;
+                       case 77:
+                               enternew(ipc, "username", aaa, 32);
+                               r = CtdlIPCChangeUsername(ipc, aaa, bbb);
+                               if (r / 100 != 2)
+                                       scr_printf("\n%s\n", bbb);
+                               break;
+
+                       case 35:
+                               set_password(ipc);
+                               break;
+
+                       case 21:
+                               if (argbuf[0] == 0)
+                                       strcpy(aaa, "?");
+                               display_help(ipc, argbuf);
+                               break;
+
+                       case 41:
+                               formout(ipc, "register");
+                               entregis(ipc);
+                               break;
+
+                       case 15:
+                               scr_printf("Are you sure (y/n)? ");
+                               if (yesno() == 1) {
+                                       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 89:
+                               network_config_management(ipc, "ignet_push_share",
+                                                         "Nodes with which we share this room");
+                               break;
+
+                       case 88:
+                               do_ignet_configuration(ipc);
+                               break;
+
+                       case 92:
+                               do_filterlist_configuration(ipc);
+                               break;
+
+                       case 6:
+                               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
new file mode 100644 (file)
index 0000000..8a65f85
--- /dev/null
@@ -0,0 +1,3470 @@
+/*
+ * 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.
+ */
+
+#include "sysdep.h"
+#if TIME_WITH_SYS_TIME
+# include <sys/time.h>
+# include <time.h>
+#else
+# if HAVE_SYS_TIME_H
+#  include <sys/time.h>
+# else
+#  include <time.h>
+# endif
+#endif
+#include <unistd.h>
+#include <stdio.h>
+#include <sys/types.h>
+#include <string.h>
+#ifdef HAVE_MALLOC_H
+#include <malloc.h>
+#endif
+#include <stdlib.h>
+#include <ctype.h>
+#include <sys/socket.h>
+#include <arpa/inet.h>
+#include <netinet/in.h>
+#include <netdb.h>
+#include <sys/un.h>
+#include <errno.h>
+#ifdef THREADED_CLIENT
+#include <pthread.h>
+#endif
+#include <libcitadel.h>
+///#include "citadel.h"
+#include "citadel_ipc.h"
+//#include "citadel_decls.h"
+//#include "citadel_dirs.h"
+#ifdef THREADED_CLIENT
+pthread_mutex_t rwlock;
+#endif
+
+#ifdef HAVE_OPENSSL
+static SSL_CTX *ssl_ctx;
+char arg_encrypt;
+char rc_encrypt;
+#ifdef THREADED_CLIENT
+pthread_mutex_t **Critters;                    /* Things that need locking */
+#endif /* THREADED_CLIENT */
+
+#endif /* HAVE_OPENSSL */
+
+#ifndef INADDR_NONE
+#define INADDR_NONE 0xffffffff
+#endif
+
+static void (*status_hook)(char *s) = NULL;
+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 file_citadel_config[PATH_MAX]="";
+
+
+char *viewdefs[]={
+        "Messages",
+        "Summary",
+        "Address book",
+        "Calendar",
+        "Tasks"
+};
+
+char *axdefs[]={
+        "Deleted",
+        "New User",
+        "Problem User",
+        "Local User",
+        "Network User",
+        "Preferred User",
+        "Aide",
+        "Sysop"
+        };
+
+
+INLINE void CtdlIPC_lock(CtdlIPC *ipc)
+{
+       if (ipc->network_status_cb) ipc->network_status_cb(1);
+#ifdef THREADED_CLIENT
+       pthread_mutex_lock(&(ipc->mutex));
+#endif
+}
+
+
+INLINE void CtdlIPC_unlock(CtdlIPC *ipc)
+{
+#ifdef THREADED_CLIENT
+       pthread_mutex_unlock(&(ipc->mutex));
+#endif
+       if (ipc->network_status_cb) ipc->network_status_cb(0);
+}
+
+#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);
+
+       snprintf(file_citadel_config, 
+                        sizeof file_citadel_config,
+                        "%scitadel.config",
+                        ctdl_autoetc_dir);
+       StripSlashes(file_citadel_config, 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)
+{
+       register int ret;
+       char *aaa;
+       
+       if (!arg) return -2;
+       if (!cret) return -2;
+
+       aaa = (char *)malloc((size_t)(strlen(arg) + 6));
+       if (!aaa) return -1;
+
+       sprintf(aaa, "ECHO %s", arg);
+       ret = CtdlIPCGenericCommand(ipc, aaa, NULL, 0, NULL, NULL, cret);
+       free(aaa);
+       return ret;
+}
+
+
+/*
+ * Asks the server to close the connecction.
+ * Should always return 200.
+ */
+int CtdlIPCQuit(CtdlIPC *ipc)
+{
+       register int ret = 221;         /* Default to successful quit */
+       char aaa[SIZ]; 
+
+       CtdlIPC_lock(ipc);
+       if (ipc->sock > -1) {
+               CtdlIPC_putline(ipc, "QUIT");
+               CtdlIPC_getline(ipc, aaa);
+               ret = atoi(aaa);
+       }
+#ifdef HAVE_OPENSSL
+       if (ipc->ssl)
+               SSL_shutdown(ipc->ssl);
+       ipc->ssl = NULL;
+#endif
+       if (ipc->sock)
+               shutdown(ipc->sock, 2); /* Close connection; we're dead */
+       ipc->sock = -1;
+       CtdlIPC_unlock(ipc);
+       return ret;
+}
+
+
+/*
+ * Asks the server to log out.  Should always return 200, even if no user
+ * was logged in.  The user will not be logged in after this!
+ */
+int CtdlIPCLogout(CtdlIPC *ipc)
+{
+       register int ret;
+       char aaa[SIZ];
+
+       CtdlIPC_lock(ipc);
+       CtdlIPC_putline(ipc, "LOUT");
+       CtdlIPC_getline(ipc, aaa);
+       ret = atoi(aaa);
+       CtdlIPC_unlock(ipc);
+       return ret;
+}
+
+
+/*
+ * First stage of authentication - pass the username.  Returns 300 if the
+ * username is able to log in, with the username correctly spelled in cret.
+ * Returns various 500 error codes if the user doesn't exist, etc.
+ */
+int CtdlIPCTryLogin(CtdlIPC *ipc, const char *username, char *cret)
+{
+       register int ret;
+       char *aaa;
+
+       if (!username) return -2;
+       if (!cret) return -2;
+
+       aaa = (char *)malloc((size_t)(strlen(username) + 6));
+       if (!aaa) return -1;
+
+       sprintf(aaa, "USER %s", username);
+       ret = CtdlIPCGenericCommand(ipc, aaa, NULL, 0, NULL, NULL, cret);
+       free(aaa);
+       return ret;
+}
+
+
+/*
+ * Second stage of authentication - provide password.  The server returns
+ * 200 and several arguments in cret relating to the user's account.
+ */
+int CtdlIPCTryPassword(CtdlIPC *ipc, const char *passwd, char *cret)
+{
+       register int ret;
+       char *aaa;
+
+       if (!passwd) return -2;
+       if (!cret) return -2;
+
+       aaa = (char *)malloc((size_t)(strlen(passwd) + 6));
+       if (!aaa) return -1;
+
+       sprintf(aaa, "PASS %s", passwd);
+       ret = CtdlIPCGenericCommand(ipc, aaa, NULL, 0, NULL, NULL, cret);
+       free(aaa);
+       return ret;
+}
+
+
+/*
+ * Second stage of authentication - provide password.  The server returns
+ * 200 and several arguments in cret relating to the user's account.
+ */
+int CtdlIPCTryApopPassword(CtdlIPC *ipc, const char *response, char *cret)
+{
+       register int ret;
+       char *aaa;
+
+       if (!response) return -2;
+       if (!cret) return -2;
+
+       aaa = (char *)malloc((size_t)(strlen(response) + 6));
+       if (!aaa) return -1;
+
+       sprintf(aaa, "PAS2 %s", response);
+       ret = CtdlIPCGenericCommand(ipc, aaa, NULL, 0, NULL, NULL, cret);
+       free(aaa);
+       return ret;
+}
+
+
+/*
+ * Create a new user.  This returns 200 plus the same arguments as TryPassword
+ * if selfservice is nonzero, unless there was a problem creating the account.
+ * If selfservice is zero, creates a new user but does not log out the existing
+ * user - intended for use by system administrators to create accounts on
+ * behalf of other users.
+ */
+int CtdlIPCCreateUser(CtdlIPC *ipc, const char *username, int selfservice, char *cret)
+{
+       register int ret;
+       char *aaa;
+
+       if (!username) return -2;
+       if (!cret) return -2;
+
+       aaa = (char *)malloc((size_t)(strlen(username) + 6));
+       if (!aaa) return -1;
+
+       sprintf(aaa, "%s %s", selfservice ? "NEWU" : "CREU",  username);
+       ret = CtdlIPCGenericCommand(ipc, aaa, NULL, 0, NULL, NULL, cret);
+       free(aaa);
+       return ret;
+}
+
+
+/*
+ * Changes the user's password.  Returns 200 if changed, errors otherwise.
+ */
+int CtdlIPCChangePassword(CtdlIPC *ipc, const char *passwd, char *cret)
+{
+       register int ret;
+       char *aaa;
+
+       if (!passwd) return -2;
+       if (!cret) return -2;
+
+       aaa = (char *)malloc((size_t)(strlen(passwd) + 6));
+       if (!aaa) return -1;
+
+       sprintf(aaa, "SETP %s", passwd);
+       ret = CtdlIPCGenericCommand(ipc, aaa, NULL, 0, NULL, NULL, cret);
+       free(aaa);
+       return ret;
+}
+
+
+/* LKRN */
+/* Caller must free the march list */
+/* Room types are defined in enum RoomList; keep these in sync! */
+/* floor is -1 for all, or floornum */
+int CtdlIPCKnownRooms(CtdlIPC *ipc, enum RoomList which, int floor, struct march **listing, char *cret)
+{
+       register int ret;
+       struct march *march = NULL;
+       static char *proto[] =
+               {"LKRA", "LKRN", "LKRO", "LZRM", "LRMS", "LPRM" };
+       char aaa[SIZ];
+       char *bbb = NULL;
+       size_t bbb_len;
+
+       if (!listing) return -2;
+       if (*listing) return -2;        /* Free the listing first */
+       if (!cret) return -2;
+       /* if (which < 0 || which > 4) return -2; */
+       if (floor < -1) return -2;      /* Can't validate upper bound, sorry */
+
+       sprintf(aaa, "%s %d", proto[which], floor);
+       ret = CtdlIPCGenericCommand(ipc, aaa, NULL, 0, &bbb, &bbb_len, cret);
+       if (ret / 100 == 1) {
+               struct march *mptr;
+
+               while (bbb && strlen(bbb)) {
+                       int a;
+
+                       extract_token(aaa, bbb, 0, '\n', sizeof aaa);
+                       a = strlen(aaa);
+                       memmove(bbb, bbb + a + 1, strlen(bbb) - a);
+                       mptr = (struct march *) malloc(sizeof (struct march));
+                       if (mptr) {
+                               mptr->next = NULL;
+                               extract_token(mptr->march_name, aaa, 0, '|', sizeof mptr->march_name);
+                               mptr->march_flags = (unsigned int) extract_int(aaa, 1);
+                               mptr->march_floor = (char) extract_int(aaa, 2);
+                               mptr->march_order = (char) extract_int(aaa, 3);
+                               mptr->march_flags2 = (unsigned int) extract_int(aaa, 4);
+                               mptr->march_access = (char) extract_int(aaa, 5);
+                               if (march == NULL)
+                                       march = mptr;
+                               else {
+                                       struct march *mptr2;
+
+                                       mptr2 = march;
+                                       while (mptr2->next != NULL)
+                                               mptr2 = mptr2->next;
+                                       mptr2->next = mptr;
+                               }
+                       }
+               }
+       }
+       *listing = march;
+       if (bbb) free(bbb);
+       return ret;
+}
+
+
+/* GETU */
+/* Caller must free the struct ctdluser; caller may pass an existing one */
+int CtdlIPCGetConfig(CtdlIPC *ipc, struct ctdluser **uret, char *cret)
+{
+       register int ret;
+
+       if (!cret) return -2;
+       if (!uret) return -2;
+       if (!*uret) *uret = (struct ctdluser *)calloc(1, sizeof (struct ctdluser));
+       if (!*uret) return -1;
+
+       ret = CtdlIPCGenericCommand(ipc, "GETU", NULL, 0, NULL, NULL, cret);
+       if (ret / 100 == 2) {
+               uret[0]->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)
+{
+       register int ret;
+       char cmd[256];
+
+       if (!oldname) return -2;
+       if (!newname) return -2;
+       if (!cret) return -2;
+
+       snprintf(cmd, sizeof cmd, "RENU %s|%s", oldname, newname);
+       ret = CtdlIPCGenericCommand(ipc, cmd, NULL, 0, NULL, NULL, cret);
+       return ret;
+}
+
+
+/* GOTO */
+int CtdlIPCGotoRoom(CtdlIPC *ipc, const char *room, const char *passwd,
+               struct ctdlipcroom **rret, char *cret)
+{
+       register int ret;
+       char *aaa;
+
+       if (!cret) return -2;
+       if (!rret) return -2;
+       if (!*rret) *rret = (struct ctdlipcroom *)calloc(1, sizeof (struct ctdlipcroom));
+       if (!*rret) return -1;
+
+       if (passwd) {
+               aaa = (char *)malloc(strlen(room) + strlen(passwd) + 7);
+               if (!aaa) {
+                       free(*rret);
+                       return -1;
+               }
+               sprintf(aaa, "GOTO %s|%s", room, passwd);
+       } else {
+               aaa = (char *)malloc(strlen(room) + 6);
+               if (!aaa) {
+                       free(*rret);
+                       return -1;
+               }
+               sprintf(aaa, "GOTO %s", room);
+       }
+       ret = CtdlIPCGenericCommand(ipc, aaa, NULL, 0, NULL, NULL, cret);
+       if (ret / 100 == 2) {
+               extract_token(rret[0]->RRname, cret, 0, '|', sizeof rret[0]->RRname);
+               rret[0]->RRunread = extract_long(cret, 1);
+               rret[0]->RRtotal = extract_long(cret, 2);
+               rret[0]->RRinfoupdated = extract_int(cret, 3);
+               rret[0]->RRflags = extract_int(cret, 4);
+               rret[0]->RRhighest = extract_long(cret, 5);
+               rret[0]->RRlastread = extract_long(cret, 6);
+               rret[0]->RRismailbox = extract_int(cret, 7);
+               rret[0]->RRaide = extract_int(cret, 8);
+               rret[0]->RRnewmail = extract_long(cret, 9);
+               rret[0]->RRfloor = extract_int(cret, 10);
+               rret[0]->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)
+{
+       register int ret;
+       register unsigned long count = 0;
+       static char *proto[] =
+               { "ALL", "OLD", "NEW", "LAST", "FIRST", "GT", "LT" };
+       char aaa[33];
+       char *bbb = NULL;
+       size_t bbb_len;
+
+       if (!cret) return -2;
+       if (!mret) return -2;
+       if (*mret) return -2;
+       if (which < 0 || which > 6) return -2;
+
+       if (which <= 2)
+               sprintf(aaa, "MSGS %s||%d", proto[which],
+                               (mtemplate) ? 1 : 0);
+       else
+               sprintf(aaa, "MSGS %s|%d|%d", proto[which], whicharg,
+                               (mtemplate) ? 1 : 0);
+       if (mtemplate) count = strlen(mtemplate);
+       ret = CtdlIPCGenericCommand(ipc, aaa, mtemplate, count, &bbb, &bbb_len, cret);
+       if (ret / 100 != 1)
+               return ret;
+       count = 0;
+       *mret = (unsigned long *)calloc(1, sizeof(unsigned long));
+       if (!*mret)
+               return -1;
+       while (bbb && strlen(bbb)) {
+               extract_token(aaa, bbb, 0, '\n', sizeof aaa);
+               remove_token(bbb, 0, '\n');
+               *mret = (unsigned long *)realloc(*mret, (size_t)((count + 2) *
+                                       sizeof (unsigned long)));
+               if (*mret) {
+                       (*mret)[count++] = atol(aaa);
+                       (*mret)[count] = 0L;
+               } else {
+                       break;
+               }
+       }
+       if (bbb) free(bbb);
+       return ret;
+}
+
+
+/* MSG0, MSG2 */
+int CtdlIPCGetSingleMessage(CtdlIPC *ipc, long msgnum, int headers, int as_mime,
+               struct ctdlipcmessage **mret, char *cret)
+{
+       register int ret;
+       char aaa[SIZ];
+       char *bbb = NULL;
+       size_t bbb_len;
+       int multipart_hunting = 0;
+       char multipart_prefix[128];
+       char encoding[256];
+
+       if (!cret) return -1;
+       if (!mret) return -1;
+       if (!*mret) *mret = (struct ctdlipcmessage *)calloc(1, sizeof (struct ctdlipcmessage));
+       if (!*mret) return -1;
+       if (!msgnum) return -1;
+
+       strcpy(encoding, "");
+       strcpy(mret[0]->content_type, "");
+       sprintf(aaa, "MSG%d %ld|%d", as_mime, msgnum, headers);
+       ret = CtdlIPCGenericCommand(ipc, aaa, NULL, 0, &bbb, &bbb_len, cret);
+       if (ret / 100 == 1) {
+               if (as_mime != 2) {
+                       strcpy(mret[0]->mime_chosen, "1");      /* Default chosen-part is "1" */
+                       while (strlen(bbb) > 4 && bbb[4] == '=') {
+                               extract_token(aaa, bbb, 0, '\n', sizeof aaa);
+                               remove_token(bbb, 0, '\n');
+
+                               if (!strncasecmp(aaa, "nhdr=yes", 8))
+                                       mret[0]->nhdr = 1;
+                               else if (!strncasecmp(aaa, "from=", 5))
+                                       safestrncpy(mret[0]->author, &aaa[5], SIZ);
+                               else if (!strncasecmp(aaa, "type=", 5))
+                                       mret[0]->type = atoi(&aaa[5]);
+                               else if (!strncasecmp(aaa, "msgn=", 5))
+                                       safestrncpy(mret[0]->msgid, &aaa[5], SIZ);
+                               else if (!strncasecmp(aaa, "subj=", 5))
+                                       safestrncpy(mret[0]->subject, &aaa[5], SIZ);
+                               else if (!strncasecmp(aaa, "rfca=", 5))
+                                       safestrncpy(mret[0]->email, &aaa[5], SIZ);
+                               else if (!strncasecmp(aaa, "hnod=", 5))
+                                       safestrncpy(mret[0]->hnod, &aaa[5], SIZ);
+                               else if (!strncasecmp(aaa, "room=", 5))
+                                       safestrncpy(mret[0]->room, &aaa[5], SIZ);
+                               else if (!strncasecmp(aaa, "node=", 5))
+                                       safestrncpy(mret[0]->node, &aaa[5], SIZ);
+                               else if (!strncasecmp(aaa, "rcpt=", 5))
+                                       safestrncpy(mret[0]->recipient, &aaa[5], SIZ);
+                               else if (!strncasecmp(aaa, "wefw=", 5))
+                                       safestrncpy(mret[0]->references, &aaa[5], SIZ);
+                               else if (!strncasecmp(aaa, "time=", 5))
+                                       mret[0]->time = atol(&aaa[5]);
+
+                               /* Multipart/alternative prefix & suffix strings help
+                                * us to determine which part we want to download.
+                                */
+                               else if (!strncasecmp(aaa, "pref=", 5)) {
+                                       extract_token(multipart_prefix, &aaa[5], 1, '|', sizeof multipart_prefix);
+                                       if (!strcasecmp(multipart_prefix,
+                                          "multipart/alternative")) {
+                                               ++multipart_hunting;
+                                       }
+                               }
+                               else if (!strncasecmp(aaa, "suff=", 5)) {
+                                       extract_token(multipart_prefix, &aaa[5], 1, '|', sizeof multipart_prefix);
+                                       if (!strcasecmp(multipart_prefix,
+                                          "multipart/alternative")) {
+                                               ++multipart_hunting;
+                                       }
+                               }
+
+                               else if (!strncasecmp(aaa, "part=", 5)) {
+                                       struct parts *ptr, *chain;
+       
+                                       ptr = (struct parts *)calloc(1, sizeof (struct parts));
+                                       if (ptr) {
+
+                                               /* Fill the buffers for the caller */
+                                               extract_token(ptr->name, &aaa[5], 0, '|', sizeof ptr->name);
+                                               extract_token(ptr->filename, &aaa[5], 1, '|', sizeof ptr->filename);
+                                               extract_token(ptr->number, &aaa[5], 2, '|', sizeof ptr->number);
+                                               extract_token(ptr->disposition, &aaa[5], 3, '|', sizeof ptr->disposition);
+                                               extract_token(ptr->mimetype, &aaa[5], 4, '|', sizeof ptr->mimetype);
+                                               ptr->length = extract_long(&aaa[5], 5);
+                                               if (!mret[0]->attachments)
+                                                       mret[0]->attachments = ptr;
+                                               else {
+                                                       chain = mret[0]->attachments;
+                                                       while (chain->next)
+                                                               chain = chain->next;
+                                                       chain->next = ptr;
+                                               }
+
+                                               /* Now handle multipart/alternative */
+                                               if (multipart_hunting > 0) {
+                                                       if ( (!strcasecmp(ptr->mimetype,
+                                                            "text/plain"))
+                                                          || (!strcasecmp(ptr->mimetype,
+                                                             "text/html")) ) {
+                                                               strcpy(mret[0]->mime_chosen,
+                                                                       ptr->number);
+                                                       }
+                                               }
+
+                                       }
+                               }
+                       }
+                       /* Eliminate "text\n" */
+                       remove_token(bbb, 0, '\n');
+
+                       /* If doing a MIME thing, pull out the extra headers */
+                       if (as_mime == 4) {
+                               do {
+                                       if (!strncasecmp(bbb, "Content-type:", 13)) {
+                                               extract_token(mret[0]->content_type, bbb, 0, '\n', sizeof mret[0]->content_type);
+                                               strcpy(mret[0]->content_type, &mret[0]->content_type[13]);
+                                               striplt(mret[0]->content_type);
+
+                                               /* strip out ";charset=" portion.  FIXME do something with
+                                                * the charset (like... convert it) instead of just throwing
+                                                * it away
+                                                */
+                                               if (strstr(mret[0]->content_type, ";") != NULL) {
+                                                       strcpy(strstr(mret[0]->content_type, ";"), "");
+                                               }
+
+                                       }
+                                       if (!strncasecmp(bbb, "X-Citadel-MSG4-Partnum:", 23)) {
+                                               extract_token(mret[0]->mime_chosen, bbb, 0, '\n', sizeof mret[0]->mime_chosen);
+                                               strcpy(mret[0]->mime_chosen, &mret[0]->mime_chosen[23]);
+                                               striplt(mret[0]->mime_chosen);
+                                       }
+                                       if (!strncasecmp(bbb, "Content-transfer-encoding:", 26)) {
+                                               extract_token(encoding, bbb, 0, '\n', sizeof encoding);
+                                               strcpy(encoding, &encoding[26]);
+                                               striplt(encoding);
+                                       }
+                                       remove_token(bbb, 0, '\n');
+                               } while ((bbb[0] != 0) && (bbb[0] != '\n'));
+                               remove_token(bbb, 0, '\n');
+                       }
+
+
+               }
+               if (strlen(bbb)) {
+
+                       if ( (!strcasecmp(encoding, "base64")) || (!strcasecmp(encoding, "quoted-printable")) ) {
+                               char *ccc = NULL;
+                               int bytes_decoded = 0;
+                               ccc = malloc(strlen(bbb) + 32768);
+                               if (!strcasecmp(encoding, "base64")) {
+                                       bytes_decoded = CtdlDecodeBase64(ccc, bbb, strlen(bbb));
+                               }
+                               else if (!strcasecmp(encoding, "quoted-printable")) {
+                                       bytes_decoded = CtdlDecodeQuotedPrintable(ccc, bbb, strlen(bbb));
+                               }
+                               ccc[bytes_decoded] = 0;
+                               free(bbb);
+                               bbb = ccc;
+                       }
+
+                       /* FIXME: Strip trailing whitespace */
+                       bbb = (char *)realloc(bbb, (size_t)(strlen(bbb) + 1));
+
+               } else {
+                       bbb = (char *)realloc(bbb, 1);
+                       *bbb = '\0';
+               }
+               mret[0]->text = bbb;
+       }
+       return ret;
+}
+
+
+/* WHOK */
+int CtdlIPCWhoKnowsRoom(CtdlIPC *ipc, char **listing, char *cret)
+{
+       register int ret;
+       size_t bytes;
+
+       if (!cret) return -2;
+       if (!listing) return -2;
+       if (*listing) return -2;
+
+       ret = CtdlIPCGenericCommand(ipc, "WHOK", NULL, 0, listing, &bytes, cret);
+       return ret;
+}
+
+
+/* INFO */
+int CtdlIPCServerInfo(CtdlIPC *ipc, char *cret)
+{
+       register int ret;
+       size_t bytes;
+       char *listing = NULL;
+       char buf[SIZ];
+
+       if (!cret) return -2;
+
+       ret = CtdlIPCGenericCommand(ipc, "INFO", NULL, 0, &listing, &bytes, cret);
+       if (ret / 100 == 1) {
+               int line = 0;
+
+               while (*listing && strlen(listing)) {
+                       extract_token(buf, listing, 0, '\n', sizeof buf);
+                       remove_token(listing, 0, '\n');
+                       switch (line++) {
+                       case 0:         ipc->ServInfo.pid = atoi(buf);
+                                       break;
+                       case 1:         strcpy(ipc->ServInfo.nodename,buf);
+                                       break;
+                       case 2:         strcpy(ipc->ServInfo.humannode,buf);
+                                       break;
+                       case 3:         strcpy(ipc->ServInfo.fqdn,buf);
+                                       break;
+                       case 4:         strcpy(ipc->ServInfo.software,buf);
+                                       break;
+                       case 5:         ipc->ServInfo.rev_level = atoi(buf);
+                                       break;
+                       case 6:         strcpy(ipc->ServInfo.site_location,buf);
+                                       break;
+                       case 7:         strcpy(ipc->ServInfo.sysadm,buf);
+                                       break;
+                       case 9:         strcpy(ipc->ServInfo.moreprompt,buf);
+                                       break;
+                       case 10:        ipc->ServInfo.ok_floors = atoi(buf);
+                                       break;
+                       case 11:        ipc->ServInfo.paging_level = atoi(buf);
+                                       break;
+                       case 13:        ipc->ServInfo.supports_qnop = atoi(buf);
+                                       break;
+                       case 14:        ipc->ServInfo.supports_ldap = atoi(buf);
+                                       break;
+                       case 15:        ipc->ServInfo.newuser_disabled = atoi(buf);
+                                       break;
+                       case 16:        strcpy(ipc->ServInfo.default_cal_zone, buf);
+                                       break;
+                       case 17:        ipc->ServInfo.load_avg = atof(buf);
+                                       break;
+                       case 18:        ipc->ServInfo.worker_avg = atof(buf);
+                                       break;
+                       case 19:        ipc->ServInfo.thread_count = atoi(buf);
+                                       break;
+                       case 20:        ipc->ServInfo.has_sieve = atoi(buf);
+                                       break;
+                       case 21:        ipc->ServInfo.fulltext_enabled = atoi(buf);
+                                       break;
+                       case 22:        strcpy(ipc->ServInfo.svn_revision, buf);
+                                       break;
+                       case 24:        ipc->ServInfo.guest_logins = atoi(buf);
+                                       break;
+                       }
+               }
+
+       }
+       if (listing) free(listing);
+       return ret;
+}
+
+
+/* RDIR */
+int CtdlIPCReadDirectory(CtdlIPC *ipc, char **listing, char *cret)
+{
+       register int ret;
+       size_t bytes;
+
+       if (!cret) return -2;
+       if (!listing) return -2;
+       if (*listing) return -2;
+
+       ret = CtdlIPCGenericCommand(ipc, "RDIR", NULL, 0, listing, &bytes, cret);
+       return ret;
+}
+
+
+/*
+ * Set last-read pointer in this room to msgnum, or 0 for HIGHEST.
+ */
+int CtdlIPCSetLastRead(CtdlIPC *ipc, long msgnum, char *cret)
+{
+       register int ret;
+       char aaa[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)
+{
+       register int ret;
+       char *aaa;
+
+       if (!cret) return -2;
+       if (!username) return -2;
+
+       aaa = (char *)malloc(strlen(username) + 6);
+       if (!aaa) return -1;
+
+       sprintf(aaa, "INVT %s", username);
+       ret = CtdlIPCGenericCommand(ipc, aaa, NULL, 0, NULL, NULL, cret);
+       free(aaa);
+       return ret;
+}
+
+
+/* KICK */
+int CtdlIPCKickoutUserFromRoom(CtdlIPC *ipc, const char *username, char *cret)
+{
+       register int ret;
+       char *aaa;
+
+       if (!cret) return -1;
+       if (!username) return -1;
+
+       aaa = (char *)malloc(strlen(username) + 6);
+
+       sprintf(aaa, "KICK %s", username);
+       ret = CtdlIPCGenericCommand(ipc, aaa, NULL, 0, NULL, NULL, cret);
+       free(aaa);
+       return ret;
+}
+
+
+/* GETR */
+int CtdlIPCGetRoomAttributes(CtdlIPC *ipc, struct ctdlroom **qret, char *cret)
+{
+       register int ret;
+
+       if (!cret) return -2;
+       if (!qret) return -2;
+       if (!*qret) *qret = (struct ctdlroom *)calloc(1, sizeof (struct ctdlroom));
+       if (!*qret) return -1;
+
+       ret = CtdlIPCGenericCommand(ipc, "GETR", NULL, 0, NULL, NULL, cret);
+       if (ret / 100 == 2) {
+               extract_token(qret[0]->QRname, cret, 0, '|', sizeof qret[0]->QRname);
+               extract_token(qret[0]->QRpasswd, cret, 1, '|', sizeof qret[0]->QRpasswd);
+               extract_token(qret[0]->QRdirname, cret, 2, '|', sizeof qret[0]->QRdirname);
+               qret[0]->QRflags = extract_int(cret, 3);
+               qret[0]->QRfloor = extract_int(cret, 4);
+               qret[0]->QRorder = extract_int(cret, 5);
+               qret[0]->QRdefaultview = extract_int(cret, 6);
+               qret[0]->QRflags2 = extract_int(cret, 7);
+       }
+       return ret;
+}
+
+
+/* SETR */
+/* set forget to kick all users out of room */
+int CtdlIPCSetRoomAttributes(CtdlIPC *ipc, int forget, struct ctdlroom *qret, char *cret)
+{
+       register int ret;
+       char *aaa;
+
+       if (!cret) return -2;
+       if (!qret) return -2;
+
+       aaa = (char *)malloc(strlen(qret->QRname) + strlen(qret->QRpasswd) +
+                       strlen(qret->QRdirname) + 64);
+       if (!aaa) return -1;
+
+       sprintf(aaa, "SETR %s|%s|%s|%d|%d|%d|%d|%d|%d",
+                       qret->QRname, qret->QRpasswd, qret->QRdirname,
+                       qret->QRflags, forget, qret->QRfloor, qret->QRorder,
+                       qret->QRdefaultview, qret->QRflags2);
+       ret = CtdlIPCGenericCommand(ipc, aaa, NULL, 0, NULL, NULL, cret);
+       free(aaa);
+       return ret;
+}
+
+
+/* GETA */
+int CtdlIPCGetRoomAide(CtdlIPC *ipc, char *cret)
+{
+       if (!cret) return -1;
+
+       return CtdlIPCGenericCommand(ipc, "GETA", NULL, 0, NULL, NULL, cret);
+}
+
+
+/* SETA */
+int CtdlIPCSetRoomAide(CtdlIPC *ipc, const char *username, char *cret)
+{
+       register int ret;
+       char *aaa;
+
+       if (!cret) return -2;
+       if (!username) return -2;
+
+       aaa = (char *)malloc(strlen(username) + 6);
+       if (!aaa) return -1;
+
+       sprintf(aaa, "SETA %s", username);
+       ret = CtdlIPCGenericCommand(ipc, aaa, NULL, 0, NULL, NULL, cret);
+       free(aaa);
+       return ret;
+}
+
+
+/* ENT0 */
+int CtdlIPCPostMessage(CtdlIPC *ipc, int flag, int *subject_required,  struct ctdlipcmessage *mr, char *cret)
+{
+       register int ret;
+       char cmd[SIZ];
+       char *ptr;
+
+       if (!cret) return -2;
+       if (!mr) return -2;
+
+       if (mr->references) {
+               for (ptr=mr->references; *ptr != 0; ++ptr) {
+                       if (*ptr == '|') *ptr = '!';
+               }
+       }
+
+       snprintf(cmd, sizeof cmd,
+                       "ENT0 %d|%s|%d|%d|%s|%s||||||%s|", flag, mr->recipient,
+                       mr->anonymous, mr->type, mr->subject, mr->author, mr->references);
+       ret = CtdlIPCGenericCommand(ipc, cmd, mr->text, strlen(mr->text), NULL,
+                       NULL, cret);
+       if ((flag == 0) && (subject_required != NULL)) {
+               /* Is the server strongly recommending that the user enter a message subject? */
+               if ((cret[3] != '\0') && (cret[4] != '\0')) {
+                       *subject_required = extract_int(&cret[4], 1);
+               }
+
+               
+       }
+       return ret;
+}
+
+
+/* RINF */
+int CtdlIPCRoomInfo(CtdlIPC *ipc, char **iret, char *cret)
+{
+       size_t bytes;
+
+       if (!cret) return -2;
+       if (!iret) return -2;
+       if (*iret) return -2;
+
+       return CtdlIPCGenericCommand(ipc, "RINF", NULL, 0, iret, &bytes, cret);
+}
+
+
+/* DELE */
+int CtdlIPCDeleteMessage(CtdlIPC *ipc, long msgnum, char *cret)
+{
+       char aaa[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)
+{
+       register int ret;
+       char *aaa;
+
+       if (!cret) return -2;
+       if (!destroom) return -2;
+       if (!msgnum) return -2;
+
+       aaa = (char *)malloc(strlen(destroom) + 28);
+       if (!aaa) return -1;
+
+       sprintf(aaa, "MOVE %ld|%s|%d", msgnum, destroom, copy);
+       ret = CtdlIPCGenericCommand(ipc, aaa, NULL, 0, NULL, NULL, cret);
+       free(aaa);
+       return ret;
+}
+
+
+/* KILL */
+int CtdlIPCDeleteRoom(CtdlIPC *ipc, int for_real, char *cret)
+{
+       char aaa[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)
+{
+       register int ret;
+       char *aaa;
+
+       if (!cret) return -2;
+       if (!roomname) return -2;
+
+       if (password) {
+               aaa = (char *)malloc(strlen(roomname) + strlen(password) + 40);
+               if (!aaa) return -1;
+               sprintf(aaa, "CRE8 %d|%s|%d|%s|%d", for_real, roomname, type,
+                               password, floor);
+       } else {
+               aaa = (char *)malloc(strlen(roomname) + 40);
+               if (!aaa) return -1;
+               sprintf(aaa, "CRE8 %d|%s|%d||%d", for_real, roomname, type,
+                               floor);
+       }
+       ret = CtdlIPCGenericCommand(ipc, aaa, NULL, 0, NULL, NULL, cret);
+       free(aaa);
+       return ret;
+}
+
+
+/* FORG */
+int CtdlIPCForgetRoom(CtdlIPC *ipc, char *cret)
+{
+       if (!cret) return -2;
+
+       return CtdlIPCGenericCommand(ipc, "FORG", NULL, 0, NULL, NULL, cret);
+}
+
+
+/* MESG */
+int CtdlIPCSystemMessage(CtdlIPC *ipc, const char *message, char **mret, char *cret)
+{
+       register int ret;
+       char *aaa;
+       size_t bytes;
+
+       if (!cret) return -2;
+       if (!mret) return -2;
+       if (*mret) return -2;
+       if (!message) return -2;
+
+       aaa = (char *)malloc(strlen(message) + 6);
+       if (!aaa) return -1;
+
+       sprintf(aaa, "MESG %s", message);
+       ret = CtdlIPCGenericCommand(ipc, aaa, NULL, 0, mret, &bytes, cret);
+       free(aaa);
+       return ret;
+}
+
+
+/* GNUR */
+int CtdlIPCNextUnvalidatedUser(CtdlIPC *ipc, char *cret)
+{
+       if (!cret) return -2;
+
+       return CtdlIPCGenericCommand(ipc, "GNUR", NULL, 0, NULL, NULL, cret);
+}
+
+
+/* GREG */
+int CtdlIPCGetUserRegistration(CtdlIPC *ipc, const char *username, char **rret, char *cret)
+{
+       register int ret;
+       char *aaa;
+       size_t bytes;
+
+       if (!cret) return -2;
+       if (!rret) return -2;
+       if (*rret) return -2;
+
+       if (username)
+               aaa = (char *)malloc(strlen(username) + 6);
+       else
+               aaa = (char *)malloc(12);
+       if (!aaa) return -1;
+
+       if (username)
+               sprintf(aaa, "GREG %s", username);
+       else
+               sprintf(aaa, "GREG _SELF_");
+       ret = CtdlIPCGenericCommand(ipc, aaa, NULL, 0, rret, &bytes, cret);
+       free(aaa);
+       return ret;
+}
+
+
+/* VALI */
+int CtdlIPCValidateUser(CtdlIPC *ipc, const char *username, int axlevel, char *cret)
+{
+       register int ret;
+       char *aaa;
+
+       if (!cret) return -2;
+       if (!username) return -2;
+       if (axlevel < 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)
+{
+       register int ret;
+
+       if (!cret) return -1;
+       if (!chek) return -1;
+
+       ret = CtdlIPCGenericCommand(ipc, "CHEK", NULL, 0, NULL, NULL, cret);
+       if (ret / 100 == 2) {
+               chek->newmail = extract_long(cret, 0);
+               chek->needregis = extract_int(cret, 1);
+               chek->needvalid = extract_int(cret, 2);
+       }
+       return ret;
+}
+
+
+/* DELF */
+int CtdlIPCDeleteFile(CtdlIPC *ipc, const char *filename, char *cret)
+{
+       register int ret;
+       char *aaa;
+
+       if (!cret) return -2;
+       if (!filename) return -2;
+       
+       aaa = (char *)malloc(strlen(filename) + 6);
+       if (!aaa) return -1;
+
+       sprintf(aaa, "DELF %s", filename);
+       ret = CtdlIPCGenericCommand(ipc, aaa, NULL, 0, NULL, NULL, cret);
+       free(aaa);
+       return ret;
+}
+
+
+/* MOVF */
+int CtdlIPCMoveFile(CtdlIPC *ipc, const char *filename, const char *destroom, char *cret)
+{
+       register int ret;
+       char *aaa;
+
+       if (!cret) return -2;
+       if (!filename) return -2;
+       if (!destroom) return -2;
+
+       aaa = (char *)malloc(strlen(filename) + strlen(destroom) + 7);
+       if (!aaa) return -1;
+
+       sprintf(aaa, "MOVF %s|%s", filename, destroom);
+       ret = CtdlIPCGenericCommand(ipc, aaa, NULL, 0, NULL, NULL, cret);
+       free(aaa);
+       return ret;
+}
+
+
+/* RWHO */
+int CtdlIPCOnlineUsers(CtdlIPC *ipc, char **listing, time_t *stamp, char *cret)
+{
+       register int ret;
+       size_t bytes;
+
+       if (!cret) return -1;
+       if (!listing) return -1;
+       if (*listing) return -1;
+
+       *stamp = CtdlIPCServerTime(ipc, cret);
+       if (!*stamp)
+               *stamp = time(NULL);
+       ret = CtdlIPCGenericCommand(ipc, "RWHO", NULL, 0, listing, &bytes, cret);
+       return ret;
+}
+
+
+/* OPEN */
+int CtdlIPCFileDownload(CtdlIPC *ipc, const char *filename, void **buf,
+               size_t resume,
+               void (*progress_gauge_callback)
+                       (CtdlIPC*, unsigned long, unsigned long),
+               char *cret)
+{
+       register int ret;
+       size_t bytes;
+       time_t last_mod;
+       char mimetype[SIZ];
+       char *aaa;
+
+       if (!cret) return -2;
+       if (!filename) return -2;
+       if (!buf) return -2;
+       if (*buf) return -2;
+       if (ipc->downloading) return -2;
+
+       aaa = (char *)malloc(strlen(filename) + 6);
+       if (!aaa) return -1;
+
+       sprintf(aaa, "OPEN %s", filename);
+       ret = CtdlIPCGenericCommand(ipc, aaa, NULL, 0, NULL, NULL, cret);
+       free(aaa);
+       if (ret / 100 == 2) {
+               ipc->downloading = 1;
+               bytes = extract_long(cret, 0);
+               last_mod = extract_int(cret, 1);
+               extract_token(mimetype, cret, 2, '|', sizeof mimetype);
+
+               ret = CtdlIPCReadDownload(ipc, buf, bytes, resume,
+                                       progress_gauge_callback, cret);
+               /*
+               ret = CtdlIPCHighSpeedReadDownload(ipc, buf, bytes, resume,
+                                       progress_gauge_callback, cret);
+               */
+
+               ret = CtdlIPCEndDownload(ipc, cret);
+               if (ret / 100 == 2)
+                       sprintf(cret, "%d|%ld|%s|%s", (int)bytes, last_mod,
+                                       filename, mimetype);
+       }
+       return ret;
+}
+
+
+/* OPNA */
+int CtdlIPCAttachmentDownload(CtdlIPC *ipc, long msgnum, const char *part,
+               void **buf,
+               void (*progress_gauge_callback)
+                       (CtdlIPC*, unsigned long, unsigned long),
+               char *cret)
+{
+       register int ret;
+       size_t bytes;
+       time_t last_mod;
+       char filename[SIZ];
+       char mimetype[SIZ];
+       char aaa[SIZ];
+
+       if (!cret) return -2;
+       if (!buf) return -2;
+       if (*buf) return -2;
+       if (!part) return -2;
+       if (!msgnum) return -2;
+       if (ipc->downloading) return -2;
+
+       sprintf(aaa, "OPNA %ld|%s", msgnum, part);
+       ret = CtdlIPCGenericCommand(ipc, aaa, NULL, 0, NULL, NULL, cret);
+       if (ret / 100 == 2) {
+               ipc->downloading = 1;
+               bytes = extract_long(cret, 0);
+               last_mod = extract_int(cret, 1);
+               extract_token(filename, cret, 2, '|', sizeof filename);
+               extract_token(mimetype, cret, 3, '|', sizeof mimetype);
+               /* ret = CtdlIPCReadDownload(ipc, buf, bytes, 0, progress_gauge_callback, cret); */
+               ret = CtdlIPCHighSpeedReadDownload(ipc, buf, bytes, 0, progress_gauge_callback, cret);
+               ret = CtdlIPCEndDownload(ipc, cret);
+               if (ret / 100 == 2)
+                       sprintf(cret, "%d|%ld|%s|%s", (int)bytes, last_mod,
+                                       filename, mimetype);
+       }
+       return ret;
+}
+
+
+/* OIMG */
+int CtdlIPCImageDownload(CtdlIPC *ipc, const char *filename, void **buf,
+               void (*progress_gauge_callback)
+                       (CtdlIPC*, unsigned long, unsigned long),
+               char *cret)
+{
+       register int ret;
+       size_t bytes;
+       time_t last_mod;
+       char mimetype[SIZ];
+       char *aaa;
+
+       if (!cret) return -1;
+       if (!buf) return -1;
+       if (*buf) return -1;
+       if (!filename) return -1;
+       if (ipc->downloading) return -1;
+
+       aaa = (char *)malloc(strlen(filename) + 6);
+       if (!aaa) return -1;
+
+       sprintf(aaa, "OIMG %s", filename);
+       ret = CtdlIPCGenericCommand(ipc, aaa, NULL, 0, NULL, NULL, cret);
+       free(aaa);
+       if (ret / 100 == 2) {
+               ipc->downloading = 1;
+               bytes = extract_long(cret, 0);
+               last_mod = extract_int(cret, 1);
+               extract_token(mimetype, cret, 2, '|', sizeof mimetype);
+/*             ret = CtdlIPCReadDownload(ipc, buf, bytes, 0, progress_gauge_callback, cret); */
+               ret = CtdlIPCHighSpeedReadDownload(ipc, buf, bytes, 0, progress_gauge_callback, cret);
+               ret = CtdlIPCEndDownload(ipc, cret);
+               if (ret / 100 == 2)
+                       sprintf(cret, "%d|%ld|%s|%s", (int)bytes, last_mod,
+                                       filename, mimetype);
+       }
+       return ret;
+}
+
+
+/* UOPN */
+int CtdlIPCFileUpload(CtdlIPC *ipc, const char *save_as, const char *comment, 
+               const char *path, 
+               void (*progress_gauge_callback)
+                       (CtdlIPC*, unsigned long, unsigned long),
+               char *cret)
+{
+       register int ret;
+       char *aaa;
+       FILE *uploadFP;
+       char MimeTestBuf[64];
+       const char *MimeType;
+       long len;
+
+       if (!cret) return -1;
+       if (!save_as) return -1;
+       if (!comment) return -1;
+       if (!path) return -1;
+       if (!*path) return -1;
+       if (ipc->uploading) return -1;
+
+       uploadFP = fopen(path, "r");
+       if (!uploadFP) return -2;
+
+       len = fread(&MimeTestBuf[0], 1, 64, uploadFP);
+       rewind (uploadFP);
+       if (len < 0) 
+               return -3;
+
+       MimeType = GuessMimeType(&MimeTestBuf[0], len);
+       aaa = (char *)malloc(strlen(save_as) + strlen(comment) + 7);
+       if (!aaa) return -1;
+
+       sprintf(aaa, "UOPN %s|%s|%s", save_as, MimeType,  comment);
+       ret = CtdlIPCGenericCommand(ipc, aaa, NULL, 0, NULL, NULL, cret);
+       free(aaa);
+       if (ret / 100 == 2) {
+               ipc->uploading = 1;
+               ret = CtdlIPCWriteUpload(ipc, uploadFP, progress_gauge_callback, cret);
+               ret = CtdlIPCEndUpload(ipc, (ret == -2 ? 1 : 0), cret);
+               ipc->uploading = 0;
+       }
+       return ret;
+}
+
+
+/* UIMG */
+int CtdlIPCImageUpload(CtdlIPC *ipc, int for_real, const char *path,
+               const char *save_as,
+               void (*progress_gauge_callback)
+                       (CtdlIPC*, unsigned long, unsigned long),
+               char *cret)
+{
+       register int ret;
+       FILE *uploadFP;
+       char *aaa;
+       char MimeTestBuf[64];
+       const char *MimeType;
+       long len;
+
+       if (!cret) return -1;
+       if (!save_as) return -1;
+       if (!path && for_real) return -1;
+       if (!*path && for_real) return -1;
+       if (ipc->uploading) return -1;
+
+       aaa = (char *)malloc(strlen(save_as) + 17);
+       if (!aaa) return -1;
+
+       uploadFP = fopen(path, "r");
+       if (!uploadFP) return -2;
+
+       len = fread(&MimeTestBuf[0], 1, 64, uploadFP);
+       rewind (uploadFP);
+       if (len < 0) 
+               return -3;
+       MimeType = GuessMimeType(&MimeTestBuf[0], 64);
+
+       sprintf(aaa, "UIMG %d|%s|%s", for_real, MimeType, save_as);
+       ret = CtdlIPCGenericCommand(ipc, aaa, NULL, 0, NULL, NULL, cret);
+       free(aaa);
+       if (ret / 100 == 2 && for_real) {
+               ipc->uploading = 1;
+               ret = CtdlIPCWriteUpload(ipc, uploadFP, progress_gauge_callback, cret);
+               ret = CtdlIPCEndUpload(ipc, (ret == -2 ? 1 : 0), cret);
+               ipc->uploading = 0;
+       }
+       return ret;
+}
+
+
+/* QUSR */
+int CtdlIPCQueryUsername(CtdlIPC *ipc, const char *username, char *cret)
+{
+       register int ret;
+       char *aaa;
+
+       if (!cret) return -2;
+       if (!username) return -2;
+
+       aaa = (char *)malloc(strlen(username) + 6);
+       if (!aaa) return -1;
+
+       sprintf(aaa, "QUSR %s", username);
+       ret = CtdlIPCGenericCommand(ipc, aaa, NULL, 0, NULL, NULL, cret);
+       free(aaa);
+       return ret;
+}
+
+
+/* LFLR */
+int CtdlIPCFloorListing(CtdlIPC *ipc, char **listing, char *cret)
+{
+       size_t bytes;
+
+       if (!cret) return -2;
+       if (!listing) return -2;
+       if (*listing) return -2;
+
+       return CtdlIPCGenericCommand(ipc, "LFLR", NULL, 0, listing, &bytes, cret);
+}
+
+
+/* CFLR */
+int CtdlIPCCreateFloor(CtdlIPC *ipc, int for_real, const char *name, char *cret)
+{
+       register int ret;
+       char aaa[SIZ];
+
+       if (!cret) return -2;
+       if (!name) return -2;
+
+       sprintf(aaa, "CFLR %s|%d", name, for_real);
+       ret = CtdlIPCGenericCommand(ipc, aaa, NULL, 0, NULL, NULL, cret);
+       return ret;
+}
+
+
+/* KFLR */
+int CtdlIPCDeleteFloor(CtdlIPC *ipc, int for_real, int floornum, char *cret)
+{
+       char aaa[SIZ];
+
+       if (!cret) return -1;
+       if (floornum < 0) return -1;
+
+       sprintf(aaa, "KFLR %d|%d", floornum, for_real);
+       return CtdlIPCGenericCommand(ipc, aaa, NULL, 0, NULL, NULL, cret);
+}
+
+
+/* EFLR */
+int CtdlIPCEditFloor(CtdlIPC *ipc, int floornum, const char *floorname, char *cret)
+{
+       register int ret;
+       char aaa[SIZ];
+
+       if (!cret) return -2;
+       if (!floorname) return -2;
+       if (floornum < 0) return -2;
+
+       sprintf(aaa, "EFLR %d|%s", floornum, floorname);
+       ret = CtdlIPCGenericCommand(ipc, aaa, NULL, 0, NULL, NULL, cret);
+       return ret;
+}
+
+
+/*
+ * IDEN 
+ *
+ * You only need to fill out hostname, the defaults will be used if any of the
+ * other fields are not set properly.
+ */
+int CtdlIPCIdentifySoftware(CtdlIPC *ipc, int developerid, int clientid,
+               int revision, const char *software_name, const char *hostname,
+               char *cret)
+{
+       register int ret;
+       char *aaa;
+
+       if (developerid < 0 || clientid < 0 || revision < 0 ||
+           !software_name) {
+               developerid = 8;
+               clientid = 0;
+               revision = 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)
+{
+       register int ret;
+       char *aaa;
+
+       if (!cret) return -2;
+       if (!username) return -2;
+
+       aaa = (char *)malloc(strlen(username) + 8);
+       if (!aaa) return -1;
+
+       if (text) {
+               sprintf(aaa, "SEXP %s|-", username);
+               ret = CtdlIPCGenericCommand(ipc, aaa, text, strlen(text),
+                               NULL, NULL, cret);
+       } else {
+               sprintf(aaa, "SEXP %s||", username);
+               ret = CtdlIPCGenericCommand(ipc, aaa, NULL, 0, NULL, NULL, cret);
+       }
+       free(aaa);
+       return ret;
+}
+
+
+/* GEXP */
+int CtdlIPCGetInstantMessage(CtdlIPC *ipc, char **listing, char *cret)
+{
+       size_t bytes;
+
+       if (!cret) return -2;
+       if (!listing) return -2;
+       if (*listing) return -2;
+
+       return CtdlIPCGenericCommand(ipc, "GEXP", NULL, 0, listing, &bytes, cret);
+}
+
+
+/* DEXP */
+/* mode is 0 = enable, 1 = disable, 2 = status */
+int CtdlIPCEnableInstantMessageReceipt(CtdlIPC *ipc, int mode, char *cret)
+{
+       char aaa[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)
+{
+       register int ret;
+       size_t bytes;
+       char *aaa;
+
+       if (!cret) return -2;
+       if (!username) return -2;
+       if (!listing) return -2;
+       if (*listing) return -2;
+
+       aaa = (char *)malloc(strlen(username) + 6);
+       if (!aaa) return -1;
+
+       sprintf(aaa, "RBIO %s", username);
+       ret = CtdlIPCGenericCommand(ipc, aaa, NULL, 0, listing, &bytes, cret);
+       free(aaa);
+       return ret;
+}
+
+
+/* LBIO */
+int CtdlIPCListUsersWithBios(CtdlIPC *ipc, char **listing, char *cret)
+{
+       size_t bytes;
+
+       if (!cret) return -2;
+       if (!listing) return -2;
+       if (*listing) return -2;
+
+       return CtdlIPCGenericCommand(ipc, "LBIO", NULL, 0, listing, &bytes, cret);
+}
+
+
+/* STEL */
+int CtdlIPCStealthMode(CtdlIPC *ipc, int mode, char *cret)
+{
+       char aaa[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)
+{
+       register int ret;
+       char *aaa;
+
+       if (!cret) return -2;
+       if (!text) return -2;
+       if (!filename) return -2;
+
+       aaa = (char *)malloc(strlen(filename) + 6);
+       if (!aaa) return -1;
+
+       sprintf(aaa, "EMSG %s", filename);
+       ret = CtdlIPCGenericCommand(ipc, aaa, text, strlen(text), NULL, NULL, cret);
+       free(aaa);
+       return ret;
+}
+
+
+/* HCHG */
+int CtdlIPCChangeHostname(CtdlIPC *ipc, const char *hostname, char *cret)
+{
+       register int ret;
+       char *aaa;
+
+       if (!cret) return -2;
+       if (!hostname) return -2;
+
+       aaa = (char *)malloc(strlen(hostname) + 6);
+       if (!aaa) return -1;
+
+       sprintf(aaa, "HCHG %s", hostname);
+       ret = CtdlIPCGenericCommand(ipc, aaa, NULL, 0, NULL, NULL, cret);
+       free(aaa);
+       return ret;
+}
+
+
+/* RCHG */
+int CtdlIPCChangeRoomname(CtdlIPC *ipc, const char *roomname, char *cret)
+{
+       register int ret;
+       char *aaa;
+
+       if (!cret) return -2;
+       if (!roomname) return -2;
+
+       aaa = (char *)malloc(strlen(roomname) + 6);
+       if (!aaa) return -1;
+
+       sprintf(aaa, "RCHG %s", roomname);
+       ret = CtdlIPCGenericCommand(ipc, aaa, NULL, 0, NULL, NULL, cret);
+       free(aaa);
+       return ret;
+}
+
+
+/* UCHG */
+int CtdlIPCChangeUsername(CtdlIPC *ipc, const char *username, char *cret)
+{
+       register int ret;
+       char *aaa;
+
+       if (!cret) return -2;
+       if (!username) return -2;
+
+       aaa = (char *)malloc(strlen(username) + 6);
+       if (!aaa) return -1;
+
+       sprintf(aaa, "UCHG %s", username);
+       ret = CtdlIPCGenericCommand(ipc, aaa, NULL, 0, NULL, NULL, cret);
+       free(aaa);
+       return ret;
+}
+
+
+/* TIME */
+/* This function returns the actual server time reported, or 0 if error */
+time_t CtdlIPCServerTime(CtdlIPC *ipc, char *cret)
+{
+       register time_t tret;
+       register int ret;
+
+       ret = CtdlIPCGenericCommand(ipc, "TIME", NULL, 0, NULL, NULL, cret);
+       if (ret / 100 == 2) {
+               tret = extract_long(cret, 0);
+       } else {
+               tret = 0L;
+       }
+       return tret;
+}
+
+
+/* AGUP */
+int CtdlIPCAideGetUserParameters(CtdlIPC *ipc, const char *who,
+                                struct ctdluser **uret, char *cret)
+{
+       register int ret;
+       char aaa[SIZ];
+
+       if (!cret) return -2;
+       if (!uret) return -2;
+       if (!*uret) *uret = (struct ctdluser *)calloc(1, sizeof(struct ctdluser));
+       if (!*uret) return -1;
+
+       sprintf(aaa, "AGUP %s", who);
+       ret = CtdlIPCGenericCommand(ipc, aaa, NULL, 0, NULL, NULL, cret);
+
+       if (ret / 100 == 2) {
+               extract_token(uret[0]->fullname, cret, 0, '|', sizeof uret[0]->fullname);
+               extract_token(uret[0]->password, cret, 1, '|', sizeof uret[0]->password);
+               uret[0]->flags = extract_int(cret, 2);
+               uret[0]->timescalled = extract_long(cret, 3);
+               uret[0]->posted = extract_long(cret, 4);
+               uret[0]->axlevel = extract_int(cret, 5);
+               uret[0]->usernum = extract_long(cret, 6);
+               uret[0]->lastcall = extract_long(cret, 7);
+               uret[0]->USuserpurge = extract_int(cret, 8);
+       }
+       return ret;
+}
+
+
+/* ASUP */
+int CtdlIPCAideSetUserParameters(CtdlIPC *ipc, const struct ctdluser *uret, char *cret)
+{
+       register int ret;
+       char *aaa;
+
+       if (!cret) return -2;
+       if (!uret) return -2;
+
+       aaa = (char *)malloc(strlen(uret->fullname) + strlen(uret->password) + 84);
+       if (!aaa) return -1;
+
+       sprintf(aaa, "ASUP %s|%s|%d|%ld|%ld|%d|%ld|%ld|%d",
+                       uret->fullname, uret->password, uret->flags,
+                       uret->timescalled, uret->posted, uret->axlevel,
+                       uret->usernum, uret->lastcall, uret->USuserpurge);
+       ret = CtdlIPCGenericCommand(ipc, aaa, NULL, 0, NULL, NULL, cret);
+       free(aaa);
+       return ret;
+}
+
+
+/* GPEX */
+/* which is 0 = room, 1 = floor, 2 = site, 3 = default for mailboxes */
+/* caller must free the struct ExpirePolicy */
+int CtdlIPCGetMessageExpirationPolicy(CtdlIPC *ipc, GPEXWhichPolicy which,
+               struct ExpirePolicy **policy, char *cret)
+{
+       static char *proto[] = {
+               strof(roompolicy),
+               strof(floorpolicy),
+               strof(sitepolicy),
+               strof(mailboxespolicy)
+       };
+       char cmd[256];
+       register int ret;
+
+       if (!cret) return -2;
+       if (!policy) return -2;
+       if (!*policy) *policy = (struct ExpirePolicy *)calloc(1, sizeof(struct ExpirePolicy));
+       if (!*policy) return -1;
+       if (which < 0 || which > 3) return -2;
+       
+       sprintf(cmd, "GPEX %s", proto[which]);
+       ret = CtdlIPCGenericCommand(ipc, cmd, NULL, 0, NULL, NULL, cret);
+       if (ret / 100 == 2) {
+               policy[0]->expire_mode = extract_int(cret, 0);
+               policy[0]->expire_value = extract_int(cret, 1);
+       }
+       return ret;
+}
+
+
+/* SPEX */
+/* which is 0 = room, 1 = floor, 2 = site, 3 = default for mailboxes */
+/* policy is 0 = inherit, 1 = no purge, 2 = by count, 3 = by age (days) */
+int CtdlIPCSetMessageExpirationPolicy(CtdlIPC *ipc, int which,
+               struct ExpirePolicy *policy, char *cret)
+{
+       char aaa[38];
+       char *whichvals[] = { "room", "floor", "site", "mailboxes" };
+
+       if (!cret) return -2;
+       if (which < 0 || which > 3) return -2;
+       if (!policy) return -2;
+       if (policy->expire_mode < 0 || policy->expire_mode > 3) return -2;
+       if (policy->expire_mode >= 2 && policy->expire_value < 1) return -2;
+
+       sprintf(aaa, "SPEX %s|%d|%d", whichvals[which],
+                       policy->expire_mode, policy->expire_value);
+       return CtdlIPCGenericCommand(ipc, aaa, NULL, 0, NULL, NULL, cret);
+}
+
+
+/* CONF GET */
+int CtdlIPCGetSystemConfig(CtdlIPC *ipc, char **listing, char *cret)
+{
+       size_t bytes;
+
+       if (!cret) return -2;
+       if (!listing) return -2;
+       if (*listing) return -2;
+
+       return CtdlIPCGenericCommand(ipc, "CONF GET", NULL, 0,
+                       listing, &bytes, cret);
+}
+
+
+/* CONF SET */
+int CtdlIPCSetSystemConfig(CtdlIPC *ipc, const char *listing, char *cret)
+{
+       if (!cret) return -2;
+       if (!listing) return -2;
+
+       return CtdlIPCGenericCommand(ipc, "CONF SET", listing, strlen(listing),
+                       NULL, NULL, cret);
+}
+
+
+/* CONF GETSYS */
+int CtdlIPCGetSystemConfigByType(CtdlIPC *ipc, const char *mimetype,
+               char **listing, char *cret)
+{
+       register int ret;
+       char *aaa;
+       size_t bytes;
+
+       if (!cret) return -2;
+       if (!mimetype) return -2;
+       if (!listing) return -2;
+       if (*listing) return -2;
+
+       aaa = malloc(strlen(mimetype) + 13);
+       if (!aaa) return -1;
+       sprintf(aaa, "CONF GETSYS|%s", mimetype);
+       ret = CtdlIPCGenericCommand(ipc, aaa, NULL, 0,
+                       listing, &bytes, cret);
+    free(aaa);
+    return ret;
+}
+
+
+/* CONF PUTSYS */
+int CtdlIPCSetSystemConfigByType(CtdlIPC *ipc, const char *mimetype,
+              const char *listing, char *cret)
+{
+    register int ret;
+       char *aaa;
+
+       if (!cret) return -2;
+       if (!mimetype) return -2;
+       if (!listing) return -2;
+
+       aaa = malloc(strlen(mimetype) + 13);
+       if (!aaa) return -1;
+       sprintf(aaa, "CONF PUTSYS|%s", mimetype);
+       ret = CtdlIPCGenericCommand(ipc, aaa, listing, strlen(listing),
+                       NULL, NULL, cret);
+    free(aaa);
+    return ret;
+}
+
+
+/* GNET */
+int CtdlIPCGetRoomNetworkConfig(CtdlIPC *ipc, char **listing, char *cret)
+{
+       size_t bytes;
+
+       if (!cret) return -2;
+       if (!listing) return -2;
+       if (*listing) return -2;
+
+       return CtdlIPCGenericCommand(ipc, "GNET", NULL, 0,
+                       listing, &bytes, cret);
+}
+
+
+/* SNET */
+int CtdlIPCSetRoomNetworkConfig(CtdlIPC *ipc, const char *listing, char *cret)
+{
+       if (!cret) return -2;
+       if (!listing) return -2;
+
+       return CtdlIPCGenericCommand(ipc, "SNET", listing, strlen(listing),
+                       NULL, NULL, cret);
+}
+
+
+/* REQT */
+int CtdlIPCRequestClientLogout(CtdlIPC *ipc, int session, char *cret)
+{
+       char aaa[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
+
+       if (!access(EGD_POOL, F_OK))
+               RAND_egd(EGD_POOL);
+
+       if (!RAND_status()) {
+               error_printf("PRNG not properly seeded\n");
+               return -2;
+       }
+
+       /* Associate network connection with SSL object */
+       if (SSL_set_fd(temp_ssl, ipc->sock) < 1) {
+               error_printf("SSL_set_fd failed: %s\n",
+                               ERR_reason_error_string(ERR_get_error()));
+               return -2;
+       }
+
+       if (status_hook != NULL)
+               status_hook("Requesting encryption...\r");
+
+       /* Ready to start SSL/TLS */
+       /* Old code
+       CtdlIPC_putline(ipc, "STLS");
+       CtdlIPC_getline(ipc, buf);
+       if (buf[0] != '2') {
+               error_printf("Server can't start TLS: %s\n", buf);
+               return 0;
+       }
+       */
+       r = CtdlIPCGenericCommand(ipc,
+                                 "STLS", NULL, 0, NULL, NULL, cret);
+       if (r / 100 != 2) {
+               error_printf("Server can't start TLS: %s\n", buf);
+               endtls(temp_ssl);
+               return r;
+       }
+
+       /* Do SSL/TLS handshake */
+       if ((a = SSL_connect(temp_ssl)) < 1) {
+               error_printf("SSL_connect failed: %s\n",
+                               ERR_reason_error_string(ERR_get_error()));
+               endtls(temp_ssl);
+               return -2;
+       }
+       ipc->ssl = temp_ssl;
+
+       if (BIO_set_close(ipc->ssl->rbio, BIO_NOCLOSE))
+       {
+               int bits, alg_bits;
+
+               bits = SSL_CIPHER_get_bits(SSL_get_current_cipher(ipc->ssl), &alg_bits);
+               error_printf("Encrypting with %s cipher %s (%d of %d bits)\n",
+                               SSL_CIPHER_get_version(SSL_get_current_cipher(ipc->ssl)),
+                               SSL_CIPHER_get_name(SSL_get_current_cipher(ipc->ssl)),
+                               bits, alg_bits);
+       }
+       return r;
+#else
+       return 0;
+#endif /* HAVE_OPENSSL */
+}
+
+
+#ifdef HAVE_OPENSSL
+static void endtls(SSL *ssl)
+{
+       if (ssl) {
+               SSL_shutdown(ssl);
+               SSL_free(ssl);
+       }
+}
+#endif
+
+
+/* QDIR */
+int CtdlIPCDirectoryLookup(CtdlIPC *ipc, const char *address, char *cret)
+{
+    register int ret;
+       char *aaa;
+
+       if (!address) return -2;
+       if (!cret) return -2;
+
+       aaa = (char *)malloc(strlen(address) + 6);
+       if (!aaa) return -1;
+
+       sprintf(aaa, "QDIR %s", address);
+       ret = CtdlIPCGenericCommand(ipc, aaa, NULL, 0, NULL, NULL, cret);
+    free(aaa);
+    return ret;
+}
+
+
+/* IPGM */
+int CtdlIPCInternalProgram(CtdlIPC *ipc, int secret, char *cret)
+{
+       char aaa[30];
+
+       if (!cret) return -2;
+       sprintf(aaa, "IPGM %d", secret);
+       return CtdlIPCGenericCommand(ipc, aaa, NULL, 0, NULL, NULL, cret);
+}
+
+
+
+/*
+ * Not implemented:
+ * 
+ * CHAT
+ * ETLS
+ * EXPI
+ * GTLS
+ * IGAB
+ * MSG3
+ * MSG4
+ * NDOP
+ * NETP
+ * NUOP
+ * SMTP
+ */
+
+
+/* ************************************************************************** */
+/*          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)
+{
+       register size_t len = 0;
+       char aaa[SIZ];
+
+       if (!buf) return 0;
+       if (!cret) return 0;
+       if (bytes < 1) return 0;
+
+       CtdlIPC_lock(ipc);
+       sprintf(aaa, "READ %d|%d", (int)offset, (int)bytes);
+       CtdlIPC_putline(ipc, aaa);
+       CtdlIPC_getline(ipc, aaa);
+       if (aaa[0] != '6')
+               strcpy(cret, &aaa[4]);
+       else {
+               len = extract_long(&aaa[4], 0);
+               *buf = (void *)realloc(*buf, (size_t)(offset + len));
+               if (*buf) {
+                       /* I know what I'm doing */
+                       serv_read(ipc, ((char *)(*buf) + offset), len);
+               } else {
+                       /* We have to read regardless */
+                       serv_read(ipc, aaa, len);
+                       len = 0;
+               }
+       }
+       CtdlIPC_unlock(ipc);
+       return len;
+}
+
+
+/* CLOS */
+int CtdlIPCEndDownload(CtdlIPC *ipc, char *cret)
+{
+       register int ret;
+
+       if (!cret) return -2;
+       if (!ipc->downloading) return -2;
+
+       ret = CtdlIPCGenericCommand(ipc, "CLOS", NULL, 0, NULL, NULL, cret);
+       if (ret / 100 == 2)
+               ipc->downloading = 0;
+       return ret;
+}
+
+
+/* MSGP */
+int CtdlIPCSpecifyPreferredFormats(CtdlIPC *ipc, char *cret, char *formats) {
+       register int ret;
+       char cmd[SIZ];
+       
+       snprintf(cmd, sizeof cmd, "MSGP %s", formats);
+       ret = CtdlIPCGenericCommand(ipc, cmd, NULL, 0, NULL, NULL, cret);
+       return ret;
+}
+
+
+
+/* READ */
+int CtdlIPCReadDownload(CtdlIPC *ipc, void **buf, size_t bytes, size_t resume,
+               void (*progress_gauge_callback)
+                       (CtdlIPC*, unsigned long, unsigned long),
+              char *cret)
+{
+       register size_t len;
+
+       if (!cret) return -1;
+       if (!buf) return -1;
+       if (*buf) return -1;
+       if (!ipc->downloading) return -1;
+
+       len = resume;
+       if (progress_gauge_callback)
+               progress_gauge_callback(ipc, len, bytes);
+       while (len < bytes) {
+               register size_t block;
+
+               block = CtdlIPCPartialRead(ipc, buf, len, 4096, cret);
+               if (block == 0) {
+                       free(*buf);
+                       return 0;
+               }
+               len += block;
+               if (progress_gauge_callback)
+                       progress_gauge_callback(ipc, len, bytes);
+       }
+       return len;
+}
+
+/* READ - pipelined */
+int CtdlIPCHighSpeedReadDownload(CtdlIPC *ipc, void **buf, size_t bytes,
+              size_t resume,
+               void (*progress_gauge_callback)
+                       (CtdlIPC*, unsigned long, unsigned long),
+              char *cret)
+{
+       register size_t len;
+       register int calls;     /* How many calls in the pipeline */
+       register int i;         /* iterator */
+       char aaa[4096];
+
+       if (!cret) return -1;
+       if (!buf) return -1;
+       if (*buf) return -1;
+       if (!ipc->downloading) return -1;
+
+       *buf = (void *)realloc(*buf, bytes - resume);
+       if (!*buf) return -1;
+
+       len = 0;
+       CtdlIPC_lock(ipc);
+       if (progress_gauge_callback)
+               progress_gauge_callback(ipc, len, bytes);
+
+       /* How many calls will be in the pipeline? */
+       calls = (bytes - resume) / 4096;
+       if ((bytes - resume) % 4096) calls++;
+
+       /* Send all requests at once */
+       for (i = 0; i < calls; i++) {
+               sprintf(aaa, "READ %d|4096", (int)(i * 4096 + resume) );
+               CtdlIPC_putline(ipc, aaa);
+       }
+
+       /* Receive all responses at once */
+       for (i = 0; i < calls; i++) {
+               CtdlIPC_getline(ipc, aaa);
+               if (aaa[0] != '6')
+                       strcpy(cret, &aaa[4]);
+               else {
+                       len = extract_long(&aaa[4], 0);
+                       /* I know what I'm doing */
+                       serv_read(ipc, ((char *)(*buf) + (i * 4096)), len);
+               }
+               if (progress_gauge_callback)
+                       progress_gauge_callback(ipc, i * 4096 + len, bytes);
+       }
+       CtdlIPC_unlock(ipc);
+       return len;
+}
+
+
+/* UCLS */
+int CtdlIPCEndUpload(CtdlIPC *ipc, int discard, char *cret)
+{
+       register int ret;
+       char cmd[8];
+
+       if (!cret) return -1;
+       if (!ipc->uploading) return -1;
+
+       sprintf(cmd, "UCLS %d", discard ? 0 : 1);
+       ret = CtdlIPCGenericCommand(ipc, cmd, NULL, 0, NULL, NULL, cret);
+       ipc->uploading = 0;
+       return ret;
+}
+
+
+/* WRIT */
+int CtdlIPCWriteUpload(CtdlIPC *ipc, FILE *uploadFP,
+               void (*progress_gauge_callback)
+                       (CtdlIPC*, unsigned long, unsigned long),
+               char *cret)
+{
+       register int ret = -1;
+       register size_t offset = 0;
+       size_t bytes;
+       char aaa[SIZ];
+       char buf[4096];
+       FILE *fd = uploadFP;
+       int ferr;
+
+       if (!cret) return -1;
+
+       fseek(fd, 0L, SEEK_END);
+       bytes = ftell(fd);
+       rewind(fd);
+
+       if (progress_gauge_callback)
+               progress_gauge_callback(ipc, 0, bytes);
+
+       while (offset < bytes) {
+               register size_t to_write;
+
+               /* Read some data in */
+               to_write = fread(buf, 1, 4096, fd);
+               if (!to_write) {
+                       if (feof(fd) || ferror(fd)) break;
+               }
+               sprintf(aaa, "WRIT %d", (int)to_write);
+               CtdlIPC_putline(ipc, aaa);
+               CtdlIPC_getline(ipc, aaa);
+               strcpy(cret, &aaa[4]);
+               ret = atoi(aaa);
+               if (aaa[0] == '7') {
+                       to_write = extract_long(&aaa[4], 0);
+                       
+                       serv_write(ipc, buf, to_write);
+                       offset += to_write;
+                       if (progress_gauge_callback)
+                               progress_gauge_callback(ipc, offset, bytes);
+                       /* Detect short reads and back up if needed */
+                       /* offset will never be negative anyway */
+                       fseek(fd, (signed)offset, SEEK_SET);
+               } else {
+                       break;
+               }
+       }
+       if (progress_gauge_callback)
+               progress_gauge_callback(ipc, 1, 1);
+       ferr = ferror(fd);
+       fclose(fd);
+       return (!ferr ? ret : -2);
+}
+
+
+/*
+ * Generic command method.  This method should handle any server command
+ * except for CHAT.  It takes the following arguments:
+ *
+ * ipc                 The server to speak with
+ * command             Preformatted command to send to server
+ * to_send             A text or binary file to send to server
+ *                     (only sent if server requests it)
+ * bytes_to_send       The number of bytes in to_send (required if
+ *                     sending binary, optional if sending listing)
+ * to_receive          Pointer to a NULL pointer, if the server
+ *                     sends text or binary we will allocate memory
+ *                     for the file and stuff it here
+ * bytes_to_receive    If a file is received, we will store its
+ *                     byte count here
+ * proto_response      The protocol response.  Caller must provide
+ *                     this buffer and ensure that it is at least
+ *                     128 bytes in length.
+ *
+ * This function returns a number equal to the protocol response number,
+ * -1 if an internal error occurred, -2 if caller provided bad values,
+ * or 0 - the protocol response number if bad values were found during
+ * the protocol exchange.
+ * It stores the protocol response string (minus the number) in 
+ * protocol_response as described above.  Some commands send additional
+ * data in this string.
+ */
+int CtdlIPCGenericCommand(CtdlIPC *ipc,
+               const char *command, const char *to_send,
+               size_t bytes_to_send, char **to_receive, 
+               size_t *bytes_to_receive, char *proto_response)
+{
+       char buf[SIZ];
+       register int ret;
+
+       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 (!(BN_hex2bn(&(dh->p), DH_P))) {
+               error_printf("Can't assign DH_P: %s\n",
+                               ERR_reason_error_string(ERR_get_error()));
+               DH_free(dh);
+               return;
+       }
+       if (!(BN_hex2bn(&(dh->g), DH_G))) {
+               error_printf("Can't assign DH_G: %s\n",
+                               ERR_reason_error_string(ERR_get_error()));
+               DH_free(dh);
+               return;
+       }
+       dh->length = DH_L;
+       SSL_CTX_set_tmp_dh(ssl_ctx, dh);
+       DH_free(dh);
+
+#ifdef THREADED_CLIENT
+       /* OpenSSL requires callbacks for threaded clients */
+       CRYPTO_set_locking_callback(ssl_lock);
+       CRYPTO_set_id_callback(id_callback);
+
+       /* OpenSSL requires us to do semaphores for threaded clients */
+       Critters = malloc(CRYPTO_num_locks() * sizeof (pthread_mutex_t *));
+       if (!Critters) {
+               perror("malloc failed");
+               exit(1);
+       } else {
+               for (a = 0; a < CRYPTO_num_locks(); a++) {
+                       Critters[a] = malloc(sizeof (pthread_mutex_t));
+                       if (!Critters[a]) {
+                               perror("malloc failed");
+                               exit(1);
+                       }
+                       pthread_mutex_init(Critters[a], NULL);
+               }
+       }
+#endif /* THREADED_CLIENT */       
+}
+
+
+
+#ifdef THREADED_CLIENT
+static unsigned long id_callback(void) {
+       return (unsigned long)pthread_self();
+}
+#endif /* THREADED_CLIENT */
+#endif /* HAVE_OPENSSL */
+
+
+int
+ReadNetworkChunk(CtdlIPC* ipc)
+{
+       fd_set read_fd;
+/*     int tries;*/
+       int ret = 0;
+       int err = 0;
+       struct timeval tv;
+       size_t n;
+
+       tv.tv_sec = 1;
+       tv.tv_usec = 1000;
+       /*tries = 0; */
+       n = 0;
+       while (1)
+       {
+               errno=0;
+               FD_ZERO(&read_fd);
+               FD_SET(ipc->sock, &read_fd);
+               ret = select(ipc->sock+1, &read_fd, NULL, NULL,  &tv);
+               
+//             fprintf(stderr, "\nselect failed: %d %d %s\n", ret,  err, strerror(err));
+               
+               if (ret > 0) {
+                       
+                       *(ipc->BufPtr) = '\0';
+//                     n = read(ipc->sock, ipc->BufPtr, ipc->BufSize  - (ipc->BufPtr - ipc->Buf) - 1);
+                       n = recv(ipc->sock, ipc->BufPtr, ipc->BufSize  - (ipc->BufPtr - ipc->Buf) - 1, 0);
+                       if (n > 0) {
+                               ipc->BufPtr[n]='\0';
+                               ipc->BufUsed += n;
+                               return n;
+                       }
+                       else 
+                               return n;
+               }
+               else if (ret < 0) {
+                       if (!(errno == EINTR || errno == EAGAIN))
+                               error_printf( "\nselect failed: %d %s\n", err, strerror(err));
+                       return -1;
+               }/*
+               else {
+                       tries ++;
+                       if (tries >= 10)
+                       n = read(ipc->sock, ipc->BufPtr, ipc->BufSize  - (ipc->BufPtr - ipc->Buf) - 1);
+                       if (n > 0) {
+                               ipc->BufPtr[n]='\0';
+                               ipc->BufUsed += n;
+                               return n;
+                       }
+                       else {
+                               connection_died(ipc, 0);
+                               return -1;
+                       }
+                       }*/
+       }
+}
+
+/*
+ * input string from socket - implemented in terms of serv_read()
+ */
+#ifdef CHUNKED_READ
+
+static void CtdlIPC_getline(CtdlIPC* ipc, char *buf)
+{
+       int i, ntries;
+       char *aptr, *bptr, *aeptr, *beptr;
+
+//     error_printf("---\n");
+
+       beptr = buf + SIZ;
+#if defined(HAVE_OPENSSL)
+       if (ipc->ssl) {
+               
+               /* Read one character at a time. */
+               for (i = 0;; i++) {
+                       serv_read(ipc, &buf[i], 1);
+                       if (buf[i] == '\n' || i == (SIZ-1))
+                               break;
+               }
+               
+               /* If we got a long line, discard characters until the newline. */
+               if (i == (SIZ-1))
+                       while (buf[i] != '\n')
+                               serv_read(ipc, &buf[i], 1);
+               
+               /* Strip the trailing newline (and carriage return, if present) */
+               if (i>=0 && buf[i] == 10) buf[i--] = 0;
+               if (i>=0 && buf[i] == 13) buf[i--] = 0;
+       }
+       else
+#endif
+       {
+               if (ipc->Buf == NULL)
+               {
+                       ipc->BufSize = SIZ;
+                       ipc->Buf = (char*) malloc(ipc->BufSize + 10);
+                       *(ipc->Buf) = '\0';
+                       ipc->BufPtr = ipc->Buf;
+               }
+
+               ntries = 0;
+//             while ((ipc->BufUsed == 0)||(ntries++ > 10))
+               if (ipc->BufUsed == 0)
+                       ReadNetworkChunk(ipc);
+
+////           if (ipc->BufUsed != 0) while (1)
+               bptr = buf;
+
+               while (1)
+               {
+                       aptr = ipc->BufPtr;
+                       aeptr = ipc->Buf + ipc->BufSize;
+                       while ((aptr < aeptr) && 
+                              (bptr < beptr) &&
+                              (*aptr != '\0') && 
+                              (*aptr != '\n'))
+                               *(bptr++) = *(aptr++);
+                       if ((*aptr == '\n') && (aptr < aeptr))
+                       {
+                               /* Terminate it right, remove the line breaks */
+                               while ((aptr < aeptr) && ((*aptr == '\n') || (*aptr == '\r')))
+                                       aptr ++;
+                               while ((aptr < aeptr ) && (*(aptr + 1) == '\0') )
+                                       aptr ++;
+                               *(bptr++) = '\0';
+//                             fprintf(stderr, "parsing %d %d %d - %d %d %d %s\n", ipc->BufPtr - ipc->Buf, aptr - ipc->BufPtr, ipc->BufUsed , *aptr, *(aptr-1), *(aptr+1), buf);
+                               if ((bptr > buf + 1) && (*(bptr-1) == '\r'))
+                                       *(--bptr) = '\0';
+                               
+                               /* is there more in the buffer we need to read later? */
+                               if (ipc->Buf + ipc->BufUsed > aptr)
+                               {
+                                       ipc->BufPtr = aptr;
+                               }
+                               else
+                               {
+                                       ipc->BufUsed = 0;
+                                       ipc->BufPtr = ipc->Buf;
+                               }
+//                             error_printf("----bla6\n");
+                               return;
+                               
+                       }/* should we move our read stuf to the bufferstart so we have more space at the end? */
+                       else if ((ipc->BufPtr != ipc->Buf) && 
+                                (ipc->BufUsed > (ipc->BufSize  - (ipc->BufSize / 4))))
+                       {
+                               size_t NewBufSize = ipc->BufSize * 2;
+                               int delta = (ipc->BufPtr - ipc->Buf);
+                               char *NewBuf;
+
+                               /* if the line would end after our buffer, we should use a bigger buffer. */
+                               NewBuf = (char *)malloc (NewBufSize + 10);
+                               memcpy (NewBuf, ipc->BufPtr, ipc->BufUsed - delta);
+                               free(ipc->Buf);
+                               ipc->Buf = ipc->BufPtr = NewBuf;
+                               ipc->BufUsed -= delta;
+                               ipc->BufSize = NewBufSize;
+                       }
+                       if (ReadNetworkChunk(ipc) <0)
+                       {
+//                             error_printf("----bla\n");
+                               return;
+                       }
+               }
+///            error_printf("----bl45761%s\nipc->BufUsed");
+       }
+//     error_printf("----bla1\n");
+}
+
+#else  /* CHUNKED_READ */
+
+static void CtdlIPC_getline(CtdlIPC* ipc, char *buf)
+{
+       int i;
+
+       /* Read one character at a time. */
+       for (i = 0;; i++) {
+               serv_read(ipc, &buf[i], 1);
+               if (buf[i] == '\n' || i == (SIZ-1))
+                       break;
+       }
+
+       /* If we got a long line, discard characters until the newline. */
+       if (i == (SIZ-1))
+               while (buf[i] != '\n')
+                       serv_read(ipc, &buf[i], 1);
+
+       /* Strip the trailing newline (and carriage return, if present) */
+       if (i>=0 && buf[i] == 10) buf[i--] = 0;
+       if (i>=0 && buf[i] == 13) buf[i--] = 0;
+}
+
+
+#endif /* CHUNKED_READ */
+
+
+void CtdlIPC_chat_recv(CtdlIPC* ipc, char* buf)
+{
+       CtdlIPC_getline(ipc, buf);
+}
+
+/*
+ * send line to server - implemented in terms of serv_write()
+ */
+static void CtdlIPC_putline(CtdlIPC *ipc, const char *buf)
+{
+       char *cmd = NULL;
+       int len;
+
+       len = strlen(buf);
+       cmd = malloc(len + 2);
+       if (!cmd) {
+               /* This requires no extra memory */
+               serv_write(ipc, buf, len);
+               serv_write(ipc, "\n", 1);
+       } else {
+               /* This is network-optimized */
+               strncpy(cmd, buf, len);
+               strcpy(cmd + len, "\n");
+               serv_write(ipc, cmd, len + 1);
+               free(cmd);
+       }
+
+       ipc->last_command_sent = time(NULL);
+}
+
+void CtdlIPC_chat_send(CtdlIPC* ipc, const char* buf)
+{
+       CtdlIPC_putline(ipc, buf);
+}
+
+
+/*
+ * attach to server
+ */
+CtdlIPC* CtdlIPC_new(int argc, char **argv, char *hostbuf, char *portbuf)
+{
+       int a;
+       char cithost[SIZ];
+       char citport[SIZ];
+       char sockpath[SIZ];
+       CtdlIPC* ipc;
+
+       ipc = ialloc(CtdlIPC);
+       if (!ipc) {
+               return 0;
+       }
+#if defined(HAVE_OPENSSL)
+       ipc->ssl = NULL;
+       CtdlIPC_init_OpenSSL();
+#endif
+#if defined(HAVE_PTHREAD_H)
+       pthread_mutex_init(&(ipc->mutex), NULL); /* Default fast mutex */
+#endif
+       ipc->sock = -1;                 /* Not connected */
+       ipc->isLocal = 0;               /* Not local, of course! */
+       ipc->downloading = 0;
+       ipc->uploading = 0;
+       ipc->last_command_sent = 0L;
+       ipc->network_status_cb = NULL;
+       ipc->Buf = NULL;
+       ipc->BufUsed = 0;
+       ipc->BufPtr = NULL;
+
+       strcpy(cithost, DEFAULT_HOST);  /* default host */
+       strcpy(citport, DEFAULT_PORT);  /* default port */
+
+       /* Allow caller to supply our values (Windows) */
+       if (hostbuf && strlen(hostbuf) > 0)
+               strcpy(cithost, hostbuf);
+       if (portbuf && strlen(portbuf) > 0)
+               strcpy(citport, portbuf);
+
+       /* Read host/port from command line if present */
+       for (a = 0; a < argc; ++a) {
+               if (a == 0) {
+                       /* do nothing */
+               } else if (a == 1) {
+                       strcpy(cithost, argv[a]);
+               } else if (a == 2) {
+                       strcpy(citport, argv[a]);
+               } else {
+                       error_printf("%s: usage: ",argv[0]);
+                       error_printf("%s [host] [port] ",argv[0]);
+                       ifree(ipc);
+                       errno = EINVAL;
+                       return 0;
+               }
+       }
+
+       if ((!strcmp(cithost, "localhost"))
+          || (!strcmp(cithost, "127.0.0.1"))) {
+               ipc->isLocal = 1;
+       }
+
+       /* If we're using a unix domain socket we can do a bunch of stuff */
+       if (!strcmp(cithost, UDS)) {
+               if (!strcasecmp(citport, DEFAULT_PORT)) {
+                       snprintf(sockpath, sizeof sockpath, "%s", file_citadel_socket);
+               }
+               else {
+                       snprintf(sockpath, sizeof sockpath, "%s/%s", citport, "citadel.socket");
+               }
+               ipc->sock = uds_connectsock(&(ipc->isLocal), sockpath);
+               if (ipc->sock == -1) {
+                       ifree(ipc);
+                       return 0;
+               }
+               if (hostbuf != NULL) strcpy(hostbuf, cithost);
+               if (portbuf != NULL) strcpy(portbuf, sockpath);
+               strcpy(ipc->ip_hostname, "");
+               strcpy(ipc->ip_address, "");
+               return ipc;
+       }
+
+       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
new file mode 100644 (file)
index 0000000..29f3e26
--- /dev/null
@@ -0,0 +1,308 @@
+/*
+ * front end for multiuser chat
+ *
+ * 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.
+ */
+
+#include "sysdep.h"
+#include <stdlib.h>
+#include <unistd.h>
+#include <stdio.h>
+#include <fcntl.h>
+#include <ctype.h>
+#include <string.h>
+#include <signal.h>
+#include <errno.h>
+
+#if TIME_WITH_SYS_TIME
+# include <sys/time.h>
+# include <time.h>
+#else
+# if HAVE_SYS_TIME_H
+#  include <sys/time.h>
+# else
+#  include <time.h>
+# endif
+#endif
+
+#include <sys/types.h>
+#ifdef HAVE_SYS_SELECT_H
+#include <sys/select.h>
+#endif
+#include <stdarg.h>
+#include <libcitadel.h>
+//#include "citadel.h"
+#include "citadel_ipc.h"
+#include "client_chat.h"
+#include "commands.h"
+#include "routines.h"
+#include "citadel_decls.h"
+#include "rooms.h"
+#include "messages.h"
+//#ifndef HAVE_SNPRINTF
+//#include "snprintf.h"
+//#endif
+#include "screen.h"
+
+#define MIN(a, b) ((a) < (b) ? (a) : (b))
+
+extern char temp[];
+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
new file mode 100644 (file)
index 0000000..3e864a4
--- /dev/null
@@ -0,0 +1,152 @@
+/*
+ * Functions which allow the client to remember usernames and passwords for
+ * various sites.
+ *
+ * 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.
+ */
+
+#include "sysdep.h"
+#include <stdlib.h>
+#include <unistd.h>
+#include <string.h>
+#include <ctype.h>
+#include <pwd.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <limits.h>
+#include <stdio.h>
+#include <libcitadel.h>
+///#include "citadel.h"
+#include "citadel_ipc.h"
+#include "commands.h"
+#include "client_passwords.h"
+
+#define PWFILENAME "%s/.citadel.passwords"
+
+void determine_pwfilename(char *pwfile, size_t n) {
+       struct passwd *p;
+
+       p = getpwuid(getuid());
+       if (p == NULL) strcpy(pwfile, "");
+       snprintf(pwfile, n, PWFILENAME, p->pw_dir);
+}
+
+
+/*
+ * Check the password file for a host/port match; if found, stuff the user
+ * name and password into the user/pass buffers
+ */
+void get_stored_password(
+               char *host,
+               char *port,
+               char *username,
+               char *password) {
+
+       char pwfile[PATH_MAX];
+       FILE *fp;
+       char buf[SIZ];
+       char buf64[SIZ];
+       char hostbuf[256], portbuf[256], ubuf[256], pbuf[256];
+
+       strcpy(username, "");
+       strcpy(password, "");
+
+       determine_pwfilename(pwfile, sizeof pwfile);
+       if (IsEmptyStr(pwfile)) return;
+
+       fp = fopen(pwfile, "r");
+       if (fp == NULL) return;
+       while (fgets(buf64, sizeof buf64, fp) != NULL) {
+               CtdlDecodeBase64(buf, buf64, sizeof(buf64));
+               extract_token(hostbuf, buf, 0, '|', sizeof hostbuf);
+               extract_token(portbuf, buf, 1, '|', sizeof portbuf);
+               extract_token(ubuf, buf, 2, '|', sizeof ubuf);
+               extract_token(pbuf, buf, 3, '|', sizeof pbuf);
+
+               if (!strcasecmp(hostbuf, host)) {
+                       if (!strcasecmp(portbuf, port)) {
+                               strcpy(username, ubuf);
+                               strcpy(password, pbuf);
+                       }
+               }
+       }
+       fclose(fp);
+}
+
+
+/*
+ * Set (or clear) stored passwords.
+ */
+void set_stored_password(
+               char *host,
+               char *port,
+               char *username,
+               char *password) {
+
+       char pwfile[PATH_MAX];
+       FILE *fp, *oldfp;
+       char buf[SIZ];
+       char buf64[SIZ];
+       char hostbuf[256], portbuf[256], ubuf[256], pbuf[256];
+
+       determine_pwfilename(pwfile, sizeof pwfile);
+       if (IsEmptyStr(pwfile)) return;
+
+       oldfp = fopen(pwfile, "r");
+       if (oldfp == NULL) oldfp = fopen("/dev/null", "r");
+       unlink(pwfile);
+       fp = fopen(pwfile, "w");
+       if (fp == NULL) fp = fopen("/dev/null", "w");
+       while (fgets(buf64, sizeof buf64, oldfp) != NULL) {
+               CtdlDecodeBase64(buf, buf64, sizeof(buf64));
+               extract_token(hostbuf, buf, 0, '|', sizeof hostbuf);
+               extract_token(portbuf, buf, 1, '|', sizeof portbuf);
+               extract_token(ubuf, buf, 2, '|', sizeof ubuf);
+               extract_token(pbuf, buf, 3, '|', sizeof pbuf);
+
+               if ( (strcasecmp(hostbuf, host)) 
+                  || (strcasecmp(portbuf, port)) ) {
+                       snprintf(buf, sizeof buf, "%s|%s|%s|%s|",
+                               hostbuf, portbuf, ubuf, pbuf);
+                       CtdlEncodeBase64(buf64, buf, strlen(buf), 0);
+                       fprintf(fp, "%s\n", buf64);
+               }
+       }
+       if (!IsEmptyStr(username)) {
+               snprintf(buf, sizeof buf, "%s|%s|%s|%s|",
+                       host, port, username, password);
+               CtdlEncodeBase64(buf64, buf, strlen(buf), 0);
+               fprintf(fp, "%s\n", buf64);
+       }
+       fclose(oldfp);
+       fclose(fp);
+       chmod(pwfile, 0600);
+}
+
+
+/*
+ * Set the password if the user wants to, clear it otherwise 
+ */
+void offer_to_remember_password(CtdlIPC *ipc,
+               char *host,
+               char *port,
+               char *username,
+               char *password) {
+
+       if (rc_remember_passwords) {
+               if (boolprompt("Remember username/password for this site", 0)) {
+                       set_stored_password(host, port, username, password);
+               }
+               else {
+                       set_stored_password(host, port, "", "");
+               }
+       }
+}
diff --git a/textclient/src/commands.c b/textclient/src/commands.c
new file mode 100644 (file)
index 0000000..4fe0883
--- /dev/null
@@ -0,0 +1,1555 @@
+/*
+ * This file contains functions which implement parts of the
+ * text-mode user interface.
+ *
+ * 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.
+ */
+
+#include "sysdep.h"
+#include <stdlib.h>
+#include <unistd.h>
+#include <stdio.h>
+#include <fcntl.h>
+#include <ctype.h>
+#include <string.h>
+#include <sys/types.h>
+
+#if TIME_WITH_SYS_TIME
+# include <sys/time.h>
+# include <time.h>
+#else
+# if HAVE_SYS_TIME_H
+#  include <sys/time.h>
+# else
+#  include <time.h>
+# endif
+#endif
+
+#ifdef HAVE_TERMIOS_H
+#include <termios.h>
+#else
+#include <sgtty.h>
+#endif
+
+#ifdef HAVE_SYS_SELECT_H
+#include <sys/select.h>
+#endif
+
+#ifdef THREADED_CLIENT
+#include <pthread.h>
+#endif
+
+#include <signal.h>
+#include <errno.h>
+#include <stdarg.h>
+#include <libcitadel.h>
+///#include "citadel.h"
+#include "citadel_ipc.h"
+#include "commands.h"
+#include "messages.h"
+#include "citadel_decls.h"
+#include "routines.h"
+#include "routines2.h"
+#include "rooms.h"
+#include "client_chat.h"
+////#include "citadel_dirs.h"
+#include "tuiconfig.h"
+//#ifndef HAVE_SNPRINTF
+//#include "snprintf.h"
+///#endif
+#include "screen.h"
+#include "ecrash.h"
+
+struct citcmd {
+       struct citcmd *next;
+       int c_cmdnum;
+       int c_axlevel;
+       char c_keys[5][64];
+};
+
+#define IFNEXPERT if ((userflags&US_EXPERT)==0)
+
+
+int rc_exp_beep;
+char rc_exp_cmd[1024];
+int rc_allow_attachments;
+int rc_display_message_numbers;
+int rc_force_mail_prompts;
+int rc_remember_passwords;
+int rc_ansi_color;
+int rc_color_use_bg;
+int rc_prompt_control = 0;
+time_t rc_idle_threshold = (time_t)900;
+char rc_url_cmd[SIZ];
+char rc_open_cmd[SIZ];
+char rc_gotmail_cmd[SIZ];
+
+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 formout(ipc, "mainmenu");
+
+       print_instant();
+       strcpy(argbuf, "");
+       cmdpos = 0;
+       for (a = 0; a < 5; ++a)
+               cmdbuf[a] = 0;
+       /* now the room prompt... */
+       ok_to_interrupt = 1;
+       color(BRIGHT_WHITE);
+       scr_printf("\n%s", room_name);
+       color(DIM_WHITE);
+       scr_printf("%c ", room_prompt(room_flags));
+
+       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 file viewer
+ */
+void display_help(CtdlIPC *ipc, char *name)
+{
+       formout(ipc, name);
+}
+
+
+/*
+ * fmout() - Citadel text formatter and paginator
+ */
+int fmout(
+       int width,      /* screen width to use */
+       FILE *fpin,     /* file to read from, or NULL to format given text */
+       char *text,     /* text to be formatted (when fpin is NULL */
+       FILE *fpout,    /* file to write to, or NULL to write to screen */
+       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
new file mode 100644 (file)
index 0000000..9ec53f0
--- /dev/null
@@ -0,0 +1,521 @@
+/*
+ * 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 "server.h"
+/// #include "sysdep_decls.h"
+//#include "support.h"
+///#include "config.h"
+//#include "citserver.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
new file mode 100644 (file)
index 0000000..19895e6
--- /dev/null
@@ -0,0 +1,32 @@
+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
new file mode 100644 (file)
index 0000000..1236743
--- /dev/null
@@ -0,0 +1,518 @@
+#ifdef __GNUC__
+#define INLINE __inline__
+#else
+#define INLINE
+#endif
+
+#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>
+////#include "sysdep.h"
+#ifdef HAVE_PTHREAD_H
+#include <pthread.h>
+#endif
+#ifdef HAVE_OPENSSL
+#include <openssl/ssl.h>
+#include <openssl/err.h>
+#include <openssl/rand.h>
+#endif
+
+#define CLIENT_VERSION          811
+#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 70
+
+
+
+/*
+ * 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;
+};
+/*
+ * User records.
+ */
+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) */
+};
+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 */
+       char node[SIZ];                 /* Short nodename of origin system */
+       char hnod[SIZ];                 /* Humannode of origin system */
+       struct parts *attachments;      /* Available attachments */
+       char *text;                     /* Message text */
+       int type;                       /* Message type */
+       time_t time;                    /* Time message was posted */
+       char nhdr;                      /* Suppress message header? */
+       char anonymous;                 /* An anonymous message */
+       char mime_chosen[SIZ];          /* Chosen MIME part to output */
+       char content_type[SIZ];         /* How would you like that? */
+       char references[SIZ];           /* Thread references */
+};
+
+
+struct ctdlipcfile {
+       char remote_name[PATH_MAX];     /* Filename on server */
+       char local_name[PATH_MAX];      /* Filename on client */
+       char description[80];           /* Description on server */
+       FILE *local_fd;                 /* Open file on client */
+       size_t size;                    /* Size of file in octets */
+       unsigned int upload:1;          /* uploading? 0 if downloading */
+       unsigned int complete:1;        /* Transfer has finished? */
+};
+
+
+struct ctdlipcmisc {
+       long newmail;                   /* Number of new Mail messages */
+       char needregis;                 /* Nonzero if user needs to register */
+       char needvalid;                 /* Nonzero if users need validation */
+};
+
+enum RoomList {
+       SubscribedRooms,
+       SubscribedRoomsWithNewMessages,
+       SubscribedRoomsWithNoNewMessages,
+       UnsubscribedRooms,
+       AllAccessibleRooms,
+       AllPublicRooms
+};
+#define AllFloors -1
+enum MessageList {
+       AllMessages,
+       OldMessages,
+       NewMessages,
+       LastMessages,
+       FirstMessages,
+       MessagesGreaterThan,
+       MessagesLessThan
+};
+enum MessageDirection {
+       ReadReverse = -1,
+       ReadForward = 1
+};
+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 CtdlIPCAideSetUserParameters(CtdlIPC *ipc, const struct ctdluser *uret, 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);
+
+
+
+INLINE void CtdlIPC_lock(CtdlIPC *ipc);
+
+INLINE 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
new file mode 100644 (file)
index 0000000..3ea65fc
--- /dev/null
@@ -0,0 +1,18 @@
+/*
+ * 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
new file mode 100644 (file)
index 0000000..2b0d0d6
--- /dev/null
@@ -0,0 +1,29 @@
+/* 
+ * 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
new file mode 100644 (file)
index 0000000..9ec98a9
--- /dev/null
@@ -0,0 +1,77 @@
+/*
+ *
+ * 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
new file mode 100644 (file)
index 0000000..e9fae96
--- /dev/null
@@ -0,0 +1,185 @@
+/*
+ * 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/md5.h b/textclient/src/include/md5.h
new file mode 100644 (file)
index 0000000..8fc2922
--- /dev/null
@@ -0,0 +1,31 @@
+
+#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
new file mode 100644 (file)
index 0000000..e959acd
--- /dev/null
@@ -0,0 +1,35 @@
+/*
+ * 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
new file mode 100644 (file)
index 0000000..71edc80
--- /dev/null
@@ -0,0 +1,61 @@
+/*
+ * 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
new file mode 100644 (file)
index 0000000..499400a
--- /dev/null
@@ -0,0 +1,24 @@
+/*
+ * 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
new file mode 100644 (file)
index 0000000..6ee9599
--- /dev/null
@@ -0,0 +1,25 @@
+/*
+ * 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
new file mode 100644 (file)
index 0000000..8e05796
--- /dev/null
@@ -0,0 +1,31 @@
+/*
+ * 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
new file mode 100644 (file)
index 0000000..7e65bc4
--- /dev/null
@@ -0,0 +1,22 @@
+/*
+ * 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 do_internet_configuration(CtdlIPC *ipc);
+void do_ignet_configuration(CtdlIPC *ipc);
+void network_config_management(CtdlIPC *ipc, char *entrytype, char *comment);
+void do_filterlist_configuration(CtdlIPC *ipc);
+void do_pop3client_configuration(CtdlIPC *ipc);
+void do_rssclient_configuration(CtdlIPC *ipc);
+void do_system_configuration(CtdlIPC *ipc);
+
+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
new file mode 100644 (file)
index 0000000..3732c39
--- /dev/null
@@ -0,0 +1,69 @@
+
+/*
+   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
new file mode 100644 (file)
index 0000000..d87cdf5
--- /dev/null
@@ -0,0 +1,97 @@
+/*
+ * Client-side IPC functions
+ */
+
+
+#include "sysdep.h"
+#undef NDEBUG
+#include <assert.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <stdio.h>
+#include <signal.h>
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <netinet/in.h>
+#include <arpa/inet.h>
+#include <sys/un.h>
+#include <netdb.h>
+#include <string.h>
+#include <pwd.h>
+#include <errno.h>
+#include <stdarg.h>
+#include <libcitadel.h>
+//#include "citadel.h"
+#include "citadel_ipc.h"
+//#include "citadel_decls.h"
+//#ifndef HAVE_SNPRINTF
+//#include "snprintf.h"
+//#endif
+#include "commands.h"
+#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
new file mode 100644 (file)
index 0000000..6e50570
--- /dev/null
@@ -0,0 +1,359 @@
+/*
+ * 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
new file mode 100644 (file)
index 0000000..66299f3
--- /dev/null
@@ -0,0 +1,1981 @@
+/*
+ * Text client functions for reading and writing of messages
+ *
+ * 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.
+ */
+
+#include "sysdep.h"
+#include <stdlib.h>
+#include <unistd.h>
+#include <sys/types.h>
+#include <fcntl.h>
+#include <stdio.h>
+#include <ctype.h>
+#include <string.h>
+#include <signal.h>
+#include <errno.h>
+#include <limits.h>
+#include <sys/wait.h>
+#include <sys/stat.h>
+
+#if TIME_WITH_SYS_TIME
+# include <sys/time.h>
+# include <time.h>
+#else
+# if HAVE_SYS_TIME_H
+#  include <sys/time.h>
+# else
+#  include <time.h>
+# endif
+#endif
+
+#ifdef HAVE_PTHREAD_H
+#include <pthread.h>
+#endif
+
+#include <stdarg.h>
+#include <libcitadel.h>
+///#include "citadel.h"
+#include "citadel_ipc.h"
+#include "citadel_decls.h"
+#include "messages.h"
+#include "commands.h"
+#include "tuiconfig.h"
+#include "rooms.h"
+//#ifndef HAVE_SNPRINTF
+///#include "snprintf.h"
+//#endif
+#include "screen.h"
+
+#define MAXWORDBUF SIZ
+#define NO_REPLY_TO    "nobody ... xxxxxx"
+
+char reply_to[SIZ];
+char reply_subject[SIZ];
+char reply_references[SIZ];
+char reply_inreplyto[SIZ];
+
+struct cittext {
+       struct cittext *next;
+       char text[MAXWORDBUF];
+};
+
+void stty_ctdl(int cmd);
+int haschar(const char *st, int ch);
+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[SIZ];
+       int format_type = 0;
+       int fr = 0;
+       int nhdr = 0;
+       struct ctdlipcmessage *message = NULL;
+       int r;                          /* IPC response code */
+       char *converted_text = NULL;
+       char *lineptr;
+       char *nextline;
+       char *searchptr;
+       int i;
+       char ch;
+       int linelen;
+       int final_line_is_blank = 0;
+
+       has_images = 0;
+
+       sigcaught = 0;
+       stty_ctdl(1);
+
+       strcpy(reply_to, NO_REPLY_TO);
+       strcpy(reply_subject, "");
+       strcpy(reply_references, "");
+       strcpy(reply_inreplyto, "");
+
+       r = CtdlIPCGetSingleMessage(ipc, num, (pagin == READ_HEADER ? 1 : 0), 4, &message, buf);
+       if (r / 100 != 1) {
+               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("hnod=%s\nroom=%s\nnode=%s\ntime=%s",
+                               message->hnod, message->room,
+                               message->node, 
+                               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 {
+               fmt_date(now, sizeof now, message->time, 0);
+               if (dest) {
+                       fprintf(dest, "%s from %s ", now, message->author);
+                       if (!IsEmptyStr(message->email)) {
+                               fprintf(dest, "<%s> ", message->email);
+                       }
+               } else {
+                       color(BRIGHT_CYAN);
+                       scr_printf("%s ", now);
+                       color(DIM_WHITE);
+                       scr_printf("from ");
+                       color(BRIGHT_CYAN);
+                       scr_printf("%s ", message->author);
+                       if (!IsEmptyStr(message->email)) {
+                               color(DIM_WHITE);
+                               scr_printf("<");
+                               color(BRIGHT_BLUE);
+                               scr_printf("%s", message->email);
+                                       color(DIM_WHITE);
+                               scr_printf("> ");
+                       }
+               }
+               if (!IsEmptyStr(message->node)) {
+                       if ((room_flags & QR_NETWORK)
+                           || ((strcasecmp(message->node, ipc->ServInfo.nodename)
+                            && (strcasecmp(message->node, ipc->ServInfo.fqdn))))) {
+                               if (IsEmptyStr(message->email)) {
+                                       if (dest) {
+                                               fprintf(dest, "@%s ", message->node);
+                                       } else {
+                                               color(DIM_WHITE);
+                                               scr_printf("@");
+                                               color(BRIGHT_YELLOW);
+                                               scr_printf("%s ", message->node);
+                                       }
+                               }
+                       }
+               }
+               if (strcasecmp(message->hnod, ipc->ServInfo.humannode)
+                   && (!IsEmptyStr(message->hnod)) && (IsEmptyStr(message->email))) {
+                       if (dest) {
+                               fprintf(dest, "(%s) ", message->hnod);
+                       } else {
+                               color(DIM_WHITE);
+                               scr_printf("(");
+                               color(BRIGHT_WHITE);
+                               scr_printf("%s", message->hnod);
+                               color(DIM_WHITE);
+                               scr_printf(") ");
+                       }
+               }
+               if (strcasecmp(message->room, room_name) && (IsEmptyStr(message->email))) {
+                       if (dest) {
+                               fprintf(dest, "in %s> ", message->room);
+                       } else {
+                               color(DIM_WHITE);
+                               scr_printf("in ");
+                               color(BRIGHT_MAGENTA);
+                               scr_printf("%s> ", message->room);
+                       }
+               }
+               if (!IsEmptyStr(message->recipient)) {
+                       if (dest) {
+                               fprintf(dest, "to %s ", message->recipient);
+                       } else {
+                               color(DIM_WHITE);
+                               scr_printf("to ");
+                               color(BRIGHT_CYAN);
+                               scr_printf("%s ", message->recipient);
+                       }
+               }
+       }
+       
+       if (dest) {
+               fprintf(dest, "\n");
+       } else {
+               scr_printf("\n");
+       }
+
+       /* Set the reply-to address to an Internet e-mail address if possible
+        */
+       if ((message->email != NULL) && (!IsEmptyStr(message->email))) {
+               if (!IsEmptyStr(message->author)) {
+                       snprintf(reply_to, sizeof reply_to, "%s <%s>", message->author, message->email);
+               }
+               else {
+                       safestrncpy(reply_to, message->email, sizeof reply_to);
+               }
+       }
+
+       /* But if we can't do that, set it to a Citadel address.
+        */
+       if (!strcmp(reply_to, NO_REPLY_TO)) {
+               snprintf(reply_to, sizeof(reply_to), "%s @ %s",
+                        message->author, message->node);
+       }
+
+       if (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 */
+       num_urls = 0;   /* Start with a clean slate */
+       searchptr = message->text;
+       while ( (searchptr != NULL) && (num_urls < MAXURLS) ) {
+               searchptr = strstr(searchptr, "http://");
+               if (searchptr != NULL) {
+                       safestrncpy(urls[num_urls], searchptr, sizeof(urls[num_urls]));
+                       for (i = 0; i < strlen(urls[num_urls]); i++) {
+                               ch = urls[num_urls][i];
+                               if (ch == '>' || ch == '\"' || ch == ')' ||
+                                   ch == ' ' || ch == '\n') {
+                                       urls[num_urls][i] = 0;
+                                       break;
+                               }
+                       }
+                       num_urls++;
+                       ++searchptr;
+               }
+       }
+
+       /*
+        * Here we go
+        */
+       if (format_type == 0) {
+               fr = fmout(screenwidth, NULL, message->text, dest, 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 test 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[SIZ];
+       char header[SIZ];
+       int cksum = 0;
+
+       if ( (mode == 2) && (IsEmptyStr(editor_path)) ) {
+               scr_printf("*** No editor available; using built-in editor.\n");
+               mode = 0;
+       }
+
+       fmt_date(datestr, sizeof datestr, time(NULL), 0);
+       header[0] = 0;
+
+       if (room_flags & QR_ANONONLY && !recipient) {
+               snprintf(header, sizeof header, " ****");
+       }
+       else {
+               snprintf(header, sizeof header,
+                       " %s from %s",
+                       datestr,
+                       (is_anonymous ? "[anonymous]" : fullname)
+                       );
+               if (!IsEmptyStr(recipient)) {
+                       size_t tmp = strlen(header);
+                       snprintf(&header[tmp], sizeof header - tmp,
+                               " to %s", recipient);
+               }
+       }
+       scr_printf("%s\n", header);
+       if (subject != NULL) if (!IsEmptyStr(subject)) {
+               scr_printf("Subject: %s\n", subject);
+       }
+       
+       if ( (subject_required) && (IsEmptyStr(subject)) ) {
+               newprompt("Subject: ", subject, 70);
+       }
+
+       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) {
+               formout(ipc, "entermsg");
+       }
+
+       /* 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
new file mode 100644 (file)
index 0000000..732661e
--- /dev/null
@@ -0,0 +1,1402 @@
+/*
+ * Client-side functions which perform room operations
+ *
+ * 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.
+ */
+
+#include "sysdep.h"
+#include <stdlib.h>
+#include <unistd.h>
+#include <fcntl.h>
+#include <stdio.h>
+#include <ctype.h>
+#include <string.h>
+#include <signal.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <sys/wait.h>
+#include <errno.h>
+#include <stdarg.h>
+#include <libcitadel.h>
+//#include "citadel.h"
+#include "citadel_ipc.h"
+#include "citadel_decls.h"
+#include "rooms.h"
+#include "commands.h"
+#include "messages.h"
+#include "tuiconfig.h"
+//#ifndef HAVE_SNPRINTF
+//#include "snprintf.h"
+//#endif
+#include "screen.h"
+//#include "citadel_dirs.h"
+
+#define IFNEXPERT if ((userflags&US_EXPERT)==0)
+
+
+void stty_ctdl(int cmd);
+void dotgoto(CtdlIPC *ipc, char *towhere, int display_name, int fromungoto);
+void progress(CtdlIPC* ipc, unsigned long curr, unsigned long cmax);
+int pattern(char *search, char *patn);
+int file_checksum(char *filename);
+int nukedir(char *dirname);
+
+extern unsigned room_flags;
+extern char room_name[];
+extern char temp[];
+extern char tempdir[];
+extern int editor_pid;
+extern int screenwidth;
+extern int screenheight;
+extern char fullname[];
+extern char sigcaught;
+extern char floor_mode;
+extern char curr_floor;
+
+
+extern int ugnum;
+extern long uglsn;
+extern char *uglist[];
+extern long uglistlsn[];
+extern int uglistsize;
+
+extern char floorlist[128][SIZ];
+
+
+void load_floorlist(CtdlIPC *ipc)
+{
+       int a;
+       char buf[SIZ];
+       char *listing = NULL;
+       int r;                  /* IPC response code */
+
+       for (a = 0; a < 128; ++a)
+               floorlist[a][0] = 0;
+
+       r = CtdlIPCFloorListing(ipc, &listing, buf);
+       if (r / 100 != 1) {
+               strcpy(floorlist[0], "Main Floor");
+               return;
+       }
+       while (*listing && !IsEmptyStr(listing)) {
+               extract_token(buf, listing, 0, '\n', sizeof buf);
+               remove_token(listing, 0, '\n');
+               extract_token(floorlist[extract_int(buf, 0)], buf, 1, '|', SIZ);
+       }
+       free(listing);
+}
+
+
+void room_tree_list(struct ctdlroomlisting *rp)
+{
+       static int c = 0;
+       char rmname[ROOMNAMELEN];
+       int f;
+
+       if (rp == NULL) {
+               c = 1;
+               return;
+       }
+
+       if (rp->lnext != NULL) {
+               room_tree_list(rp->lnext);
+       }
+
+       if (sigcaught == 0) {
+               strcpy(rmname, rp->rlname);
+               f = rp->rlflags;
+               if ((c + strlen(rmname) + 4) > screenwidth) {
+
+                       /* line break, check the paginator */
+                       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) && (f & QR_NETWORK))
+                       scr_printf("}  ");
+               else if (f & QR_DIRECTORY)
+                       scr_printf("]  ");
+               else if (f & QR_NETWORK)
+                       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;
+    case 6:
+       scr_printf("\n   Shared rooms:\n");
+           listrms(listing, LISTRMS_ALL, -1, QR_NETWORK, 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 raide[USERNAME_SIZE];
+       char buf[SIZ];
+       struct ctdlroom *attr = NULL;
+       struct ExpirePolicy *eptr = NULL;
+       int r;                          /* IPC response code */
+
+       /* Fetch the existing room config */
+       r = CtdlIPCGetRoomAttributes(ipc, &attr, buf);
+       if (r / 100 != 2) {
+               scr_printf("%s\n", buf);
+               return;
+       }
+       eptr = &(attr->QRep);
+
+       /* Fetch the name of the current room aide */
+       r = CtdlIPCGetRoomAide(ipc, buf);
+       if (r / 100 == 2) {
+               safestrncpy(raide, buf, sizeof raide);
+       } else {
+               strcpy(raide, "");
+       }
+       if (IsEmptyStr(raide)) {
+               strcpy(raide, "none");
+       }
+
+       /* Fetch the expire policy (this will silently fail on old servers,
+        * resulting in "default" policy)
+        */
+       r = CtdlIPCGetMessageExpirationPolicy(ipc, 0, &eptr, buf);
+
+       /* Now interact with the user. */
+
+       strprompt("Room name", attr->QRname, ROOMNAMELEN-1);
+       attr->QRfloor = select_floor(ipc, attr->QRfloor);
+       attr->QRflags = set_room_attr(ipc, attr->QRflags, "Private room", QR_PRIVATE);
+       if (attr->QRflags & QR_PRIVATE) {
+               attr->QRflags = set_room_attr(ipc, attr->QRflags,
+                                      "Hidden room (accessible to anyone who knows the room name)",
+                                      QR_GUESSNAME);
+       }
+
+       /* if it's public, clear the privacy classes */
+       if ((attr->QRflags & QR_PRIVATE) == 0) {
+               if (attr->QRflags & QR_GUESSNAME) {
+                       attr->QRflags = attr->QRflags - QR_GUESSNAME;
+               }
+               if (attr->QRflags & QR_PASSWORDED) {
+                       attr->QRflags = attr->QRflags - QR_PASSWORDED;
+               }
+       }
+
+       /* if it's private, choose the privacy classes */
+       if ((attr->QRflags & QR_PRIVATE)
+           && ((attr->QRflags & QR_GUESSNAME) == 0)) {
+               attr->QRflags = set_room_attr(ipc, attr->QRflags,
+                                      "Accessible by entering a password",
+                                      QR_PASSWORDED);
+       }
+       if ((attr->QRflags & QR_PRIVATE)
+           && ((attr->QRflags & QR_PASSWORDED) == QR_PASSWORDED)) {
+               strprompt("Room password", attr->QRpasswd, 9);
+       }
+
+       if ((attr->QRflags & QR_PRIVATE) == QR_PRIVATE) {
+               rbump = boolprompt("Cause current users to forget room", 0);
+       }
+
+       attr->QRflags = set_room_attr(ipc, attr->QRflags,
+                                       "Preferred users only", QR_PREFONLY);
+       attr->QRflags = set_room_attr(ipc, attr->QRflags,
+                                       "Read-only room", QR_READONLY);
+       attr->QRflags2 = set_room_attr(ipc, attr->QRflags2,
+                               "Allow message deletion by anyone who can post",
+                               QR2_COLLABDEL);
+       attr->QRflags = set_room_attr(ipc, attr->QRflags,
+                                       "Permanent room", QR_PERMANENT);
+       attr->QRflags2 = set_room_attr(ipc, attr->QRflags2,
+                                                                  "Subject Required (Force "
+                                                                  "users to specify a message "
+                                   "subject)", QR2_SUBJECTREQ);
+       attr->QRflags = set_room_attr(ipc, attr->QRflags,
+                                       "Directory room", QR_DIRECTORY);
+       if (attr->QRflags & QR_DIRECTORY) {
+               strprompt("Directory name", attr->QRdirname, 14);
+               attr->QRflags =
+                   set_room_attr(ipc, attr->QRflags,
+                                               "Uploading allowed", QR_UPLOAD);
+               attr->QRflags =
+                   set_room_attr(ipc, attr->QRflags, "Downloading allowed",
+                                 QR_DOWNLOAD);
+               attr->QRflags =
+                   set_room_attr(ipc, attr->QRflags,
+                                               "Visible directory", QR_VISDIR);
+       }
+       attr->QRflags = set_room_attr(ipc, attr->QRflags,
+                                       "Network shared room", QR_NETWORK);
+       attr->QRflags2 = set_room_attr(ipc, attr->QRflags2,
+                               "Self-service list subscribe/unsubscribe",
+                               QR2_SELFLIST);
+       attr->QRflags2 = set_room_attr(ipc, attr->QRflags2,
+                               "public posting to this room via room_roomname@yourcitadel.org",
+                               QR2_SMTP_PUBLIC);
+       attr->QRflags2 = set_room_attr(ipc, attr->QRflags2,
+                               "moderated mailinglist",
+                               QR2_MODERATED);
+       attr->QRflags = set_room_attr(ipc, attr->QRflags,
+                              "Automatically make all messages anonymous",
+                              QR_ANONONLY);
+       if ((attr->QRflags & QR_ANONONLY) == 0) {
+               attr->QRflags = set_room_attr(ipc, attr->QRflags,
+                                      "Ask users whether to make messages anonymous",
+                                      QR_ANONOPT);
+       }
+       attr->QRorder = intprompt("Listing order", attr->QRorder, 0, 127);
+
+       /* Ask about the room aide */
+       do {
+               strprompt("Room aide (or 'none')", raide, 29);
+               if (!strcasecmp(raide, "none")) {
+                       strcpy(raide, "");
+                       break;
+               } else {
+                       r = CtdlIPCQueryUsername(ipc, raide, buf);
+                       if (r / 100 != 2)
+                               scr_printf("%s\n", buf);
+               }
+       } while (r / 100 != 2);
+
+       /* Angels and demons dancing in my head... */
+       do {
+               snprintf(buf, sizeof buf, "%d", attr->QRep.expire_mode);
+               strprompt("Message expire policy (? for list)", buf, 1);
+               if (buf[0] == '?') {
+                       scr_printf("\n"
+                               "0. Use the default for this floor\n"
+                               "1. Never automatically expire messages\n"
+                               "2. Expire by message count\n"
+                               "3. Expire by message age\n");
+               }
+       } while ((buf[0] < 48) || (buf[0] > 51));
+       attr->QRep.expire_mode = buf[0] - 48;
+
+       /* ...lunatics and monsters underneath my bed */
+       if (attr->QRep.expire_mode == 2) {
+               snprintf(buf, sizeof buf, "%d", attr->QRep.expire_value);
+               strprompt("Keep how many messages online?", buf, 10);
+               attr->QRep.expire_value = atol(buf);
+       }
+
+       if (attr->QRep.expire_mode == 3) {
+               snprintf(buf, sizeof buf, "%d", attr->QRep.expire_value);
+               strprompt("Keep messages for how many days?", buf, 10);
+               attr->QRep.expire_value = atol(buf);
+       }
+
+       /* Give 'em a chance to change their minds */
+       scr_printf("Save changes (y/n)? ");
+
+       if (yesno() == 1) {
+               r = CtdlIPCSetRoomAide(ipc, raide, buf);
+               if (r / 100 != 2) {
+                       scr_printf("%s\n", buf);
+               }
+
+               r = CtdlIPCSetMessageExpirationPolicy(ipc, 0, eptr, buf);
+               if (r / 100 != 2) {
+                       scr_printf("%s\n", buf);
+               }
+
+               r = CtdlIPCSetRoomAttributes(ipc, rbump, attr, buf);
+               scr_printf("%s\n", buf);
+               strncpy(buf, attr->QRname, ROOMNAMELEN);
+               free(attr);
+               if (r / 100 == 2)
+                       dotgoto(ipc, buf, 2, 0);
+       }
+       else free(attr);
+}
+
+
+/*
+ * un-goto the previous room, or a specified room
+ */
+void dotungoto(CtdlIPC *ipc, char *towhere)
+  {
+    /* Find this 'towhere' room in the list ungoto from this room to
+       that at the messagepointer position in that room in our ungoto list.
+       I suppose I could be a real dick and just ungoto that many places
+       in our list. */
+    int found = -1;
+    int lp;
+       char buf[SIZ];
+       struct ctdlipcroom *rret = NULL;        /* ignored */
+       int r;
+
+       if (uglistsize == 0)
+      {
+               scr_printf("No rooms to ungoto.\n");
+               return;
+      }
+       if (towhere == NULL)
+      {
+               scr_printf("Must specify a room to ungoto.\n");
+               return;
+      }
+       if (IsEmptyStr(towhere))
+      {
+               scr_printf("Must specify a room to ungoto.\n");
+               return;
+      }
+    for (lp = uglistsize-1; lp >= 0; lp--)
+      {
+        if (strcasecmp(towhere, uglist[lp]) == 0)
+          {
+            found = lp;
+            break;
+          }
+      }
+    if (found == -1)
+      {
+               scr_printf("Room: %s not in ungoto list.\n", towhere);
+       return;
+      }
+
+       r = CtdlIPCGotoRoom(ipc, uglist[found], "", &rret, buf);
+       if (rret) free(rret);   /* ignored */
+       if (r / 100 != 2) {
+               scr_printf("%s\n", buf);
+               return;
+       }
+       r = CtdlIPCSetLastRead(ipc, uglistlsn[found] ? uglistlsn[found] : 1, buf);
+       if (r / 100 != 2) {
+               scr_printf("%s\n", buf);
+       }
+       safestrncpy(buf, uglist[found], sizeof(buf));
+    /* we queue ungoto information here, because we're not really
+       ungotoing, we're really going to a random spot in some arbitrary
+       room list. */
+       dotgoto(ipc, buf, 0, 0);
+  }
+
+void ungoto(CtdlIPC *ipc)
+{
+       char buf[SIZ];
+       struct ctdlipcroom *rret = NULL;        /* ignored */
+       int r;
+
+       if (uglistsize == 0)
+               return;
+
+       r = CtdlIPCGotoRoom(ipc, uglist[uglistsize-1], "", &rret, buf);
+       if (rret) free(rret);   /* ignored */
+       if (r / 100 != 2) {
+               scr_printf("%s\n", buf);
+               return;
+       }
+       r = CtdlIPCSetLastRead(ipc, uglistlsn[uglistsize-1] ? uglistlsn[uglistsize-1] : 1, buf);
+       if (r / 100 != 2) {
+               scr_printf("%s\n", buf);
+       }
+       safestrncpy(buf, uglist[uglistsize-1], sizeof(buf));
+       uglistsize--;
+       free(uglist[uglistsize]);
+       /* Don't queue ungoto info or we end up in a loop */
+       dotgoto(ipc, buf, 0, 1);
+}
+
+
+/*
+ * saves filelen bytes from file at pathname
+ */
+int save_buffer(void *file, size_t filelen, const char *pathname)
+{
+       size_t block = 0;
+       size_t bytes_written = 0;
+       FILE *fp;
+
+       fp = fopen(pathname, "w");
+       if (!fp) {
+               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 Aide)\n"
+                       "<5>Personal room (accessible to you only)\n"
+                       "Enter room type: "
+               );
+               do {
+                       b = inkey();
+               } while (((b < '1') || (b > '5')) && (b != '?'));
+               if (b == '?') {
+                       scr_printf("?\n");
+                       formout(ipc, "roomaccess");
+               }
+       } while ((b < '1') || (b > '5'));
+       b -= '0';                       /* Portable */
+       scr_printf("%d\n", b);
+       new_room_type = b - 1;
+       if (new_room_type == 2) {
+               newprompt("Enter a room password: ", new_room_pass, 9);
+               for (a = 0; !IsEmptyStr(&new_room_pass[a]); ++a)
+                       if (new_room_pass[a] == '|')
+                               new_room_pass[a] = '_';
+       } else {
+               strcpy(new_room_pass, "");
+       }
+
+       scr_printf("\042%s\042, a", new_room_name);
+       if (b == 1)
+               scr_printf(" public room.");
+       if (b == 2)
+               scr_printf(" hidden room.");
+       if (b == 3)
+               scr_printf(" passworded room, password: %s", new_room_pass);
+       if (b == 4)
+               scr_printf("n invitation-only room.");
+       if (b == 5)
+               scr_printf(" personal room.");
+       scr_printf("\nInstall it? (y/n) : ");
+       if (yesno() == 0) {
+               return;
+       }
+
+       r = CtdlIPCCreateRoom(ipc, 1, new_room_name, new_room_type,
+                             new_room_pass, new_room_floor, buf);
+       if (r / 100 != 2) {
+               scr_printf("%s\n", buf);
+               return;
+       }
+
+       /* command succeeded... now GO to the new room! */
+       dotgoto(ipc, new_room_name, 0, 0);
+}
+
+
+
+void readinfo(CtdlIPC *ipc)
+{                              /* read info file for current room */
+       char buf[SIZ];
+       char raide[64];
+       int r;                  /* IPC response code */
+       char *text = NULL;
+
+       /* Name of currernt room aide */
+       r = CtdlIPCGetRoomAide(ipc, buf);
+       if (r / 100 == 2)
+               safestrncpy(raide, buf, sizeof raide);
+       else
+               strcpy(raide, "");
+
+       if (!IsEmptyStr(raide))
+               scr_printf("Room aide is %s.\n\n", raide);
+
+       r = CtdlIPCRoomInfo(ipc, &text, buf);
+       if (r / 100 != 1)
+               return;
+
+       if (text) {
+               fmout(screenwidth, NULL, text, NULL, 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
new file mode 100644 (file)
index 0000000..33e05c8
--- /dev/null
@@ -0,0 +1,626 @@
+/*
+ * Client-side support functions.
+ *
+ * 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.
+ */
+
+#include "sysdep.h"
+#include <stdlib.h>
+#include <unistd.h>
+#include <fcntl.h>
+#include <stdio.h>
+#include <ctype.h>
+#include <string.h>
+#include <sys/types.h>
+#include <sys/ioctl.h>
+#include <pwd.h>
+#include <signal.h>
+#include <dirent.h>
+#include <errno.h>
+
+#if TIME_WITH_SYS_TIME
+# include <sys/time.h>
+# include <time.h>
+#else
+# if HAVE_SYS_TIME_H
+#  include <sys/time.h>
+# else
+#  include <time.h>
+# endif
+#endif
+
+#ifdef HAVE_LIMITS_H
+#include <limits.h>
+#endif
+#ifdef HAVE_UTMP_H
+#include <utmp.h>
+#endif
+#ifdef HAVE_UTMPX_H
+#include <utmpx.h>
+#endif
+
+#include <libcitadel.h>
+///#include "citadel.h"
+#include "citadel_ipc.h"
+#include "screen.h"
+
+#ifndef HAVE_GETUTLINE
+struct utmp *getutline(struct utmp *ut);
+#endif
+
+#define ROUTINES_C
+
+///#include "citadel.h"
+#include "routines.h"
+#include "commands.h"
+#include "citadel_decls.h"
+#include "routines2.h"
+#include "tuiconfig.h"
+
+#define IFAIDE if(axlevel>=AxAideU)
+#define IFNAIDE if (axlevel<AxAideU)
+
+extern unsigned userflags;
+//extern char *axdefs[8];
+extern char sigcaught;
+extern char rc_floor_mode;
+extern int rc_ansi_color;
+extern int rc_prompt_control;
+
+/* Destructive backspace */
+void back(int spaces) {
+       int a;
+       for (a=0; a<spaces; ++a) {
+               scr_putc(8);
+               scr_putc(32);
+               scr_putc(8);
+       }
+}
+
+/*
+ * Edit or delete a user (cmd=25 to edit/create, 96 to delete)
+ */
+void edituser(CtdlIPC *ipc, int cmd)
+{
+       char buf[SIZ];
+       char who[USERNAME_SIZE];
+       char newname[USERNAME_SIZE];
+       struct ctdluser *user = NULL;
+       int newnow = 0;
+       int r;                          /* IPC response code */
+       int change_name = 0;
+
+       strcpy(newname, "");
+
+       newprompt("User name: ", who, 29);
+       while ((r = CtdlIPCAideGetUserParameters(ipc, who, &user, buf)) / 100 != 2) {
+               scr_printf("%s\n", buf);
+               if (cmd == 25) {
+                       scr_printf("Do you want to create this user? ");
+                       if (yesno()) {
+                               r = CtdlIPCCreateUser(ipc, who, 0, buf);
+                               if (r / 100 == 2) {
+                                       newnow = 1;
+                                       continue;
+                               }
+                               scr_printf("%s\n", buf);
+                       }
+               }
+               free(user);
+               return;
+       }
+
+       if (cmd == 25) {
+               val_user(ipc, user->fullname, 0); /* Display registration */
+
+               if (!newnow) {
+                       change_name = 1;
+                       while (change_name == 1) {
+                               if (boolprompt("Change name", 0)) {
+                                       strprompt("New name", newname, USERNAME_SIZE-1);
+                                       r = CtdlIPCRenameUser(ipc, user->fullname, newname, buf);
+                                       if (r / 100 != 2) {
+                                               scr_printf("%s\n", buf);
+                                       }
+                                       else {
+                                               strcpy(user->fullname, newname);
+                                               change_name = 0;
+                                       }
+                               }
+                               else {
+                                       change_name = 0;
+                               }
+                       }
+               }
+
+               if (newnow || boolprompt("Change password", 0)) {
+                       strprompt("Password", user->password, -19);
+               }
+       
+               user->axlevel = intprompt("Access level", user->axlevel, 0, 6);
+               if (boolprompt("Permission to send Internet mail", (user->flags & US_INTERNET)))
+                       user->flags |= US_INTERNET;
+               else
+                       user->flags &= ~US_INTERNET;
+               if (boolprompt("Ask user to register again", !(user->flags & US_REGIS)))
+                       user->flags &= ~US_REGIS;
+               else
+                       user->flags |= US_REGIS;
+               user->timescalled = intprompt("Times called",
+                               user->timescalled, 0, INT_MAX);
+               user->posted = intprompt("Messages posted",
+                                       user->posted, 0, INT_MAX);
+               user->lastcall = boolprompt("Set last call to now", 0) ?
+                                       time(NULL) : user->lastcall;
+               user->USuserpurge = intprompt("Purge time (in days, 0 for system default",
+                               user->USuserpurge, 0, INT_MAX);
+       }
+
+       if (cmd == 96) {
+               scr_printf("Do you want to delete this user? ");
+               if (!yesno()) {
+                       free(user);
+                       return;
+               }
+               user->axlevel = AxDeleted;
+       }
+
+       r = CtdlIPCAideSetUserParameters(ipc, user, buf);
+       if (r / 100 != 2) {
+               scr_printf("%s\n", buf);
+       }
+       free(user);
+}
+
+
+/* Display a prompt and flip a bit based on whether the user answers
+ * yes or no.  Yes=1 and No=0, unless 'backwards' is set to a nonzero value
+ * in which case No=1 and Yes=0.
+ */
+int set_attr(CtdlIPC *ipc, unsigned int sval, char *prompt, unsigned int sbit, int backwards)
+{
+       int a;
+       int temp;
+
+       temp = sval;
+       color(DIM_WHITE);
+       scr_printf("%50s ", prompt);
+       color(DIM_MAGENTA);
+       scr_printf("[");
+       color(BRIGHT_MAGENTA);
+
+       if (backwards) {
+               scr_printf("%3s", ((temp&sbit) ? "No":"Yes"));
+       }
+       else {
+               scr_printf("%3s", ((temp&sbit) ? "Yes":"No"));
+       }
+
+       color(DIM_MAGENTA);
+       scr_printf("]? ");
+       color(BRIGHT_CYAN);
+       a = (temp & sbit);
+       if (a != 0) a = 1;
+       if (backwards) a = 1 - a;
+       a = yesno_d(a);
+       if (backwards) a = 1 - a;
+       color(DIM_WHITE);
+       temp = (temp|sbit);
+       if (!a) temp = (temp^sbit);
+       return(temp);
+}
+
+/*
+ * modes are:  0 - .EC command, 1 - .EC for new user,
+ *             2 - toggle Xpert mode  3 - toggle floor mode
+ */
+void enter_config(CtdlIPC *ipc, int mode)
+{
+       char buf[SIZ];
+       struct ctdluser *user = NULL;
+       int r;                          /* IPC response code */
+
+       r = CtdlIPCGetConfig(ipc, &user, buf);
+       if (r / 100 != 2) {
+               scr_printf("%s\n", buf);
+               free(user);
+               return;
+       }
+
+       if (mode == 0 || mode == 1) {
+
+               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]);
+       }
+
+}
+
+
+#ifndef HAVE_STRERROR
+/*
+ * replacement strerror() for systems that don't have it
+ */
+char *strerror(int e)
+{
+       static char buf[128];
+
+       snprintf(buf, sizeof buf, "errno = %d",e);
+       return(buf);
+}
+#endif
+
+
+void progress(CtdlIPC* ipc, unsigned long curr, unsigned long cmax)
+{
+       static char dots[] =
+               "**************************************************";
+       char dots_printed[51];
+       char fmt[42];
+       unsigned long a;
+
+       if (curr >= cmax) {
+               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)
+{
+#ifndef HAVE_UTMP_H
+       char buf[SIZ];
+       FILE *who;
+       int a,b;
+
+       who = (FILE *)popen("who am i","r");
+       if (who==NULL) {
+               strcpy(hbuf, ipc->ServInfo.fqdn);
+               return; 
+       }
+       fgets(buf,sizeof buf,who);
+       pclose(who);
+
+       b = 0;
+       for (a=0; !IsEmptyStr(&buf[a]); ++a) {
+               if ((buf[a]=='(')||(buf[a]==')')) ++b;
+       }
+       if (b<2) {
+               strcpy(hbuf, ipc->ServInfo.fqdn);
+               return;
+       }
+
+       for (a=0; a<strlen(buf); ++a) {
+               if (buf[a]=='(') {
+                       strcpy(buf,&buf[a+1]);
+               }
+       }
+       for (a=0; a<strlen(buf); ++a) {
+               if (buf[a]==')') buf[a] = 0;
+       }
+
+       if (IsEmptyStr(buf)) strcpy(hbuf, ipc->ServInfo.fqdn);
+       else strncpy(hbuf,buf,24);
+#else
+       char *tty = ttyname(0);
+#ifdef HAVE_GETUTXLINE
+       struct utmpx ut, *put;
+#else
+       struct utmp ut, *put;
+#endif
+
+       if (tty == NULL) {
+           fail:
+               safestrncpy(hbuf, ipc->ServInfo.fqdn, 24);
+               return;
+       }
+
+       if (strncmp(tty, "/dev/", 5))
+               goto fail;
+
+       safestrncpy(ut.ut_line, &tty[5], sizeof ut.ut_line);
+
+#ifdef HAVE_GETUTXLINE /* Solaris uses this */
+       if ((put = getutxline(&ut)) == NULL)
+#else
+       if ((put = getutline(&ut)) == NULL)
+#endif
+               goto fail;
+
+#if defined(HAVE_UT_TYPE) || defined(HAVE_GETUTXLINE)
+       if (put->ut_type == USER_PROCESS) {
+#endif
+#if defined(HAVE_UT_HOST) || defined(HAVE_GETUTXLINE)
+               if (*put->ut_host)
+                       safestrncpy(hbuf, put->ut_host, 24);
+               else
+#endif
+                       safestrncpy(hbuf, put->ut_line, 24);
+#if defined(HAVE_UT_TYPE) || defined(HAVE_GETUTXLINE)
+       }
+       else goto fail;
+#endif
+#endif /* HAVE_UTMP_H */
+}
+
+/*
+ * miscellaneous server commands (testing, etc.)
+ */
+void misc_server_cmd(CtdlIPC *ipc, char *cmd) {
+       char buf[SIZ];
+
+       CtdlIPC_chat_send(ipc, cmd);
+       CtdlIPC_chat_recv(ipc, buf);
+       scr_printf("%s\n",buf);
+       if (buf[0]=='1') {
+               set_keepalives(KA_HALF);
+               while (CtdlIPC_chat_recv(ipc, buf), strcmp(buf,"000")) {
+                       scr_printf("%s\n",buf);
+               }
+               set_keepalives(KA_YES);
+               return;
+       }
+       if (buf[0]=='4') {
+               do {
+                       newprompt("> ",buf,255);
+                       CtdlIPC_chat_send(ipc, buf);
+               } while(strcmp(buf,"000"));
+               return;
+       }
+}
+
+
+/*
+ * compute the checksum of a file
+ */
+int file_checksum(char *filename)
+{
+       int cksum = 0;
+       int ch;
+       FILE *fp;
+
+       fp = fopen(filename,"r");
+       if (fp == NULL) return(0);
+
+       /* yes, this algorithm may allow cksum to overflow, but that's ok
+        * as long as it overflows consistently, which it will.
+        */
+       while (ch=getc(fp), ch>=0) {
+               cksum = (cksum + ch);
+       }
+
+       fclose(fp);
+       return(cksum);
+}
+
+/*
+ * nuke a directory and its contents
+ */
+int nukedir(char *dirname)
+{
+       DIR *dp;
+       struct dirent *d;
+       char filename[SIZ];
+
+       dp = opendir(dirname);
+       if (dp == NULL) {
+               return(errno);
+       }
+
+       while (d = readdir(dp), d != NULL) {
+               snprintf(filename, sizeof filename, "%s/%s",
+                       dirname, d->d_name);
+               unlink(filename);
+       }
+
+       closedir(dp);
+       return(rmdir(dirname));
+}
diff --git a/textclient/src/routines2.c b/textclient/src/routines2.c
new file mode 100644 (file)
index 0000000..32ec9fc
--- /dev/null
@@ -0,0 +1,628 @@
+/*
+ * More client-side support functions.
+ * Unlike routines.c, some of these DO use global variables.
+ *
+ * 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.
+ */
+
+#include <stdlib.h>
+#include <unistd.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+#include <stdio.h>
+#include <ctype.h>
+#include <string.h>
+#include <limits.h>
+
+#if TIME_WITH_SYS_TIME
+# include <sys/time.h>
+# include <time.h>
+#else
+# if HAVE_SYS_TIME_H
+#  include <sys/time.h>
+# else
+#  include <time.h>
+# endif
+#endif
+
+#include <signal.h>
+#include <pwd.h>
+#include <errno.h>
+#include <stdarg.h>
+#include <libcitadel.h>
+#include "sysdep.h"
+///#include "citadel.h"
+#include "citadel_ipc.h"
+#include "citadel_decls.h"
+#include "routines2.h"
+#include "routines.h"
+#include "commands.h"
+//#include "messages.h"
+//#ifndef HAVE_SNPRINTF
+//#include "snprintf.h"
+//#endif
+#include "screen.h"
+
+/* work around solaris include files */
+#ifdef reg
+#undef reg
+#endif
+
+extern char temp[];
+extern char tempdir[];
+extern char *axdefs[8];
+extern long highest_msg_read;
+extern long maxmsgnum;
+extern unsigned room_flags;
+extern int screenwidth;
+
+
+/*
+int eopen(char *name, int mode)
+{
+       int ret;
+       ret = open(name, mode);
+       if (ret < 0) {
+               scr_printf("Cannot open file '%s', mode=%d, errno=%d\n",
+                       name, mode, errno);
+               interr(errno);
+       }
+       return (ret);
+}
+*/
+
+
+int room_prompt(unsigned int qrflags)
+{                              /* return proper room prompt character */
+       int a;
+       a = '>';
+       if (qrflags & QR_DIRECTORY)
+               a = ']';
+       if ((a == ']') && (qrflags & QR_NETWORK))
+               a = '}';
+       if ((a == '>') && (qrflags & QR_NETWORK))
+               a = ')';
+       return (a);
+}
+
+void entregis(CtdlIPC *ipc)
+{                              /* register with name and address */
+
+       char buf[SIZ];
+       char tmpname[30];
+       char tmpaddr[25];
+       char tmpcity[15];
+       char tmpstate[3];
+       char tmpzip[11];
+       char tmpphone[15];
+       char tmpemail[SIZ];
+       char tmpcountry[32];
+       char diruser[256];
+       char dirnode[256];
+       char holdemail[SIZ];
+       char *reg = NULL;
+       int ok = 0;
+       int r;                          /* IPC response code */
+
+       strcpy(tmpname, "");
+       strcpy(tmpaddr, "");
+       strcpy(tmpcity, "");
+       strcpy(tmpstate, "");
+       strcpy(tmpzip, "");
+       strcpy(tmpphone, "");
+       strcpy(tmpemail, "");
+       strcpy(tmpcountry, "");
+
+       r = CtdlIPCGetUserRegistration(ipc, NULL, &reg, buf);
+       if (r / 100 == 1) {
+               int a = 0;
+
+               while (reg && !IsEmptyStr(reg)) {
+
+                       extract_token(buf, reg, 0, '\n', sizeof buf);
+                       remove_token(reg, 0, '\n');
+
+                       if (a == 2)
+                               safestrncpy(tmpname, buf, sizeof tmpname);
+                       else if (a == 3)
+                               safestrncpy(tmpaddr, buf, sizeof tmpaddr);
+                       else if (a == 4)
+                               safestrncpy(tmpcity, buf, sizeof tmpcity);
+                       else if (a == 5)
+                               safestrncpy(tmpstate, buf, sizeof tmpstate);
+                       else if (a == 6)
+                               safestrncpy(tmpzip, buf, sizeof tmpzip);
+                       else if (a == 7)
+                               safestrncpy(tmpphone, buf, sizeof tmpphone);
+                       else if (a == 9)
+                               safestrncpy(tmpemail, buf, sizeof tmpemail);
+                       else if (a == 10)
+                               safestrncpy(tmpcountry, buf, sizeof tmpcountry);
+                       ++a;
+               }
+       }
+       strprompt("REAL name", tmpname, 29);
+       strprompt("Address", tmpaddr, 24);
+       strprompt("City/town", tmpcity, 14);
+       strprompt("State/province", tmpstate, 2);
+       strprompt("ZIP/Postal Code", tmpzip, 10);
+       strprompt("Country", tmpcountry, 31);
+       strprompt("Telephone number", tmpphone, 14);
+
+       do {
+               ok = 1;
+               safestrncpy(holdemail, tmpemail, sizeof holdemail);
+               strprompt("Email address", tmpemail, 31);
+               r = CtdlIPCDirectoryLookup(ipc, tmpemail, buf);
+               if (r / 100 == 2) {
+                       extract_token(diruser, buf, 0, '@', sizeof diruser);
+                       extract_token(dirnode, buf, 1, '@', sizeof dirnode);
+                       striplt(diruser);
+                       striplt(dirnode);
+                       if ((strcasecmp(diruser, fullname))
+                          || (strcasecmp(dirnode, ipc->ServInfo.nodename))) {
+                               scr_printf(
+                                       "\nYou can't use %s as your address.\n",
+                                       tmpemail);
+                               scr_printf(
+                                       "It is already in use by %s @ %s.\n",
+                                       diruser, dirnode);
+                               ok = 0;
+                               safestrncpy(tmpemail, holdemail, sizeof tmpemail);
+                       }
+               }
+       } while (ok == 0);
+
+       /* now send the registration info back to the server */
+       reg = (char *)realloc(reg, SIZ);
+       if (reg) {
+               sprintf(reg, "%s\n%s\n%s\n%s\n%s\n%s\n%s\n%s\n",
+                       tmpname, tmpaddr, tmpcity, tmpstate,
+                       tmpzip, tmpphone, tmpemail, tmpcountry);
+               r = CtdlIPCSetRegistration(ipc, reg, buf);
+               if (r / 100 != 4)
+                       scr_printf("%s\n", buf);
+               free(reg);
+       }
+       scr_printf("\n");
+}
+
+void updatels(CtdlIPC *ipc)
+{                              /* make all messages old in current room */
+       char buf[256];
+       int r;                          /* IPC response code */
+
+       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);
+}
+
+
+void validate(CtdlIPC *ipc)
+{                              /* validate new users */
+       char cmd[256];
+       char buf[256];
+       int finished = 0;
+       int r;                          /* IPC response code */
+
+       do {
+               r = CtdlIPCNextUnvalidatedUser(ipc, cmd);
+               if (r / 100 != 3)
+                       finished = 1;
+               if (r / 100 == 2)
+                       scr_printf("%s\n", cmd);
+               if (r / 100 == 3) {
+                       extract_token(buf, cmd, 0, '|', sizeof buf);
+                       if (val_user(ipc, buf, 1) != 0) finished = 1;
+               }
+       } while (finished == 0);
+}
+
+void subshell(void)
+{
+       int a, b;
+
+       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
new file mode 100644 (file)
index 0000000..4144c24
--- /dev/null
@@ -0,0 +1,276 @@
+/*
+ * Screen output handling
+ *
+ * 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.
+ */
+
+
+#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"
+///#ifndef HAVE_SNPRINTF
+///#include "snprintf.h"
+///#endif
+#include <libcitadel.h>
+///#include "citadel.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;
+       register 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
new file mode 100644 (file)
index 0000000..254f4e6
--- /dev/null
@@ -0,0 +1,1254 @@
+/*
+ * Configuration screens that are part of the text mode client.
+ *
+ * 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.
+ */
+
+#include <stdlib.h>
+#include <unistd.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+#include <stdio.h>
+#include <ctype.h>
+#include <string.h>
+#include <limits.h>
+
+#if TIME_WITH_SYS_TIME
+# include <sys/time.h>
+# include <time.h>
+#else
+# if HAVE_SYS_TIME_H
+#  include <sys/time.h>
+# else
+#  include <time.h>
+# endif
+#endif
+
+#include <signal.h>
+#include <pwd.h>
+#include <errno.h>
+#include <stdarg.h>
+#include <libcitadel.h>
+#include "sysdep.h"
+///#include "citadel.h"
+#include "citadel_ipc.h"
+#include "citadel_decls.h"
+#include "tuiconfig.h"
+#include "messages.h"
+#include "routines.h"
+#include "commands.h"
+///#ifndef HAVE_SNPRINTF
+///#include "snprintf.h"
+///#endif
+#include "screen.h"
+
+/* work around solaris include files */
+#ifdef reg
+#undef reg
+#endif
+
+extern char temp[];
+extern char tempdir[];
+extern char *axdefs[8];
+extern long highest_msg_read;
+extern long maxmsgnum;
+extern unsigned room_flags;
+extern int screenwidth;
+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 aide privs to a user who creates a private room",
+               atoi(&sc[4][0]))));
+
+       snprintf(sc[8], sizeof sc[8], "%d", (boolprompt(
+               "Automatically move problem user messages to twit room",
+               atoi(&sc[8][0]))));
+
+       strprompt("Name of twit room", &sc[9][0], ROOMNAMELEN);
+       snprintf(sc[11], sizeof sc[11], "%d", (boolprompt(
+               "Restrict Internet mail to only those with that privilege",
+               atoi(&sc[11][0]))));
+       snprintf(sc[26], sizeof sc[26], "%d", (boolprompt(
+               "Allow Aides to Zap (forget) rooms",
+               atoi(&sc[26][0]))));
+
+       if (!IsEmptyStr(&sc[18][0])) {
+               logpages = 1;
+       }
+       else {
+               logpages = 0;
+       }
+       logpages = boolprompt("Log all instant messages", logpages);
+       if (logpages) {
+               strprompt("Name of logging room", &sc[18][0], ROOMNAMELEN);
+       }
+       else {
+               sc[18][0] = 0;
+       }
+
+       /* Commented out because this setting isn't really appropriate to
+        * change while the server is running.
+        *
+        * snprintf(sc[52], sizeof sc[52], "%d", (boolprompt(
+        *      "Use system authentication",
+        *      atoi(&sc[52][0]))));
+        */
+
+       /* Server tuning */
+
+       strprompt("Server connection idle timeout (in seconds)", &sc[5][0], 4);
+       strprompt("Maximum concurrent sessions", &sc[14][0], 4);
+       strprompt("Maximum message length", &sc[20][0], 20);
+       strprompt("Minimum number of worker threads", &sc[21][0], 3);
+       strprompt("Maximum number of worker threads", &sc[22][0], 3);
+       snprintf(sc[43], sizeof sc[43], "%d", (boolprompt(
+               "Automatically delete committed database logs",
+               atoi(&sc[43][0]))));
+
+       strprompt("Server IP address (* for 'any')", &sc[37][0], 15);
+       strprompt("POP3 server port (-1 to disable)", &sc[23][0], 5);
+       strprompt("POP3S server port (-1 to disable)", &sc[40][0], 5);
+       strprompt("IMAP server port (-1 to disable)", &sc[27][0], 5);
+       strprompt("IMAPS server port (-1 to disable)", &sc[39][0], 5);
+       strprompt("SMTP MTA server port (-1 to disable)", &sc[24][0], 5);
+       strprompt("SMTP MSA server port (-1 to disable)", &sc[38][0], 5);
+       strprompt("SMTPS server port (-1 to disable)", &sc[41][0], 5);
+       strprompt("Postfix TCP Dictionary Port server port (-1 to disable)", &sc[50][0], 5);
+       strprompt("ManageSieve server port (-1 to disable)", &sc[51][0], 5);
+
+       strprompt("XMPP (Jabber) client to server port (-1 to disable)", &sc[62][0], 5);
+       /* No prompt because we don't implement this service yet, it's just a placeholder */
+       /* strprompt("XMPP (Jabber) server to server port (-1 to disable)", &sc[63][0], 5); */
+
+       /* This logic flips the question around, because it's one of those
+        * situations where 0=yes and 1=no
+        */
+       a = atoi(sc[25]);
+       a = (a ? 0 : 1);
+       a = boolprompt("Correct forged From: lines during authenticated SMTP",
+               a);
+       a = (a ? 0 : 1);
+       snprintf(sc[25], sizeof sc[25], "%d", a);
+
+       snprintf(sc[66], sizeof sc[66], "%d", (boolprompt(
+               "Flag messages as spam instead of rejecting",
+               atoi(&sc[66][0]))));
+
+       /* This logic flips the question around, because it's one of those
+        * situations where 0=yes and 1=no
+        */
+       a = atoi(sc[61]);
+       a = (a ? 0 : 1);
+       a = boolprompt("Force IMAP posts in public rooms to be from the user who submitted them", a);
+       a = (a ? 0 : 1);
+       snprintf(sc[61], sizeof sc[61], "%d", a);
+
+       snprintf(sc[45], sizeof sc[45], "%d", (boolprompt(
+               "Allow unauthenticated SMTP clients to spoof my domains",
+               atoi(&sc[45][0]))));
+       snprintf(sc[57], sizeof sc[57], "%d", (boolprompt(
+               "Perform RBL checks at greeting instead of after RCPT",
+               atoi(&sc[57][0]))));
+       snprintf(sc[44], sizeof sc[44], "%d", (boolprompt(
+               "Instantly expunge deleted IMAP messages",
+               atoi(&sc[44][0]))));
+
+       /* LDAP settings */
+       if (ipc->ServInfo.supports_ldap) {
+               a = strlen(&sc[32][0]);
+               a = (a ? 1 : 0);        /* Set only to 1 or 0 */
+               a = boolprompt("Do you want to configure LDAP authentication?", a);
+               if (a) {
+                       strprompt("Host name of LDAP server",
+                               &sc[32][0], 127);
+                       strprompt("Port number of LDAP service",
+                               &sc[33][0], 5);
+                       strprompt("Base DN", &sc[34][0], 255);
+                       strprompt("Bind DN (or blank for anonymous bind)", &sc[35][0], 255);
+                       strprompt("Password for bind DN (or blank for anonymous bind)", &sc[36][0], 255);
+               }
+               else {
+                       strcpy(&sc[32][0], "");
+               }
+       }
+
+       /* Expiry settings */
+       strprompt("Default user purge time (days)", &sc[16][0], 5);
+       strprompt("Default room purge time (days)", &sc[17][0], 5);
+
+       /* Angels and demons dancing in my head... */
+       do {
+               snprintf(buf, sizeof buf, "%d", site_expirepolicy->expire_mode);
+               strprompt("System default message expire policy (? for list)",
+                         buf, 1);
+               if (buf[0] == '?') {
+                       scr_printf("\n"
+                               "1. Never automatically expire messages\n"
+                               "2. Expire by message count\n"
+                               "3. Expire by message age\n");
+               }
+       } while ((buf[0] < '1') || (buf[0] > '3'));
+       site_expirepolicy->expire_mode = buf[0] - '0';
+
+       /* ...lunatics and monsters underneath my bed */
+       if (site_expirepolicy->expire_mode == 2) {
+               snprintf(buf, sizeof buf, "%d", site_expirepolicy->expire_value);
+               strprompt("Keep how many messages online?", buf, 10);
+               site_expirepolicy->expire_value = atol(buf);
+       }
+       if (site_expirepolicy->expire_mode == 3) {
+               snprintf(buf, sizeof buf, "%d", site_expirepolicy->expire_value);
+               strprompt("Keep messages for how many days?", buf, 10);
+               site_expirepolicy->expire_value = atol(buf);
+       }
+
+       /* Media messiahs preying on my fears... */
+       do {
+               snprintf(buf, sizeof buf, "%d", mbx_expirepolicy->expire_mode);
+               strprompt("Mailbox default message expire policy (? for list)",
+                         buf, 1);
+               if (buf[0] == '?') {
+                       scr_printf("\n"
+                               "0. Go with the system default\n"
+                               "1. Never automatically expire messages\n"
+                               "2. Expire by message count\n"
+                               "3. Expire by message age\n");
+               }
+       } while ((buf[0] < '0') || (buf[0] > '3'));
+       mbx_expirepolicy->expire_mode = buf[0] - '0';
+
+       /* ...Pop culture prophets playing in my ears */
+       if (mbx_expirepolicy->expire_mode == 2) {
+               snprintf(buf, sizeof buf, "%d", mbx_expirepolicy->expire_value);
+               strprompt("Keep how many messages online?", buf, 10);
+               mbx_expirepolicy->expire_value = atol(buf);
+       }
+       if (mbx_expirepolicy->expire_mode == 3) {
+               snprintf(buf, sizeof buf, "%d", mbx_expirepolicy->expire_value);
+               strprompt("Keep messages for how many days?", buf, 10);
+               mbx_expirepolicy->expire_value = atol(buf);
+       }
+
+       strprompt("How often to run network jobs (in seconds)", &sc[28][0], 5);
+       strprompt("Default frequency to run POP3 collection (in seconds)", &sc[64][0], 5);
+       strprompt("Fastest frequency to run POP3 collection (in seconds)", &sc[65][0], 5);
+       strprompt("Hour to run purges (0-23)", &sc[31][0], 2);
+       snprintf(sc[42], sizeof sc[42], "%d", (boolprompt(
+               "Enable full text search index (warning: resource intensive)",
+               atoi(&sc[42][0]))));
+
+       snprintf(sc[46], sizeof sc[46], "%d", (boolprompt(
+               "Perform journaling of email messages",
+               atoi(&sc[46][0]))));
+       snprintf(sc[47], sizeof sc[47], "%d", (boolprompt(
+               "Perform journaling of non-email messages",
+               atoi(&sc[47][0]))));
+       if ( (atoi(&sc[46][0])) || (atoi(&sc[47][0])) ) {
+               strprompt("Email destination of journalized messages",
+                       &sc[48][0], 127);
+       }
+
+       /* Funambol push stuff */
+       int yes_funambol = 0;
+       if (strlen(sc[53]) > 0) yes_funambol = 1;
+       yes_funambol = boolprompt("Connect to an external Funambol sync server", yes_funambol);
+       if (yes_funambol) {
+               strprompt("Funambol server (blank to disable)", &sc[53][0], 63);
+               strprompt("Funambol server port", &sc[54][0], 5);
+               strprompt("Funambol sync source", &sc[55][0], 63);
+               strprompt("Funambol authentication details (user:pass in Base64)", &sc[56][0],63);
+       }
+       else {
+               sc[53][0] = 0;
+               sc[54][0] = 0;
+               sc[55][0] = 0;
+               sc[56][0] = 0;
+       }
+
+       /* External pager stuff */
+       int yes_pager = 0;
+       if (strlen(sc[60]) > 0) yes_pager = 1;
+       yes_pager = boolprompt("Configure an external pager tool", yes_pager);
+       if (yes_pager) {
+               strprompt("External pager tool", &sc[60][0], 255);
+       }
+       else {
+               sc[60][0] = 0;
+       }
+
+       /* Master user account */
+       int yes_muacct = 0;
+       if (strlen(sc[58]) > 0) yes_muacct = 1;
+       yes_muacct = boolprompt("Enable a 'master user' account", yes_muacct);
+       if (yes_muacct) {
+               strprompt("Master user name", &sc[58][0], 31);
+               strprompt("Master user password", &sc[59][0], -31);
+       }
+       else {
+               strcpy(&sc[58][0], "");
+               strcpy(&sc[59][0], "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx");
+       }
+
+       /* Save it */
+       scr_printf("Save this configuration? ");
+       if (yesno()) {
+               r = 1;
+               for (a = 0; a < NUM_CONFIGS; a++) {
+                       r += 1 + strlen(sc[a]);
+               }
+               resp = (char *)calloc(1, r);
+               if (!resp) {
+                       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> directory      (Consult the Global Address Book)\n");
+       keyopt(" <5> SpamAssassin   (Address of SpamAssassin server)\n");
+       keyopt(" <6> RBL            (domain suffix of spam hunting RBL)\n");
+       keyopt(" <7> masq domains   (Domains as which users are allowed to masquerade)\n");
+       keyopt(" <8> 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, "directory");
+                       return;
+               case 5: strcpy(buf, "spamassassin");
+                       return;
+               case 6: strcpy(buf, "rbl");
+                       return;
+               case 7: strcpy(buf, "masqdomain");
+                       return;
+               case 8: 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');
+                       ++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);
+}
+
+
+/*
+ * IGnet node configuration
+ */
+void do_ignet_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;
+       int r;
+
+       r = CtdlIPCGetSystemConfigByType(ipc, IGNETCFG, &listing, buf);
+       if (r / 100 == 1) while (*listing && !IsEmptyStr(listing)) {
+               extract_token(buf, listing, 0, '\n', sizeof buf);
+               remove_token(listing, 0, '\n');
+
+               ++num_recs;
+               if (num_recs == 1) recs = malloc(sizeof(char *));
+               else recs = realloc(recs, (sizeof(char *)) * num_recs);
+               recs[num_recs-1] = malloc(SIZ);
+               strcpy(recs[num_recs-1], buf);
+       }
+       if (listing) free(listing);
+
+       do {
+               scr_printf("\n");
+               color(BRIGHT_WHITE);
+               scr_printf(     "### "
+                       "   Node          "
+                       "  Secret           "
+                       "          Host or IP             "
+                       "Port#\n");
+               color(DIM_WHITE);
+               scr_printf(     "--- "
+                       "---------------- "
+                       "------------------ "
+                       "-------------------------------- "
+                       "-----\n");
+               for (i=0; i<num_recs; ++i) {
+               color(DIM_WHITE);
+               scr_printf("%3d ", i+1);
+               extract_token(buf, recs[i], 0, '|', sizeof buf);
+               color(BRIGHT_CYAN);
+               scr_printf("%-16s ", buf);
+               extract_token(buf, recs[i], 1, '|', sizeof buf);
+               color(BRIGHT_MAGENTA);
+               scr_printf("%-18s ", buf);
+               extract_token(buf, recs[i], 2, '|', sizeof buf);
+               color(BRIGHT_CYAN);
+               scr_printf("%-32s ", buf);
+               extract_token(buf, recs[i], 3, '|', sizeof buf);
+               color(BRIGHT_MAGENTA);
+               scr_printf("%-3s\n", buf);
+               color(DIM_WHITE);
+               }
+               scr_printf("\n");
+
+               ch = keymenu("", "<A>dd|<D>elete|<S>ave|<Q>uit");
+               switch(ch) {
+                       case 'a':
+                               ++num_recs;
+                               if (num_recs == 1)
+                                       recs = malloc(sizeof(char *));
+                               else recs = realloc(recs,
+                                       (sizeof(char *)) * num_recs);
+                               newprompt("Enter node name    : ", buf, 16);
+                               strcat(buf, "|");
+                               newprompt("Enter shared secret: ",
+                                       &buf[strlen(buf)], 18);
+                               strcat(buf, "|");
+                               newprompt("Enter host or IP   : ",
+                                       &buf[strlen(buf)], 32);
+                               strcat(buf, "|504");
+                               strprompt("Enter port number  : ",
+                                       &buf[strlen(buf)-3], 5);
+                               recs[num_recs-1] = strdup(buf);
+                               modified = 1;
+                               break;
+                       case 'd':
+                               i = intprompt("Delete which one",
+                                       1, 1, num_recs) - 1;
+                               free(recs[i]);
+                               --num_recs;
+                               for (j=i; j<num_recs; ++j)
+                                       recs[j] = recs[j+1];
+                               modified = 1;
+                               break;
+                       case 's':
+                               r = 1;
+                               for (i = 0; i < num_recs; ++i)
+                                       r += 1 + strlen(recs[i]);
+                               listing = (char*) calloc(1, r);
+                               if (!listing) {
+                                       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");
+                               }
+                               r = CtdlIPCSetSystemConfigByType(ipc, IGNETCFG, listing, buf);
+                               if (r / 100 != 4) {
+                                       scr_printf("%s\n", buf);
+                               } else {
+                                       scr_printf("Wrote %d records.\n", num_recs);
+                                       modified = 0;
+                               }
+                free(listing);
+                               break;
+                       case 'q':
+                               quitting = !modified || boolprompt(
+                                       "Quit without saving", 0);
+                               break;
+                       default:
+                               break;
+               }
+       } while (!quitting);
+
+       if (recs != NULL) {
+               for (i=0; i<num_recs; ++i) free(recs[i]);
+               free(recs);
+       }
+}
+
+
+/*
+ * Filter list configuration
+ */
+void do_filterlist_configuration(CtdlIPC *ipc)
+{
+       char buf[SIZ];
+       int num_recs = 0;
+       char **recs = NULL;
+       char ch;
+       int i, j;
+       int quitting = 0;
+       int modified = 0;
+       char *listing = NULL;
+       int r;
+
+       r = CtdlIPCGetSystemConfigByType(ipc, FILTERLIST, &listing, buf);
+       if (r / 100 == 1) while (*listing && !IsEmptyStr(listing)) {
+               extract_token(buf, listing, 0, '\n', sizeof buf);
+               remove_token(listing, 0, '\n');
+
+               ++num_recs;
+               if (num_recs == 1) recs = malloc(sizeof(char *));
+               else recs = realloc(recs, (sizeof(char *)) * num_recs);
+               recs[num_recs-1] = malloc(SIZ);
+               strcpy(recs[num_recs-1], buf);
+       }
+       if (listing) free(listing);
+
+       do {
+               scr_printf("\n");
+               color(BRIGHT_WHITE);
+               scr_printf(     "### "
+                       "         User name           "
+                       "         Room name           "
+                       "    Node name    "
+                       "\n");
+               color(DIM_WHITE);
+               scr_printf(     "--- "
+                       "---------------------------- "
+                       "---------------------------- "
+                       "---------------- "
+                       "\n");
+               for (i=0; i<num_recs; ++i) {
+               color(DIM_WHITE);
+               scr_printf("%3d ", i+1);
+               extract_token(buf, recs[i], 0, '|', sizeof buf);
+               color(BRIGHT_CYAN);
+               scr_printf("%-28s ", buf);
+               extract_token(buf, recs[i], 1, '|', sizeof buf);
+               color(BRIGHT_MAGENTA);
+               scr_printf("%-28s ", buf);
+               extract_token(buf, recs[i], 2, '|', sizeof buf);
+               color(BRIGHT_CYAN);
+               scr_printf("%-16s\n", buf);
+               extract_token(buf, recs[i], 3, '|', sizeof buf);
+               color(DIM_WHITE);
+               }
+
+               ch = keymenu("", "<A>dd|<D>elete|<S>ave|<Q>uit");
+               switch(ch) {
+                       case 'a':
+                               ++num_recs;
+                               if (num_recs == 1)
+                                       recs = malloc(sizeof(char *));
+                               else recs = realloc(recs,
+                                       (sizeof(char *)) * num_recs);
+                               newprompt("Enter user name: ", buf, 28);
+                               strcat(buf, "|");
+                               newprompt("Enter room name: ",
+                                       &buf[strlen(buf)], 28);
+                               strcat(buf, "|");
+                               newprompt("Enter node name: ",
+                                       &buf[strlen(buf)], 16);
+                               strcat(buf, "|");
+                               recs[num_recs-1] = strdup(buf);
+                               modified = 1;
+                               break;
+                       case 'd':
+                               i = intprompt("Delete which one",
+                                       1, 1, num_recs) - 1;
+                               free(recs[i]);
+                               --num_recs;
+                               for (j=i; j<num_recs; ++j)
+                                       recs[j] = recs[j+1];
+                               modified = 1;
+                               break;
+                       case 's':
+                               r = 1;
+                               for (i = 0; i < num_recs; ++i)
+                                       r += 1 + strlen(recs[i]);
+                               listing = (char*) calloc(1, r);
+                               if (!listing) {
+                                       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");
+                               }
+                               r = CtdlIPCSetSystemConfigByType(ipc, FILTERLIST, listing, buf);
+                               if (r / 100 != 4) {
+                                       scr_printf("%s\n", buf);
+                               } else {
+                                       scr_printf("Wrote %d records.\n", num_recs);
+                                       modified = 0;
+                               }
+                free(listing);
+                               break;
+                       case 'q':
+                               quitting = !modified || boolprompt(
+                                       "Quit without saving", 0);
+                               break;
+                       default:
+                               break;
+               }
+       } while (!quitting);
+
+       if (recs != NULL) {
+               for (i=0; i<num_recs; ++i) free(recs[i]);
+               free(recs);
+       }
+}
+
+
+
+
+/*
+ * POP3 aggregation client configuration
+ */
+void do_pop3client_configuration(CtdlIPC *ipc)
+{
+       char buf[SIZ];
+       int num_recs = 0;
+       char **recs = NULL;
+       char ch;
+       int i, j;
+       int quitting = 0;
+       int modified = 0;
+       char *listing = NULL;
+       char *other_listing = NULL;
+       int r;
+       char instr[SIZ];
+
+       r = CtdlIPCGetRoomNetworkConfig(ipc, &listing, buf);
+       if (r / 100 == 1) {
+               while(listing && !IsEmptyStr(listing)) {
+                       extract_token(buf, listing, 0, '\n', sizeof buf);
+                       remove_token(listing, 0, '\n');
+                       extract_token(instr, buf, 0, '|', sizeof instr);
+                       if (!strcasecmp(instr, "pop3client")) {
+
+                               ++num_recs;
+                               if (num_recs == 1) recs = malloc(sizeof(char *));
+                               else recs = realloc(recs, (sizeof(char *)) * num_recs);
+                               recs[num_recs-1] = malloc(SIZ);
+                               strcpy(recs[num_recs-1], buf);
+
+                       }
+               }
+       }
+       if (listing) {
+               free(listing);
+               listing = NULL;
+       }
+
+       do {
+               scr_printf("\n");
+               color(BRIGHT_WHITE);
+               scr_printf(     "### "
+                       "      Remote POP3 host       "
+                       "         User name           "
+                       "Keep on server? "
+                       "Fetching inteval"
+                       "\n");
+               color(DIM_WHITE);
+               scr_printf(     "--- "
+                       "---------------------------- "
+                       "---------------------------- "
+                       "--------------- "
+                       "---------------- "
+                       "\n");
+               for (i=0; i<num_recs; ++i) {
+               color(DIM_WHITE);
+               scr_printf("%3d ", i+1);
+
+               extract_token(buf, recs[i], 1, '|', sizeof buf);
+               color(BRIGHT_CYAN);
+               scr_printf("%-28s ", buf);
+
+               extract_token(buf, recs[i], 2, '|', sizeof buf);
+               color(BRIGHT_MAGENTA);
+               scr_printf("%-28s ", buf);
+
+               color(BRIGHT_CYAN);
+               scr_printf("%-15s ", (extract_int(recs[i], 4) ? "Yes" : "No") );
+               color(BRIGHT_MAGENTA);
+               scr_printf("%ld\n", extract_long(recs[i], 5) );
+               color(DIM_WHITE);
+               }
+
+               ch = keymenu("", "<A>dd|<D>elete|<S>ave|<Q>uit");
+               switch(ch) {
+                       case 'a':
+                               ++num_recs;
+                               if (num_recs == 1) {
+                                       recs = malloc(sizeof(char *));
+                               }
+                               else {
+                                       recs = realloc(recs, (sizeof(char *)) * num_recs);
+                               }
+                               strcpy(buf, "pop3client|");
+                               newprompt("Enter host name: ", &buf[strlen(buf)], 28);
+                               strcat(buf, "|");
+                               newprompt("Enter user name: ", &buf[strlen(buf)], 28);
+                               strcat(buf, "|");
+                               newprompt("Enter password : ", &buf[strlen(buf)], 16);
+                               strcat(buf, "|");
+                               scr_printf("Keep messages on server instead of deleting them? ");
+                               sprintf(&buf[strlen(buf)], "%d", yesno());
+                               strcat(buf, "|");
+                               newprompt("Enter interval : ", &buf[strlen(buf)], 5);
+                               strcat(buf, "|");
+                               recs[num_recs-1] = strdup(buf);
+                               modified = 1;
+                               break;
+                       case 'd':
+                               i = intprompt("Delete which one",
+                                       1, 1, num_recs) - 1;
+                               free(recs[i]);
+                               --num_recs;
+                               for (j=i; j<num_recs; ++j)
+                                       recs[j] = recs[j+1];
+                               modified = 1;
+                               break;
+                       case 's':
+                               r = 1;
+                               for (i = 0; i < num_recs; ++i) {
+                                       r += 1 + strlen(recs[i]);
+                               }
+                               listing = (char*) calloc(1, r);
+                               if (!listing) {
+                                       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);
+       }
+}
+
+
index 9d20819dc05af3cafb479b74b967b61684ff5f03..157c953976e4fc7ee05d2a092be5b6a9a467974e 100644 (file)
@@ -271,7 +271,7 @@ int Conditional_DAV_NSCURRENT(StrBuf *Target, WCTemplputParams *TP)
        wcsession *WCC = WC;
        void *vHandler;
 
-       vHandler = CTX;
+       vHandler = CTX(CTX_NONE);
        return WCC->Hdr->HR.Handler == vHandler;
 }
 
@@ -282,8 +282,9 @@ void tmplput_DAV_NAMESPACE(StrBuf *Target, WCTemplputParams *TP)
 
        if (TP->Filter.ContextType == CTX_DAVNS) {
                WebcitHandler *H;
-               H = (WebcitHandler*) CTX;
-               StrBufAppendTemplate(Target, TP, H->Name, 0);
+               H = (WebcitHandler*) CTX(CTX_DAVNS);
+               if (H != NULL)
+                       StrBufAppendTemplate(Target, TP, H->Name, 0);
        }
        else if (WCC->Hdr->HR.Handler != NULL) {
                StrBufAppendTemplate(Target, TP, WCC->Hdr->HR.Handler->Name, 0);
index 60783c5685cad880b94c1f877dc821c473891218..f7f9c9dc52d390bea015b6fdee455b44ff3267c9 100644 (file)
@@ -38,22 +38,22 @@ void FreeFiles(void *vFile)
 /* -------------------------------------------------------------------------------- */
 void tmplput_FILE_NAME(StrBuf *Target, WCTemplputParams *TP)
 {
-       FileListStruct *F = (FileListStruct*) CTX;
+       FileListStruct *F = (FileListStruct*) CTX(CTX_FILELIST);
        StrBufAppendTemplate(Target, TP, F->Filename, 0);
 }
 void tmplput_FILE_SIZE(StrBuf *Target, WCTemplputParams *TP)
 {
-       FileListStruct *F = (FileListStruct*) CTX;
+       FileListStruct *F = (FileListStruct*) CTX(CTX_FILELIST);
        StrBufAppendPrintf(Target, "%ld", F->FileSize);
 }
 void tmplput_FILEMIMETYPE(StrBuf *Target, WCTemplputParams *TP)
 {
-       FileListStruct *F = (FileListStruct*) CTX;
+       FileListStruct *F = (FileListStruct*) CTX(CTX_FILELIST);
        StrBufAppendTemplate(Target, TP, F->MimeType, 0);
 }
 void tmplput_FILE_COMMENT(StrBuf *Target, WCTemplputParams *TP)
 {
-       FileListStruct *F = (FileListStruct*) CTX;
+       FileListStruct *F = (FileListStruct*) CTX(CTX_FILELIST);
        StrBufAppendTemplate(Target, TP, F->Comment, 0);
 }
 
@@ -61,7 +61,7 @@ void tmplput_FILE_COMMENT(StrBuf *Target, WCTemplputParams *TP)
 
 int Conditional_FILE_ISPIC(StrBuf *Target, WCTemplputParams *TP)
 {
-       FileListStruct *F = (FileListStruct*) CTX;
+       FileListStruct *F = (FileListStruct*) CTX(CTX_FILELIST);
        return F->IsPic;
 }
 
index cbf9dc38f44007c7658d737af306bbbed9c966a7..31aec39861201d1ab941a9dc5b0ebc399d66a754 100644 (file)
@@ -41,7 +41,6 @@ void do_generic(void)
                return;
        }
 
-        memset(&SubTP, 0, sizeof(WCTemplputParams));
        Buf = NewStrBuf();
        serv_puts(bstr("g_cmd"));
        StrBuf_ServGetln(Buf);
@@ -91,11 +90,11 @@ void do_generic(void)
        begin_burst();
        output_headers(1, 0, 0, 0, 1, 0);
 
-        SubTP.Filter.ContextType = CTX_STRBUF;
-        SubTP.Context = Buf;
-
-        DoTemplate(HKEY("aide_display_generic_result"), NULL, &SubTP);
-
+       StackContext(NULL, &SubTP, Buf, CTX_STRBUF, 0, NULL);
+       {
+               DoTemplate(HKEY("aide_display_generic_result"), NULL, &SubTP);
+       }
+       UnStackContext(&SubTP);
         wDumpContent(1);
 
        FreeStrBuf(&Buf);
index 2ce2ff6c8afe15cd3c600fca3450dec7c969e3b6..88404111839f02762a9c6c85536aac0eb097d604 100644 (file)
@@ -238,10 +238,11 @@ int read_message(StrBuf *Target, const char *tmpl, long tmpllen, long msgnum, co
                evaluate_mime_part(Msg, Mime);
        }
        DeleteHashPos(&it);
-       memset(&SubTP, 0, sizeof(WCTemplputParams));
-       SubTP.Filter.ContextType = CTX_MAILSUM;
-       SubTP.Context = Msg;
-       *OutMime = DoTemplate(tmpl, tmpllen, Target, &SubTP);
+       StackContext(NULL, &SubTP, Msg, CTX_MAILSUM, 0, NULL);
+       {
+               *OutMime = DoTemplate(tmpl, tmpllen, Target, &SubTP);
+       }
+       UnStackContext(&SubTP);
 
        DestroyMessageSummary(Msg);
        FreeStrBuf(&FoundCharset);
@@ -786,11 +787,14 @@ void readloop(long oper, eCustomRoomRenderer ForceRenderer)
 
        if (Stat.sortit) {
                CompareFunc SortIt;
-               memset(&SubTP, 0, sizeof(WCTemplputParams));
-               SubTP.Filter.ContextType = CTX_MAILSUM;
-               SubTP.Context = NULL;
-               SortIt =  RetrieveSort(&SubTP, NULL, 0,
-                                      HKEY("date"), Stat.defaultsortorder);
+               StackContext(NULL, &SubTP, NULL, CTX_MAILSUM, 0, NULL);
+               {
+                       SortIt =  RetrieveSort(&SubTP,
+                                              NULL, 0,
+                                              HKEY("date"),
+                                              Stat.defaultsortorder);
+               }
+               UnStackContext(&SubTP);
                if (SortIt != NULL)
                        SortByPayload(WCC->summ, SortIt);
        }
@@ -1314,14 +1318,28 @@ long l_msgn;
 long l_from;
 long l_rcpt;
 long l_cccc;
+long l_replyto;
 long l_node;
 long l_rfca;
 
+const char *ReplyToModeStrings [3] = {
+       "reply",
+       "replyalle",
+       "forward"
+};
+typedef enum _eReplyToNodes {
+       eReply,
+       eReplyAll,
+       eForward
+}eReplyToNodes;
+       
 /*
  * display the message entry screen
  */
 void display_enter(void)
 {
+       const char *ReplyingModeStr;
+       eReplyToNodes ReplyMode = eReply;
        StrBuf *Line;
        long Result;
        int rc;
@@ -1398,6 +1416,15 @@ void display_enter(void)
        }
 
 
+       ReplyingModeStr = bstr("replying_mode");
+       if (ReplyingModeStr != NULL) for (i = 0; i < 3; i++) {
+                       if (strcmp(ReplyingModeStr, ReplyToModeStrings[i]) == 0) {
+                               ReplyMode = (eReplyToNodes) i;
+                               break;
+                       }
+               }
+               
+
        /*
         * If the "replying_to" variable is set, it refers to a message
         * number from which we must extract some header fields...
@@ -1412,6 +1439,7 @@ void display_enter(void)
                StrBuf *rfca = NULL;
                StrBuf *rcpt = NULL;
                StrBuf *cccc = NULL;
+               StrBuf *replyto = NULL;
                serv_printf("MSG0 %ld|1", replying_to); 
 
                StrBuf_ServGetln(Line);
@@ -1431,7 +1459,7 @@ void display_enter(void)
                                        StrBuf *subj = NewStrBuf();
                                        StrBuf *FlatSubject;
 
-                                       if (!strcasecmp(bstr("replying_mode"), "forward")) {
+                                       if (ReplyMode == eForward) {
                                                if (strncasecmp(ChrPtr(Line) + 5, "Fw:", 3)) {
                                                        StrBufAppendBufPlain(subj, HKEY("Fw: "), 0);
                                                }
@@ -1495,7 +1523,9 @@ void display_enter(void)
                                else if (which == l_node) {
                                        node = NewStrBufPlain(ChrPtr(Line) + 5, StrLength(Line) - 5);
                                }
-                               
+                               else if (which == l_replyto) {
+                                       replyto = NewStrBufPlain(ChrPtr(Line) + 5, StrLength(Line) - 5);
+                               }                               
                                else if (which == l_rfca) {
                                        StrBuf *FlatRFCA;
                                        rfca = NewStrBufPlain(ChrPtr(Line) + 5, StrLength(Line) - 5);
@@ -1526,11 +1556,14 @@ void display_enter(void)
                /*
                 * If this is a Reply or a ReplyAll, copy the sender's email into the To: field
                 */
-               if (    (!strcasecmp(bstr("replying_mode"), "reply"))
-                       || (!strcasecmp(bstr("replying_mode"), "replyall"))
-               ) {
+               if ((ReplyMode == eReply) || (ReplyMode == eReplyAll))
+               {
                        StrBuf *to_rcpt;
-                       if (StrLength(rfca) > 0) {
+                       if ((StrLength(replyto) > 0) && (ReplyMode == eReplyAll)) {
+                               to_rcpt = NewStrBuf();
+                               StrBufAppendBuf(to_rcpt, replyto, 0);
+                       }
+                       else if (StrLength(rfca) > 0) {
                                to_rcpt = NewStrBuf();
                                StrBufAppendBuf(to_rcpt, from, 0);
                                StrBufAppendBufPlain(to_rcpt, HKEY(" <"), 0);
@@ -1553,11 +1586,12 @@ void display_enter(void)
                /*
                 * Only if this is a ReplyAll, copy all recipients into the Cc: field
                 */
-               if (    (!strcasecmp(bstr("replying_mode"), "replyall"))
-               {
+               if (ReplyMode == eReplyAll)
+               {
                        StrBuf *cc_rcpt = rcpt;
                        rcpt = NULL;
-                       if (StrLength(cccc) > 0) {
+                       if ((StrLength(cccc) > 0) && (StrLength(replyto) == 0))
+                       {
                                if (cc_rcpt != NULL)  {
                                        StrBufAppendPrintf(cc_rcpt, ", ");
                                        StrBufAppendBuf(cc_rcpt, cccc, 0);
@@ -2020,6 +2054,7 @@ InitModule_MSG
        l_from = FourHash("from", 4);
        l_rcpt = FourHash("rcpt", 4);
        l_cccc = FourHash("cccc", 4);
+       l_replyto = FourHash("rep2", 4);
        l_node = FourHash("node", 4);
        l_rfca = FourHash("rfca", 4);
 
index a063170e046c0d96ae688b75555f1e36bed785a8..21ae4746782909142e936b19f54e4e4f5a7548a0 100644 (file)
@@ -40,6 +40,7 @@ typedef struct _message_summary {
        StrBuf *subj;           /* the title / subject */
        StrBuf *reply_inreplyto;
        StrBuf *reply_references;
+       StrBuf *ReplyTo;
        StrBuf *cccc;
        StrBuf *hnod;
        StrBuf *AllRcpt;
index d2de1f4bdf89313408ce64e2eff38ed9169b9876..3e5c29fcef4d1c93ee0e575c381161bbe0c10cba 100644 (file)
@@ -45,6 +45,7 @@ void DestroyMessageSummary(void *vMsg)
        FreeStrBuf(&Msg->reply_inreplyto);
        FreeStrBuf(&Msg->reply_references);
        FreeStrBuf(&Msg->cccc);
+       FreeStrBuf(&Msg->ReplyTo);
        FreeStrBuf(&Msg->hnod);
        FreeStrBuf(&Msg->AllRcpt);
        FreeStrBuf(&Msg->Room);
@@ -247,7 +248,7 @@ void examine_nhdr(message_summary *Msg, StrBuf *HdrLine, StrBuf *FoundCharset)
 }
 int Conditional_ANONYMOUS_MESSAGE(StrBuf *Target, WCTemplputParams *TP)
 {
-       message_summary *Msg = (message_summary*) CTX;
+       message_summary *Msg = (message_summary*) CTX(CTX_MAILSUM);
        return Msg->nhdr != 0;
 }
 
@@ -273,7 +274,7 @@ void examine_from(message_summary *Msg, StrBuf *HdrLine, StrBuf *FoundCharset)
 }
 void tmplput_MAIL_SUMM_FROM(StrBuf *Target, WCTemplputParams *TP)
 {
-       message_summary *Msg = (message_summary*) CTX;
+       message_summary *Msg = (message_summary*) CTX(CTX_MAILSUM);
        StrBufAppendTemplate(Target, TP, Msg->from, 0);
 }
 
@@ -293,7 +294,7 @@ void examine_subj(message_summary *Msg, StrBuf *HdrLine, StrBuf *FoundCharset)
 }
 void tmplput_MAIL_SUMM_SUBJECT(StrBuf *Target, WCTemplputParams *TP)
 {
-       message_summary *Msg = (message_summary*) CTX;
+       message_summary *Msg = (message_summary*) CTX(CTX_MAILSUM);
 
        if (TP->Tokens->nParameters == 4)
        {
@@ -312,7 +313,7 @@ void tmplput_MAIL_SUMM_SUBJECT(StrBuf *Target, WCTemplputParams *TP)
 }
 int Conditional_MAIL_SUMM_SUBJECT(StrBuf *Target, WCTemplputParams *TP)
 {
-       message_summary *Msg = (message_summary*) CTX;
+       message_summary *Msg = (message_summary*) CTX(CTX_MAILSUM);
 
 
        return StrLength(Msg->subj) > 0;
@@ -335,13 +336,13 @@ void examine_msgn(message_summary *Msg, StrBuf *HdrLine, StrBuf *FoundCharset)
 }
 void tmplput_MAIL_SUMM_INREPLYTO(StrBuf *Target, WCTemplputParams *TP)
 {
-       message_summary *Msg = (message_summary*) CTX;
+       message_summary *Msg = (message_summary*) CTX(CTX_MAILSUM);
        StrBufAppendTemplate(Target, TP, Msg->reply_inreplyto, 0);
 }
 
 int Conditional_MAIL_SUMM_UNREAD(StrBuf *Target, WCTemplputParams *TP)
 {
-       message_summary *Msg = (message_summary*) CTX;
+       message_summary *Msg = (message_summary*) CTX(CTX_MAILSUM);
        return (Msg->Flags & MSGFLAG_READ) != 0;
 }
 
@@ -361,10 +362,35 @@ void examine_wefw(message_summary *Msg, StrBuf *HdrLine, StrBuf *FoundCharset)
 }
 void tmplput_MAIL_SUMM_REFIDS(StrBuf *Target, WCTemplputParams *TP)
 {
-       message_summary *Msg = (message_summary*) CTX;
+       message_summary *Msg = (message_summary*) CTX(CTX_MAILSUM);
        StrBufAppendTemplate(Target, TP, Msg->reply_references, 0);
 }
 
+void examine_replyto(message_summary *Msg, StrBuf *HdrLine, StrBuf *FoundCharset)
+{
+       wcsession *WCC = WC;
+
+       CheckConvertBufs(WCC);
+       FreeStrBuf(&Msg->ReplyTo);
+       Msg->ReplyTo = NewStrBufPlain(NULL, StrLength(HdrLine));
+       StrBuf_RFC822_2_Utf8(Msg->ReplyTo, 
+                            HdrLine, 
+                            WCC->DefaultCharset, 
+                            FoundCharset,
+                            WCC->ConvertBuf1,
+                            WCC->ConvertBuf2);
+       if (Msg->AllRcpt == NULL)
+               Msg->AllRcpt = NewStrBufPlain(NULL, StrLength(HdrLine));
+       if (StrLength(Msg->AllRcpt) > 0) {
+               StrBufAppendBufPlain(Msg->AllRcpt, HKEY(", "), 0);
+       }
+       StrBufAppendBuf(Msg->AllRcpt, Msg->ReplyTo, 0);
+}
+void tmplput_MAIL_SUMM_REPLYTO(StrBuf *Target, WCTemplputParams *TP)
+{
+       message_summary *Msg = (message_summary*) CTX(CTX_MAILSUM);
+       StrBufAppendTemplate(Target, TP, Msg->ReplyTo, 0);
+}
 
 void examine_cccc(message_summary *Msg, StrBuf *HdrLine, StrBuf *FoundCharset)
 {
@@ -388,7 +414,7 @@ void examine_cccc(message_summary *Msg, StrBuf *HdrLine, StrBuf *FoundCharset)
 }
 void tmplput_MAIL_SUMM_CCCC(StrBuf *Target, WCTemplputParams *TP)
 {
-       message_summary *Msg = (message_summary*) CTX;
+       message_summary *Msg = (message_summary*) CTX(CTX_MAILSUM);
        StrBufAppendTemplate(Target, TP, Msg->cccc, 0);
 }
 
@@ -403,7 +429,7 @@ void examine_room(message_summary *Msg, StrBuf *HdrLine, StrBuf *FoundCharset)
 }
 void tmplput_MAIL_SUMM_ORGROOM(StrBuf *Target, WCTemplputParams *TP)
 {
-       message_summary *Msg = (message_summary*) CTX;
+       message_summary *Msg = (message_summary*) CTX(CTX_MAILSUM);
        StrBufAppendTemplate(Target, TP, Msg->Room, 0);
 }
 
@@ -415,19 +441,24 @@ void examine_rfca(message_summary *Msg, StrBuf *HdrLine, StrBuf *FoundCharset)
 }
 void tmplput_MAIL_SUMM_RFCA(StrBuf *Target, WCTemplputParams *TP)
 {
-       message_summary *Msg = (message_summary*) CTX;
+       message_summary *Msg = (message_summary*) CTX(CTX_MAILSUM);
        StrBufAppendTemplate(Target, TP, Msg->Rfca, 0);
 }
 int Conditional_MAIL_SUMM_RFCA(StrBuf *Target, WCTemplputParams *TP)
 {
-       message_summary *Msg = (message_summary*) CTX;
+       message_summary *Msg = (message_summary*) CTX(CTX_MAILSUM);
        return StrLength(Msg->Rfca) > 0;
 }
 int Conditional_MAIL_SUMM_CCCC(StrBuf *Target, WCTemplputParams *TP)
 {
-       message_summary *Msg = (message_summary*) CTX;
+       message_summary *Msg = (message_summary*) CTX(CTX_MAILSUM);
        return StrLength(Msg->cccc) > 0;
 }
+int Conditional_MAIL_SUMM_REPLYTO(StrBuf *Target, WCTemplputParams *TP)
+{
+       message_summary *Msg = (message_summary*) CTX(CTX_MAILSUM);
+       return StrLength(Msg->ReplyTo) > 0;
+}
 
 void examine_node(message_summary *Msg, StrBuf *HdrLine, StrBuf *FoundCharset)
 {
@@ -443,12 +474,12 @@ void examine_node(message_summary *Msg, StrBuf *HdrLine, StrBuf *FoundCharset)
 }
 void tmplput_MAIL_SUMM_OTHERNODE(StrBuf *Target, WCTemplputParams *TP)
 {
-       message_summary *Msg = (message_summary*) CTX;
+       message_summary *Msg = (message_summary*) CTX(CTX_MAILSUM);
        StrBufAppendTemplate(Target, TP, Msg->OtherNode, 0);
 }
 int Conditional_MAIL_SUMM_OTHERNODE(StrBuf *Target, WCTemplputParams *TP)
 {
-       message_summary *Msg = (message_summary*) CTX;
+       message_summary *Msg = (message_summary*) CTX(CTX_MAILSUM);
        return StrLength(Msg->OtherNode) > 0;
 }
 
@@ -475,22 +506,22 @@ void examine_rcpt(message_summary *Msg, StrBuf *HdrLine, StrBuf *FoundCharset)
 }
 void tmplput_MAIL_SUMM_TO(StrBuf *Target, WCTemplputParams *TP)
 {
-       message_summary *Msg = (message_summary*) CTX;
+       message_summary *Msg = (message_summary*) CTX(CTX_MAILSUM);
        StrBufAppendTemplate(Target, TP, Msg->to, 0);
 }
 int Conditional_MAIL_SUMM_TO(StrBuf *Target, WCTemplputParams *TP) 
 {
-       message_summary *Msg = (message_summary*) CTX;
+       message_summary *Msg = (message_summary*) CTX(CTX_MAILSUM);
        return StrLength(Msg->to) != 0;
 }
 int Conditional_MAIL_SUMM_SUBJ(StrBuf *Target, WCTemplputParams *TP) 
 {
-       message_summary *Msg = (message_summary*) CTX;
+       message_summary *Msg = (message_summary*) CTX(CTX_MAILSUM);
        return StrLength(Msg->subj) != 0;
 }
 void tmplput_MAIL_SUMM_ALLRCPT(StrBuf *Target, WCTemplputParams *TP)
 {
-       message_summary *Msg = (message_summary*) CTX;
+       message_summary *Msg = (message_summary*) CTX(CTX_MAILSUM);
        StrBufAppendTemplate(Target, TP, Msg->AllRcpt, 0);
 }
 
@@ -513,27 +544,27 @@ void examine_time(message_summary *Msg, StrBuf *HdrLine, StrBuf *FoundCharset)
 void tmplput_MAIL_SUMM_DATE_BRIEF(StrBuf *Target, WCTemplputParams *TP)
 {
        char datebuf[64];
-       message_summary *Msg = (message_summary*) CTX;
+       message_summary *Msg = (message_summary*) CTX(CTX_MAILSUM);
        webcit_fmt_date(datebuf, 64, Msg->date, DATEFMT_BRIEF);
        StrBufAppendBufPlain(Target, datebuf, -1, 0);
 }
 
 void tmplput_MAIL_SUMM_EUID(StrBuf *Target, WCTemplputParams *TP)
 {
-       message_summary *Msg = (message_summary*) CTX;
+       message_summary *Msg = (message_summary*) CTX(CTX_MAILSUM);
        StrBufAppendTemplate(Target, TP, Msg->euid, 0);
 }
 
 void tmplput_MAIL_SUMM_DATE_FULL(StrBuf *Target, WCTemplputParams *TP)
 {
        char datebuf[64];
-       message_summary *Msg = (message_summary*) CTX;
+       message_summary *Msg = (message_summary*) CTX(CTX_MAILSUM);
        webcit_fmt_date(datebuf, 64, Msg->date, DATEFMT_FULL);
        StrBufAppendBufPlain(Target, datebuf, -1, 0);
 }
 void tmplput_MAIL_SUMM_DATE_NO(StrBuf *Target, WCTemplputParams *TP)
 {
-       message_summary *Msg = (message_summary*) CTX;
+       message_summary *Msg = (message_summary*) CTX(CTX_MAILSUM);
        StrBufAppendPrintf(Target, "%ld", Msg->date, 0);
 }
 
@@ -755,7 +786,7 @@ void evaluate_mime_part(message_summary *Msg, wc_mime_attachment *Mime)
 
 void tmplput_MAIL_SUMM_NATTACH(StrBuf *Target, WCTemplputParams *TP)
 {
-       message_summary *Msg = (message_summary*) CTX;
+       message_summary *Msg = (message_summary*) CTX(CTX_MAILSUM);
        StrBufAppendPrintf(Target, "%ld", GetCount(Msg->Attachments));
 }
 
@@ -776,12 +807,12 @@ void examine_hnod(message_summary *Msg, StrBuf *HdrLine, StrBuf *FoundCharset)
 }
 void tmplput_MAIL_SUMM_H_NODE(StrBuf *Target, WCTemplputParams *TP)
 {
-       message_summary *Msg = (message_summary*) CTX;
+       message_summary *Msg = (message_summary*) CTX(CTX_MAILSUM);
        StrBufAppendTemplate(Target, TP, Msg->hnod, 0);
 }
 int Conditional_MAIL_SUMM_H_NODE(StrBuf *Target, WCTemplputParams *TP)
 {
-       message_summary *Msg = (message_summary*) CTX;
+       message_summary *Msg = (message_summary*) CTX(CTX_MAILSUM);
        return StrLength(Msg->hnod) > 0;
 }
 
@@ -860,14 +891,14 @@ void examine_content_type(message_summary *Msg, StrBuf *HdrLine, StrBuf *FoundCh
 
 void tmplput_MAIL_SUMM_N(StrBuf *Target, WCTemplputParams *TP)
 {
-       message_summary *Msg = (message_summary*) CTX;
+       message_summary *Msg = (message_summary*) CTX(CTX_MAILSUM);
        StrBufAppendPrintf(Target, "%ld", Msg->msgnum);
 }
 
 
 void tmplput_MAIL_SUMM_PERMALINK(StrBuf *Target, WCTemplputParams *TP)
 {
-       message_summary *Msg = (message_summary*) CTX;
+       message_summary *Msg = (message_summary*) CTX(CTX_MAILSUM);
        char perma_link[1024];
 
        strcpy(perma_link, "/readfwd?go=");
@@ -879,25 +910,25 @@ void tmplput_MAIL_SUMM_PERMALINK(StrBuf *Target, WCTemplputParams *TP)
 
 int Conditional_MAIL_MIME_ALL(StrBuf *Target, WCTemplputParams *TP)
 {
-       message_summary *Msg = (message_summary*) CTX;
+       message_summary *Msg = (message_summary*) CTX(CTX_MAILSUM);
        return GetCount(Msg->Attachments) > 0;
 }
 
 int Conditional_MAIL_MIME_SUBMESSAGES(StrBuf *Target, WCTemplputParams *TP)
 {
-       message_summary *Msg = (message_summary*) CTX;
+       message_summary *Msg = (message_summary*) CTX(CTX_MAILSUM);
        return GetCount(Msg->Submessages) > 0;
 }
 
 int Conditional_MAIL_MIME_ATTACHLINKS(StrBuf *Target, WCTemplputParams *TP)
 {
-       message_summary *Msg = (message_summary*) CTX;
+       message_summary *Msg = (message_summary*) CTX(CTX_MAILSUM);
        return GetCount(Msg->AttachLinks) > 0;
 }
 
 int Conditional_MAIL_MIME_ATTACH(StrBuf *Target, WCTemplputParams *TP)
 {
-       message_summary *Msg = (message_summary*) CTX;
+       message_summary *Msg = (message_summary*) CTX(CTX_MAILSUM);
        return GetCount(Msg->AllAttach) > 0;
 }
 
@@ -953,7 +984,7 @@ void tmplput_EDIT_WIKI_BODY(StrBuf *Target, WCTemplputParams *TP)
 
 void tmplput_MAIL_BODY(StrBuf *Target, WCTemplputParams *TP)
 {
-       message_summary *Msg = (message_summary*) CTX;
+       message_summary *Msg = (message_summary*) CTX(CTX_MAILSUM);
        StrBufAppendTemplate(Target, TP, Msg->MsgBody->Data, 0);
 }
 
@@ -1127,58 +1158,58 @@ void render_MAIL_UNKNOWN(wc_mime_attachment *Mime, StrBuf *RawData, StrBuf *Foun
 
 HashList *iterate_get_mime_All(StrBuf *Target, WCTemplputParams *TP)
 {
-       message_summary *Msg = (message_summary*) CTX;
+       message_summary *Msg = (message_summary*) CTX(CTX_MAILSUM);
        return Msg->Attachments;
 }
 HashList *iterate_get_mime_Submessages(StrBuf *Target, WCTemplputParams *TP)
 {
-       message_summary *Msg = (message_summary*) CTX;
+       message_summary *Msg = (message_summary*) CTX(CTX_MAILSUM);
        return Msg->Submessages;
 }
 HashList *iterate_get_mime_AttachLinks(StrBuf *Target, WCTemplputParams *TP)
 {
-       message_summary *Msg = (message_summary*) CTX;
+       message_summary *Msg = (message_summary*) CTX(CTX_MAILSUM);
        return Msg->AttachLinks;
 }
 HashList *iterate_get_mime_Attachments(StrBuf *Target, WCTemplputParams *TP)
 {
-       message_summary *Msg = (message_summary*) CTX;
+       message_summary *Msg = (message_summary*) CTX(CTX_MAILSUM);
        return Msg->AllAttach;
 }
 
 void tmplput_MIME_Name(StrBuf *Target, WCTemplputParams *TP)
 {
-       wc_mime_attachment *mime = (wc_mime_attachment*) CTX;
+       wc_mime_attachment *mime = (wc_mime_attachment*) CTX(CTX_MIME_ATACH);
        StrBufAppendTemplate(Target, TP, mime->Name, 0);
 }
 
 void tmplput_MIME_FileName(StrBuf *Target, WCTemplputParams *TP)
 {
-       wc_mime_attachment *mime = (wc_mime_attachment*) CTX;
+       wc_mime_attachment *mime = (wc_mime_attachment*) CTX(CTX_MIME_ATACH);
        StrBufAppendTemplate(Target, TP, mime->FileName, 0);
 }
 
 void tmplput_MIME_PartNum(StrBuf *Target, WCTemplputParams *TP)
 {
-       wc_mime_attachment *mime = (wc_mime_attachment*) CTX;
+       wc_mime_attachment *mime = (wc_mime_attachment*) CTX(CTX_MIME_ATACH);
        StrBufAppendTemplate(Target, TP, mime->PartNum, 0);
 }
 
 void tmplput_MIME_MsgNum(StrBuf *Target, WCTemplputParams *TP)
 {
-       wc_mime_attachment *mime = (wc_mime_attachment*) CTX;
+       wc_mime_attachment *mime = (wc_mime_attachment*) CTX(CTX_MIME_ATACH);
        StrBufAppendPrintf(Target, "%ld", mime->msgnum);
 }
 
 void tmplput_MIME_Disposition(StrBuf *Target, WCTemplputParams *TP)
 {
-       wc_mime_attachment *mime = (wc_mime_attachment*) CTX;
+       wc_mime_attachment *mime = (wc_mime_attachment*) CTX(CTX_MIME_ATACH);
        StrBufAppendTemplate(Target, TP, mime->Disposition, 0);
 }
 
 void tmplput_MIME_ContentType(StrBuf *Target, WCTemplputParams *TP)
 {
-       wc_mime_attachment *mime = (wc_mime_attachment*) CTX;
+       wc_mime_attachment *mime = (wc_mime_attachment*) CTX(CTX_MIME_ATACH);
        StrBufAppendTemplate(Target, TP, mime->ContentType, 0);
 }
 
@@ -1189,13 +1220,13 @@ void examine_charset(message_summary *Msg, StrBuf *HdrLine, StrBuf *FoundCharset
 
 void tmplput_MIME_Charset(StrBuf *Target, WCTemplputParams *TP)
 {
-       wc_mime_attachment *mime = (wc_mime_attachment*) CTX;
+       wc_mime_attachment *mime = (wc_mime_attachment*) CTX(CTX_MIME_ATACH);
        StrBufAppendTemplate(Target, TP, mime->Charset, 0);
 }
 
 void tmplput_MIME_Data(StrBuf *Target, WCTemplputParams *TP)
 {
-       wc_mime_attachment *mime = (wc_mime_attachment*) CTX;
+       wc_mime_attachment *mime = (wc_mime_attachment*) CTX(CTX_MIME_ATACH);
        if (mime->Renderer != NULL)
                mime->Renderer->f(mime, NULL, NULL);
        StrBufAppendTemplate(Target, TP, mime->Data, 0);
@@ -1205,7 +1236,7 @@ void tmplput_MIME_Data(StrBuf *Target, WCTemplputParams *TP)
 void tmplput_MIME_LoadData(StrBuf *Target, WCTemplputParams *TP)
 {
        wcsession *WCC = WC;    
-       wc_mime_attachment *mime = (wc_mime_attachment*) CTX;
+       wc_mime_attachment *mime = (wc_mime_attachment*) CTX(CTX_MIME_ATACH);
        wc_mime_attachment *att;
        
        if (( (!strcasecmp(ChrPtr(mime->Disposition), "inline"))||
@@ -1233,7 +1264,7 @@ void tmplput_MIME_LoadData(StrBuf *Target, WCTemplputParams *TP)
 
 void tmplput_MIME_Length(StrBuf *Target, WCTemplputParams *TP)
 {
-       wc_mime_attachment *mime = (wc_mime_attachment*) CTX;
+       wc_mime_attachment *mime = (wc_mime_attachment*) CTX(CTX_MIME_ATACH);
        StrBufAppendPrintf(Target, "%ld", mime->length);
 }
 
@@ -1430,10 +1461,7 @@ int json_RenderView_or_Tail(SharedMessageStatus *Stat,
                            void **ViewSpecific, 
                            long oper)
 {
-       WCTemplputParams SubTP;
-
-       memset(&SubTP, 0, sizeof(WCTemplputParams));
-       DoTemplate(HKEY("mailsummary_json"),NULL, &SubTP);
+       DoTemplate(HKEY("mailsummary_json"),NULL, NULL);
        
        return 0;
 }
@@ -1508,6 +1536,7 @@ InitModule_MSGRENDERERS
        RegisterNamespace("MAIL:SUMM:SUBJECT", 0, 4, tmplput_MAIL_SUMM_SUBJECT,  NULL, CTX_MAILSUM);
        RegisterNamespace("MAIL:SUMM:NTATACH", 0, 0, tmplput_MAIL_SUMM_NATTACH,  NULL, CTX_MAILSUM);
        RegisterNamespace("MAIL:SUMM:CCCC", 0, 2, tmplput_MAIL_SUMM_CCCC, NULL, CTX_MAILSUM);
+       RegisterNamespace("MAIL:SUMM:REPLYTO", 0, 2, tmplput_MAIL_SUMM_REPLYTO, NULL, CTX_MAILSUM);
        RegisterNamespace("MAIL:SUMM:H_NODE", 0, 2, tmplput_MAIL_SUMM_H_NODE,  NULL, CTX_MAILSUM);
        RegisterNamespace("MAIL:SUMM:ALLRCPT", 0, 2, tmplput_MAIL_SUMM_ALLRCPT,  NULL, CTX_MAILSUM);
        RegisterNamespace("MAIL:SUMM:ORGROOM", 0, 2, tmplput_MAIL_SUMM_ORGROOM,  NULL, CTX_MAILSUM);
@@ -1521,6 +1550,7 @@ InitModule_MSGRENDERERS
        RegisterNamespace("MAIL:EDITWIKI", 1, 2, tmplput_EDIT_WIKI_BODY,  NULL, CTX_NONE);
        RegisterConditional(HKEY("COND:MAIL:SUMM:RFCA"), 0, Conditional_MAIL_SUMM_RFCA,  CTX_MAILSUM);
        RegisterConditional(HKEY("COND:MAIL:SUMM:CCCC"), 0, Conditional_MAIL_SUMM_CCCC,  CTX_MAILSUM);
+       RegisterConditional(HKEY("COND:MAIL:SUMM:REPLYTO"), 0, Conditional_MAIL_SUMM_REPLYTO,  CTX_MAILSUM);
        RegisterConditional(HKEY("COND:MAIL:SUMM:UNREAD"), 0, Conditional_MAIL_SUMM_UNREAD, CTX_MAILSUM);
        RegisterConditional(HKEY("COND:MAIL:SUMM:H_NODE"), 0, Conditional_MAIL_SUMM_H_NODE, CTX_MAILSUM);
        RegisterConditional(HKEY("COND:MAIL:SUMM:OTHERNODE"), 0, Conditional_MAIL_SUMM_OTHERNODE, CTX_MAILSUM);
@@ -1583,6 +1613,7 @@ InitModule_MSGRENDERERS
        RegisterMsgHdr(HKEY("msgn"), examine_msgn, 0);
        RegisterMsgHdr(HKEY("wefw"), examine_wefw, 0);
        RegisterMsgHdr(HKEY("cccc"), examine_cccc, 0);
+       RegisterMsgHdr(HKEY("rep2"), examine_replyto, 0);
        RegisterMsgHdr(HKEY("hnod"), examine_hnod, 0);
        RegisterMsgHdr(HKEY("room"), examine_room, 0);
        RegisterMsgHdr(HKEY("rfca"), examine_rfca, 0);
index caecca46f42681401be6e724d4067d40fd0a8864..4dd20d45eb79c788097b06eaad856b16971b02c9 100644 (file)
@@ -193,15 +193,15 @@ void display_edit_node(void)
                DeleteHash(&NodeConfig);
                return;
        }
-       
-       memset(&SubTP, 0, sizeof(WCTemplputParams));
-       SubTP.Filter.ContextType = CTX_NODECONF;
-       SubTP.Context = vNode;
-       begin_burst();
-       Tmpl = sbstr("template");
-        output_headers(1, 0, 0, 0, 1, 0);
-        DoTemplate(SKEY(Tmpl), NULL, &SubTP);
-        end_burst();                                                                               
+       StackContext(NULL, &SubTP, vNode, CTX_NODECONF, 0, NULL);
+       {
+               begin_burst();
+               Tmpl = sbstr("template");
+               output_headers(1, 0, 0, 0, 1, 0);
+               DoTemplate(SKEY(Tmpl), NULL, &SubTP);
+               end_burst();
+       }
+       UnStackContext(&SubTP);
        DeleteHash(&NodeConfig);
        
 }
@@ -261,25 +261,25 @@ void delete_node(void)
 
 void tmplput_NodeName(StrBuf *Target, WCTemplputParams *TP)
 {
-       NodeConf *Node = (NodeConf*) CTX;       
+       NodeConf *Node = (NodeConf*) CTX(CTX_NODECONF); 
        StrBufAppendTemplate(Target, TP, Node->NodeName, 0);
 }
 
 void tmplput_Secret(StrBuf *Target, WCTemplputParams *TP)
 {
-       NodeConf *Node = (NodeConf*) CTX;
+       NodeConf *Node = (NodeConf*) CTX(CTX_NODECONF);
        StrBufAppendTemplate(Target, TP, Node->Secret, 0);
 }
 
 void tmplput_Host(StrBuf *Target, WCTemplputParams *TP) 
 {
-       NodeConf *Node= (NodeConf*) CTX;
+       NodeConf *Node= (NodeConf*) CTX(CTX_NODECONF);
        StrBufAppendTemplate(Target, TP, Node->Host, 0);
 }
 
 void tmplput_Port(StrBuf *Target, WCTemplputParams *TP)
 {
-       NodeConf *Node= (NodeConf*) CTX;
+       NodeConf *Node= (NodeConf*) CTX(CTX_NODECONF);
        StrBufAppendTemplate(Target, TP, Node->Port, 0);
 }
 
index d9d0ebf65b4867dcf869f529bd618e241c2fc021..11412484097d26ed352d45092b4d8ead3cfde49b 100644 (file)
@@ -351,61 +351,61 @@ void add_new_note(void) {
 
 void tmpl_vcard_put_posleft(StrBuf *Target, WCTemplputParams *TP)
 {
-       struct vnote *v = (struct vnote *) CTX;
+       struct vnote *v = (struct vnote *) CTX(CTX_VNOTE);
        StrBufAppendPrintf(Target, "%d", v->pos_left);
 }
 
 void tmpl_vcard_put_postop(StrBuf *Target, WCTemplputParams *TP)
 {
-       struct vnote *v = (struct vnote *) CTX;
+       struct vnote *v = (struct vnote *) CTX(CTX_VNOTE);
        StrBufAppendPrintf(Target, "%d", v->pos_top);
 }
 
 void tmpl_vcard_put_poswidth(StrBuf *Target, WCTemplputParams *TP)
 {
-       struct vnote *v = (struct vnote *) CTX;
+       struct vnote *v = (struct vnote *) CTX(CTX_VNOTE);
        StrBufAppendPrintf(Target, "%d", v->pos_width);
 }
 
 void tmpl_vcard_put_posheight(StrBuf *Target, WCTemplputParams *TP)
 {
-       struct vnote *v = (struct vnote *) CTX;
+       struct vnote *v = (struct vnote *) CTX(CTX_VNOTE);
        StrBufAppendPrintf(Target, "%d", v->pos_height);
 }
 
 void tmpl_vcard_put_posheight2(StrBuf *Target, WCTemplputParams *TP)
 {
-       struct vnote *v = (struct vnote *) CTX;
+       struct vnote *v = (struct vnote *) CTX(CTX_VNOTE);
        StrBufAppendPrintf(Target, "%d", (v->pos_height / 16) - 5);
 }
 
 void tmpl_vcard_put_width2(StrBuf *Target, WCTemplputParams *TP)
 {
-       struct vnote *v = (struct vnote *) CTX;
+       struct vnote *v = (struct vnote *) CTX(CTX_VNOTE);
        StrBufAppendPrintf(Target, "%d", (v->pos_width / 9) - 1);
 }
 
 void tmpl_vcard_put_color(StrBuf *Target, WCTemplputParams *TP)
 {
-       struct vnote *v = (struct vnote *) CTX;
+       struct vnote *v = (struct vnote *) CTX(CTX_VNOTE);
        StrBufAppendPrintf(Target, "%02X%02X%02X", v->color_red, v->color_green, v->color_blue);
 }
 
 void tmpl_vcard_put_bgcolor(StrBuf *Target, WCTemplputParams *TP)
 {
-       struct vnote *v = (struct vnote *) CTX;
+       struct vnote *v = (struct vnote *) CTX(CTX_VNOTE);
        StrBufAppendPrintf(Target, "%02X%02X%02X", v->color_red/2, v->color_green/2, v->color_blue/2);
 }
 
 void tmpl_vcard_put_message(StrBuf *Target, WCTemplputParams *TP)
 {
-       struct vnote *v = (struct vnote *) CTX;
+       struct vnote *v = (struct vnote *) CTX(CTX_VNOTE);
        StrEscAppend(Target, NULL, v->body, 0, 0); /*TODO?*/
 }
 
 void tmpl_vcard_put_uid(StrBuf *Target, WCTemplputParams *TP)
 {
-       struct vnote *v = (struct vnote *) CTX;
+       struct vnote *v = (struct vnote *) CTX(CTX_VNOTE);
        StrBufAppendBufPlain(Target, v->uid, -1, 0);
 }
 
index 2cdc91f53ea1287c44bc8de40f397b3160b03178..92fd026b13d5f45fb9975252db923598f4f428d8 100644 (file)
@@ -13,9 +13,7 @@ void display_pushemail(void)
        WCTemplputParams SubTP;
        char mobnum[20];
 
-       memset(&SubTP, 0, sizeof(WCTemplputParams));
-       SubTP.Filter.ContextType = CTX_LONGVECTOR;
-       SubTP.Context = &vector;
+       StackContext(NULL, &SubTP, &vector, CTX_LONGVECTOR, 0, NULL);
        vector[0] = 16;
 
        /* Find any existing settings*/
@@ -89,6 +87,7 @@ void display_pushemail(void)
        output_headers(1, 1, 1, 0, 0, 0);
        DoTemplate(HKEY("prefs_pushemail"), NULL, &SubTP);
        wDumpContent(1);
+       UnStackContext(&SubTP);
        FreeStrBuf(&Buf);
 }
 
index beb2faae7437b482cff1f246165940fc9c8c3bd7..5ade41ead4365c3069c5a69b2fe8577cc1ed3e6f 100644 (file)
@@ -595,7 +595,7 @@ int CompareRooms(const folder *room1, const folder *room2)
 int ConditionalRoomIsRESTSubRoom(StrBuf *Target, WCTemplputParams *TP)
 {
        wcsession  *WCC = WC;
-       folder     *Folder = (folder *)CTX;
+       folder     *Folder = (folder *)CTX(CTX_ROOMS);
        HashPos    *it;
        StrBuf     * Dir;
        void       *vDir;
index 6325a10df7ce149f98e1ed61ecf62f3e44e66935..0c3eaa21d1867131f99fd479a896d00dc290d130 100644 (file)
@@ -1369,6 +1369,7 @@ InitModule_ROOMOPS
        REGISTERTokenParamDefine(UA_POSTALLOWED);
        REGISTERTokenParamDefine(UA_ADMINALLOWED);
        REGISTERTokenParamDefine(UA_DELETEALLOWED);
+       REGISTERTokenParamDefine(UA_REPLYALLOWED);
        REGISTERTokenParamDefine(UA_ISTRASH);
 
        REGISTERTokenParamDefine(US_NEEDVALID);
index c7eec125728ff08f8070a4293c8282a6c2abf762..7a993dd6af96d169e704c9c17f482c55916b77e2 100644 (file)
@@ -61,7 +61,7 @@ void tmplput_roombanner(StrBuf *Target, WCTemplputParams *TP)
 
 void tmplput_FLOOR_ID(StrBuf *Target, WCTemplputParams *TP) 
 {
-       Floor *myFloor = (Floor *)CTX;
+       Floor *myFloor = (Floor *)CTX(CTX_FLOORS);
 
        StrBufAppendPrintf(Target, "%d", myFloor->ID);
 }
@@ -69,14 +69,14 @@ void tmplput_FLOOR_ID(StrBuf *Target, WCTemplputParams *TP)
 
 void tmplput_ROOM_FLOORID(StrBuf *Target, WCTemplputParams *TP) 
 {
-       folder *Folder = (folder *)CTX;
+       folder *Folder = (folder *)CTX(CTX_ROOMS);
        StrBufAppendPrintf(Target, "%d", Folder->floorid);
 }
 
 
 void tmplput_ROOM_FLOOR_ID(StrBuf *Target, WCTemplputParams *TP) 
 {
-       folder *Folder = (folder *)CTX;
+       folder *Folder = (folder *)CTX(CTX_ROOMS);
        const Floor *pFloor = Folder->Floor;
 
        if (pFloor == NULL)
@@ -88,7 +88,7 @@ void tmplput_ROOM_FLOOR_ID(StrBuf *Target, WCTemplputParams *TP)
 
 void tmplput_ROOM_FLOOR_NAME(StrBuf *Target, WCTemplputParams *TP) 
 {
-       folder *Folder = (folder *)CTX;
+       folder *Folder = (folder *)CTX(CTX_ROOMS);
        const Floor *pFloor = Folder->Floor;
 
        if (pFloor == NULL)
@@ -117,7 +117,7 @@ void tmplput_ThisRoomFloorName(StrBuf *Target, WCTemplputParams *TP)
 
 void tmplput_FLOOR_NAME(StrBuf *Target, WCTemplputParams *TP) 
 {
-       Floor *myFloor = (Floor *)CTX;
+       Floor *myFloor = (Floor *)CTX(CTX_FLOORS);
 
        StrBufAppendTemplate(Target, TP, myFloor->Name, 0);
 }
@@ -125,7 +125,7 @@ void tmplput_FLOOR_NAME(StrBuf *Target, WCTemplputParams *TP)
 
 void tmplput_FLOOR_NROOMS(StrBuf *Target, WCTemplputParams *TP) 
 {
-       Floor *myFloor = (Floor *)CTX;
+       Floor *myFloor = (Floor *)CTX(CTX_FLOORS);
 
        StrBufAppendPrintf(Target, "%d", myFloor->NRooms);
 }
@@ -133,7 +133,7 @@ void tmplput_FLOOR_NROOMS(StrBuf *Target, WCTemplputParams *TP)
 
 void tmplput_ROOM_FLOOR_NROOMS(StrBuf *Target, WCTemplputParams *TP) 
 {
-       folder *Folder = (folder *)CTX;
+       folder *Folder = (folder *)CTX(CTX_ROOMS);
        const Floor *pFloor = Folder->Floor;
 
        if (pFloor == NULL)
@@ -144,7 +144,7 @@ void tmplput_ROOM_FLOOR_NROOMS(StrBuf *Target, WCTemplputParams *TP)
 
 int ConditionalFloorHaveNRooms(StrBuf *Target, WCTemplputParams *TP)
 {
-       Floor *MyFloor = (Floor *)CTX;
+       Floor *MyFloor = (Floor *)CTX(CTX_FLOORS);
        int HaveN;
 
        HaveN = GetTemplateTokenNumber(Target, TP, 0, 0);
@@ -156,7 +156,7 @@ int ConditionalFloorHaveNRooms(StrBuf *Target, WCTemplputParams *TP)
 int ConditionalFloorIsRESTSubFloor(StrBuf *Target, WCTemplputParams *TP)
 {
        wcsession  *WCC = WC;
-       Floor *MyFloor = (Floor *)CTX;
+       Floor *MyFloor = (Floor *)CTX(CTX_FLOORS);
        /** if we have dav_depth the client just wants the subfloors */
        if ((WCC->Hdr->HR.dav_depth == 1) && 
            (GetCount(WCC->Directory) == 0))
@@ -168,7 +168,7 @@ int ConditionalFloorIsRESTSubFloor(StrBuf *Target, WCTemplputParams *TP)
 int ConditionalFloorIsSUBROOM(StrBuf *Target, WCTemplputParams *TP)
 {
        wcsession  *WCC = WC;
-       Floor *MyFloor = (Floor *)CTX;
+       Floor *MyFloor = (Floor *)CTX(CTX_FLOORS);
 
        return WCC->CurRoom.floorid == MyFloor->ID;
 }
@@ -176,7 +176,7 @@ int ConditionalFloorIsSUBROOM(StrBuf *Target, WCTemplputParams *TP)
 
 int ConditionalFloorIsVirtual(StrBuf *Target, WCTemplputParams *TP)
 {
-       Floor *MyFloor = (Floor *)CTX;
+       Floor *MyFloor = (Floor *)CTX(CTX_FLOORS);
 
        return MyFloor->ID == VIRTUAL_MY_FLOOR;
 }
@@ -202,7 +202,7 @@ void tmplput_ThisRoom(StrBuf *Target, WCTemplputParams *TP)
 
 void tmplput_ROOM_NAME(StrBuf *Target, WCTemplputParams *TP) 
 {
-       folder *Folder = (folder *)CTX;
+       folder *Folder = (folder *)CTX(CTX_ROOMS);
 
        StrBufAppendTemplate(Target, TP, Folder->name, 0);
 }
@@ -210,7 +210,7 @@ void tmplput_ROOM_NAME(StrBuf *Target, WCTemplputParams *TP)
 
 void tmplput_ROOM_BASENAME(StrBuf *Target, WCTemplputParams *TP) 
 {
-       folder *room = (folder *)CTX;
+       folder *room = (folder *)CTX(CTX_ROOMS);
 
        if (room->nRoomNameParts > 1)
                StrBufAppendTemplate(Target, TP, 
@@ -222,7 +222,7 @@ void tmplput_ROOM_BASENAME(StrBuf *Target, WCTemplputParams *TP)
 
 void tmplput_ROOM_LEVEL_N_TIMES(StrBuf *Target, WCTemplputParams *TP) 
 {
-       folder *room = (folder *)CTX;
+       folder *room = (folder *)CTX(CTX_ROOMS);
        int i;
         const char *AppendMe;
         long AppendMeLen;
@@ -239,7 +239,7 @@ void tmplput_ROOM_LEVEL_N_TIMES(StrBuf *Target, WCTemplputParams *TP)
 
 int ConditionalRoomIsInbox(StrBuf *Target, WCTemplputParams *TP)
 {
-       folder *Folder = (folder *)CTX;
+       folder *Folder = (folder *)CTX(CTX_ROOMS);
        return Folder->is_inbox;
 }
 
@@ -295,7 +295,7 @@ int ConditionalRoomHas_QRFlag(StrBuf *Target, WCTemplputParams *TP)
 
 void tmplput_ROOM_QRFLAGS(StrBuf *Target, WCTemplputParams *TP) 
 {
-       folder *Folder = (folder *)CTX;
+       folder *Folder = (folder *)CTX(CTX_ROOMS);
        StrBufAppendPrintf(Target, "%d", Folder->QRFlags);
 }
 
@@ -351,7 +351,7 @@ int ConditionalRoomHas_UAFlag(StrBuf *Target, WCTemplputParams *TP)
 
 void tmplput_ROOM_ACL(StrBuf *Target, WCTemplputParams *TP) 
 {
-       folder *Folder = (folder *)CTX;
+       folder *Folder = (folder *)CTX(CTX_ROOMS);
 
        StrBufAppendPrintf(Target, "%ld", Folder->RAFlags, 0);
 }
@@ -459,7 +459,7 @@ int ConditionalThisRoomOrder(StrBuf *Target, WCTemplputParams *TP)
 
 void tmplput_ROOM_LISTORDER(StrBuf *Target, WCTemplputParams *TP) 
 {
-       folder *Folder = (folder *)CTX;
+       folder *Folder = (folder *)CTX(CTX_ROOMS);
        StrBufAppendPrintf(Target, "%d", Folder->Order);
 }
 
@@ -516,7 +516,7 @@ void tmplput_ThisRoomInfoText(StrBuf *Target, WCTemplputParams *TP)
 
 void tmplput_ROOM_LASTCHANGE(StrBuf *Target, WCTemplputParams *TP) 
 {
-       folder *Folder = (folder *)CTX;
+       folder *Folder = (folder *)CTX(CTX_ROOMS);
        StrBufAppendPrintf(Target, "%d", Folder->lastchange);
 }
 
@@ -556,7 +556,7 @@ void tmplput_ThisRoomX_FileString(StrBuf *Target, WCTemplputParams *TP)
 
 int ConditionalIsThisThatRoom(StrBuf *Target, WCTemplputParams *TP)
 {
-       folder *Folder = (folder *)CTX;
+       folder *Folder = (folder *)CTX(CTX_ROOMS);
        wcsession *WCC = WC;
 
        if (WCC == NULL)
index f297bc020112f035a0e6ed8036dbbe5eaa0ad671..eacc55cf5e705273deeec3a2c05e0fe5508d9abb 100644 (file)
@@ -63,7 +63,7 @@ void initialize_viewdefs(void) {
 
 void tmplput_ROOM_COLLECTIONTYPE(StrBuf *Target, WCTemplputParams *TP) 
 {
-       folder *Folder = (folder *)CTX;
+       folder *Folder = (folder *)CTX(CTX_ROOMS);
        
        switch(Folder->view) {
        case VIEW_CALENDAR:
@@ -91,7 +91,7 @@ void tmplput_ROOM_COLLECTIONTYPE(StrBuf *Target, WCTemplputParams *TP)
 
 int ConditionalRoomHasGroupdavContent(StrBuf *Target, WCTemplputParams *TP)
 {
-       folder *Folder = (folder *)CTX;
+       folder *Folder = (folder *)CTX(CTX_ROOMS);
 
        syslog(0, "-> %s: %d\n", ChrPtr(Folder->name), Folder->view);
 
@@ -228,12 +228,12 @@ int ConditionalThisRoomHaveView(StrBuf *Target, WCTemplputParams *TP)
 
 void tmplput_ROOM_VIEW(StrBuf *Target, WCTemplputParams *TP) 
 {
-       folder *Folder = (folder *)CTX;
+       folder *Folder = (folder *)CTX(CTX_ROOMS);
        StrBufAppendPrintf(Target, "%d", Folder->view);
 }
 void tmplput_ROOM_DEFVIEW(StrBuf *Target, WCTemplputParams *TP) 
 {
-       folder *Folder = (folder *)CTX;
+       folder *Folder = (folder *)CTX(CTX_ROOMS);
        StrBufAppendPrintf(Target, "%d", Folder->defview);
 }
 
index d93fdcc6c0c3964702c99e589f2c022adea3c674..a49d6b30d29a2fe063b33ea4ffb9a83890317dec 100644 (file)
@@ -488,22 +488,22 @@ typedef struct __SieveListing {
 
 int ConditionalSieveScriptIsActive(StrBuf *Target, WCTemplputParams *TP)
 {
-       SieveListing     *SieveList = (SieveListing *)CTX;
+       SieveListing     *SieveList = (SieveListing *)CTX(CTX_SIEVELIST);
        return SieveList->IsActive;
 }
 int ConditionalSieveScriptIsRulesScript(StrBuf *Target, WCTemplputParams *TP)
 {
-       SieveListing     *SieveList = (SieveListing *)CTX;
+       SieveListing     *SieveList = (SieveListing *)CTX(CTX_SIEVELIST);
        return SieveList->IsActive;
 }
 void tmplput_SieveScriptName(StrBuf *Target, WCTemplputParams *TP) 
 {
-       SieveListing     *SieveList = (SieveListing *)CTX;
+       SieveListing     *SieveList = (SieveListing *)CTX(CTX_SIEVELIST);
        StrBufAppendTemplate(Target, TP, SieveList->Name, 0);
 }
 void tmplput_SieveScriptContent(StrBuf *Target, WCTemplputParams *TP) 
 {
-       SieveListing     *SieveList = (SieveListing *)CTX;
+       SieveListing     *SieveList = (SieveListing *)CTX(CTX_SIEVELIST);
        StrBufAppendTemplate(Target, TP, SieveList->Content, 0);
 }
 void FreeSieveListing(void *vSieveListing)
@@ -683,7 +683,7 @@ typedef struct __SieveRule {
 
 int ConditionalSieveRule_hfield(StrBuf *Target, WCTemplputParams *TP)
 {
-       SieveRule     *Rule = (SieveRule *)CTX;
+       SieveRule     *Rule = (SieveRule *)CTX(CTX_SIEVESCRIPT);
        
         return GetTemplateTokenNumber(Target, 
                                       TP, 
@@ -694,7 +694,7 @@ int ConditionalSieveRule_hfield(StrBuf *Target, WCTemplputParams *TP)
 }
 int ConditionalSieveRule_compare(StrBuf *Target, WCTemplputParams *TP)
 {
-       SieveRule     *Rule = (SieveRule *)CTX;
+       SieveRule     *Rule = (SieveRule *)CTX(CTX_SIEVESCRIPT);
         return GetTemplateTokenNumber(Target, 
                                       TP, 
                                       3, 
@@ -704,7 +704,7 @@ int ConditionalSieveRule_compare(StrBuf *Target, WCTemplputParams *TP)
 }
 int ConditionalSieveRule_action(StrBuf *Target, WCTemplputParams *TP)
 {
-       SieveRule     *Rule = (SieveRule *)CTX;
+       SieveRule     *Rule = (SieveRule *)CTX(CTX_SIEVESCRIPT);
         return GetTemplateTokenNumber(Target, 
                                       TP, 
                                       3, 
@@ -714,7 +714,7 @@ int ConditionalSieveRule_action(StrBuf *Target, WCTemplputParams *TP)
 }
 int ConditionalSieveRule_sizecomp(StrBuf *Target, WCTemplputParams *TP)
 {
-       SieveRule     *Rule = (SieveRule *)CTX;
+       SieveRule     *Rule = (SieveRule *)CTX(CTX_SIEVESCRIPT);
         return GetTemplateTokenNumber(Target, 
                                       TP, 
                                       3, 
@@ -724,7 +724,7 @@ int ConditionalSieveRule_sizecomp(StrBuf *Target, WCTemplputParams *TP)
 }
 int ConditionalSieveRule_final(StrBuf *Target, WCTemplputParams *TP)
 {
-       SieveRule     *Rule = (SieveRule *)CTX;
+       SieveRule     *Rule = (SieveRule *)CTX(CTX_SIEVESCRIPT);
         return GetTemplateTokenNumber(Target, 
                                       TP, 
                                       3, 
@@ -734,7 +734,7 @@ int ConditionalSieveRule_final(StrBuf *Target, WCTemplputParams *TP)
 }
 int ConditionalSieveRule_ThisRoom(StrBuf *Target, WCTemplputParams *TP)
 {
-       SieveRule     *Rule = (SieveRule *)CTX;
+       SieveRule     *Rule = (SieveRule *)CTX(CTX_SIEVESCRIPT);
         return GetTemplateTokenNumber(Target, 
                                       TP, 
                                       3, 
@@ -744,39 +744,39 @@ int ConditionalSieveRule_ThisRoom(StrBuf *Target, WCTemplputParams *TP)
 }
 int ConditionalSieveRule_Active(StrBuf *Target, WCTemplputParams *TP)
 {
-       SieveRule     *Rule = (SieveRule *)CTX;
+       SieveRule     *Rule = (SieveRule *)CTX(CTX_SIEVESCRIPT);
         return Rule->active;
 }
 void tmplput_SieveRule_htext(StrBuf *Target, WCTemplputParams *TP) 
 {
-       SieveRule     *Rule = (SieveRule *)CTX;
+       SieveRule     *Rule = (SieveRule *)CTX(CTX_SIEVESCRIPT);
        StrBufAppendTemplate(Target, TP, Rule->htext, 0);
 }
 void tmplput_SieveRule_fileinto(StrBuf *Target, WCTemplputParams *TP) 
 {
-       SieveRule     *Rule = (SieveRule *)CTX;
+       SieveRule     *Rule = (SieveRule *)CTX(CTX_SIEVESCRIPT);
        StrBufAppendTemplate(Target, TP, Rule->fileinto, 0);
 }
 void tmplput_SieveRule_redirect(StrBuf *Target, WCTemplputParams *TP) 
 {
-       SieveRule     *Rule = (SieveRule *)CTX;
+       SieveRule     *Rule = (SieveRule *)CTX(CTX_SIEVESCRIPT);
        StrBufAppendTemplate(Target, TP, Rule->redirect, 0);
 }
 void tmplput_SieveRule_automsg(StrBuf *Target, WCTemplputParams *TP) 
 {
-       SieveRule     *Rule = (SieveRule *)CTX;
+       SieveRule     *Rule = (SieveRule *)CTX(CTX_SIEVESCRIPT);
        StrBufAppendTemplate(Target, TP, Rule->automsg, 0);
 }
 void tmplput_SieveRule_sizeval(StrBuf *Target, WCTemplputParams *TP) 
 {
-       SieveRule     *Rule = (SieveRule *)CTX;
+       SieveRule     *Rule = (SieveRule *)CTX(CTX_SIEVESCRIPT);
        StrBufAppendPrintf(Target, "%d", Rule->sizeval);
 }
 
 void tmplput_SieveRule_lookup_FileIntoRoom(StrBuf *Target, WCTemplputParams *TP) 
 {
        void *vRoom;
-       SieveRule     *Rule = (SieveRule *)CTX;
+       SieveRule     *Rule = (SieveRule *)CTX(CTX_SIEVESCRIPT);
         wcsession *WCC = WC;
        HashList *Rooms = GetRoomListHashLKRA(Target, TP);
 
index a16d9cfe83a44007a7252f7c22b046e2438a529c..5bd9fffcb23146ee15f0dbdc4d3601b3c68b39bd 100644 (file)
@@ -375,7 +375,7 @@ int ConditionalServCfgCTXStrBuf(StrBuf *Target, WCTemplputParams *TP)
        wcsession *WCC = WC;
        void *vBuf;
        StrBuf *Buf;
-       StrBuf *ZoneToCheck = (StrBuf*) CTX;
+       StrBuf *ZoneToCheck = (StrBuf*) CTX(CTX_STRBUF);
 
        if ((WCC->is_aide) || (ZoneToCheck == NULL)) {
                if (WCC->ServCfg == NULL)
@@ -407,13 +407,13 @@ void DeleteLogStatusStruct(void *v)
 
 void tmplput_servcfg_LogName(StrBuf *Target, WCTemplputParams *TP)
 {
-        LogStatusStruct *Stat = (LogStatusStruct*) CTX;
+        LogStatusStruct *Stat = (LogStatusStruct*) CTX(CTX_SRVLOG);
        StrBufAppendTemplate(Target, TP, Stat->Name, 1);
 }
 
 int ConditionalServCfgThisLogEnabled(StrBuf *Target, WCTemplputParams *TP)
 {
-        LogStatusStruct *Stat = (LogStatusStruct*) CTX;
+        LogStatusStruct *Stat = (LogStatusStruct*) CTX(CTX_SRVLOG);
        return Stat->Enable;
 }
 
index 360886380e69347663f08b1a9fd24a153c83a791..fc95a0dc6e0c5073dfc52b61d52ff40d9b1fa96e 100644 (file)
@@ -126,54 +126,54 @@ OneQueItem *DeserializeQueueItem(StrBuf *RawQItem, long QueMsgID)
 
 void tmplput_MailQID(StrBuf *Target, WCTemplputParams *TP)
 {
-       OneQueItem *Item = (OneQueItem*) CTX;
+       OneQueItem *Item = (OneQueItem*) CTX(CTX_MAILQITEM);
        StrBufAppendPrintf(Target, "%ld", Item->QueMsgID);;
 }
 void tmplput_MailQPayloadID(StrBuf *Target, WCTemplputParams *TP)
 {
-       OneQueItem *Item = (OneQueItem*) CTX;
+       OneQueItem *Item = (OneQueItem*) CTX(CTX_MAILQITEM);
        StrBufAppendPrintf(Target, "%ld", Item->MessageID);
 }
 void tmplput_MailQBounceTo(StrBuf *Target, WCTemplputParams *TP)
 {
-       OneQueItem *Item = (OneQueItem*) CTX;
+       OneQueItem *Item = (OneQueItem*) CTX(CTX_MAILQITEM);
        StrBufAppendTemplate(Target, TP, Item->BounceTo, 0);
 }
 void tmplput_MailQAttempted(StrBuf *Target, WCTemplputParams *TP)
 {
         char datebuf[64];
-       OneQueItem *Item = (OneQueItem*) CTX;
+       OneQueItem *Item = (OneQueItem*) CTX(CTX_MAILQITEM);
         webcit_fmt_date(datebuf, 64, Item->ReattemptWhen, DATEFMT_BRIEF);
         StrBufAppendBufPlain(Target, datebuf, -1, 0);
 }
 void tmplput_MailQSubmitted(StrBuf *Target, WCTemplputParams *TP)
 {
         char datebuf[64];
-       OneQueItem *Item = (OneQueItem*) CTX;
+       OneQueItem *Item = (OneQueItem*) CTX(CTX_MAILQITEM);
         webcit_fmt_date(datebuf, 64, Item->Submitted, DATEFMT_BRIEF);
         StrBufAppendBufPlain(Target, datebuf, -1, 0);
 }
 void tmplput_MailQEnvelopeFrom(StrBuf *Target, WCTemplputParams *TP)
 {
-       OneQueItem *Item = (OneQueItem*) CTX;
+       OneQueItem *Item = (OneQueItem*) CTX(CTX_MAILQITEM);
        StrBufAppendTemplate(Target, TP, Item->EnvelopeFrom, 0);
 }
 void tmplput_MailQSourceRoom(StrBuf *Target, WCTemplputParams *TP)
 {
-       OneQueItem *Item = (OneQueItem*) CTX;
+       OneQueItem *Item = (OneQueItem*) CTX(CTX_MAILQITEM);
        StrBufAppendTemplate(Target, TP, Item->SenderRoom, 0);
 }
 
 int Conditional_MailQ_HaveSourceRoom(StrBuf *Target, WCTemplputParams *TP)
 {
-       OneQueItem *Item = (OneQueItem*) CTX;
+       OneQueItem *Item = (OneQueItem*) CTX(CTX_MAILQITEM);
        return StrLength(Item->SenderRoom) > 0;
 }
 
 void tmplput_MailQRetry(StrBuf *Target, WCTemplputParams *TP)
 {
         char datebuf[64];
-       OneQueItem *Item = (OneQueItem*) CTX;
+       OneQueItem *Item = (OneQueItem*) CTX(CTX_MAILQITEM);
 
        if (Item->Retry == 0) {
                StrBufAppendBufPlain(Target, _("First Attempt pending"), -1, 0);
@@ -186,23 +186,23 @@ void tmplput_MailQRetry(StrBuf *Target, WCTemplputParams *TP)
 
 void tmplput_MailQRCPT(StrBuf *Target, WCTemplputParams *TP)
 {
-       MailQEntry *Entry = (MailQEntry*) CTX;
+       MailQEntry *Entry = (MailQEntry*) CTX(CTX_MAILQ_RCPT);
        StrBufAppendTemplate(Target, TP, Entry->Recipient, 0);
 }
 void tmplput_MailQRCPTStatus(StrBuf *Target, WCTemplputParams *TP)
 {
-       MailQEntry *Entry = (MailQEntry*) CTX;
+       MailQEntry *Entry = (MailQEntry*) CTX(CTX_MAILQ_RCPT);
        StrBufAppendPrintf(Target, "%ld", Entry->Status);
 }
 void tmplput_MailQStatusMsg(StrBuf *Target, WCTemplputParams *TP)
 {
-       MailQEntry *Entry = (MailQEntry*) CTX;
+       MailQEntry *Entry = (MailQEntry*) CTX(CTX_MAILQ_RCPT);
        StrBufAppendTemplate(Target, TP, Entry->StatusMessage, 0);
 }
 
 HashList *iterate_get_Recipients(StrBuf *Target, WCTemplputParams *TP)
 {
-       OneQueItem *Item = (OneQueItem*) CTX;
+       OneQueItem *Item = (OneQueItem*) CTX(CTX_MAILQITEM);
        return Item->MailQEntries;
 }
 
index 0c5f6823b05e14ec5e14714c02e470708417a7b8..1b7b559c5db6135afb1a3a65b88987d16ab78569 100644 (file)
@@ -1,5 +1,5 @@
 <tr class="<?ITERATE:ODDEVEN>"><td>
-    <?!("COND:USERLIST:HAVEBIO", 1)><a href="showuser?who=<?USERLIST:USERNAME('U')>"><?USERLIST:USERNAME('X')></a><?!("X", 1)><??("COND:USERLIST:HAVEBIO", 2)><?USERLIST:USERNAME('X')><??("X", 2)>
+    <?!("COND:USERLIST:HAVEBIO", 1)><a href="do_template?temlpate=user_show?who=<?USERLIST:USERNAME('U')>"><?USERLIST:USERNAME('X')></a><?!("X", 1)><??("COND:USERLIST:HAVEBIO", 2)><?USERLIST:USERNAME('X')><??("X", 2)>
   </td>
 <td><?USERLIST:UID></td>
 <td><?USERLIST:ACCLVLSTR>(<?USERLIST:ACCLVLNO>)</td>
index a37fdc0e2ce5960fd58c54d05131a5b57c469039..8c118f97777530ddd465c8903c81d0081cec5c63 100644 (file)
@@ -2,8 +2,8 @@
 <div class="blog_comment_header">
 <span>
  <??("COND:MAIL:ANON",1)>
-   <?!("COND:MAIL:SUMM:RFCA", 2)><a href="showuser?who=<?MAIL:SUMM:FROM("Q")>">"<?MAIL:SUMM:FROM("X")>" &lt;<?MAIL:SUMM:RFCA>&gt;</a><?!("X", 2)>
-   <??("COND:MAIL:SUMM:RFCA", 3)><a href="showuser?who=<?MAIL:SUMM:FROM("Q")>"><?MAIL:SUMM:FROM("X")></a> 
+   <?!("COND:MAIL:SUMM:RFCA", 2)><a href="do_template?temlpate=user_show?who=<?MAIL:SUMM:FROM("Q")>">"<?MAIL:SUMM:FROM("X")>" &lt;<?MAIL:SUMM:RFCA>&gt;</a><?!("X", 2)>
+   <??("COND:MAIL:SUMM:RFCA", 3)><a href="do_template?temlpate=user_show?who=<?MAIL:SUMM:FROM("Q")>"><?MAIL:SUMM:FROM("X")></a> 
       <??("X", 3)><?!("COND:MAIL:SUMM:OTHERNODE",4)> @ <?MAIL:SUMM:H_NODE><??("X",4)>
  <??("X", 1)>
 </span>
index 0dfee3de41602c9ff56174f4deca7b829c34562a..ff1b11b0760184d8e25de09a074f8ba99bc11a9a 100644 (file)
        </div>
        <div id="comment_replyto" style="display:none"><?MAIL:SUMM:INREPLYTO("X")></div>
 </div>
+<<<<<<< HEAD
+=======
+<div class="blog_post_header">
+<span>
+ <??("COND:MAIL:ANON",1)>
+   <?!("COND:MAIL:SUMM:RFCA", 2)><a href="do_template?temlpate=user_show?who=<?MAIL:SUMM:FROM("Q")>">"<?MAIL:SUMM:FROM("X")>" &lt;<?MAIL:SUMM:RFCA>&gt;</a><?!("X", 2)>
+   <??("COND:MAIL:SUMM:RFCA", 3)><a href="do_template?temlpate=user_show?who=<?MAIL:SUMM:FROM("Q")>"><?MAIL:SUMM:FROM("X")></a> 
+      <??("X", 3)><?!("COND:MAIL:SUMM:OTHERNODE",4)> @ <?MAIL:SUMM:H_NODE><??("X",4)>
+ <??("X", 1)>
+</span>
+ <span><?MAIL:SUMM:DATEFULL></span>
+<?!("COND:ROOM:EDITACCESS", 16)>
+   <a href="delete_msg?msgid=<?MAIL:SUMM:N>" onclick="return confirm('<?_("Delete this message?")>');"><span>[</span><?_("Delete")><span>]</span> </a> 
+<??("X", 16)>
+</div>
+<div class="blog_post_content"><div>
+<?MAIL:BODY>
+<?!("COND:MAIL:MIME:ATTACH:SUBMESSAGES", 10)>
+<?ITERATE("MAIL:MIME:ATTACH:SUBMESSAGES", ="view_message_inline_attach")>
+<?!("X", 10)>
+<?!("COND:MAIL:MIME:ATTACH:LINKS", 11)>
+<?ITERATE("MAIL:MIME:ATTACH:LINKS", ="view_message_list_attach")>
+<?!("X", 11)>
+</div>
+</div>
+<div id="comment_replyto" style="display:none"><?MAIL:SUMM:INREPLYTO("X")></div>
+>>>>>>> 8fae1a3f7a2c53606f1c7418637f141e424e5b26
index c3b37c672f23c2232b4871ca818a5f90fbd68f96..8e8b5c6d8a47659818529f4791c2315dce933402 100644 (file)
@@ -6,8 +6,8 @@
  <span><?MAIL:SUMM:DATEFULL></span>
  <?_("from ")>
  <??("COND:MAIL:ANON",1)>
-   <?!("COND:MAIL:SUMM:RFCA", 2)><a href="showuser?who=<?MAIL:SUMM:FROM("Q")>">"<?MAIL:SUMM:FROM("X")>" &lt;<?MAIL:SUMM:RFCA>&gt;</a><?!("X", 2)>
-   <??("COND:MAIL:SUMM:RFCA", 3)><a href="showuser?who=<?MAIL:SUMM:FROM("Q")>"><?MAIL:SUMM:FROM("X")></a> 
+   <?!("COND:MAIL:SUMM:RFCA", 2)><a href="do_template?temlpate=user_show?who=<?MAIL:SUMM:FROM("Q")>">"<?MAIL:SUMM:FROM("X")>" &lt;<?MAIL:SUMM:RFCA>&gt;</a><?!("X", 2)>
+   <??("COND:MAIL:SUMM:RFCA", 3)><a href="do_template?temlpate=user_show?who=<?MAIL:SUMM:FROM("Q")>"><?MAIL:SUMM:FROM("X")></a> 
       <??("X", 3)><?!("COND:MAIL:SUMM:OTHERNODE",4)> @ <?MAIL:SUMM:H_NODE><??("X",4)>
  <??("X", 1)>
  <?!("COND:MAIL:ANON", 4)>****<??("X", 4)>
index 5f2048d887bd64c3e5ed47fbfb7b773af67c88ab..32eafb0d91ee5a89b5d0317d564c716974b889b7 100644 (file)
@@ -6,25 +6,21 @@
                <ul class="room_actions"></ul>
        </div>
        <div id="navbar"><ul>
-               <li><a href="display_pageWHONAME">
+               <li><a href="display_page?recp=<?BSTR("who", "U")>">
                        <img src="static/webcit_icons/essen/16x16/chat.png" alt="">
-                       <span class="navbar_link">chat</span>
+                       <span class="navbar_link"><?_("Click here to send an instant message to")> <?BSTR("who", "X")> </span>
                </a></li>
-               <?!("WHO:ISME", 2)><li><a href="edit_me">
-                       <img src="static/webcit_icons/essen/16x16/config.png" alt="">
-                       <span class="navbar_link"><?_("edit")></span>
-               </a></li><?!("X", 2)>
        </ul></div>
 </div>
 <div id="content" class="service">
        <div id="bio_page">
                <div id="bio_pic">
-                               <img src="image?name=_userpic_?parm=<?WHO:NAME("X")>">
+               <??("COND:USER:PIC", 1, B"who")><img src="image?name=_userpic_?parm=<?BSTR("who", "U")>" alt=""><??("X", 1)>
                </div>
                <?!("X", 1)>
-               <div id="bio_title"><h1><?WHO:NAME("X")></h1></div>
+               <div id="bio_title"><h1><<?BSTR("who", "X")></h1></div>
                <div id="bio_text">
-                       <?WHO:RBIO("X")>
+       <?USER:BIO(B"who", "FJUSTIFY")>
                </div>
        </div>
 <?=("trailing")>
index 7c3cf766592ed1705c726aab4b8dcf7c1f63bce1..fadb728e95fa09b28fbd79962e968f27c5116267 100644 (file)
@@ -1 +1 @@
-<li class="<??("WHO:IDLE", 4)>activeuser<?!("X", 4)><?!("WHO:IDLE", 5)>inactiveuser<?!("X", 5)>"><a href='showuser?who=<?WHO:NAME("U")>'><?WHO:NAME("X")></a></li>
+<li class="<??("WHO:IDLE", 4)>activeuser<?!("X", 4)><?!("WHO:IDLE", 5)>inactiveuser<?!("X", 5)>"><a href="do_template?temlpate=user_show?who=<?WHO:NAME("U")>'><?WHO:NAME("X")></a></li>
index 07ffcd373f03132de4ac4ad9a820dd2ba7df9444..7be58d37817532ffe53373dbbdaa500af07f443b 100644 (file)
@@ -22,7 +22,7 @@
        </td>
        <!--  username (link to user bio/photo page)  -->
        <td>
-               <a href="showuser?who=<?WHO:NAME("U")>"><?WHO:NAME("X")></a>
+               <a href="do_template?temlpate=user_show?who=<?WHO:NAME("U")>"><?WHO:NAME("X")></a>
                <??("WHO:NSESSIONS", 6, 1)>[<?WHO:NSESSIONS>]<?!("X", 6)>
        </td>
        <!-- room -->
index fa323dc0a8e38c5a72100b32a76dcc67fe810a6f..09f0a5338249d06f1d18d641c9bcbf3c348257f2 100644 (file)
@@ -14,7 +14,7 @@
        </td>
        <td>
                <!--  username (link to user bio/photo page)  -->
-               <a href="showuser?who=<?WHO:NAME("U")>"> <?WHO:NAME("X")></a> 
+               <a href="do_template?temlpate=user_show?who=<?WHO:NAME("U")>"> <?WHO:NAME("X")></a> 
                <??("WHO:NSESSIONS", 6, 0, 1)>[<?WHO:NSESSIONS>]<?!("X", 6)>
                <!-- room -->
        </td>
index 1a8d029f0eef959e6f96c4ae65bd6d7b6219c1d9..aa8d6665a863a9ef6417f71297cd8a81af9d86cd 100644 (file)
@@ -117,7 +117,47 @@ const char *CtxNames[]  = {
        "Context UNKNOWN"
 };
 
+void StackContext(WCTemplputParams *Super, 
+                 WCTemplputParams *Sub, 
+                 void *Context,
+                 int ContextType,
+                 int nArgs,
+                 WCTemplateToken *Tokens)
+{
+       memset(Sub, 0, sizeof(WCTemplputParams));
+
+       if (Super != NULL) {
+               Sub->Sub = Super->Sub;
+               Super->Sub = Sub;
+       }
+       Sub->Super = Super;
+       
+       Sub->Context = Context;
+       Sub->Filter.ContextType = ContextType;
+       Sub->nArgs = nArgs;
+       Sub->Tokens = Tokens;
+}
+
+void UnStackContext(WCTemplputParams *Sub)
+{
+       if (Sub->Super != NULL)
+       {
+               Sub->Super->Sub = Sub->Sub;
+       }
+}
+
+void *GetContextPayload(WCTemplputParams *TP, int ContextType)
+{
+       WCTemplputParams *whichTP = TP;
 
+       if (ContextType == CTX_NONE)
+               return TP->Context;
+
+       while ((whichTP != NULL) && (whichTP->Filter.ContextType != ContextType))
+               whichTP = whichTP->Super;
+
+       return whichTP->Context;        
+}
 
 void DestroySortStruct(void *vSort)
 {
@@ -163,23 +203,19 @@ void LogTemplateError (StrBuf *Target, const char *Type, int ErrorPos, WCTemplpu
        if (TP->Tokens != NULL) 
        {
                syslog(1, "%s [%s]  (in '%s' line %ld); %s; [%s]\n", 
-                       Type, 
-                       Err, 
-                       ChrPtr(TP->Tokens->FileName),
-                       TP->Tokens->Line, 
-                       ChrPtr(Error), 
-                       ChrPtr(TP->Tokens->FlatToken));
+                      Type, 
+                      Err, 
+                      ChrPtr(TP->Tokens->FileName),
+                      TP->Tokens->Line, 
+                      ChrPtr(Error), 
+                      ChrPtr(TP->Tokens->FlatToken));
        }
        else 
        {
                syslog(1, "%s: %s;\n", 
-                       Type, 
-                       ChrPtr(Error));
+                      Type, 
+                      ChrPtr(Error));
        }
-/*
-       if (Target == NULL) 
-               return;
-*/
        WCC = WC;
        if (WCC == NULL) {
                FreeStrBuf(&Info);
@@ -295,38 +331,31 @@ void RegisterNS(const char *NSName,
        NewHandler->Filter.nMinArgs = nMinArgs;
        NewHandler->Filter.nMaxArgs = nMaxArgs;
        NewHandler->Filter.ContextType = ContextRequired;
-       NewHandler->Filter.ControlContextType = CTX_NONE;
 
        NewHandler->PreEvalFunc = PreevalFunc;
        NewHandler->HandlerFunc = HandlerFunc;  
        Put(GlobalNS, NSName, len, NewHandler, NULL);
 }
 
-void RegisterControlNS(const char *NSName, 
-                      long len, 
-                      int nMinArgs, 
-                      int nMaxArgs, 
-                      WCHandlerFunc HandlerFunc, 
-                      int ControlContextRequired)
+
+
+int CheckContext(StrBuf *Target, ContextFilter *Need, WCTemplputParams *TP, const char *ErrType)
 {
-       HashHandler *NewHandler;
+       WCTemplputParams *TPP = TP;
        
-       NewHandler = (HashHandler*) malloc(sizeof(HashHandler));
-       memset(NewHandler, 0, sizeof(HashHandler));
-       NewHandler->Filter.nMinArgs = nMinArgs;
-       NewHandler->Filter.nMaxArgs = nMaxArgs;
-       NewHandler->Filter.ContextType = CTX_NONE;
-       NewHandler->Filter.ControlContextType = ControlContextRequired;
-       NewHandler->HandlerFunc = HandlerFunc;  
-       Put(GlobalNS, NSName, len, NewHandler, NULL);
-}
+       if ((Need != NULL) &&
+           (Need->ContextType != CTX_NONE) && 
+           (Need->ContextType != TPP->Filter.ContextType)) {
 
+               while ((TPP != NULL) && 
+                      (Need->ContextType != TPP->Filter.ContextType))
+               {
+                       TPP = TPP->Super;
+               }
 
+               if (TPP != NULL)
+                       return 1;
 
-int CheckContext(StrBuf *Target, ContextFilter *Need, WCTemplputParams *TP, const char *ErrType)
-{
-       if ((Need->ContextType != CTX_NONE) && 
-           (Need->ContextType != TP->Filter.ContextType)) {
                 LogTemplateError(
                         Target, ErrType, ERR_PARM1, TP,
                        "  WARNING: requires Context: [%s], have [%s]!", 
@@ -334,16 +363,6 @@ int CheckContext(StrBuf *Target, ContextFilter *Need, WCTemplputParams *TP, cons
                        ContextName(TP->Filter.ContextType));
                return 0;
        }
-
-       if ((Need->ControlContextType != CTX_NONE) && 
-           (Need->ControlContextType != TP->Filter.ControlContextType)) {
-                LogTemplateError(
-                        Target, ErrType, ERR_PARM1, TP,
-                       "  WARNING: requires Control Context: [%s], have [%s]!", 
-                       ContextName(Need->ControlContextType), 
-                       ContextName(TP->Filter.ControlContextType));
-               return 0;
-       }
 /*                     
        if (TP->Tokens->nParameters < Need->nMinArgs) {
                LogTemplateError(Target, ErrType, ERR_NAME, TP,
@@ -427,7 +446,7 @@ void GetTemplateTokenString(StrBuf *Target,
                            long *len)
 {
        StrBuf *Buf;
-       WCTemplputParams SubTP;
+///    WCTemplputParams SubTP;
 
        if (N >= TP->Tokens->nParameters) {
                LogTemplateError(Target, 
@@ -495,11 +514,9 @@ void GetTemplateTokenString(StrBuf *Target,
                        break;
                }
 
-               memset(&SubTP, 0, sizeof(WCTemplputParams *));
-               SubTP.Context = TP->Context;
-               SubTP.Filter.ContextType = TP->Filter.ContextType;
                Buf = NewStrBuf();
-               DoTemplate(TKEY(N), Buf, &SubTP);
+               DoTemplate(TKEY(N), Buf, TP);
+
                *Value = ChrPtr(Buf);
                *len = StrLength(Buf);
                /* we can't free it here, so we put it into the subst so its discarded later on. */
@@ -1573,12 +1590,24 @@ int EvaluateToken(StrBuf *Target, int state, WCTemplputParams *TP)
                TmplGettext(Target, TP);
                break;
        case SV_CONDITIONAL: /** Forward conditional evaluation */
+               Handler = (HashHandler*) TP->Tokens->PreEval;
+               if (!CheckContext(Target, &Handler->Filter, TP, "Conditional")) {
+                       return -1;
+               }
                return EvaluateConditional(Target, 1, state, TP);
                break;
        case SV_NEG_CONDITIONAL: /** Reverse conditional evaluation */
+               Handler = (HashHandler*) TP->Tokens->PreEval;
+               if (!CheckContext(Target, &Handler->Filter, TP, "Conditional")) {
+                       return -1;
+               }
                return EvaluateConditional(Target, 0, state, TP);
                break;
        case SV_CUST_STR_CONDITIONAL: /** Conditional put custom strings from params */
+               Handler = (HashHandler*) TP->Tokens->PreEval;
+               if (!CheckContext(Target, &Handler->Filter, TP, "Conditional")) {
+                       return -1;
+               }
                if (TP->Tokens->nParameters >= 6) {
                        if (EvaluateConditional(Target, 0, state, TP)) {
                                GetTemplateTokenString(Target, TP, 5, &AppendMe, &AppendMeLen);
@@ -1645,7 +1674,8 @@ const StrBuf *ProcessTemplate(WCTemplate *Tmpl, StrBuf *Target, WCTemplputParams
        memcpy(&TP.Filter, &CallingTP->Filter, sizeof(ContextFilter));
 
        TP.Context = CallingTP->Context;
-       TP.ControlContext = CallingTP->ControlContext;
+       TP.Sub = CallingTP->Sub;
+       TP.Super = CallingTP->Super;
 
        if (LoadTemplates != 0) {                       
                if (LoadTemplates > 1)
@@ -1877,6 +1907,7 @@ void tmpl_iterate_subtmpl(StrBuf *Target, WCTemplputParams *TP)
        void *vContext;
        void *vLastContext = NULL;
        StrBuf *SubBuf;
+       WCTemplputParams IterateTP;
        WCTemplputParams SubTP;
        IterateStruct Status;
 
@@ -1885,7 +1916,6 @@ void tmpl_iterate_subtmpl(StrBuf *Target, WCTemplputParams *TP)
        long StopAt = -1;
 
        memset(&Status, 0, sizeof(IterateStruct));
-       memcpy (&SubTP, &TP, sizeof(WCTemplputParams));
        
        It = (HashIterator*) TP->Tokens->Preeval2;
        if (It == NULL) {
@@ -1940,42 +1970,47 @@ void tmpl_iterate_subtmpl(StrBuf *Target, WCTemplputParams *TP)
                }
        }
        nMembersUsed = GetCount(List);
-       SubBuf = NewStrBuf();
-       SubTP.Filter.ContextType = It->ContextType;
-       SubTP.Filter.ControlContextType = CTX_ITERATE;
-       SubTP.ControlContext = &Status;
+
+       StackContext (TP, &IterateTP, &Status, CTX_ITERATE, 0, TP->Tokens);
+       {
+               SubBuf = NewStrBuf();
        
-       if (HAVE_PARAM(2)) {
-               StartAt = GetTemplateTokenNumber(Target, TP, 2, 0);
-       }
-       if (HAVE_PARAM(3)) {
-               StepWidth = GetTemplateTokenNumber(Target, TP, 3, 0);
-       }
-       if (HAVE_PARAM(4)) {
-               StopAt = GetTemplateTokenNumber(Target, TP, 4, -1);
-       }
-       it = GetNewHashPos(List, StepWidth);
-       if (StopAt < 0) {
-               StopAt = GetCount(List);
-       }
-       while (GetNextHashPos(List, it, &Status.KeyLen, &Status.Key, &vContext)) {
-               if ((Status.n >= StartAt) && (Status.n <= StopAt)) {
-                       if (DetectGroupChange && Status.n > 0) {
-                               Status.GroupChange = SortBy->GroupChange(vContext, vLastContext);
+               if (HAVE_PARAM(2)) {
+                       StartAt = GetTemplateTokenNumber(Target, TP, 2, 0);
+               }
+               if (HAVE_PARAM(3)) {
+                       StepWidth = GetTemplateTokenNumber(Target, TP, 3, 0);
+               }
+               if (HAVE_PARAM(4)) {
+                       StopAt = GetTemplateTokenNumber(Target, TP, 4, -1);
+               }
+               it = GetNewHashPos(List, StepWidth);
+               if (StopAt < 0) {
+                       StopAt = GetCount(List);
+               }
+               while (GetNextHashPos(List, it, &Status.KeyLen, &Status.Key, &vContext)) {
+                       if ((Status.n >= StartAt) && (Status.n <= StopAt)) {
+                               if (DetectGroupChange && Status.n > 0) {
+                                       Status.GroupChange = SortBy->GroupChange(vContext, vLastContext);
+                               }
+                               Status.LastN = (Status.n + 1) == nMembersUsed;
+                               StackContext(&IterateTP, &SubTP, vContext, It->ContextType, 0, NULL);
+                               {
+                                       if (It->DoSubTemplate != NULL)
+                                               It->DoSubTemplate(SubBuf, &SubTP);
+                                       DoTemplate(TKEY(1), SubBuf, &SubTP);
+
+                                       StrBufAppendBuf(Target, SubBuf, 0);
+                                       FlushStrBuf(SubBuf);
+                               }
+                               UnStackContext(&SubTP);
+                               Status.oddeven = ! Status.oddeven;
+                               vLastContext = vContext;
                        }
-                       Status.LastN = (Status.n + 1) == nMembersUsed;
-                       SubTP.Context = vContext;
-                       if (It->DoSubTemplate != NULL)
-                               It->DoSubTemplate(SubBuf, &SubTP);
-                       DoTemplate(TKEY(1), SubBuf, &SubTP);
-                       
-                       StrBufAppendBuf(Target, SubBuf, 0);
-                       FlushStrBuf(SubBuf);
-                       Status.oddeven = ! Status.oddeven;
-                       vLastContext = vContext;
+                       Status.n++;
                }
-               Status.n++;
        }
+       UnStackContext(&IterateTP);
        FreeStrBuf(&SubBuf);
        DeleteHashPos(&it);
        if (It->Destructor != NULL)
@@ -1985,7 +2020,7 @@ void tmpl_iterate_subtmpl(StrBuf *Target, WCTemplputParams *TP)
 
 int conditional_ITERATE_ISGROUPCHANGE(StrBuf *Target, WCTemplputParams *TP)
 {
-       IterateStruct *Ctx = CCTX;
+       IterateStruct *Ctx = CTX(CTX_ITERATE);
        if (TP->Tokens->nParameters < 3)
                return  Ctx->GroupChange;
 
@@ -1994,7 +2029,7 @@ int conditional_ITERATE_ISGROUPCHANGE(StrBuf *Target, WCTemplputParams *TP)
 
 void tmplput_ITERATE_ODDEVEN(StrBuf *Target, WCTemplputParams *TP)
 {
-       IterateStruct *Ctx = CCTX;
+       IterateStruct *Ctx = CTX(CTX_ITERATE);
        if (Ctx->oddeven)
                StrBufAppendBufPlain(Target, HKEY("odd"), 0);
        else
@@ -2004,7 +2039,7 @@ void tmplput_ITERATE_ODDEVEN(StrBuf *Target, WCTemplputParams *TP)
 
 void tmplput_ITERATE_KEY(StrBuf *Target, WCTemplputParams *TP)
 {
-       IterateStruct *Ctx = CCTX;
+       IterateStruct *Ctx = CTX(CTX_ITERATE);
 
        StrBufAppendBufPlain(Target, Ctx->Key, Ctx->KeyLen, 0);
 }
@@ -2012,19 +2047,19 @@ void tmplput_ITERATE_KEY(StrBuf *Target, WCTemplputParams *TP)
 
 void tmplput_ITERATE_LASTN(StrBuf *Target, WCTemplputParams *TP)
 {
-       IterateStruct *Ctx = CCTX;
+       IterateStruct *Ctx = CTX(CTX_ITERATE);
        StrBufAppendPrintf(Target, "%d", Ctx->n);
 }
 
 int conditional_ITERATE_FIRSTN(StrBuf *Target, WCTemplputParams *TP)
 {
-       IterateStruct *Ctx = CCTX;
+       IterateStruct *Ctx = CTX(CTX_ITERATE);
        return Ctx->n == 0;
 }
 
 int conditional_ITERATE_LASTN(StrBuf *Target, WCTemplputParams *TP)
 {
-       IterateStruct *Ctx = CCTX;
+       IterateStruct *Ctx = CTX(CTX_ITERATE);
        return Ctx->LastN;
 }
 
@@ -2054,6 +2089,7 @@ int EvaluateConditional(StrBuf *Target, int Neg, int state, WCTemplputParams *TP
        if (!CheckContext(Target, &Cond->Filter, TP, "Conditional")) {
                return 0;
        }
+
        res = Cond->CondF(Target, TP);
        if (res == Neg)
                rc = TP->Tokens->Params[1]->lvalue;
@@ -2078,25 +2114,6 @@ void RegisterConditional(const char *Name, long len,
        Cond->Filter.nMinArgs = nParams;
        Cond->CondF = CondF;
        Cond->Filter.ContextType = ContextRequired;
-       Cond->Filter.ControlContextType = CTX_NONE;
-       Put(Conditionals, Name, len, Cond, NULL);
-}
-
-void RegisterControlConditional(const char *Name, long len, 
-                               int nParams,
-                               WCConditionalFunc CondF, 
-                               int ControlContextRequired)
-{
-       ConditionalStruct *Cond;
-
-       Cond = (ConditionalStruct*)malloc(sizeof(ConditionalStruct));
-       memset(Cond, 0, sizeof(ConditionalStruct));
-       Cond->PlainName = Name;
-       Cond->Filter.nMaxArgs = nParams;
-       Cond->Filter.nMinArgs = nParams;
-       Cond->CondF = CondF;
-       Cond->Filter.ContextType = CTX_NONE;
-       Cond->Filter.ControlContextType = ControlContextRequired;
        Put(Conditionals, Name, len, Cond, NULL);
 }
 
@@ -2150,11 +2167,11 @@ HashList *Defines;
  */
 void tmplput_ContextString(StrBuf *Target, WCTemplputParams *TP)
 {
-       StrBufAppendTemplate(Target, TP, (StrBuf*)CTX, 0);
+       StrBufAppendTemplate(Target, TP, (StrBuf*)CTX(CTX_STRBUF), 0);
 }
 int ConditionalContextStr(StrBuf *Target, WCTemplputParams *TP)
 {
-       StrBuf *TokenText = (StrBuf*) CTX;
+       StrBuf *TokenText = (StrBuf*) CTX((CTX_STRBUF));
        const char *CompareToken;
        long len;
 
@@ -2164,7 +2181,7 @@ int ConditionalContextStr(StrBuf *Target, WCTemplputParams *TP)
 
 void tmplput_ContextStringArray(StrBuf *Target, WCTemplputParams *TP)
 {
-       HashList *Arr = (HashList*) CTX;
+       HashList *Arr = (HashList*) CTX(CTX_STRBUFARR);
        void *pV;
        int val;
 
@@ -2176,7 +2193,7 @@ void tmplput_ContextStringArray(StrBuf *Target, WCTemplputParams *TP)
 }
 int ConditionalContextStrinArray(StrBuf *Target, WCTemplputParams *TP)
 {
-       HashList *Arr = (HashList*) CTX;
+       HashList *Arr = (HashList*) CTX(CTX_STRBUFARR);
        void *pV;
        int val;
        const char *CompareToken;
@@ -2218,10 +2235,12 @@ void tmpl_do_boxed(StrBuf *Target, WCTemplputParams *TP)
                }
        }
        /* else TODO error? logging? */
-       memcpy (&SubTP, TP, sizeof(WCTemplputParams));
-       SubTP.Context = Headline;
-       SubTP.Filter.ContextType = CTX_STRBUF;
-       DoTemplate(HKEY("box_begin"), Target, &SubTP);
+
+       StackContext (TP, &SubTP, Headline, CTX_STRBUF, 0, NULL);
+       {
+               DoTemplate(HKEY("box_begin"), Target, &SubTP);
+       }
+       UnStackContext(&SubTP);
        DoTemplate(TKEY(0), Target, TP);
        DoTemplate(HKEY("box_end"), Target, TP);
        FreeStrBuf(&Headline);
@@ -2244,7 +2263,6 @@ int preeval_do_tabbed(WCTemplateToken *Token)
        long len;
        int i, nTabs;
 
-
        memset(&TPP, 0, sizeof(WCTemplputParams));
        TP = &TPP;
        TP->Tokens = Token;
@@ -2307,7 +2325,6 @@ void tmpl_do_tabbed(StrBuf *Target, WCTemplputParams *TP)
        WCTemplputParams SubTP;
 
        memset(&TS, 0, sizeof(tab_struct));
-       memcpy (&SubTP, &TP, sizeof(WCTemplputParams));
 
        nTabs = ntabs = TP->Tokens->nParameters / 2;
        TabNames = (StrBuf **) malloc(ntabs * sizeof(StrBuf*));
@@ -2334,33 +2351,37 @@ void tmpl_do_tabbed(StrBuf *Target, WCTemplputParams *TP)
                        nTabs --;
                }
        }
-       memcpy (&SubTP, TP, sizeof(WCTemplputParams));
-       SubTP.Filter.ControlContextType = CTX_TAB;
-       SubTP.ControlContext = &TS;
+       StackContext (TP, &SubTP, &TS, CTX_TAB, 0, NULL);
+       {
+////   TODO jetzt      memcpy (&SubTP, TP, sizeof(WCTemplputParams));
+//             SubTP.Filter.ControlContextType = ;
 
-       StrTabbedDialog(Target, nTabs, TabNames);
-       for (i = 0; i < ntabs; i++) {
-               memset(&TS, 0, sizeof(tab_struct));
-               TS.CurrentTab = i;
-               TS.TabTitle = TabNames[i];
-               StrBeginTab(Target, i, nTabs, TabNames);
-               DoTemplate(TKEY(i * 2 + 1), Target, &SubTP);
-               StrEndTab(Target, i, nTabs);
+               StrTabbedDialog(Target, nTabs, TabNames);
+               for (i = 0; i < ntabs; i++) {
+                       memset(&TS, 0, sizeof(tab_struct));
+                       TS.CurrentTab = i;
+                       TS.TabTitle = TabNames[i];
+                       StrBeginTab(Target, i, nTabs, TabNames);
+                       DoTemplate(TKEY(i * 2 + 1), Target, &SubTP);
+                       StrEndTab(Target, i, nTabs);
+               }
+               for (i = 0; i < ntabs; i++) 
+                       FreeStrBuf(&TabNames[i]);
+               free(TabNames);
        }
-       for (i = 0; i < ntabs; i++) 
-               FreeStrBuf(&TabNames[i]);
+       UnStackContext(&SubTP);
 }
 
 void tmplput_TAB_N(StrBuf *Target, WCTemplputParams *TP)
 {
-       tab_struct *Ctx = CCTX;
+       tab_struct *Ctx = CTX(CTX_TAB);
 
        StrBufAppendPrintf(Target, "%d", Ctx->CurrentTab);
 }
 
 void tmplput_TAB_TITLE(StrBuf *Target, WCTemplputParams *TP)
 {
-       tab_struct *Ctx = CCTX;
+       tab_struct *Ctx = CTX(CTX_TAB);
        StrBufAppendTemplate(Target, TP, Ctx->TabTitle, 0);
 }
 
@@ -2647,7 +2668,7 @@ void tmplput_SORT_ORDER(StrBuf *Target, WCTemplputParams *TP)
 
 void tmplput_long_vector(StrBuf *Target, WCTemplputParams *TP)
 {
-       long *LongVector = (long*) CTX;
+       long *LongVector = (long*) CTX(CTX_LONGVECTOR);
 
        if ((TP->Tokens->Params[0]->Type == TYPE_LONG) && 
            (TP->Tokens->Params[0]->lvalue <= LongVector[0]))
@@ -2690,7 +2711,7 @@ void dbg_print_longvector(long *LongVector)
 
 int ConditionalLongVector(StrBuf *Target, WCTemplputParams *TP)
 {
-       long *LongVector = (long*) CTX;
+       long *LongVector = (long*) CTX(CTX_LONGVECTOR);
 
        if ((TP->Tokens->Params[2]->Type == TYPE_LONG) && 
            (TP->Tokens->Params[2]->lvalue <= LongVector[0])&&
@@ -2740,8 +2761,8 @@ InitModule_SUBST
        RegisterNamespace("ITERATE", 2, 100, tmpl_iterate_subtmpl, preeval_iterate, CTX_NONE);
        RegisterNamespace("DOBOXED", 1, 2, tmpl_do_boxed, NULL, CTX_NONE);
        RegisterNamespace("DOTABBED", 2, 100, tmpl_do_tabbed, preeval_do_tabbed, CTX_NONE);
-       RegisterControlNS(HKEY("TAB:N"), 0, 0, tmplput_TAB_N, CTX_TAB);
-       RegisterControlNS(HKEY("TAB:SUBJECT"), 0, 1, tmplput_TAB_TITLE, CTX_TAB);
+       RegisterNamespace("TAB:N", 0, 0, tmplput_TAB_N, NULL, CTX_TAB);
+       RegisterNamespace("TAB:SUBJECT", 0, 1, tmplput_TAB_TITLE, NULL, CTX_TAB);
 
 
        RegisterNamespace("LONGVECTOR", 1, 1, tmplput_long_vector, NULL, CTX_LONGVECTOR);
@@ -2752,19 +2773,19 @@ InitModule_SUBST
        RegisterConditional(HKEY("COND:LONGVECTOR"), 4, ConditionalLongVector, CTX_LONGVECTOR);
 
 
-       RegisterControlConditional(HKEY("COND:ITERATE:ISGROUPCHANGE"), 2, 
-                                  conditional_ITERATE_ISGROUPCHANGE, 
-                                  CTX_ITERATE);
-       RegisterControlConditional(HKEY("COND:ITERATE:LASTN"), 2, 
-                                  conditional_ITERATE_LASTN, 
-                                  CTX_ITERATE);
-       RegisterControlConditional(HKEY("COND:ITERATE:FIRSTN"), 2, 
-                                  conditional_ITERATE_FIRSTN, 
-                                  CTX_ITERATE);
+       RegisterConditional(HKEY("COND:ITERATE:ISGROUPCHANGE"), 2, 
+                           conditional_ITERATE_ISGROUPCHANGE, 
+                           CTX_ITERATE);
+       RegisterConditional(HKEY("COND:ITERATE:LASTN"), 2, 
+                           conditional_ITERATE_LASTN, 
+                           CTX_ITERATE);
+       RegisterConditional(HKEY("COND:ITERATE:FIRSTN"), 2, 
+                           conditional_ITERATE_FIRSTN, 
+                           CTX_ITERATE);
 
-       RegisterControlNS(HKEY("ITERATE:ODDEVEN"), 0, 0, tmplput_ITERATE_ODDEVEN, CTX_ITERATE);
-       RegisterControlNS(HKEY("ITERATE:KEY"), 0, 0, tmplput_ITERATE_KEY, CTX_ITERATE);
-       RegisterControlNS(HKEY("ITERATE:N"), 0, 0, tmplput_ITERATE_LASTN, CTX_ITERATE);
+       RegisterNamespace("ITERATE:ODDEVEN", 0, 0, tmplput_ITERATE_ODDEVEN, NULL, CTX_ITERATE);
+       RegisterNamespace("ITERATE:KEY", 0, 0, tmplput_ITERATE_KEY, NULL, CTX_ITERATE);
+       RegisterNamespace("ITERATE:N", 0, 0, tmplput_ITERATE_LASTN, NULL, CTX_ITERATE);
        RegisterNamespace("CURRENTFILE", 0, 1, tmplput_CURRENT_FILE, NULL, CTX_NONE);
        RegisterNamespace("DEF:STR", 1, 1, tmplput_DefStr, NULL, CTX_NONE);
        RegisterNamespace("DEF:VAL", 1, 1, tmplput_DefVal, NULL, CTX_NONE);
index b44b33a5217061a3a01a8753d3d1a748a46d2070..f0aa90c2407ffcc96ad4870369492d5e9413dd55 100644 (file)
@@ -35,9 +35,6 @@ enum {
        WCS_LONG          /* its an integer */
 };
 
-#define CTX TP->Context
-#define CCTX TP->ControlContext
-
 #define CTX_NONE 0
 #define CTX_SITECFG 1
 #define CTX_SESSION 2
@@ -78,7 +75,6 @@ enum {
  */
 typedef struct _contexts {
        int ContextType;                /* do we require a User Context ? */
-       int ControlContextType;         /* are we inside of a control structure? */
        int nMinArgs;                   /* How many arguments do we need at least? */
        int nMaxArgs;                   /* up to how many arguments can we handle? */
 } ContextFilter;
@@ -97,6 +93,9 @@ typedef int (*WCPreevalFunc)(WCTemplateToken *Token);
 /* make a template token a lookup key: */
 #define TKEY(a) TP->Tokens->Params[a]->Start, TP->Tokens->Params[a]->len
 
+void *GetContextPayload(WCTemplputParams *TP, int ContextType);
+#define CTX(a) GetContextPayload(TP, a)
+
 /**
  * @ingroup subst
  * this is the signature of a conditional function 
@@ -165,9 +164,9 @@ struct WCTemplateToken {
 struct WCTemplputParams {
        ContextFilter Filter;
        void *Context;
-       void *ControlContext;
        int nArgs;
        WCTemplateToken *Tokens;
+       WCTemplputParams *Sub, *Super;
 };
 
 
@@ -363,6 +362,14 @@ void RegisterITERATOR(const char *Name, long len, /* Our identifier */
 
 
 
+void StackContext(WCTemplputParams *Super, 
+                 WCTemplputParams *Sub, 
+                 void *Context,
+                 int ContextType,
+                 int nArgs,
+                 WCTemplateToken *Tokens);
+
+void UnStackContext(WCTemplputParams *Sub);
 
 
 
index 743c6f8b8ab6b17a3a4e19716e625f77bdb7fabc..657588a0d019809382e472ab7fc3e90d38f2a0d9 100644 (file)
@@ -330,80 +330,80 @@ HashList *iterate_load_userlist(StrBuf *Target, WCTemplputParams *TP)
 
 void tmplput_USERLIST_UserName(StrBuf *Target, WCTemplputParams *TP)
 {
-       UserListEntry *ul = (UserListEntry*) CTX;
+       UserListEntry *ul = (UserListEntry*) CTX(CTX_USERLIST);
        StrBufAppendTemplate(Target, TP, ul->UserName, 0);
 }
 
 void tmplput_USERLIST_Password(StrBuf *Target, WCTemplputParams *TP)
 {
-       UserListEntry *ul = (UserListEntry*) CTX;
+       UserListEntry *ul = (UserListEntry*) CTX(CTX_USERLIST);
        StrBufAppendTemplate(Target, TP, ul->Passvoid, 0);
 }
 
 void tmplput_USERLIST_AccessLevelNo(StrBuf *Target, WCTemplputParams *TP)
 {
-       UserListEntry *ul = (UserListEntry*) CTX;
+       UserListEntry *ul = (UserListEntry*) CTX(CTX_USERLIST);
 
        StrBufAppendPrintf(Target, "%d", ul->AccessLevel, 0);
 }
 
 void tmplput_USERLIST_AccessLevelStr(StrBuf *Target, WCTemplputParams *TP)
 {
-       UserListEntry *ul = (UserListEntry*) CTX;
+       UserListEntry *ul = (UserListEntry*) CTX(CTX_USERLIST);
        
        StrBufAppendBufPlain(Target, _(axdefs[ul->AccessLevel]), -1, 0);
 }
 
 void tmplput_USERLIST_UID(StrBuf *Target, WCTemplputParams *TP)
 {
-       UserListEntry *ul = (UserListEntry*) CTX;
+       UserListEntry *ul = (UserListEntry*) CTX(CTX_USERLIST);
 
        StrBufAppendPrintf(Target, "%d", ul->UID, 0);
 }
 
 void tmplput_USERLIST_LastLogonNo(StrBuf *Target, WCTemplputParams *TP)
 {
-       UserListEntry *ul = (UserListEntry*) CTX;
+       UserListEntry *ul = (UserListEntry*) CTX(CTX_USERLIST);
 
        StrBufAppendPrintf(Target,"%ld", ul->LastLogonT, 0);
 }
 void tmplput_USERLIST_LastLogonStr(StrBuf *Target, WCTemplputParams *TP)
 {
-       UserListEntry *ul = (UserListEntry*) CTX;
+       UserListEntry *ul = (UserListEntry*) CTX(CTX_USERLIST);
        StrEscAppend(Target, NULL, asctime(localtime(&ul->LastLogonT)), 0, 0);
 }
 
 void tmplput_USERLIST_nLogons(StrBuf *Target, WCTemplputParams *TP)
 {
-       UserListEntry *ul = (UserListEntry*) CTX;
+       UserListEntry *ul = (UserListEntry*) CTX(CTX_USERLIST);
 
        StrBufAppendPrintf(Target, "%d", ul->nLogons, 0);
 }
 
 void tmplput_USERLIST_nPosts(StrBuf *Target, WCTemplputParams *TP)
 {
-       UserListEntry *ul = (UserListEntry*) CTX;
+       UserListEntry *ul = (UserListEntry*) CTX(CTX_USERLIST);
 
        StrBufAppendPrintf(Target, "%d", ul->nPosts, 0);
 }
 
 void tmplput_USERLIST_Flags(StrBuf *Target, WCTemplputParams *TP)
 {
-       UserListEntry *ul = (UserListEntry*) CTX;
+       UserListEntry *ul = (UserListEntry*) CTX(CTX_USERLIST);
 
        StrBufAppendPrintf(Target, "%d", ul->Flags, 0);
 }
 
 void tmplput_USERLIST_DaysTillPurge(StrBuf *Target, WCTemplputParams *TP)
 {
-       UserListEntry *ul = (UserListEntry*) CTX;
+       UserListEntry *ul = (UserListEntry*) CTX(CTX_USERLIST);
 
        StrBufAppendPrintf(Target, "%d", ul->DaysTillPurge, 0);
 }
 
 int ConditionalUser(StrBuf *Target, WCTemplputParams *TP)
 {
-       UserListEntry *ul = (UserListEntry*) CTX;
+       UserListEntry *ul = (UserListEntry*) CTX(CTX_USERLIST);
        if (havebstr("usernum")) {
                return ibstr("usernum") == ul->UID;
        }
@@ -416,13 +416,13 @@ int ConditionalUser(StrBuf *Target, WCTemplputParams *TP)
 
 int ConditionalFlagINetEmail(StrBuf *Target, WCTemplputParams *TP)
 {
-       UserListEntry *ul = (UserListEntry*) CTX;
+       UserListEntry *ul = (UserListEntry*) CTX(CTX_USERLIST);
        return (ul->Flags & US_INTERNET) != 0;
 }
 
 int ConditionalUserAccess(StrBuf *Target, WCTemplputParams *TP)
 {
-       UserListEntry *ul = (UserListEntry*) CTX;
+       UserListEntry *ul = (UserListEntry*) CTX(CTX_USERLIST);
        
        if (ul == NULL)
                return 0;
@@ -436,7 +436,7 @@ int ConditionalUserAccess(StrBuf *Target, WCTemplputParams *TP)
 }
 int ConditionalHaveBIO(StrBuf *Target, WCTemplputParams *TP)
 {
-       UserListEntry *ul = (UserListEntry*) CTX;
+       UserListEntry *ul = (UserListEntry*) CTX(CTX_USERLIST);
        
        if (ul == NULL)
                return 0;
@@ -461,8 +461,10 @@ void tmplput_USER_BIO(StrBuf *Target, WCTemplputParams *TP)
                        if ( (StrLength(Buf)==3) && 
                             !strcmp(ChrPtr(Buf), "000")) 
                                Done = 1;
-                       else
+                       else {
                                StrBufAppendBuf(BioBuf, Buf, 0);
+                               StrBufAppendBufPlain(BioBuf, HKEY("\n"), 0);
+                       }
                }
                StrBufAppendTemplate(Target, TP, BioBuf, 1);
                FreeStrBuf(&BioBuf);
@@ -800,19 +802,10 @@ void _display_edituser(void) {
        display_edituser(NULL, 0);
 }
 
-void showuser(void)
-{
-       output_headers(1, 0, 0, 0, 1, 0);
-       do_template("user_show");
-       end_burst();
-}
-
-
 void 
 InitModule_USEREDIT
 (void)
 {
-       WebcitAddUrlHandler(HKEY("showuser"), "", 0, showuser, 0);
        WebcitAddUrlHandler(HKEY("select_user_to_edit"), "", 0, _select_user_to_edit, 0);
        WebcitAddUrlHandler(HKEY("display_edituser"), "", 0, _display_edituser, 0);
        WebcitAddUrlHandler(HKEY("edituser"), "", 0, edituser, 0);
index d531098c66a944b09a131aea69814bd267587001..38b799316afb66916abe3d56cad3a11b82f14cfb 100644 (file)
@@ -651,7 +651,7 @@ TODO: check for layer II
 
 void tmplput_VCARD_ITEM(StrBuf *Target, WCTemplputParams *TP)
 {
-       HashList *VC = CTX;
+       HashList *VC = CTX(CTX_VCARD);
        eVC evc;
        void *vStr;
 
index d3ef2977202e2c008f3d4e8f91aa21c8f2023832..b3440092078d57d12e5c37f94735645fa82b536d 100644 (file)
@@ -149,70 +149,6 @@ extern char *ssl_cipher_list;
 #endif
 
 
-/*
- * Room flags (from Citadel)
- *
- * bucket one...
- */
-#define QR_PERMANENT   1               /* Room does not purge                  */
-#define QR_INUSE       2               /* Set if in use, clear if avail        */
-#define QR_PRIVATE     4               /* Set for any type of private room     */
-#define QR_PASSWORDED  8               /* Set if there's a password too        */
-#define QR_GUESSNAME   16              /* Set if it's a guessname room         */
-#define QR_DIRECTORY   32              /* Directory room                       */
-#define QR_UPLOAD      64              /* Allowed to upload                    */
-#define QR_DOWNLOAD    128             /* Allowed to download                  */
-#define QR_VISDIR      256             /* Visible directory                    */
-#define QR_ANONONLY    512             /* Anonymous-Only room                  */
-#define QR_ANONOPT     1024            /* Anonymous-Option room                */
-#define QR_NETWORK     2048            /* Shared network room                  */
-#define QR_PREFONLY    4096            /* Preferred status needed to enter     */
-#define QR_READONLY    8192            /* Aide status required to post         */
-#define QR_MAILBOX     16384           /* Set if this is a private mailbox     */
-
-/*
- * bucket two...
- */
-#define QR2_SYSTEM     1               /* System room; hide by default         */
-#define QR2_SELFLIST   2               /* Self-service mailing list mgmt       */
-#define QR2_COLLABDEL  4               /* Anyone who can post can also delete  */
-#define QR2_SUBJECTREQ  8               /* Subject strongly recommended                */
-#define QR2_SMTP_PUBLIC 16              /* smtp public postable room           */
-#define QR2_MODERATED  32              /* Listservice aide has to permit posts */
-
-/*
- * user/room access
- */
-#define UA_KNOWN               2
-#define UA_GOTOALLOWED         4
-#define UA_HASNEWMSGS          8
-#define UA_ZAPPED              16
-#define UA_POSTALLOWED          32
-#define UA_ADMINALLOWED         64
-#define UA_DELETEALLOWED        128
-#define UA_ISTRASH              256    /* Only available in room view... */
-
-
-/*
- * User flags (from Citadel)
- */
-#define US_NEEDVALID   1               /* User needs to be validated           */
-#define US_PERM                4               /* Permanent user                       */
-#define US_LASTOLD     16              /* Print last old message with new      */
-#define US_EXPERT      32              /* Experienced user                     */
-#define US_UNLISTED    64              /* Unlisted userlog entry               */
-#define US_NOPROMPT    128             /* Don't prompt after each message      */
-#define US_PROMPTCTL   256             /* <N>ext & <S>top work at prompt       */
-#define US_DISAPPEAR   512             /* Use "disappearing msg prompts"       */
-#define US_REGIS       1024            /* Registered user                      */
-#define US_PAGINATOR   2048            /* Pause after each screen of text      */
-#define US_INTERNET    4096            /* Internet mail privileges             */
-#define US_FLOORS      8192            /* User wants to see floors             */
-#define US_COLOR       16384           /* User wants ANSI color support        */
-#define US_USER_SET    (US_LASTOLD | US_EXPERT | US_UNLISTED | \
-                       US_NOPROMPT | US_DISAPPEAR | US_PAGINATOR | \
-                       US_FLOORS | US_COLOR | US_PROMPTCTL )
-
 
 
 #define SRV_STATUS_MSG(ServerLineBuf) (ChrPtr(ServerLineBuf) + 4), (StrLength(ServerLineBuf) - 4)
index 66667e758fa6de6f1e02a4682a06711f1c892491..ac30cc72dd2e040fdc4d2ac212ea10bf692649fb 100644 (file)
@@ -214,83 +214,83 @@ void DeleteWholistHash(HashList **KillMe)
 
 void tmplput_who_username(StrBuf *Target, WCTemplputParams *TP)
 {
-       UserStateStruct *User = (UserStateStruct*) CTX;
+       UserStateStruct *User = (UserStateStruct*) CTX(CTX_WHO);
        StrBufAppendTemplate(Target, TP, User->UserName, 0);
 }
 
 void tmplput_who_room(StrBuf *Target, WCTemplputParams *TP)
 {
-       UserStateStruct *User = (UserStateStruct*) CTX;
+       UserStateStruct *User = (UserStateStruct*) CTX(CTX_WHO);
        StrBufAppendTemplate(Target, TP, User->Room, 0);
 }
 
 void tmplput_who_host(StrBuf *Target, WCTemplputParams *TP)
 {
-       UserStateStruct *User = (UserStateStruct*) CTX;
+       UserStateStruct *User = (UserStateStruct*) CTX(CTX_WHO);
        StrBufAppendTemplate(Target, TP, User->Host, 0);
 }
 
 void tmplput_who_realroom(StrBuf *Target, WCTemplputParams *TP)
 {
-       UserStateStruct *User = (UserStateStruct*) CTX;
+       UserStateStruct *User = (UserStateStruct*) CTX(CTX_WHO);
        StrBufAppendTemplate(Target, TP, User->RealRoom, 0);
 }
 int conditional_who_realroom(StrBuf *Target, WCTemplputParams *TP)
 {
-       UserStateStruct *User = (UserStateStruct*) CTX;
+       UserStateStruct *User = (UserStateStruct*) CTX(CTX_WHO);
        return StrLength(User->RealRoom) > 0;
 }
 
 void tmplput_who_realhost(StrBuf *Target, WCTemplputParams *TP)
 {
-       UserStateStruct *User = (UserStateStruct*) CTX;
+       UserStateStruct *User = (UserStateStruct*) CTX(CTX_WHO);
        StrBufAppendTemplate(Target, TP, User->RealHost, 0);
 }
 int conditional_who_realhost(StrBuf *Target, WCTemplputParams *TP)
 {
-       UserStateStruct *User = (UserStateStruct*) CTX;
+       UserStateStruct *User = (UserStateStruct*) CTX(CTX_WHO);
        return StrLength(User->RealHost) > 0;
 }
 
 void tmplput_who_lastactive(StrBuf *Target, WCTemplputParams *TP)
 {
-       UserStateStruct *User = (UserStateStruct*) CTX;
+       UserStateStruct *User = (UserStateStruct*) CTX(CTX_WHO);
        StrBufAppendPrintf(Target, "%d", User->LastActive);
 }
 
 void tmplput_who_idlesince(StrBuf *Target, WCTemplputParams *TP)
 {
-       UserStateStruct *User = (UserStateStruct*) CTX;
+       UserStateStruct *User = (UserStateStruct*) CTX(CTX_WHO);
        StrBufAppendPrintf(Target, "%d", User->IdleSince);
 }
 
 void tmplput_who_session(StrBuf *Target, WCTemplputParams *TP)
 {
-       UserStateStruct *User = (UserStateStruct*) CTX;
+       UserStateStruct *User = (UserStateStruct*) CTX(CTX_WHO);
        StrBufAppendPrintf(Target, "%d", User->Session);
 }
 
 int conditional_who_idle(StrBuf *Target, WCTemplputParams *TP)
 {
-       UserStateStruct *User = (UserStateStruct*) CTX;
+       UserStateStruct *User = (UserStateStruct*) CTX(CTX_WHO);
        return User->Idle;
 }
 
 int conditional_who_nsessions(StrBuf *Target, WCTemplputParams *TP)
 {
-       UserStateStruct *User = (UserStateStruct*) CTX;
+       UserStateStruct *User = (UserStateStruct*) CTX(CTX_WHO);
        return User->SessionCount;
 }
 
 void tmplput_who_nsessions(StrBuf *Target, WCTemplputParams *TP)
 {
-       UserStateStruct *User = (UserStateStruct*) CTX;
+       UserStateStruct *User = (UserStateStruct*) CTX(CTX_WHO);
        StrBufAppendPrintf(Target, "%d", User->SessionCount);
 }
 
 int conditional_who_isme(StrBuf *Target, WCTemplputParams *TP)
 {
-       UserStateStruct *User = (UserStateStruct*) CTX;
+       UserStateStruct *User = (UserStateStruct*) CTX(CTX_WHO);
        return (User->Session == WC->ctdl_pid);
 }