From 022803c7eb543a8d3f241e9375cc7a2e52a1e8e3 Mon Sep 17 00:00:00 2001 From: Manvendra Bhangui Date: Sun, 23 Apr 2023 21:05:38 +0530 Subject: [PATCH 1/4] updated courier-imap, maildrop --- courier-imap-x/.gitignore | 1 + courier-imap-x/conf-version | 2 +- courier-imap-x/configure.ac | 2 +- courier-imap-x/libs/imap/ChangeLog | 7 + courier-imap-x/libs/maildrop/Makefile.am | 1 + courier-imap-x/libs/maildrop/buffer.C | 129 +--- courier-imap-x/libs/maildrop/buffer.h | 67 +- courier-imap-x/libs/maildrop/configure.ac | 31 +- courier-imap-x/libs/maildrop/deliver.C | 47 +- courier-imap-x/libs/maildrop/dotlock.C | 26 +- courier-imap-x/libs/maildrop/dotlock.h | 3 +- .../libs/maildrop/dotlockmaildrop.C | 24 +- courier-imap-x/libs/maildrop/filter.C | 25 +- courier-imap-x/libs/maildrop/formatmbox.C | 38 +- courier-imap-x/libs/maildrop/formatmbox.h | 18 +- courier-imap-x/libs/maildrop/funcs.C | 16 +- courier-imap-x/libs/maildrop/lexer.C | 54 +- courier-imap-x/libs/maildrop/lexer.h | 2 +- courier-imap-x/libs/maildrop/log.C | 27 +- courier-imap-x/libs/maildrop/log.h | 3 +- courier-imap-x/libs/maildrop/maildir.C | 98 ++- courier-imap-x/libs/maildrop/maildir.h | 6 +- courier-imap-x/libs/maildrop/maildrop.1.in | 12 +- courier-imap-x/libs/maildrop/maildrop.h | 12 +- .../libs/maildrop/maildropfilter.7.in | 66 +- .../libs/maildrop/maildropfilter.html.in | 39 +- courier-imap-x/libs/maildrop/main.C | 123 ++-- courier-imap-x/libs/maildrop/message.C | 42 +- courier-imap-x/libs/maildrop/message.h | 9 +- courier-imap-x/libs/maildrop/messageinfo.C | 20 +- courier-imap-x/libs/maildrop/messageinfo.h | 2 +- courier-imap-x/libs/maildrop/mio.C | 4 +- courier-imap-x/libs/maildrop/mio.h | 3 +- courier-imap-x/libs/maildrop/recipe.C | 2 +- courier-imap-x/libs/maildrop/recipenode.C | 646 ++++++++---------- courier-imap-x/libs/maildrop/recipenode.h | 30 +- courier-imap-x/libs/maildrop/reeval.h | 5 + courier-imap-x/libs/maildrop/reformail.C | 495 ++++++-------- courier-imap-x/libs/maildrop/search.C | 81 ++- courier-imap-x/libs/maildrop/search.h | 21 +- courier-imap-x/libs/maildrop/testsuite.in | 135 +++- .../libs/maildrop/testsuite.txt.idn | 494 ++++++++++++++ courier-imap-x/libs/maildrop/token.C | 14 +- courier-imap-x/libs/maildrop/token.h | 8 +- courier-imap-x/libs/maildrop/varlist.C | 156 ++--- courier-imap-x/libs/maildrop/varlist.h | 14 +- courier-imap-x/libs/numlib/changeuidgid.c | 41 +- courier-imap-x/libs/rfc1035/configure.ac | 2 +- courier-imap-x/libs/rfc822/configure.ac | 24 +- courier-imap-x/libs/tcpd/configure.ac | 2 +- 50 files changed, 1614 insertions(+), 1515 deletions(-) diff --git a/courier-imap-x/.gitignore b/courier-imap-x/.gitignore index 3b2bb00f2..395bc4220 100644 --- a/courier-imap-x/.gitignore +++ b/courier-imap-x/.gitignore @@ -359,6 +359,7 @@ libs/maildrop/maildrop libs/maildrop/maildrop.1 libs/maildrop/maildropfilter.7 libs/maildrop/reformail +libs/maildrop/testtimer libtool maildiracl maildiracl.1 diff --git a/courier-imap-x/conf-version b/courier-imap-x/conf-version index ce7f2b425..c0baecbaa 100644 --- a/courier-imap-x/conf-version +++ b/courier-imap-x/conf-version @@ -1 +1 @@ -5.2.2 +5.2.3 diff --git a/courier-imap-x/configure.ac b/courier-imap-x/configure.ac index af9ee416d..56057ff93 100644 --- a/courier-imap-x/configure.ac +++ b/courier-imap-x/configure.ac @@ -47,7 +47,7 @@ case "$host" in ;; esac -PKG_CHECK_MODULES(LIBIDN2, libidn2 >= 0.0.0, [libidn=yes], [libidn=no]) +PKG_CHECK_MODULES(LIBIDN2, libidn2 >= 2.0.5, [libidn=yes], [libidn=no]) if test "$libidn" != "yes" then PKG_CHECK_MODULES(LIBIDN1, libidn >= 0.0.0, [libidn=yes], [libidn=no]) diff --git a/courier-imap-x/libs/imap/ChangeLog b/courier-imap-x/libs/imap/ChangeLog index ee9012f92..7589be497 100644 --- a/courier-imap-x/libs/imap/ChangeLog +++ b/courier-imap-x/libs/imap/ChangeLog @@ -1,3 +1,10 @@ +5.2.3 + +2023-02-19 Sam Varshavchik + + * all: update configure.ac to require at least version 2.0.5 of + libidn + 5.2.2 2023-02-14 Sam Varshavchik diff --git a/courier-imap-x/libs/maildrop/Makefile.am b/courier-imap-x/libs/maildrop/Makefile.am index f47b5900b..274641c2a 100644 --- a/courier-imap-x/libs/maildrop/Makefile.am +++ b/courier-imap-x/libs/maildrop/Makefile.am @@ -55,6 +55,7 @@ reformail_LDADD = libmdcommon.la reformail_DEPENDENCIES = $(reformail_LDADD) bin_PROGRAMS=maildrop reformail mailbot +noinst_PROGRAMS=testtimer mailbot_SOURCES=mailbot.c mailbot_DEPENDENCIES=../rfc2045/librfc2045.la ../maildir/libmaildir.la \ diff --git a/courier-imap-x/libs/maildrop/buffer.C b/courier-imap-x/libs/maildrop/buffer.C index acb10065f..658e02624 100644 --- a/courier-imap-x/libs/maildrop/buffer.C +++ b/courier-imap-x/libs/maildrop/buffer.C @@ -7,135 +7,34 @@ #include -#define CHUNK 512 - -void Buffer::append(int c) -{ -int newsize=bufsize+CHUNK; -unsigned char *newbuf=new unsigned char[newsize]; - - if (!newbuf) outofmem(); - if (bufsize) memcpy(newbuf, buf, bufsize); - if (buf) delete[] buf; - buf=newbuf; - bufsize = newsize; - buf[buflength++]=c; -} - -void Buffer::set(const char *p) +void add_integer(std::string &b, unsigned long n) { -int l=strlen(p); - - if (bufsize < l) - { - int newsize=l + CHUNK-1; - - newsize -= (newsize % CHUNK); - unsigned char *newbuf=new unsigned char[newsize]; - - if (!newbuf) outofmem(); - if (buf) delete[] buf; - buf=newbuf; - bufsize=newsize; - } - memcpy(buf, p, l); - buflength=l; -} - -int Buffer::operator-(const Buffer &o) const -{ -int i; - - for (i=0; i o.buf[i]) return (1); - } - return (buflength < o.buflength ? -1: - buflength > o.buflength ? 1:0); -} - -int Buffer::operator-(const char *o) const -{ -int i; - - for (i=0; i o[i]) return (1); - } - return (i < buflength ? 1: o[i] ? -1:0); -} - -Buffer &Buffer::operator=(const Buffer &o) -{ - if (bufsize < o.buflength) - { - int newsize=(o.buflength + CHUNK-1); - - newsize -= (newsize % CHUNK); - unsigned char *newbuf=new unsigned char[newsize]; - - if (!newbuf) outofmem(); - if (buf) delete[] buf; - buf=newbuf; - bufsize=newsize; - } - if (o.buflength) memcpy(buf, o.buf, o.buflength); - buflength=o.buflength; - return (*this); -} - -void Buffer::append(const void *p, int l) -{ - if (bufsize - buflength < l) - { - int newsize=(buflength+l + CHUNK-1); - - newsize -= (newsize % CHUNK); - unsigned char *newbuf=new unsigned char[newsize]; - - if (!newbuf) outofmem(); - if (buf) - { - memcpy(newbuf, buf, buflength); - delete[] buf; - } - buf=newbuf; - bufsize=newsize; - } - if (l) memcpy(buf+buflength, p, l); - buflength += l; -} - -void Buffer::append(unsigned long n) -{ -char tbuf[40]; -char *p=tbuf+sizeof(tbuf)-1; + char tbuf[40]; + char *p=tbuf+sizeof(tbuf)-1; *p=0; do { *--p= (n % 10) + '0'; } while ( (n /= 10) != 0); - append(p); -} + b += p; +} -void Buffer::append(double d) +void add_number(std::string &buf, double d) { -char tbuf[MAXLONGSIZE < 40 ? 40:MAXLONGSIZE+4]; + char tbuf[MAXLONGSIZE < 40 ? 40:MAXLONGSIZE+4]; sprintf(tbuf, "%1.*g", MAXLONGSIZE, d); - operator += (tbuf); + buf += (tbuf); } -int Buffer::Int(const char *def) const +int extract_int(const std::string &buf, const char *def) { -const unsigned char *p=buf; -int l=buflength; -int minus=0; -unsigned num=0; + const char *p=buf.c_str(); + auto l=buf.size(); + int minus=0; + unsigned num=0; while (l && isspace(*p)) { @@ -145,7 +44,7 @@ unsigned num=0; if ((!l || (*p != '-' && (*p < '0' || *p > '9'))) && def != 0) { - p= (const unsigned char *)def; + p=def; l=strlen(def); } diff --git a/courier-imap-x/libs/maildrop/buffer.h b/courier-imap-x/libs/maildrop/buffer.h index f98e82437..fbe46c24d 100644 --- a/courier-imap-x/libs/maildrop/buffer.h +++ b/courier-imap-x/libs/maildrop/buffer.h @@ -3,65 +3,18 @@ #include "config.h" -#include +#include -/////////////////////////////////////////////////////////////////////////// -// -// Generic text/data buffer. Not null-terminated by default. -// -// This class is used to store arbitrary text strings. It is used by -// the lexical scanner to build up text that's recognized as a token, -// and the rest of the code to store strings. -// -/////////////////////////////////////////////////////////////////////////// +void add_number(std::string &buf, double val); +void add_integer(std::string &buf, unsigned long n); +inline void set_integer(std::string &buf, unsigned long n) +{ + std::string b; -class Buffer { + add_integer(b, n); + buf=b; +} - unsigned char *buf; - int bufsize; - int buflength; -public: - Buffer() : buf(0), bufsize(0), buflength(0) {} - ~Buffer() { if (buf) delete[] buf; } - const unsigned char *Ptr() const { return (buf); } - operator const unsigned char *() const { return (buf); } - operator const char *() const { return ((const char *)buf); } - int Int(const char * =0) const; - int Length() const { return (buflength); } - void Length(int l) { if (l < buflength) buflength=l; } - - Buffer(const Buffer &); // UNDEFINED - Buffer &operator=(const Buffer &); - - void push(int c) { if (buflength < bufsize) - { - buf[buflength]=c; - ++buflength; - } - else append(c); } - int pop() { return (buflength ? buf[--buflength]:0); } - int peek() { return (buflength ? buf[buflength-1]:0); } - void reset() { buflength=0; } - -private: - void append(int); -public: - void append(const void *, int); - void set(const char *); - void append(const char *p) { append(p, strlen(p)); } - void set(unsigned long n) { buflength=0; append(n); } - void append(unsigned long n); - void append(double); - Buffer &operator=(const char *p) { set(p); return (*this); } - Buffer &operator += (const Buffer &p) { append(p.buf, p.buflength); return (*this); } - Buffer &operator += (const char *p) { append(p, strlen(p)); return (*this); } - Buffer &operator += (char c) { push(c); return (*this); } - int operator-(const Buffer &) const; - int operator-(const char *) const; - int operator==(const Buffer &b) const - { return ( operator-(b) == 0); } - int operator==(const char *b) const - { return ( operator-(b) == 0); } -} ; +int extract_int(const std::string &buf, const char * =0); #endif diff --git a/courier-imap-x/libs/maildrop/configure.ac b/courier-imap-x/libs/maildrop/configure.ac index 67ae1c1d0..65be18c7e 100644 --- a/courier-imap-x/libs/maildrop/configure.ac +++ b/courier-imap-x/libs/maildrop/configure.ac @@ -2,12 +2,12 @@ dnl dnl Copyright 1998 - 2022 Double Precision, Inc. See COPYING for dnl distribution information. -AC_INIT([maildrop],[3.1.1],[courier-users@lists.sourceforge.net]) -AC_CONFIG_MACRO_DIR([m4]) +AC_INIT([maildrop],[3.1.4],[courier-users@lists.sourceforge.net]) >confdefs.h # Kill PACKAGE_ macros AC_CONFIG_SRCDIR(alarm.C) +AC_CONFIG_AUX_DIR(../..) LPATH="$PATH:/usr/local/bin" AM_INIT_AUTOMAKE([foreign no-define]) @@ -204,6 +204,7 @@ AC_DEFINE_UNQUOTED(MAXLONGSIZE, $MAXLONGSIZE, dnl Checks for library functions. AC_FUNC_CLOSEDIR_VOID + AC_CHECK_FUNCS(setgroups setlocale) AC_CHECK_FUNC(getpgrp, HAS_GETPGRP=1, HAS_GETPGRP=0) @@ -293,14 +294,6 @@ fi AC_DEFINE_UNQUOTED(DEFAULT_PATH,"$maildrop_cv_SYS_DEFAULT_PATH", [ Default value of the PATH variable ]) -### use option --enable-indimail to compile in the INDIMAIL support -AC_ARG_ENABLE(indimail, - [ --enable-indimail compile in INDIMAIL protocol support], - [with_INDIMAIL=$enableval], - [with_INDIMAIL=no]) -test "$with_INDIMAIL" = "yes" && AC_DEFINE(INDIMAIL,1,Define if you want INDIMAIL support compiled in) -AM_CONDITIONAL(INDIMAIL, test "$with_INDIMAIL" = yes) - if test -x /var/qmail/bin/qmail-inject then QMAIL="/var/qmail/bin/qmail-inject" @@ -469,9 +462,9 @@ done # Not found, possibly qmail $HOME/Mailbox -if test "$QMAIL" != "" -o "$with_INDIMAIL" != "" +if test "$QMAIL" != "" then - SPOOLDIR="./Maildir/" + SPOOLDIR="./Mailbox" else AC_MSG_ERROR(Cannot determine default mailbox) fi @@ -486,10 +479,10 @@ check_spooldir() { fi } -if test "$with_INDIMAIL" != "yes" -then - AC_CACHE_CHECK(location of system mailboxes,maildrop_cv_SYS_INSTALL_MBOXDIR, check_spooldir maildrop_cv_SYS_INSTALL_MBOXDIR="$MBOX_DIR") -fi +AC_CACHE_CHECK(location of system mailboxes,maildrop_cv_SYS_INSTALL_MBOXDIR, +check_spooldir +maildrop_cv_SYS_INSTALL_MBOXDIR="$MBOX_DIR" +) AC_CACHE_CHECK(whether maildrop should reset its group ID,maildrop_cv_SYS_INSTALL_RESET_GID, check_spooldir @@ -701,10 +694,12 @@ y*|Y*|1*) ;; esac + + AC_SUBST(VERSION) -CFLAGS="$CFLAGS -std=c99 -I$srcdir/../rfc822 -I$srcdir/../rfc2045 -I.. -I$srcdir/.. -I ../.. -I$srcdir/../.." -CXXFLAGS="$CXXFLAGS -std=c++11 -I$srcdir/../rfc822 -I$srcdir/../rfc2045 -I.. -I$srcdir/.. -I../.. -I$srcdir/../.." +CFLAGS="$CFLAGS -I$srcdir/../rfc822 -I$srcdir/../rfc2045 -I.. -I$srcdir/.. -I ../.. -I$srcdir/../.." +CXXFLAGS="$CXXFLAGS -I$srcdir/../rfc822 -I$srcdir/../rfc2045 -I.. -I$srcdir/.. -I../.. -I$srcdir/../.." AC_ARG_WITH(etcdir, [ --with-etcdir=dir Instead of /etc, use this. ], [ withetcdir=$withval], diff --git a/courier-imap-x/libs/maildrop/deliver.C b/courier-imap-x/libs/maildrop/deliver.C index d6d215690..b2bd1fd7e 100644 --- a/courier-imap-x/libs/maildrop/deliver.C +++ b/courier-imap-x/libs/maildrop/deliver.C @@ -1,5 +1,5 @@ /* -** Copyright 1998 - 2008 Double Precision, Inc. +** Copyright 1998 - 2023 Double Precision, Inc. ** See COPYING for distribution information. */ @@ -53,19 +53,15 @@ FormatMbox format_mbox; if (format_mbox.HasMsg()) return (0); -Buffer b; + std::string b; if ( *mailbox == '!' || *mailbox == '|' ) { - Buffer cmdbuf; + std::string cmdbuf; if (*mailbox == '!') { - b="SENDMAIL"; - - const char *sendmail=GetVarStr(b); - - cmdbuf=sendmail; + cmdbuf=GetVar("SENDMAIL"); cmdbuf += " -f '' "; cmdbuf += mailbox+1; @@ -73,11 +69,10 @@ Buffer b; else cmdbuf= mailbox+1; - cmdbuf += '\0'; if (VerboseLevel() > 0) merr << "maildrop: Delivering to |" << - (const char *)cmdbuf << "\n"; + cmdbuf.c_str() << "\n"; PipeFds pipe; @@ -97,7 +92,7 @@ Buffer b; try { - subshell(cmdbuf); + subshell(cmdbuf.c_str()); } catch (const char *p) { @@ -140,13 +135,13 @@ Buffer b; log(mailbox, rc || wait_stat, format_mbox); { - Buffer name, val; + std::string name, val; if (rc) wait_stat= -1; else wait_stat= WIFEXITED(wait_stat) ? WEXITSTATUS(wait_stat):-1; - val.append( (unsigned long)wait_stat); + add_integer(val, wait_stat); name="EXITCODE"; SetVar(name, val); } @@ -188,13 +183,12 @@ Buffer b; struct stat stat_buf; Mio mio; - Buffer name_buf; + std::string name_buf; - name_buf="UMASK"; - const char *um=GetVarStr(name_buf); + std::string um=GetVar("UMASK"); unsigned int umask_val=077; - sscanf(um, "%o", &umask_val); + sscanf(um.c_str(), "%o", &umask_val); umask_val=umask(umask_val); @@ -261,18 +255,19 @@ Buffer b; void subshell(const char *cmd) { -Buffer b; + std::string b; - b="SHELL"; + std::string shell=GetVar("SHELL"); -const char *shell=GetVarStr(b); + const char *p, *q; -const char *p, *q; - - for (p=q=shell; *p; p++) + for (p=q=shell.c_str(); *p; p++) if (*p == SLASH_CHAR) q=p+1; -char **env=ExportEnv(); + std::vector> strings; + std::vector env; + + ExportEnv(strings, env); int n; @@ -286,9 +281,9 @@ int n; _exit(100); } ExitTrap::onfork(); - execle(shell, q, "-c", cmd, (const char *)0, env); + execle(shell.c_str(), q, "-c", cmd, (const char *)NULL, env.data()); if (write (2, "Unable to execute ", 18) < 0 || - write (2, shell, strlen(shell)) < 0 || + write (2, shell.c_str(), shell.size()) < 0 || write (2, "\n", 1) < 0) ; /* ignored */ _exit(100); diff --git a/courier-imap-x/libs/maildrop/dotlock.C b/courier-imap-x/libs/maildrop/dotlock.C index f60ee4791..7348c50f8 100644 --- a/courier-imap-x/libs/maildrop/dotlock.C +++ b/courier-imap-x/libs/maildrop/dotlock.C @@ -1,5 +1,5 @@ /* -** Copyright 1998 - 2006 Double Precision, Inc. +** Copyright 1998 - 2023 Double Precision, Inc. ** See COPYING for distribution information. */ @@ -37,22 +37,21 @@ void DotLock::Unlock() int DotLock::attemptlock(const char *templock, const char *finallock) { Mio mio; -Buffer b; -static Buffer errbuf; +std::string b; +static std::string errbuf; if (mio.Open(templock, O_CREAT | O_WRONLY, 0644) < 0) { errbuf="Unable to create a dot-lock at "; - errbuf += (const char *)templock; + errbuf += templock; errbuf += ".\n"; - errbuf += '\0'; - throw (const char *)errbuf; + throw errbuf.c_str(); } - b.append( (unsigned long)getpid() ); - b += '\n'; - if (mio.write((const char *)b, b.Length()) < 0 || mio.flush() < 0) + add_integer(b, getpid() ); + b += "\n"; + if (mio.write(b.c_str(), b.size()) < 0 || mio.flush() < 0) { mio.Close(); unlink(templock); @@ -138,20 +137,19 @@ AlarmTimer stat_timer; void DotLock::LockMailbox(const char *mailbox) { struct stat stat_buf; -Buffer dotlock_name; +std::string dotlock_name; if (stat(mailbox, &stat_buf) < 0 || ( !S_ISCHR(stat_buf.st_mode) && !S_ISBLK(stat_buf.st_mode))) { dotlock_name=mailbox; - const char *p=GetLockExt(); + std::string p=GetLockExt(); - if (!p || !*p) dotlock_name += LOCKEXT_DEF; + if (p.empty()) dotlock_name += LOCKEXT_DEF; else dotlock_name += p; - dotlock_name += '\0'; - Lock( dotlock_name ); + Lock( dotlock_name.c_str() ); } } diff --git a/courier-imap-x/libs/maildrop/dotlock.h b/courier-imap-x/libs/maildrop/dotlock.h index 1e1fd20b5..b4bbb5056 100644 --- a/courier-imap-x/libs/maildrop/dotlock.h +++ b/courier-imap-x/libs/maildrop/dotlock.h @@ -4,6 +4,7 @@ #include "tempfile.h" #include "dotlockrefresh.h" +#include ///////////////////////////////////////////////////////////////////////////// // @@ -35,6 +36,6 @@ class DotLock : public TempFile { private: static unsigned GetLockTimeout(); static unsigned GetLockRefresh(); - static const char *GetLockExt(); + static std::string GetLockExt(); } ; #endif diff --git a/courier-imap-x/libs/maildrop/dotlockmaildrop.C b/courier-imap-x/libs/maildrop/dotlockmaildrop.C index 67032db52..b28051bec 100644 --- a/courier-imap-x/libs/maildrop/dotlockmaildrop.C +++ b/courier-imap-x/libs/maildrop/dotlockmaildrop.C @@ -6,34 +6,20 @@ unsigned DotLock::GetLockSleep() { -Buffer b; - - b="LOCKSLEEP"; - - return GetVar(b)->Int(LOCKSLEEP_DEF); + return extract_int(GetVar("LOCKSLEEP"), LOCKSLEEP_DEF); } unsigned DotLock::GetLockTimeout() { -Buffer b; - - b="LOCKTIMEOUT"; - return (GetVar(b)->Int(LOCKTIMEOUT_DEF)); + return extract_int(GetVar("LOCKTIMEOUT"), LOCKTIMEOUT_DEF); } unsigned DotLock::GetLockRefresh() { -Buffer b; - - b="LOCKREFRESH"; - return (GetVar(b)->Int(LOCKREFRESH_DEF)); + return extract_int(GetVar("LOCKREFRESH"), LOCKREFRESH_DEF); } -const char *DotLock::GetLockExt() +std::string DotLock::GetLockExt() { -Buffer varname; - - varname="LOCKEXT"; - - return (GetVarStr(varname)); + return GetVar("LOCKEXT"); } diff --git a/courier-imap-x/libs/maildrop/filter.C b/courier-imap-x/libs/maildrop/filter.C index bae1ca7b0..89a5f5523 100644 --- a/courier-imap-x/libs/maildrop/filter.C +++ b/courier-imap-x/libs/maildrop/filter.C @@ -1,5 +1,5 @@ /* -** Copyright 1998 - 2006 Double Precision, Inc. +** Copyright 1998 - 2023 Double Precision, Inc. ** See COPYING for distribution information. */ @@ -70,10 +70,9 @@ char buffer[1024]; // if (format_mbox.HasMsg()) return (0); // Empty (void)format_mbox.HasMsg(); -Buffer cmdbuf; +std::string cmdbuf; cmdbuf= filtercmd; - cmdbuf += '\0'; PipeFds pipe0, pipe1; @@ -157,7 +156,7 @@ int maxfd=pipe1.fds[0]; if (!writebuflen && pipe0.fds[1] >= 0) { // Need more to write. - Buffer *p=format_mbox.NextLine(); + std::string *p=format_mbox.NextLine(); if (!p) { @@ -166,8 +165,8 @@ int maxfd=pipe1.fds[0]; } else { - writebufptr= *p; - writebuflen= p->Length(); + writebufptr= p->c_str(); + writebuflen= p->size(); } } @@ -263,11 +262,10 @@ int maxfd=pipe1.fds[0]; wait_stat= -1; { - Buffer name, val; + std::string val; - val.append( (unsigned long)wait_stat); - name="RETURNCODE"; - SetVar(name, val); + add_integer(val, wait_stat); + SetVar("RETURNCODE", val); } if (wait_stat) @@ -328,10 +326,9 @@ void executesystem(const char *cmd) wait_stat= WIFEXITED(wait_stat) ? WEXITSTATUS(wait_stat):-1; { - Buffer name, val; + std::string val; - val.append( (unsigned long)wait_stat); - name="RETURNCODE"; - SetVar(name, val); + add_integer(val, wait_stat); + SetVar("RETURNCODE", val); } } diff --git a/courier-imap-x/libs/maildrop/formatmbox.C b/courier-imap-x/libs/maildrop/formatmbox.C index 3b189275c..3aa5f05fa 100644 --- a/courier-imap-x/libs/maildrop/formatmbox.C +++ b/courier-imap-x/libs/maildrop/formatmbox.C @@ -15,7 +15,7 @@ int FormatMbox::HasMsg() { maildrop.msgptr->Rewind(); - msglinebuf.reset(); + msglinebuf.clear(); if (maildrop.msgptr->appendline(msglinebuf,0) < 0) return (-1); // Empty message, do not deliver. @@ -35,7 +35,7 @@ void FormatMbox::Init(int flag) next_func= &FormatMbox::GetFromLine; } -Buffer *FormatMbox::GetFromLine(void) +std::string *FormatMbox::GetFromLine(void) { time_t tm; @@ -43,30 +43,30 @@ time_t tm; tempbuf="From "; tempbuf += maildrop.msginfo.fromname; - tempbuf += ' '; + tempbuf += " "; const char *p=ctime(&tm); while (*p && *p != '\n') { - tempbuf.push(*p); + tempbuf.push_back(*p); ++p; } #if CRLF_TERM tempbuf.push('\r'); #endif - tempbuf.push('\n'); + tempbuf += "\n"; next_func= &FormatMbox::GetLineBuffer; return (&tempbuf); } -Buffer *FormatMbox::GetLineBuffer(void) +std::string *FormatMbox::GetLineBuffer(void) { - if (!(const char *)msglinebuf) return (0); + if (!msglinebuf.c_str()) return (0); if (do_escape) { - const char *p=msglinebuf; - int l=msglinebuf.Length(); + const char *p=msglinebuf.c_str(); + auto l=msglinebuf.size(); while (l && *p == '>') p++, l--; if (l >= 5 && @@ -78,12 +78,12 @@ Buffer *FormatMbox::GetLineBuffer(void) msglinebuf=tempbuf; } } - if (inheader && *(const char *)msglinebuf == '\n') + if (inheader && *msglinebuf.c_str() == '\n') inheader=0; if (inheader) { - const char *p=msglinebuf; - Buffer *bufp=0; + const char *p=msglinebuf.c_str(); + std::string *bufp=0; if ( tolower(*p) == 'f' && tolower(p[1]) == 'r' && tolower(p[2]) == 'o' && tolower(p[3]) == 'm' && @@ -107,23 +107,23 @@ Buffer *FormatMbox::GetLineBuffer(void) while (*p != '\n' && isspace(*p)) p++; for (l=0; p[l] != '\n'; l++) ; - bufp->append(p, l); + bufp->append(p, p+l); } } #if CRLF_TERM msglinebuf.pop(); // Drop terminating \n msglinebuf.push('\r'); - msglinebuf.push('\n'); + msglinebuf += "\n"; #endif next_func= &FormatMbox::GetNextLineBuffer; - msgsize += msglinebuf.Length(); + msgsize += msglinebuf.size(); return (&msglinebuf); } -Buffer *FormatMbox::GetNextLineBuffer(void) +std::string *FormatMbox::GetNextLineBuffer(void) { - msglinebuf.reset(); + msglinebuf.clear(); if (maildrop.msgptr->appendline(msglinebuf,0) == 0) return (GetLineBuffer()); return (0); // END OF FILE @@ -131,11 +131,11 @@ Buffer *FormatMbox::GetNextLineBuffer(void) int FormatMbox::DeliverTo(class Mio &mio) { -Buffer *bufptr; +std::string *bufptr; while ((bufptr=NextLine()) != NULL) { - if (mio.write((const char *)*bufptr, bufptr->Length()) < 0) + if (mio.write(bufptr->c_str(), bufptr->size()) < 0) { write_error: merr << "maildrop: error writing to mailbox.\n"; diff --git a/courier-imap-x/libs/maildrop/formatmbox.h b/courier-imap-x/libs/maildrop/formatmbox.h index 427fd2c24..2234d5215 100644 --- a/courier-imap-x/libs/maildrop/formatmbox.h +++ b/courier-imap-x/libs/maildrop/formatmbox.h @@ -21,7 +21,7 @@ // have a From line (and embedded From lines to be // escaped). Flag is zero if the message should not // be molested by From lines (when writing to a pipe). -// Buffer NextLine() - return consecutive lines, until a NULL pointer is +// std::string NextLine() - return consecutive lines, until a NULL pointer is // returned. // // When NULL pointer is returned, the hdrfrom, hdrsubject, and msgsize will @@ -31,20 +31,20 @@ class FormatMbox { - Buffer msglinebuf; - Buffer tempbuf; + std::string msglinebuf; + std::string tempbuf; int do_escape; - Buffer * (FormatMbox::* next_func)(void); + std::string * (FormatMbox::* next_func)(void); - Buffer *GetFromLine(void); - Buffer *GetLineBuffer(void); - Buffer *GetNextLineBuffer(void); + std::string *GetFromLine(void); + std::string *GetLineBuffer(void); + std::string *GetNextLineBuffer(void); int inheader; public: - Buffer hdrfrom, hdrsubject; + std::string hdrfrom, hdrsubject; unsigned long msgsize; FormatMbox() {} @@ -52,7 +52,7 @@ class FormatMbox { int HasMsg(); void Init(int); - Buffer *NextLine() + std::string *NextLine() { return ( (this->*next_func)() ); } diff --git a/courier-imap-x/libs/maildrop/funcs.C b/courier-imap-x/libs/maildrop/funcs.C index d25571d3d..2d57d1c01 100644 --- a/courier-imap-x/libs/maildrop/funcs.C +++ b/courier-imap-x/libs/maildrop/funcs.C @@ -43,7 +43,7 @@ void seekerr() const char *TempName(const char *dir, unsigned l) { -static Buffer buf; +static std::string buf; static unsigned counter=0; char hostname[256]; @@ -52,15 +52,14 @@ char hostname[256]; hostname[sizeof(hostname)-1]=0; buf=dir; - if (l > 0) buf.Length(l); - buf.append( (unsigned long)getpid() ); - buf += '.'; - buf.append( (unsigned long)counter++ ); - buf += '.'; + if (l > 0 && l < buf.size()) buf.resize(l); + add_integer(buf, getpid() ); + buf += "."; + add_integer(buf, counter++ ); + buf += "."; buf += hostname; - buf += '\0'; - return (buf); + return (buf.c_str()); } int backslash_char(int c) @@ -141,4 +140,3 @@ void killprocgroup() #endif #endif } - diff --git a/courier-imap-x/libs/maildrop/lexer.C b/courier-imap-x/libs/maildrop/lexer.C index 65e019af8..711068ba7 100644 --- a/courier-imap-x/libs/maildrop/lexer.C +++ b/courier-imap-x/libs/maildrop/lexer.C @@ -47,13 +47,12 @@ void Lexer::token(Token &t) case Token::logfile: case Token::log: { - Buffer errmsg; + std::string errmsg; errmsg="maildrop: '"; errmsg += t.Name(); errmsg += "' disabled in embedded mode.\n"; - errmsg += '\0'; - error((const char *)errmsg); + error(errmsg.c_str()); t.Type( Token::error ); break; } @@ -63,13 +62,12 @@ void Lexer::token(Token &t) if (VerboseLevel() > 8) { - Buffer debug; + std::string debug; debug="Tokenized "; debug += t.Name(); - debug += '\n'; - debug += '\0'; - error((const char *)debug); + debug += "\n"; + error(debug.c_str()); } } @@ -129,8 +127,8 @@ int c; // String, quoted by ", ', or ` -Buffer &pattern=t.String(); - pattern.reset(); +std::string &pattern=t.String(); + pattern.clear(); if (c == '\'' || c == '"' || c == '`') { @@ -160,7 +158,7 @@ missquote: if (q != '\\') { nextchar(); - pattern.push(q); + pattern.push_back(q); continue; } nextchar(); @@ -175,8 +173,8 @@ missquote: if (!isspace(qq) && qq != '\r' && qq != '\n') { if (qq != quote_char && qq != '\\') - pattern.push('\\'); - pattern.push(qq); + pattern.push_back('\\'); + pattern.push_back(qq); nextchar(); continue; } @@ -186,8 +184,8 @@ missquote: // current length of the string, and backtrack if // necessary. - int l=pattern.Length(); - pattern.push('\\'); + auto l=pattern.size(); + pattern.push_back('\\'); // Collect all whitespace after the backslash, // not including newline characters. @@ -195,7 +193,7 @@ missquote: while ((q=curchar()) >= 0 && isspace(q) && q != '\r' && q != '\n') { - pattern.push(q); + pattern.push_back(q); nextchar(); } if (q < 0) goto missquote; @@ -204,7 +202,7 @@ missquote: // a comment, we have a continuation. if (q != '#' && q != '\r' && q != '\n') continue; - pattern.Length(l); // Discard padding + pattern.resize(l); // Discard padding while (q != '\n') { if (q < 0) goto missquote; @@ -229,7 +227,7 @@ missquote: lasttokentype != Token::tokento && lasttokentype != Token::tokencc) { - pattern.push(c); + pattern.push_back(c); nextchar(); c=curchar(); if (c == '\r' || c == '\n' || c < 0 || isspace(c)) @@ -245,26 +243,26 @@ missquote: // an error if (c == '\\') { - pattern.push(c); + pattern.push_back(c); nextchar(); c=curchar(); if (c < 0 || c == '\r' || c == '\n') return; } - pattern.push(c); + pattern.push_back(c); nextchar(); } - pattern.push(c); + pattern.push_back(c); nextchar(); if ((c=curchar()) == ':') { - pattern.push(c); + pattern.push_back(c); nextchar(); while ( (c=curchar()) >= 0 && (isalnum(c) || c == '-' || c == '+' || c == '.' || c == ',')) { - pattern.push(c); + pattern.push_back(c); nextchar(); } } @@ -288,7 +286,7 @@ missquote: do { nextchar(); - pattern.push(c); + pattern.push_back(c); c=curchar(); } while ( ISUNQSTRING(c) ); @@ -297,10 +295,10 @@ missquote: nextchar(); c=curchar(); } - if (pattern.Length() == 2) + if (pattern.size() == 2) { - int n= ((int)(unsigned char)*(const char *)pattern) << 8 - | (unsigned char)((const char *)pattern)[1]; + int n= ((int)(unsigned char)*pattern.c_str()) << 8 + | (unsigned char)(pattern.c_str())[1]; switch (n) { case (('l' << 8) | 't'): @@ -510,11 +508,11 @@ void Lexer::errmsg(const char *emsg) void Lexer::errmsg(unsigned long lnum, const char *emsg) { -Buffer errbuf; +std::string errbuf; errbuf=filename; errbuf += "("; - errbuf.append(lnum); + add_integer(errbuf, lnum); errbuf += "): "; errbuf += emsg; errbuf += "\n"; diff --git a/courier-imap-x/libs/maildrop/lexer.h b/courier-imap-x/libs/maildrop/lexer.h index fb9b21210..f670a447a 100644 --- a/courier-imap-x/libs/maildrop/lexer.h +++ b/courier-imap-x/libs/maildrop/lexer.h @@ -27,7 +27,7 @@ class Lexer { Mio file; int linenum; - Buffer filename; + std::string filename; Token::tokentype lasttokentype; // curchar() represents the next character in the file. // Calling curchar() does NOT actual "read" the character, this diff --git a/courier-imap-x/libs/maildrop/log.C b/courier-imap-x/libs/maildrop/log.C index def176b7e..e318c33db 100644 --- a/courier-imap-x/libs/maildrop/log.C +++ b/courier-imap-x/libs/maildrop/log.C @@ -15,38 +15,41 @@ void log(const char *mailbox, int status, class FormatMbox &msg) { time_t t; -Buffer tbuf; -Buffer szbuf; +std::string tbuf; +std::string szbuf; if (maildrop.logfile.fd() < 0) return; // Logfile not open time(&t); tbuf=ctime(&t); - tbuf.pop(); // Drop trailing newline - msg.hdrfrom.Length(72); - msg.hdrsubject.Length(72); + tbuf.pop_back(); // Drop trailing newline + + if (msg.hdrfrom.size() > 72) + msg.hdrfrom.resize(72); + if (msg.hdrsubject.size() > 72) + msg.hdrsubject.resize(72); maildrop.logfile << "Date: " << tbuf << EOL; maildrop.logfile << "From: " << msg.hdrfrom << EOL; maildrop.logfile << "Subj: " << msg.hdrsubject << EOL; szbuf="("; - szbuf.append( (unsigned long)msg.msgsize); + add_integer(szbuf, msg.msgsize); szbuf += ")"; tbuf=mailbox; -int l= 72 - szbuf.Length(); +size_t l= 72 - szbuf.size(); - while (tbuf.Length() < l-1) - tbuf.push(' '); - tbuf.Length(l-1); - tbuf.push(' '); + while (tbuf.size() < l-1) + tbuf.push_back(' '); + tbuf.resize(l-1); + tbuf.push_back(' '); tbuf += szbuf; maildrop.logfile << (status ? "!Err: ":"File: ") << tbuf << EOL << EOL; maildrop.logfile.flush(); } -void log_line(const class Buffer &buf) +void log_line(const std::string &buf) { if (maildrop.logfile.fd() < 0) return; // Logfile not open maildrop.logfile << buf; diff --git a/courier-imap-x/libs/maildrop/log.h b/courier-imap-x/libs/maildrop/log.h index 36eb02ec5..f19c7972e 100644 --- a/courier-imap-x/libs/maildrop/log.h +++ b/courier-imap-x/libs/maildrop/log.h @@ -3,6 +3,7 @@ #include "config.h" +#include //////////////////////////////////////////////////////////////////////// // @@ -12,6 +13,6 @@ //////////////////////////////////////////////////////////////////////// void log(const char *mailbox, int status, class FormatMbox &); -void log_line(const class Buffer &); +void log_line(const std::string &); #endif diff --git a/courier-imap-x/libs/maildrop/maildir.C b/courier-imap-x/libs/maildrop/maildir.C index 6907b7d9b..b1ca5a127 100644 --- a/courier-imap-x/libs/maildrop/maildir.C +++ b/courier-imap-x/libs/maildrop/maildir.C @@ -1,5 +1,5 @@ /* -** Copyright 1998 - 2009 Double Precision, Inc. +** Copyright 1998 - 2023 Double Precision, Inc. ** See COPYING for distribution information. */ @@ -49,48 +49,42 @@ Maildir::~Maildir() int Maildir::IsMaildir(const char *name) { -Buffer dirname; -Buffer subdirname; +std::string dirname; +std::string subdirname; struct stat stat_buf; -int c; - if (!name || !*name) return (0); // Nope, not a Maildir dirname=name; - c=dirname.pop(); - if (c != SLASH_CHAR) dirname.push(c); // Strip trailing / + + if (dirname.back() == SLASH_CHAR) + dirname.pop_back(); // Strip trailing / subdirname=dirname; subdirname += "/tmp"; - subdirname += '\0'; - if ( stat( (const char *)subdirname, &stat_buf ) || + if ( stat( subdirname.c_str(), &stat_buf ) || ! S_ISDIR(stat_buf.st_mode) ) return (0); subdirname=dirname; subdirname += "/new"; - subdirname += '\0'; - if ( stat( (const char *)subdirname, &stat_buf ) || + if ( stat( subdirname.c_str(), &stat_buf ) || ! S_ISDIR(stat_buf.st_mode) ) return (0); subdirname=dirname; subdirname += "/cur"; - subdirname += '\0'; - if ( stat( (const char *)subdirname, &stat_buf ) || + if ( stat( subdirname.c_str(), &stat_buf ) || ! S_ISDIR(stat_buf.st_mode) ) return (0); return (1); // If it looks like a duck, walks like a duck... } int Maildir::MaildirOpen(const char *dir, Mio &file, off_t s) { - Buffer buf; + std::string buf; struct maildirsize quotainfo; const char *quotap; - Buffer quotabuf; + std::string quotabuf; - quotabuf="MAILDIRQUOTA"; /* Reuse a convenient buffer */ - quotabuf= *GetVar(quotabuf); - quotabuf += '\0'; + quotabuf= GetVar("MAILDIRQUOTA"); - quotap=quotabuf; + quotap=quotabuf.c_str(); if (!*quotap) quotap=NULL; @@ -100,28 +94,26 @@ int Maildir::MaildirOpen(const char *dir, Mio &file, off_t s) AlarmTimer abort_timer; static long counter=0; - buf.set(counter++); - buf += '\0'; + set_integer(buf, counter++); struct maildir_tmpcreate_info createInfo; maildir_tmpcreate_init(&createInfo); createInfo.maildir=dir; - createInfo.uniq=(const char *)buf; + createInfo.uniq=buf.c_str(); createInfo.msgsize=s; createInfo.openmode=0666; abort_timer.Set( 24 * 60 * 60 ); while (!abort_timer.Expired()) { - Buffer name_buf; + std::string name_buf; - name_buf="UMASK"; - const char *um=GetVarStr(name_buf); + std::string um=GetVar("UMASK"); unsigned int umask_val=077; - sscanf(um, "%o", &umask_val); + sscanf(um.c_str(), "%o", &umask_val); umask_val=umask(umask_val); @@ -130,18 +122,15 @@ static long counter=0; if (f >= 0) { - Buffer b; + std::string b; - b="FLAGS"; - - const char *flags=GetVarStr(b); + std::string flags=GetVar("FLAGS"); tmpname=createInfo.tmpname; - tmpname += '\0'; - if (flags) + if (!flags.empty()) { - const char *p=flags; + const char *p=flags.c_str(); while (*p) { @@ -154,12 +143,10 @@ static long counter=0; } } - if (flags && *flags) + if (!flags.empty()) { newname=createInfo.curname; - newname += ':'; - newname += '2'; - newname += ','; + newname += ":2,"; newname += flags; } else @@ -167,19 +154,17 @@ static long counter=0; newname=createInfo.newname; } - newname += '\0'; maildir_tmpcreate_free(&createInfo); file.fd(f); is_open=1; maildirRoot=dir; - maildirRoot += '\0'; if (maildir_quota_add_start(dir, "ainfo, s, 1, quotap)) { file.fd(-1); - unlink( (const char *)tmpname ); + unlink( tmpname.c_str() ); is_open=0; maildir_deliver_quota_warning(dir, quota_warn_percent, @@ -210,13 +195,9 @@ void Maildir::MaildirSave() { if (is_open) { - Buffer keywords; - - keywords="KEYWORDS"; - keywords=*GetVar(keywords); - keywords += '\0'; + std::string keywords=GetVar("KEYWORDS"); - const char *keywords_s=keywords; + const char *keywords_s=keywords.c_str(); while (*keywords_s && *keywords_s == ',') ++keywords_s; @@ -263,8 +244,8 @@ void Maildir::MaildirSave() char *tmpkname, *newkname; - if (maildir_kwSave( (const char *)maildirRoot, - strrchr(newname, '/')+1, kwm, + if (maildir_kwSave( maildirRoot.c_str(), + strrchr(newname.c_str(), '/')+1, kwm, &tmpkname, &newkname, 0) < 0) { libmail_kwmDestroy(kwm); @@ -279,7 +260,7 @@ void Maildir::MaildirSave() struct stat stat_buf; - if (stat(maildirRoot, &stat_buf) < 0) + if (stat(maildirRoot.c_str(), &stat_buf) < 0) { free(tmpkname); free(newkname); @@ -304,12 +285,12 @@ void Maildir::MaildirSave() free(newkname); } - Buffer dir; + std::string dir; - if (link( (const char *)tmpname, (const char *)newname) < 0) + if (link( tmpname.c_str(), newname.c_str()) < 0) { if (errno == EXDEV){ - if(rename((const char *)tmpname, (const char *)newname) < 0) + if(rename(tmpname.c_str(), newname.c_str()) < 0) throw "rename() failed."; is_afs = 1; } @@ -319,13 +300,12 @@ void Maildir::MaildirSave() } } dir=newname; - const char *p=dir; + const char *p=dir.c_str(); const char *q=strrchr(p, '/'); if (q) { - dir.Length(q-p); - dir.push((char)0); + dir.resize(q-p); #if EXPLICITDIRSYNC int syncfd=open(dir, O_RDONLY); @@ -337,11 +317,11 @@ void Maildir::MaildirSave() } #endif - dir.Length(q-p); + dir.resize(q-p); dir += "/../"; - dir.push((char)0); - maildir_deliver_quota_warning(dir, quota_warn_percent, + maildir_deliver_quota_warning(dir.c_str(), + quota_warn_percent, quota_warn_message); } } @@ -349,5 +329,5 @@ void Maildir::MaildirSave() void Maildir::MaildirAbort() { - if (is_open && !is_afs) unlink( (const char *)tmpname ); + if (is_open && !is_afs) unlink( tmpname.c_str() ); } diff --git a/courier-imap-x/libs/maildrop/maildir.h b/courier-imap-x/libs/maildrop/maildir.h index 7d7a6dabe..43c80d1b6 100644 --- a/courier-imap-x/libs/maildrop/maildir.h +++ b/courier-imap-x/libs/maildrop/maildir.h @@ -16,10 +16,10 @@ class Mio; class Maildir { int is_open; int is_afs; - Buffer maildirRoot; + std::string maildirRoot; public: - Buffer tmpname; - Buffer newname; + std::string tmpname; + std::string newname; Maildir(); virtual ~Maildir(); diff --git a/courier-imap-x/libs/maildrop/maildrop.1.in b/courier-imap-x/libs/maildrop/maildrop.1.in index 33ba57e0c..166411234 100644 --- a/courier-imap-x/libs/maildrop/maildrop.1.in +++ b/courier-imap-x/libs/maildrop/maildrop.1.in @@ -382,18 +382,8 @@ option is \fImailfilter\-lists\-maildrop\fR, \fBmaildrop\fR will try to open the following files, in order: -.IP \[bu] 2 -$HOME/\&.mailfilters/mailfilter\-lists\-maildrop -.IP \[bu] -$HOME/\&.mailfilters/mailfilter\-lists\-maildrop\-default -.IP \[bu] -$HOME/\&.mailfilters/mailfilter\-lists\-default -.IP \[bu] -$HOME/\&.mailfilters/mailfilter\-default -.IP \[bu] -$HOME/\&.mailfilters/default +.sp .if n \{\ .RS 4 .\} .nf $HOME/\&.mailfilters/mailfilter\-lists\-maildrop $HOME/\&.mailfilters/mailfilter\-lists\-maildrop\-default $HOME/\&.mailfilters/mailfilter\-lists\-default $HOME/\&.mailfilters/mailfilter\-default $HOME/\&.mailfilters/default .fi .if n \{\ .RE .\} .sp -.RS 0 Note that \fBmaildrop\fR looks for \-default files ONLY if diff --git a/courier-imap-x/libs/maildrop/maildrop.h b/courier-imap-x/libs/maildrop/maildrop.h index d450d29a6..946f42fe8 100644 --- a/courier-imap-x/libs/maildrop/maildrop.h +++ b/courier-imap-x/libs/maildrop/maildrop.h @@ -30,14 +30,14 @@ static int sigfpe; // Floating point exception trapped. #if SHARED_TEMPDIR #else - Buffer tempdir; // Directory for temporary files + std::string tempdir; // Directory for temporary files #endif - Buffer init_home; // Initial HOME - Buffer init_logname; // Initial LOGNAME - Buffer init_shell; // Initial SHELL - Buffer init_default; // Initial DEFAULT - Buffer init_quota; // Initial MAILDIRQUOTA + std::string init_home; // Initial HOME + std::string init_logname; // Initial LOGNAME + std::string init_shell; // Initial SHELL + std::string init_default; // Initial DEFAULT + std::string init_quota; // Initial MAILDIRQUOTA Mio logfile; // Log file. Maildrop(); diff --git a/courier-imap-x/libs/maildrop/maildropfilter.7.in b/courier-imap-x/libs/maildrop/maildropfilter.7.in index cd67212bd..6894b3f9f 100644 --- a/courier-imap-x/libs/maildrop/maildropfilter.7.in +++ b/courier-imap-x/libs/maildrop/maildropfilter.7.in @@ -4,12 +4,12 @@ .\" Title: maildropfilter .\" Author: Sam Varshavchik .\" Generator: DocBook XSL Stylesheets vsnapshot -.\" Date: 11/12/2022 +.\" Date: 04/02/2023 .\" Manual: Double Precision, Inc. .\" Source: Courier Mail Server .\" Language: English .\" -.TH "MAILDROPFILTER" "7" "11/12/2022" "Courier Mail Server" "Double Precision, Inc\&." +.TH "MAILDROPFILTER" "7" "04/02/2023" "Courier Mail Server" "Double Precision, Inc\&." .\" ----------------------------------------------------------------- .\" * Define some portability stuff .\" ----------------------------------------------------------------- @@ -383,37 +383,6 @@ seconds before trying to create its own dot\-lock file, in order to avoid a race Name of the user to who the message is being delivered\&. .RE .PP -\fIMAILDROP_OLD_REGEXP\fR -.RS 4 -Revert to using the old legacy pattern matching engine\&. Versions of -\fBmaildrop\fR -prior to version 2\&.0 (included in the -Courier -mail server 0\&.51, and earlier), used a built\-in pattern matching engine, instead of using the -PCRE -library (see -the section called \(lqPatterns\(rq)\&. -\fBmaildrop\fR -1\&.x used a different syntax for patterns, which is no longer described in this manual page\&. The old pattern matching engine is still available, by setting -\fIMAILDROP_OLD_REGEXP\fR -to -\(lq1\(rq\&. Setting this variable will use the legacy pattern matching engine for the rest of the -\fBmaildrop\fR -recipe file\&. -.sp -The pattern matching engine will be removed completely in a future version of maildrop\&. This setting provides for a transitional period of converting old recipes\&. -\fIMAILDROP_OLD_REGEXP\fR -can be set to -\(lq1\(rq -in the global -maildroprc -file, then reset to -\(lq0\(rq -in each individual -\fBmaildrop\fR -recipe file, after it gets converted to the new syntax\&. -.RE -.PP \fIMAILFILTER\fR .RS 4 This is the name of the original @@ -885,24 +854,6 @@ header is usually much more complicated, and can\*(Aqt be handled that easily\&. .ps -1 .br .PP -Subpatterns are not processed in the -foreach -statement\&. -.sp .5v -.RE -.if n \{\ -.sp -.\} -.RS 4 -.it 1 an-trap -.nr an-no-space-flag 1 -.nr an-break-flag 1 -.br -.ps +1 -\fBNote\fR -.ps -1 -.br -.PP The contents of all \fIMATCH\fR variables use UTF\-8 encoding\&. As explained in @@ -1362,19 +1313,16 @@ The \fBforeach\fR statement executes a block of statements for each occurrence of the given pattern in the given message, or expression\&. On every iteration \fIMATCH\fR -variable will be set to the matched string\&. All the usual options may be applied to the pattern match, EXCEPT the following: +variable will be set to the matched string\&. Subpatterns set +\fIMATCH1\fR, +\fIMATCH2\fR, and so on, before beginning each iteration\&. +.PP +All the usual options may be applied to the pattern match, EXCEPT the following: .PP ,xxx,yyy .RS 4 Weighted scoring is meaningless, in this context\&. .RE -.PP -( \&.\&.\&. ) -.RS 4 -Subpatterns are not processed\&. Only the -\fIMATCH\fR -variable will be set for each found pattern\&. -.RE .RE .sp .it 1 an-trap diff --git a/courier-imap-x/libs/maildrop/maildropfilter.html.in b/courier-imap-x/libs/maildrop/maildropfilter.html.in index 39a8e61d4..ca716db1f 100644 --- a/courier-imap-x/libs/maildrop/maildropfilter.html.in +++ b/courier-imap-x/libs/maildrop/maildropfilter.html.in @@ -217,27 +217,7 @@ MAILBOX="${HOME-WORD}/Mailbox"

LOGNAME

Name of the user to who the message is being delivered. -

MAILDROP_OLD_REGEXP

- Revert to using the old legacy pattern matching engine. - Versions of maildrop prior to version 2.0 - (included in the Courier mail server 0.51, - and earlier), used a built-in pattern matching engine, instead of using the - PCRE - library (see - the section called “Patterns”). - maildrop 1.x used a different syntax for patterns, which - is no longer described in this manual page. - The old pattern matching engine is still available, by - setting MAILDROP_OLD_REGEXP to 1. - Setting this variable will use the legacy pattern matching engine for the - rest of the maildrop recipe file.

- The pattern matching engine will be removed completely in a future version - of maildrop. - This setting provides for a transitional period of converting old recipes. - MAILDROP_OLD_REGEXP can be set to 1 in - the global maildroprc file, then reset to 0 - in each individual maildrop recipe file, after it gets - converted to the new syntax.

MAILFILTER

This is the name of the original filter file +

MAILFILTER

This is the name of the original filter file that was given to maildrop on the command line. This is mostly useful to -default filter files, it allows them to obtain the value of the -M option @@ -513,8 +493,6 @@ From: postmaster@localhost Of course, in real world the From: header is usually much more complicated, and can't be handled that easily. This is just an illustrative example.

Note

- Subpatterns are not processed in the foreach - statement.

Note

The contents of all MATCH variables use UTF-8 encoding. As explained in the section called “Patterns” the message gets transcoded into a canonical UTF-8 format, @@ -692,15 +670,18 @@ foreach (expression) =~ /pattern/:options

The foreach statement executes a block of statements for each - occurrence of the given pattern in the given message, or expression. On every - iteration MATCH variable will be set to the matched string. + occurrence of the given pattern in the given message, or expression. + On every + iteration MATCH variable will be set to the + matched string. + Subpatterns set MATCH1, + MATCH2, and so on, before + beginning each iteration. +

All the usual options may be applied to the pattern match, EXCEPT the following:

,xxx,yyy

- Weighted scoring is meaningless, in this context.

( ... )

- Subpatterns are not processed. - Only the MATCH variable will be set for each found - pattern.

+ Weighted scoring is meaningless, in this context.

if - conditional execution

 if (expression)
 {
diff --git a/courier-imap-x/libs/maildrop/main.C b/courier-imap-x/libs/maildrop/main.C
index e414e3ddf..528edd9cc 100644
--- a/courier-imap-x/libs/maildrop/main.C
+++ b/courier-imap-x/libs/maildrop/main.C
@@ -1,5 +1,5 @@
 /*
-** Copyright 1998 - 2009 Double Precision, Inc.
+** Copyright 1998 - 2023 Double Precision, Inc.
 ** See COPYING for distribution information.
 */
 
@@ -168,7 +168,7 @@ static int trusted_uidgid(uid_t uid, gid_t gid, gid_t gid2)
 	return (0);
 }
 
-static void sethostname(Buffer &buf)
+static void sethostname(std::string &buf)
 {
 char    hostname[256];
 
@@ -182,7 +182,7 @@ char    hostname[256];
 
 static void copyright()
 {
-static const char msg[]="maildrop " VERSION " Copyright 1998-2018 Double Precision, Inc."
+static const char msg[]="maildrop " VERSION " Copyright 1998-2023 Double Precision, Inc."
 
 #if CRLF_TERM
 	"\r\n"
@@ -255,7 +255,7 @@ static const char msg[]="maildrop " VERSION " Copyright 1998-2018 Double Precisi
 void Maildrop::reset_vars()
 {
 int	i;
-Buffer	name, value;
+std::string	name, value;
 
 	for (i=0; i<(int)(sizeof(defaults_vars)/sizeof(defaults_vars[0])); i++)
 	{
@@ -276,7 +276,7 @@ Buffer	name, value;
 	value="077";
 	SetVar(name, value);
 
-	if (maildrop.init_quota.Length() > 0)
+	if (maildrop.init_quota.size() > 0)
 	{
 		name="MAILDIRQUOTA";
 		SetVar(name, maildrop.init_quota);
@@ -299,10 +299,9 @@ static int callback_authlib(struct authinfo *auth,
 
 	if (VerboseLevel() > 1)
 	{
-		Buffer b;
+		std::string b;
 
-		b.set(auth->sysgroupid);
-		b.push(0);
+		add_integer(b, auth->sysgroupid);
 
 		merr << "maildrop: authlib: groupid="
 		     << b << "\n";
@@ -335,10 +334,9 @@ static int callback_authlib(struct authinfo *auth,
 
 	if (VerboseLevel() > 1)
 	{
-		Buffer b;
+		std::string b;
 
-		b.set(u);
-		b.push(0);
+		add_integer(b, u);
 
 		merr << "maildrop: authlib: userid="
 		     << b << "\n";
@@ -406,10 +404,7 @@ static int callback_dovecotauth(struct dovecotauthinfo *auth,
 
 	if (VerboseLevel() > 1)
 	{
-		Buffer b;
-
-		b.set(auth->sysgroupid);
-		b.push(0);
+		std::string b = std::to_string(auth->sysgroupid);
 
 		merr << "maildrop: dovecotauth: groupid="
 		     << b << "\n";
@@ -438,10 +433,7 @@ static int callback_dovecotauth(struct dovecotauthinfo *auth,
 
 	if (VerboseLevel() > 1)
 	{
-		Buffer b;
-
-		b.set(u);
-		b.push(0);
+		std::string b = std::to_string(u);
 
 		merr << "maildrop: dovecotauth: userid="
 		     << b << "\n";
@@ -503,10 +495,10 @@ const	char *deliverymode=0;
 char *embedded_filter=0;
 const	char *from=0;
 int     explicit_from=0;
-Buffer	recipe;
+std::string	recipe;
 uid_t	orig_uid;
 gid_t	orig_gid, orig_gid2;
-Buffer	extra_headers;
+std::string	extra_headers;
 struct passwd *my_pw;
 int	found;
 #if	HAVE_COURIER
@@ -545,7 +537,7 @@ const	char *dovecotauth_addr=0;
 			break;
 		case 'V':
 			if (!*optarg && argn < argc)	optarg=argv[argn++];
-			maildrop.verbose_level=atoi(optarg);
+			SetVar("VERBOSE", optarg);
 			break;
 		case 'v':
 			copyright();
@@ -578,7 +570,7 @@ const	char *dovecotauth_addr=0;
 			if (*optarg)
 			{
 				extra_headers += optarg;
-				extra_headers += '\n';
+				extra_headers += "\n";
 			}
 			break;
 		case 'f':
@@ -802,8 +794,8 @@ uid_t	my_u=getuid();
 	}
 
 int	i;
-Buffer	name;
-Buffer	value;
+std::string	name;
+std::string	value;
 
 	for (i=0; environ[i]; i++)
 	{
@@ -811,13 +803,13 @@ Buffer	value;
 
 		char	*p=strchr(environ[i], '=');
 
-		value= p ? (name.Length(p - environ[i]), p+1):"";
+		value= p ? (name.resize(p - environ[i]), p+1):"";
 
 		if (maildrop.isdelivery)
 		{
 			if (name == "LANG" ||
 			    name == "LANGUAGE" ||
-			    strncmp(name, "LC_", 3) == 0)
+			    strncmp(name.c_str(), "LC_", 3) == 0)
 				;
 			else
 				continue;
@@ -830,7 +822,7 @@ Buffer	value;
 	while (argn < argc)
 	{
 		name="";
-		name.append( (unsigned long)i);
+		add_integer(name, i);
 		value=argv[argn++];
 		SetVar(name, value);
 		++i;
@@ -871,12 +863,11 @@ Buffer	value;
 	if (deliverymode)
 	{
 	struct	stat	buf;
-	Buffer	b;
+	std::string	b;
 
 		b=maildrop.init_home;
-		b += '\0';
 
-	const char *h=b;
+		const char *h=b.c_str();
 
 		if (VerboseLevel() > 1)
 			merr << "maildrop: Changing to " << h << "\n";
@@ -925,12 +916,11 @@ Buffer	value;
 #else
 	maildrop.tempdir=maildrop.init_home;
 	maildrop.tempdir += "/" TEMPDIR;
-	maildrop.tempdir += '\0';
-	mkdir( (const char *)maildrop.tempdir, 0700 );
+	mkdir( maildrop.tempdir.c_str(), 0700 );
 #endif
 	maildrop.reset_vars();
 
-Buffer	msg;
+std::string	msg;
 
 	signal(SIGALRM, alarm_handler);
 	alarm(GLOBAL_TIMEOUT);
@@ -958,7 +948,7 @@ Buffer	msg;
 		!trusted_uidgid(orig_uid, orig_gid, orig_gid2) ||
 #endif
 
-		maildrop.msginfo.fromname.Length() == 0)
+		maildrop.msginfo.fromname.size() == 0)
 	{
 		maildrop.msginfo.fromname=maildrop.init_logname;
 	}
@@ -972,13 +962,12 @@ Buffer	msg;
 
 	if (VerboseLevel() > 1)
 	{
-		msg.reset();
-		msg.append("Message envelope sender=");
-		if (maildrop.msginfo.fromname.Length() > 0)
+		msg.clear();
+		msg += "Message envelope sender=";
+		if (maildrop.msginfo.fromname.size() > 0)
 			msg += maildrop.msginfo.fromname;
-		msg.append("\n");
-		msg += '\0';
-		merr.write(msg);
+		msg += "\n";
+		merr.write(msg.c_str());
 	}
 
 	name="HOSTNAME";
@@ -1036,16 +1025,14 @@ int	firstdefault=1;
 			msg += recipe;
 			if (VerboseLevel() > 1)
 				merr << "maildrop: Attempting " << msg << "\n";
-			msg += '\0';
-			fd=in.Open((const char *)msg);
+			fd=in.Open(msg.c_str());
 		}
 		else
 		{
 			msg=recipe;
 			if (VerboseLevel() > 1)
 				merr << "maildrop: Attempting " << msg << "\n";
-			msg += '\0';
-			fd=in.Open((const char *)msg);
+			fd=in.Open(msg.c_str());
 			break;
 		}
 #ifndef	DEFAULTEXT
@@ -1064,24 +1051,25 @@ int	firstdefault=1;
 		// Pop DEFAULTEXT bytes from end of recipe name
 
 		for (fd=sizeof(DEFAULTEXT)-1; fd; --fd)
-			recipe.pop();
+			recipe.pop_back();
 
-		while (recipe.Length())
+		while (recipe.size())
 		{
-			if (recipe.pop() == '-')
+			auto dash=recipe.back();
+			recipe.pop_back();
+			if (dash == '-')
 			{
 				recipe += DEFAULTEXT;
 				break;
 			}
 		}
-		if (recipe.Length() == 0)
+		if (recipe.size() == 0)
 		{
 			msg=".mailfilters/";
 			msg += DEFAULTEXT+1;
 			if (VerboseLevel() > 1)
 				merr << "maildrop: Attempting " << msg << "\n";
-			msg += '\0';
-			fd=in.Open((const char *)msg);
+			fd=in.Open(msg.c_str());
 			break;
 		}
 #endif
@@ -1132,24 +1120,19 @@ int	firstdefault=1;
 
 	if (!maildrop.embedded_mode)
 	{
-		name="DEFAULT";
-
-	const char *v=GetVarStr(name);
+		value=GetVar("DEFAULT");
 
-		if (!v)
+		if (value.empty())
 		{
 			errexit=EX_TEMPFAIL;
 			throw "DEFAULT mailbox not defined.";
 		}
 
-		value=v;
-		value += '\0';
-		if (delivery((const char *)value) < 0)
+		if (delivery(value.c_str()) < 0)
 			return (EX_TEMPFAIL);
 	}
 
-	value="EXITCODE";
-	return ( GetVar(value)->Int("0") );
+	return extract_int( GetVar("EXITCODE"), "0");
 }
 
 int main(int argc, char **argv)
@@ -1164,7 +1147,7 @@ int main(int argc, char **argv)
 
 const char *GetDefaultMailbox(const char *username)
 {
-static Buffer buf;
+static std::string buf;
 int	isfile=0;
 
 	buf="";
@@ -1174,7 +1157,7 @@ const	char *p=DEFAULT_DEF;
 	if (*p != SLASH_CHAR)	// Relative to home directory
 	{
 		buf=maildrop.init_home;
-		buf.push(SLASH_CHAR);
+		buf.push_back(SLASH_CHAR);
 		isfile=1;
 	}
 
@@ -1182,7 +1165,7 @@ const	char *p=DEFAULT_DEF;
 	{
 		if (*p != '=')
 		{
-			buf.push(*p);
+			buf.push_back(*p);
 			++p;
 		}
 
@@ -1190,7 +1173,7 @@ const	char *p=DEFAULT_DEF;
 
 		while (*p == '=')
 		{
-			buf.push (*q ? *q:'.');
+			buf.push_back(*q ? *q:'.');
 			if (*q)	q++;
 			p++;
 		}
@@ -1198,11 +1181,10 @@ const	char *p=DEFAULT_DEF;
 
 	if (!isfile)
 	{
-		buf.push(SLASH_CHAR);
+		buf.push_back(SLASH_CHAR);
 		buf += username;
 	}
-	buf += '\0';
-	return (buf);
+	return (buf.c_str());
 }
 
 #if	SHARED_TEMPDIR
@@ -1210,11 +1192,10 @@ const	char *p=DEFAULT_DEF;
 #else
 const char *TempName()
 {
-Buffer	t;
+std::string	t;
 
-	t=(const char *)maildrop.tempdir;
+	t=maildrop.tempdir.c_str();
 	t += "/tmp.";
-	t += '\0';
-	return (TempName((const char *)t, 0));
+	return (TempName(t.c_str(), 0));
 }
 #endif
diff --git a/courier-imap-x/libs/maildrop/message.C b/courier-imap-x/libs/maildrop/message.C
index a32ae6e7c..2ab6be41a 100644
--- a/courier-imap-x/libs/maildrop/message.C
+++ b/courier-imap-x/libs/maildrop/message.C
@@ -73,7 +73,7 @@ void Message::Init()
 	rfc2045p=rfc2045_alloc();
 }
 
-void Message::Init(int fd, const Buffer &extra_headers)
+void Message::Init(int fd, const std::string &extra_headers)
 {
 	Init();
 	ExtraHeaders(extra_headers);
@@ -192,9 +192,9 @@ void Message::Init(const void *data, unsigned cnt)
 	msgsize += cnt;
 }
 
-void Message::ExtraHeaders(const Buffer &buf)
+void Message::ExtraHeaders(const std::string &buf)
 {
-	rfc2045_parse(rfc2045p, (const char *)buf, buf.Length());
+	rfc2045_parse(rfc2045p, buf.c_str(), buf.size());
 
 	if ( extra_headers )
 	{
@@ -202,12 +202,12 @@ void Message::ExtraHeaders(const Buffer &buf)
 		extra_headers=0;
 	}
 	extra_headersptr=0;
-	if (!buf.Length())	return;
+	if (!buf.size())	return;
 
-	extra_headers=new char[buf.Length()+1];
+	extra_headers=new char[buf.size()+1];
 	if (!extra_headers)	outofmem();
-	memcpy(extra_headers, (const char *)buf, buf.Length());
-	extra_headers[buf.Length()]=0;
+	memcpy(extra_headers, buf.c_str(), buf.size());
+	extra_headers[buf.size()]=0;
 	extra_headersptr=extra_headers;
 	if (!*extra_headersptr)	extra_headersptr=0;
 }
@@ -242,7 +242,7 @@ void Message::seekerr()
 	throw "Seek error.";
 }
 
-int Message::appendline(Buffer &buf, int stripcr)
+int Message::appendline(std::string &buf, int stripcr)
 {
 	if (mio.fd() >= 0 || extra_headersptr)
 	{
@@ -253,13 +253,13 @@ int Message::appendline(Buffer &buf, int stripcr)
 		while ((c=get_c()) > 0 && c != '\n')
 		{
 			eof=0;
-			buf.push(c);
+			buf.push_back(c);
 			lastc=c;
 		}
 		if (c < 0 && eof)	return (-1);
-		if (stripcr && lastc == '\r')	buf.pop();
+		if (stripcr && lastc == '\r')	buf.pop_back();
 						// Drop trailing CRs
-		buf.push('\n');
+		buf += "\n";
 		return (0);
 	}
 
@@ -275,34 +275,34 @@ unsigned i;
 		if (bufptr[i] == '\n')
 		{
 			if (i > 0 && stripcr && bufptr[i-1] == '\r')
-				buf.append(bufptr, i-1);
+				buf.append(bufptr, bufptr+i-1);
 				// Drop trailing CRs
 			else
-				buf.append(bufptr, i);
-			buf += '\n';
+				buf.append(bufptr, bufptr+i);
+			buf += "\n";
 			bufptr += ++i;
 			return (0);
 		}
 
 	if (stripcr && bufptr[i-1] == '\r')
-		buf.append(bufptr, cnt-1);
+		buf.append(bufptr, bufptr+cnt-1);
 				// Drop trailing CRs
 	else
-		buf.append(bufptr, cnt);
+		buf.append(bufptr, bufptr+cnt);
 	bufptr += cnt;
-	buf += '\n';
+	buf += "\n";
 	return (0);
 }
 
 void Message::setmsgsize()
 {
-Buffer	n,v;
+std::string	n,v;
 
 	n="SIZE";
-	v.append((unsigned long)MessageSize());
+	add_integer(v, MessageSize());
 	SetVar(n,v);
 	n="LINES";
-	v.reset();
-	v.append((unsigned long)MessageLines());
+	v.clear();
+	add_integer(v, MessageLines());
 	SetVar(n,v);
 }
diff --git a/courier-imap-x/libs/maildrop/message.h b/courier-imap-x/libs/maildrop/message.h
index f149f7f11..6795bd372 100644
--- a/courier-imap-x/libs/maildrop/message.h
+++ b/courier-imap-x/libs/maildrop/message.h
@@ -2,6 +2,7 @@
 #define	message_h
 
 #include "rfc2045/rfc2045.h"
+#include 
 
 //////////////////////////////////////////////////////////////////////////////
 //
@@ -44,8 +45,6 @@
 
 #endif
 
-class Buffer;
-
 class Message {
 	Mio mio;
 	TempFile tempfile;
@@ -61,15 +60,15 @@ class Message {
 public:
 	Message();
 	~Message();
-	void Init(int, const Buffer &extra_headers);
+	void Init(int, const std::string &extra_headers);
 	// Initialize from file descriptor
 
 	void Init();		// Begin initializing externally
 	void Init(const void *, unsigned);	// From existing contents.
-	void ExtraHeaders(const Buffer &);
+	void ExtraHeaders(const std::string &);
 	void Rewind();		// Start reading the message
 	void RewindIgnore();	// Rewind, ignore msginfo
-	int appendline(Buffer &, int=1);	// Read newline-terminated line.
+	int appendline(std::string &, int=1);	// Read newline-terminated line.
 	void seek(off_t);
 	off_t tell();
 	int get_c();
diff --git a/courier-imap-x/libs/maildrop/messageinfo.C b/courier-imap-x/libs/maildrop/messageinfo.C
index a7c339edf..aef003237 100644
--- a/courier-imap-x/libs/maildrop/messageinfo.C
+++ b/courier-imap-x/libs/maildrop/messageinfo.C
@@ -5,29 +5,29 @@
 
 void MessageInfo::info(Message &msg)
 {
-Buffer	buf;
+std::string	buf;
 
 	msg.Rewind();
-	fromname.set("MAILER-DAEMON");
+	fromname="MAILER-DAEMON";
 
 	for (;;)
 	{
-		buf.reset();
+		buf.clear();
 		if (msg.appendline(buf) < 0)	return;
 
-		int	l=buf.Length();
+		auto	l=buf.size();
 
-		const char *p=buf;
+		const char *p=buf.c_str();
 
 		if (l && p[l-1] == '\n')
 		{
 			--l;
-			buf.Length(l);
+			buf.resize(l);
 		}
 
 		if (l == 0)	break;
 
-		p=buf;
+		p=buf.c_str();
 		if (strncasecmp(p, "Return-Path:", 12))
 			continue;
 
@@ -46,7 +46,7 @@ Buffer	buf;
 			--l;
 		}
 
-		int i;
+		size_t i;
 
 		for (i=0; i
 #include	
+#include	
 
 // Fault-tolerant wrappers around I/O functions - they automatically retry
 // when they get an EINTR (for kernels that do not restart the calls themselves)
@@ -64,7 +65,7 @@ class Mio {
 	void write(const char *p) { write(p, strlen(p)); }
 
 	Mio &operator<<(const char *p) { write(p); return (*this); }
-	Mio &operator<<(const class Buffer &);
+	Mio &operator<<(const std::string &);
 
 private:
 	int fill();
diff --git a/courier-imap-x/libs/maildrop/recipe.C b/courier-imap-x/libs/maildrop/recipe.C
index 88f6bc486..77c82f45f 100644
--- a/courier-imap-x/libs/maildrop/recipe.C
+++ b/courier-imap-x/libs/maildrop/recipe.C
@@ -44,7 +44,7 @@ void Recipe::errmsg(RecipeNode &r, const char *emsg)
 
 void Recipe::ExecuteRecipe()
 {
-Buffer	b;
+std::string	b;
 
 	if (topNode)	topNode->Evaluate(*this, b);
 }
diff --git a/courier-imap-x/libs/maildrop/recipenode.C b/courier-imap-x/libs/maildrop/recipenode.C
index 296efcd94..fe9ed1906 100644
--- a/courier-imap-x/libs/maildrop/recipenode.C
+++ b/courier-imap-x/libs/maildrop/recipenode.C
@@ -1,5 +1,5 @@
 /*
-** Copyright 1998 - 2009 Double Precision, Inc.
+** Copyright 1998 - 2023 Double Precision, Inc.
 ** See COPYING for distribution information.
 */
 
@@ -27,7 +27,7 @@
 
 extern int xfilter(const char *, int);
 
-static void parse_backslash(const Buffer &, Buffer &);
+static void parse_backslash(const std::string &, std::string &);
 
 //////////////////////////////////////////////////////////////////////////
 //
@@ -63,17 +63,17 @@ void	RecipeNode::AppendSibling(RecipeNode *chld)
 	lastChild=chld;
 }
 
-void	RecipeNode::Evaluate(Recipe &r, Buffer &b)
+void	RecipeNode::Evaluate(Recipe &r, std::string &b)
 {
 RecipeNode	*c;
 
-	b.reset();
+	b.clear();
 	switch (nodeType)	{
 	case statementlist:
 
 		for (c=firstChild; c; c=c->nextSibling)
 		{
-			b.reset();
+			b.clear();
 			c->Evaluate(r, b);
 		}
 		break;
@@ -84,19 +84,18 @@ RecipeNode	*c;
 		firstChild->nextSibling->Evaluate(r, b);
 		if (VerboseLevel() > 3)
 		{
-		Buffer	s;
+		std::string	s;
 
 			s=firstChild->str;
 			s += "=\"";
 			s += b;
 			s += "\"";
-			s += '\0';
-			r.errmsg(*this, s);
+			r.errmsg(*this, s.c_str());
 		}
 		SetVar(firstChild->str, b);
 		break;
 	case regexpr:
-		EvaluateRegExp(r, b, (Buffer *)NULL);
+		EvaluateRegExp(r, b, nullptr);
 		break;
 	case qstring:
 	case sqstring:
@@ -119,8 +118,8 @@ RecipeNode	*c;
 			throw "Internal error in evaluate binary.";
 		{
 		double a1, a2;
-		Buffer ba1, ba2;
-		Buffer debug;
+		std::string ba1, ba2;
+		std::string debug;
 
 			firstChild->Evaluate(r, ba1);
 			firstChild->nextSibling->Evaluate(r, ba2);
@@ -133,11 +132,9 @@ RecipeNode	*c;
 				debug += ba2;
 			}
 
-			ba1 += '\0';
-			ba2 += '\0';
 			maildrop.sigfpe=0;
-			a1=atof( (const char *)ba1 );
-			a2=atof( (const char *)ba2 );
+			a1=atof( ba1.c_str() );
+			a2=atof( ba2.c_str() );
 			switch (nodeType)	{
 			case add:
 				a1 += a2;
@@ -207,13 +204,12 @@ RecipeNode	*c;
 				r.errmsg(*this, "Numerical exception.\n");
 				a1=0;
 			}
-			b.append(a1);
+			add_number(b, a1);
 			if (VerboseLevel() > 5)
 			{
 				debug += ", result is ";
 				debug += b;
-				debug += '\0';
-				r.errmsg(*this, debug);
+				r.errmsg(*this, debug.c_str());
 			}
 		}
 		break;
@@ -226,8 +222,8 @@ RecipeNode	*c;
 		if (!firstChild || !firstChild->nextSibling)
 			throw "Internal error in evaluate binary.";
 		{
-		Buffer ba1, ba2;
-		Buffer debug;
+		std::string ba1, ba2;
+		std::string debug;
 
 			firstChild->Evaluate(r, ba1);
 			firstChild->nextSibling->Evaluate(r, ba2);
@@ -240,10 +236,8 @@ RecipeNode	*c;
 				debug += ba2;
 			}
 
-			ba1 += '\0';
-			ba2 += '\0';
 
-		int	n=strcmp( (const char *)ba1, (const char *)ba2);
+			int	n=strcmp( ba1.c_str(), ba2.c_str());
 
 			switch (nodeType)	{
 			case strlessthan:
@@ -279,13 +273,12 @@ RecipeNode	*c;
 			default:
 				break;
 			}
-			b.append((unsigned long)n);
+			add_integer(b, n);
 			if (VerboseLevel() > 5)
 			{
 				debug += ", result is ";
 				debug += b;
-				debug += '\0';
-				r.errmsg(*this, debug);
+				r.errmsg(*this, debug.c_str());
 			}
 			break;
 		}
@@ -295,28 +288,26 @@ RecipeNode	*c;
 		firstChild->Evaluate(r, b);
 		if (VerboseLevel() > 5)
 		{
-		Buffer	debug;
+		std::string	debug;
 
 			debug="Operation on: ";
 			debug += b;
 			debug += " - logical not.";
-			debug += '\0';
-			r.errmsg(*this, debug);
+			r.errmsg(*this, debug.c_str());
 		}
 		{
 		int n= !boolean(b);
 
-			b.reset();
-			b.append( (unsigned long)n );
+			b.clear();
+			add_integer(b, n );
 		}
 		if (VerboseLevel() > 5)
 		{
-		Buffer	debug;
+		std::string	debug;
 
 			debug="Operation: logical not=";
 			debug += b;
-			debug += '\0';
-			r.errmsg(*this, debug);
+			r.errmsg(*this, debug.c_str());
 		}
 		break;
 	case bitwisenot:
@@ -325,28 +316,26 @@ RecipeNode	*c;
 		firstChild->Evaluate(r, b);
 		if (VerboseLevel() > 5)
 		{
-		Buffer	debug;
+		std::string	debug;
 
 			debug="Operation on: ";
 			debug += b;
 			debug += " - bitwise not.";
-			debug += '\0';
-			r.errmsg(*this, debug);
+			r.errmsg(*this, debug.c_str());
 		}
 		maildrop.sigfpe=0;
 		{
-			b += '\0';
 
-		long n=atol( (const char *) b);
+			long n=atol( b.c_str());
 
 			n= ~n;
-			b.reset();
+			b.clear();
 			if (n < 0)
 			{
-				b += '-';
+				b += "-";
 				n= -n;
 			}
-			b.append( (unsigned long)n );
+			add_integer(b, n );
 		}
 		if (maildrop.sigfpe)
 		{
@@ -354,12 +343,11 @@ RecipeNode	*c;
 		}
 		if (VerboseLevel() > 5)
 		{
-		Buffer	debug;
+		std::string	debug;
 
 			debug="Operation: bitwise not=";
 			debug += b;
-			debug += '\0';
-			r.errmsg(*this, debug);
+			r.errmsg(*this, debug.c_str());
 		}
 		break;
 	case logicalor:
@@ -368,33 +356,30 @@ RecipeNode	*c;
 		firstChild->Evaluate(r, b);
 		if (VerboseLevel() > 5)
 		{
-		Buffer	debug;
+		std::string	debug;
 
 			debug="Operation: logical or, left hand side=";
 			debug += b;
-			debug += '\0';
-			r.errmsg(*this, debug);
+			r.errmsg(*this, debug.c_str());
 		}
 		if (!boolean(b))
 		{
 			if (VerboseLevel() > 5)
 			{
-			Buffer	debug;
+			std::string	debug;
 
 				debug="Operation: logical or, evaluating right hand size.";
-				debug += '\0';
-				r.errmsg(*this, debug);
+				r.errmsg(*this, debug.c_str());
 			}
 			firstChild->nextSibling->Evaluate(r, b);
 		}
 		if (VerboseLevel() > 5)
 		{
-		Buffer debug;
+		std::string debug;
 
 			debug="Operation: logical or, result=";
 			debug += b;
-			debug += '\0';
-			r.errmsg(*this, debug);
+			r.errmsg(*this, debug.c_str());
 		}
 		break;
 	case logicaland:
@@ -403,38 +388,35 @@ RecipeNode	*c;
 		firstChild->Evaluate(r, b);
 		if (VerboseLevel() > 5)
 		{
-		Buffer	debug;
+		std::string	debug;
 
 			debug="Operation: logical and, left hand side=";
 			debug += b;
-			debug += '\0';
-			r.errmsg(*this, debug);
+			r.errmsg(*this, debug.c_str());
 		}
 		if (boolean(b))
 		{
 			if (VerboseLevel() > 5)
 			{
-			Buffer	debug;
+			std::string	debug;
 
 				debug="Operation: logical and, evaluating right hand size.";
-				debug += '\0';
-				r.errmsg(*this, debug);
+				r.errmsg(*this, debug.c_str());
 			}
 			firstChild->nextSibling->Evaluate(r, b);
 		}
 		if (VerboseLevel() > 5)
 		{
-		Buffer	debug;
+		std::string	debug;
 
 			debug="Operation: logical and, result=";
 			debug += b;
-			debug += '\0';
-			r.errmsg(*this, debug);
+			r.errmsg(*this, debug.c_str());
 		}
 		break;
 	case concat:
 		{
-		Buffer	bb;
+		std::string	bb;
 
 			for (c=firstChild; c; c=c->nextSibling)
 			{
@@ -449,93 +431,87 @@ RecipeNode	*c;
 		firstChild->Evaluate(r, b);
 		if (VerboseLevel() > 5)
 		{
-		Buffer	debug;
+		std::string	debug;
 
 			debug="Operation on: ";
 			debug += b;
 			debug += " - strlength.";
-			debug += '\0';
-			r.errmsg(*this, debug);
+			r.errmsg(*this, debug.c_str());
 		}
 		{
-		unsigned long	n=b.Length();
+		unsigned long	n=b.size();
 
-			b.reset();
-			b.append(n);
+			b.clear();
+			add_integer(b, n);
 		}
 		if (VerboseLevel() > 5)
 		{
-		Buffer	debug;
+		std::string	debug;
 
 			debug="Operation: strlength=";
 			debug += b;
-			debug += '\0';
-			r.errmsg(*this, debug);
+			r.errmsg(*this, debug.c_str());
 		}
 		break;
 	case	strsubstr:
 		if (!firstChild || !firstChild->nextSibling)
 			throw "Internal error in evaluate unary.";
 		{
-		Buffer	bb, cc;
+		std::string	bb, cc;
 
 			firstChild->Evaluate(r, bb);
 			firstChild->nextSibling->Evaluate(r, cc);
-			cc += '\0';
 
-		long n=atol( (const char *) cc);
+			long n=atol( cc.c_str());
 
 			if (VerboseLevel() > 5)
 			{
-			Buffer	debug;
+			std::string	debug;
 
 				debug="Operation on: ";
 				debug += bb;
 				debug += " - strsubstr ";
-				debug.append( (unsigned long) n);
-				debug += '\0';
-				r.errmsg(*this, debug);
+				add_integer(debug, n);
+				r.errmsg(*this, debug.c_str());
 			}
 
-		long l=bb.Length();
+		long l=bb.size();
 
 			if (n < 0 || n > l)	n=l;
-			b.append( (const char *)bb + n, l-n);
+			b.append( bb.c_str() + n,
+				  bb.c_str() + l);
 			if (firstChild->nextSibling->nextSibling)
 			{
 				firstChild->nextSibling->nextSibling->
 							Evaluate(r, cc);
-				cc += '\0';
-				n=atol( (const char *)cc);
-				if (n < b.Length())
-					b.Length(n);
+				n=atol( cc.c_str());
+				if ((size_t)n < b.size())
+					b.resize(n);
 
 				if (VerboseLevel() > 5)
 				{
-				Buffer	debug;
+				std::string	debug;
 
 					debug="Operation on: ";
 					debug += bb;
 					debug += " - strsubstr chop ";
-					debug.append( (unsigned long)n);
-					debug += '\0';
-					r.errmsg(*this, debug);
+					add_integer(debug, n);
+					r.errmsg(*this, debug.c_str());
 				}
 			}
 			if (VerboseLevel() > 5)
 			{
-			Buffer	debug;
+			std::string	debug;
 
 				debug="Operation: ";
 				debug += " strsubstr=";
-				debug.append(b);
-				debug += '\0';
-				r.errmsg(*this, debug);
+				debug += b;
+				r.errmsg(*this, debug.c_str());
 			}
 		}
 		break;
 	case strregexp:
-		EvaluateStrRegExp(r, b, (Buffer *)NULL);
+		EvaluateStrRegExp(r, b, nullptr);
 		break;
 	case whileloop:
 		if (!firstChild || !firstChild->nextSibling)
@@ -545,20 +521,16 @@ RecipeNode	*c;
 		{
 			if (VerboseLevel() > 3)
 			{
-			Buffer	buf;
-
-				buf="Evaluating WHILE condition.";
-				r.errmsg(*this, buf);
+				r.errmsg(*this, "Evaluating WHILE condition.");
 			}
 			firstChild->Evaluate(r,b);
 			if (VerboseLevel() > 3)
 			{
-			Buffer	buf;
+			std::string	buf;
 
 				buf="While condition evaluated, result=";
 				buf += b;
-				buf += '\0';
-				r.errmsg(*this, buf);
+				r.errmsg(*this, buf.c_str());
 			}
 			if (! boolean(b))	break;
 
@@ -570,22 +542,20 @@ RecipeNode	*c;
 			throw "Internal error in while loop.";
 		if (VerboseLevel() > 3)
 		{
-		Buffer	buf;
+		std::string	buf;
 
 			buf="Evaluating IF condition.";
-			buf += '\0';
-			r.errmsg(*this, buf);
+			r.errmsg(*this, buf.c_str());
 		}
 		firstChild->Evaluate(r,b);
 
 		if (VerboseLevel() > 3)
 		{
-		Buffer	buf;
+		std::string	buf;
 
 			buf="IF evaluated, result=";
 			buf += b;
-			buf += '\0';
-			r.errmsg(*this, buf);
+			r.errmsg(*this, buf.c_str());
 		}
 		if (boolean(b))
 			firstChild->nextSibling->Evaluate(r, b);
@@ -596,17 +566,14 @@ RecipeNode	*c;
 		if (!firstChild)
 			throw "Internal error in delivery statement.";
 		firstChild->Evaluate(r,b);
-		b += '\0';
-		if (delivery(b) < 0)
+		if (delivery(b.c_str()) < 0)
 			throw "Unable to deliver to mailbox.";
-		b="EXITCODE";
-		throw ( GetVar(b)->Int("0") );
+		throw (extract_int(GetVar("EXITCODE"), "0"));
 	case delivercc:
 		if (!firstChild)
 			throw "Internal error in delivery statement.";
 		firstChild->Evaluate(r,b);
-		b += '\0';
-		if (delivery(b) < 0)
+		if (delivery(b.c_str()) < 0)
 			throw "Unable to deliver to mailbox.";
 		b = "0";
 		break;
@@ -614,11 +581,10 @@ RecipeNode	*c;
 		if (!firstChild)
 			throw "Internal error in xfilter statement.";
 		firstChild->Evaluate(r,b);
-		b += '\0';
 		if (VerboseLevel() > 0)
 			merr << "maildrop: Filtering through xfilter " <<
-				(const char *)b << "\n";
-		if (filter(b) < 0)
+				b.c_str() << "\n";
+		if (filter(b.c_str()) < 0)
 			throw "Unable to filter message.";
 		b = "0";
 		break;
@@ -626,11 +592,10 @@ RecipeNode	*c;
 		if (!firstChild)
 			throw "Internal error in system statement.";
 		firstChild->Evaluate(r,b);
-		b += '\0';
 		if (VerboseLevel() > 0)
 			merr << "maildrop: Executing system command " <<
-				(const char *)b << "\n";
-		executesystem(b);
+				b.c_str() << "\n";
+		executesystem(b.c_str());
 		b = "0";
 		break;
 	case exception:
@@ -640,21 +605,19 @@ RecipeNode	*c;
 		{
 			if (VerboseLevel() > 3)
 			{
-			Buffer	buf;
+			std::string	buf;
 
 				buf="Trapping exceptions.";
-				buf += '\0';
-				r.errmsg(*this, buf);
+				r.errmsg(*this, buf.c_str());
 			}
 			firstChild->Evaluate(r, b);
 			b="";
 			if (VerboseLevel() > 3)
 			{
-			Buffer	buf;
+			std::string	buf;
 
 				buf="Exception trapping removed.";
-				buf += '\0';
-				r.errmsg(*this, buf);
+				r.errmsg(*this, buf.c_str());
 			}
 		}
 		catch (const char *p)
@@ -662,11 +625,10 @@ RecipeNode	*c;
 			b=p;
 			if (VerboseLevel() > 3)
 			{
-			Buffer	buf;
+			std::string	buf;
 
 				buf="Trapped exception.";
-				buf += '\0';
-				r.errmsg(*this, buf);
+				r.errmsg(*this, buf.c_str());
 			}
 		}
 #if NEED_NONCONST_EXCEPTIONS
@@ -674,11 +636,10 @@ RecipeNode	*c;
 		{
 			if (VerboseLevel() > 3)
 			{
-			Buffer	buf;
+			std::string	buf;
 
 				buf="Trapped exceptions.";
-				buf += '\0';
-				r.errmsg(*this, buf);
+				r.errmsg(*this, buf.c_str());
 			}
 			b=p;
 		}
@@ -691,11 +652,10 @@ RecipeNode	*c;
 			b="";
 			if (VerboseLevel() > 3)
 			{
-			Buffer	buf;
+			std::string	buf;
 
 				buf="Trapped exception.";
-				buf += '\0';
-				r.errmsg(*this, buf);
+				r.errmsg(*this, buf.c_str());
 			}
 		}
 		break;
@@ -704,7 +664,7 @@ RecipeNode	*c;
 			throw "Internal error in echo statement.";
 		firstChild->Evaluate(r, b);
 		{
-		Buffer	s;
+		std::string	s;
 
 			parse_backslash(b, s);
 			mout << s;
@@ -716,21 +676,19 @@ RecipeNode	*c;
 		firstChild->Evaluate(r, b);
 		if (VerboseLevel() > 3)
 		{
-		Buffer	s;
+		std::string	s;
 
 			s="Creating dotlock ";
 			s += b;
-			s += '\0';
-			r.errmsg(*this, s);
+			r.errmsg(*this, s.c_str());
 		}
-		b += '\0';
 		{
 			DotLock	d;
 
 			{
 				block_sigalarm pause;
 
-				d.Lock(b);
+				d.Lock(b.c_str());
 			}
 			firstChild->nextSibling->Evaluate(r, b);
 			d.Unlock();
@@ -742,18 +700,16 @@ RecipeNode	*c;
 		firstChild->Evaluate(r, b);
 		if (VerboseLevel() > 3)
 		{
-		Buffer	s;
+		std::string	s;
 
 			s="Creating flock ";
 			s += b;
-			s += '\0';
-			r.errmsg(*this, s);
+			r.errmsg(*this, s.c_str());
 		}
-		b += '\0';
 		{
-		FileLock	filelock;
+			FileLock	filelock;
 
-			filelock.Lock(b);
+			filelock.Lock(b.c_str());
 			firstChild->nextSibling->Evaluate(r, b);
 		}
 		break;
@@ -763,16 +719,15 @@ RecipeNode	*c;
 		firstChild->Evaluate(r, b);
 		if (VerboseLevel() > 3)
 		{
-		Buffer	s;
+		std::string	s;
 
 			s="Opening logfile ";
 			s += b;
-			s += '\0';
-			r.errmsg(*this, s);
+			r.errmsg(*this, s.c_str());
 		}
-		b += '\0';
 		maildrop.logfile.Close();
-		if (maildrop.logfile.Open(b, O_CREAT | O_WRONLY | O_APPEND,
+		if (maildrop.logfile.Open(b.c_str(),
+					  O_CREAT | O_WRONLY | O_APPEND,
 			0600) < 0)
 			throw "Unable to create log file.";
 		break;
@@ -781,7 +736,7 @@ RecipeNode	*c;
 			throw "Internal error in logfile statement.";
 		firstChild->Evaluate(r, b);
 		{
-		Buffer	s;
+		std::string	s;
 
 			parse_backslash(b, s);
 			log_line(s);
@@ -792,7 +747,7 @@ RecipeNode	*c;
 			throw "Internal error in import statement.";
 		firstChild->Evaluate(r, b);
 		{
-		Buffer	s;
+		std::string	s;
 
 			if (VerboseLevel() > 3)
 			{
@@ -800,16 +755,14 @@ RecipeNode	*c;
 				s += " \"";
 				s += b;
 				s += "\"";
-				s += '\0';
-				r.errmsg(*this, s);
+				r.errmsg(*this, s.c_str());
 			}
 
 			s=b;
 
-			s += '\0';
 
-		const char *name=s;
-		const char *val=getenv(name);
+			const char *name=s.c_str();
+			const char *val=getenv(name);
 
 			if (!val)	val="";
 			s=val;
@@ -829,28 +782,26 @@ RecipeNode	*c;
 		firstChild->Evaluate(r, b);
 		if (VerboseLevel() > 3)
 		{
-		Buffer	s;
+		std::string	s;
 
 			s="Opening include file ";
 			s += b;
-			s += '\0';
-			r.errmsg(*this, s);
+			r.errmsg(*this, s.c_str());
 		}
-		b += '\0';
 		{
 		Recipe	r;
 		Lexer	in;
 		MaildropSaveEM save_embedded_mode;
 		static const char embedded_mode_directory[]=ETCDIR "/maildroprcs/";
 
-			if (strncmp( (const char *)b, embedded_mode_directory,
+			if (strncmp( b.c_str(), embedded_mode_directory,
 				sizeof(embedded_mode_directory)-1) == 0 &&
-				strchr( (const char *)b, '.') == 0)
+				strchr( b.c_str(), '.') == 0)
 			{
 				maildrop.embedded_mode=0;
 				maildrop.reset_vars();
 			}
-			if (in.Open( (const char *)b ) < 0)
+			if (in.Open( b.c_str() ) < 0)
 				throw "Unable to open include file.";
 			if (r.ParseRecipe(in) < 0)
 				throw EX_TEMPFAIL;
@@ -869,40 +820,33 @@ RecipeNode	*c;
 		}
 		break;
 	case exit:
-		b="EXITCODE";
-		throw ( GetVar(b)->Int("0") );
+		throw extract_int( GetVar("EXITCODE"), "0");
 	case foreach:
 		if (!firstChild || !firstChild->nextSibling ||
 			( firstChild->nodeType != regexpr &&
 				firstChild->nodeType != strregexp))
 			throw "Internal error in foreach statement.";
 		{
-		Buffer	foreachbuf;
-		Buffer	varname;
-		Buffer	varvalue;
+			foreach_t foreachbuf;
 
 			if (firstChild->nodeType == regexpr)
 				firstChild->EvaluateRegExp(r, b, &foreachbuf);
 			else
 				firstChild->EvaluateStrRegExp(r,b,&foreachbuf);
 
-		const char *p=foreachbuf;
-		int l=foreachbuf.Length();
-
-			while (l)
+			for (auto &matches:foreachbuf)
 			{
-				varvalue.reset();
-
-			int	i;
-
-				for (i=0; inextSibling->Evaluate(r, b);
 			}
 		}
@@ -923,7 +867,7 @@ RecipeNode	*c;
 		if (!firstChild || !firstChild->nextSibling)
 			throw "Internal error in lookup statement.";
 		{
-		Buffer	expr, file, opts;
+		std::string	expr, file, opts;
 
 			firstChild->Evaluate(r, expr);
 			firstChild->nextSibling->Evaluate(r, file);
@@ -941,14 +885,14 @@ RecipeNode	*c;
 			throw "Internal error in tolower statement.";
 		firstChild->Evaluate(r, b);
 		{
-		Buffer	bb;
-		const char *p=b;
-		int	l=b.Length();
+		std::string	bb;
+		const char *p=b.c_str();
+		auto	l=b.size();
 
 			while (l)
 			{
 				--l;
-				bb.push( tolower( *p++ ));
+				bb.push_back( tolower( *p++ ));
 			}
 			b=bb;
 		}
@@ -958,14 +902,14 @@ RecipeNode	*c;
 			throw "Internal error in toupper statement.";
 		firstChild->Evaluate(r, b);
 		{
-		Buffer	bb;
-		const char *p=b;
-		int	l=b.Length();
+		std::string	bb;
+		const char *p=b.c_str();
+		auto	l=b.size();
 
 			while (l)
 			{
 				--l;
-				bb.push( toupper( *p++ ));
+				bb.push_back( toupper( *p++ ));
 			}
 			b=bb;
 		}
@@ -976,13 +920,13 @@ RecipeNode	*c;
 		firstChild->Evaluate(r, b);
 		if (rfc822hasaddr(b))
 		{
-			b.reset();
-			b.push('1');
+			b.clear();
+			b.push_back('1');
 		}
 		else
 		{
-			b.reset();
-			b.push('0');
+			b.clear();
+			b.push_back('0');
 		}
 		break;
 #ifdef	DbObj
@@ -990,17 +934,15 @@ RecipeNode	*c;
 		if (!firstChild)
 			throw "Internal error in evaluate gdbmopen.";
 		firstChild->Evaluate(r, b);
-		b += '\0';
 		{
-		const char *filename=b;
-		const char *openmode="R";
-		Buffer	bb;
+			const char *filename=b.c_str();
+			const char *openmode="R";
+			std::string	bb;
 
 			if (firstChild->nextSibling)
 			{
 				firstChild->nextSibling->Evaluate(r, bb);
-				bb += '\0';
-				openmode=bb;
+				openmode=bb.c_str();
 			}
 			if (maildrop.embedded_mode)
 				switch ( *openmode )	{
@@ -1013,9 +955,9 @@ RecipeNode	*c;
 				}
 
 		int	n=r.gdbm_file.Open(filename, openmode);
-			b.reset();
-			if (n < 0) { b += '-'; n= -n; }
-			b.append( (unsigned long)n );
+			b.clear();
+			if (n < 0) { b += "-"; n= -n; }
+			add_integer(b, n );
 		}
 		break;
 	case gdbmclose:
@@ -1027,21 +969,20 @@ RecipeNode	*c;
 		firstChild->Evaluate(r, b);
 		{
 		size_t	result_size;
-		Buffer	optbuf;
+		std::string	optbuf;
 
 			if (firstChild->nextSibling)
 				firstChild->nextSibling->Evaluate(r, optbuf);
 
-			optbuf += '\0';
 
 		char	*result=r.gdbm_file.Fetch(
-				(const char *)b, b.Length(), result_size,
-				(const char *)optbuf);
+				b.c_str(), b.size(), result_size,
+				optbuf.c_str());
 
-			b.reset();
+			b.clear();
 			if (result)
 			{
-				b.append(result, result_size);
+				b.append(result, result+result_size);
 				free(result);
 			}
 			else if (firstChild->nextSibling &&
@@ -1054,17 +995,18 @@ RecipeNode	*c;
 		if (!firstChild || !firstChild->nextSibling)
 			throw "Internal error in evaluate gdbmstore.";
 		{
-		Buffer	key, val;
+		std::string	key, val;
 
 			firstChild->Evaluate(r, key);
 			firstChild->nextSibling->Evaluate(r, val);
 
-		int	n=r.gdbm_file.Store(key, key.Length(),
-						val, val.Length(), "R");
+			int	n=r.gdbm_file.Store(key.c_str(), key.size(),
+						    val.c_str(), val.size(),
+						    "R");
 
-			b.reset();
-			if (n < 0) { b += '-'; n= -n; }
-			b.append( (unsigned long)n );
+			b.clear();
+			if (n < 0) { b += "-"; n= -n; }
+			add_integer(b, n );
 		}
 		break;
 #else
@@ -1072,17 +1014,17 @@ RecipeNode	*c;
 	case gdbmclose:
 	case gdbmfetch:
 	case gdbmstore:
-		b.reset();
-		b.push('0');
+		b.clear();
+		b.push_back('0');
 		break;
 #endif
 	case timetoken:
-		b.reset();
+		b.clear();
 		{
 		time_t	t;
 
 			time(&t);
-			b.append( (unsigned long)t );
+			add_integer(b, t );
 		}
 		break;
 	case unset:
@@ -1090,14 +1032,13 @@ RecipeNode	*c;
 			throw "Internal error in unset statement.";
 		firstChild->Evaluate(r, b);
 		{
-		Buffer	s;
+		std::string	s;
 
 			if (VerboseLevel() > 3)
 			{
 				s="unset ";
 				s += b;
-				s += '\0';
-				r.errmsg(*this, s);
+				r.errmsg(*this, s.c_str());
 			}
 
 			UnsetVar(b);
@@ -1106,90 +1047,82 @@ RecipeNode	*c;
 	}
 }
 
-void RecipeNode::EvaluateRegExp(Recipe &r, Buffer &b, Buffer *foreachp)
+void RecipeNode::EvaluateRegExp(Recipe &r, std::string &b, foreach_t *foreachp)
 {
 Search	c;
-Buffer	buf1, buf2;
+std::string	buf1, buf2;
 
 	ParseRegExp(str, buf1, buf2);
 
 	dollarexpand(r, buf1);
-	buf1 += '\0';
-	buf2 += '\0';
-	if (c.find( *maildrop.msgptr, maildrop.msginfo, buf1, buf2, foreachp))
+	if (c.find( *maildrop.msgptr, maildrop.msginfo,
+		    buf1.c_str(), buf2.c_str(), foreachp))
 	{
 		c.score=0;
 		r.errmsg(*this, "Syntax error in /pattern/.\n");
 	}
 	else if (VerboseLevel() > 3)
 	{
-	Buffer	buf;
+	std::string	buf;
 
 		buf="Search of ";
-		buf += (const char *)buf1;
+		buf += buf1.c_str();
 		buf += " = ";
-		buf.append(c.score);
-		buf += '\0';
-		r.errmsg(*this, buf);
+		add_number(buf, c.score);
+		r.errmsg(*this, buf.c_str());
 	}
-	b.append(c.score);
+	add_number(b, c.score);
 }
 
-void RecipeNode::EvaluateStrRegExp(Recipe &r, Buffer &b, Buffer *foreachp)
+void RecipeNode::EvaluateStrRegExp(Recipe &r, std::string &b, foreach_t *foreachp)
 {
 	if (!firstChild || !firstChild->nextSibling ||
 		firstChild->nextSibling->nodeType != regexpr)
 		throw "Internal error in evaluate =~.";
 
 Search	c;
-Buffer	buf1, buf2;
+std::string	buf1, buf2;
 
 	firstChild->Evaluate(r,str);
 	ParseRegExp( firstChild->nextSibling->str, buf1, buf2);
 
 	dollarexpand(r, buf1);
-	buf1 += '\0';
-	buf2 += '\0';
-	str += '\0';
-	if (c.find( str, buf1, buf2, foreachp))
+	if (c.find( str.c_str(), buf1.c_str(), buf2.c_str(), foreachp))
 	{
 		c.score=0;
 		r.errmsg(*this, "Syntax error in /pattern/.\n");
 	}
 	else if (VerboseLevel() > 3)
 	{
-	Buffer	buf;
+	std::string	buf;
 
 		buf="Search of ";
-		buf += (const char *)buf1;
+		buf += buf1.c_str();
 		buf += " = ";
-		buf.append(c.score);
-		buf += '\0';
-		r.errmsg(*this, buf);
+		add_number(buf, c.score);
+		r.errmsg(*this, buf.c_str());
 	}
-	b.append(c.score);
+	add_number(b, c.score);
 }
 
 // Break down /foo/:bar into foo and bar.
 
-void	RecipeNode::ParseRegExp(const Buffer &str, Buffer &buf1, Buffer &buf2)
+void	RecipeNode::ParseRegExp(const std::string &str, std::string &buf1, std::string &buf2)
 {
 	buf2=str;
-	buf2 += '\0';
 
-const	char *p;
+	const	char *p;
 
-	p=buf2;
+	p=buf2.c_str();
 
 	if (*p != '/')	throw "Internal error.";
 	++p;
 	buf1=p;
 
-int	l=buf1.Length(), i;
+	size_t	l=buf1.size(), i;
 
-	buf1 += '\0';
-	buf2.reset();
-	p=buf1;
+	buf2.clear();
+	p=buf1.c_str();
 	for (i=0; i 0)
 			merr << "maildrop: Filtering through `" <<
-				(const char *)buf << "`\n";
+				buf.c_str() << "`\n";
 		try
 		{
-		int	rc=::xfilter(buf, 1);
-		int	l=0;
+			int	rc=::xfilter(buf.c_str(), 1);
+			size_t	l=0;
 
 			if (rc < 0)
 			{
@@ -1247,12 +1179,12 @@ Buffer	buf;
 			while (rc >= 0)
 			{
 				if (rc == '\r' || rc == '\n')	rc=' ';
-				b.push(rc);
-				if (!isspace(rc))	l=b.Length();
+				b.push_back(rc);
+				if (!isspace(rc))	l=b.size();
 				rc=maildrop.savemsgptr->get_c();
 			}
 			maildrop.savemsgptr->Init();
-			b.Length(l);
+			b.resize(l);
 		}
 		catch (...)
 		{
@@ -1265,12 +1197,12 @@ Buffer	buf;
 	}
 }
 
-void	RecipeNode::dollarexpand(Recipe &r, Buffer &b)
+void	RecipeNode::dollarexpand(Recipe &r, std::string &b)
 {
-int	i, l;
-const char *p=b;
+	size_t	i, l;
+	const char *p=b.c_str();
 
-	for (i=0, l=b.Length(); i= l)	return (index+1);
 
-const	char *p=b;
-Buffer	varname;
-int	j;
+	const	char *p=b.c_str();
+	std::string	varname;
+	size_t	j;
 
 	if (p[index+1] == '{')
 	{
@@ -1348,13 +1280,12 @@ int	j;
 			if (j >= l || isspace(p[j]))
 			{
 				varname="Terminating } is missing.\n";
-				varname += '\0';
-				r.errmsg(*this, varname);
+				r.errmsg(*this, varname.c_str());
 				return (index+1);
 			}
 			if (p[j] == '}')
 				break;
-			varname += p[j];
+			varname.push_back(p[j]);
 			++j;
 		}
 		++j;
@@ -1367,21 +1298,17 @@ int	j;
 		for (j=index+1; j= '0' && *p <= '7');
-			s.push(c);
+			s.push_back(c);
 			continue;
 		}
 		c=backslash_char(*p);
@@ -1457,22 +1384,21 @@ int append_newline=1;
 			append_newline=0;
 			break;
 		}
-		s.push( c );
+		s.push_back( c );
 	}
 	if (append_newline)
 	{
 #if	CRLF_TERM
-		s.push('\r');
+		s.push_back('\r');
 #endif
-		s.push('\n');
+		s.push_back('\n');
 	}
 }
 
-void RecipeNode::rfc822getaddr(Buffer &buf)
+void RecipeNode::rfc822getaddr(std::string &buf)
 {
-	buf += '\0';
 
-	struct	rfc822t	*p=rfc822t_alloc_new(buf, NULL, NULL);
+	struct	rfc822t	*p=rfc822t_alloc_new(buf.c_str(), NULL, NULL);
 
 	if (!p)	outofmem();
 
@@ -1483,7 +1409,7 @@ void RecipeNode::rfc822getaddr(Buffer &buf)
 	try
 	{
 		int n;
-		Buffer newbuf;
+		std::string newbuf;
 
 		for (n=0; nnaddrs; n++)
 			if (a->addrs[n].tokens)
@@ -1516,25 +1442,24 @@ void RecipeNode::rfc822getaddr(Buffer &buf)
 	rfc822t_free(p);
 }
 
-int RecipeNode::rfc822hasaddr(Buffer &buf)
+int RecipeNode::rfc822hasaddr(std::string &buf)
 {
-Buffer	lower_addr;
-Buffer	next_line;
-const char *p;
-int	l;
-Buffer	header;
+	std::string	lower_addr;
+	std::string	next_line;
+	const char *p;
+	size_t	l;
+	std::string	header;
 
-	for (p=buf, l=buf.Length(); l; --l)
-		lower_addr.push(tolower(*p++));
-	lower_addr += '\0';
+	for (p=buf.c_str(), l=buf.size(); l; --l)
+		lower_addr.push_back(tolower(*p++));
 
 	if (VerboseLevel() > 5)
 		merr << "maildrop: hasaddr('" <<
-			(const char *)lower_addr << "')\n";
+			lower_addr.c_str() << "')\n";
 
 	maildrop.msgptr->Rewind();
 	if (maildrop.msgptr->appendline(next_line))	return (0);
-	while ( *(p=next_line) != '\n')
+	while ( *(p=next_line.c_str()) != '\n')
 	{
 	int	c;
 
@@ -1556,37 +1481,35 @@ Buffer	header;
 			;
 		else
 		{
-			next_line.reset();
+			next_line.clear();
 			if (maildrop.msgptr->appendline(next_line))
 				return (0);
 			continue;
 		}
-		next_line += '\0';
-		header=(const char *)next_line+3+l;
+		header=next_line.c_str()+3+l;
 		for (;;)
 		{
-			next_line.reset();
+			next_line.clear();
 			if (maildrop.msgptr->appendline(next_line))
-				return ( rfc822hasaddr(lower_addr, header));
-			if (*(p=next_line) == '\n')	break;
+				return ( rfc822hasaddr(lower_addr.c_str(), header));
+			if (*(p=next_line.c_str()) == '\n')	break;
 			if ( !isspace(*p))	break;
-			header += ' ';
+			header += " ";
 			header += next_line;
 		}
-		if (rfc822hasaddr(lower_addr, header))	return (1);
+		if (rfc822hasaddr(lower_addr.c_str(), header))	return (1);
 	}
 	return (0);
 }
 
-int RecipeNode::rfc822hasaddr(const char *addr, Buffer &header)
+int RecipeNode::rfc822hasaddr(const char *addr, std::string &header)
 {
-	header += '\0';
 
 	if (VerboseLevel() > 5)
 		merr << "maildrop: hasaddr: rfc822 parsing: "
-			<< (const char *)header << "\n";
+			<< header.c_str() << "\n";
 
-	struct	rfc822t	*p=rfc822t_alloc_new(header, NULL, NULL);
+	struct	rfc822t	*p=rfc822t_alloc_new(header.c_str(), NULL, NULL);
 
 	if (!p)	outofmem();
 
@@ -1595,7 +1518,7 @@ int RecipeNode::rfc822hasaddr(const char *addr, Buffer &header)
 	if (!a)	outofmem();
 
 int	i;
-Buffer	rfc822buf;
+std::string	rfc822buf;
 int	found=0;
 
 	for (i=0; inaddrs; i++)
@@ -1612,11 +1535,10 @@ int	found=0;
 
 		rfc822buf=p;
 		free(p);
-		rfc822buf.push('\0');
 		if (VerboseLevel() > 5)
 			merr << "maildrop: hasaddr: rfc822 parsed: "
-				<< (const char *)rfc822buf << "\n";
-		if (strcmp(addr, rfc822buf) == 0)
+				<< rfc822buf.c_str() << "\n";
+		if (strcmp(addr, rfc822buf.c_str()) == 0)
 		{
 			found=1;
 			break;
@@ -1627,27 +1549,22 @@ int	found=0;
 	return (found);
 }
 
-int RecipeNode::dolookup(Buffer &strng, Buffer &filename, Buffer &opts)
+int RecipeNode::dolookup(std::string &strng, std::string &filename, std::string &opts)
 {
-static Buffer	errbuf;
-Buffer	real_opts;
+static std::string	errbuf;
+std::string	real_opts;
 
-	filename += '\0';
 
-	strng += '\0';
-	opts += '\0';
-	if (strchr (opts, 'D'))	real_opts.push('D');	// Only allow this opt
-	real_opts += '\0';
+	if (strchr (opts.c_str(), 'D'))	real_opts.push_back('D');	// Only allow this opt
 
 Mio	fp;
 
-	if (fp.Open((const char *)filename, O_RDONLY) < 0)
+	if (fp.Open(filename.c_str(), O_RDONLY) < 0)
 	{
 		errbuf="Unable to open ";
-		errbuf += (const char *)filename;
+		errbuf += filename.c_str();
 		errbuf += ".\n";
-		errbuf += '\0';
-		throw (const char *)errbuf;
+		throw errbuf.c_str();
 	}
 
 	for (;;)
@@ -1655,29 +1572,28 @@ Mio	fp;
 	int	c;
 	const	char *p;
 
-		errbuf.reset();
+		errbuf.clear();
 		while ((c=fp.get()) >= 0 && c != '\r' && c != '\n')
-			errbuf.push(c);
-		if (c < 0 && errbuf.Length() == 0)	break;
-		errbuf.push(0);
+			errbuf.push_back(c);
+		if (c < 0 && errbuf.size() == 0)	break;
 
-		p=errbuf;
+		p=errbuf.c_str();
 		while (*p && isspace(*p))	p++;
 		if (!*p || *p == '#')	continue;
 
-	Search	srch;
+		Search	srch;
 
-		if (srch.find( strng, p, real_opts, (Buffer *)NULL))
+		if (srch.find( strng.c_str(), p,
+			       real_opts.c_str(), nullptr))
 		{
 			opts=p;		// Convenient buffer
 
 			errbuf="Bad pattern in ";
-			errbuf += (const char *)filename;
+			errbuf += filename.c_str();
 			errbuf += ": ";
 			errbuf += opts;
-			errbuf += '\n';
-			errbuf += '\0';
-			throw (const char *)errbuf;
+			errbuf += "\n";
+			throw errbuf.c_str();
 		}
 		if (srch.score)
 			return (1);
diff --git a/courier-imap-x/libs/maildrop/recipenode.h b/courier-imap-x/libs/maildrop/recipenode.h
index 4a76a4244..5dff26842 100644
--- a/courier-imap-x/libs/maildrop/recipenode.h
+++ b/courier-imap-x/libs/maildrop/recipenode.h
@@ -3,6 +3,8 @@
 
 
 #include	"buffer.h"
+#include	"search.h"
+
 class Recipe;
 
 /////////////////////////////////////////////////////////////////////////////
@@ -48,11 +50,11 @@ class RecipeNode {
 	RecipeNode *prevSibling, *nextSibling;	// Siblings of this node.
 	RecipeNode *firstChild, *lastChild;	// Its own children.
 
-	Buffer	str;
+	std::string	str;
 	int	linenum;
 
-	void dollarexpand(Recipe &, Buffer &);
-	int dollarexpand(Recipe &, Buffer &, int);
+	void dollarexpand(Recipe &, std::string &);
+	size_t dollarexpand(Recipe &, std::string &, size_t);
 
 public:
 	friend class Recipe;
@@ -122,20 +124,20 @@ class RecipeNode {
 
 	RecipeNode(RecipeNodeType);
 	~RecipeNode()					{}
-	void	Evaluate(Recipe &, Buffer &);
+	void	Evaluate(Recipe &, std::string &);
 private:
 	void	AppendSibling(RecipeNode *);
-	void	EvaluateString(Recipe &r, Buffer &b);
+	void	EvaluateString(Recipe &r, std::string &b);
 
-	void EvaluateStrRegExp(Recipe &, Buffer &, Buffer *);
-	void EvaluateRegExp(Recipe &, Buffer &, Buffer *);
-static	void	ParseRegExp(const Buffer &, Buffer &, Buffer &);
-static	int	boolean(const Buffer &);
+	void EvaluateStrRegExp(Recipe &, std::string &, foreach_t *);
+	void EvaluateRegExp(Recipe &, std::string &, foreach_t *);
+static	void	ParseRegExp(const std::string &, std::string &, std::string &);
+static	int	boolean(const std::string &);
 
-	void rfc822getaddr(Buffer &);
-	int rfc822hasaddr(Buffer &);
-	int rfc822hasaddr(const char *, Buffer &);
-	void SpecialEscape(Buffer &);
-	int dolookup(Buffer &, Buffer &, Buffer &);
+	void rfc822getaddr(std::string &);
+	int rfc822hasaddr(std::string &);
+	int rfc822hasaddr(const char *, std::string &);
+	void SpecialEscape(std::string &);
+	int dolookup(std::string &, std::string &, std::string &);
 	} ;
 #endif
diff --git a/courier-imap-x/libs/maildrop/reeval.h b/courier-imap-x/libs/maildrop/reeval.h
index 37d59a8ad..c8a9274e6 100644
--- a/courier-imap-x/libs/maildrop/reeval.h
+++ b/courier-imap-x/libs/maildrop/reeval.h
@@ -25,6 +25,11 @@ class ReEval {
 			if (nodenums)	delete[] nodenums;
 			}
 	void	init(unsigned maxsize);
+
+	ReEval(const ReEval &)=delete;
+	ReEval &operator=(const ReEval &)=delete;
+
+
 private:
 	unsigned	arysize;
 } ;
diff --git a/courier-imap-x/libs/maildrop/reformail.C b/courier-imap-x/libs/maildrop/reformail.C
index fec2a6853..da6c8b951 100644
--- a/courier-imap-x/libs/maildrop/reformail.C
+++ b/courier-imap-x/libs/maildrop/reformail.C
@@ -1,5 +1,5 @@
 /*
-** Copyright 1998 - 2009 Double Precision, Inc.
+** Copyright 1998 - 2023 Double Precision, Inc.
 ** See COPYING for distribution information.
 */
 
@@ -34,6 +34,9 @@
 #include	"buffer.h"
 #include	"liblock/config.h"
 #include	"liblock/liblock.h"
+#include 
+#include 
+#include 
 
 #if	HAS_GETHOSTNAME
 #else
@@ -43,14 +46,19 @@ extern "C" int gethostname(const char *, size_t);
 
 static int inbody=0, addcrs=0, catenate=0;
 static const char *(*append_more_headers)();
-static Buffer	optx, optX, opta, optA, opti, optI, optu, optU, optubuf, optUbuf, optR;
 
-static Buffer add_from_filter_buf;
+typedef std::vector multibuf;
+static multibuf	optx, optX, opta, optA, opti, optI, optu, optU, optubuf, optUbuf, optR;
+
+typedef multibuf::iterator multibuf_iterator;
+
+
+static std::string add_from_filter_buf;
 static const char *add_from_filter_buf_ptr;
 static const char *cache_maxlen="", *cache_name="";
 
 static const char *( *from_filter)();
-static Buffer	current_line;
+static std::string	current_line;
 
 void outofmem()
 {
@@ -68,21 +76,22 @@ void help()
 
 const char *NextLine()
 {
-static Buffer buf;
+static std::string buf;
 int	c;
 
-	buf.reset();
+	buf.clear();
 	while ((c=std::cin.get()) >= 0 && c != '\n')
-		buf.push(c);
-	if (c < 0 && buf.Length() == 0)	return (0);
-	if (buf.Length())	// Strip CRs
+		buf.push_back(c);
+	if (c < 0 && buf.size() == 0)	return (0);
+	if (buf.size())	// Strip CRs
 	{
-		c=buf.pop();
-		if (c != '\r')	buf.push(c);
+		c=buf.back();
+
+		if (c == '\r')
+			buf.pop_back();
 	}
-	buf.push('\n');
-	buf.push('\0');
-	return (buf);
+	buf.push_back('\n');
+	return (buf.c_str());
 }
 
 // from_filter is the initial filtering done on the message.
@@ -122,7 +131,7 @@ const char *no_from_filter_header()
 {
 const	char *p;
 
-static Buffer buf;
+static std::string buf;
 
 	from_filter= &no_from_filter_header;
 
@@ -130,14 +139,13 @@ static Buffer buf;
 		current_line += p;
 
 	buf=current_line;
-	buf+='\0';
 	if (!p || *p == '\n')
 	{
 		from_filter= &read_blank;
-		return (buf);
+		return (buf.c_str());
 	}
 	current_line=p;
-	return (buf);
+	return (buf.c_str());
 }
 
 const char *read_blank()
@@ -168,64 +176,61 @@ int n;
 	current_line=p;
 	if (strncmp(p, "From ", 5) == 0)
 		return ( no_from_filter_header() );
-	add_from_filter_buf.reset();
+	add_from_filter_buf.clear();
 	while (p && *p != '\n')
 	{
 		add_from_filter_buf += p;
 		p=NextLine();
 	}
 
-	add_from_filter_buf += '\0';
 
-static Buffer	return_path;
-static Buffer	from_header;
+static std::string	return_path;
+static std::string	from_header;
 
-	return_path.reset();
-	from_header.reset();
+	return_path.clear();
+	from_header.clear();
 
-	for (p=add_from_filter_buf; *p; )
+	for (p=add_from_filter_buf.c_str(); *p; )
 	{
-	Buffer	header;
+	std::string	header;
 
 		while (*p && *p != ':' && *p != '\n')
 		{
 		int	c= (unsigned char)*p++;
 
 			c=tolower(c);
-			header.push(c);
+			header.push_back(c);
 		}
 		for (;;)
 		{
 			while (*p && *p != '\n')
 			{
-				header.push(*p);
+				header.push_back(*p);
 				p++;
 			}
 			if (!*p)	break;
 			++p;
-			header.push('\n');
+			header.push_back('\n');
 			if (!*p || !isspace((unsigned char)*p))	break;
 		}
-		header.push('\0');
-		if (strncmp(header, "return-path:", 12) == 0 ||
-			strncmp(header, ">return-path:", 13) == 0 ||
-			strncmp(header, "errors-to:", 10) == 0 ||
-			strncmp(header, ">errors-to:", 11) == 0)
+		if (strncmp(header.c_str(), "return-path:", 12) == 0 ||
+		    strncmp(header.c_str(), ">return-path:", 13) == 0 ||
+		    strncmp(header.c_str(), "errors-to:", 10) == 0 ||
+		    strncmp(header.c_str(), ">errors-to:", 11) == 0)
 		{
 			const char *p;
 
-			for (p=header; *p != ':'; p++)
+			for (p=header.c_str(); *p != ':'; p++)
 				;
 			return_path=p;
 		}
 
-		if (strncmp(header, "from:", 5) == 0)
-			from_header=(const char *)header + 5;
+		if (strncmp(header.c_str(), "from:", 5) == 0)
+			from_header=header.c_str() + 5;
 	}
-	if (return_path.Length() == 0)	return_path=from_header;
-	return_path += '\0';
+	if (return_path.size() == 0)	return_path=from_header;
 
-	struct rfc822t *rfc=rfc822t_alloc_new( (const char *)return_path,
+	struct rfc822t *rfc=rfc822t_alloc_new( return_path.c_str(),
 					       NULL, NULL);
 
 	if (!rfc)	outofmem();
@@ -234,7 +239,7 @@ static Buffer	from_header;
 
 	if (!rfca)	outofmem();
 
-	from_header.reset();
+	from_header.clear();
 
 	for (n=0; nnaddrs; ++n)
 	{
@@ -260,33 +265,32 @@ static Buffer	from_header;
 	rfc822a_free(rfca);
 	rfc822t_free(rfc);
 
-	if (from_header.Length() == 0)	from_header="root";
+	if (from_header.size() == 0)	from_header="root";
 	return_path="From ";
 	return_path += from_header;
-	return_path.push(' ');
+	return_path.push_back(' ');
 time_t	t;
 
 	time(&t);
 	p=ctime(&t);
 	while (*p && *p != '\n')
 	{
-		return_path.push(*p);
+		return_path.push_back(*p);
 		p++;
 	}
-	return_path += '\n';
-	return_path += '\0';
+	return_path += "\n";
 	from_filter=add_from_filter_header;
-	add_from_filter_buf_ptr=add_from_filter_buf;
-	return (return_path);
+	add_from_filter_buf_ptr=add_from_filter_buf.c_str();
+	return (return_path.c_str());
 }
 
 const char *add_from_filter_body();
 
 const char *add_from_filter_header()
 {
-static Buffer buf;
+static std::string buf;
 
-	buf.reset();
+	buf.clear();
 
 	if (*add_from_filter_buf_ptr == '\0')
 	{
@@ -298,13 +302,12 @@ static Buffer buf;
 	{
 		while (*add_from_filter_buf_ptr)
 		{
-			buf.push ( (unsigned char)*add_from_filter_buf_ptr );
+			buf.push_back( (unsigned char)*add_from_filter_buf_ptr );
 			if ( *add_from_filter_buf_ptr++ == '\n')	break;
 		}
 	} while ( *add_from_filter_buf_ptr && *add_from_filter_buf_ptr != '\n'
 		&& isspace( (unsigned char)*add_from_filter_buf_ptr ));
-	buf += '\0';
-	return (buf);
+	return (buf.c_str());
 }
 
 const char *add_from_filter_body()
@@ -319,12 +322,11 @@ const char *q;
 		;
 	if (strncmp(q, "From ", 5))	return (p);
 
-static Buffer add_from_buf;
+static std::string add_from_buf;
 
 	add_from_buf=">";
 	add_from_buf += p;
-	add_from_buf += '\0';
-	return (add_from_buf);
+	return (add_from_buf.c_str());
 }
 
 ////////////////////////////////////////////////////////////////////////////
@@ -351,10 +353,8 @@ const	char *p;
 		return ( no_from_filter_header() );
 	}
 
-	for (const char *q="Return-Path: <"; *q; ++q)
-	{
-		optI.push(*q);
-	}
+	std::string new_opti="Return-Path: <";
+
 	for (p += 5; *p && *p != '\n' && isspace(*p); ++p)
 		;
 	if (*p == '<')
@@ -362,11 +362,11 @@ const	char *p;
 
 	while (*p && *p != '\n' && *p != '>' && !isspace(*p))
 	{
-		optI.push(*p);
+		new_opti.push_back(*p);
 		++p;
 	}
-	optI.push('>');
-	optI.push(0);
+	new_opti.push_back('>');
+	optI.push_back(new_opti);
 
 	p=NextLine();
 	if (!p)	return (p);
@@ -421,104 +421,78 @@ static char hostname_buf[256];
 //
 // Return TRUE if header is already in a list of headers.
 //
-// hdrs: null separated list of headers (and header contents)
+// hdrs: list of headers
 // hdr - header to check (must be lowercase and terminated by a colon)
-// pos - offset into hdrs where it's found.
-//
+// pos - if found, the specific header, and the iterator to one past the
+//       colon.
 
-static int has_hdr(const Buffer &hdrs, const char *hdr, unsigned &pos)
+static bool has_hdr(multibuf &hdrs, const std::string &hdr,
+		    multibuf_iterator &ret)
 {
-const char *r=hdrs;
-int l=hdrs.Length();
-Buffer	buf2;
-unsigned pos2=0;
-
-	while (l)
+	for (auto b=hdrs.begin(), e=hdrs.end(); b != e; ++b)
 	{
-		buf2.reset();
-		pos=pos2;
-		while (l)
+		if (b->size() < hdr.size())
+			continue;
+
+		auto p=b->begin();
+
+		auto hb=hdr.begin(), he=hdr.end();
+
+		for (; hb != he; ++hb, ++p)
 		{
-			--l;
-			++pos2;
-			buf2.push( tolower(*r));
-			if (*r++ == 0)	break;
+			if (*hb != tolower(*p))
+				break;
+		}
+
+		if (hb == he)
+		{
+			ret=b;
+			return true;
 		}
-		buf2.push('\0');
-		if (strncmp(hdr, buf2, strlen(hdr)) == 0)	return (1);
 	}
-	return (0);
+	return false;
 }
 
-static int has_hdr(const Buffer &hdrs, const char *hdr)
+static bool has_hdr(multibuf &hdrs, const std::string &hdr)
 {
-unsigned dummy;
+	multibuf_iterator dummy;
 
 	return (has_hdr(hdrs, hdr, dummy));
 }
 
-static void strip_empty_header(Buffer &buf)
+static void strip_empty_header(multibuf &buf)
 {
-Buffer	newbuf;
-int l;
-const char *p;
+	auto b=buf.begin(), e=buf.end(), p=b;
 
-	for (p=buf, l=buf.Length(); l; )
+	while (b != e)
 	{
-		if (p[strlen(p)-1] == ':')
-		{
-			while (l)
-			{
-				--l;
-				if (*p++ == '\0')	break;
-			}
-			continue;
-		}
-		while (l)
-		{
-			--l;
-			newbuf.push( *p );
-			if (*p++ == '\0')	break;
-		}
-	}
-	buf=newbuf;
-}
+		auto ce=b->end();
 
-static void strip_header(Buffer &header, unsigned offset)
-{
-Buffer	buf1;
-const char *p=header;
-int l=header.Length();
+		auto ptr=std::find(b->begin(), ce, ':');
 
-	while (l)
-	{
-		if (!offset)
+		if (ptr != ce && ++ptr == ce)
 		{
-			while (l)
-			{
-				--l;
-				if (*p++ == '\0')	break;
-			}
-			break;
+			++b;
+			continue;
 		}
-		buf1.push( *p++ );
-		--l;
-		--offset;
+
+		if (b != p)
+			*p=*b;
+		++p;
+		++b;
 	}
-	while (l--)
-		buf1.push( *p++ );
-	header=buf1;
+	buf.erase(p, e);
 }
 
 const char *ReadLineAddNewHeader();
 
 const char *ReadLineAddHeader()
 {
-Buffer	buf1;
-const char *q;
-const char *p;
-unsigned pos;
-static Buffer oldbuf;
+	std::string buf1;
+	const char *q;
+	const char *p;
+	multibuf_iterator pos;
+	static std::string oldbuf;
 
 	for (;;)
 	{
@@ -531,13 +505,12 @@ static Buffer oldbuf;
 			strip_empty_header(optI);
 			return ( ReadLineAddNewHeader());
 		}
-		buf1.reset();
+		buf1.clear();
 		for (q=p; *q && *q != '\n'; q++)
 		{
-			buf1.push( tolower(*q) );
+			buf1.push_back( tolower(*q) );
 			if (*q == ':')	break;
 		}
-		buf1 += '\0';
 
 		if (has_hdr(opti, buf1))
 		{
@@ -545,27 +518,22 @@ static Buffer oldbuf;
 			oldbuf += buf1;
 			buf1=oldbuf;
 
-		Buffer	tbuf;
+			std::string	tbuf;
 
 			tbuf="Old-";
 			tbuf += p;
 			oldbuf=tbuf;
-			oldbuf += '\0';
-			p=oldbuf;
+			p=oldbuf.c_str();
 		}
 		if (has_hdr(optR, buf1, pos))
 		{
-		Buffer	tbuf;
+			std::string tbuf=pos->substr(buf1.size());
 
-			q=optR;
-			q += pos + strlen(buf1);
-			tbuf=q;
+			p += buf1.size();
 
-			p += strlen(buf1);
 			tbuf += p;
 			oldbuf=tbuf;
-			oldbuf += '\0';
-			p=oldbuf;
+			p=oldbuf.c_str();
 		}
 
 		if (has_hdr(optI, buf1))
@@ -574,11 +542,7 @@ static Buffer oldbuf;
 		{
 			if (!has_hdr(optubuf, buf1))
 			{
-				q=p;
-				do
-				{
-					optubuf.push( *q );
-				} while (*q++);
+				optubuf.push_back(p);
 				break;
 			}
 			continue;
@@ -587,23 +551,20 @@ static Buffer oldbuf;
 		if (has_hdr(optU, buf1))
 		{
 			if (has_hdr(optUbuf, buf1, pos))
-				strip_header(optUbuf, pos);
-			while (*p)
-			{
-				optUbuf.push( *p );
-				p++;
-			}
-			optUbuf.pop();
-			optUbuf.push('\0');
+				optUbuf.erase(pos);
+
+			std::string s{p};
+
+			s.pop_back();
+
+			optUbuf.push_back(std::move(s));
 			continue;
 		}
 		break;
 	}
 
-unsigned offset;
-
-	if (has_hdr(opta, buf1, offset))
-		strip_header(opta, offset);
+	if (has_hdr(opta, buf1, pos))
+		opta.erase(pos);
 	return (p);
 }
 
@@ -613,46 +574,28 @@ const char *ReadLineAddNewHeader()
 {
 	append_more_headers= &ReadLineAddNewHeader;
 
-Buffer	*bufptr;
+	multibuf *bufptr;
 
-	if (opta.Length())	bufptr= &opta;
-	else if (optA.Length())	bufptr= &optA;
-	else if (opti.Length())	bufptr= &opti;
-	else if (optI.Length())	bufptr= &optI;
-	else if (optUbuf.Length())	bufptr= &optUbuf;
+	if (opta.size())	bufptr= &opta;
+	else if (optA.size())	bufptr= &optA;
+	else if (opti.size())	bufptr= &opti;
+	else if (optI.size())	bufptr= &optI;
+	else if (optUbuf.size())	bufptr= &optUbuf;
 	else
 	{
 		append_more_headers=&ReadLineAddNewHeaderDone;
 		return ("\n");
 	}
 
-static Buffer buf1;
-Buffer	buf2;
+	static std::string buf1;
 
-	buf1.reset();
+	buf1= (*bufptr)[0];
 
-const char *p= *bufptr;
-int l= bufptr->Length();
+	buf1.push_back('\n');
 
-	while (l)
-	{
-		if ( !*p )
-		{
-			p++;
-			l--;
-			break;
-		}
-		buf1.push( *p );
-		p++;
-		l--;
-	}
-	buf1.push('\n');
-	buf1.push('\0');
+	(*bufptr).erase((*bufptr).begin());
 
-	while (l--)
-		buf2.push (*p++);
-	*bufptr=buf2;
-	return (buf1);
+	return (buf1.c_str());
 }
 
 const char *ReadLineAddNewHeaderDone()
@@ -667,7 +610,7 @@ const char *p=(*append_more_headers)();
 
 	if (!p)	return (p);
 
-static Buffer	buf;
+static std::string	buf;
 
 	if (*p == '\n')
 		inbody=1;
@@ -676,12 +619,12 @@ static Buffer	buf;
 	{
 	const char *q;
 
-		buf.reset();
+		buf.clear();
 		for (q=p; *q; q++)
 		{
 			if (*q != '\n')
 			{
-				buf.push(*q);
+				buf.push_back(*q);
 				continue;
 			}
 			do
@@ -689,22 +632,20 @@ static Buffer	buf;
 				++q;
 			} while (*q && isspace(*q));
 			if (*q)
-				buf.push(' ');
+				buf.push_back(' ');
 			--q;
 		}
-		if (addcrs)	buf.push('\r');
-		buf.push('\n');
-		buf.push('\0');
-		return (buf);
+		if (addcrs)	buf.push_back('\r');
+		buf.push_back('\n');
+		return (buf.c_str());
 	}
 
 	if (addcrs)
 	{
 		buf=p;
-		buf.pop();
+		buf.pop_back();
 		buf += "\r\n";
-		buf += '\0';
-		return (buf);
+		return (buf.c_str());
 	}
 	return (p);
 }
@@ -727,7 +668,7 @@ const char *p;
 void cache(int, char *[], int)
 {
 const char *p;
-Buffer	buf;
+std::string	buf;
 int found=0;
 
 	addcrs=0;
@@ -736,29 +677,33 @@ int found=0;
 	int	c;
 
 		if (inbody)	break;
-		buf.reset();
+		buf.clear();
 		while (*p && *p != '\n')
 		{
 			c= (unsigned char)*p;
 			c=tolower(c);
-			buf.push(c);
+			buf.push_back(c);
 			if (*p++ == ':')	break;
 		}
 		if (!(buf == "message-id:"))	continue;
-		buf += '\0';
 		while (*p && isspace( (unsigned char)*p))	p++;
-		buf.reset();
+		buf.clear();
 		while (*p)
 		{
-			buf.push(*p);
+			buf.push_back(*p);
 			++p;
 		}
 
-		while ( (c=(unsigned char)buf.pop()) != 0 && isspace(c))
-			;
-		if (c)	buf.push(c);
-		if (buf.Length() == 0)	break;
-		buf.push('\0');
+		while (buf.size())
+		{
+			auto c=buf.back();
+
+			if (!isspace(c))
+				break;
+			buf.pop_back();
+		}
+
+		if (buf.size() == 0)	break;
 
 	int	fd=open(cache_name, O_RDWR | O_CREAT, 0600);
 
@@ -790,7 +735,7 @@ int found=0;
 
 		if (newpos < pos)	newpos=pos;
 
-		if ((charbuf=new char[newpos+buf.Length()+1]) == NULL)
+		if ((charbuf=new char[newpos+buf.size()+1]) == NULL)
 			outofmem();
 
 	off_t	readcnt=read(fd, charbuf, newpos);
@@ -802,7 +747,7 @@ int found=0;
 		for (q=r=charbuf; q');
+	buf.push_back('>');
 }
 
-static void add_opta(Buffer &buf, const char *optarg)
+static void add_opta(multibuf &buf, const char *optarg)
 {
-Buffer	chk_buf;
-const char *c;
+	std::string chk_buf;
+	const char *c;
+
+	chk_buf.reserve(strlen(optarg));
 
 	for (c=optarg; *c; c++)
-		chk_buf.push ( tolower( (unsigned char)*c ));
+		chk_buf.push_back( tolower( (unsigned char)*c ));
 	if (chk_buf == "message-id:" || chk_buf == "resent_message_id:")
 	{
 		chk_buf=optarg;
-		chk_buf += ' ';
+		chk_buf += " ";
 		add_messageid(chk_buf);
-		chk_buf += '\0';
-		optarg=chk_buf;
+		optarg=chk_buf.c_str();
 	}
 
-	do
-	{
-		buf.push( *optarg );
-	} while (*optarg++);
+	buf.push_back(optarg);
 }
 
 int main(int argc, char *argv[])
@@ -1137,63 +1081,52 @@ void	(*function)(int, char *[], int)=0;
 			if (!optarg && argn+1 < argc)	optarg=argv[++argn];
 			if (!optarg || !*optarg)	help();
 			if (function)	help();
-			do
-			{
-				opti.push( *optarg );
-			} while (*optarg++);
+
+			opti.push_back(optarg);
 			break;
 		case 'I':
 			if (!optarg && argn+1 < argc)	optarg=argv[++argn];
 			if (!optarg || !*optarg)	help();
 			if (function)	help();
-			do
-			{
-				optI.push( *optarg );
-			} while (*optarg++);
+			optI.push_back(optarg);
 			break;
 		case 'R':
 			if (!optarg && argn+1 < argc)	optarg=argv[++argn];
 			if (!optarg || !*optarg)	help();
 			if (function)	help();
-			while (*optarg)
-				optR.push(*optarg++);
+
+			optR.push_back(optarg);
 			if (argn+1 >= argc)	help();
 			optarg=argv[++argn];
-			while (*optarg)
-				optR.push(*optarg++);
-			optR.push(0);
+
+			(*--optR.end()) += optarg;
 			break;
 		case 'u':
 			if (!optarg && argn+1 < argc)	optarg=argv[++argn];
 			if (!optarg || !*optarg)	help();
 			if (function)	help();
-			while (*optarg)
-				optu.push(*optarg++);
-			optu.push(0);
+
+			optu.push_back(optarg);
 			break;
 		case 'U':
 			if (!optarg && argn+1 < argc)	optarg=argv[++argn];
 			if (!optarg || !*optarg)	help();
 			if (function)	help();
-			while (*optarg)
-				optU.push(*optarg++);
-			optU.push(0);
+
+			optU.push_back(optarg);
 			break;
 		case 'x':
 			if (!optarg && argn+1 < argc)	optarg=argv[++argn];
 			if (!optarg || !*optarg)	help();
 			if (function)	help();
-			while (*optarg)
-				optx.push(*optarg++);
-			optx.push(0);
+
+			optx.push_back(optarg);
 			break;
 		case 'X':
 			if (!optarg && argn+1 < argc)	optarg=argv[++argn];
 			if (!optarg || !*optarg)	help();
 			if (function)	help();
-			while (*optarg)
-				optX.push(*optarg++);
-			optX.push(0);
+			optX.push_back(optarg);
 			break;
 		case 's':
 			if (function)	help();
@@ -1206,7 +1139,7 @@ void	(*function)(int, char *[], int)=0;
 		}
 		if (done)	break;
 	}
-	if (optx.Length() || optX.Length())
+	if (optx.size() || optX.size())
 	{
 		if (function)	help();
 		function=extract_headers;
diff --git a/courier-imap-x/libs/maildrop/search.C b/courier-imap-x/libs/maildrop/search.C
index 32d4ce654..fa8696eb9 100644
--- a/courier-imap-x/libs/maildrop/search.C
+++ b/courier-imap-x/libs/maildrop/search.C
@@ -8,7 +8,7 @@
 #include	
 #include	
 #include	
-
+#include	
 
 void Search::cleanup()
 {
@@ -61,20 +61,19 @@ int	Search::init(const char *expr, const char *opts)
 
 	if (!pcre_regexp)
 	{
-		Buffer b;
+		std::string b;
 
 		PCRE2_UCHAR buffer[256];
 		pcre2_get_error_message(errcode, buffer, sizeof(buffer));
 
 		b="Invalid regular expression, offset ";
-		b.append((unsigned long)errindex);
+		add_integer(b, errindex);
 		b += " of: ";
 		b += expr;
 		b += ": ";
 		b += (char *)buffer;
 		b += "\n";
-		b += '\0';
-		merr.write(b);
+		merr.write(b.c_str());
 		return -1;
 	}
 
@@ -84,13 +83,12 @@ int	Search::init(const char *expr, const char *opts)
 
 	if (!match_data)
 	{
-		Buffer b;
+		std::string b;
 
 		b="Failed to create match data for: ";
 		b += expr;
 		b += "\n";
-		b += '\0';
-		merr.write(b);
+		merr.write(b.c_str());
 		cleanup();
 		return -1;
 	}
@@ -119,7 +117,7 @@ int	Search::init(const char *expr, const char *opts)
 }
 
 int Search::find(Message &msg, MessageInfo &,
-	const char *expr, const char *opts, Buffer *foreachp)
+	const char *expr, const char *opts, foreach_t *foreachp)
 {
 	if (init(expr, opts))	return (-1);
 
@@ -128,21 +126,20 @@ int Search::find(Message &msg, MessageInfo &,
 }
 
 int Search::find(const char *str, const char *expr, const char *opts,
-		Buffer *foreachp)
+		foreach_t *foreachp)
 {
 	if (init(expr, opts))	return (-1);
 
 	if (VerboseLevel() > 2)
 	{
-	Buffer	msg;
+	std::string	msg;
 
 		msg="Matching /";
-		msg.append(expr);
-		msg.append("/ against ");
+		msg += expr;
+		msg += "/ against ";
 		msg += str;
-		msg += '\n';
-		msg += '\0';
-		merr.write(msg);
+		msg += "\n";
+		merr.write(msg.c_str());
 	}
 
 	int startoffset=0;
@@ -192,7 +189,7 @@ int Search::find(const char *str, const char *expr, const char *opts,
 //
 //////////////////////////////////////////////////////////////////////////////
 
-int Search::findinline(Message &msg, const char *expr, Buffer *foreachp)
+int Search::findinline(Message &msg, const char *expr, foreach_t *foreachp)
 {
 	struct rfc2045_decodemsgtoutf8_cb decode_cb;
 
@@ -206,13 +203,13 @@ int Search::findinline(Message &msg, const char *expr, Buffer *foreachp)
 	if (!match_body)
 		decode_cb.flags |= RFC2045_DECODEMSG_NOBODY;
 
-	current_line.reset();
+	current_line.clear();
 	decode_cb.output_func=&Search::search_cb;
 	decode_cb.arg=this;
 	foreachp_arg=foreachp;
 	rfc2045_decodemsgtoutf8(&msg.rfc2045src_parser,
 				msg.rfc2045p, &decode_cb);
-	if (current_line.Length() >= 1)
+	if (current_line.size() >= 1)
 		search_cb("\n", 1);
 	return 0;
 }
@@ -230,30 +227,22 @@ int Search::search_cb(const char *ptr, size_t cnt)
 
 		if (*ptr == '\n')
 		{
-			current_line += '\0';
 
 			if (VerboseLevel() > 2)
 			{
-			Buffer	msg;
+			std::string	msg;
 
 				msg="Matching /";
 
-				{
-					Buffer cpy;
+				msg += search_expr;
 
-					cpy += search_expr;
-					cpy += '\0';
-					msg.append(cpy);
-				}
-				msg.append("/ against ");
-				msg += current_line;
-				msg.pop();	// Trailing null byte.
-				msg += '\n';
-				msg += '\0';
-				merr.write(msg);
+				msg += "/ against ";
+				msg += current_line.c_str();
+				msg += "\n";
+				merr.write(msg.c_str());
 			}
 
-			const char *orig_str=current_line;
+			const char *orig_str=current_line.c_str();
 
 			int rc=pcre2_match(pcre_regexp,
 					   (PCRE2_SPTR8)orig_str,
@@ -289,7 +278,7 @@ int Search::search_cb(const char *ptr, size_t cnt)
 			else	if (VerboseLevel() > 2)
 				merr.write("Not matched.\n");
 
-			current_line.reset();
+			current_line.clear();
 
 			++ptr;
 			--cnt;
@@ -300,7 +289,7 @@ int Search::search_cb(const char *ptr, size_t cnt)
 		for (i=0; i foreach_values;
+
 	for (cnt=0; cntpush_back(std::move(foreach_values));
+	}
 }
diff --git a/courier-imap-x/libs/maildrop/search.h b/courier-imap-x/libs/maildrop/search.h
index 963ea8c44..9e31625f6 100644
--- a/courier-imap-x/libs/maildrop/search.h
+++ b/courier-imap-x/libs/maildrop/search.h
@@ -3,6 +3,7 @@
 
 
 #include	"buffer.h"
+#include 
 
 #define PCRE2_CODE_UNIT_WIDTH 8
 
@@ -38,13 +39,15 @@
 class MessageInfo;
 class Message;
 
+typedef std::list> foreach_t;
+
 class Search {
 
 	pcre2_code *pcre_regexp;
 	pcre2_match_data *match_data;
 
-	Buffer	current_line;
-	Buffer	next_line;
+	std::string	current_line;
+	std::string	next_line;
 
 	int	match_top_header, match_other_headers, match_body;
 	double	weight1, weight2;
@@ -62,17 +65,17 @@ class Search {
 		   match_data(NULL) {}
 	~Search()	{ cleanup(); }
 	int find(Message &, MessageInfo &, const char *, const char *,
-		Buffer *);
-	int find(const char *, const char *, const char *, Buffer *);
+		foreach_t *);
+	int find(const char *, const char *, const char *, foreach_t *);
 private:
-	int findinline(Message &, const char *, Buffer *);
-	int findinsection(Message &, const char *, Buffer *);
+	int findinline(Message &, const char *, foreach_t *);
+	int findinsection(Message &, const char *, foreach_t *);
 	void init_match_vars(const char *,
 			     PCRE2_SIZE *,
 			     uint32_t,
-			     Buffer *);
-	Buffer search_expr;
-	Buffer *foreachp_arg;
+			     foreach_t *);
+	std::string search_expr;
+	foreach_t *foreachp_arg;
 	static int search_cb(const char *ptr, size_t cnt, void *arg);
 	int search_cb(const char *ptr, size_t cnt);
 } ;
diff --git a/courier-imap-x/libs/maildrop/testsuite.in b/courier-imap-x/libs/maildrop/testsuite.in
index 39c169334..d8bf1c15e 100644
--- a/courier-imap-x/libs/maildrop/testsuite.in
+++ b/courier-imap-x/libs/maildrop/testsuite.in
@@ -1,6 +1,6 @@
 #!/bin/sh
 #
-# Copyright 1998 - 2011 Double Precision, Inc.  See COPYING for
+# Copyright 1998 - 2023 Double Precision, Inc.  See COPYING for
 # distribution information.
 
 PWD=`pwd`
@@ -18,12 +18,15 @@ unset LC_TELEPHONE
 unset LC_MEASUREMENT
 unset LC_IDENTIFICATION
 unset LC_ALL
+unset VERBOSE
 
 if test "$VALGRIND" != ""
 then
     VALGRIND="$VALGRIND --tool=memcheck --error-exitcode=1"
 fi
 
+VALGRIND=""
+
 set -e
 
 $VALGRIND ./testtimer
@@ -40,7 +43,7 @@ EOF
 $VALGRIND ../makedat/makedatprog testsuite.chk testsuite.tmp testsuite.dat
 
 cat <testsuite.recipe
-if ( gdbmopen("$PWD/testsuite.dat", "R") != 0 )
+if ( gdbmopen("$PWD/testsuite.dat", "W") != 0 )
 {
 	exit
 }
@@ -52,6 +55,9 @@ FOO=gdbmfetch("foobar@foo.bar.com", "D")
 echo "FOO: \$FOO"
 FOO=gdbmfetch("foobaz@foo.bar.com", "D")
 echo "FOO: \$FOO"
+gdbmstore("foobaz@foo.bar.com", "BAR");
+FOO=gdbmfetch("foobaz@foo.bar.com", "D")
+echo "FOO: \$FOO"
 EOF
 
 chmod go-rw testsuite.recipe
@@ -59,8 +65,10 @@ cat <testsuite.chk
 FOO: bar
 FOO: barfoo
 FOO: foobar
+FOO: BAR
 EOF
-$VALGRIND ./maildrop ./testsuite.recipe teststdin
+diff teststdin testsuite.chk
 
 fi
 
@@ -107,8 +115,8 @@ Delivered message one
 Done.
 EOF
 	echo "To: testsuite" >testsuite.stdin
-	$VALGRIND ./maildrop ./testsuite.recipe testsuite.chk2
+	diff testsuite.chk2 testsuite.chk
 fi
 
 
@@ -591,9 +599,124 @@ LANG=en_US.utf-8 $VALGRIND ./mailbot -T feedback -R abuse -n -N -m testmailbot.m
     --feedback-incidents 2 \
     testsuite.msg <testsuite.recipe <&1 testsuite.msg <testsuite.chk <testsuite.recipe <&1 testsuite.recipe <testsuite.msg <&1 testsuite.recipe < 2 && 2 > 3)
+{
+}
+
+if ( 1 <= 2 && 2 >= 1 && 1 == 1 && 1 != 2 && "a" lt "b" && "a" le "a" && \
+     "b" gt "a" && b ge "b" && "a" eq "a" && "a" ne "b")
+{
+}
+
+1 | 2;
+3 & 1;
+1 + 2*3 - 4 / 4
+length("abc")
+exit
+EOF
+$VALGRIND ./maildrop -V 9 ./testsuite.recipe 2>&1 
+Tokenized string: "2"
+Tokenized &&
+Tokenized string: "2"
+Tokenized >
+Tokenized string: "3"
+Tokenized )
+Tokenized ;
+Tokenized {
+Tokenized ;
+Tokenized }
+Tokenized ;
+Tokenized ;
+Tokenized if
+Tokenized (
+Tokenized string: "1"
+Tokenized <=
+Tokenized string: "2"
+Tokenized &&
+Tokenized string: "2"
+Tokenized >=
+Tokenized string: "1"
+Tokenized &&
+Tokenized string: "1"
+Tokenized ==
+Tokenized string: "1"
+Tokenized &&
+Tokenized string: "1"
+Tokenized !=
+Tokenized string: "2"
+Tokenized &&
+Tokenized string: "a"
+Tokenized lt
+Tokenized string: "b"
+Tokenized &&
+Tokenized string: "a"
+Tokenized le
+Tokenized string: "a"
+Tokenized &&
+Tokenized string: "b"
+Tokenized gt
+Tokenized string: "a"
+Tokenized &&
+Tokenized string: "b"
+Tokenized ge
+Tokenized string: "b"
+Tokenized &&
+Tokenized string: "a"
+Tokenized eq
+Tokenized string: "a"
+Tokenized &&
+Tokenized string: "a"
+Tokenized ne
+Tokenized string: "b"
+Tokenized )
+Tokenized ;
+Tokenized {
+Tokenized ;
+Tokenized }
+Tokenized ;
+Tokenized ;
+Tokenized string: "1"
+Tokenized |
+Tokenized string: "2"
+Tokenized ;
+Tokenized ;
+Tokenized string: "3"
+Tokenized &
+Tokenized string: "1"
+Tokenized ;
+Tokenized ;
+Tokenized string: "1"
+Tokenized +
+Tokenized string: "2"
+Tokenized *
+Tokenized string: "3"
+Tokenized -
+Tokenized string: "4"
+Tokenized /
+Tokenized string: "4"
+Tokenized ;
+Tokenized length
+Tokenized (
+Tokenized string: "abc"
+Tokenized )
+Tokenized ;
+Tokenized exit
+Tokenized ;
+Tokenized eof
+./testsuite.recipe(1): Evaluating IF condition.
+./testsuite.recipe(1): Operation on: 0 and 1 - less than, result is 1
+./testsuite.recipe(1): Operation: logical or, left hand side=1
+./testsuite.recipe(1): Operation: logical or, result=1
+./testsuite.recipe(1): IF evaluated, result=1
+./testsuite.recipe(5): Evaluating IF condition.
+./testsuite.recipe(5): Operation on: 1 and 2 - greater than, result is 0
+./testsuite.recipe(5): Operation: logical and, left hand side=0
+./testsuite.recipe(5): Operation: logical and, result=0
+./testsuite.recipe(5): IF evaluated, result=0
+./testsuite.recipe(9): Evaluating IF condition.
+./testsuite.recipe(9): Operation on: 1 and 2 - less than or equal, result is 1
+./testsuite.recipe(9): Operation: logical and, left hand side=1
+./testsuite.recipe(9): Operation: logical and, evaluating right hand size.
+./testsuite.recipe(9): Operation on: 2 and 1 - greater than or equal, result is 1
+./testsuite.recipe(9): Operation: logical and, result=1
+./testsuite.recipe(9): Operation: logical and, left hand side=1
+./testsuite.recipe(9): Operation: logical and, evaluating right hand size.
+./testsuite.recipe(9): Operation on: 1 and 1 - equal, result is 1
+./testsuite.recipe(9): Operation: logical and, result=1
+./testsuite.recipe(9): Operation: logical and, left hand side=1
+./testsuite.recipe(9): Operation: logical and, evaluating right hand size.
+./testsuite.recipe(9): Operation on: 1 and 2 - not equal, result is 1
+./testsuite.recipe(9): Operation: logical and, result=1
+./testsuite.recipe(9): Operation: logical and, left hand side=1
+./testsuite.recipe(9): Operation: logical and, evaluating right hand size.
+./testsuite.recipe(9): Operation on: a and b - string less than, result is 1
+./testsuite.recipe(9): Operation: logical and, result=1
+./testsuite.recipe(9): Operation: logical and, left hand side=1
+./testsuite.recipe(9): Operation: logical and, evaluating right hand size.
+./testsuite.recipe(9): Operation on: a and a - string less than or equal, result is 1
+./testsuite.recipe(9): Operation: logical and, result=1
+./testsuite.recipe(9): Operation: logical and, left hand side=1
+./testsuite.recipe(9): Operation: logical and, evaluating right hand size.
+./testsuite.recipe(9): Operation on: b and a - string greater than, result is 1
+./testsuite.recipe(9): Operation: logical and, result=1
+./testsuite.recipe(9): Operation: logical and, left hand side=1
+./testsuite.recipe(9): Operation: logical and, evaluating right hand size.
+./testsuite.recipe(9): Operation on: b and b - string grater than or equal, result is 1
+./testsuite.recipe(9): Operation: logical and, result=1
+./testsuite.recipe(9): Operation: logical and, left hand side=1
+./testsuite.recipe(9): Operation: logical and, evaluating right hand size.
+./testsuite.recipe(9): Operation on: a and a - string equal, result is 1
+./testsuite.recipe(9): Operation: logical and, result=1
+./testsuite.recipe(9): Operation: logical and, left hand side=1
+./testsuite.recipe(9): Operation: logical and, evaluating right hand size.
+./testsuite.recipe(9): Operation on: a and b - string not equal, result is 1
+./testsuite.recipe(9): Operation: logical and, result=1
+./testsuite.recipe(9): IF evaluated, result=1
+./testsuite.recipe(13): Operation on: 1 and 2 - bitwise or, result is 3
+./testsuite.recipe(14): Operation on: 3 and 1 - bitwise and, result is 1
+./testsuite.recipe(15): Operation on: 2 and 3 - multiply, result is 6
+./testsuite.recipe(15): Operation on: 1 and 6 - add, result is 7
+./testsuite.recipe(15): Operation on: 4 and 4 - divide, result is 1
+./testsuite.recipe(15): Operation on: 7 and 1 - subtract, result is 6
+./testsuite.recipe(16): Operation on: abc - strlength.
+./testsuite.recipe(16): Operation: strlength=3
diff --git a/courier-imap-x/libs/maildrop/token.C b/courier-imap-x/libs/maildrop/token.C
index 59ca3e098..497de92fa 100644
--- a/courier-imap-x/libs/maildrop/token.C
+++ b/courier-imap-x/libs/maildrop/token.C
@@ -73,7 +73,7 @@ static const char *names[]={
 		"unset"
 		} ;
 
-static Buffer namebuf;
+static std::string namebuf;
 
 const char *Token::Name()
 {
@@ -82,8 +82,7 @@ const char *Token::Name()
 		namebuf="string: \"";
 		namebuf += buf;
 		namebuf += "\"";
-		namebuf.push(0);
-		return (namebuf);
+		return (namebuf.c_str());
 	}
 
 	if (type == sqstring)
@@ -91,8 +90,7 @@ const char *Token::Name()
 		namebuf="string: '";
 		namebuf += buf;
 		namebuf += "'";
-		namebuf.push(0);
-		return (namebuf);
+		return (namebuf.c_str());
 	}
 
 	if (type == btstring)
@@ -100,16 +98,14 @@ const char *Token::Name()
 		namebuf="string: `";
 		namebuf += buf;
 		namebuf += "`";
-		namebuf.push(0);
-		return (namebuf);
+		return (namebuf.c_str());
 	}
 
 	if (type == regexpr)
 	{
 		namebuf="regexp: ";
 		namebuf += buf;
-		namebuf.push(0);
-		return (namebuf);
+		return (namebuf.c_str());
 	}
 
 unsigned	t=(unsigned)type;
diff --git a/courier-imap-x/libs/maildrop/token.h b/courier-imap-x/libs/maildrop/token.h
index f2829b195..785976c58 100644
--- a/courier-imap-x/libs/maildrop/token.h
+++ b/courier-imap-x/libs/maildrop/token.h
@@ -14,7 +14,7 @@
 
 class	Token {
 
-	Buffer	buf;
+	std::string	buf;
 
 public:
 	enum tokentype {
@@ -96,10 +96,10 @@ class	Token {
 	Token &operator=(const Token &t) { type=t.type; buf=t.buf; return (*this); }
 
 	void Type(tokentype t) { type=t; }
-	void Type(tokentype t, const Buffer &tbuf) { type=t; buf=tbuf; }
+	void Type(tokentype t, const std::string &tbuf) { type=t; buf=tbuf; }
 	tokentype Type() const { return (type); }
-	const Buffer &String() const { return (buf); }
-	Buffer &String() { return (buf); }
+	const std::string &String() const { return (buf); }
+	std::string &String() { return (buf); }
 
 	const char *Name();
 } ;
diff --git a/courier-imap-x/libs/maildrop/varlist.C b/courier-imap-x/libs/maildrop/varlist.C
index 4ec045c04..3aa811a37 100644
--- a/courier-imap-x/libs/maildrop/varlist.C
+++ b/courier-imap-x/libs/maildrop/varlist.C
@@ -5,145 +5,77 @@
 #include	"maildrop.h"
 
 #include	
+#include	
 
+std::unordered_map varlist;
 
-class Variable {
-public:
-
-	Variable *next;
-	Buffer	name, value;
-	} ;
-
-static Variable *varlist[101];
-
-void UnsetVar(const Buffer &var)
+void UnsetVar(const std::string &var)
 {
-int varlen=var.Length();
-unsigned n=0;
-int i;
-const char *p=var;
-	for (i=varlen; i; --i)
-		n = (n << 1) ^ (unsigned char)*p++;
-
-	if (var.Length() == 7 &&
-		strncmp( (const char *)var, "VERBOSE", 7) == 0)
+	varlist.erase(var);
+
+	if (var == "VERBOSE")
 	{
 		maildrop.verbose_level=0;
 	}
 
-	n %= sizeof(varlist)/sizeof(varlist[0]);
-
-Variable **v;
-
-	for (v= &varlist[n]; *v; v= &(*v)->next)
-		if ( (*v)->name == var )
-		{
-		Variable *vv= (*v);
-
-			(*v)= vv->next;
-			delete vv;
-			break;
-		}
 	return;
 }
 
-void SetVar(const Buffer &var, const Buffer &value)
+void SetVar(const std::string &var, const std::string &value)
 {
-int varlen=var.Length();
-unsigned n=0;
-int i;
-const char *p=var;
-
-	for (i=varlen; i; --i)
-		n = (n << 1) ^ (unsigned char)*p++;
+	varlist[var]=value;
 
-	if (var.Length() == 7 &&
-		strncmp( (const char *)var, "VERBOSE", 7) == 0)
+	if (var == "VERBOSE")
 	{
-		maildrop.verbose_level= value.Int("0");
+		maildrop.verbose_level= extract_int(value, "0");
 		if (maildrop.isdelivery)	maildrop.verbose_level=0;
 	}
+}
 
-	n %= sizeof(varlist)/sizeof(varlist[0]);
-
-Variable *v;
+std::string GetVar(const std::string &var)
+{
+	auto iter=varlist.find(var);
 
-	for (v=varlist[n]; v; v=v->next)
-		if ( v->name == var )
-		{
-			v->value=value;
-			return;
-		}
+	if (iter != varlist.end())
+		return iter->second;
 
-	v=new Variable;
-	if (!v)	outofmem();
-	v->name=var;
-	v->value=value;
-	v->next=varlist[n];
-	varlist[n]=v;
+	return "";
 }
 
-static Buffer zero;
+// Create environment for a child process.
 
-const Buffer *GetVar(const Buffer &var)
+void ExportEnv(std::vector> &strings,
+	       std::vector &pts)
 {
-int varlen=var.Length(),i;
-unsigned n=0;
-const char *p=var;
+	strings.clear();
+	strings.reserve(varlist.size());
 
-	for (i=varlen; i; --i)
-		n = (n << 1) ^ (unsigned char)*p++;
-	n %= sizeof(varlist)/sizeof(varlist[0]);
+	std::string v;
 
-Variable *v;
+	for (auto &kv:varlist)
+	{
+		v.clear();
+		v.reserve(kv.first.size()+kv.second.size()+2);
 
-	for (v=varlist[n]; v; v=v->next)
-		if ( v->name == var)	return ( &v->value );
-	return (&zero);
-}
+		v=kv.first;
+		v += "=";
+		v += kv.second;
 
-const char *GetVarStr(const Buffer &var)
-{
-static Buffer tempbuf;
+		std::vector new_buf;
 
-	tempbuf= *GetVar(var);
-	tempbuf += '\0';
-	return (tempbuf);
-}
+		new_buf.reserve(v.size()+1);
 
-// Create environment for a child process.
+		new_buf.insert(new_buf.end(), v.begin(), v.end());
+		new_buf.push_back(0);
+		strings.push_back(std::move(new_buf));
+	}
 
-char	**ExportEnv()
-{
-unsigned	i,n,l;
-Variable	*v;
-char	**envp;
-char	*envdatap=0;
-
-	for (i=n=l=0; inext)
-		{
-			l += v->name.Length() + v->value.Length()+2;
-			++n;
-		}
-
-	if ((envp=new char *[n+1]) == NULL)	outofmem();
-	if (l && (envdatap=new char[l]) == NULL)	outofmem();
-
-	for (i=n=0; inext)
-		{
-			envp[n]=envdatap;
-			memcpy(envdatap, (const char *)v->name,
-							v->name.Length());
-			envdatap += v->name.Length();
-			*envdatap++ = '=';
-			memcpy(envdatap, (const char *)v->value,
-							v->value.Length());
-			envdatap += v->value.Length();
-			*envdatap++ = 0;
-			n++;
-		}
-	envp[n]=0;
-	return (envp);
+	pts.clear();
+	pts.reserve(strings.size()+1);
+
+	for (auto &s:strings)
+	{
+		pts.push_back(s.data());
+	}
+	pts.push_back(nullptr);
 }
diff --git a/courier-imap-x/libs/maildrop/varlist.h b/courier-imap-x/libs/maildrop/varlist.h
index 390e114bc..30d0a972c 100644
--- a/courier-imap-x/libs/maildrop/varlist.h
+++ b/courier-imap-x/libs/maildrop/varlist.h
@@ -1,16 +1,16 @@
 #ifndef	varlist_h
 #define	varlist_h
 
+#include 
+#include 
 
 //
 // Quick hack to implement variables - get them and set them.
 //
 
-class Buffer;
-
-void UnsetVar(const Buffer &);
-void SetVar(const Buffer &, const Buffer &);
-const Buffer *GetVar(const Buffer &);
-const char *GetVarStr(const Buffer &);
-char **ExportEnv();
+void UnsetVar(const std::string &);
+void SetVar(const std::string &, const std::string &);
+std::string GetVar(const std::string &);
+void ExportEnv(std::vector> &strings,
+	       std::vector &pts);
 #endif
diff --git a/courier-imap-x/libs/numlib/changeuidgid.c b/courier-imap-x/libs/numlib/changeuidgid.c
index ce69839fb..1c9be5939 100644
--- a/courier-imap-x/libs/numlib/changeuidgid.c
+++ b/courier-imap-x/libs/numlib/changeuidgid.c
@@ -52,10 +52,11 @@ void libmail_changeuidgid(uid_t uid, gid_t gid)
  */
 uid_t libmail_getuid(const char *uname, gid_t *pw_gid)
 {
-	size_t bufsize;
+	int bufsize;
 	char *buf;
 	struct passwd pwbuf;
 	struct passwd *pw;
+	int s;
 
 	/*
 	** uname might be a pointer returned from a previous called to getpw(),
@@ -70,35 +71,39 @@ uid_t libmail_getuid(const char *uname, gid_t *pw_gid)
 	}
 	strcpy(p, uname);
 
-#ifdef _SC_GETGR_R_SIZE_MAX
-	bufsize = sysconf(_SC_GETGR_R_SIZE_MAX);
+#ifdef _SC_GETPW_R_SIZE_MAX
+	bufsize = sysconf(_SC_GETPW_R_SIZE_MAX);
 	if (bufsize == -1)          /* Value was indeterminate */
-	{
 #endif
-		bufsize = 16384;        /* Should be more than enough */
-	}
-
-	buf = malloc(bufsize);
-	if (buf == NULL)
 	{
-		perror("malloc");
-		exit(1);
+		bufsize = 16384;        /* Should be more than enough */
 	}
 
+	do {
+		buf = malloc(bufsize);
+		if (buf == NULL)
+		{
+			perror("malloc");
+			exit(1);
+		}
 
-	errno=ENOENT;
-
-	getpwnam_r(p, &pwbuf, buf, bufsize, &pw);
+		s = getpwnam_r(p, &pwbuf, buf, bufsize, &pw);
+		if (s == ERANGE) {
+			free(buf);
+			bufsize += 1024;
+		}
+	} while (s == ERANGE && bufsize <= 65536);
 
-	free(buf);
+	free(p);
 
 	if (pw == 0)
 	{
-		free(p);
-		perror("getpwnam");
+		errno = s;
+		perror("getpwnam_r");
 		exit(1);
 	}
-	free(p);
+
+	free(buf);
 
 	if ( pw_gid ) *pw_gid = pw->pw_gid;
 
diff --git a/courier-imap-x/libs/rfc1035/configure.ac b/courier-imap-x/libs/rfc1035/configure.ac
index 8a6ab707c..6f81896e9 100644
--- a/courier-imap-x/libs/rfc1035/configure.ac
+++ b/courier-imap-x/libs/rfc1035/configure.ac
@@ -22,7 +22,7 @@ LT_INIT
 
 dnl Checks for libraries.
 
-PKG_CHECK_MODULES(LIBIDN2, libidn2 >= 0.0.0, [libidn=yes], [libidn=no])
+PKG_CHECK_MODULES(LIBIDN2, libidn2 >= 2.0.5, [libidn=yes], [libidn=no])
 if test "$libidn" = "no"
 then
 	PKG_CHECK_MODULES(LIBIDN1, libidn >= 0.0.0, [libidn=yes], [libidn=no])
diff --git a/courier-imap-x/libs/rfc822/configure.ac b/courier-imap-x/libs/rfc822/configure.ac
index 57be735cf..a17a9b93c 100644
--- a/courier-imap-x/libs/rfc822/configure.ac
+++ b/courier-imap-x/libs/rfc822/configure.ac
@@ -37,30 +37,12 @@ AC_SYS_LARGEFILE
 
 dnl Checks for library functions.
 
-AC_ARG_WITH(libidn, AS_HELP_STRING([--with-libidn=[DIR]],[Support IDN (needs GNU libidn)]),
-	libidn=$withval, libidn=yes)
+PKG_CHECK_MODULES(LIBIDN, libidn2 >= 2.0.5, [libidn=yes], [libidn=no])
 
-if test "$libidn" = "yes"
+if test "$libidn" = "no"
 then
-	PKG_CHECK_MODULES(LIBIDN2, libidn2 >= 0.0.0, [libidn=yes], [libidn=no])
-	if test "$libidn" != "yes"
-	then
-		PKG_CHECK_MODULES(LIBIDN1, libidn >= 0.0.0, [libidn=yes], [libidn=no])
-		if test "$libidn" != "yes"
-		then
-			libidn=no
-			AC_MSG_WARN([libidn not found])
-		else
-			libidn=yes
-			AC_DEFINE(LIBIDN1, 1, [Define to 1 if you want libidn.])
-		fi
-	else
-		libidn=yes
-		AC_DEFINE(LIBIDN2, 1, [Define to 1 if you want libidn.])
-	fi
+	AC_MSG_ERROR(libidn2 not found)
 fi
-AC_MSG_CHECKING([if libidn should be used])
-AC_MSG_RESULT($libidn)
 
 AC_CHECK_FUNCS(setlocale)
 
diff --git a/courier-imap-x/libs/tcpd/configure.ac b/courier-imap-x/libs/tcpd/configure.ac
index f4a6fbe02..498b10e18 100644
--- a/courier-imap-x/libs/tcpd/configure.ac
+++ b/courier-imap-x/libs/tcpd/configure.ac
@@ -60,7 +60,7 @@ saveLIBS="$LIBS"
 AC_CHECK_LIB(dl, dlopen, [ LIBDL="-ldl" ])
 LIBS="$saveLIBS"
 
-PKG_CHECK_MODULES(LIBIDN2, libidn2 >= 0.0.0, [libidn=yes], [libidn=no])
+PKG_CHECK_MODULES(LIBIDN2, libidn2 >= 2.0.5, [libidn=yes], [libidn=no])
 if test "$libidn" != "yes"
 then
 	PKG_CHECK_MODULES(LIBIDN1, libidn >= 0.0.0, [libidn=yes], [libidn=no])

From 734e9b0e67a0fa2c1b201112e2780c953def5a98 Mon Sep 17 00:00:00 2001
From: Manvendra Bhangui 
Date: Sun, 23 Apr 2023 21:53:04 +0530
Subject: [PATCH 2/4] fix libidn2 minimum version

---
 courier-imap-x/configure.ac                                 | 2 +-
 courier-imap-x/libs/rfc1035/configure.ac                    | 2 +-
 courier-imap-x/libs/rfc822/configure.ac                     | 2 +-
 courier-imap-x/libs/tcpd/configure.ac                       | 2 +-
 indimail-access/debian/indimail-access-xUbuntu_16.04.dsc.in | 2 +-
 indimail-access/doc/ChangeLog                               | 3 +++
 6 files changed, 8 insertions(+), 5 deletions(-)

diff --git a/courier-imap-x/configure.ac b/courier-imap-x/configure.ac
index 56057ff93..af9ee416d 100644
--- a/courier-imap-x/configure.ac
+++ b/courier-imap-x/configure.ac
@@ -47,7 +47,7 @@ case "$host" in
 	;;
 esac
 
-PKG_CHECK_MODULES(LIBIDN2, libidn2 >= 2.0.5, [libidn=yes], [libidn=no])
+PKG_CHECK_MODULES(LIBIDN2, libidn2 >= 0.0.0, [libidn=yes], [libidn=no])
 if test "$libidn" != "yes"
 then
 	PKG_CHECK_MODULES(LIBIDN1, libidn >= 0.0.0, [libidn=yes], [libidn=no])
diff --git a/courier-imap-x/libs/rfc1035/configure.ac b/courier-imap-x/libs/rfc1035/configure.ac
index 6f81896e9..ee8d0d73d 100644
--- a/courier-imap-x/libs/rfc1035/configure.ac
+++ b/courier-imap-x/libs/rfc1035/configure.ac
@@ -22,7 +22,7 @@ LT_INIT
 
 dnl Checks for libraries.
 
-PKG_CHECK_MODULES(LIBIDN2, libidn2 >= 2.0.5, [libidn=yes], [libidn=no])
+PKG_CHECK_MODULES(LIBIDN2, libidn2 >= 2.0.4, [libidn=yes], [libidn=no])
 if test "$libidn" = "no"
 then
 	PKG_CHECK_MODULES(LIBIDN1, libidn >= 0.0.0, [libidn=yes], [libidn=no])
diff --git a/courier-imap-x/libs/rfc822/configure.ac b/courier-imap-x/libs/rfc822/configure.ac
index a17a9b93c..77f150178 100644
--- a/courier-imap-x/libs/rfc822/configure.ac
+++ b/courier-imap-x/libs/rfc822/configure.ac
@@ -37,7 +37,7 @@ AC_SYS_LARGEFILE
 
 dnl Checks for library functions.
 
-PKG_CHECK_MODULES(LIBIDN, libidn2 >= 2.0.5, [libidn=yes], [libidn=no])
+PKG_CHECK_MODULES(LIBIDN, libidn2 >= 2.0.4, [libidn=yes], [libidn=no])
 
 if test "$libidn" = "no"
 then
diff --git a/courier-imap-x/libs/tcpd/configure.ac b/courier-imap-x/libs/tcpd/configure.ac
index 498b10e18..213af7f5d 100644
--- a/courier-imap-x/libs/tcpd/configure.ac
+++ b/courier-imap-x/libs/tcpd/configure.ac
@@ -60,7 +60,7 @@ saveLIBS="$LIBS"
 AC_CHECK_LIB(dl, dlopen, [ LIBDL="-ldl" ])
 LIBS="$saveLIBS"
 
-PKG_CHECK_MODULES(LIBIDN2, libidn2 >= 2.0.5, [libidn=yes], [libidn=no])
+PKG_CHECK_MODULES(LIBIDN2, libidn2 >= 2.0.4, [libidn=yes], [libidn=no])
 if test "$libidn" != "yes"
 then
 	PKG_CHECK_MODULES(LIBIDN1, libidn >= 0.0.0, [libidn=yes], [libidn=no])
diff --git a/indimail-access/debian/indimail-access-xUbuntu_16.04.dsc.in b/indimail-access/debian/indimail-access-xUbuntu_16.04.dsc.in
index 167583cae..c8c991052 100644
--- a/indimail-access/debian/indimail-access-xUbuntu_16.04.dsc.in
+++ b/indimail-access/debian/indimail-access-xUbuntu_16.04.dsc.in
@@ -10,4 +10,4 @@ Homepage: https://github.com/mbhangui/indimail-virtualdomains
 Debtransform-Release: 1
 Debtransform-Tar: debian.tar.gz
 Standards-Version: 3.9.1
-Build-Depends: cdbs, debhelper (>= 9), gcc, g++, sed, pkg-config, binutils, coreutils, automake, libpam0g-dev, libpcre2-dev, libpcre3-dev, libgdbm-dev, libdb-dev, libgcrypt20-dev, python (>= 2.0), libssl-dev, bison (>= 1.2), flex (>= 2.5), libidn11-dev, mime-support, m4, gawk, procps
+Build-Depends: cdbs, debhelper (>= 9), gcc, g++, sed, pkg-config, binutils, coreutils, automake, libpam0g-dev, libpcre2-dev, libpcre3-dev, libgdbm-dev, libdb-dev, libgcrypt20-dev, python (>= 2.0), libssl-dev, bison (>= 1.2), flex (>= 2.5), libidn2-0-dev, libidn11-dev, mime-support, m4, gawk, procps
diff --git a/indimail-access/doc/ChangeLog b/indimail-access/doc/ChangeLog
index a9b199835..e26668a15 100644
--- a/indimail-access/doc/ChangeLog
+++ b/indimail-access/doc/ChangeLog
@@ -17,6 +17,9 @@ Release 1.1.1-1.1 Start 01/03/2021 End 01/04/2023
 46. upgraded courier-imap to 5.2.2
 47. upgraded maildrop to 3.1.1
 - 25/03/2023 - indimail-3.4.3
+- 23/04/2023
+48. upgraded courier-imap to 5.2.3
+49. upgraded maildrop to 3.1.4
 
 * Thu 08 Sep 2022 14:38:16 +0000 Manvendra Bhangui  1.1.0-1.1%{?dist}
 Release 1.1.0-1.1 Start 01/03/2021 End 08/09/2022

From 0764dfae06ffab397d4559a9683bba7388328930 Mon Sep 17 00:00:00 2001
From: Manvendra Bhangui 
Date: Sun, 23 Apr 2023 22:14:54 +0530
Subject: [PATCH 3/4] reverted rfc822/configure.ac

---
 courier-imap-x/libs/rfc822/configure.ac | 24 +++++++++++++++++++++---
 1 file changed, 21 insertions(+), 3 deletions(-)

diff --git a/courier-imap-x/libs/rfc822/configure.ac b/courier-imap-x/libs/rfc822/configure.ac
index 77f150178..57be735cf 100644
--- a/courier-imap-x/libs/rfc822/configure.ac
+++ b/courier-imap-x/libs/rfc822/configure.ac
@@ -37,12 +37,30 @@ AC_SYS_LARGEFILE
 
 dnl Checks for library functions.
 
-PKG_CHECK_MODULES(LIBIDN, libidn2 >= 2.0.4, [libidn=yes], [libidn=no])
+AC_ARG_WITH(libidn, AS_HELP_STRING([--with-libidn=[DIR]],[Support IDN (needs GNU libidn)]),
+	libidn=$withval, libidn=yes)
 
-if test "$libidn" = "no"
+if test "$libidn" = "yes"
 then
-	AC_MSG_ERROR(libidn2 not found)
+	PKG_CHECK_MODULES(LIBIDN2, libidn2 >= 0.0.0, [libidn=yes], [libidn=no])
+	if test "$libidn" != "yes"
+	then
+		PKG_CHECK_MODULES(LIBIDN1, libidn >= 0.0.0, [libidn=yes], [libidn=no])
+		if test "$libidn" != "yes"
+		then
+			libidn=no
+			AC_MSG_WARN([libidn not found])
+		else
+			libidn=yes
+			AC_DEFINE(LIBIDN1, 1, [Define to 1 if you want libidn.])
+		fi
+	else
+		libidn=yes
+		AC_DEFINE(LIBIDN2, 1, [Define to 1 if you want libidn.])
+	fi
 fi
+AC_MSG_CHECKING([if libidn should be used])
+AC_MSG_RESULT($libidn)
 
 AC_CHECK_FUNCS(setlocale)
 

From 082f086c04656cc00963398e62cefd4b16312993 Mon Sep 17 00:00:00 2001
From: Manvendra Bhangui 
Date: Sun, 23 Apr 2023 22:33:46 +0530
Subject: [PATCH 4/4] added -std=c++11 flag

---
 courier-imap-x/libs/maildrop/configure.ac | 29 +++++++++++++----------
 1 file changed, 17 insertions(+), 12 deletions(-)

diff --git a/courier-imap-x/libs/maildrop/configure.ac b/courier-imap-x/libs/maildrop/configure.ac
index 65be18c7e..16c8a61f6 100644
--- a/courier-imap-x/libs/maildrop/configure.ac
+++ b/courier-imap-x/libs/maildrop/configure.ac
@@ -3,11 +3,11 @@ dnl Copyright 1998 - 2022 Double Precision, Inc.  See COPYING for
 dnl distribution information.
 
 AC_INIT([maildrop],[3.1.4],[courier-users@lists.sourceforge.net])
+AC_CONFIG_MACRO_DIR([m4])
 
 >confdefs.h  # Kill PACKAGE_ macros
 
 AC_CONFIG_SRCDIR(alarm.C)
-AC_CONFIG_AUX_DIR(../..)
 LPATH="$PATH:/usr/local/bin"
 AM_INIT_AUTOMAKE([foreign no-define])
 
@@ -204,7 +204,6 @@ AC_DEFINE_UNQUOTED(MAXLONGSIZE, $MAXLONGSIZE,
 dnl Checks for library functions.
 
 AC_FUNC_CLOSEDIR_VOID
-
 AC_CHECK_FUNCS(setgroups setlocale)
 
 AC_CHECK_FUNC(getpgrp, HAS_GETPGRP=1, HAS_GETPGRP=0)
@@ -294,6 +293,14 @@ fi
 AC_DEFINE_UNQUOTED(DEFAULT_PATH,"$maildrop_cv_SYS_DEFAULT_PATH",
 	[ Default value of the PATH variable ])
 
+###	use option --enable-indimail to compile in the INDIMAIL support
+AC_ARG_ENABLE(indimail,
+	[  --enable-indimail            compile in INDIMAIL protocol support],
+	[with_INDIMAIL=$enableval],
+	[with_INDIMAIL=no])
+test "$with_INDIMAIL" = "yes" && AC_DEFINE(INDIMAIL,1,Define if you want INDIMAIL support compiled in)
+AM_CONDITIONAL(INDIMAIL, test "$with_INDIMAIL" = yes)
+
 if test -x /var/qmail/bin/qmail-inject
 then
 	QMAIL="/var/qmail/bin/qmail-inject"
@@ -462,9 +469,9 @@ done
 
 # Not found, possibly qmail $HOME/Mailbox
 
-if test "$QMAIL" != ""
+if test "$QMAIL" != "" -o "$with_INDIMAIL" != ""
 then
-	SPOOLDIR="./Mailbox"
+	SPOOLDIR="./Maildir/"
 else
 	AC_MSG_ERROR(Cannot determine default mailbox)
 fi
@@ -479,10 +486,10 @@ check_spooldir() {
   fi
 }
 
-AC_CACHE_CHECK(location of system mailboxes,maildrop_cv_SYS_INSTALL_MBOXDIR,
-check_spooldir
-maildrop_cv_SYS_INSTALL_MBOXDIR="$MBOX_DIR"
-)
+if test "$with_INDIMAIL" != "yes"
+then
+	AC_CACHE_CHECK(location of system mailboxes,maildrop_cv_SYS_INSTALL_MBOXDIR, check_spooldir maildrop_cv_SYS_INSTALL_MBOXDIR="$MBOX_DIR")
+fi
 
 AC_CACHE_CHECK(whether maildrop should reset its group ID,maildrop_cv_SYS_INSTALL_RESET_GID,
 check_spooldir
@@ -694,12 +701,10 @@ y*|Y*|1*)
 	;;
 esac
 
-
-
 AC_SUBST(VERSION)
 
-CFLAGS="$CFLAGS -I$srcdir/../rfc822 -I$srcdir/../rfc2045 -I.. -I$srcdir/.. -I ../.. -I$srcdir/../.."
-CXXFLAGS="$CXXFLAGS -I$srcdir/../rfc822 -I$srcdir/../rfc2045 -I.. -I$srcdir/.. -I../.. -I$srcdir/../.."
+CFLAGS="$CFLAGS -std=c99 -I$srcdir/../rfc822 -I$srcdir/../rfc2045 -I.. -I$srcdir/.. -I ../.. -I$srcdir/../.."
+CXXFLAGS="$CXXFLAGS -std=c++11 -I$srcdir/../rfc822 -I$srcdir/../rfc2045 -I.. -I$srcdir/.. -I../.. -I$srcdir/../.."
 
 AC_ARG_WITH(etcdir,    [  --with-etcdir=dir            Instead of /etc, use this. ],
 	[ withetcdir=$withval],