]> code.citadel.org Git - citadel.git/commitdiff
Fix ansi auto-detect master
authorArt Cancro <ajc@citadel.org>
Wed, 5 Jun 2024 00:09:53 +0000 (20:09 -0400)
committerArt Cancro <ajc@citadel.org>
Wed, 5 Jun 2024 00:09:53 +0000 (20:09 -0400)
38 files changed:
citadel/configure
citadel/dumploadtest.sh
citadel/server/context.c
citadel/server/context.h
citadel/server/modules/autocompletion/serv_autocompletion.c
citadel/server/modules/bio/serv_bio.c
citadel/server/modules/crypto/serv_crypto.c
citadel/server/modules/crypto/serv_crypto.h
citadel/server/modules/ctdlproto/serv_ctdlproto.c
citadel/server/modules/expire/expire_policy.c
citadel/server/modules/expire/serv_expire.c
citadel/server/modules/imap/serv_imap.c
citadel/server/modules/listdeliver/serv_listdeliver.c
citadel/server/modules/nntp/serv_nntp.c
citadel/server/modules/pop3/serv_pop3.c
citadel/server/modules/smtp/serv_smtp.c
citadel/server/modules/smtp/serv_smtpclient.c
citadel/server/modules/xmpp/serv_xmpp.c
citadel/server/modules/xmpp/xmpp_queue.c
citadel/server/serv_extensions.c
citadel/server/server.h
citadel/server/sysdep.c
citadel/utils/ctdlload.c
libcitadel/lib/html_to_ascii.c
libcitadel/lib/libcitadel.h
libcitadel/lib/stringbuf.c
textclient/README.txt
textclient/citadel.c
textclient/citadel.rc
textclient/citadel_ipc.c
textclient/commands.c
textclient/messages.c
webcit-ng/static/css/webcit.css
webcit-ng/static/index.html
webcit-ng/static/js/view_calendar.js
webcit-ng/static/js/views.js
webcit-ng/static/sounds/hi_from_stu.wav
webcit/static/t/room/edit/tab_share.html

index ad136e3ac1b2ed79061c1688d1e648c2efc7e43b..381a2544c14090f781953154938489a6bf6ab279 100755 (executable)
@@ -74,7 +74,6 @@ int main(int argc, char **argv) {
 }
 !
 $CC $CFLAGS $CPPFLAGS $tempcc -o $tempfile $LDFLAGS -lssl -lcrypto && $tempfile >/dev/null 2>&1 && {
-       CFLAGS=${CFLAGS}' -DHAVE_OPENSSL'
        LDFLAGS=${LDFLAGS}' -lssl -lcrypto -lz'
 } || {
        echo Citadel Server requires OpenSSL which is not present.
index 7179f399180e853ec0f8a55db8c27197b979139c..835986ec79463d7cc3e2af079a5837991d8bfb18 100755 (executable)
@@ -1,20 +1,31 @@
 #!/bin/bash
 
-
 # This script dumps the database, deletes the database, loads the database, dumps it again...
 # ...and then compares the two dumps to see if we have full fidelity between them.
 #
 # Did you read that correctly?  Yes, it will DELETE your database.  So don't run this.
 
-exit 0         # In fact, here's an exit statement you must delete before you can even run it.
+
+if [ "${YES_DELETE_MY_DATABASE}" != '' ] ; then
+       echo Ah, I see you have set YES_DELETE_MY_DATABASE to a non-empty value.
+       echo The dump and load test will now proceed.
+else
+       echo 'This script dumps the database, deletes the database, loads the database, dumps it again...'
+       echo '...and then compares the two dumps to see if we have full fidelity between them.'
+       echo 'Did you read that correctly?  Yes, it will DELETE your database.'
+       echo 'If this is really what you want, set the environment variable YES_DELETE_MY_DATABASE'
+       echo 'to a non-empty value, and run it again.'
+       exit 0
+fi
 
 ps ax | grep citserver | grep -v grep >/dev/null 2>/dev/null && {
-       echo dont do this while the server is running
+       echo Do not do this while the server is running.
        exit 1
 }
 
 ./ctdldump -y >dump.dat
 first=$(md5sum dump.dat | awk ' { print $1 } ' )
+
 rm -fv data/*
 ./ctdlload -y <dump.dat
 ./ctdldump -y >dump.dat
index 6415ce12abd290ea52e8d846fd4dc252701195a3..81709b5b59e81ee0056bb9301df2f149e1953e33 100644 (file)
@@ -318,10 +318,7 @@ CitContext *CloneContext(CitContext *CloneMe) {
        me->MigrateBuf = NULL;
        me->sMigrateBuf = NULL;
        me->redirect_buffer = NULL;
-#ifdef HAVE_OPENSSL
        me->ssl = NULL;
-#endif
-
        me->download_fp = NULL;
        me->upload_fp = NULL;
        me->ma = NULL;
index d28dff7a7f59dadf481957b5bf65d9c8947c7245..1b207b64527b9a5349ea812ec26fe641f79c0370 100644 (file)
@@ -56,10 +56,8 @@ struct CitContext {
        // Redirect this session's output to a memory buffer?
        StrBuf *redirect_buffer;                // the buffer
        StrBuf *StatusMessage;
-#ifdef HAVE_OPENSSL
        SSL *ssl;
        int redirect_ssl;
-#endif
 
        char curr_user[USERNAME_SIZE];  // name of current user
        int logged_in;          // logged in?
index 413bf472b739920999baeff92ae7e3768206cecb..822e01aac6ec869e7fa6a478272071f5afb01ca9 100644 (file)
@@ -1,9 +1,6 @@
 // Autocompletion of email recipients, etc.
-//
 // Copyright (c) 1987-2023 by the citadel.org team
-//
-// This program is open source software.  Use, duplication, or disclosure
-// is subject to the terms of the GNU General Public License version 3.
+// This program is open source software.  Use, duplication, or disclosure is subject to the GNU General Public License version 3.
 
 #include "../../ctdl_module.h"
 #include "serv_autocompletion.h"
index 8be817ae687dbcdf7f999b1ad7aee2290347747d..6ff861551dd0cbbba682c10023ea51ec6f30e894 100644 (file)
@@ -3,9 +3,7 @@
 //
 // Copyright (c) 1987-2022 by the citadel.org team
 //
-// This program is open source software.  Use, duplication, or disclosure
-// is subject to the terms of the GNU General Public License, version 3.
-// The program is distributed without any warranty, expressed or implied.
+// This program is open source software.  Use, duplication, or disclosure is subject to the GNU General Public License, version 3.
 
 #include <sys/types.h>
 #include <sys/stat.h>
index 5d41fa27c924817cb29cc58c517417ba7daf996c..541479af14c73f4848cc789b46fd27a80f30470d 100644 (file)
@@ -7,11 +7,9 @@
 #include <sys/types.h>
 #include "../../sysdep.h"
 
-#ifdef HAVE_OPENSSL
 #include <openssl/ssl.h>
 #include <openssl/err.h>
 #include <openssl/rand.h>
-#endif
 
 #include <time.h>
 
@@ -32,8 +30,6 @@
 #include "../../config.h"
 #include "../../ctdl_module.h"
 
-#ifdef HAVE_OPENSSL
-
 SSL_CTX *ssl_ctx = NULL;               // This SSL context is used for all sessions.
 char *ssl_cipher_list = CIT_CIPHERS;
 
@@ -609,5 +605,3 @@ void endtls(void) {
        CC->ssl = NULL;
        CC->redirect_ssl = 0;
 }
-
-#endif // HAVE_OPENSSL
index b5ee85d53bd970058333422d20ce73e017134254..4ef8033d531d393b5220858171f9a48198a9e2b8 100644 (file)
@@ -7,7 +7,6 @@
 // Which ciphers will be offered; see https://www.openssl.org/docs/manmaster/man1/ciphers.html
 #define CIT_CIPHERS    "ALL:RC4+RSA:+SSLv2:+TLSv1:!MD5:@STRENGTH"
 
-#ifdef HAVE_OPENSSL
 #define OPENSSL_NO_KRB5                /* work around redhat b0rken ssl headers */
 void init_ssl(void);
 void client_write_ssl (const char *buf, int nbytes);
@@ -19,5 +18,3 @@ void cmd_gtls(char *params);
 void endtls(void);
 void CtdlStartTLS(char *ok_response, char *nosup_response, char *error_response);
 extern SSL_CTX *ssl_ctx;  
-
-#endif
index ae267b596fc5645f1a662b159c87066f32a803c3..d5e7ffe61b0c1bf8a634a9d2acec5fe7f2207f43 100644 (file)
@@ -1,72 +1,56 @@
-/* 
- * Citadel protocol main dispatcher
- *
- * Copyright (c) 1987-2017 by the citadel.org team
- *
- * This program is open source software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License, version 3.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- * GNU General Public License for more details.
- */
+// Citadel protocol main dispatcher
+// Copyright (c) 1987-2024 by the citadel.org team
+// This program is open source software.  Use, duplication, or disclosure are subject to the GNU General Public License v3.
 
 #include <stdio.h>
 #include <libcitadel.h>
-
 #include "../../citserver.h"
 #include "../../ctdl_module.h"
 #include "../../config.h"
-/*
- * This loop recognizes all server commands.
- */
+
+// This loop recognizes all server commands.
 void do_command_loop(void) {
-       struct CitContext *CCC = CC;
        char cmdbuf[SIZ];
        
-       time(&CCC->lastcmd);
-       memset(cmdbuf, 0, sizeof cmdbuf); /* Clear it, just in case */
+       time(&CC->lastcmd);
+       memset(cmdbuf, 0, sizeof cmdbuf); // Clear it, just in case
        if (client_getln(cmdbuf, sizeof cmdbuf) < 1) {
                syslog(LOG_INFO, "Citadel client disconnected: ending session.");
-               CCC->kill_me = KILLME_CLIENT_DISCONNECTED;
+               CC->kill_me = KILLME_CLIENT_DISCONNECTED;
                return;
        }
 
-       /* Log the server command, but don't show passwords... */
-       if ( (strncasecmp(cmdbuf, "PASS", 4)) && (strncasecmp(cmdbuf, "SETP", 4)) ) {
-               syslog(LOG_DEBUG, "[%s(%ld)] %s",
-                       CCC->curr_user, CCC->user.usernum, cmdbuf
-               );
+       // Log the server command, but don't show passwords...
+       if (    (strncasecmp(cmdbuf, "PASS", 4))
+               && (strncasecmp(cmdbuf, "SETP", 4))
+       ) {
+               syslog(LOG_DEBUG, "[%s(%ld)] %s", CC->curr_user, CC->user.usernum, cmdbuf);
        }
        else {
-               syslog(LOG_DEBUG, "[%s(%ld)] <password command hidden from log>",
-                           CCC->curr_user, CCC->user.usernum
-               );
+               syslog(LOG_DEBUG, "[%s(%ld)] <password command hidden from log>", CC->curr_user, CC->user.usernum);
        }
 
        buffer_output();
 
-       /*
-        * Let other clients see the last command we executed, and
-        * update the idle time, but not NOOP, QNOP, PEXP, GEXP, RWHO, or TIME.
-        */
-       if ( (strncasecmp(cmdbuf, "NOOP", 4))
-          && (strncasecmp(cmdbuf, "QNOP", 4))
-          && (strncasecmp(cmdbuf, "PEXP", 4))
-          && (strncasecmp(cmdbuf, "GEXP", 4))
-          && (strncasecmp(cmdbuf, "RWHO", 4))
-          && (strncasecmp(cmdbuf, "TIME", 4)) ) {
-               strcpy(CCC->lastcmdname, "    ");
-               safestrncpy(CCC->lastcmdname, cmdbuf, sizeof(CCC->lastcmdname));
-               time(&CCC->lastidle);
+       // Let other clients see the last command we executed, and
+       // update the idle time, but not NOOP, QNOP, PEXP, GEXP, RWHO, or TIME.
+       if (    (strncasecmp(cmdbuf, "NOOP", 4))
+               && (strncasecmp(cmdbuf, "QNOP", 4))
+               && (strncasecmp(cmdbuf, "PEXP", 4))
+               && (strncasecmp(cmdbuf, "GEXP", 4))
+               && (strncasecmp(cmdbuf, "RWHO", 4))
+               && (strncasecmp(cmdbuf, "TIME", 4))
+       ) {
+               strcpy(CC->lastcmdname, "    ");
+               safestrncpy(CC->lastcmdname, cmdbuf, sizeof(CC->lastcmdname));
+               time(&CC->lastidle);
        }
        
-       if ((strncasecmp(cmdbuf, "ENT0", 4))
-          && (strncasecmp(cmdbuf, "MESG", 4))
-          && (strncasecmp(cmdbuf, "MSGS", 4)))
-       {
-          CCC->cs_flags &= ~CS_POSTING;
+       if (    (strncasecmp(cmdbuf, "ENT0", 4))
+               && (strncasecmp(cmdbuf, "MESG", 4))
+               && (strncasecmp(cmdbuf, "MSGS", 4))
+       {
+               CC->cs_flags &= ~CS_POSTING;
        }
                   
        if (!DLoader_Exec_Cmd(cmdbuf)) {
@@ -75,7 +59,7 @@ void do_command_loop(void) {
 
        unbuffer_output();
 
-       /* Run any after-each-command routines registered by modules */
+       // Run any after-each-command routines registered by modules
        PerformSessionHooks(EVT_CMD);
 }
 
index 32db98ad2ccc62b5b4d4849b59fc19afa122c5a6..ce302a6b310dd83265c36a1957ebbc2418848f40 100644 (file)
@@ -1,15 +1,6 @@
-/* 
- * Functions which manage expire policy for rooms
- * Copyright (c) 1987-2015 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.
- */
+// Functions which manage expire policy for rooms
+// Copyright (c) 1987-2024 by citadel.org (Art Cancro et al.)
+// This program is open source software.  Use, duplication, or disclosure is subject to the GNU General Public License, version 3.
 
 #include "../../sysdep.h"
 #include <stdlib.h>
@@ -17,7 +8,6 @@
 #include <stdio.h>
 #include <sys/stat.h>
 #include <string.h>
-
 #include <time.h>
 #include <limits.h>
 #include <libcitadel.h>
 #include "../../ctdl_module.h"
 #include "../../user_ops.h"
 
-/*
- * Retrieve the applicable expire policy for a specific room
- */
+// Retrieve the applicable expire policy for a specific room
 void GetExpirePolicy(struct ExpirePolicy *epbuf, struct ctdlroom *qrbuf) {
        struct floor *fl;
 
-       /* If the room has its own policy, return it */ 
+       // If the room has its own policy, return it 
        if (qrbuf->QRep.expire_mode != 0) {
                memcpy(epbuf, &qrbuf->QRep, sizeof(struct ExpirePolicy));
                return;
        }
 
-       /* (non-mailbox rooms)
-        * If the floor has its own policy, return it
-        */
+       // (non-mailbox rooms)
+       // If the floor has its own policy, return it
        if ( (qrbuf->QRflags & QR_MAILBOX) == 0) {
                fl = CtdlGetCachedFloor(qrbuf->QRfloor);
                if (fl->f_ep.expire_mode != 0) {
@@ -56,9 +43,8 @@ void GetExpirePolicy(struct ExpirePolicy *epbuf, struct ctdlroom *qrbuf) {
                }
        }
 
-       /* (Mailbox rooms)
-        * If there is a default policy for mailbox rooms, return it
-        */
+       // (Mailbox rooms)
+       // If there is a default policy for mailbox rooms, return it
        if (qrbuf->QRflags & QR_MAILBOX) {
                if (CtdlGetConfigInt("c_mbxep_mode") != 0) {
                        epbuf->expire_mode = CtdlGetConfigInt("c_mbxep_mode");
@@ -67,15 +53,13 @@ void GetExpirePolicy(struct ExpirePolicy *epbuf, struct ctdlroom *qrbuf) {
                }
        }
 
-       /* Otherwise, fall back on the system default */
+       // Otherwise, fall back on the system default
        epbuf->expire_mode = CtdlGetConfigInt("c_ep_mode");
        epbuf->expire_value = CtdlGetConfigInt("c_ep_value");
 }
 
 
-/*
- * Get Policy EXpire
- */
+// Get Policy EXpire
 void cmd_gpex(char *argbuf) {
        struct ExpirePolicy exp;
        struct floor *fl;
@@ -107,9 +91,7 @@ void cmd_gpex(char *argbuf) {
 }
 
 
-/*
- * Set Policy EXpire
- */
+// Set Policy EXpire
 void cmd_spex(char *argbuf) {
        struct ExpirePolicy exp;
        struct floor flbuf;
@@ -125,8 +107,7 @@ void cmd_spex(char *argbuf) {
                return;
        }
 
-       if ((!strcasecmp(which, strof(roompolicy))) || (!strcasecmp(which, "room")))
-       {
+       if ((!strcasecmp(which, strof(roompolicy))) || (!strcasecmp(which, "room"))) {
                if (!is_room_aide()) {
                        cprintf("%d Higher access required.\n", ERROR + HIGHER_ACCESS_REQUIRED);
                        return;
@@ -148,8 +129,7 @@ void cmd_spex(char *argbuf) {
                return;
        }
 
-       if ((!strcasecmp(which, strof(floorpolicy))) || (!strcasecmp(which, "floor")))
-       {
+       if ((!strcasecmp(which, strof(floorpolicy))) || (!strcasecmp(which, "floor"))) {
                lgetfloor(&flbuf, CC->room.QRfloor);
                memcpy(&flbuf.f_ep, &exp, sizeof(struct ExpirePolicy));
                lputfloor(&flbuf, CC->room.QRfloor);
@@ -157,16 +137,14 @@ void cmd_spex(char *argbuf) {
                return;
        }
 
-       else if ((!strcasecmp(which, strof(mailboxespolicy))) || (!strcasecmp(which, "mailboxes")))
-       {
+       else if ((!strcasecmp(which, strof(mailboxespolicy))) || (!strcasecmp(which, "mailboxes"))) {
                CtdlSetConfigInt("c_mbxep_mode", exp.expire_mode);
                CtdlSetConfigInt("c_mbxep_value", exp.expire_value);
                cprintf("%d Default expire policy for mailboxes set.\n", CIT_OK);
                return;
        }
 
-       else if ((!strcasecmp(which, strof(sitepolicy))) || (!strcasecmp(which, "site")))
-       {
+       else if ((!strcasecmp(which, strof(sitepolicy))) || (!strcasecmp(which, "site"))) {
                if (exp.expire_mode == EXPIRE_NEXTLEVEL) {
                        cprintf("%d Invalid policy (no higher level)\n", ERROR + ILLEGAL_VALUE);
                        return;
index fe4b60b4a20375610c7d1b60954bae7e4dce4221..63b8fc19173bc8fefcca5db2fabaefaa8104a281 100644 (file)
@@ -2,10 +2,9 @@
 //
 // You might also see this module affectionately referred to as TDAP (The Dreaded Auto-Purger).
 //
-// Copyright (c) 1988-2023 by citadel.org (Art Cancro, Wilifried Goesgens, and others)
+// Copyright (c) 1988-2024 by citadel.org (Art Cancro et al.)
 //
-// This program is open source software.  Use, duplication, or disclosure
-// is subject to the terms of the GNU General Public License, version 3.
+// This program is open source software.  Use, duplication, or disclosure is subject to the GNU General Public License, version 3.
 
 
 #include "../../sysdep.h"
index d55971bb5f76de414b88cd4122b3bf9ad3ea2d8a..b9d2a41af95b0efdaef904e38f40b740718a4e32 100644 (file)
@@ -429,9 +429,7 @@ void imap_cleanup_function(void) {
 void imap_output_capability_string(void) {
        IAPuts("CAPABILITY IMAP4REV1 NAMESPACE ID AUTH=PLAIN AUTH=LOGIN UIDPLUS");
 
-#ifdef HAVE_OPENSSL
        if (!CC->redirect_ssl) IAPuts(" STARTTLS");
-#endif
 
 #ifndef DISABLE_IMAP_ACL
        IAPuts(" ACL");
@@ -516,9 +514,7 @@ void imap_greeting(void) {
  */
 void imaps_greeting(void) {
        CtdlModuleStartCryptoMsgs(NULL, NULL, NULL);
-#ifdef HAVE_OPENSSL
        if (!CC->redirect_ssl) CC->kill_me = KILLME_NO_CRYPTO;          /* kill session if no crypto */
-#endif
        imap_greeting();
 }
 
@@ -1544,9 +1540,7 @@ char *ctdl_module_init_imap(void) {
        RegisterImapCMD("LOGIN", "", imap_login, I_FLAG_NONE);
        RegisterImapCMD("AUTHENTICATE", "", imap_authenticate, I_FLAG_NONE);
        RegisterImapCMD("CAPABILITY", "", imap_capability, I_FLAG_NONE);
-#ifdef HAVE_OPENSSL
        RegisterImapCMD("STARTTLS", "", imap_starttls, I_FLAG_NONE);
-#endif
 
        /* The commans below require a logged-in state */
        RegisterImapCMD("SELECT", "", imap_select, I_FLAG_LOGGED_IN);
@@ -1584,9 +1578,7 @@ char *ctdl_module_init_imap(void) {
 
        if (!threading) {
                CtdlRegisterServiceHook(CtdlGetConfigInt("c_imap_port"), NULL, imap_greeting, imap_command_loop, NULL, CitadelServiceIMAP);
-#ifdef HAVE_OPENSSL
                CtdlRegisterServiceHook(CtdlGetConfigInt("c_imaps_port"), NULL, imaps_greeting, imap_command_loop, NULL, CitadelServiceIMAPS);
-#endif
                CtdlRegisterSessionHook(imap_cleanup_function, EVT_STOP, PRIO_STOP + 30);
        }
        
index fd1356079f104eaa907075af27275336949b8442..a6cb74ad40da811b5bbf406b5e674930e070abb1 100644 (file)
@@ -1,14 +1,6 @@
 // This module delivers messages to mailing lists.
-//
-// Copyright (c) 2002-2024 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.
+// Copyright (c) 2002-2024 by the citadel.org team (Art Cancro et al.)
+// This program is open source software.  Use, duplication, or disclosure are subject to the GNU General Public License v3.
 
 #include "../../sysdep.h"
 #include <stdlib.h>
@@ -40,7 +32,6 @@
 
 int doing_listdeliver = 0;
 
-
 // data passed back and forth between listdeliver_do_msg() and listdeliver_sweep_room()
 struct lddata {
        long msgnum;            // number of most recent message processed
@@ -48,7 +39,6 @@ struct lddata {
 };
 
 
-
 void listdeliver_do_msg(long msgnum, void *userdata) {
        struct lddata *ld = (struct lddata *) userdata;
        if (!ld) return;
@@ -63,6 +53,8 @@ void listdeliver_do_msg(long msgnum, void *userdata) {
        struct CtdlMessage *TheMessage = CtdlFetchMessage(msgnum, 1);
        if (!TheMessage) return;
 
+       // FIXME add the list unsubscribe instructions directly to the message text.  Do it right here.
+
        // If the subject line does not contain the name of the room, add it now.
        if (!bmstrcasestr(TheMessage->cm_fields[eMsgSubject], CC->room.QRname)) {
                snprintf(buf, sizeof buf, "[%s] %s", CC->room.QRname, TheMessage->cm_fields[eMsgSubject]);
@@ -78,6 +70,13 @@ void listdeliver_do_msg(long msgnum, void *userdata) {
        CM_SetField(TheMessage, erFc822Addr, buf);
        CM_SetField(TheMessage, eReplyTo, buf);
 
+       // To: likewise needs to have something in it, definitely not the name of an actual mailing list member.
+       // Let's use the address and name of the room.
+       strcat(buf, " (");
+       strcat(buf, CC->room.QRname);
+       strcat(buf, " )");
+       CM_SetField(TheMessage, eRecipient, buf);
+
        // With that out of the way, let's figure out who this message needs to be sent to.
        char *recipients = malloc(strlen(ld->netconf));
        if (recipients) {
index 4c581db632432c11313da844022969afb446807d..231b694cddb1c8a310767d4794e1219669169ee8 100644 (file)
@@ -175,9 +175,7 @@ void nntp_greeting(void) {
 // NNTPS is just like NNTP, except it goes crypto right away.
 void nntps_greeting(void) {
        CtdlModuleStartCryptoMsgs(NULL, NULL, NULL);
-#ifdef HAVE_OPENSSL
        if (!CC->redirect_ssl) CC->kill_me = KILLME_NO_CRYPTO;          // kill session if no crypto
-#endif
        nntp_greeting();
 }
 
@@ -204,9 +202,7 @@ void nntp_capabilities(void) {
        cprintf("MODE-READER\r\n");
        cprintf("LIST ACTIVE NEWSGROUPS\r\n");
        cprintf("OVER\r\n");
-#ifdef HAVE_OPENSSL
        cprintf("STARTTLS\r\n");
-#endif
        if (!CC->logged_in) {
                cprintf("AUTHINFO USER\r\n");
        }
@@ -1070,14 +1066,12 @@ char *ctdl_module_init_nntp(void) {
                                        NULL, 
                                        CitadelServiceNNTP);
 
-#ifdef HAVE_OPENSSL
                CtdlRegisterServiceHook(CtdlGetConfigInt("c_nntps_port"),
                                        NULL,
                                        nntps_greeting,
                                        nntp_command_loop,
                                        NULL,
                                        CitadelServiceNNTPS);
-#endif
 
                CtdlRegisterSessionHook(nntp_cleanup_function, EVT_STOP, PRIO_STOP + 250);
        }
index 322f2d826038834c47f55d21ef714339a75c98e9..d624cfe9e07ebf749efb40e488e42b3f2e04f84a 100644 (file)
 #include "../../ctdl_module.h"
 
 
-// This cleanup function blows away the temporary memory and files used by
-// the POP3 server.
+// This cleanup function blows away the temporary memory and files used by the POP3 server.
 void pop3_cleanup_function(void) {
-       /* Don't do this stuff if this is not a POP3 session! */
+       // Don't do this stuff if this is not a POP3 session!
        if (CC->h_command_function != pop3_command_loop) return;
 
        struct citpop3 *pop3 = ((struct citpop3 *)CC->session_specific_data);
@@ -78,13 +77,10 @@ void pop3_greeting(void) {
 void pop3s_greeting(void) {
        CtdlModuleStartCryptoMsgs(NULL, NULL, NULL);
 
-/* kill session if no crypto */
-#ifdef HAVE_OPENSSL
-       if (!CC->redirect_ssl) CC->kill_me = KILLME_NO_CRYPTO;
-#else
-       CC->kill_me = KILLME_NO_CRYPTO;
-#endif
-
+       // kill the session if TLS is not running by now
+       if (!CC->redirect_ssl) {
+               CC->kill_me = KILLME_NO_CRYPTO;
+       }
        pop3_greeting();
 }
 
@@ -149,10 +145,10 @@ int pop3_grab_mailbox(void) {
 
        if (CtdlGetRoom(&CC->room, MAILROOM) != 0) return(-1);
 
-       /* Load up the messages */
+       // Load up the messages
        CtdlForEachMessage(MSGS_ALL, 0L, NULL, NULL, NULL, pop3_add_message, NULL);
 
-       /* Figure out which are old and which are new */
+       // Figure out which are old and which are new
         CtdlGetRelationship(&vbuf, &CC->user, &CC->room);
        POP3->lastseen = (-1);
        if (POP3->num_msgs) for (i=0; i<POP3->num_msgs; ++i) {
@@ -516,11 +512,9 @@ void pop3_command_loop(void) {
                pop3_pass(&cmdbuf[5]);
        }
 
-#ifdef HAVE_OPENSSL
        else if (!strncasecmp(cmdbuf, "STLS", 4)) {
                pop3_stls();
        }
-#endif
 
        else if (!CC->logged_in) {
                cprintf("-ERR Not logged in.\r\n");
@@ -582,17 +576,15 @@ char *ctdl_module_init_pop3(void) {
                                        pop3_command_loop,
                                        NULL,
                                        CitadelServicePop3);
-#ifdef HAVE_OPENSSL
                CtdlRegisterServiceHook(CtdlGetConfigInt("c_pop3s_port"),
                                        NULL,
                                        pop3s_greeting,
                                        pop3_command_loop,
                                        NULL,
                                        CitadelServicePop3S);
-#endif
                CtdlRegisterSessionHook(pop3_cleanup_function, EVT_STOP, PRIO_STOP + 30);
        }
        
-       /* return our module name for the log */
+       // return our module name for the log
        return "pop3";
 }
index b153207567cbf0aebb36357624d57e7cb2fe5b0d..9ecf144b1a6601a802fa1502a0b631f30ca387f0 100644 (file)
@@ -134,9 +134,9 @@ void smtp_greeting(int is_msa) {
 // SMTPS is just like SMTP, except it goes crypto right away.
 void smtps_greeting(void) {
        CtdlModuleStartCryptoMsgs(NULL, NULL, NULL);
-#ifdef HAVE_OPENSSL
-       if (!CC->redirect_ssl) CC->kill_me = KILLME_NO_CRYPTO;          // kill session if no crypto
-#endif
+       if (!CC->redirect_ssl) {
+               CC->kill_me = KILLME_NO_CRYPTO;         // kill session if no crypto
+       }
        smtp_greeting(0);
 }
 
@@ -214,7 +214,6 @@ void smtp_hello(int which_command) {
                cprintf("250-HELP\r\n");
                cprintf("250-SIZE %ld\r\n", CtdlGetConfigLong("c_maxmsglen"));
 
-#ifdef HAVE_OPENSSL
                // Offer the STARTTLS option...
                if (    (!CC->redirect_ssl)                                                     // not if we're already TLS
                        && (    (SMTP->is_msa)                                                  // Always on port 587
@@ -223,7 +222,6 @@ void smtp_hello(int which_command) {
                ) {
                        cprintf("250-STARTTLS\r\n");
                }
-#endif
 
                cprintf("250-AUTH LOGIN PLAIN\r\n"
                        "250-AUTH=LOGIN PLAIN\r\n"
@@ -964,12 +962,10 @@ void smtp_command_loop(void) {
                smtp_rcpt();
                return;
        }
-#ifdef HAVE_OPENSSL
        if (!strncasecmp(ChrPtr(SMTP->Cmd), "STARTTLS", 8)) {
                smtp_starttls();
                return;
        }
-#endif
 
        cprintf("502 I'm afraid I can't do that.\r\n");
 }
@@ -1015,14 +1011,12 @@ char *ctdl_module_init_smtp(void) {
                                        NULL, 
                                        CitadelServiceSMTP_MTA);
 
-#ifdef HAVE_OPENSSL
                CtdlRegisterServiceHook(CtdlGetConfigInt("c_smtps_port"),       // SMTPS MTA
                                        NULL,
                                        smtps_greeting,
                                        smtp_command_loop,
                                        NULL,
                                        CitadelServiceSMTPS_MTA);
-#endif
 
                CtdlRegisterServiceHook(CtdlGetConfigInt("c_msa_port"),         // SMTP MSA
                                        NULL,
index 03f54b108b0725c794e0fb2f6c5b8dcbf4bdd5ca..6eedc56d02bb37d3523a6cd1b282c71e077650d0 100644 (file)
@@ -224,15 +224,13 @@ int smtp_attempt_delivery(long msgid, char *recp, char *envelope_from, char *sou
                char unsubscribe_url[SIZ];
                snprintf(base_url, sizeof base_url, "https://%s/listsub", CtdlGetConfigStr("c_fqdn"));
                generate_one_click_url(unsubscribe_url, base_url, "unsubscribe", source_room, recp);
-               cprintf("List-Unsubscribe: %s\r\n", unsubscribe_url);
+               cprintf("List-Unsubscribe: <%s>\r\n", unsubscribe_url);                 // RFC 2369
                cprintf("List-Unsubscribe-Post: List-Unsubscribe=One-Click\r\n");       // RFC 8058
-
        }
 
        CtdlOutputMsg(msgid, MT_RFC822, HEADERS_ALL, 0, 1, NULL, 0, NULL, &fromaddr, NULL);
        s.TheMessage = CC->redirect_buffer;
        CC->redirect_buffer = NULL;
-       syslog(LOG_DEBUG, "fromaddr=<%s>",fromaddr);
 
        // If we have a DKIM key, try to sign the message.
        char *dkim_private_key = CtdlGetConfigStr("dkim_private_key");
index 9dacbffad865639f8172a470fb4f24ba723d9d74..81b239c90ab4eebf71d8f0f5fd9483eae971b8eb 100644 (file)
@@ -192,11 +192,9 @@ void xmpp_stream_start(void *data, const char *supplied_el, const char **attr)
        /*
         * TLS encryption (but only if it isn't already active)
         */ 
-#ifdef HAVE_OPENSSL
        if (!CC->redirect_ssl) {
                cprintf("<starttls xmlns='urn:ietf:params:xml:ns:xmpp-tls'></starttls>");
        }
-#endif
 
        if (!CC->logged_in) {
                /* If we're not logged in yet, offer SASL as our feature set */
@@ -487,14 +485,9 @@ void xmpp_xml_end(void *data, const char *supplied_el) {
        }
 
        else if (!strcasecmp(el, "starttls")) {
-#ifdef HAVE_OPENSSL
                cprintf("<proceed xmlns='urn:ietf:params:xml:ns:xmpp-tls'/>");
                CtdlModuleStartCryptoMsgs(NULL, NULL, NULL);
                if (!CC->redirect_ssl) CC->kill_me = KILLME_NO_CRYPTO;
-#else
-               cprintf("<failure xmlns='urn:ietf:params:xml:ns:xmpp-tls'/>");
-               CC->kill_me = KILLME_NO_CRYPTO;
-#endif
        }
 
        else if (!strcasecmp(el, "ping")) {
index d1929a22689810954e42930329098c8d28cc3c9d..f52a6e26f3fb48c3f530a4c0e476bace18d9db11 100644 (file)
@@ -1,16 +1,6 @@
-/*
- * XMPP event queue
- *
- * Copyright (c) 2007-2021 by Art Cancro
- *
- * 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.
- */
+// XMPP event queue
+// Copyright (c) 2007-2024 by Art Cancro
+// This program is open source software.  Use, duplication, or disclosure is subject to the GNU General Public License v3.
 
 #include "../../sysdep.h"
 #include <stdlib.h>
@@ -49,7 +39,7 @@ void xmpp_queue_event(int event_type, char *email_addr) {
 
        syslog(LOG_DEBUG, "xmpp: xmpp_queue_event(%d, %s)", event_type, email_addr);
 
-       /* Purge events more than a minute old */
+       // Purge events more than a minute old
        begin_critical_section(S_XMPP_QUEUE);
        do {
                purged_something = 0;
@@ -64,7 +54,7 @@ void xmpp_queue_event(int event_type, char *email_addr) {
        } while(purged_something);
        end_critical_section(S_XMPP_QUEUE);
 
-       /* Create a new event */
+       // Create a new event
        new_event = (struct xmpp_event *) malloc(sizeof(struct xmpp_event));
        new_event->next = NULL;
        new_event->event_time = time(NULL);
@@ -73,7 +63,7 @@ void xmpp_queue_event(int event_type, char *email_addr) {
        new_event->session_which_generated_this_event = CC->cs_pid;
        safestrncpy(new_event->event_jid, email_addr, sizeof new_event->event_jid);
 
-       /* Add it to the list */
+       // Add it to the list
        begin_critical_section(S_XMPP_QUEUE);
        if (xmpp_queue == NULL) {
                xmpp_queue = new_event;
@@ -88,7 +78,7 @@ void xmpp_queue_event(int event_type, char *email_addr) {
        }
        end_critical_section(S_XMPP_QUEUE);
 
-       /* Tell the sessions that something is happening */
+       // Tell the sessions that something is happening
        begin_critical_section(S_SESSION_TABLE);
        for (cptr = ContextList; cptr != NULL; cptr = cptr->next) {
                if ((cptr->logged_in) && (cptr->h_async_function == xmpp_async_loop)) {
@@ -99,15 +89,13 @@ void xmpp_queue_event(int event_type, char *email_addr) {
 }
 
 
-/* 
- * Are we interested in anything from the queue?  (Called in async loop)
- */
+// Are we interested in anything from the queue?  (Called in async loop)
 void xmpp_process_events(void) {
        struct xmpp_event *xptr = NULL;
        int highest_event = 0;
 
        for (xptr=xmpp_queue; xptr!=NULL; xptr=xptr->next) {
-               if (xptr->event_seq > XMPP->last_event_processed) {             // we are getting crashes on this line?
+               if (xptr->event_seq > XMPP->last_event_processed) {
 
                        switch(xptr->event_type) {
 
index 8787beae805027c94b5d7336cea1e755473ea8dc..260bd8dc23a6bb994fd04b9348bf4d16e0f8dd25 100644 (file)
@@ -526,9 +526,7 @@ int PerformXmsgHooks(char *sender, char *sender_email, char *recp, char *msg) {
 
 // "Start TLS" function that is (hopefully) adaptable for any protocol
 void CtdlModuleStartCryptoMsgs(char *ok_response, char *nosup_response, char *error_response) {
-#ifdef HAVE_OPENSSL
        CtdlStartTLS (ok_response, nosup_response, error_response);
-#endif
 }
 
 
index 8d1f7d0ff3bbf99c93e9bfa5895d407dc53e286b..24833b737cd9bd881e8e68245c8025211f932f58 100644 (file)
 #endif
 
 #include "citadel_defs.h"
-#ifdef HAVE_OPENSSL
 #define OPENSSL_NO_KRB5                        // work around redhat b0rken ssl headers
 #include <openssl/ssl.h>
-#endif
 
 
 // New format for a message in memory
index 58f94624dfd5b07cf01c60465c48b6a7dcc4f281..6f3209468c2298bd91fe03fedd9cad74d7713166 100644 (file)
@@ -69,9 +69,7 @@ void init_sysdep(void) {
 #endif
 
        // If we've got OpenSSL, we're going to use it.
-#ifdef HAVE_OPENSSL
        init_ssl();
-#endif
 
        if (pthread_key_create(&MyConKey, NULL) != 0) {                         // TSD for sessions
                syslog(LOG_CRIT, "sysdep: can't create TSD key: %m");
@@ -252,19 +250,17 @@ static unsigned on = 1, off = 0;
 
 void buffer_output(void) {
 #ifdef HAVE_TCP_BUFFERING
-#ifdef HAVE_OPENSSL
-       if (!CC->redirect_ssl)
-#endif
+       if (!CC->redirect_ssl) {
                setsockopt(CC->client_socket, IPPROTO_TCP, TCP_CORK, &on, 4);
+       }
 #endif
 }
 
 void unbuffer_output(void) {
 #ifdef HAVE_TCP_BUFFERING
-#ifdef HAVE_OPENSSL
-       if (!CC->redirect_ssl)
-#endif
+       if (!CC->redirect_ssl) {
                setsockopt(CC->client_socket, IPPROTO_TCP, TCP_CORK, &off, 4);
+       }
 #endif
 }
 
@@ -305,12 +301,10 @@ int client_write(const char *buf, int nbytes) {
                return 0;
        }
 
-#ifdef HAVE_OPENSSL
        if (Ctx->redirect_ssl) {
                client_write_ssl(buf, nbytes);
                return 0;
        }
-#endif
        if (Ctx->client_socket == -1) return -1;
 
        fdflags = fcntl(Ctx->client_socket, F_GETFL);
@@ -397,16 +391,13 @@ int client_read_blob(StrBuf *Target, int bytes, int timeout) {
        const char *Error;
        int retval = 0;
 
-#ifdef HAVE_OPENSSL
        if (CC->redirect_ssl) {
                retval = client_read_sslblob(Target, bytes, timeout);
                if (retval < 0) {
                        syslog(LOG_ERR, "sysdep: client_read_blob() failed");
                }
        }
-       else 
-#endif
-       {
+       else {
                retval = StrBufReadBLOBBuffered(Target, 
                                                CC->RecvBuf.Buf,
                                                &CC->RecvBuf.ReadWritePointer,
@@ -505,14 +496,11 @@ int CtdlClientGetLine(StrBuf *Target) {
        int rc;
 
        FlushStrBuf(Target);
-#ifdef HAVE_OPENSSL
        if (CC->redirect_ssl) {
                rc = client_readline_sslbuffer(Target, CC->RecvBuf.Buf, &CC->RecvBuf.ReadWritePointer, 1);
                return rc;
        }
-       else 
-#endif
-       {
+       else {
                rc = StrBufTCP_read_buffered_line_fast(Target, 
                                                       CC->RecvBuf.Buf,
                                                       &CC->RecvBuf.ReadWritePointer,
index cc25ef28a8a896882e6c027b8f932d133cc8cb9b..aed19b472cc1ec0e9d2d73d4ef7551afa4e343e1 100644 (file)
@@ -118,6 +118,7 @@ int import_user(char *line, struct cdbkeyval *kv) {
        char userkey[USERNAME_SIZE];
        char *token;
        struct ctdluser *u;
+       int dlen = 0;
 
        u = malloc(sizeof(struct ctdluser));
        if (!u) {
@@ -163,7 +164,6 @@ int import_user(char *line, struct cdbkeyval *kv) {
                                u->msgnum_pic = atol(token);
                                break;
                        case 12:
-                               int dlen;
                                dlen = CtdlDecodeBase64(token, token, strlen(token));                   // Decode in place
                                if (dlen >= sizeof(u->emailaddrs)) {
                                        dlen = sizeof(u->emailaddrs) - 1;
@@ -696,6 +696,7 @@ void ingest_one(char *line, struct cdbkeyval *kv) {
                ++good_rows;
        }
        else {
+               fprintf(stderr, "bad row: <%s>\n", line);
                ++bad_rows;
        }
 
@@ -736,6 +737,10 @@ void ingest(void) {
                }
 
                if (line_len > 0) {
+                       if (!strncasecmp(line, HKEY("end|"))) {
+                               fprintf(stderr, "\n");
+                               end_found = 1;
+                       }
                        if ( (begin_found) && (!end_found) ) {
                                ingest_one(line, &kv);
                        }
@@ -743,10 +748,6 @@ void ingest(void) {
                                begin_found = 1;
                                fprintf(stderr, "   good rows / bad rows:\n");
                        }
-                       if (!strncasecmp(line, HKEY("end|"))) {
-                               fprintf(stderr, "\n");
-                               end_found = 1;
-                       }
                }
 
        } while (ch >= 0);
index ca2de8df8d9e7b0eb756ca1a7186a16d5f571641..8f3c9eca0cbdb4b87df032dcad58ff52f1c7677b 100644 (file)
@@ -1,8 +1,7 @@
-// Functions which handle translation between HTML and plain text
-// Copyright (c) 2000-2023 by the citadel.org team
+// This is an HTML to plain text converter.
+// Copyright (c) 2000-2024 by the citadel.org team (Art Cancro et al.)
 //
-// This program is open source software.  Use, duplication, or disclosure
-// is subject to the terms of the GNU General Public License, version 3.
+// This program is open source software.  Use, duplication, or disclosure is subject to the GNU General Public License version 3.
 
 #include <stdlib.h>
 #include <unistd.h>
 #include <limits.h>
 #include <time.h>
 #include "libcitadel.h"
+
+int u8_wc_toutf8(char *dest, u_int32_t ch) {
+       if (ch < 0x80) {
+               dest[0] = (char)ch;
+               return 1;
+       }
+       if (ch < 0x800) {
+               dest[0] = (ch>>6) | 0xC0;
+               dest[1] = (ch & 0x3F) | 0x80;
+               return 2;
+       }
+       if (ch < 0x10000) {
+               dest[0] = (ch>>12) | 0xE0;
+               dest[1] = ((ch>>6) & 0x3F) | 0x80;
+               dest[2] = (ch & 0x3F) | 0x80;
+               return 3;
+       }
+       if (ch < 0x110000) {
+               dest[0] = (ch>>18) | 0xF0;
+               dest[1] = ((ch>>12) & 0x3F) | 0x80;
+               dest[2] = ((ch>>6) & 0x3F) | 0x80;
+               dest[3] = (ch & 0x3F) | 0x80;
+               return 4;
+       }
+       return 0;
+}
+
+
+// Try to embed an image in the display stream.
+// out                 = the StrBuf to which we are writing the display stream
+// url                 = the URL of the image (warning: it might be a data: URL)
+// display_protocol    = currently only H2A_SIXEL is supported
+void h2a_embed_image(StrBuf *out, char *url, int display_protocol) {
+
+       char buf[4096];
+       snprintf(buf, sizeof(buf), "curl -s '%s' | img2sixel - | fold", url);
+
+       FILE *cmd = popen(buf, "r");
+       if (!cmd) {
+               return;
+       }
+
+       size_t bytes;
+       while (bytes = fread(buf, 1, sizeof(buf), cmd), bytes>0) {
+               StrBufAppendBufPlain(out, buf, bytes, 0);
+       }
+       pclose(cmd);
+}
+
 
 // Convert HTML to plain text.
 //
-// inputmsg    = pointer to raw HTML message
-// msglen      = stop reading after this many bytes
-// screenwidth = desired output screenwidth
-// ansi                = if nonzero, assume output is to a terminal that supports ANSI escape codes
+// inputmsg     = pointer to raw HTML message
+// msglen       = stop reading after this many bytes
+// screenwidth  = desired output screenwidth
+// flags        = Flags that can be set:
+//              H2A_ANSI       = Output ANSI terminal escape sequences
+//              H2A_SIXEL      = Output Sixel graphics (not yet implemented)
 //
-char *html_to_ascii(const char *inputmsg, int msglen, int screenwidth, int ansi) {
-       char inbuf[SIZ];
+char *html_to_ascii(const char *inputmsg, int msglen, int screenwidth, unsigned int flags) {
+       char *inbuf = NULL;
        int inbuf_len = 0;
-       char outbuf[SIZ];
        char tag[1024];
-       int done_reading = 0;
-       const char *inptr;
+       char *tag_start = NULL;
+       char *tag_end = NULL;
+       StrBuf *out;
        char *outptr;
-       size_t outptr_buffer_size;
-       size_t output_len = 0;
-       int i, j, ch, did_out, rb, scanch;
-       int nest = 0;                           // Bracket nesting level
+       int j;
+       char ch;
+       int tag_nesting_level = 0;              // angle bracket nesting level
        int blockquote = 0;                     // BLOCKQUOTE nesting level
        int styletag = 0;                       // STYLE tag nesting level
-       int styletag_start = 0;
-       int bytes_processed = 0;
-       char nl[128];
+       char nl[128];                           // The current value of what a "newline" looks like (changes during blockquotes)
+
+       int ansi = (flags & H2A_ANSI) ? 1 : 0;          // Output to a terminal that can accept ANSI escape sequences
+       int sixel = (flags & H2A_SIXEL) ? 1 : 0;        // Output to a terminal that can accept Sixel graphics
+
+       out = NewStrBuf();
+       if (!out) {
+               return(NULL);
+       }
 
        tag[0] = '\0';
        strcpy(nl, "\n");
-       inptr = inputmsg;
-       strcpy(inbuf, "");
-       strcpy(outbuf, "");
-       if (msglen == 0) msglen = strlen(inputmsg);
-
-       outptr_buffer_size = strlen(inptr) + SIZ;
-       outptr = malloc(outptr_buffer_size);
-       if (outptr == NULL) return NULL;
-       strcpy(outptr, "");
-       output_len = 0;
-
-       do {
-               // Fill the input buffer
-               inbuf_len = strlen(inbuf);
-               if ( (done_reading == 0) && (inbuf_len < (SIZ-128)) ) {
-
-                       ch = *inptr++;
-                       if (ch != 0) {
-                               inbuf[inbuf_len++] = ch;
-                               inbuf[inbuf_len] = 0;
-                       } 
-                       else {
-                               done_reading = 1;
-                       }
+       if (msglen == 0) {
+               msglen = strlen(inputmsg);
+       }
 
-                       ++bytes_processed;
-                       if (bytes_processed > msglen) {
-                               done_reading = 1;
-                       }
+       inbuf = strdup(inputmsg);
+       if (!inbuf) {
+               return NULL;
+       }
+
+       // "inbuf" ingests the unparsed HTML while we work with it.
+       inbuf_len = strlen(inbuf);
+       if (inbuf_len > msglen) {
+               inbuf[msglen] = 0;
+               inbuf_len = msglen;
+       }
 
+       // Do some parsing
+       if (!IsEmptyStr(inbuf)) {
+
+               // Convert newlines, carriage returns, and tabs to spaces
+               char *sp;
+               while ( (sp = strchr(inbuf, '\r'))
+                       || (sp = strchr(inbuf, '\n'))
+                       || (sp = strchr(inbuf, '\t'))
+               ) {
+                       *sp = ' ';
                }
 
-               // Do some parsing
-               if (!IsEmptyStr(inbuf)) {
-
-                   // Fold in all the spacing
-                   for (i=0; !IsEmptyStr(&inbuf[i]); ++i) {
-                       if (inbuf[i]==10) inbuf[i]=32;
-                       if (inbuf[i]==13) inbuf[i]=32;
-                       if (inbuf[i]==9) inbuf[i]=32;
-                   }
-                   for (i=0; !IsEmptyStr(&inbuf[i]); ++i) {
-                       while ((inbuf[i]==32)&&(inbuf[i+1]==32)) {
-                               strcpy(&inbuf[i], &inbuf[i+1]);
-                       }
-                   }
+               // Convert multiple spaces to a single space.
+               while (sp = strstr(inbuf, "  "), sp!=NULL) {
+                       strcpy(sp, sp+1);
+               }
 
-                   for (i=0; !IsEmptyStr(&inbuf[i]); ++i) {
+               // Run through the markup performing the conversion.
+               char *inptr = inbuf;
+               int linelen = 0;
+               while (ch = inptr[0], ch != 0) {
 
-                       ch = inbuf[i];
+                       // Keep track of how many angle brackets were found in case someone is sloppy with them
+                       // or tries to nest tags.  If nest is 0 then we are within text; if it is nonzero then we
+                       // are within a tag.
 
-                       if (ch == '<') {
-                               ++nest;
+                       if (ch == '<') {                // We have hit the beginning of a tag.
+                               ++tag_nesting_level;
+                               tag_start = inptr + 1;
                                strcpy(tag, "");
                        }
 
-                       else if (ch == '>') {   // We have a tag.
-                               if (nest > 0) --nest;
-
-                               // Unqualify the tag (truncate at first space)
-                               if (strchr(tag, ' ') != NULL) {
-                                       strcpy(strchr(tag, ' '), "");
-                               }
-                               
-                               if (!strcasecmp(tag, "P")) {
-                                       strcat(outbuf, nl);
-                                       strcat(outbuf, nl);
+                       else if (ch == '>') {           // We have hit the end of a tag.
+                               if (tag_nesting_level > 0) {
+                                       --tag_nesting_level;
                                }
+                               if (tag_nesting_level == 0) {
+                                       tag_end = inptr;
 
-                               if (!strcasecmp(tag, "/DIV")) {
-                                       strcat(outbuf, nl);
-                                       strcat(outbuf, nl);
-                               }
-
-                               if (!strcasecmp(tag, "LI")) {
-                                       strcat(outbuf, nl);
-                                       strcat(outbuf, " * ");
-                               }
-
-                               else if (!strcasecmp(tag, "/UL")) {
-                                       strcat(outbuf, nl);
-                                       strcat(outbuf, nl);
-                               }
-
-                               else if (!strcasecmp(tag, "H1")) {
-                                       strcat(outbuf, nl);
-                                       strcat(outbuf, nl);
-                               }
-
-                               else if (!strcasecmp(tag, "H2")) {
-                                       strcat(outbuf, nl);
-                                       strcat(outbuf, nl);
-                               }
-
-                               else if (!strcasecmp(tag, "H3")) {
-                                       strcat(outbuf, nl);
-                                       strcat(outbuf, nl);
-                               }
-
-                               else if (!strcasecmp(tag, "H4")) {
-                                       strcat(outbuf, nl);
-                                       strcat(outbuf, nl);
-                               }
-
-                               else if (!strcasecmp(tag, "/H1")) {
-                                       strcat(outbuf, nl);
-                               }
-
-                               else if (!strcasecmp(tag, "/H2")) {
-                                       strcat(outbuf, nl);
-                               }
-
-                               else if (!strcasecmp(tag, "/H3")) {
-                                       strcat(outbuf, nl);
-                               }
-
-                               else if (!strcasecmp(tag, "/H4")) {
-                                       strcat(outbuf, nl);
-                               }
-
-                               else if (!strcasecmp(tag, "HR")) {
-                                       strcat(outbuf, nl);
-                                       strcat(outbuf, " ");
-                                       for (j=0; j<screenwidth-2; ++j)
-                                               strcat(outbuf, "-");
-                                       strcat(outbuf, nl);
-                               }
-
-                               else if (
-                                       (!strcasecmp(tag, "B"))
-                                       || (!strcasecmp(tag, "STRONG"))
-                               ) {
-                                       if (ansi) {
-                                               strcat(outbuf, "\033[1m");
+                                       size_t tag_len = tag_end - tag_start;
+                                       if (tag_len >= sizeof(tag)) {
+                                               tag_len = sizeof(tag);
                                        }
-                               }
-                               else if (
-                                       (!strcasecmp(tag, "/B"))
-                                       || (!strcasecmp(tag, "/STRONG"))
-                               ) {
-                                       if (ansi) {
-                                               strcat(outbuf, "\033[22m");
-                                       }
-                               }
+                                       strncpy(tag, tag_start, tag_len);
+                                       tag[tag_len] = 0;
 
-                               else if (
-                                       (!strcasecmp(tag, "I"))
-                                       || (!strcasecmp(tag, "EM"))
-                               ) {
-                                       if (ansi) {
-                                               strcat(outbuf, "\033[3m");
+                                       // Unqualify the tag (truncate at first space)
+                                       char *tagsp = strchr(tag, ' ');
+                                       if (tagsp) {
+                                               *tagsp = 0;
                                        }
-                               }
 
-                               else if (
-                                       (!strcasecmp(tag, "/I"))
-                                       || (!strcasecmp(tag, "/EM"))
-                               ) {
-                                       if (ansi) {
-                                               strcat(outbuf, "\033[23m");
+                                       // IMG tag on sixel terminals -- try to display the image
+                                       if ( (!strcasecmp(tag, "img")) && sixel) {
+                                               char *q1, *q2;
+
+                                               // look for src attribute
+                                               char *src = bmstrcasestr(tag_start, "src=");
+                                               q1 = q2 = NULL;
+                                               if (src && src<tag_end) {
+                                                       if (q1 = strchr(src, '"'), q1 && q1<tag_end) {          // in double quotes
+                                                               ++q1;
+                                                               q2 = strchr(q1, '"');
+                                                       }
+                                                       else if (q1 = strchr(src, '\''), q1 && q1<tag_end) {    // in single quotes
+                                                               ++q1;
+                                                               q2 = strchr(q1, '\'');
+                                                       }
+                                                       if (q1 && q1<q2 && q2<tag_end) {
+                                                               char url[SIZ];
+                                                               memcpy(url, q1, q2-q1);
+                                                               url[q2-q1] = 0;
+                                                               h2a_embed_image(out, url, H2A_SIXEL);           // try to display
+                                                               linelen = 0;
+                                                       }
+                                               }
                                        }
-                               }
 
-                               else if (!strcasecmp(tag, "U")) {
-                                       if (ansi) {
-                                               strcat(outbuf, "\033[4m");
+                                       // IMG tag on non-sixel terminals -- we can display the alt text
+                                       if ( (!strcasecmp(tag, "img")) && !sixel) {
+                                               char *q1, *q2;
+
+                                               // look for alt text
+                                               char *alt = bmstrcasestr(tag_start, "alt=");
+                                               q1 = q2 = NULL;
+                                               if (alt && alt<tag_end) {
+                                                       if (q1 = strchr(alt, '"'), q1 && q1<tag_end) {          // in double quotes
+                                                               ++q1;
+                                                               q2 = strchr(q1, '"');
+                                                       }
+                                                       else if (q1 = strchr(alt, '\''), q1 && q1<tag_end) {    // in single quotes
+                                                               ++q1;
+                                                               q2 = strchr(q1, '\'');
+                                                       }
+                                                       if (q1 && q1<q2 && q2<tag_end) {
+                                                               StrBufAppendBufPlain(out, q1, (long)(q2-q1), 0);
+                                                       }
+                                               }
                                        }
-                               }
 
-                               else if (!strcasecmp(tag, "/U")) {
-                                       if (ansi) {
-                                               strcat(outbuf, "\033[24m");
+                                       if (!strcasecmp(tag, "P")) {
+                                               StrBufAppendBufPlain(out, nl, -1, 0);
+                                               StrBufAppendBufPlain(out, nl, -1, 0);
+                                               linelen = 0;
                                        }
-                               }
 
-                               else if (!strcasecmp(tag, "BR")) {
-                                       strcat(outbuf, nl);
-                               }
-
-                               else if (!strcasecmp(tag, "TR")) {
-                                       strcat(outbuf, nl);
-                               }
-
-                               else if (!strcasecmp(tag, "/TABLE")) {
-                                       strcat(outbuf, nl);
-                               }
-
-                               else if (!strcasecmp(tag, "BLOCKQUOTE")) {
-                                       ++blockquote;
-                                       strcpy(nl, "\n");
-                                       if ( (blockquote == 1) && (ansi) ) {
-                                               strcat(nl, "\033[2m\033[2m");
+                                       if (!strcasecmp(tag, "/DIV")) {
+                                               StrBufAppendBufPlain(out, nl, -1, 0);
+                                               StrBufAppendBufPlain(out, nl, -1, 0);
+                                               linelen = 0;
                                        }
-                                       for (j=0; j<blockquote; ++j) strcat(nl, ">");
-                                       strcat(outbuf, nl);
-                               }
 
-                               else if (!strcasecmp(tag, "/BLOCKQUOTE")) {
-                                       strcat(outbuf, "\n");
-                                       --blockquote;
-                                       if ( (blockquote == 0) && (ansi) ) {
-                                               strcat(outbuf, "\033[22m\033[22m");
+                                       if (!strcasecmp(tag, "LI")) {
+                                               StrBufAppendBufPlain(out, nl, -1, 0);
+                                               StrBufAppendBufPlain(out, HKEY(" * "), 0);
+                                               linelen = 3;
                                        }
-                                       strcpy(nl, "\n");
-                                       for (j=0; j<blockquote; ++j) strcat(nl, ">");
-                                       strcat(outbuf, nl);
-                               }
 
-                               else if (!strcasecmp(tag, "STYLE")) {
-                                       ++styletag;
-                                       if (styletag == 1) {
-                                               styletag_start = strlen(outbuf);
+                                       else if (!strcasecmp(tag, "/UL")) {
+                                               StrBufAppendBufPlain(out, nl, -1, 0);
+                                               StrBufAppendBufPlain(out, nl, -1, 0);
+                                               linelen = 0;
                                        }
-                               }
 
-                               else if (!strcasecmp(tag, "/STYLE")) {
-                                       --styletag;
-                                       if (styletag == 0) {
-                                               outbuf[styletag_start] = 0;
+                                       else if (!strcasecmp(tag, "H1")) {
+                                               StrBufAppendBufPlain(out, nl, -1, 0);
+                                               StrBufAppendBufPlain(out, nl, -1, 0);
+                                               linelen = 0;
                                        }
-                               }
-
-                       }
-
-                       else if ((nest > 0) && (strlen(tag)<(sizeof(tag)-1))) {
-                               tag[strlen(tag)+1] = 0;
-                               tag[strlen(tag)] = ch;
-                       }
-                               
-                       else if ((!nest) && (styletag == 0)) {
-                               outbuf[strlen(outbuf)+1] = 0;
-                               outbuf[strlen(outbuf)] = ch;
-                       }
-                   }
-                   strcpy(inbuf, &inbuf[i]);
-               }
-
-               // Convert &; tags to the forbidden characters
-               if (!IsEmptyStr(outbuf)) for (i=0; !IsEmptyStr(&outbuf[i]); ++i) {
-
-                       // Character entity references
-                       if (!strncasecmp(&outbuf[i], "&nbsp;", 6)) {
-                               outbuf[i] = ' ';
-                               strcpy(&outbuf[i+1], &outbuf[i+6]);
-                       }
-
-                       if (!strncasecmp(&outbuf[i], "&ensp;", 6)) {
-                               outbuf[i] = ' ';
-                               strcpy(&outbuf[i+1], &outbuf[i+6]);
-                       }
-
-                       if (!strncasecmp(&outbuf[i], "&emsp;", 6)) {
-                               outbuf[i] = ' ';
-                               strcpy(&outbuf[i+1], &outbuf[i+6]);
-                       }
 
-                       if (!strncasecmp(&outbuf[i], "&thinsp;", 8)) {
-                               outbuf[i] = ' ';
-                               strcpy(&outbuf[i+1], &outbuf[i+8]);
-                       }
-
-                       else if (!strncasecmp(&outbuf[i], "&lt;", 4)) {
-                               outbuf[i] = '<';
-                               strcpy(&outbuf[i+1], &outbuf[i+4]);
-                       }
-
-                       else if (!strncasecmp(&outbuf[i], "&gt;", 4)) {
-                               outbuf[i] = '>';
-                               strcpy(&outbuf[i+1], &outbuf[i+4]);
-                       }
-
-                       else if (!strncasecmp(&outbuf[i], "&amp;", 5)) {
-                               strcpy(&outbuf[i+1], &outbuf[i+5]);
-                       }
-
-                       else if (!strncasecmp(&outbuf[i], "&quot;", 6)) {
-                               outbuf[i] = '\"';
-                               strcpy(&outbuf[i+1], &outbuf[i+6]);
-                       }
-
-                       else if (!strncasecmp(&outbuf[i], "&lsquo;", 7)) {
-                               outbuf[i] = '`';
-                               strcpy(&outbuf[i+1], &outbuf[i+7]);
-                       }
-
-                       else if (!strncasecmp(&outbuf[i], "&rsquo;", 7)) {
-                               outbuf[i] = '\'';
-                               strcpy(&outbuf[i+1], &outbuf[i+7]);
-                       }
-
-                       else if (!strncasecmp(&outbuf[i], "&copy;", 6)) {
-                               outbuf[i] = '(';
-                               outbuf[i+1] = 'c';
-                               outbuf[i+2] = ')';
-                               strcpy(&outbuf[i+3], &outbuf[i+6]);
-                       }
-
-                       else if (!strncasecmp(&outbuf[i], "&bull;", 6)) {
-                               outbuf[i] = ' ';
-                               outbuf[i+1] = '*';
-                               outbuf[i+2] = ' ';
-                               strcpy(&outbuf[i+3], &outbuf[i+6]);
-                       }
-
-                       else if (!strncasecmp(&outbuf[i], "&hellip;", 8)) {
-                               outbuf[i] = '.';
-                               outbuf[i+1] = '.';
-                               outbuf[i+2] = '.';
-                               strcpy(&outbuf[i+3], &outbuf[i+8]);
-                       }
+                                       else if (!strcasecmp(tag, "H2")) {
+                                               StrBufAppendBufPlain(out, nl, -1, 0);
+                                               StrBufAppendBufPlain(out, nl, -1, 0);
+                                               linelen = 0;
+                                       }
 
-                       else if (!strncasecmp(&outbuf[i], "&trade;", 7)) {
-                               outbuf[i] = '(';
-                               outbuf[i+1] = 't';
-                               outbuf[i+2] = 'm';
-                               outbuf[i+3] = ')';
-                               strcpy(&outbuf[i+4], &outbuf[i+7]);
-                       }
+                                       else if (!strcasecmp(tag, "H3")) {
+                                               StrBufAppendBufPlain(out, nl, -1, 0);
+                                               StrBufAppendBufPlain(out, nl, -1, 0);
+                                               linelen = 0;
+                                       }
 
-                       else if (!strncasecmp(&outbuf[i], "&reg;", 5)) {
-                               outbuf[i] = '(';
-                               outbuf[i+1] = 'r';
-                               outbuf[i+2] = ')';
-                               strcpy(&outbuf[i+3], &outbuf[i+5]);
-                       }
+                                       else if (!strcasecmp(tag, "H4")) {
+                                               StrBufAppendBufPlain(out, nl, -1, 0);
+                                               StrBufAppendBufPlain(out, nl, -1, 0);
+                                               linelen = 0;
+                                       }
 
-                       else if (!strncasecmp(&outbuf[i], "&frac14;", 8)) {
-                               outbuf[i] = '1';
-                               outbuf[i+1] = '/';
-                               outbuf[i+2] = '4';
-                               strcpy(&outbuf[i+3], &outbuf[i+8]);
-                       }
+                                       else if (!strcasecmp(tag, "/H1")) {
+                                               StrBufAppendBufPlain(out, nl, -1, 0);
+                                               linelen = 0;
+                                       }
 
-                       else if (!strncasecmp(&outbuf[i], "&frac12;", 8)) {
-                               outbuf[i] = '1';
-                               outbuf[i+1] = '/';
-                               outbuf[i+2] = '2';
-                               strcpy(&outbuf[i+3], &outbuf[i+8]);
-                       }
+                                       else if (!strcasecmp(tag, "/H2")) {
+                                               StrBufAppendBufPlain(out, nl, -1, 0);
+                                               linelen = 0;
+                                       }
 
-                       else if (!strncasecmp(&outbuf[i], "&frac34;", 8)) {
-                               outbuf[i] = '3';
-                               outbuf[i+1] = '/';
-                               outbuf[i+2] = '4';
-                               strcpy(&outbuf[i+3], &outbuf[i+8]);
-                       }
+                                       else if (!strcasecmp(tag, "/H3")) {
+                                               StrBufAppendBufPlain(out, nl, -1, 0);
+                                               linelen = 0;
+                                       }
 
-                       else if (!strncasecmp(&outbuf[i], "&ndash;", 7)) {
-                               outbuf[i] = '-';
-                               outbuf[i+1] = '-';
-                               strcpy(&outbuf[i+2], &outbuf[i+7]);
-                       }
+                                       else if (!strcasecmp(tag, "/H4")) {
+                                               StrBufAppendBufPlain(out, nl, -1, 0);
+                                               linelen = 0;
+                                       }
 
-                       else if (!strncasecmp(&outbuf[i], "&mdash;", 7)) {
-                               outbuf[i] = '-';
-                               outbuf[i+1] = '-';
-                               outbuf[i+2] = '-';
-                               strcpy(&outbuf[i+3], &outbuf[i+7]);
-                       }
+                                       else if (!strcasecmp(tag, "HR")) {
+                                               StrBufAppendBufPlain(out, nl, -1, 0);
+                                               StrBufAppendBufPlain(out, HKEY(" "), 0);
+                                               for (j = 0; j < screenwidth - 2; ++j) {
+                                                       StrBufAppendBufPlain(out, HKEY("-"), 0);
+                                               }
+                                               StrBufAppendBufPlain(out, nl, -1, 0);
+                                               linelen = 0;
+                                       }
 
-                       else if (!strncmp(&outbuf[i], "&Ccedil;", 8)) {
-                               outbuf[i] = 'C';
-                               strcpy(&outbuf[i+1], &outbuf[i+8]);
-                       }
+                                       else if (       (!strcasecmp(tag, "B"))
+                                                       || (!strcasecmp(tag, "STRONG"))
+                                       ) {
+                                               if (ansi) {
+                                                       StrBufAppendBufPlain(out, HKEY("\033[1m"), 0);
+                                               }
+                                       }
+                                       else if (       (!strcasecmp(tag, "/B"))
+                                                       || (!strcasecmp(tag, "/STRONG"))
+                                       ) {
+                                               if (ansi) {
+                                                       StrBufAppendBufPlain(out, HKEY("\033[22m"), 0);
+                                               }
+                                       }
 
-                       else if (!strncasecmp(&outbuf[i], "&ccedil;", 8)) {
-                               outbuf[i] = 'c';
-                               strcpy(&outbuf[i+1], &outbuf[i+8]);
-                       }
+                                       else if (       (!strcasecmp(tag, "I"))
+                                                       || (!strcasecmp(tag, "EM"))
+                                       ) {
+                                               if (ansi) {
+                                                       StrBufAppendBufPlain(out, HKEY("\033[3m"), 0);
+                                               }
+                                       }
 
-                       else if (!strncmp(&outbuf[i], "&Egrave;", 8)) {
-                               outbuf[i] = 'E';
-                               strcpy(&outbuf[i+1], &outbuf[i+8]);
-                       }
+                                       else if (       (!strcasecmp(tag, "/I"))
+                                                       || (!strcasecmp(tag, "/EM"))
+                                       ) {
+                                               if (ansi) {
+                                                       StrBufAppendBufPlain(out, HKEY("\033[23m"), 0);
+                                               }
+                                       }
 
-                       else if (!strncasecmp(&outbuf[i], "&egrave;", 8)) {
-                               outbuf[i] = 'e';
-                               strcpy(&outbuf[i+1], &outbuf[i+8]);
-                       }
+                                       else if (!strcasecmp(tag, "U")) {
+                                               if (ansi) {
+                                                       StrBufAppendBufPlain(out, HKEY("\033[4m"), 0);
+                                               }
+                                       }
 
-                       else if (!strncmp(&outbuf[i], "&Ecirc;", 7)) {
-                               outbuf[i] = 'E';
-                               strcpy(&outbuf[i+1], &outbuf[i+7]);
-                       }
+                                       else if (!strcasecmp(tag, "/U")) {
+                                               if (ansi) {
+                                                       StrBufAppendBufPlain(out, HKEY("\033[24m"), 0);
+                                               }
+                                       }
 
-                       else if (!strncasecmp(&outbuf[i], "&ecirc;", 7)) {
-                               outbuf[i] = 'e';
-                               strcpy(&outbuf[i+1], &outbuf[i+7]);
-                       }
+                                       else if (!strcasecmp(tag, "BR")) {
+                                               StrBufAppendBufPlain(out, nl, -1, 0);
+                                               linelen = 0;
+                                       }
 
-                       else if (!strncmp(&outbuf[i], "&Eacute;", 8)) {
-                               outbuf[i] = 'E';
-                               strcpy(&outbuf[i+1], &outbuf[i+8]);
-                       }
+                                       else if (!strcasecmp(tag, "TR")) {
+                                               StrBufAppendBufPlain(out, nl, -1, 0);
+                                               linelen = 0;
+                                       }
 
-                       else if (!strncasecmp(&outbuf[i], "&eacute;", 8)) {
-                               outbuf[i] = 'e';
-                               strcpy(&outbuf[i+1], &outbuf[i+8]);
-                       }
+                                       else if (!strcasecmp(tag, "/TABLE")) {
+                                               StrBufAppendBufPlain(out, nl, -1, 0);
+                                               linelen = 0;
+                                       }
 
-                       else if (!strncmp(&outbuf[i], "&Agrave;", 8)) {
-                               outbuf[i] = 'A';
-                               strcpy(&outbuf[i+1], &outbuf[i+8]);
-                       }
+                                       else if (!strcasecmp(tag, "BLOCKQUOTE")) {
+                                               ++blockquote;
+                                               strcpy(nl, "\n");
+                                               if ((blockquote == 1) && (ansi)) {
+                                                       strcat(nl, "\033[2m\033[2m");
+                                               }
+                                               for (j = 0; j < blockquote; ++j) {
+                                                       strcat(nl, ">");
+                                               }
+                                               StrBufAppendBufPlain(out, nl, -1, 0);
+                                               linelen = 0;
+                                       }
 
-                       else if (!strncasecmp(&outbuf[i], "&agrave;", 8)) {
-                               outbuf[i] = 'a';
-                               strcpy(&outbuf[i+1], &outbuf[i+8]);
-                       }
+                                       else if (!strcasecmp(tag, "/BLOCKQUOTE")) {
+                                               StrBufAppendBufPlain(out, HKEY("\n"), 0);
+                                               --blockquote;
+                                               if ((blockquote == 0) && (ansi)) {
+                                                       StrBufAppendBufPlain(out, HKEY("\033[22m\033[22m"), 0);
+                                               }
+                                               strcpy(nl, "\n");
+                                               for (j = 0; j < blockquote; ++j) {
+                                                       strcat(nl, ">");
+                                               }
+                                               StrBufAppendBufPlain(out, nl, -1, 0);
+                                               linelen = 0;
+                                       }
 
-                       else if (!strncasecmp(&outbuf[i], "&ldquo;", 7)) {
-                               outbuf[i] = '\"';
-                               strcpy(&outbuf[i+1], &outbuf[i+7]);
-                       }
+                                       else if (!strcasecmp(tag, "STYLE")) {
+                                               ++styletag;
+                                       }
 
-                       else if (!strncasecmp(&outbuf[i], "&rdquo;", 7)) {
-                               outbuf[i] = '\"';
-                               strcpy(&outbuf[i+1], &outbuf[i+7]);
-                       }
+                                       else if (!strcasecmp(tag, "/STYLE")) {
+                                               --styletag;
+                                       }
+                               }
 
-                       else if (!strncasecmp(&outbuf[i], "&acute;", 7)) {
-                               outbuf[i] = '\'';
-                               strcpy(&outbuf[i+1], &outbuf[i+7]);
                        }
 
-                       else if (!strncasecmp(&outbuf[i], "&#8217;", 7)) {
-                               outbuf[i] = '\'';
-                               strcpy(&outbuf[i+1], &outbuf[i+7]);
+                       // copy non-tag text to the output buffer
+                       else if ((!tag_nesting_level) && (styletag == 0)) {
+                               StrBufAppendBufPlain(out, &ch, 1, 0);
+                               ++linelen;
                        }
 
-                       else if (!strncasecmp(&outbuf[i], "&#8211;", 7)) {
-                               outbuf[i] = '-';
-                               strcpy(&outbuf[i+1], &outbuf[i+7]);
-                       }
+                       // Handle numeric entities
+                       if (ch == ';') {
 
-                       // two-digit decimal equivalents
-                       else if (outbuf[i] == '&'       &&
-                                outbuf[i + 1] == '#'   &&
-                                isdigit(outbuf[i + 2]) && 
-                                isdigit(outbuf[i + 3]) &&
-                                (outbuf[i+4] == ';') ) 
-                       {
-                               scanch = 0;
-                               sscanf(&outbuf[i+2], "%02d", &scanch);
-                               outbuf[i] = scanch;
-                               strcpy(&outbuf[i+1], &outbuf[i+5]);
-                       }
+                               u_int32_t scanch = 0;
+                               int elen = 0;
 
-                       // three-digit decimal equivalents
-                       else if (outbuf[i] == '&'       &&
-                                outbuf[i + 1] == '#'   &&
-                                isdigit(outbuf[i + 2]) && 
-                                isdigit(outbuf[i + 3]) && 
-                                isdigit(outbuf[i + 4]) &&
-                                (outbuf[i + 5] == ';') ) 
-                       {
-                               scanch = 0;
-                               sscanf(&outbuf[i+2], "%03d", &scanch);
-                               outbuf[i] = scanch;
-                               strcpy(&outbuf[i+1], &outbuf[i+6]);
-                       }
+                               if ( (linelen >= 5) && (*(inptr-4) == '&') && (*(inptr-3) == '#') ) {
+                                       sscanf(inptr-2, "%02d", &scanch);
+                                       elen = 5;
+                               }
+                               else if ( (linelen >= 6) && (*(inptr-5) == '&') && (*(inptr-4) == '#') ) {
+                                       sscanf(inptr-3, "%03d", &scanch);
+                                       elen = 6;
+                               }
+                               else if ( (linelen >= 7) && (*(inptr-6) == '&') && (*(inptr-5) == '#') ) {
+                                       sscanf(inptr-3, "%04d", &scanch);
+                                       elen = 7;
+                               }
+                               else if ( (linelen >= 8) && (*(inptr-7) == '&') && (*(inptr-6) == '#') ) {
+                                       sscanf(inptr-4, "%05d", &scanch);
+                                       elen = 8;
+                               }
 
-                       // four-digit decimal equivalents
-                       else if (outbuf[i] == '&'       &&
-                                outbuf[i + 1] == '#'   &&
-                                isdigit(outbuf[i + 2]) && 
-                                isdigit(outbuf[i + 3]) && 
-                                isdigit(outbuf[i + 4]) &&
-                                isdigit(outbuf[i + 5]) &&
-                                (outbuf[i + 6] == ';') ) 
-                       {
-                               scanch = 0;
-                               sscanf(&outbuf[i+2], "%04d", &scanch);
-                               outbuf[i] = scanch;
-                               strcpy(&outbuf[i+1], &outbuf[i+7]);
-                       }
+                               if (scanch) {
+                                       StrBufCutRight(out, elen);
+                                       linelen -= elen;
 
-               }
+                                       char utf[5];
+                                       int ulen = u8_wc_toutf8(utf, scanch);
+                                       utf[ulen] = 0;
+                                       StrBufAppendBufPlain(out, utf, ulen, 0);
+                                       linelen += elen;
+                               }
 
-               // Make sure the output buffer is big enough
-               if ((output_len + strlen(outbuf) + SIZ) > outptr_buffer_size) {
-                       outptr_buffer_size += SIZ;
-                       outptr = realloc(outptr, outptr_buffer_size);
-                       if (outptr == NULL) {
-                               abort();
                        }
-               }
-
-               // Output any lines terminated with hard line breaks
-               do {
-                       did_out = 0;
-                       if (strlen(outbuf) > 0) {
-                           for (i = 0; i<strlen(outbuf); ++i) {
-                               if ( (i<(screenwidth-2)) && (outbuf[i]=='\n')) {
-
-                                       strncpy(&outptr[output_len], outbuf, i+1);
-                                       output_len += (i+1);
 
-                                       strcpy(outbuf, &outbuf[i+1]);
-                                       i = 0;
-                                       did_out = 1;
+                       // Add soft line breaks when necessary
+                       if (linelen > (screenwidth - 8)) {
+                               char *ptr = (char *)ChrPtr(out) + StrLength(out) - linelen;
+                               char *rightmost_space = strrchr(ptr, ' ');
+                               if (rightmost_space && rightmost_space > ptr) {
+                                       int space_pos = rightmost_space - ChrPtr(out);
+                                       StrBufReplaceToken(out, (long)space_pos, 1, nl, strlen(nl));
+                                       linelen = strlen(rightmost_space) - 1;
                                }
                        }
-                   }
-               } while (did_out);
-
-               // Add soft line breaks
-               if (strlen(outbuf) > (screenwidth - 2 )) {
-                       rb = (-1);
-                       for (i=0; i<(screenwidth-2); ++i) {
-                               if (outbuf[i]==32) rb = i;
-                       }
-                       if (rb>=0) {
-                               strncpy(&outptr[output_len], outbuf, rb);
-                               output_len += rb;
-                               strcpy(&outptr[output_len], nl);
-                               output_len += strlen(nl);
-                               strcpy(outbuf, &outbuf[rb+1]);
-                       }
-                       else {
-                               strncpy(&outptr[output_len], outbuf, screenwidth-2);
-                               output_len += (screenwidth-2);
-                               strcpy(&outptr[output_len], nl);
-                               output_len += strlen(nl);
-                               strcpy(outbuf, &outbuf[screenwidth-2]);
-                       }
-               }
-
-       } while (done_reading == 0);
 
-       strcpy(&outptr[output_len], outbuf);
-       output_len += strlen(outbuf);
-
-       // Strip leading/trailing whitespace.
+                       // Advance to the next byte of input.
+                       inptr++;
+               }
+       }
+       free(inbuf);
+
+       // Convert entity tags to printable characters
+       StrBufReplaceAllOccurrences(out, "&nbsp;", " ");
+       StrBufReplaceAllOccurrences(out, "&ensp;", " ");
+       StrBufReplaceAllOccurrences(out, "&emsp;", " ");
+       StrBufReplaceAllOccurrences(out, "&thinsp;", " ");
+       StrBufReplaceAllOccurrences(out, "&lt;", "<");
+       StrBufReplaceAllOccurrences(out, "&gt;", ">");
+       StrBufReplaceAllOccurrences(out, "&amp;", "&");
+       StrBufReplaceAllOccurrences(out, "&quot;", "\"");
+       StrBufReplaceAllOccurrences(out, "&lsquo;", "`");
+       StrBufReplaceAllOccurrences(out, "&rsquo;", "'");
+       StrBufReplaceAllOccurrences(out, "&bull;", " * ");
+       StrBufReplaceAllOccurrences(out, "&hellip;", "…");
+       StrBufReplaceAllOccurrences(out, "&copy;", "©");
+       StrBufReplaceAllOccurrences(out, "&trade;", "™");
+       StrBufReplaceAllOccurrences(out, "&reg;", "®");
+       StrBufReplaceAllOccurrences(out, "&frac14;", "¼");
+       StrBufReplaceAllOccurrences(out, "&frac12;", "½");
+       StrBufReplaceAllOccurrences(out, "&frac34;", "¾");
+       StrBufReplaceAllOccurrences(out, "&ndash;", "–");
+       StrBufReplaceAllOccurrences(out, "&mdash;", "—");
+       StrBufReplaceAllOccurrences(out, "&Ccedil;", "Ç");
+       StrBufReplaceAllOccurrences(out, "&ccedil;", "ç");
+       StrBufReplaceAllOccurrences(out, "&Egrave;", "È");
+       StrBufReplaceAllOccurrences(out, "&egrave;", "è");
+       StrBufReplaceAllOccurrences(out, "&Ecirc;", "Ê");
+       StrBufReplaceAllOccurrences(out, "&ecirc;", "ê");
+       StrBufReplaceAllOccurrences(out, "&Eacute;", "É");
+       StrBufReplaceAllOccurrences(out, "&eacute;", "é");
+       StrBufReplaceAllOccurrences(out, "&Agrave;", "À");
+       StrBufReplaceAllOccurrences(out, "&agrave;", "à");
+       StrBufReplaceAllOccurrences(out, "&ldquo;", "\"");
+       StrBufReplaceAllOccurrences(out, "&rdquo;", "\"");
+       StrBufReplaceAllOccurrences(out, "&acute;", "'");
+       StrBufReplaceAllOccurrences(out, "&#8217;", "'");
+       StrBufReplaceAllOccurrences(out, "&#8211;", "-");
+
+       // Convert from a StrBuf to a plain C string
+       int output_len = StrLength(out);
+       outptr = SmashStrBuf(&out);
+
+       // Strip leading whitespace
        while ((output_len > 0) && (isspace(outptr[0]))) {
                strcpy(outptr, &outptr[1]);
                --output_len;
        }
-       while ((output_len > 0) && (isspace(outptr[output_len-1]))) {
-               outptr[output_len-1] = 0;
+
+       // Strip trailing whitespace
+       while ((output_len > 0) && (isspace(outptr[output_len - 1]))) {
+               outptr[output_len - 1] = 0;
                --output_len;
        }
 
        // Make sure the final line ends with a newline character.
-       if ((output_len > 0) && (outptr[output_len-1] != '\n')) {
+       if ((output_len > 0) && (outptr[output_len - 1] != '\n')) {
                strcat(outptr, "\n");
                ++output_len;
        }
 
        return outptr;
-
 }
index 655ebe4452baf6cb5f15653c923d03cffdb4201e..9601f0dc10e65242c125e7ecf6fe974b5cc7eb60 100644 (file)
@@ -252,6 +252,7 @@ int StrBufReplaceToken(StrBuf *Buf, long where, long HowLong, const char *Repl,
 int StrBufExtract_tokenFromStr(StrBuf *dest, const char *Source, long SourceLen, int parmnum, char separator);
 int StrBufExtract_token(StrBuf *dest, const StrBuf *Source, int parmnum, char separator);
 int StrBufSub(StrBuf *dest, const StrBuf *Source, unsigned long Offset, size_t nChars);
+int StrBufReplaceAllOccurrences(StrBuf *Buf, const char *fromstr, const char *tostr);
 
 unsigned long StrBufExtract_unsigned_long(const StrBuf* Source, int parmnum, char separator);
 long StrBufExtract_long(const StrBuf* Source, int parmnum, char separator);
@@ -430,9 +431,15 @@ void CtdlMakeTempFileName(char *name, int len);
 char *rfc2047encode(const char *line, long length);
 int is_msg_in_mset(const char *mset, long msgnum);
 int pattern2(char *search, char *patn);
-char *html_to_ascii(const char *inputmsg, int msglen, int screenwidth, int ansi);
 void LoadEntityList(char *FileName);
 void utf8ify_rfc822_string(char *buf);
+int u8_wc_toutf8(char *dest, u_int32_t ch);
+void h2a_embed_image(StrBuf *out, char *url, int display_protocol);
+
+// flags for html_to_ascii
+#define H2A_ANSI       0x01            // it is acceptable to display ANSI graphics on this terminal
+#define H2A_SIXEL      0x02            // sixel graphics are supported (not yet fully implemented)
+char *html_to_ascii(const char *inputmsg, int msglen, int screenwidth, unsigned int flags);
 
 
 typedef struct {
index 7f136deb05155c3f509e498964802ba943134315..fb1eb14266f2c803216f93f76f1a58bd694a49ee 100644 (file)
@@ -913,7 +913,7 @@ void StrBufCutLeft(StrBuf *Buf, int nChars) {
 
 // Cut the trailing n Chars from the string
 // Buf Buffer to modify
-// nChars how many chars should be trunkated?
+// nChars how many chars should be truncated?
 void StrBufCutRight(StrBuf *Buf, int nChars) {
        if ((Buf == NULL) || (Buf->BufUsed == 0) || (Buf->buf == NULL))
                return;
@@ -929,7 +929,7 @@ void StrBufCutRight(StrBuf *Buf, int nChars) {
 
 //  Cut the string after n Chars
 //  Buf Buffer to modify
-//  AfternChars after how many chars should we trunkate the string?
+//  AfternChars after how many chars should we truncate the string?
 //  At if non-null and points inside of our string, cut it there.
 void StrBufCutAt(StrBuf *Buf, int AfternChars, const char *At) {
        if ((Buf == NULL) || (Buf->BufUsed == 0)) return;
@@ -1065,6 +1065,19 @@ int StrBufReplaceToken(StrBuf *Buf, long where, long HowLong, const char *Repl,
        return Buf->BufUsed;
 }
 
+
+int StrBufReplaceAllOccurrences(StrBuf *Buf, const char *fromstr, const char *tostr) {
+
+       char *found;
+       while (found = strstr(Buf->buf, fromstr), (found != NULL)) {
+               StrBufReplaceToken(Buf, (found - Buf->buf), strlen(fromstr), tostr, strlen(tostr));
+       }
+
+       return Buf->BufUsed;
+}
+
+
+
 //  Counts the numbmer of tokens in a buffer
 //  source String to count tokens in
 //  tok    Tokenizer char to count
index 88fca9d5be9ae7b12d70933c1c9fc20699c4138e..72abda458c583c30ab106acc528598a7bc3434f6 100644 (file)
@@ -2,7 +2,7 @@
 This is a text mode user interface for the Citadel system.  It presents
 a Citadel site to users in the form of a traditional BBS.
 
-All code is Copyright (c) 1987-2022 by the citadel.org team.
+All code is Copyright (c) 1987-2024 by the citadel.org team.
 
 This program is open source software.  Use, duplication, and/or
 disclosure are subject to the GNU General Purpose License, version 3.
index ba1be6cb3ce5ce958e1fdc893543fbba34ef943a..b5682cfb28dea9fd065d4ffc9a7c59dc8db23cd2 100644 (file)
@@ -52,6 +52,7 @@ 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 rc_sixel;           /* sixel graphics value from citadel.rc */
 extern int next_lazy_cmd;
 
 CtdlIPC *ipc_for_signal_handlers;      /* KLUDGE cover your eyes */
index 7f184dbcf4319fbc7309917d491a4443bc317006..7db4f04ffd4bb0106089fb388e029762b11aade6 100644 (file)
@@ -34,7 +34,14 @@ encrypt=default
 # 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
+ansi_color=auto
+
+# Sixel graphics support (experimental and probably insecure)
+# Set this to "on" to output images embedded in HTML messages to the terminal
+# in Sixel graphics format.  This obviously requires a terminal that has
+# support for Sixel.  It also requires the "curl", "img2sixel", and "fold"
+# commands to be available on your system.
+use_sixel=off
 
 # 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,
index 3b3cb45939e4aab085fb14f92f786734643b1e58..c426a29e0ad0092abfbda6fed9e2b542e7015992 100644 (file)
@@ -8,7 +8,7 @@
 static SSL_CTX *ssl_ctx;
 char arg_encrypt;
 char rc_encrypt;
-#endif                         /* HAVE_OPENSSL */
+#endif                         // HAVE_OPENSSL
 
 #ifndef INADDR_NONE
 #define INADDR_NONE 0xffffffff
@@ -136,7 +136,7 @@ static void serv_write(CtdlIPC * ipc, const char *buf, unsigned int nbytes);
 static void serv_read_ssl(CtdlIPC * ipc, char *buf, unsigned int bytes);
 static void serv_write_ssl(CtdlIPC * ipc, const char *buf, unsigned int nbytes);
 static void endtls(SSL * ssl);
-#endif                         /* HAVE_OPENSSL */
+#endif                         // HAVE_OPENSSL
 static void CtdlIPC_getline(CtdlIPC * ipc, char *buf);
 static void CtdlIPC_putline(CtdlIPC * ipc, const char *buf);
 
@@ -144,32 +144,29 @@ 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) {
+// 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.
- */
+// Does nothing interesting.  The server should always return 200 along with your string.
 int CtdlIPCEcho(CtdlIPC * ipc, const char *arg, char *cret) {
        int ret;
        char *aaa;
 
-       if (!arg)
+       if (!arg) {
                return -2;
-       if (!cret)
+       }
+       if (!cret) {
                return -2;
+       }
 
        aaa = (char *) malloc((size_t) (strlen(arg) + 6));
-       if (!aaa)
+       if (!aaa) {
                return -1;
+       }
 
        sprintf(aaa, "ECHO %s", arg);
        ret = CtdlIPCGenericCommand(ipc, aaa, NULL, 0, NULL, NULL, cret);
@@ -178,12 +175,9 @@ int CtdlIPCEcho(CtdlIPC * ipc, const char *arg, char *cret) {
 }
 
 
-/*
- * Asks the server to close the connecction.
- * Should always return 200.
- */
+// Asks the server to close the connection.  Should always return 200.
 int CtdlIPCQuit(CtdlIPC * ipc) {
-       int ret = 221;          /* Default to successful quit */
+       int ret = 221;          // Default to successful quit
        char aaa[SIZ];
 
        CtdlIPC_lock(ipc);
@@ -193,22 +187,22 @@ int CtdlIPCQuit(CtdlIPC * ipc) {
                ret = atoi(aaa);
        }
 #ifdef HAVE_OPENSSL
-       if (ipc->ssl)
+       if (ipc->ssl) {
                SSL_shutdown(ipc->ssl);
+       }
        ipc->ssl = NULL;
 #endif
-       if (ipc->sock)
-               shutdown(ipc->sock, 2); /* Close connection; we're dead */
+       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!
- */
+// Asks the server to log out.  Should always return 200, even if no user
+// was logged in.  The user will not be logged in after this!
 int CtdlIPCLogout(CtdlIPC * ipc) {
        int ret;
        char aaa[SIZ];
@@ -222,23 +216,24 @@ int CtdlIPCLogout(CtdlIPC * ipc) {
 }
 
 
-/*
- * 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.
- */
+// First stage of authentication - pass the username.  Returns 300 if the
+// username is able to log in, with the username correctly spelled in cret.
+// Returns various 500 error codes if the user doesn't exist, etc.
 int CtdlIPCTryLogin(CtdlIPC * ipc, const char *username, char *cret) {
        int ret;
        char *aaa;
 
-       if (!username)
+       if (!username) {
                return -2;
-       if (!cret)
+       }
+       if (!cret) {
                return -2;
+       }
 
        aaa = (char *) malloc((size_t) (strlen(username) + 6));
-       if (!aaa)
+       if (!aaa) {
                return -1;
+       }
 
        sprintf(aaa, "USER %s", username);
        ret = CtdlIPCGenericCommand(ipc, aaa, NULL, 0, NULL, NULL, cret);
@@ -247,22 +242,23 @@ int CtdlIPCTryLogin(CtdlIPC * ipc, const char *username, char *cret) {
 }
 
 
-/*
- * Second stage of authentication - provide password.  The server returns
- * 200 and several arguments in cret relating to the user's account.
- */
+// Second stage of authentication - provide password.  The server returns
+// 200 and several arguments in cret relating to the user's account.
 int CtdlIPCTryPassword(CtdlIPC * ipc, const char *passwd, char *cret) {
        int ret;
        char *aaa;
 
-       if (!passwd)
+       if (!passwd) {
                return -2;
-       if (!cret)
+       }
+       if (!cret) {
                return -2;
+       }
 
        aaa = (char *) malloc((size_t) (strlen(passwd) + 6));
-       if (!aaa)
+       if (!aaa) {
                return -1;
+       }
 
        sprintf(aaa, "PASS %s", passwd);
        ret = CtdlIPCGenericCommand(ipc, aaa, NULL, 0, NULL, NULL, cret);
@@ -271,25 +267,26 @@ int CtdlIPCTryPassword(CtdlIPC * ipc, const char *passwd, char *cret) {
 }
 
 
-/*
- * 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.
- */
+// Create a new user.  This returns 200 plus the same arguments as TryPassword
+// if selfservice is nonzero, unless there was a problem creating the account.
+// If selfservice is zero, creates a new user but does not log out the existing
+// user - intended for use by system administrators to create accounts on
+// behalf of other users.
 int CtdlIPCCreateUser(CtdlIPC * ipc, const char *username, int selfservice, char *cret) {
        int ret;
        char *aaa;
 
-       if (!username)
+       if (!username) {
                return -2;
-       if (!cret)
+       }
+       if (!cret) {
                return -2;
+       }
 
        aaa = (char *) malloc((size_t) (strlen(username) + 6));
-       if (!aaa)
+       if (!aaa) {
                return -1;
+       }
 
        sprintf(aaa, "%s %s", selfservice ? "NEWU" : "CREU", username);
        ret = CtdlIPCGenericCommand(ipc, aaa, NULL, 0, NULL, NULL, cret);
@@ -298,21 +295,22 @@ int CtdlIPCCreateUser(CtdlIPC * ipc, const char *username, int selfservice, char
 }
 
 
-/*
- * Changes the user's password.  Returns 200 if changed, errors otherwise.
- */
+// Changes the user's password.  Returns 200 if changed, errors otherwise.
 int CtdlIPCChangePassword(CtdlIPC * ipc, const char *passwd, char *cret) {
        int ret;
        char *aaa;
 
-       if (!passwd)
+       if (!passwd) {
                return -2;
-       if (!cret)
+       }
+       if (!cret) {
                return -2;
+       }
 
        aaa = (char *) malloc((size_t) (strlen(passwd) + 6));
-       if (!aaa)
+       if (!aaa) {
                return -1;
+       }
 
        sprintf(aaa, "SETP %s", passwd);
        ret = CtdlIPCGenericCommand(ipc, aaa, NULL, 0, NULL, NULL, cret);
@@ -321,13 +319,13 @@ int CtdlIPCChangePassword(CtdlIPC * ipc, const char *passwd, char *cret) {
 }
 
 
-/* LKRN */
+// LKRN
 
-/* Caller must free the march list */
+// Caller must free the march list
 
-/* Room types are defined in enum RoomList; keep these in sync! */
+// Room types are defined in enum RoomList; keep these in sync!
 
-/* floor is -1 for all, or floornum */
+// floor is -1 for all, or floornum
 int CtdlIPCKnownRooms(CtdlIPC * ipc, enum RoomList which, int floor, struct march **listing, char *cret) {
        int ret;
        struct march *march = NULL;
@@ -339,12 +337,12 @@ int CtdlIPCKnownRooms(CtdlIPC * ipc, enum RoomList which, int floor, struct marc
        if (!listing)
                return -2;
        if (*listing)
-               return -2;      /* Free the listing first */
+               return -2;      // Free the listing first
        if (!cret)
                return -2;
-       /* if (which < 0 || which > 4) return -2; */
+       // if (which < 0 || which > 4) return -2;
        if (floor < -1)
-               return -2;      /* Can't validate upper bound, sorry */
+               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);
@@ -551,7 +549,7 @@ int CtdlIPCGetMessages(CtdlIPC * ipc, enum MessageList which, int whicharg, cons
 }
 
 
-/* MSG0, MSG2 */
+/* MSG0, MSG2, MSG4 */
 int CtdlIPCGetSingleMessage(CtdlIPC * ipc, long msgnum, int headers, int as_mime, struct ctdlipcmessage **mret, char *cret) {
        int ret;
        char aaa[SIZ];
@@ -2570,10 +2568,15 @@ int CtdlIPCWriteUpload(CtdlIPC * ipc, FILE * uploadFP, void (*progress_gauge_cal
  * 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) {
+int CtdlIPCGenericCommand(
+       CtdlIPC * ipc,
+       const char *command,
+       const char *to_send,
+       size_t bytes_to_send,
+       char **to_receive,
+       size_t *bytes_to_receive,
+       char *proto_response
+) {
        char buf[SIZ];
        int ret;
 
@@ -3023,15 +3026,24 @@ static void CtdlIPC_getline(CtdlIPC * ipc, char *buf) {
                }
 
                /* If we got a long line, discard characters until the newline. */
-               if (i == (SIZ - 1))
-                       while (buf[i] != '\n')
+               if (i == (SIZ - 1)) {
+
+
+                       abort();
+
+
+                       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)
+               if (i >= 0 && buf[i] == 10) {
                        buf[i--] = 0;
-               if (i >= 0 && buf[i] == 13)
+               }
+               if (i >= 0 && buf[i] == 13) {
                        buf[i--] = 0;
+               }
        }
        else
 #endif
index c241f014d70a08c55c24e164cb0df6b889564044..dfc3f0601892736373c41d53bf1f57e7d3177c30 100644 (file)
@@ -423,6 +423,7 @@ int rc_display_message_numbers;
 int rc_force_mail_prompts;
 int rc_remember_passwords;
 int rc_ansi_color;
+int rc_sixel;
 int rc_color_use_bg;
 int rc_prompt_control = 0;
 time_t rc_idle_threshold = (time_t) 900;
@@ -439,10 +440,10 @@ 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
+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
@@ -483,7 +484,7 @@ void display_instant_messages(void) {
        char sender[64];
        char node[64];
        char *listing = NULL;
-       int r;                  // IPC result code
+       int r;                                  // IPC result code
 
        if (instant_msgs == 0) {
                return;
@@ -745,7 +746,7 @@ int ctdl_getline(char *string, int lim, int noshow, int bs) {
        while (1) {
                ch = inkey();
 
-               if ((ch == 8) && (pos > 0)) {   // backspace
+               if ((ch == 8) && (pos > 0)) {                   // backspace
                        --pos;
                        scr_putc(8);
                        scr_putc(32);
@@ -756,7 +757,7 @@ int ctdl_getline(char *string, int lim, int noshow, int bs) {
                        return (-1);
                }
 
-               else if ((ch == 23) && (pos > 0)) {     // Ctrl-W deletes a word
+               else if ((ch == 23) && (pos > 0)) {             // Ctrl-W deletes a word
                        while ((pos > 0) && !isspace(string[pos])) {
                                --pos;
                                scr_putc(8);
@@ -777,7 +778,7 @@ int ctdl_getline(char *string, int lim, int noshow, int bs) {
                        return (pos);
                }
 
-               else if (isprint(ch)) { // payload characters
+               else if (isprint(ch)) {                         // payload characters
                        scr_putc((noshow ? '*' : ch));
                        string[pos] = ch;
                        ++pos;
@@ -860,8 +861,9 @@ void newprompt(char *prompt, char *str, int len) {
 int lkey(void) {
        int a;
        a = inkey();
-       if (isupper(a))
+       if (isupper(a)) {
                a = tolower(a);
+       }
        return (a);
 }
 
@@ -889,6 +891,7 @@ void load_command_set(void) {
        rc_display_message_numbers = 0;
        rc_force_mail_prompts = 0;
        rc_ansi_color = 0;
+       rc_sixel = 0;
        rc_color_use_bg = 0;
        strcpy(rc_url_cmd, "");
        strcpy(rc_open_cmd, "");
@@ -997,6 +1000,10 @@ void load_command_set(void) {
                        if (!strncasecmp(&buf[12], "on", 2))
                                enable_status_line = 1;
                }
+               if (!strncasecmp(buf, "use_sixel=", 10)) {
+                       if (!strncasecmp(&buf[10], "on", 2))
+                               rc_sixel = 1;
+               }
                if (!strncasecmp(buf, "use_background=", 15)) {
                        if (!strncasecmp(&buf[15], "on", 2))
                                rc_color_use_bg = 9;
@@ -1177,10 +1184,12 @@ int getcmd(CtdlIPC * ipc, char *argbuf) {
 
        // Switch color support on or off if we're in user mode
        if (rc_ansi_color == 3) {
-               if (userflags & US_COLOR)
+               if (userflags & US_COLOR) {
                        enable_color = 1;
-               else
+               }
+               else {
                        enable_color = 0;
+               }
        }
 
        // if we're running in idiot mode, display a cute little menu
@@ -1270,13 +1279,13 @@ int getcmd(CtdlIPC * ipc, char *argbuf) {
 
                                // 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)
-                                   ) {
+                               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;
                                }
 
@@ -1286,7 +1295,6 @@ int getcmd(CtdlIPC * ipc, char *argbuf) {
                                }
 
                                return (cptr->c_cmdnum);
-
                        }
                }
 
@@ -1322,7 +1330,7 @@ int getcmd(CtdlIPC * ipc, char *argbuf) {
 // set tty modes.  commands are:
 // 
 // 01- set to Citadel mode
-// 2 - save current settings for later restoral
+// 2 - save current settings for later restore
 // 3 - restore saved settings
 void stty_ctdl(int cmd) {
        struct termios live;
@@ -1388,10 +1396,11 @@ void display_help(CtdlIPC * ipc, char *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
+       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
@@ -1574,11 +1583,12 @@ void color(int colornum) {
                // 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)
+               if (colornum == ORIGINAL_PAIR) {
                        printf("\033[0;39;49m");
-               else
+               }
+               else {
                        printf("\033[%d;3%d;4%dm", (colornum & 8) ? 1 : 0, (colornum & 7), rc_color_use_bg);
-
+               }
        }
 }
 
@@ -1601,6 +1611,7 @@ void send_ansi_detect(void) {
 }
 
 
+// Turn ANSI color (etc.) support on, or off, or auto-detect support, depending on the configuration
 void look_for_ansi(void) {
        fd_set rfds;
        struct timeval tv;
@@ -1608,21 +1619,19 @@ void look_for_ansi(void) {
        time_t now;
        int a, rv;
 
-       if (rc_ansi_color == 0) {
+       if (rc_ansi_color == 0) {                       // Configured to never use color
                enable_color = 0;
        }
-       else if (rc_ansi_color == 1) {
+       else if (rc_ansi_color == 1) {                  // Configured to always use color
                enable_color = 1;
        }
-       else if (rc_ansi_color == 2) {
-
-               /* otherwise, do the auto-detect */
+       else if (rc_ansi_color == 2) {                  // Configured to auto-detect ANSI color support
 
                strcpy(abuf, "");
-
                time(&now);
-               if ((now - AnsiDetect) < 2)
+               if ((now - AnsiDetect) < 2) {
                        sleep(1);
+               }
 
                do {
                        FD_ZERO(&rfds);
@@ -1642,8 +1651,10 @@ void look_for_ansi(void) {
                } while (FD_ISSET(0, &rfds));
 
                for (a = 0; !IsEmptyStr(&abuf[a]); ++a) {
-                       if ((abuf[a] == 27) && (abuf[a + 1] == '[')
-                           && (abuf[a + 2] == '?')) {
+                       if (    (abuf[a] == 27)
+                               && (abuf[a + 1] == '[')
+                               && (abuf[a + 2] == '?')
+                       ) {                                     // ANSI support was detected!
                                enable_color = 1;
                        }
                }
index 343d2064fedad6b06315e1b1baa4fdf04d1271ca..2b7e8a3a1ad97ce82db53cb5d1ffb87d2bf01adf 100644 (file)
@@ -4,7 +4,7 @@
 // late 1980s when my coding style was absolute garbage.  It
 // works, but we probably should replace most of it.
 //
-// Copyright (c) 1987-2022 by the citadel.org team
+// Copyright (c) 1987-2024 by the citadel.org team
 //
 // This program is open source software.  Use, duplication, or disclosure is subject to the GNU General Public License version 3.
 
@@ -53,13 +53,14 @@ extern char printcmd[];
 extern int rc_allow_attachments;
 extern int rc_display_message_numbers;
 extern int rc_force_mail_prompts;
+extern int rc_sixel;
 extern int editor_pid;
-extern CtdlIPC *ipc_for_signal_handlers;       /* KLUDGE cover your eyes */
+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 */
+int has_images = 0;                            // Current msg has images
+struct parts *last_message_parts = NULL;       // Parts from last msg
 
 
 void ka_sigcatch(int signum) {
@@ -69,9 +70,7 @@ void ka_sigcatch(int signum) {
 }
 
 
-/*
- * server keep-alive version of wait() (needed for external editor)
- */
+// server keep-alive version of wait() (needed for external editor)
 pid_t ka_wait(int *kstatus) {
        pid_t p;
 
@@ -87,9 +86,7 @@ pid_t ka_wait(int *kstatus) {
 }
 
 
-/*
- * version of system() that uses ka_wait()
- */
+// version of system() that uses ka_wait()
 int ka_system(char *shc) {
        pid_t childpid;
        pid_t waitpid;
@@ -119,9 +116,7 @@ int ka_system(char *shc) {
 }
 
 
-/*
- * add a newline to the buffer...
- */
+// add a newline to the buffer...
 void add_newline(struct cittext *textlist) {
        struct cittext *ptr;
 
@@ -140,9 +135,7 @@ void add_newline(struct cittext *textlist) {
 }
 
 
-/*
- * add a word to the buffer...
- */
+// add a word to the buffer...
 void add_word(struct cittext *textlist, char *wordbuf) {
        struct cittext *ptr;
 
@@ -163,9 +156,7 @@ void add_word(struct cittext *textlist, char *wordbuf) {
 }
 
 
-/*
- * begin editing of an opened file pointed to by fp
- */
+// begin editing of an opened file pointed to by fp
 void citedit(FILE * fp) {
        int a, prev, finished, b, last_space;
        int appending = 0;
@@ -174,7 +165,7 @@ void citedit(FILE * fp) {
        char wordbuf[MAXWORDBUF];
        int rv = 0;
 
-       /* first, load the text into the buffer */
+       // first, load the text into the buffer
        fseek(fp, 0L, 0);
        textlist = (struct cittext *) malloc(sizeof(struct cittext));
        textlist->next = NULL;
@@ -204,7 +195,7 @@ void citedit(FILE * fp) {
                prev = a;
        }
 
-       /* get text */
+       // get text
        finished = 0;
        prev = (appending ? 13 : (-1));
        strcpy(wordbuf, "");
@@ -293,7 +284,7 @@ void citedit(FILE * fp) {
                prev = a;
        } while (finished == 0);
 
-       /* write the buffer back to disk */
+       // write the buffer back to disk
        fseek(fp, 0L, 0);
        for (ptr = textlist; ptr != NULL; ptr = ptr->next) {
                fprintf(fp, "%s", ptr->text);
@@ -305,7 +296,7 @@ void citedit(FILE * fp) {
                scr_printf("failed to set message buffer: %s\n", strerror(errno));
 
 
-       /* and deallocate the memory we used */
+       // and deallocate the memory we used
        while (textlist != NULL) {
                ptr = textlist->next;
                free(textlist);
@@ -314,9 +305,7 @@ void citedit(FILE * fp) {
 }
 
 
-/*
- * Free the struct parts
- */
+// Free the struct parts
 void free_parts(struct parts *p) {
        struct parts *a_part = p;
 
@@ -330,11 +319,9 @@ void free_parts(struct parts *p) {
 }
 
 
-/*
- * This is a mini RFC2047 decoder.
- * It only handles strings encoded from UTF-8 as Quoted-printable.
- * We can do this "in place" because the converted string will always be smaller than the source string.
- */
+// This is a mini RFC2047 decoder.
+// It only handles strings encoded from UTF-8 as Quoted-printable.
+// We can do this "in place" because the converted string will always be smaller than the source string.
 void mini_2047_decode(char *s) {
        if (!s) {               // no null strings allowed!
                return;
@@ -389,20 +376,19 @@ void mini_2047_decode(char *s) {
 }
 
 
-/*
- * 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 */
-    ) {
+// Read a message from the server
+int read_message(CtdlIPC *ipc,
+       long num,       // message number
+       int pagin,      // 0 = normal read, 1 = read with pagination, 2 = header
+       FILE *dest      // Destination file, NULL for screen
+) {
        char buf[SIZ];
        char now[256];
        int format_type = 0;
        int fr = 0;
        int nhdr = 0;
        struct ctdlipcmessage *message = NULL;
-       int r;                  /* IPC response code */
+       int r;                  // IPC response code
        char *converted_text = NULL;
        char *lineptr;
        char *nextline;
@@ -444,7 +430,7 @@ int read_message(CtdlIPC * ipc, long num,   /* message number */
                color(BRIGHT_CYAN);
        }
 
-       /* View headers only */
+       // 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);
@@ -594,18 +580,18 @@ int read_message(CtdlIPC * ipc, long num, /* message number */
                }
        }
 
-       if (pagin == 1 && !dest) {
+       //if (pagin == 1 && !dest) {
+       if (!dest) {
                color(BRIGHT_WHITE);
        }
 
-       /******* end of header output, start of message text output *******/
+       // ****** end of header output, start of message text output ******
 
-       /*
-        * Convert HTML to plain text, formatting for the actual width
-        * of the client screen.
-        */
+       // 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, (enable_color ? 1 : 0));
+               converted_text = html_to_ascii(message->text, 0, screenwidth,
+                       ((enable_color ? H2A_ANSI : 0) | (rc_sixel ? H2A_SIXEL : 0))
+               );
                if (converted_text != NULL) {
                        free(message->text);
                        message->text = converted_text;
@@ -613,24 +599,24 @@ int read_message(CtdlIPC * ipc, long num, /* message number */
                }
        }
 
-       /* Text/plain is a different type */
+       // Text/plain is a different type
        if (!strcasecmp(message->content_type, "text/plain")) {
                format_type = 1;
        }
 
-       /* Render text/x-markdown as plain text */
+       // Render text/x-markdown as plain text
        if (!strcasecmp(message->content_type, "text/x-markdown")) {
                format_type = 1;
        }
 
-       /* Extract URL's */
+       // Extract URL's
        static char *urlprefixes[] = {
                "http://",
                "https://",
                "ftp://"
        };
        int p = 0;
-       num_urls = 0;           /* Start with a clean slate */
+       num_urls = 0;           // Start with a clean slate
        for (p = 0; p < (sizeof urlprefixes / sizeof(char *)); ++p) {
                searchptr = message->text;
                while ((searchptr != NULL) && (num_urls < MAXURLS)) {
@@ -650,17 +636,14 @@ int read_message(CtdlIPC * ipc, long num, /* message number */
                }
        }
 
-       /*
-        * Here we go
-        */
+       // Here we go
        if (format_type == 0) {
+               // renderer for legacy Citadel format
                fr = fmout(screenwidth, NULL, message->text, dest, 1);
        }
        else {
-               /* renderer for text/plain */
-
+               // renderer for text/plain
                lineptr = message->text;
-
                do {
                        nextline = strchr(lineptr, '\n');
                        if (nextline != NULL) {
@@ -700,18 +683,18 @@ int read_message(CtdlIPC * ipc, long num, /* message number */
                }
        }
 
-       /* Enumerate any attachments */
+       // 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))
-                                   ) {
+                       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);
@@ -730,23 +713,22 @@ int read_message(CtdlIPC * ipc, long num, /* message number */
                }
        }
 
-       /* Save the attachments info for later */
+       // Save the attachments info for later
        last_message_parts = message->attachments;
 
-       /* Now we're done */
+       // Now we're done
        free(message->text);
        free(message);
 
-       if (pagin == 1 && !dest)
+       if (pagin == 1 && !dest) {
                color(DIM_WHITE);
+       }
        stty_ctdl(0);
        return (fr);
 }
 
 
-/*
- * replace string function for the built-in editor
- */
+// replace string function for the built-in editor
 void replace_string(char *filename, long int startpos) {
        char buf[512];
        char srch_str[128];
@@ -791,7 +773,7 @@ void replace_string(char *filename, long int startpos) {
                        rv = fwrite((char *) buf, 128, 1, fp);
                        if (rv < 0) {
                                scr_printf("failed to replace string: %s\n", strerror(errno));
-                               break;  /*whoopsi! */
+                               break;          // No replacement happened; break out of the loop
                        }
                        strcpy(buf, &buf[128]);
                        wpos = ftell(fp);
@@ -810,10 +792,15 @@ void replace_string(char *filename, long int startpos) {
 
 
 // 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) {
+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;
@@ -875,7 +862,7 @@ int client_make_message(CtdlIPC * ipc, char *filename,      // temporary file name
                }
        }
 
-      ME1:switch (mode) {
+ME1:   switch (mode) {
 
        case 0:
                fp = fopen(filename, "r+");
@@ -911,8 +898,8 @@ int client_make_message(CtdlIPC * ipc, char *filename,      // temporary file name
                break;
 
        case 2:
-       default:                /* allow 2+ modes */
-               e_ex_code = 1;  /* start with a failed exit code */
+       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);
@@ -1001,9 +988,7 @@ int client_make_message(CtdlIPC * ipc, char *filename,     // temporary file name
 }
 
 
-/*
- * Make sure there's room in msg_arr[] for at least one more.
- */
+// 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;
@@ -1012,10 +997,7 @@ void check_msg_arr_size(void) {
 }
 
 
-/*
- * break_big_lines()  -  break up lines that are >1024 characters
- *                       otherwise the server will truncate
- */
+// break_big_lines()  -  break up lines that are >1024 characters, otherwise the server will truncate them.
 void break_big_lines(char *msg) {
        char *ptr;
        char *break_here;
@@ -1036,14 +1018,13 @@ void break_big_lines(char *msg) {
 }
 
 
-/*
- * 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? */
-    ) {
+// 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;
@@ -1053,15 +1034,13 @@ int entmsg(CtdlIPC * ipc, int is_reply, /* nonzero if this was a <R>eply command
        char subject[SIZ];
        struct ctdlipcmessage message;
        unsigned long *msgarr = NULL;
-       int r;                  /* IPC response code */
+       int r;                  // IPC response code
        int subject_required = 0;
 
-       /*
-        * 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.
-        */
+       // 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.
        if (entmsg_ok == ENTMSG_OK_YES) {
-               /* no problem, go right ahead */
+               // no problem, go right ahead
        }
        else if (entmsg_ok == ENTMSG_OK_BLOG) {
                if (!is_reply) {
@@ -1092,7 +1071,7 @@ int entmsg(CtdlIPC * ipc, int is_reply,   /* nonzero if this was a <R>eply command
        strcpy(message.author, "");
        strcpy(message.subject, "");
        strcpy(message.references, "");
-       message.text = "";      /* point to "", changes later */
+       message.text = "";      // point to "", changes later
        message.anonymous = 0;
        message.type = mode;
 
@@ -1111,9 +1090,8 @@ int entmsg(CtdlIPC * ipc, int is_reply,   /* nonzero if this was a <R>eply command
                        }
                }
 
-               /* Trim down excessively long lists of thread references.  We eliminate the
-                * second one in the list so that the thread root remains intact.
-                */
+               // 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)) {
@@ -1121,7 +1099,8 @@ int entmsg(CtdlIPC * ipc, int is_reply,   /* nonzero if this was a <R>eply command
                }
 
                snprintf(message.references, sizeof message.references, "%s%s%s",
-                        reply_references, (IsEmptyStr(reply_references) ? "" : "|"), reply_inreplyto);
+                        reply_references, (IsEmptyStr(reply_references) ? "" : "|"), reply_inreplyto
+               );
        }
 
        r = CtdlIPCPostMessage(ipc, 0, &subject_required, &message, buf);
@@ -1131,22 +1110,20 @@ int entmsg(CtdlIPC * ipc, int is_reply, /* nonzero if this was a <R>eply command
                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.
-        */
+       // 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 the user is a dumbass, tell them how to type.
        if ((userflags & US_EXPERT) == 0) {
                scr_printf("Entering message.  Word wrap will give you soft linebreaks.  Pressing the\n");
                scr_printf("'enter' key will give you a hard linebreak and an indent.  Press 'enter' twice\n");
                scr_printf("when finished.\n");
        }
 
-       /* Handle the selection of a recipient, if necessary. */
+       // Handle the selection of a recipient, if necessary.
        strcpy(buf, "");
        if (need_recp == 1) {
                if (axlevel >= AxProbU) {
@@ -1172,7 +1149,7 @@ int entmsg(CtdlIPC * ipc, int is_reply,   /* nonzero if this was a <R>eply command
                        message.anonymous = 1;
        }
 
-       /* If it's mail, we've got to check the validity of the recipient... */
+       // 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) {
@@ -1181,9 +1158,7 @@ int entmsg(CtdlIPC * ipc, int is_reply,   /* nonzero if this was a <R>eply command
                }
        }
 
-       /* Learn the number of the newest message in in the room, so we can
-        * tell upon saving whether someone else has posted too.
-        */
+       // 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) {
@@ -1193,14 +1168,14 @@ int entmsg(CtdlIPC * ipc, int is_reply, /* nonzero if this was a <R>eply command
                for (num_msgs = 0; msgarr[num_msgs]; num_msgs++);
        }
 
-       /* Now compose the message... */
+       // 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 */
+       // 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))) {
@@ -1212,19 +1187,17 @@ int entmsg(CtdlIPC * ipc, int is_reply, /* nonzero if this was a <R>eply command
        if (fp)
                fclose(fp);
 
-       /* Break lines that are >1024 characters, otherwise the server
-        * will truncate them.
-        */
+       // Break lines that are >1024 characters, otherwise the server will truncate them.
        break_big_lines(message.text);
 
-       /* Transmit message to the server */
+       // 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 */
+       // Yes, unlink it now, so it doesn't stick around if we crash
        unlink(temp);
 
        if (num_msgs >= 1)
@@ -1241,10 +1214,10 @@ int entmsg(CtdlIPC * ipc, int is_reply, /* nonzero if this was a <R>eply command
                for (num_msgs = 0; msgarr[num_msgs]; num_msgs++);
        }
 
-       /* get new highest message number in room to set lrp for goto... */
+       // 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 */
+       // now see if anyone else has posted in here
        b = (-1);
        for (a = 0; a < num_msgs; ++a) {
                if (msgarr[a] > highmsg) {
@@ -1256,9 +1229,7 @@ int entmsg(CtdlIPC * ipc, int is_reply,   /* nonzero if this was a <R>eply command
        }
        msgarr = NULL;
 
-       /* In the Mail> room, this algorithm always counts one message
-        * higher than in public rooms, so we decrement it by one.
-        */
+       // 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;
        }
@@ -1270,28 +1241,24 @@ int entmsg(CtdlIPC * ipc, int is_reply, /* nonzero if this was a <R>eply command
                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
- */
+// 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
+       // 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 */
+       // Display the quotable text with line numbers added
        line = 0;
        if (fgets(buf, 128, qfile) == NULL) {
-               /* we're skipping a line here */
+               // we're skipping a line here
        }
        while (fgets(buf, 128, qfile) != NULL) {
                scr_printf("%3d %s", ++line, buf);
@@ -1303,7 +1270,7 @@ void process_quote(void) {
        rewind(qfile);
        line = 0;
        if (fgets(buf, 128, qfile) == NULL) {
-               /* we're skipping a line here */
+               // we're skipping a line here
        }
        tfile = fopen(temp, "w");
        while (fgets(buf, 128, qfile) != NULL) {
@@ -1318,9 +1285,7 @@ void process_quote(void) {
 }
 
 
-/*
- * List the URLs which were embedded in the previous message
- */
+// List the URLs which were embedded in the previous message
 void list_urls(CtdlIPC * ipc) {
        int i;
        char cmd[SIZ];
@@ -1345,9 +1310,7 @@ void list_urls(CtdlIPC * ipc) {
 }
 
 
-/*
- * Run image viewer in background
- */
+// Run image viewer in background
 int do_image_view(const char *filename) {
        char cmd[SIZ];
        pid_t childpid;
@@ -1406,19 +1369,18 @@ int do_image_view(const char *filename) {
 }
 
 
-/*
- * View an image attached to a message
- */
+// 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 */
+       // 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)) {
+               if (    (!strcasecmp(ptr->disposition, "attachment")
+                       || !strcasecmp(ptr->disposition, "inline"))
+                       && !strncmp(ptr->mimetype, "image/", 6)
+               ) {
                        found++;
                        if (found == 1) {
                                strcpy(part, ptr->number);
@@ -1427,8 +1389,9 @@ void image_view(CtdlIPC * ipc, unsigned long msg) {
        }
 
        while (found > 0) {
-               if (found > 1)
+               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")
@@ -1437,10 +1400,10 @@ void image_view(CtdlIPC * ipc, unsigned long msg) {
                            && !strcasecmp(ptr->number, part)) {
                                char tmp[PATH_MAX];
                                char buf[SIZ];
-                               void *file = NULL;      /* The downloaded file */
+                               void *file = NULL;      // The downloaded file
                                int r;
 
-                               /* view image */
+                               // view image
                                found = -found;
                                r = CtdlIPCAttachmentDownload(ipc, msg, ptr->number, &file, progress, buf);
                                if (r / 100 != 2) {
@@ -1467,13 +1430,12 @@ void image_view(CtdlIPC * ipc, unsigned long msg) {
 }
 
 
-/*
- * 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)
-    ) {
+// 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;
@@ -1486,11 +1448,11 @@ void readmsgs(CtdlIPC * ipc, enum MessageList c,        // see listing in citadel_ipc.h
        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 */
+       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);
 
@@ -1519,7 +1481,7 @@ void readmsgs(CtdlIPC * ipc, enum MessageList c,  // see listing in citadel_ipc.h
                return;
        }
 
-       /* this loop cycles through each message... */
+       // 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) {
@@ -1532,30 +1494,30 @@ void readmsgs(CtdlIPC * ipc, enum MessageList c,        // see listing in citadel_ipc.h
                         && (quotflag == 0)
                         && (userflags & US_PAGINATOR)) ? 1 : 0;
 
-               /* If we're doing a quote, set the screenwidth to 72 */
+               // 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 printing or archiving, set the screenwidth to 80
                if (arcflag) {
                        hold_sw = screenwidth;
                        screenwidth = 80;
                }
 
-               /* clear parts list */
+               // clear parts list
                free_parts(last_message_parts);
                last_message_parts = NULL;
 
-               /* now read the message... */
+               // now read the message...
                e = read_message(ipc, msg_arr[a], pagin, dest);
 
-               /* ...and set the screenwidth back if we have to */
+               // ...and set the screenwidth back if we have to
                if ((quotflag) || (arcflag)) {
                        screenwidth = hold_sw;
                }
-             RMSGREAD:
+RMSGREAD:
                highest_msg_read = msg_arr[a];
                if (quotflag) {
                        fclose(dest);
@@ -1574,7 +1536,7 @@ void readmsgs(CtdlIPC * ipc, enum MessageList c,  // see listing in citadel_ipc.h
                        f = fork();
                        if (f == 0) {
                                if (freopen(prtfile, "r", stdin) == NULL) {
-                                       /* we probably should handle the error condition here */
+                                       // we probably should handle the error condition here
                                }
                                stty_ctdl(SB_RESTORE);
                                ka_system(printcmd);
@@ -1614,43 +1576,46 @@ void readmsgs(CtdlIPC * ipc, enum MessageList c,        // see listing in citadel_ipc.h
                                e = (inkey() & 127);
                                e = tolower(e);
 
-/* return key same as <N> */ if (e == 10)
+                                if (e == 10) {                                 // return key same as <N>
                                        e = 'n';
+                               }
 
-/* space key same as <N> */ if (e == 32)
+                                if (e == 32) {                                 // space key same as <N>
                                        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'))
+                               if (    (!is_room_aide)                         // delete/move are available only to admins
+                                       && ((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)))
+                               if ((e == 'p') && (IsEmptyStr(printcmd))) {             // print, if available
                                        e = 0;
+                               }
 
-/* can't file if not allowed */
-                               if ((e == 'f')
-                                   && (rc_allow_attachments == 0))
+                               if ((e == 'f') && (rc_allow_attachments == 0)) {        // file attachments, if available
                                        e = 0;
+                               }
 
-/* link only if browser avail*/
-                               if ((e == 'u')
-                                   && (IsEmptyStr(rc_url_cmd)))
+                               if ((e == 'u') && (IsEmptyStr(rc_url_cmd))) {           // display urls, if a browser is available
                                        e = 0;
-                               if ((e == 'i')
-                                   && (IsEmptyStr(imagecmd) || !has_images))
+                               }
+
+                               if ((e == 'i') && (IsEmptyStr(imagecmd) || !has_images)) {      // display images, if available
                                        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'));
+                                && (e != 'i') && (e != 'o')
+                       );
                        switch (e) {
                        case 's':
                                scr_printf("Stop");
@@ -1708,7 +1673,7 @@ void readmsgs(CtdlIPC * ipc, enum MessageList c,  // see listing in citadel_ipc.h
                        else
                                scr_printf("\n");
                }
-             DONE_QUOTING:switch (e) {
+DONE_QUOTING:  switch (e) {
                case '?':
                        scr_printf("Options available here:\n"
                                   " ?  Help (prints this message)\n"
@@ -1770,8 +1735,9 @@ void readmsgs(CtdlIPC * ipc, enum MessageList c,  // see listing in citadel_ipc.h
                        else {
                                goto RMSGREAD;
                        }
-                       if (r / 100 != 2)       /* r will be init'ed, FIXME */
-                               goto RMSGREAD;  /* the logic here sucks */
+                       if (r / 100 != 2) {     // r will be initialized.  The logic here sucks.
+                               goto RMSGREAD;
+                       }
                        break;
                case 'o':
                case 'f':
@@ -1782,23 +1748,21 @@ void readmsgs(CtdlIPC * ipc, enum MessageList c,        // see listing in citadel_ipc.h
                        }
                        else {
                                extract_token(filename, cmd, 2, '|', sizeof filename);
-                               /*
-                                * Part 1 won't have a filename; use the
-                                * subject of the message instead. IO
-                                */
+                               // 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 */
+                               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)
+                                       if (rv != 0) {
                                                scr_printf("failed to save %s Reason %d\n", cmd, rv);
+                                       }
                                }
-                               else {  /* save attachment to disk */
+                               else {  // save attachment to disk
                                        destination_directory(save_to, filename);
                                        save_buffer(attachment, extract_unsigned_long(cmd, 0), save_to);
                                }
@@ -1865,9 +1829,7 @@ void readmsgs(CtdlIPC * ipc, enum MessageList c,  // see listing in citadel_ipc.h
 }                              /* end read routine */
 
 
-/*
- * View and edit a system message
- */
+// View and edit a system message
 void edit_system_message(CtdlIPC * ipc, char *which_message) {
        char desc[SIZ];
        char read_cmd[SIZ];
@@ -1880,10 +1842,8 @@ void edit_system_message(CtdlIPC * ipc, char *which_message) {
 }
 
 
-/*
- * Loads the contents of a file into memory.  Caller must free the allocated memory.
- */
-char *load_message_from_file(FILE * src) {
+// 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;
@@ -1893,8 +1853,9 @@ char *load_message_from_file(FILE * src) {
        rewind(src);
 
        dest = (char *) calloc(1, i + 1);
-       if (!dest)
+       if (!dest) {
                return NULL;
+       }
 
        while (got < i) {
                size_t g;
@@ -1902,10 +1863,10 @@ char *load_message_from_file(FILE * src) {
                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 */
+                       if (errno == EINTR) {
+                               continue;               // Interrupted system call, keep going
+                       }
+                       // At this point we have either EOF or error
                        i = got;
                        break;
                }
index 01c168bb162757e6e9a4a3dd5f3e8f19619043aa..06ef01033a7ce0f480cc9632771cfe40d42b238a 100644 (file)
@@ -696,3 +696,60 @@ blockquote pre {
        font-size: 2em;
        color: White;
 }
+
+.ctdl-calendar-container {                     /* Flexbox container for calendars; child elements are headings and content */
+       display: flex;
+       flex-direction: column;
+       flex-wrap: nowrap;
+       justify-content: space-between;
+       align-items: center;
+       margin: 0;
+       width: 100%;
+       height: 100%;
+       overflow: hidden;
+}
+
+.ctdl-calendar-toplabels {                     /* days and numbers of the week, up top */
+       height: 5%;
+       width: 100%;
+}
+
+.ctdl-calendar-content {                       /* days and numbers of the week, up top */
+       flex-grow: 0;
+       height: 95%;
+       width: 100%;
+}
+
+.ctdl-calendar-week {                          /* Flexbox container for day, 3-day, 5-day, week view */
+       display: flex;
+       flex-direction: row;
+       flex-wrap: nowrap;
+       justify-content: space-between;
+       align-items: center;
+       margin: 0;
+       width: 100%;
+       height: 100%;
+       overflow: hidden;
+}
+
+.ctdl-calendar-ruler {
+       height: 100%;                           /* Occupy the entire height of the main pane */
+       border: 1px solid GhostWhite;
+}
+
+.ctdl-calendar-label {                         /* Flexbox child for LABEL of one day of the week */
+       flex-grow: 0;                           /* Keep all columns the same size */
+       width: 100%;                            /* Fill the width of the main pane; don't compress them into narrow boxes */
+       justify-content: center;
+       align-items: center;
+       text-align: center;
+       font-weight: bold;
+}
+
+.ctdl-calendar-day {                           /* Flexbox child for one day of the week */
+       flex-grow: 0;                           /* Keep all columns the same size */
+       background-color: White;
+       border: 1px solid SlateGrey;
+       height: 100%;                           /* Occupy the entire height of the main pane */
+       width: 100%;                            /* Fill the width of the main pane; don't compress them into narrow boxes */
+}
index cb42038e0f933089dd80e76763d9901ee8503762..2c155ac2120738f4935e1ac8d803c9c4afc23128 100644 (file)
@@ -37,6 +37,7 @@
                                <li><button id="ctdl-skip-button" style="display:none" onClick="gotonext(1);">skip</button></li>
                                <li><button id="ctdl-goto-button" style="display:none" onClick="gotonext(2);">goto</button></li>
                                <li><button id="ctdl-delete-button" style="display:none" onClick="delete_dispatcher(2);">delete</button></li>
+                               <li><div id="ctdl-calendar-nav" style="display:none">calnav</div>
                        </div>
                        <div id="ctdl-banner-right" class="ctdl-banner-right">
                                <li><button id="lilo">Login</button></li>
index 28fcbd68cbb028ffc8344db3a321d9bbfd5fb4b9..76de28902e36dead5766c19586cc9bc20bc75a32 100644 (file)
@@ -1,13 +1,12 @@
 // This module handles the view for "calendar" rooms.
 //
-// Copyright (c) 2016-2023 by the citadel.org team
+// Copyright (c) 2016-2024 by the citadel.org team
 //
 // This program is open source software.  Use, duplication, or
 // disclosure is subject to the GNU General Public License v3.
 
 
-// RENDERER FOR THIS VIEW
-function view_render_calendar() {
+function XXXXXview_render_calendar() {
 
        let options = {
                method: "REPORT",
@@ -56,3 +55,173 @@ function view_render_calendar() {
                document.getElementById("ctdl-main").innerHTML = `<div class="ctdl-fatal-error">${error}</div>`;
        });
 }
+
+var date_being_displayed = new Date();
+var days_being_displayed = 7;          /* 1 = day view; 5 = work week view; 7 = week view */
+var start_of_week = 1;                 /* 0 = Sunday; 1 = Monday */
+
+// Update the calendar display (we might have changed dates, or added/removed data)
+function update_calendar_display() {
+
+       // Clone the "date_being_displayed" and scope it locally for this function.
+       let this_column_date = new Date(date_being_displayed);
+
+       // If displaying a whole week, start the week on the correct day of the week (usually Sunday or Monday)
+       if (days_being_displayed == 7) {
+               while (this_column_date.getDay() != start_of_week) {
+                       this_column_date.setDate(this_column_date.getDate() - 1);
+               }
+       }
+
+       // If displaying a work week, start the week on Monday
+       if (days_being_displayed == 5) {
+               while (this_column_date.getDay() != 1) {
+                       this_column_date.setDate(this_column_date.getDate() - 1);
+               }
+       }
+
+       // Go through every day on the screen (up to a week I guess)
+       for (let i=0; i<days_being_displayed; ++i) {
+
+               // Populate the columns
+               let year = this_column_date.getFullYear();
+               let month = this_column_date.getMonth() + 1;
+               let day_of_month = this_column_date.getDate();
+               let weekday = this_column_date.toLocaleString("default", { weekday: "short" })
+               let monthname = this_column_date.toLocaleString("default", { month: "short" })
+               document.getElementById("ctdl-cal-label" + i).innerHTML = weekday + " " + year + "-" + monthname + "-" + day_of_month;
+               document.getElementById("ctdl-cal-day" + i).innerHTML = "This is the content for " + this_column_date;
+
+               // Get ready for the next column
+               this_column_date.setDate(this_column_date.getDate() + 1);
+       }
+}
+
+
+// Set the week view to 1-day (day), 3-day, 5-day (work week), or 7-day (week).
+// Any value is possible but the above are ones which seem to make sense.
+// You could for example do 14 for a "two week view" but it would start to look compressed.
+function set_week_days(days) {
+       days_being_displayed = days;
+       view_render_calendar();
+}
+
+
+// Go back by one year.
+function go_back_one_year() {
+       date_being_displayed.setYear(date_being_displayed.getFullYear() - 1 );
+       update_calendar_display();
+}
+
+
+// Go back by one month.
+function go_back_one_month() {
+       date_being_displayed.setMonth(date_being_displayed.getMonth() - 1 );
+       update_calendar_display();
+}
+
+
+// Go back by one week.
+function go_back_one_week() {
+       date_being_displayed.setDate(date_being_displayed.getDate() - 7 );
+       update_calendar_display();
+}
+
+
+// Go back by one day.
+function go_back_one_day() {
+       date_being_displayed.setDate(date_being_displayed.getDate() - 1);
+       update_calendar_display();
+}
+
+
+// Flip the calendar to today's date.
+function go_to_today() {
+       date_being_displayed = new Date();
+       update_calendar_display();
+}
+
+
+// Advance the calendar by one day.
+function go_forward_one_day() {
+       date_being_displayed.setDate(date_being_displayed.getDate() + 1);
+       update_calendar_display();
+}
+
+
+// Advance the calendar by one week.
+function go_forward_one_week() {
+       date_being_displayed.setDate(date_being_displayed.getDate() + 7);
+       update_calendar_display();
+}
+
+
+// Advance the calendar by one month.
+function go_forward_one_month() {
+       date_being_displayed.setMonth(date_being_displayed.getMonth() + 1 );
+       update_calendar_display();
+}
+
+
+// Advance the calendar by one year.
+function go_forward_one_year() {
+       date_being_displayed.setYear(date_being_displayed.getFullYear() + 1 );
+       update_calendar_display();
+}
+
+
+// RENDERER FOR THIS VIEW
+function view_render_calendar() {
+
+       // These are TEMPORARY navigation buttons.
+       document.getElementById("ctdl-calendar-nav").innerHTML = `
+               <a href="javascript:set_week_days(1);">Day</a>&nbsp;|&nbsp;
+               <a href="javascript:set_week_days(3);">3-Day</a>&nbsp;|&nbsp;
+               <a href="javascript:set_week_days(5);">WorkWeek</a>&nbsp;|&nbsp;
+               <a href="javascript:set_week_days(7);">Week</a>&nbsp;|&nbsp;
+               <a href="javascript:go_back_one_year();">←Y</a>&nbsp;|&nbsp;
+               <a href="javascript:go_back_one_month();">←M</a>&nbsp;|&nbsp;
+               <a href="javascript:go_back_one_week();">←W</a>&nbsp;|&nbsp;
+               <a href="javascript:go_back_one_day();">←D</a>&nbsp;|&nbsp;
+               <a href="javascript:go_to_today();">today</a>&nbsp;|&nbsp;
+               <a href="javascript:go_forward_one_day();">D→</a>&nbsp;|&nbsp;
+               <a href="javascript:go_forward_one_week();">W→</a>&nbsp;|&nbsp;
+               <a href="javascript:go_forward_one_month();">M→</a>&nbsp;|&nbsp;
+               <a href="javascript:go_forward_one_year();">Y→</a>&nbsp;
+       `;
+       document.getElementById("ctdl-calendar-nav").style.display = "block";
+
+       let caldisplay = "";
+       caldisplay += `<div class="ctdl-calendar-container">`;          // top level container
+       caldisplay += `<div class="ctdl-calendar-toplabels">`;          // the row of labels above the calendar content
+
+       // This section renders the day labels at the top of the screen
+       caldisplay += `<div class="ctdl-calendar-week">`;               // container for ruler+days
+       caldisplay += `<div class="ctdl-calendar-ruler">`;              // the hours ruler on the left side of the screen
+       caldisplay += "</div>";                                         // closes ctdl-calendar-ruler
+       for (let i=0; i<days_being_displayed; ++i) {
+               caldisplay += `<div id="ctdl-cal-label` + i + `" class="ctdl-calendar-label">Hi!</div>`;
+       }
+       caldisplay += "</div>";                                         // closes ctdl-calendar-week
+
+       caldisplay += `</div>`;                                         // closes ctdl-calendar-toplabels
+       caldisplay += `<div class="ctdl-calendar-content">`;            // the actual boxes of content below the labels
+       caldisplay += `<div class="ctdl-calendar-week">`;               // container for ruler+days
+       caldisplay += `<div class="ctdl-calendar-ruler">`;              // the hours ruler on the left side of the screen
+       for (let i=0; i<24; ++i) {
+               caldisplay += i + "<br>";
+       };
+       caldisplay += `</div>`;                                         // closes ctdl-calendar-ruler
+
+       // This section renders the day boxes.
+       for (let i=0; i<days_being_displayed; ++i) {
+               caldisplay += `<div id="ctdl-cal-day` + i + `" class="ctdl-calendar-day">Hi!</div>`;
+       }
+       caldisplay += "</div>";                                         // closes ctdl-calendar-week
+       caldisplay += "</div>";                                         // closes ctdl-calendar-content
+       caldisplay += "</div>";                                         // closes ctdl-calendar-container
+       document.getElementById("ctdl-main").innerHTML = caldisplay;
+
+       // Start on today.
+       go_to_today();
+}
index e6b769d194e3aafd4951dd8cd34b7aed8eae15c4..af53ade410f0e34179a1fc1e629827579a844314 100644 (file)
@@ -23,7 +23,7 @@ function clear_drop_handlers() {
 
 // Clear the top bar navigation buttons.  The view renderer will set its own buttons.
 function clear_navigation_buttons() {
-       for (const d of ["ctdl-newmsg-button", "ctdl-ungoto-button", "ctdl-skip-button", "ctdl-goto-button", "ctdl-delete-button"]) {
+       for (const d of ["ctdl-newmsg-button", "ctdl-ungoto-button", "ctdl-skip-button", "ctdl-goto-button", "ctdl-delete-button", "ctdl-calendar-nav"]) {
                document.getElementById(d).style.display = "none";
        }
 }
index c0f2cdc8d3a09002bf6392e1b1e4cd87ad9a3e83..05f2a3840638332999ac0ae22089ba65d26db8ad 100644 (file)
Binary files a/webcit-ng/static/sounds/hi_from_stu.wav and b/webcit-ng/static/sounds/hi_from_stu.wav differ
index efe04a3a668054752fb43b2d1e133bac606bbd2c..7d0eba4c0c8e7ba9b154b142c518d20a387e275c 100644 (file)
@@ -1,47 +1,2 @@
 <div class="tabcontent">
-       <br />
-       <table border="1" cellpadding="5">
-               <tr>
-                       <td><b><i><?_("Not shared with")></i></b></td>
-                       <td><b><i><?_("Shared with")></i></b></td>
-               </tr>
-               <tr>
-                       <td valign="top">
-                               <table border="0" cellpadding="5">
-                                       <tr class="tab_cell">
-                                               <td><?_("Remote node name")></td>
-                                               <td><?_("Remote room name")></td>
-                                               <td><?_("Actions")></td>
-                                       </tr>
-                                       <?ITERATE("NODECONFIG", ="room_edit_shared_room_add")>
-                               </table>
-                       </td>
-                       <td valign="top">
-                               <table border="0" cellpadding="5">
-                                       <tr class="tab_cell">
-                                               <td><?_("Remote node name")></td>
-                                               <td><?_("Remote room name")></td>
-                                               <td><?_("Actions")></td>
-                                       </tr>
-                                       <?ITERATE("ITERATE:THISROOM:GNET", ="room_edit_shared_room_removal", 0, 0, -1, #"ignet_push_share")>
-                               </table>
-                       </td>
-               </tr>
-       </table>
-       <br />
-       <div id="hints">
-               <b><?_("Notes:")></b>
-               <div><ul>
-                       <li>
-                               <?_("When sharing a room, it must be shared from both ends.  Adding a node to the 'shared' list sends messages out, but in order to receive messages, the other nodes must be configured to send messages out to your system as well.")>
-                       <li>
-                         <?_("If the remote room name is blank, it is assumed that the room name is identical on the remote node.")>
-                       </li> <li>
-                         <?_("If the remote room name is different, the remote node must also configure the name of the room here.")>
-                       </li> <li>
-                         (<img src="static/webcit_icons/essen/16x16/refresh.png" alt='<?_("resend messages to this node")>' width="16" height="16">)<?_("Re-sharing may stress your system and produce large spoolfiles that need to be transmitted; All messages in this room not originating from this node are re-spooled and re-sent with the next networker run.")>
-                       </li>
-               </ul></div>
-       </div>
-       <br />
 </div>