summaryrefslogtreecommitdiff
path: root/web/src/alpined.d
diff options
context:
space:
mode:
Diffstat (limited to 'web/src/alpined.d')
-rw-r--r--web/src/alpined.d/Makefile.am52
-rw-r--r--web/src/alpined.d/Makefile.in695
-rw-r--r--web/src/alpined.d/alpined.c16404
-rw-r--r--web/src/alpined.d/alpined.h49
-rw-r--r--web/src/alpined.d/alpineldap.c181
-rw-r--r--web/src/alpined.d/busy.c49
-rw-r--r--web/src/alpined.d/color.c678
-rw-r--r--web/src/alpined.d/color.h25
-rw-r--r--web/src/alpined.d/debug.c151
-rw-r--r--web/src/alpined.d/debug.h52
-rw-r--r--web/src/alpined.d/imap.c516
-rw-r--r--web/src/alpined.d/imap.h23
-rw-r--r--web/src/alpined.d/ldap.c241
-rw-r--r--web/src/alpined.d/ldap.h48
-rw-r--r--web/src/alpined.d/remote.c78
-rw-r--r--web/src/alpined.d/signal.c280
-rw-r--r--web/src/alpined.d/signal.h15
-rw-r--r--web/src/alpined.d/status.c78
-rw-r--r--web/src/alpined.d/stubs.c169
-rw-r--r--web/src/alpined.d/stubs.h25
-rw-r--r--web/src/alpined.d/wpcomm.c197
21 files changed, 20006 insertions, 0 deletions
diff --git a/web/src/alpined.d/Makefile.am b/web/src/alpined.d/Makefile.am
new file mode 100644
index 00000000..9101e4fb
--- /dev/null
+++ b/web/src/alpined.d/Makefile.am
@@ -0,0 +1,52 @@
+## Process this file with automake to produce Makefile.in
+## Use aclocal -I m4; automake
+
+# ========================================================================
+# Copyright 2006 University of Washington
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# ========================================================================
+
+# This is because alpined, libwpcomm and friends are
+# not intended for system-wide consumption
+locbindir = @abs_srcdir@/../../bin
+loclibdir = @abs_srcdir@/../../lib
+
+locbin_PROGRAMS = alpined alpineldap
+
+alpined_SOURCES = alpined.c busy.c color.c imap.c ldap.c remote.c \
+ signal.c debug.c status.c stubs.c \
+ alpined.h color.h ldap.h
+
+alpineldap_SOURCES = alpineldap.c busy.c color.c imap.c ldap.c remote.c \
+ signal.c debug.c status.c stubs.c \
+ alpined.h color.h ldap.h
+
+LDADD = local.o \
+ @top_srcdir@/pith/libpith.a \
+ @top_srcdir@/pith/osdep/libpithosd.a \
+ @top_srcdir@/pith/charconv/libpithcc.a \
+ @top_srcdir@/c-client/c-client.a \
+ $(WEB_PUBCOOKIE_LIB)
+
+loclib_LTLIBRARIES = libwpcomm.la
+
+libwpcomm_la_SOURCES = wpcomm.c
+
+libwpcomm_la_LDFLAGS = -rpath '$(loclibdir)' -version-info 1:0:0
+
+AM_CPPFLAGS = -I@top_builddir@/include -I@top_srcdir@/include
+
+AM_LDFLAGS = `cat @top_srcdir@/c-client/LDFLAGS`
+
+CLEANFILES = local.c
+
+local.c: alpineldap.c color.c imap.c ldap.c remote.c signal.c \
+ debug.c status.c stubs.c alpined.h color.h ldap.h
+ echo "char datestamp[]="\"`date`\"";" > local.c
+ echo "char hoststamp[]="\"`hostname`\"";" >> local.c
diff --git a/web/src/alpined.d/Makefile.in b/web/src/alpined.d/Makefile.in
new file mode 100644
index 00000000..644fd530
--- /dev/null
+++ b/web/src/alpined.d/Makefile.in
@@ -0,0 +1,695 @@
+# Makefile.in generated by automake 1.11.1 from Makefile.am.
+# @configure_input@
+
+# Copyright (C) 1994, 1995, 1996, 1997, 1998, 1999, 2000, 2001, 2002,
+# 2003, 2004, 2005, 2006, 2007, 2008, 2009 Free Software Foundation,
+# Inc.
+# This Makefile.in is free software; the Free Software Foundation
+# gives unlimited permission to copy and/or distribute it,
+# with or without modifications, as long as this notice is preserved.
+
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY, to the extent permitted by law; without
+# even the implied warranty of MERCHANTABILITY or FITNESS FOR A
+# PARTICULAR PURPOSE.
+
+@SET_MAKE@
+
+# ========================================================================
+# Copyright 2006 University of Washington
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# ========================================================================
+
+
+VPATH = @srcdir@
+pkgdatadir = $(datadir)/@PACKAGE@
+pkgincludedir = $(includedir)/@PACKAGE@
+pkglibdir = $(libdir)/@PACKAGE@
+pkglibexecdir = $(libexecdir)/@PACKAGE@
+am__cd = CDPATH="$${ZSH_VERSION+.}$(PATH_SEPARATOR)" && cd
+install_sh_DATA = $(install_sh) -c -m 644
+install_sh_PROGRAM = $(install_sh) -c
+install_sh_SCRIPT = $(install_sh) -c
+INSTALL_HEADER = $(INSTALL_DATA)
+transform = $(program_transform_name)
+NORMAL_INSTALL = :
+PRE_INSTALL = :
+POST_INSTALL = :
+NORMAL_UNINSTALL = :
+PRE_UNINSTALL = :
+POST_UNINSTALL = :
+build_triplet = @build@
+host_triplet = @host@
+locbin_PROGRAMS = alpined$(EXEEXT) alpineldap$(EXEEXT)
+subdir = web/src/alpined.d
+DIST_COMMON = $(srcdir)/Makefile.am $(srcdir)/Makefile.in
+ACLOCAL_M4 = $(top_srcdir)/aclocal.m4
+am__aclocal_m4_deps = $(top_srcdir)/m4/acx_pthread.m4 \
+ $(top_srcdir)/m4/gettext.m4 $(top_srcdir)/m4/iconv.m4 \
+ $(top_srcdir)/m4/lib-ld.m4 $(top_srcdir)/m4/lib-link.m4 \
+ $(top_srcdir)/m4/lib-prefix.m4 $(top_srcdir)/m4/libtool.m4 \
+ $(top_srcdir)/m4/ltoptions.m4 $(top_srcdir)/m4/ltsugar.m4 \
+ $(top_srcdir)/m4/ltversion.m4 $(top_srcdir)/m4/lt~obsolete.m4 \
+ $(top_srcdir)/m4/nls.m4 $(top_srcdir)/m4/po.m4 \
+ $(top_srcdir)/m4/progtest.m4 $(top_srcdir)/VERSION \
+ $(top_srcdir)/configure.ac
+am__configure_deps = $(am__aclocal_m4_deps) $(CONFIGURE_DEPENDENCIES) \
+ $(ACLOCAL_M4)
+mkinstalldirs = $(SHELL) $(top_srcdir)/mkinstalldirs
+CONFIG_HEADER = $(top_builddir)/include/config.h
+CONFIG_CLEAN_FILES =
+CONFIG_CLEAN_VPATH_FILES =
+am__vpath_adj_setup = srcdirstrip=`echo "$(srcdir)" | sed 's|.|.|g'`;
+am__vpath_adj = case $$p in \
+ $(srcdir)/*) f=`echo "$$p" | sed "s|^$$srcdirstrip/||"`;; \
+ *) f=$$p;; \
+ esac;
+am__strip_dir = f=`echo $$p | sed -e 's|^.*/||'`;
+am__install_max = 40
+am__nobase_strip_setup = \
+ srcdirstrip=`echo "$(srcdir)" | sed 's/[].[^$$\\*|]/\\\\&/g'`
+am__nobase_strip = \
+ for p in $$list; do echo "$$p"; done | sed -e "s|$$srcdirstrip/||"
+am__nobase_list = $(am__nobase_strip_setup); \
+ for p in $$list; do echo "$$p $$p"; done | \
+ sed "s| $$srcdirstrip/| |;"' / .*\//!s/ .*/ ./; s,\( .*\)/[^/]*$$,\1,' | \
+ $(AWK) 'BEGIN { files["."] = "" } { files[$$2] = files[$$2] " " $$1; \
+ if (++n[$$2] == $(am__install_max)) \
+ { print $$2, files[$$2]; n[$$2] = 0; files[$$2] = "" } } \
+ END { for (dir in files) print dir, files[dir] }'
+am__base_list = \
+ sed '$$!N;$$!N;$$!N;$$!N;$$!N;$$!N;$$!N;s/\n/ /g' | \
+ sed '$$!N;$$!N;$$!N;$$!N;s/\n/ /g'
+am__installdirs = "$(DESTDIR)$(loclibdir)" "$(DESTDIR)$(locbindir)"
+LTLIBRARIES = $(loclib_LTLIBRARIES)
+libwpcomm_la_LIBADD =
+am_libwpcomm_la_OBJECTS = wpcomm.lo
+libwpcomm_la_OBJECTS = $(am_libwpcomm_la_OBJECTS)
+libwpcomm_la_LINK = $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) \
+ $(LIBTOOLFLAGS) --mode=link $(CCLD) $(AM_CFLAGS) $(CFLAGS) \
+ $(libwpcomm_la_LDFLAGS) $(LDFLAGS) -o $@
+PROGRAMS = $(locbin_PROGRAMS)
+am_alpined_OBJECTS = alpined.$(OBJEXT) busy.$(OBJEXT) color.$(OBJEXT) \
+ imap.$(OBJEXT) ldap.$(OBJEXT) remote.$(OBJEXT) \
+ signal.$(OBJEXT) debug.$(OBJEXT) status.$(OBJEXT) \
+ stubs.$(OBJEXT)
+alpined_OBJECTS = $(am_alpined_OBJECTS)
+alpined_LDADD = $(LDADD)
+am__DEPENDENCIES_1 =
+alpined_DEPENDENCIES = local.o @top_srcdir@/pith/libpith.a \
+ @top_srcdir@/pith/osdep/libpithosd.a \
+ @top_srcdir@/pith/charconv/libpithcc.a \
+ @top_srcdir@/c-client/c-client.a $(am__DEPENDENCIES_1)
+am_alpineldap_OBJECTS = alpineldap.$(OBJEXT) busy.$(OBJEXT) \
+ color.$(OBJEXT) imap.$(OBJEXT) ldap.$(OBJEXT) remote.$(OBJEXT) \
+ signal.$(OBJEXT) debug.$(OBJEXT) status.$(OBJEXT) \
+ stubs.$(OBJEXT)
+alpineldap_OBJECTS = $(am_alpineldap_OBJECTS)
+alpineldap_LDADD = $(LDADD)
+alpineldap_DEPENDENCIES = local.o @top_srcdir@/pith/libpith.a \
+ @top_srcdir@/pith/osdep/libpithosd.a \
+ @top_srcdir@/pith/charconv/libpithcc.a \
+ @top_srcdir@/c-client/c-client.a $(am__DEPENDENCIES_1)
+DEFAULT_INCLUDES =
+depcomp = $(SHELL) $(top_srcdir)/depcomp
+am__depfiles_maybe = depfiles
+am__mv = mv -f
+COMPILE = $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) \
+ $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS)
+LTCOMPILE = $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) \
+ --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) \
+ $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS)
+CCLD = $(CC)
+LINK = $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) \
+ --mode=link $(CCLD) $(AM_CFLAGS) $(CFLAGS) $(AM_LDFLAGS) \
+ $(LDFLAGS) -o $@
+SOURCES = $(libwpcomm_la_SOURCES) $(alpined_SOURCES) \
+ $(alpineldap_SOURCES)
+DIST_SOURCES = $(libwpcomm_la_SOURCES) $(alpined_SOURCES) \
+ $(alpineldap_SOURCES)
+ETAGS = etags
+CTAGS = ctags
+DISTFILES = $(DIST_COMMON) $(DIST_SOURCES) $(TEXINFOS) $(EXTRA_DIST)
+ACLOCAL = @ACLOCAL@
+AMTAR = @AMTAR@
+AM_CFLAGS = @AM_CFLAGS@
+AM_LDFLAGS = `cat @top_srcdir@/c-client/LDFLAGS`
+AR = @AR@
+AUTOCONF = @AUTOCONF@
+AUTOHEADER = @AUTOHEADER@
+AUTOMAKE = @AUTOMAKE@
+AWK = @AWK@
+CC = @CC@
+CCDEPMODE = @CCDEPMODE@
+CFLAGS = @CFLAGS@
+CP = @CP@
+CPP = @CPP@
+CPPFLAGS = @CPPFLAGS@
+CYGPATH_W = @CYGPATH_W@
+C_CLIENT_CFLAGS = @C_CLIENT_CFLAGS@
+C_CLIENT_GCCOPTLEVEL = @C_CLIENT_GCCOPTLEVEL@
+C_CLIENT_LDFLAGS = @C_CLIENT_LDFLAGS@
+C_CLIENT_SPECIALS = @C_CLIENT_SPECIALS@
+C_CLIENT_TARGET = @C_CLIENT_TARGET@
+C_CLIENT_WITH_IPV6 = @C_CLIENT_WITH_IPV6@
+DEFS = @DEFS@
+DEPDIR = @DEPDIR@
+DSYMUTIL = @DSYMUTIL@
+DUMPBIN = @DUMPBIN@
+ECHO_C = @ECHO_C@
+ECHO_N = @ECHO_N@
+ECHO_T = @ECHO_T@
+EGREP = @EGREP@
+EXEEXT = @EXEEXT@
+FGREP = @FGREP@
+GMSGFMT = @GMSGFMT@
+GMSGFMT_015 = @GMSGFMT_015@
+GREP = @GREP@
+INSTALL = @INSTALL@
+INSTALL_DATA = @INSTALL_DATA@
+INSTALL_PROGRAM = @INSTALL_PROGRAM@
+INSTALL_SCRIPT = @INSTALL_SCRIPT@
+INSTALL_STRIP_PROGRAM = @INSTALL_STRIP_PROGRAM@
+INTLLIBS = @INTLLIBS@
+INTL_MACOSX_LIBS = @INTL_MACOSX_LIBS@
+ISPELLPROG = @ISPELLPROG@
+LD = @LD@
+LDFLAGS = @LDFLAGS@
+LIBICONV = @LIBICONV@
+LIBINTL = @LIBINTL@
+LIBOBJS = @LIBOBJS@
+LIBS = @LIBS@
+LIBTOOL = @LIBTOOL@
+LIPO = @LIPO@
+LN = @LN@
+LN_S = @LN_S@
+LTLIBICONV = @LTLIBICONV@
+LTLIBINTL = @LTLIBINTL@
+LTLIBOBJS = @LTLIBOBJS@
+MAINT = @MAINT@
+MAKE = @MAKE@
+MAKEINFO = @MAKEINFO@
+MKDIR_P = @MKDIR_P@
+MSGFMT = @MSGFMT@
+MSGFMT_015 = @MSGFMT_015@
+MSGMERGE = @MSGMERGE@
+NM = @NM@
+NMEDIT = @NMEDIT@
+NPA_PROG = @NPA_PROG@
+OBJDUMP = @OBJDUMP@
+OBJEXT = @OBJEXT@
+OTOOL = @OTOOL@
+OTOOL64 = @OTOOL64@
+PACKAGE = @PACKAGE@
+PACKAGE_BUGREPORT = @PACKAGE_BUGREPORT@
+PACKAGE_NAME = @PACKAGE_NAME@
+PACKAGE_STRING = @PACKAGE_STRING@
+PACKAGE_TARNAME = @PACKAGE_TARNAME@
+PACKAGE_URL = @PACKAGE_URL@
+PACKAGE_VERSION = @PACKAGE_VERSION@
+PATH_SEPARATOR = @PATH_SEPARATOR@
+POSUB = @POSUB@
+PTHREAD_CC = @PTHREAD_CC@
+PTHREAD_CFLAGS = @PTHREAD_CFLAGS@
+PTHREAD_LIBS = @PTHREAD_LIBS@
+PWPROG = @PWPROG@
+RANLIB = @RANLIB@
+REGEX_BUILD = @REGEX_BUILD@
+RM = @RM@
+SED = @SED@
+SENDMAIL = @SENDMAIL@
+SET_MAKE = @SET_MAKE@
+SHELL = @SHELL@
+SPELLPROG = @SPELLPROG@
+STRIP = @STRIP@
+USE_NLS = @USE_NLS@
+VERSION = @VERSION@
+WEB_BINDIR = @WEB_BINDIR@
+WEB_BUILD = @WEB_BUILD@
+WEB_PUBCOOKIE_BUILD = @WEB_PUBCOOKIE_BUILD@
+WEB_PUBCOOKIE_LIB = @WEB_PUBCOOKIE_LIB@
+WEB_PUBCOOKIE_LINK = @WEB_PUBCOOKIE_LINK@
+XGETTEXT = @XGETTEXT@
+XGETTEXT_015 = @XGETTEXT_015@
+abs_builddir = @abs_builddir@
+abs_srcdir = @abs_srcdir@
+abs_top_builddir = @abs_top_builddir@
+abs_top_srcdir = @abs_top_srcdir@
+ac_ct_CC = @ac_ct_CC@
+ac_ct_DUMPBIN = @ac_ct_DUMPBIN@
+acx_pthread_config = @acx_pthread_config@
+alpine_interactive_spellcheck = @alpine_interactive_spellcheck@
+alpine_simple_spellcheck = @alpine_simple_spellcheck@
+am__include = @am__include@
+am__leading_dot = @am__leading_dot@
+am__quote = @am__quote@
+am__tar = @am__tar@
+am__untar = @am__untar@
+bindir = @bindir@
+build = @build@
+build_alias = @build_alias@
+build_cpu = @build_cpu@
+build_os = @build_os@
+build_vendor = @build_vendor@
+builddir = @builddir@
+datadir = @datadir@
+datarootdir = @datarootdir@
+docdir = @docdir@
+dvidir = @dvidir@
+exec_prefix = @exec_prefix@
+host = @host@
+host_alias = @host_alias@
+host_cpu = @host_cpu@
+host_os = @host_os@
+host_vendor = @host_vendor@
+htmldir = @htmldir@
+includedir = @includedir@
+infodir = @infodir@
+install_sh = @install_sh@
+libdir = @libdir@
+libexecdir = @libexecdir@
+localedir = @localedir@
+localstatedir = @localstatedir@
+lt_ECHO = @lt_ECHO@
+mandir = @mandir@
+mkdir_p = @mkdir_p@
+oldincludedir = @oldincludedir@
+pdfdir = @pdfdir@
+prefix = @prefix@
+program_transform_name = @program_transform_name@
+psdir = @psdir@
+sbindir = @sbindir@
+sharedstatedir = @sharedstatedir@
+srcdir = @srcdir@
+sysconfdir = @sysconfdir@
+target_alias = @target_alias@
+top_build_prefix = @top_build_prefix@
+top_builddir = @top_builddir@
+top_srcdir = @top_srcdir@
+
+# This is because alpined, libwpcomm and friends are
+# not intended for system-wide consumption
+locbindir = @abs_srcdir@/../../bin
+loclibdir = @abs_srcdir@/../../lib
+alpined_SOURCES = alpined.c busy.c color.c imap.c ldap.c remote.c \
+ signal.c debug.c status.c stubs.c \
+ alpined.h color.h ldap.h
+
+alpineldap_SOURCES = alpineldap.c busy.c color.c imap.c ldap.c remote.c \
+ signal.c debug.c status.c stubs.c \
+ alpined.h color.h ldap.h
+
+LDADD = local.o \
+ @top_srcdir@/pith/libpith.a \
+ @top_srcdir@/pith/osdep/libpithosd.a \
+ @top_srcdir@/pith/charconv/libpithcc.a \
+ @top_srcdir@/c-client/c-client.a \
+ $(WEB_PUBCOOKIE_LIB)
+
+loclib_LTLIBRARIES = libwpcomm.la
+libwpcomm_la_SOURCES = wpcomm.c
+libwpcomm_la_LDFLAGS = -rpath '$(loclibdir)' -version-info 1:0:0
+AM_CPPFLAGS = -I@top_builddir@/include -I@top_srcdir@/include
+CLEANFILES = local.c
+all: all-am
+
+.SUFFIXES:
+.SUFFIXES: .c .lo .o .obj
+$(srcdir)/Makefile.in: @MAINTAINER_MODE_TRUE@ $(srcdir)/Makefile.am $(am__configure_deps)
+ @for dep in $?; do \
+ case '$(am__configure_deps)' in \
+ *$$dep*) \
+ ( cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh ) \
+ && { if test -f $@; then exit 0; else break; fi; }; \
+ exit 1;; \
+ esac; \
+ done; \
+ echo ' cd $(top_srcdir) && $(AUTOMAKE) --foreign web/src/alpined.d/Makefile'; \
+ $(am__cd) $(top_srcdir) && \
+ $(AUTOMAKE) --foreign web/src/alpined.d/Makefile
+.PRECIOUS: Makefile
+Makefile: $(srcdir)/Makefile.in $(top_builddir)/config.status
+ @case '$?' in \
+ *config.status*) \
+ cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh;; \
+ *) \
+ echo ' cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__depfiles_maybe)'; \
+ cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__depfiles_maybe);; \
+ esac;
+
+$(top_builddir)/config.status: $(top_srcdir)/configure $(CONFIG_STATUS_DEPENDENCIES)
+ cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh
+
+$(top_srcdir)/configure: @MAINTAINER_MODE_TRUE@ $(am__configure_deps)
+ cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh
+$(ACLOCAL_M4): @MAINTAINER_MODE_TRUE@ $(am__aclocal_m4_deps)
+ cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh
+$(am__aclocal_m4_deps):
+install-loclibLTLIBRARIES: $(loclib_LTLIBRARIES)
+ @$(NORMAL_INSTALL)
+ test -z "$(loclibdir)" || $(MKDIR_P) "$(DESTDIR)$(loclibdir)"
+ @list='$(loclib_LTLIBRARIES)'; test -n "$(loclibdir)" || list=; \
+ list2=; for p in $$list; do \
+ if test -f $$p; then \
+ list2="$$list2 $$p"; \
+ else :; fi; \
+ done; \
+ test -z "$$list2" || { \
+ echo " $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=install $(INSTALL) $(INSTALL_STRIP_FLAG) $$list2 '$(DESTDIR)$(loclibdir)'"; \
+ $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=install $(INSTALL) $(INSTALL_STRIP_FLAG) $$list2 "$(DESTDIR)$(loclibdir)"; \
+ }
+
+uninstall-loclibLTLIBRARIES:
+ @$(NORMAL_UNINSTALL)
+ @list='$(loclib_LTLIBRARIES)'; test -n "$(loclibdir)" || list=; \
+ for p in $$list; do \
+ $(am__strip_dir) \
+ echo " $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=uninstall rm -f '$(DESTDIR)$(loclibdir)/$$f'"; \
+ $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=uninstall rm -f "$(DESTDIR)$(loclibdir)/$$f"; \
+ done
+
+clean-loclibLTLIBRARIES:
+ -test -z "$(loclib_LTLIBRARIES)" || rm -f $(loclib_LTLIBRARIES)
+ @list='$(loclib_LTLIBRARIES)'; for p in $$list; do \
+ dir="`echo $$p | sed -e 's|/[^/]*$$||'`"; \
+ test "$$dir" != "$$p" || dir=.; \
+ echo "rm -f \"$${dir}/so_locations\""; \
+ rm -f "$${dir}/so_locations"; \
+ done
+libwpcomm.la: $(libwpcomm_la_OBJECTS) $(libwpcomm_la_DEPENDENCIES)
+ $(libwpcomm_la_LINK) -rpath $(loclibdir) $(libwpcomm_la_OBJECTS) $(libwpcomm_la_LIBADD) $(LIBS)
+install-locbinPROGRAMS: $(locbin_PROGRAMS)
+ @$(NORMAL_INSTALL)
+ test -z "$(locbindir)" || $(MKDIR_P) "$(DESTDIR)$(locbindir)"
+ @list='$(locbin_PROGRAMS)'; test -n "$(locbindir)" || list=; \
+ for p in $$list; do echo "$$p $$p"; done | \
+ sed 's/$(EXEEXT)$$//' | \
+ while read p p1; do if test -f $$p || test -f $$p1; \
+ then echo "$$p"; echo "$$p"; else :; fi; \
+ done | \
+ sed -e 'p;s,.*/,,;n;h' -e 's|.*|.|' \
+ -e 'p;x;s,.*/,,;s/$(EXEEXT)$$//;$(transform);s/$$/$(EXEEXT)/' | \
+ sed 'N;N;N;s,\n, ,g' | \
+ $(AWK) 'BEGIN { files["."] = ""; dirs["."] = 1 } \
+ { d=$$3; if (dirs[d] != 1) { print "d", d; dirs[d] = 1 } \
+ if ($$2 == $$4) files[d] = files[d] " " $$1; \
+ else { print "f", $$3 "/" $$4, $$1; } } \
+ END { for (d in files) print "f", d, files[d] }' | \
+ while read type dir files; do \
+ if test "$$dir" = .; then dir=; else dir=/$$dir; fi; \
+ test -z "$$files" || { \
+ echo " $(INSTALL_PROGRAM_ENV) $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=install $(INSTALL_PROGRAM) $$files '$(DESTDIR)$(locbindir)$$dir'"; \
+ $(INSTALL_PROGRAM_ENV) $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=install $(INSTALL_PROGRAM) $$files "$(DESTDIR)$(locbindir)$$dir" || exit $$?; \
+ } \
+ ; done
+
+uninstall-locbinPROGRAMS:
+ @$(NORMAL_UNINSTALL)
+ @list='$(locbin_PROGRAMS)'; test -n "$(locbindir)" || list=; \
+ files=`for p in $$list; do echo "$$p"; done | \
+ sed -e 'h;s,^.*/,,;s/$(EXEEXT)$$//;$(transform)' \
+ -e 's/$$/$(EXEEXT)/' `; \
+ test -n "$$list" || exit 0; \
+ echo " ( cd '$(DESTDIR)$(locbindir)' && rm -f" $$files ")"; \
+ cd "$(DESTDIR)$(locbindir)" && rm -f $$files
+
+clean-locbinPROGRAMS:
+ @list='$(locbin_PROGRAMS)'; test -n "$$list" || exit 0; \
+ echo " rm -f" $$list; \
+ rm -f $$list || exit $$?; \
+ test -n "$(EXEEXT)" || exit 0; \
+ list=`for p in $$list; do echo "$$p"; done | sed 's/$(EXEEXT)$$//'`; \
+ echo " rm -f" $$list; \
+ rm -f $$list
+alpined$(EXEEXT): $(alpined_OBJECTS) $(alpined_DEPENDENCIES)
+ @rm -f alpined$(EXEEXT)
+ $(LINK) $(alpined_OBJECTS) $(alpined_LDADD) $(LIBS)
+alpineldap$(EXEEXT): $(alpineldap_OBJECTS) $(alpineldap_DEPENDENCIES)
+ @rm -f alpineldap$(EXEEXT)
+ $(LINK) $(alpineldap_OBJECTS) $(alpineldap_LDADD) $(LIBS)
+
+mostlyclean-compile:
+ -rm -f *.$(OBJEXT)
+
+distclean-compile:
+ -rm -f *.tab.c
+
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/alpined.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/alpineldap.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/busy.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/color.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/debug.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/imap.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/ldap.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/remote.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/signal.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/status.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/stubs.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/wpcomm.Plo@am__quote@
+
+.c.o:
+@am__fastdepCC_TRUE@ $(COMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ $<
+@am__fastdepCC_TRUE@ $(am__mv) $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='$<' object='$@' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(COMPILE) -c $<
+
+.c.obj:
+@am__fastdepCC_TRUE@ $(COMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ `$(CYGPATH_W) '$<'`
+@am__fastdepCC_TRUE@ $(am__mv) $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='$<' object='$@' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(COMPILE) -c `$(CYGPATH_W) '$<'`
+
+.c.lo:
+@am__fastdepCC_TRUE@ $(LTCOMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ $<
+@am__fastdepCC_TRUE@ $(am__mv) $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='$<' object='$@' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(LTCOMPILE) -c -o $@ $<
+
+mostlyclean-libtool:
+ -rm -f *.lo
+
+clean-libtool:
+ -rm -rf .libs _libs
+
+ID: $(HEADERS) $(SOURCES) $(LISP) $(TAGS_FILES)
+ list='$(SOURCES) $(HEADERS) $(LISP) $(TAGS_FILES)'; \
+ unique=`for i in $$list; do \
+ if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \
+ done | \
+ $(AWK) '{ files[$$0] = 1; nonempty = 1; } \
+ END { if (nonempty) { for (i in files) print i; }; }'`; \
+ mkid -fID $$unique
+tags: TAGS
+
+TAGS: $(HEADERS) $(SOURCES) $(TAGS_DEPENDENCIES) \
+ $(TAGS_FILES) $(LISP)
+ set x; \
+ here=`pwd`; \
+ list='$(SOURCES) $(HEADERS) $(LISP) $(TAGS_FILES)'; \
+ unique=`for i in $$list; do \
+ if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \
+ done | \
+ $(AWK) '{ files[$$0] = 1; nonempty = 1; } \
+ END { if (nonempty) { for (i in files) print i; }; }'`; \
+ shift; \
+ if test -z "$(ETAGS_ARGS)$$*$$unique"; then :; else \
+ test -n "$$unique" || unique=$$empty_fix; \
+ if test $$# -gt 0; then \
+ $(ETAGS) $(ETAGSFLAGS) $(AM_ETAGSFLAGS) $(ETAGS_ARGS) \
+ "$$@" $$unique; \
+ else \
+ $(ETAGS) $(ETAGSFLAGS) $(AM_ETAGSFLAGS) $(ETAGS_ARGS) \
+ $$unique; \
+ fi; \
+ fi
+ctags: CTAGS
+CTAGS: $(HEADERS) $(SOURCES) $(TAGS_DEPENDENCIES) \
+ $(TAGS_FILES) $(LISP)
+ list='$(SOURCES) $(HEADERS) $(LISP) $(TAGS_FILES)'; \
+ unique=`for i in $$list; do \
+ if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \
+ done | \
+ $(AWK) '{ files[$$0] = 1; nonempty = 1; } \
+ END { if (nonempty) { for (i in files) print i; }; }'`; \
+ test -z "$(CTAGS_ARGS)$$unique" \
+ || $(CTAGS) $(CTAGSFLAGS) $(AM_CTAGSFLAGS) $(CTAGS_ARGS) \
+ $$unique
+
+GTAGS:
+ here=`$(am__cd) $(top_builddir) && pwd` \
+ && $(am__cd) $(top_srcdir) \
+ && gtags -i $(GTAGS_ARGS) "$$here"
+
+distclean-tags:
+ -rm -f TAGS ID GTAGS GRTAGS GSYMS GPATH tags
+
+distdir: $(DISTFILES)
+ @srcdirstrip=`echo "$(srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \
+ topsrcdirstrip=`echo "$(top_srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \
+ list='$(DISTFILES)'; \
+ dist_files=`for file in $$list; do echo $$file; done | \
+ sed -e "s|^$$srcdirstrip/||;t" \
+ -e "s|^$$topsrcdirstrip/|$(top_builddir)/|;t"`; \
+ case $$dist_files in \
+ */*) $(MKDIR_P) `echo "$$dist_files" | \
+ sed '/\//!d;s|^|$(distdir)/|;s,/[^/]*$$,,' | \
+ sort -u` ;; \
+ esac; \
+ for file in $$dist_files; do \
+ if test -f $$file || test -d $$file; then d=.; else d=$(srcdir); fi; \
+ if test -d $$d/$$file; then \
+ dir=`echo "/$$file" | sed -e 's,/[^/]*$$,,'`; \
+ if test -d "$(distdir)/$$file"; then \
+ find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \
+ fi; \
+ if test -d $(srcdir)/$$file && test $$d != $(srcdir); then \
+ cp -fpR $(srcdir)/$$file "$(distdir)$$dir" || exit 1; \
+ find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \
+ fi; \
+ cp -fpR $$d/$$file "$(distdir)$$dir" || exit 1; \
+ else \
+ test -f "$(distdir)/$$file" \
+ || cp -p $$d/$$file "$(distdir)/$$file" \
+ || exit 1; \
+ fi; \
+ done
+check-am: all-am
+check: check-am
+all-am: Makefile $(LTLIBRARIES) $(PROGRAMS)
+installdirs:
+ for dir in "$(DESTDIR)$(loclibdir)" "$(DESTDIR)$(locbindir)"; do \
+ test -z "$$dir" || $(MKDIR_P) "$$dir"; \
+ done
+install: install-am
+install-exec: install-exec-am
+install-data: install-data-am
+uninstall: uninstall-am
+
+install-am: all-am
+ @$(MAKE) $(AM_MAKEFLAGS) install-exec-am install-data-am
+
+installcheck: installcheck-am
+install-strip:
+ $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \
+ install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \
+ `test -z '$(STRIP)' || \
+ echo "INSTALL_PROGRAM_ENV=STRIPPROG='$(STRIP)'"` install
+mostlyclean-generic:
+
+clean-generic:
+ -test -z "$(CLEANFILES)" || rm -f $(CLEANFILES)
+
+distclean-generic:
+ -test -z "$(CONFIG_CLEAN_FILES)" || rm -f $(CONFIG_CLEAN_FILES)
+ -test . = "$(srcdir)" || test -z "$(CONFIG_CLEAN_VPATH_FILES)" || rm -f $(CONFIG_CLEAN_VPATH_FILES)
+
+maintainer-clean-generic:
+ @echo "This command is intended for maintainers to use"
+ @echo "it deletes files that may require special tools to rebuild."
+clean: clean-am
+
+clean-am: clean-generic clean-libtool clean-locbinPROGRAMS \
+ clean-loclibLTLIBRARIES mostlyclean-am
+
+distclean: distclean-am
+ -rm -rf ./$(DEPDIR)
+ -rm -f Makefile
+distclean-am: clean-am distclean-compile distclean-generic \
+ distclean-tags
+
+dvi: dvi-am
+
+dvi-am:
+
+html: html-am
+
+html-am:
+
+info: info-am
+
+info-am:
+
+install-data-am: install-locbinPROGRAMS install-loclibLTLIBRARIES
+
+install-dvi: install-dvi-am
+
+install-dvi-am:
+
+install-exec-am:
+
+install-html: install-html-am
+
+install-html-am:
+
+install-info: install-info-am
+
+install-info-am:
+
+install-man:
+
+install-pdf: install-pdf-am
+
+install-pdf-am:
+
+install-ps: install-ps-am
+
+install-ps-am:
+
+installcheck-am:
+
+maintainer-clean: maintainer-clean-am
+ -rm -rf ./$(DEPDIR)
+ -rm -f Makefile
+maintainer-clean-am: distclean-am maintainer-clean-generic
+
+mostlyclean: mostlyclean-am
+
+mostlyclean-am: mostlyclean-compile mostlyclean-generic \
+ mostlyclean-libtool
+
+pdf: pdf-am
+
+pdf-am:
+
+ps: ps-am
+
+ps-am:
+
+uninstall-am: uninstall-locbinPROGRAMS uninstall-loclibLTLIBRARIES
+
+.MAKE: install-am install-strip
+
+.PHONY: CTAGS GTAGS all all-am check check-am clean clean-generic \
+ clean-libtool clean-locbinPROGRAMS clean-loclibLTLIBRARIES \
+ ctags distclean distclean-compile distclean-generic \
+ distclean-libtool distclean-tags distdir dvi dvi-am html \
+ html-am info info-am install install-am install-data \
+ install-data-am install-dvi install-dvi-am install-exec \
+ install-exec-am install-html install-html-am install-info \
+ install-info-am install-locbinPROGRAMS \
+ install-loclibLTLIBRARIES install-man install-pdf \
+ install-pdf-am install-ps install-ps-am install-strip \
+ installcheck installcheck-am installdirs maintainer-clean \
+ maintainer-clean-generic mostlyclean mostlyclean-compile \
+ mostlyclean-generic mostlyclean-libtool pdf pdf-am ps ps-am \
+ tags uninstall uninstall-am uninstall-locbinPROGRAMS \
+ uninstall-loclibLTLIBRARIES
+
+
+local.c: alpineldap.c color.c imap.c ldap.c remote.c signal.c \
+ debug.c status.c stubs.c alpined.h color.h ldap.h
+ echo "char datestamp[]="\"`date`\"";" > local.c
+ echo "char hoststamp[]="\"`hostname`\"";" >> local.c
+
+# Tell versions [3.59,3.63) of GNU make to not export all variables.
+# Otherwise a system limit (for SysV at least) may be exceeded.
+.NOEXPORT:
diff --git a/web/src/alpined.d/alpined.c b/web/src/alpined.d/alpined.c
new file mode 100644
index 00000000..e35ba9e6
--- /dev/null
+++ b/web/src/alpined.d/alpined.c
@@ -0,0 +1,16404 @@
+#if !defined(lint) && !defined(DOS)
+static char rcsid[] = "$Id: alpined.c 1266 2009-07-14 18:39:12Z hubert@u.washington.edu $";
+#endif
+
+/* ========================================================================
+ * Copyright 2006-2008 University of Washington
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * ========================================================================
+ */
+
+/* ========================================================================
+ Implement alpine TCL interfaces. Execute TCL interfaces
+ via interpreter reading commands and writing results over
+ UNIX domain socket.
+ ======================================================================== */
+
+
+#include <system.h>
+#include <general.h>
+
+#include "../../../c-client/c-client.h"
+#include "../../../c-client/imap4r1.h"
+
+#include "../../../pith/osdep/color.h" /* color support library */
+#include "../../../pith/osdep/canaccess.h"
+#include "../../../pith/osdep/temp_nam.h"
+#include "../../../pith/osdep/collate.h"
+#include "../../../pith/osdep/filesize.h"
+#include "../../../pith/osdep/writ_dir.h"
+#include "../../../pith/osdep/err_desc.h"
+
+#include "../../../pith/stream.h"
+#include "../../../pith/context.h"
+#include "../../../pith/state.h"
+#include "../../../pith/msgno.h"
+#include "../../../pith/debug.h"
+#include "../../../pith/init.h"
+#include "../../../pith/conf.h"
+#include "../../../pith/conftype.h"
+#include "../../../pith/detoken.h"
+#include "../../../pith/flag.h"
+#include "../../../pith/help.h"
+#include "../../../pith/remote.h"
+#include "../../../pith/status.h"
+#include "../../../pith/mailcmd.h"
+#include "../../../pith/savetype.h"
+#include "../../../pith/save.h"
+#include "../../../pith/reply.h"
+#include "../../../pith/sort.h"
+#include "../../../pith/ldap.h"
+#include "../../../pith/addrbook.h"
+#include "../../../pith/ablookup.h"
+#include "../../../pith/takeaddr.h"
+#include "../../../pith/bldaddr.h"
+#include "../../../pith/copyaddr.h"
+#include "../../../pith/thread.h"
+#include "../../../pith/folder.h"
+#include "../../../pith/mailview.h"
+#include "../../../pith/indxtype.h"
+#include "../../../pith/icache.h"
+#include "../../../pith/mailindx.h"
+#include "../../../pith/mailpart.h"
+#include "../../../pith/mimedesc.h"
+#include "../../../pith/detach.h"
+#include "../../../pith/newmail.h"
+#include "../../../pith/charset.h"
+#include "../../../pith/util.h"
+#include "../../../pith/rfc2231.h"
+#include "../../../pith/string.h"
+#include "../../../pith/send.h"
+#include "../../../pith/options.h"
+#include "../../../pith/list.h"
+#include "../../../pith/mimetype.h"
+#include "../../../pith/mailcap.h"
+#include "../../../pith/sequence.h"
+#include "../../../pith/smime.h"
+#include "../../../pith/url.h"
+#include "../../../pith/charconv/utf8.h"
+
+#include "alpined.h"
+#include "color.h"
+#include "imap.h"
+#include "ldap.h"
+#include "debug.h"
+#include "stubs.h"
+
+#include <tcl.h>
+
+
+/*
+ * Fake screen dimension for word wrap and such
+ */
+#define FAKE_SCREEN_WIDTH 80
+#define FAKE_SCREEN_LENGTH 24
+
+/*
+ * Aribtrary minimum display width (in characters)
+ */
+#define MIN_SCREEN_COLS 20
+
+
+/*
+ * Maximum number of lines allowed in signatures
+ */
+#define SIG_MAX_LINES 24
+#define SIG_MAX_COLS 1024
+
+
+/*
+ * Number of seconds we'll wait before we assume the client has wondered
+ * on to more interesting content
+ */
+#define PE_INPUT_TIMEOUT 1800
+
+
+/*
+ * Posting error lenght max
+ */
+#define WP_MAX_POST_ERROR 128
+
+
+/*
+ * AUTH Response Tokens
+ */
+#define AUTH_EMPTY_STRING "NOPASSWD"
+#define AUTH_FAILURE_STRING "BADPASSWD"
+
+/*
+ * CERT Response Tokens
+ */
+#define CERT_QUERY_STRING "CERTQUERY"
+#define CERT_FAILURE_STRING "CERTFAIL"
+
+
+/*
+ * Charset used within alpined and to communicate with alpined
+ * Note: posting-charset still respected
+ */
+#define WP_INTERNAL_CHARSET "UTF-8"
+
+
+/*
+ * Globals referenced throughout pine...
+ */
+struct pine *ps_global; /* THE global variable! */
+
+
+/*
+ * More global state
+ */
+long gPeITop, gPeICount;
+
+long gPeInputTimeout = PE_INPUT_TIMEOUT;
+long gPEAbandonTimeout = 0;
+
+
+/*
+ * Authorization issues
+ */
+int peNoPassword, peCredentialError;
+int peCertFailure, peCertQuery;
+char peCredentialRequestor[CRED_REQ_SIZE];
+
+char *peSocketName;
+
+char **peTSig;
+
+CONTEXT_S *config_context_list;
+
+STRLIST_S *peCertHosts;
+
+bitmap_t changed_feature_list;
+#define F_CH_ON(feature) (bitnset((feature),changed_feature_list))
+#define F_CH_OFF(feature) (!F_CH_ON(feature))
+#define F_CH_TURN_ON(feature) (setbitn((feature),changed_feature_list))
+#define F_CH_TURN_OFF(feature) (clrbitn((feature),changed_feature_list))
+#define F_CH_SET(feature,value) ((value) ? F_CH_TURN_ON((feature)) \
+ : F_CH_TURN_OFF((feature)))
+
+
+typedef struct _status_msg {
+ time_t posted;
+ unsigned type:3;
+ unsigned seen:1;
+ long id;
+ char *text;
+ struct _status_msg *next;
+} STATMSG_S;
+
+static STATMSG_S *peStatList;
+
+typedef struct _composer_attachment {
+ unsigned file:1;
+ unsigned body:1;
+ char *id;
+ union {
+ struct {
+ char *local;
+ char *remote;
+ char *type;
+ char *subtype;
+ char *description;
+ long size;
+ } f;
+ struct {
+ BODY *body;
+ } b;
+ struct {
+ long msgno;
+ char *part;
+ } msg;
+ } l;
+ struct _composer_attachment *next;
+} COMPATT_S;
+
+static COMPATT_S *peCompAttach;
+
+/*
+ * Holds data passed
+ */
+typedef struct _msg_data {
+ ENVELOPE *outgoing;
+ METAENV *metaenv;
+ PINEFIELD *custom;
+ STORE_S *msgtext;
+ STRLIST_S *attach;
+ char *fcc;
+ int fcc_colid;
+ int postop_fcc_no_attach;
+ char *charset;
+ char *priority;
+ int (*postfunc)(METAENV *, BODY *, char *, CONTEXT_S **, char *);
+ unsigned flowed:1;
+ unsigned html:1;
+ unsigned qualified_addrs:1;
+} MSG_COL_S;
+
+
+/*
+ * locally global structure to keep track of various bits of state
+ * needed to collect filtered output
+ */
+static struct _embedded_data {
+ Tcl_Interp *interp;
+ Tcl_Obj *obj;
+ STORE_S *store;
+ long uid;
+ HANDLE_S *handles;
+ char inhandle;
+ ENVELOPE *env;
+ BODY *body;
+ struct {
+ char fg[7];
+ char bg[7];
+ char fgdef[7];
+ char bgdef[7];
+ } color;
+} peED;
+
+
+/*
+ * RSS stream cache
+ */
+typedef struct _rss_cache_s {
+ char *link;
+ time_t stale;
+ int referenced;
+ RSS_FEED_S *feed;
+} RSS_CACHE_S;
+
+#define RSS_NEWS_CACHE_SIZE 1
+#define RSS_WEATHER_CACHE_SIZE 1
+
+
+#ifdef ENABLE_LDAP
+WPLDAP_S *wpldap_global;
+#endif
+
+/*
+ * random string generator flags
+ */
+#define PRS_NONE 0x0000
+#define PRS_LOWER_CASE 0x0001
+#define PRS_UPPER_CASE 0x0002
+#define PRS_MIXED_CASE 0x0004
+
+/*
+ * peSaveWork flag definitions
+ */
+#define PSW_NONE 0x00
+#define PSW_COPY 0x01
+#define PSW_MOVE 0x02
+
+/*
+ * Message Collector flags
+ */
+#define PMC_NONE 0x00
+#define PMC_FORCE_QUAL 0x01
+#define PMC_PRSRV_ATT 0x02
+
+/*
+ * length of thread info string
+ */
+#define WP_MAX_THRD_S 64
+
+/*
+ * static buf size for putenv() if necessary
+ */
+#define PUTENV_MAX 64
+
+
+
+/*----------------------------------------------------------------------
+ General use big buffer. It is used in the following places:
+ compose_mail: while parsing header of postponed message
+ append_message2: while writing header into folder
+ q_status_messageX: while doing printf formatting
+ addr_book: Used to return expanded address in. (Can only use here
+ because mm_log doesn't q_status on PARSE errors !)
+ alpine.c: When address specified on command line
+ init.c: When expanding variable values
+ and many many more...
+
+ ----*/
+char tmp_20k_buf[20480];
+
+
+
+
+/* Internal prototypes */
+void peReturn(int, char *, char *);
+int peWrite(int, char *);
+char *peCreateUserContext(Tcl_Interp *, char *, char *, char *);
+void peDestroyUserContext(struct pine **);
+char *peLoadConfig(struct pine *);
+int peCreateStream(Tcl_Interp *, CONTEXT_S *, char *, int);
+void peDestroyStream(struct pine *);
+void pePrepareForAuthException(void);
+char *peAuthException(void);
+void peInitVars(struct pine *);
+int peSelect(Tcl_Interp *, int, Tcl_Obj **, int);
+int peSelectNumber(Tcl_Interp *, int, Tcl_Obj **, int);
+int peSelectDate(Tcl_Interp *, int, Tcl_Obj **, int);
+int peSelectText(Tcl_Interp *, int, Tcl_Obj **, int);
+int peSelectStatus(Tcl_Interp *, int, Tcl_Obj **, int);
+char *peSelValTense(Tcl_Obj *);
+char *peSelValYear(Tcl_Obj *);
+char *peSelValMonth(Tcl_Obj *);
+char *peSelValDay(Tcl_Obj *);
+int peSelValCase(Tcl_Obj *);
+int peSelValField(Tcl_Obj *);
+int peSelValFlag(Tcl_Obj *);
+int peSelected(Tcl_Interp *, int, Tcl_Obj **, int);
+int peSelectError(Tcl_Interp *, char *);
+int peApply(Tcl_Interp *, int, Tcl_Obj **);
+char *peApplyFlag(MAILSTREAM *, MSGNO_S *, char, int, long *);
+int peApplyError(Tcl_Interp *, char *);
+int peIndexFormat(Tcl_Interp *);
+int peAppendIndexParts(Tcl_Interp *, imapuid_t, Tcl_Obj *, int *);
+int peAppendIndexColor(Tcl_Interp *, imapuid_t, Tcl_Obj *, int *);
+int peMessageStatusBits(Tcl_Interp *, imapuid_t, int, Tcl_Obj **);
+char *peMsgStatBitString(struct pine *, MAILSTREAM *, MSGNO_S *, long, long, long, int *);
+Tcl_Obj *peMsgStatNameList(Tcl_Interp *, struct pine *, MAILSTREAM *, MSGNO_S *, long, long, long, int *);
+int peNewMailResult(Tcl_Interp *);
+int peMessageSize(Tcl_Interp *, imapuid_t, int, Tcl_Obj **);
+int peMessageDate(Tcl_Interp *, imapuid_t, int, Tcl_Obj **);
+int peMessageSubject(Tcl_Interp *, imapuid_t, int, Tcl_Obj **);
+int peMessageFromAddr(Tcl_Interp *, imapuid_t, int, Tcl_Obj **);
+int peMessageToAddr(Tcl_Interp *, imapuid_t, int, Tcl_Obj **);
+int peMessageCcAddr(Tcl_Interp *, imapuid_t, int, Tcl_Obj **);
+int peMessageField(Tcl_Interp *, imapuid_t, char *);
+int peMessageStatus(Tcl_Interp *, imapuid_t, int, Tcl_Obj **);
+int peMessageCharset(Tcl_Interp *, imapuid_t, int, Tcl_Obj **);
+int peMessageBounce(Tcl_Interp *, imapuid_t, int, Tcl_Obj **);
+int peMessageSpamNotice(Tcl_Interp *, imapuid_t, int, Tcl_Obj **);
+char *peSendSpamReport(long, char *, char *, char *);
+int peMsgnoFromUID(Tcl_Interp *, imapuid_t, int, Tcl_Obj **);
+int peMessageText(Tcl_Interp *, imapuid_t, int, Tcl_Obj **);
+int peMessageHeader(Tcl_Interp *, imapuid_t, int, Tcl_Obj **);
+void peFormatEnvelope(MAILSTREAM *, long, char *, ENVELOPE *, gf_io_t, long, char *, int);
+void peFormatEnvelopeAddress(MAILSTREAM *, long, char *, char *, ADDRESS *, int, char *, gf_io_t);
+void peFormatEnvelopeNewsgroups(char *, char *, int, gf_io_t);
+void peFormatEnvelopeText(char *, char *);
+int peMessageAttachments(Tcl_Interp *, imapuid_t, int, Tcl_Obj **);
+int peMessageBody(Tcl_Interp *, imapuid_t, int, Tcl_Obj **);
+int peMessagePartFromCID(Tcl_Interp *, imapuid_t, int, Tcl_Obj **);
+int peLocateBodyByCID(char *, char *, BODY *);
+char *peColorStr(char *, char *);
+int peInterpWritec(int);
+int peInterpFlush(void);
+int peNullWritec(int);
+void peGetMimeTyping(BODY *, Tcl_Obj **, Tcl_Obj **, Tcl_Obj **, Tcl_Obj **);
+int peGetFlag(Tcl_Interp *, imapuid_t, int, Tcl_Obj **);
+int peIsFlagged(MAILSTREAM *, imapuid_t, char *);
+int peSetFlag(Tcl_Interp *, imapuid_t, int, Tcl_Obj **);
+int peMsgSelect(Tcl_Interp *, imapuid_t, int, Tcl_Obj **);
+int peReplyHeaders(Tcl_Interp *, imapuid_t, int, Tcl_Obj **);
+int peAppListF(Tcl_Interp *, Tcl_Obj *, char *, ...);
+void pePatAppendID(Tcl_Interp *, Tcl_Obj *, PAT_S *);
+void pePatAppendPattern(Tcl_Interp *, Tcl_Obj *, PAT_S *);
+char *pePatStatStr(int);
+int peReplyText(Tcl_Interp *, imapuid_t, int, Tcl_Obj **);
+int peSoStrToList(Tcl_Interp *, Tcl_Obj *, STORE_S *);
+int peForwardHeaders(Tcl_Interp *, imapuid_t, int, Tcl_Obj **);
+int peForwardText(Tcl_Interp *, imapuid_t, int, Tcl_Obj **);
+int peDetach(Tcl_Interp *, imapuid_t, int, Tcl_Obj **);
+int peAttachInfo(Tcl_Interp *, imapuid_t, int, Tcl_Obj **);
+int peSaveDefault(Tcl_Interp *, imapuid_t, int, Tcl_Obj **);
+int peSaveWork(Tcl_Interp *, imapuid_t, int, Tcl_Obj **, long);
+int peSave(Tcl_Interp *, imapuid_t, int, Tcl_Obj **);
+int peCopy(Tcl_Interp *, imapuid_t, int, Tcl_Obj **);
+int peMove(Tcl_Interp *, imapuid_t, int, Tcl_Obj **);
+int peGotoDefault(Tcl_Interp *, imapuid_t, Tcl_Obj **);
+int peTakeaddr(Tcl_Interp *, imapuid_t, int, Tcl_Obj **);
+int peTakeFrom(Tcl_Interp *, imapuid_t, int, Tcl_Obj **);
+int peAddSuggestedContactInfo(Tcl_Interp *, Tcl_Obj *, ADDRESS *);
+int peReplyQuote(Tcl_Interp *, imapuid_t, int, Tcl_Obj **);
+long peMessageNumber(imapuid_t);
+long peSequenceNumber(imapuid_t);
+int peMsgCollector(Tcl_Interp *, int, Tcl_Obj **,
+ int (*)(METAENV *, BODY *, char *, CONTEXT_S **, char *), long);
+int peMsgCollected(Tcl_Interp *, MSG_COL_S *, char *, long);
+void peMsgSetParm(PARAMETER **, char *, char *);
+Tcl_Obj *peMsgAttachCollector(Tcl_Interp *, BODY *);
+int peFccAppend(Tcl_Interp *, Tcl_Obj *, char *, int);
+int peDoPost(METAENV *, BODY *, char *, CONTEXT_S **, char *);
+int peDoPostpone(METAENV *, BODY *, char *, CONTEXT_S **, char *);
+int peWriteSig (Tcl_Interp *, char *, Tcl_Obj **);
+int peInitAddrbooks(Tcl_Interp *, int);
+int peRuleStatVal(char *, int *);
+int peRuleSet(Tcl_Interp *, Tcl_Obj **);
+int peAppendCurrentSort(Tcl_Interp *interp);
+int peAppendDefaultSort(Tcl_Interp *interp);
+#if 0
+ADDRESS *peAEToAddress(AdrBk_Entry *);
+char *peAEFcc(AdrBk_Entry *);
+#endif
+NAMEVAL_S *sort_key_rules(int);
+NAMEVAL_S *wp_indexheight_rules(int);
+PINEFIELD *peCustomHdrs(void);
+STATMSG_S *sml_newmsg(int, char *);
+char *sml_getmsg(void);
+char **sml_getmsgs(void);
+void sml_seen(void);
+#ifdef ENABLE_LDAP
+int peLdapQueryResults(Tcl_Interp *);
+int peLdapStrlist(Tcl_Interp *, Tcl_Obj *, char **);
+int init_ldap_pname(struct pine *);
+#endif /* ENABLE_LDAP */
+char *strqchr(char *, int, int *, int);
+Tcl_Obj *wp_prune_folders(CONTEXT_S *, char *, int, char *,
+ unsigned, int *, int, Tcl_Interp *);
+int hex_colorstr(char *, char *);
+int hexval(char);
+int ascii_colorstr(char *, char *);
+COMPATT_S *peNewAttach(void);
+void peFreeAttach(COMPATT_S **);
+COMPATT_S *peGetAttachID(char *);
+char *peFileAttachID(char *, char *, char *, char *, char *, int);
+char *peBodyAttachID(BODY *);
+void peBodyMoveContents(BODY *, BODY *);
+int peClearAttachID(char *);
+char *peRandomString(char *, int, int);
+void ms_init(STRING *, void *, unsigned long);
+char ms_next(STRING *);
+void ms_setpos(STRING *, unsigned long);
+long peAppendMsg(MAILSTREAM *, void *, char **, char **, STRING **);
+int remote_pinerc_failure(void);
+char *peWebAlpinePrefix(void);
+void peNewMailAnnounce(MAILSTREAM *, long, long);
+int peMessageNeedPassphrase(Tcl_Interp *, imapuid_t, int, Tcl_Obj **);
+int peRssReturnFeed(Tcl_Interp *, char *, char *);
+int peRssPackageFeed(Tcl_Interp *, RSS_FEED_S *);
+RSS_FEED_S *peRssFeed(Tcl_Interp *, char *, char *);
+RSS_FEED_S *peRssFetch(Tcl_Interp *, char *);
+void peRssComponentFree(char **,char **,char **,char **,char **,char **);
+void peRssClearCacheEntry(RSS_CACHE_S *);
+
+
+/* Prototypes for Tcl-exported methods */
+int PEInit(Tcl_Interp *interp, char *);
+void PEExitCleanup(ClientData);
+int PEInfoCmd(ClientData clientData, Tcl_Interp *interp,
+ int objc, Tcl_Obj *CONST objv[]);
+int PEConfigCmd(ClientData clientData, Tcl_Interp *interp,
+ int objc, Tcl_Obj *CONST objv[]);
+int PEDebugCmd(ClientData clientData, Tcl_Interp *interp,
+ int objc, Tcl_Obj *CONST objv[]);
+int PESessionCmd(ClientData clientData, Tcl_Interp *interp,
+ int objc, Tcl_Obj *CONST objv[]);
+int PEMailboxCmd(ClientData clientData, Tcl_Interp *interp,
+ int objc, Tcl_Obj *CONST objv[]);
+int PEThreadCmd(ClientData clientData, Tcl_Interp *interp,
+ int objc, Tcl_Obj *CONST objv[]);
+int PEMessageCmd(ClientData clientData, Tcl_Interp *interp,
+ int objc, Tcl_Obj *CONST objv[]);
+int PEFolderCmd(ClientData clientData, Tcl_Interp *interp,
+ int objc, Tcl_Obj *CONST objv[]);
+int PEComposeCmd(ClientData clientData, Tcl_Interp *interp,
+ int objc, Tcl_Obj *CONST objv[]);
+int PEPostponeCmd(ClientData clientData, Tcl_Interp *interp,
+ int objc, Tcl_Obj *CONST objv[]);
+int PEAddressCmd(ClientData clientData, Tcl_Interp *interp,
+ int objc, Tcl_Obj *CONST objv[]);
+int PEClistCmd(ClientData clientData, Tcl_Interp *interp,
+ int objc, Tcl_Obj *CONST objv[]);
+int PELdapCmd(ClientData clientData, Tcl_Interp *interp,
+ int objc, Tcl_Obj *CONST objv[]);
+int PERssCmd(ClientData clientData, Tcl_Interp *interp,
+ int objc, Tcl_Obj *CONST objv[]);
+
+/* Append package */
+typedef struct append_pkg {
+ MAILSTREAM *stream; /* source stream */
+ unsigned long msgno; /* current message number */
+ unsigned long msgmax; /* maximum message number */
+ char *flags; /* current flags */
+ char *date; /* message internal date */
+ STRING *message; /* stringstruct of message */
+} APPEND_PKG;
+
+STRINGDRIVER mstring = {
+ ms_init, /* initialize string structure */
+ ms_next, /* get next byte in string structure */
+ ms_setpos /* set position in string structure */
+};
+
+
+/*----------------------------------------------------------------------
+ main routine -- entry point
+
+ Args: argv, argc -- The command line arguments
+
+
+ Setup c-client drivers and dive into TCL interpreter engine
+
+ ----*/
+
+int
+main(int argc, char *argv[])
+{
+ int ev = 1, s, cs, n, co, o, l, bl = 256, argerr;
+ char *buf, sname[256];
+ struct sockaddr_un name;
+ Tcl_Interp *interp;
+#if PUBCOOKIE
+ extern AUTHENTICATOR auth_gss_proxy;
+#endif
+
+ srandom(getpid() + time(0));
+
+ /*----------------------------------------------------------------------
+ Initialize c-client
+ ----------------------------------------------------------------------*/
+
+ /*
+ * NO LOCAL DRIVERS ALLOWED
+ * For this to change pintecld *MUST* be running under the user's UID and
+ * and signal.[ch] need to get fixed to handle KOD rather than change
+ * the debug level
+ */
+ mail_link (&imapdriver); /* link in the imap driver */
+ mail_link (&unixdriver); /* link in the unix driver */
+ mail_link (&dummydriver); /* link in the dummy driver */
+
+ /* link authentication drivers */
+#if PUBCOOKIE
+ auth_link (&auth_gss_proxy); /* pubcoookie proxy authenticator */
+#endif
+ auth_link (&auth_md5); /* link in the md5 authenticator */
+ auth_link (&auth_pla);
+ auth_link (&auth_log); /* link in the log authenticator */
+ ssl_onceonlyinit ();
+ mail_parameters (NIL,SET_DISABLEPLAINTEXT,(void *) 2);
+
+#if PUBCOOKIE
+ /* if REMOTE_USER set, use it as username */
+ if(buf = getenv("REMOTE_USER"))
+ env_init(buf, "/tmp");
+#endif
+
+ if(!mail_parameters(NULL, DISABLE_DRIVER, "unix")){
+ fprintf(stderr, "Can't disable unix driver");
+ exit(1);
+ }
+
+ /*
+ * Set network timeouts so we don't hang forever
+ * The open timeout can be pretty short since we're
+ * just opening tcp connection. The read timeout needs
+ * to be longer because the response to some actions can
+ * take awhile. Hopefully this is well within httpd's
+ * cgi timeout threshold.
+ */
+ mail_parameters(NULL, SET_OPENTIMEOUT, (void *)(long) 30);
+ mail_parameters(NULL, SET_READTIMEOUT, (void *)(long) 60);
+
+ /*----------------------------------------------------------------------
+ Initialize pith library
+ ----------------------------------------------------------------------*/
+ pith_opt_remote_pinerc_failure = remote_pinerc_failure;
+ pith_opt_user_agent_prefix = peWebAlpinePrefix;
+ pith_opt_newmail_announce = peNewMailAnnounce;
+
+ setup_for_index_index_screen();
+
+
+ /*----------------------------------------------------------------------
+ Parse arguments
+ ----------------------------------------------------------------------*/
+ debug = 0;
+ for(argerr = 0; !argerr && ((n = getopt(argc,argv,"d")) != -1); ) {
+ switch(n) {
+ case 'd' : debug++; break;
+ case '?' : argerr = 1; break;
+ }
+ }
+
+ if(argerr || optind != argc){
+ char *p = strrchr(argv[0],'/');
+ fprintf(stderr, "Usage: %s [-d]\n", p ? p + 1 : argv[0]);
+ exit(1);
+ }
+
+ /*----------------------------------------------------------------------
+ Hop into the Tcl processing loop
+ ----------------------------------------------------------------------*/
+
+ buf = (char *) fs_get(bl * sizeof(char));
+
+ if(fgets(sname, 255, stdin) && *sname){
+ if(sname[l = strlen(sname) - 1] == '\n')
+ sname[l] = '\0';
+
+ if((s = socket(AF_UNIX, SOCK_STREAM, 0)) != -1){
+
+ name.sun_family = AF_UNIX;
+ strcpy(name.sun_path, peSocketName = sname);
+ l = sizeof(name);
+
+ if(bind(s, (struct sockaddr *) &name, l) == 0){
+ if(listen(s, 5) == 0){
+ /*
+ * after the groundwork's done, go into the background.
+ * the fork saves the caller from invoking us in the background
+ * which introduces a timing race between the first client
+ * request arrival and our being prepared to accept it.
+ */
+ if(debug < 10){
+ switch(fork()){
+ case -1 : /* error */
+ perror("fork");
+ exit(1);
+
+ case 0 : /* child */
+ close(0); /* disassociate */
+ close(1);
+ close(2);
+ setpgrp(0, 0);
+ break;
+
+ default : /* parent */
+ exit(0);
+ }
+ }
+
+ debug_init();
+ dprint((SYSDBG_INFO, "started"));
+
+ interp = Tcl_CreateInterp();
+
+ PEInit(interp, sname);
+
+ while(1){
+ struct timeval tv;
+ fd_set rfd;
+
+ FD_ZERO(&rfd);
+ FD_SET(s, &rfd);
+ tv.tv_sec = (gPEAbandonTimeout) ? gPEAbandonTimeout : gPeInputTimeout;
+ tv.tv_usec = 0;
+ if((n = select(s+1, &rfd, 0, 0, &tv)) > 0){
+ socklen_t ll = l;
+
+ gPEAbandonTimeout = 0;
+
+ if((cs = accept(s, (struct sockaddr *) &name, &ll)) == -1){
+ dprint((SYSDBG_ERR, "accept failure: %s",
+ error_description(errno)));
+ break;
+ }
+
+ dprint((5, "accept success: %d", cs));
+
+ /*
+ * tcl commands are prefixed with a number representing
+ * the length of the command string and a newline character.
+ * the characters representing the length and the newline
+ * are not included in the command line length calculation.
+ */
+ o = co = 0;
+ while((n = read(cs, buf + o, bl - o - 1)) > 0){
+ o += n;
+ if(!co){
+ int i, x = 0;
+
+ for(i = 0; i < o; i++)
+ if(buf[i] == '\n'){
+ co = ++i;
+ l = x + co;
+ if(bl < l + 1){
+ bl = l + 1;
+ fs_resize((void **) &buf, bl * sizeof(char));
+ }
+
+ break;
+ }
+ else
+ x = (x * 10) + (buf[i] - '0');
+ }
+
+ if(o && o == l)
+ break;
+ }
+
+ if(n == 0){
+ dprint((SYSDBG_ERR, "read EOF"));
+ }
+ else if(n < 0){
+ dprint((SYSDBG_ERR, "read failure: %s", error_description(errno)));
+ }
+ else{
+ buf[o] = '\0';
+
+ /* Log every Eval if somebody *really* wants to see it. */
+ if(debug > 6){
+ char dbuf[5120];
+ int dlim = (debug >= 9) ? 256 : 5120 - 32;
+
+ snprintf(dbuf, sizeof(dbuf), "Tcl_Eval(%.*s)", dlim, &buf[co]);
+
+ /* But DON'T log any clear-text credentials */
+ if(dbuf[9] == 'P'
+ && dbuf[10] == 'E'
+ && dbuf[11] == 'S'
+ && !strncmp(dbuf + 12, "ession creds ", 13)){
+ char *p;
+
+ for(p = &dbuf[25]; *p; p++)
+ *p = 'X';
+ }
+
+ dprint((1, dbuf));
+ }
+
+ switch(Tcl_Eval(interp, &buf[co])){
+ case TCL_OK : peReturn(cs, "OK", interp->result); break;
+ case TCL_ERROR : peReturn(cs, "ERROR", interp->result); break;
+ case TCL_BREAK : peReturn(cs, "BREAK", interp->result); break;
+ case TCL_RETURN : peReturn(cs, "RETURN", interp->result); break;
+ default : peReturn(cs, "BOGUS", "eval returned unexpected value"); break;
+ }
+ }
+
+ close(cs);
+ }
+ else if(errno != EINTR){
+ if(n < 0){
+ dprint((SYSDBG_ALERT, "select failure: %s", error_description(errno)));
+ }
+ else{
+ dprint((SYSDBG_INFO, "timeout after %d seconds", tv.tv_sec));
+ }
+
+ Tcl_Exit(0);
+
+ /* Tcl_Exit should never return. Getting here is an error. */
+ dprint((SYSDBG_ERR, "Tcl_Exit failure"));
+ }
+ }
+ }
+ else
+ perror("listen");
+ }
+ else
+ perror("bind");
+
+ close(s);
+ unlink(sname);
+ }
+ else
+ perror("socket");
+ }
+ else
+ fprintf(stderr, "Can't read socket name\n");
+
+ exit(ev);
+}
+
+
+/*
+ * peReturn - common routine to return TCL result
+ */
+void
+peReturn(int sock, char *status, char *result)
+{
+ if(peWrite(sock, status))
+ if(peWrite(sock, "\n"))
+ peWrite(sock, result);
+}
+
+/*
+ * peWrite - write all the given string on the given socket
+ */
+int
+peWrite(int sock, char *s)
+{
+ int i, n;
+
+ for(i = 0, n = strlen(s); n; n = n - i)
+ if((i = write(sock, s + i, n)) < 0){
+ dprint((SYSDBG_ERR, "write: %s", error_description(errno)));
+ return(0);
+ }
+
+ return(1);
+}
+
+/*
+ * PEInit - Initialize exported TCL functions
+ */
+int
+PEInit(Tcl_Interp *interp, char *sname)
+{
+ dprint((2, "PEInit: %s", sname));
+
+ if(Tcl_Init(interp) == TCL_ERROR) {
+ return(TCL_ERROR);
+ }
+
+ Tcl_CreateObjCommand(interp, "PEInfo", PEInfoCmd,
+ (ClientData) NULL, (Tcl_CmdDeleteProc *) NULL);
+
+ Tcl_CreateObjCommand(interp, "PEConfig", PEConfigCmd,
+ (ClientData) NULL, (Tcl_CmdDeleteProc *) NULL);
+
+ Tcl_CreateObjCommand(interp, "PEDebug", PEDebugCmd,
+ (ClientData) NULL, (Tcl_CmdDeleteProc *) NULL);
+
+ Tcl_CreateObjCommand(interp, "PESession", PESessionCmd,
+ (ClientData) NULL, (Tcl_CmdDeleteProc *) NULL);
+
+ Tcl_CreateObjCommand(interp, "PEFolder", PEFolderCmd,
+ (ClientData) NULL, (Tcl_CmdDeleteProc *) NULL);
+
+ Tcl_CreateObjCommand(interp, "PEMailbox", PEMailboxCmd,
+ (ClientData) NULL, (Tcl_CmdDeleteProc *) NULL);
+
+ Tcl_CreateObjCommand(interp, "PEThread", PEThreadCmd,
+ (ClientData) NULL, (Tcl_CmdDeleteProc *) NULL);
+
+ Tcl_CreateObjCommand(interp, "PEMessage", PEMessageCmd,
+ (ClientData) NULL, (Tcl_CmdDeleteProc *) NULL);
+
+ Tcl_CreateObjCommand(interp, "PECompose", PEComposeCmd,
+ (ClientData) NULL, (Tcl_CmdDeleteProc *) NULL);
+
+ Tcl_CreateObjCommand(interp, "PEPostpone", PEPostponeCmd,
+ (ClientData) NULL, (Tcl_CmdDeleteProc *) NULL);
+
+ Tcl_CreateObjCommand(interp, "PEAddress", PEAddressCmd,
+ (ClientData) NULL, (Tcl_CmdDeleteProc *) NULL);
+
+ Tcl_CreateObjCommand(interp, "PEClist", PEClistCmd,
+ (ClientData) NULL, (Tcl_CmdDeleteProc *) NULL);
+
+ Tcl_CreateObjCommand(interp, "PELdap", PELdapCmd,
+ (ClientData) NULL, (Tcl_CmdDeleteProc *) NULL);
+
+ Tcl_CreateObjCommand(interp, "PERss", PERssCmd,
+ (ClientData) NULL, (Tcl_CmdDeleteProc *) NULL);
+
+ Tcl_CreateExitHandler(PEExitCleanup, sname);
+
+#ifdef ENABLE_LDAP
+ wpldap_global = (WPLDAP_S *)fs_get(sizeof(WPLDAP_S));
+ wpldap_global->query_no = 0;
+ wpldap_global->ldap_search_list = NULL;
+#endif /* ENABLE_LDAP */
+
+ return(TCL_OK);
+}
+
+
+void
+PEExitCleanup(ClientData clientData)
+{
+ dprint((4, "PEExitCleanup"));
+
+ if(ps_global){
+ /* destroy any open stream */
+ peDestroyStream(ps_global);
+
+ /* destroy user context */
+ peDestroyUserContext(&ps_global);
+ }
+
+#ifdef ENABLE_LDAP
+ if(wpldap_global){
+ if(wpldap_global->ldap_search_list)
+ free_wpldapres(wpldap_global->ldap_search_list);
+ fs_give((void **)&wpldap_global);
+ }
+#endif /* ENABLE_LDAP */
+
+ if((char *) clientData)
+ unlink((char *) clientData);
+
+ peFreeAttach(&peCompAttach);
+
+ dprint((SYSDBG_INFO, "finished"));
+}
+
+
+/*
+ * PEInfoCmd - export various bits of alpine state
+ */
+int
+PEInfoCmd(ClientData clientData, Tcl_Interp *interp, int objc, Tcl_Obj *CONST objv[])
+{
+ char *err = "Unknown PEInfo request";
+
+ dprint((2, "PEInfoCmd"));
+
+ if(objc == 1){
+ Tcl_WrongNumArgs(interp, 1, objv, "cmd ?args?");
+ }
+ else{
+ char *s1 = Tcl_GetStringFromObj(objv[1], NULL);
+
+ if(s1){
+ if(!strcmp(s1, "colorset")){
+ char *varname, *fghex, *bghex;
+ char tvname[256], asciicolor[256];
+ struct variable *vtmp;
+ Tcl_Obj **cObj;
+ int cObjc;
+ SPEC_COLOR_S *hcolors, *thc;
+
+ if(!(varname = Tcl_GetStringFromObj(objv[2], NULL))){
+ Tcl_SetResult(interp, "colorset: can't read variable name", TCL_STATIC);
+ return(TCL_ERROR);
+ }
+
+ if(!strcmp(varname, "viewer-hdr-colors")){
+ char *newhdr = NULL, *newpat = NULL, *utype;
+ int hindex, i;
+
+ if(objc < 5){
+ Tcl_SetResult(interp, "colorset: too few view-hdr args", TCL_STATIC);
+ return(TCL_ERROR);
+ }
+
+ hcolors = spec_colors_from_varlist(ps_global->VAR_VIEW_HDR_COLORS, 0);
+ if(!(utype = Tcl_GetStringFromObj(objv[3], NULL))){
+ Tcl_SetResult(interp, "colorset: can't read operation", TCL_STATIC);
+ return(TCL_ERROR);
+ }
+
+ if(!strcmp(utype, "delete")){
+ if(!hcolors){
+ Tcl_SetResult(interp, "colorset: no viewer-hdrs to delete", TCL_STATIC);
+ return(TCL_ERROR);
+ }
+
+ if(Tcl_GetIntFromObj(interp, objv[4], &hindex) == TCL_ERROR){
+ Tcl_SetResult(interp, "colorset: can't read index", TCL_STATIC);
+ return(TCL_ERROR);
+ }
+
+ if(hindex == 0){
+ thc = hcolors;
+ hcolors = hcolors->next;
+ thc->next = NULL;
+ free_spec_colors(&thc);
+ }
+ else{
+ /* zero based */
+ for(thc = hcolors, i = 1; thc && i < hindex; thc = thc->next, i++)
+ ;
+
+ if(thc && thc->next){
+ SPEC_COLOR_S *thc2 = thc->next;
+
+ thc->next = thc2->next;
+ thc2->next = NULL;
+ free_spec_colors(&thc2);
+ }
+ else{
+ Tcl_SetResult(interp, "colorset: invalid index", TCL_STATIC);
+ return(TCL_ERROR);
+ }
+ }
+ }
+ else if(!strcmp(utype, "add")){
+ if(objc != 6){
+ Tcl_SetResult(interp, "colorset: wrong number of view-hdr add args", TCL_STATIC);
+ return(TCL_ERROR);
+ }
+
+ if(Tcl_ListObjGetElements(interp, objv[4], &cObjc, &cObj) != TCL_OK)
+ return (TCL_ERROR);
+
+ if(cObjc != 2){
+ Tcl_SetResult(interp, "colorset: wrong number of hdrs for view-hdr add", TCL_STATIC);
+ return(TCL_ERROR);
+ }
+
+ newhdr = Tcl_GetStringFromObj(cObj[0], NULL);
+ newpat = Tcl_GetStringFromObj(cObj[1], NULL);
+ if(Tcl_ListObjGetElements(interp, objv[5], &cObjc, &cObj) != TCL_OK)
+ return (TCL_ERROR);
+
+ if(cObjc != 2){
+ Tcl_SetResult(interp, "colorset: wrong number of colors for view-hdr add", TCL_STATIC);
+ return(TCL_ERROR);
+ }
+
+ fghex = Tcl_GetStringFromObj(cObj[0], NULL);
+ bghex = Tcl_GetStringFromObj(cObj[1], NULL);
+ if(newhdr && newpat && fghex && bghex){
+ SPEC_COLOR_S **hcp;
+
+ for(hcp = &hcolors; *hcp != NULL; hcp = &(*hcp)->next)
+ ;
+
+ *hcp = (SPEC_COLOR_S *)fs_get(sizeof(SPEC_COLOR_S));
+ (*hcp)->inherit = 0;
+ (*hcp)->spec = cpystr(newhdr);
+ (*hcp)->fg = cpystr((ascii_colorstr(asciicolor, fghex) == 0) ? asciicolor : "black");
+ (*hcp)->bg = cpystr((ascii_colorstr(asciicolor, bghex) == 0) ? asciicolor : "white");
+
+ if(newpat && *newpat)
+ (*hcp)->val = string_to_pattern(newpat);
+ else
+ (*hcp)->val = NULL;
+
+ (*hcp)->next = NULL;
+ }
+ else{
+ Tcl_SetResult(interp, "colorset: invalid args for view-hdr add", TCL_STATIC);
+ return(TCL_ERROR);
+ }
+ }
+ else if(!strcmp(utype, "update")){
+ if(objc != 6){
+ Tcl_SetResult(interp, "colorset: wrong number of view-hdr update args", TCL_STATIC);
+ return(TCL_ERROR);
+ }
+
+ if(!(Tcl_ListObjGetElements(interp, objv[4], &cObjc, &cObj) == TCL_OK
+ && cObjc == 3
+ && Tcl_GetIntFromObj(interp, cObj[0], &hindex) == TCL_OK
+ && (newhdr = Tcl_GetStringFromObj(cObj[1], NULL))
+ && (newpat = Tcl_GetStringFromObj(cObj[2], NULL)))){
+ Tcl_SetResult(interp, "colorset: view-hdr update can't read index or header", TCL_STATIC);
+ return (TCL_ERROR);
+ }
+
+ if(!(Tcl_ListObjGetElements(interp, objv[5], &cObjc, &cObj) == TCL_OK
+ && cObjc == 2
+ && (fghex = Tcl_GetStringFromObj(cObj[0], NULL))
+ && (bghex = Tcl_GetStringFromObj(cObj[1], NULL)))){
+ Tcl_SetResult(interp, "colorset: view-hdr update can't read colors", TCL_STATIC);
+ return (TCL_ERROR);
+ }
+
+ for(thc = hcolors, i = 0; thc && i < hindex; thc = thc->next, i++)
+ ;
+
+ if(!thc){
+ Tcl_SetResult(interp, "colorset: view-hdr update invalid index", TCL_STATIC);
+ return (TCL_ERROR);
+ }
+
+ if(thc->spec)
+ fs_give((void **)&thc->spec);
+
+ thc->spec = cpystr(newhdr);
+ if(ascii_colorstr(asciicolor, fghex) == 0) {
+ if(thc->fg)
+ fs_give((void **)&thc->fg);
+
+ thc->fg = cpystr(asciicolor);
+ }
+ else{
+ snprintf(tmp_20k_buf, SIZEOF_20KBUF, "colorset: invalid foreground color value %.100s", fghex);
+ Tcl_SetResult(interp, tmp_20k_buf, TCL_VOLATILE);
+ return(TCL_ERROR);
+ }
+
+ if(ascii_colorstr(asciicolor, bghex) == 0) {
+ if(thc->bg)
+ fs_give((void **)&thc->bg);
+
+ thc->bg = cpystr(asciicolor);
+ }
+ else{
+ snprintf(tmp_20k_buf, SIZEOF_20KBUF, "colorset: invalid background color value %.100s", bghex);
+ Tcl_SetResult(interp, tmp_20k_buf, TCL_VOLATILE);
+ return(TCL_ERROR);
+ }
+
+ if(thc->val)
+ fs_give((void **)&thc->val);
+
+ if(newpat && *newpat){
+ thc->val = string_to_pattern(newpat);
+ }
+ }
+ else{
+ Tcl_SetResult(interp, "colorset: unknown operation", TCL_STATIC);
+ return(TCL_ERROR);
+ }
+
+ vtmp = &ps_global->vars[V_VIEW_HDR_COLORS];
+ for(i = 0; vtmp->main_user_val.l && vtmp->main_user_val.l[i]; i++)
+ fs_give((void **)&vtmp->main_user_val.l[i]);
+
+ if(vtmp->main_user_val.l)
+ fs_give((void **)&vtmp->main_user_val.l);
+
+ vtmp->main_user_val.l = varlist_from_spec_colors(hcolors);
+ set_current_val(vtmp, FALSE, FALSE);
+ free_spec_colors(&hcolors);
+ return(TCL_OK);
+ }
+ else {
+ if(objc != 4){
+ Tcl_SetResult(interp, "colorset: Wrong number of args", TCL_STATIC);
+ return(TCL_ERROR);
+ }
+
+ if(!(Tcl_ListObjGetElements(interp, objv[3], &cObjc, &cObj) == TCL_OK
+ && cObjc == 2
+ && (fghex = Tcl_GetStringFromObj(cObj[0], NULL))
+ && (bghex = Tcl_GetStringFromObj(cObj[1], NULL)))){
+ Tcl_SetResult(interp, "colorset: Problem reading fore/back ground colors", TCL_STATIC);
+ return (TCL_ERROR);
+ }
+
+ snprintf(tvname, sizeof(tvname), "%.200s-foreground-color", varname);
+ for(vtmp = &ps_global->vars[V_NORM_FORE_COLOR];
+ vtmp->name && strucmp(vtmp->name, tvname);
+ vtmp++)
+ ;
+
+ if(!vtmp->name || vtmp->is_list){
+ snprintf(tmp_20k_buf, SIZEOF_20KBUF, "colorset: invalid background var %.100s", varname);
+ Tcl_SetResult(interp, tmp_20k_buf, TCL_VOLATILE);
+ return(TCL_ERROR);
+ }
+
+ if(ascii_colorstr(asciicolor, fghex) == 0) {
+ if(vtmp->main_user_val.p)
+ fs_give((void **)&vtmp->main_user_val.p);
+
+ vtmp->main_user_val.p = cpystr(asciicolor);
+ set_current_val(vtmp, FALSE, FALSE);
+ if(!strucmp(varname, "normal"))
+ pico_set_fg_color(asciicolor);
+ }
+ else{
+ snprintf(tmp_20k_buf, SIZEOF_20KBUF, "colorset: invalid color value %.100s", fghex);
+ Tcl_SetResult(interp, tmp_20k_buf, TCL_VOLATILE);
+ return(TCL_ERROR);
+ }
+
+ snprintf(tvname, sizeof(tvname), "%.200s%.50s", varname, "-background-color");
+ vtmp++;
+ if((vtmp->name && strucmp(vtmp->name, tvname)) || !vtmp->name)
+ for(vtmp = &ps_global->vars[V_NORM_FORE_COLOR];
+ vtmp->name && strucmp(vtmp->name, tvname);
+ vtmp++)
+ ;
+
+ if(!vtmp->name || vtmp->is_list){
+ snprintf(tmp_20k_buf, SIZEOF_20KBUF, "colorset: invalid background var %.100s", varname);
+ Tcl_SetResult(interp, tmp_20k_buf, TCL_VOLATILE);
+ return(TCL_ERROR);
+ }
+
+ if(ascii_colorstr(asciicolor, bghex) == 0) {
+ if(vtmp->main_user_val.p)
+ fs_give((void **)&vtmp->main_user_val.p);
+
+ vtmp->main_user_val.p = cpystr(asciicolor);
+ set_current_val(vtmp, FALSE, FALSE);
+ if(!strucmp(varname, "normal"))
+ pico_set_bg_color(asciicolor);
+ }
+ else{
+ snprintf(tmp_20k_buf, SIZEOF_20KBUF, "colorset: invalid background color value %.100s", bghex);
+ Tcl_SetResult(interp, tmp_20k_buf, TCL_VOLATILE);
+ return(TCL_ERROR);
+ }
+
+ Tcl_SetResult(interp, "1", TCL_STATIC);
+ return(TCL_OK);
+ }
+ }
+ else if(!strcmp(s1, "lappend")){
+ if(objc >= 4){
+ Tcl_Obj *dObj;
+ int i;
+
+ if((dObj = Tcl_ObjGetVar2(interp, objv[2], NULL, TCL_LEAVE_ERR_MSG)) != NULL){
+ for(i = 3; i < objc; i++)
+ if(Tcl_ListObjAppendElement(interp, dObj, objv[i]) != TCL_OK)
+ return(TCL_ERROR);
+
+ if(i == objc){
+ return(TCL_OK);
+ }
+ }
+ else
+ err = "PEInfo lappend: Unknown list name";
+ }
+ else
+ err = "PEInfo lappend: Too few args";
+ }
+ else if(objc == 2){
+ if(!strcmp(s1, "version")){
+ char buf[256];
+
+ /*
+ * CMD: version
+ *
+ * Returns: string representing Pine version
+ * engine built on
+ */
+ Tcl_SetResult(interp, ALPINE_VERSION, TCL_STATIC);
+ return(TCL_OK);
+ }
+ else if(!strcmp(s1, "revision")){
+ char buf[16];
+
+ /*
+ * CMD: revision
+ *
+ * Returns: string representing Pine SVN revision
+ * engine built on
+ */
+
+ Tcl_SetResult(interp, get_alpine_revision_number(buf, sizeof(buf)), TCL_VOLATILE);
+ return(TCL_OK);
+ }
+ else if(!strcmp(s1, "key")){
+ static char key[64];
+
+ if(!key[0])
+ peRandomString(key,32,PRS_UPPER_CASE);
+
+ Tcl_SetResult(interp, key, TCL_STATIC);
+ return(TCL_OK);
+ }
+ else if(!strcmp(s1, "indexheight")){
+ Tcl_SetResult(interp, ps_global->VAR_WP_INDEXHEIGHT ?
+ ps_global->VAR_WP_INDEXHEIGHT : "", TCL_VOLATILE);
+ return(TCL_OK);
+ }
+ else if(!strcmp(s1, "indexlines")){
+ Tcl_SetResult(interp, ps_global->VAR_WP_INDEXLINES ?
+ ps_global->VAR_WP_INDEXLINES : "0", TCL_VOLATILE);
+ return(TCL_OK);
+ }
+ else if(!strcmp(s1, "aggtabstate")){
+ Tcl_SetResult(interp, ps_global->VAR_WP_AGGSTATE ?
+ ps_global->VAR_WP_AGGSTATE : "0", TCL_VOLATILE);
+ return(TCL_OK);
+ }
+ else if(!strcmp(s1, "alpinestate")){
+ char *wps, *p, *q;
+
+ if((wps = ps_global->VAR_WP_STATE) != NULL){
+ wps = p = q = cpystr(wps);
+ do
+ if(*q == '\\' && *(q+1) == '$')
+ q++;
+ while((*p++ = *q++) != '\0');
+ }
+
+ Tcl_SetResult(interp, wps ? wps : "", TCL_VOLATILE);
+
+ if(wps)
+ fs_give((void **) &wps);
+
+ return(TCL_OK);
+ }
+ else if(!strcmp(s1, "foreground")){
+ char *color;
+
+ if(!((color = pico_get_last_fg_color())
+ && (color = color_to_asciirgb(color))
+ && (color = peColorStr(color,tmp_20k_buf))))
+ color = "000000";
+
+ Tcl_SetResult(interp, color, TCL_VOLATILE);
+ return(TCL_OK);
+ }
+ else if(!strcmp(s1, "background")){
+ char *color;
+
+ if(!((color = pico_get_last_bg_color())
+ && (color = color_to_asciirgb(color))
+ && (color = peColorStr(color,tmp_20k_buf))))
+ color = "FFFFFF";
+
+ Tcl_SetResult(interp, color, TCL_VOLATILE);
+ return(TCL_OK);
+ }
+ else if(!strcmp(s1, "flaglist")){
+ int i;
+ char *p;
+ Tcl_Obj *itemObj;
+
+ /*
+ * BUG: This list should get merged with the static list in "cmd_flag"
+ * and exported via some function similar to "feature_list()"
+ */
+ static char *flag_list[] = {
+ "Important", "New", "Answered", "Deleted", NULL
+ };
+
+ /*
+ * CMD: flaglist
+ *
+ * Returns: list of FLAGS available for setting
+ */
+ for(i = 0; (p = flag_list[i]); i++)
+ if((itemObj = Tcl_NewStringObj(p, -1)) != NULL){
+ if(Tcl_ListObjAppendElement(interp,
+ Tcl_GetObjResult(interp),
+ itemObj) != TCL_OK)
+ ;
+ }
+
+ return(TCL_OK);
+ }
+ else if(!strcmp(s1, "featurelist")){
+ int i;
+ char *curfeature, *s;
+ FEATURE_S *feature;
+ Tcl_Obj *itemObj, *secObj = NULL, *resObj = NULL;
+
+ /*
+ * CMD: featurelist
+ *
+ * Returns: list of FEATURES available for setting
+ */
+ for(i = 0, curfeature = NULL; (feature = feature_list(i)); i++)
+ if((s = feature_list_section(feature)) != NULL){
+ if(!curfeature || strucmp(s, curfeature)){
+ if(resObj) {
+ Tcl_ListObjAppendElement(interp,
+ secObj,
+ resObj);
+ Tcl_ListObjAppendElement(interp,
+ Tcl_GetObjResult(interp),
+ secObj);
+ }
+
+ secObj = Tcl_NewListObj(0, NULL);
+ resObj = Tcl_NewListObj(0, NULL);
+ if(Tcl_ListObjAppendElement(interp,
+ secObj,
+ Tcl_NewStringObj(s,-1)) != TCL_OK)
+ ;
+
+ curfeature = s;
+ }
+
+ if((itemObj = Tcl_NewStringObj(feature->name, -1)) != NULL){
+ if(Tcl_ListObjAppendElement(interp,
+ resObj,
+ itemObj) != TCL_OK)
+ ;
+ }
+ }
+
+ if(resObj){
+ Tcl_ListObjAppendElement(interp, secObj, resObj);
+ Tcl_ListObjAppendElement(interp, Tcl_GetObjResult(interp), secObj);
+ }
+
+ return(TCL_OK);
+ }
+ else if(!strcmp(s1, "featuresettings")){
+ int i;
+ FEATURE_S *feature;
+ Tcl_Obj *itemObj;
+
+ /*
+ * CMD: featuresettings
+ *
+ * Returns: list of FEATURES currently SET
+ */
+ for(i = 0; (feature = feature_list(i)); i++)
+ if(feature_list_section(feature)){
+ if(F_ON(feature->id, ps_global)){
+ if((itemObj = Tcl_NewStringObj(feature->name, -1)) != NULL){
+ if(Tcl_ListObjAppendElement(interp,
+ Tcl_GetObjResult(interp),
+ itemObj) != TCL_OK)
+ ;
+ }
+ }
+ }
+
+ return(TCL_OK);
+ }
+ else if(!strcmp(s1, "signature")){
+ char *sig;
+
+ if((ps_global->VAR_LITERAL_SIG
+ || (ps_global->VAR_SIGNATURE_FILE
+ && IS_REMOTE(ps_global->VAR_SIGNATURE_FILE)))
+ && (sig = detoken(NULL, NULL, 2, 0, 1, NULL, NULL))){
+ char *p, *q;
+
+ for(p = sig; (q = strindex(p, '\n')); p = q + 1)
+ Tcl_ListObjAppendElement(interp, Tcl_GetObjResult(interp),
+ Tcl_NewStringObj(p, q - p));
+
+ if(*p)
+ Tcl_ListObjAppendElement(interp, Tcl_GetObjResult(interp),
+ Tcl_NewStringObj(p, -1));
+
+ fs_give((void **) &sig);
+ }
+ else
+ Tcl_SetResult(interp, "", TCL_STATIC);
+
+ return(TCL_OK);
+ }
+ else if(!strcmp(s1, "rawsig")){
+ char *err = NULL, *sig = NULL, *p, *q;
+
+ if(ps_global->VAR_LITERAL_SIG){
+ char *err = NULL;
+ char **apval;
+
+ if(ps_global->restricted){
+ err = "Alpine demo can't change config file";
+ }
+ else{
+ /* BUG: no "exceptions file" support */
+ if((apval = APVAL(&ps_global->vars[V_LITERAL_SIG], Main)) != NULL){
+ sig = (char *) fs_get((strlen(*apval ? *apval : "") + 1) * sizeof(char));
+ sig[0] = '\0';
+ cstring_to_string(*apval, sig);
+ }
+ else
+ err = "Problem accessing configuration";
+ }
+ }
+ else if(!IS_REMOTE(ps_global->VAR_SIGNATURE_FILE))
+ snprintf(err = tmp_20k_buf, SIZEOF_20KBUF, "Non-Remote signature file: %s",
+ ps_global->VAR_SIGNATURE_FILE ? ps_global->VAR_SIGNATURE_FILE : "<null>");
+ else if(!(sig = simple_read_remote_file(ps_global->VAR_SIGNATURE_FILE, REMOTE_SIG_SUBTYPE)))
+ err = "Can't read remote pinerc";
+
+ if(err){
+ Tcl_SetResult(interp, err, TCL_VOLATILE);
+ return(TCL_ERROR);
+ }
+
+ for(p = sig; (q = strindex(p, '\n')); p = q + 1)
+ Tcl_ListObjAppendElement(interp, Tcl_GetObjResult(interp),
+ Tcl_NewStringObj(p, q - p));
+
+ Tcl_ListObjAppendElement(interp, Tcl_GetObjResult(interp),
+ Tcl_NewStringObj(p, -1));
+ fs_give((void **) &sig);
+ return(TCL_OK);
+ }
+ else if(!strcmp(s1, "statmsg")){
+ char *s = sml_getmsg();
+ /* BUG: can this be removed? */
+
+ Tcl_SetResult(interp, s ? s : "", TCL_VOLATILE);
+ return(TCL_OK);
+ }
+ else if(!strcmp(s1, "statmsgs")){
+ char **s = sml_getmsgs();
+ char **tmps, *lmsg = NULL;
+
+ for(tmps = s; tmps && *tmps; lmsg = *tmps++)
+ if(!lmsg || strcmp(lmsg, *tmps))
+ Tcl_ListObjAppendElement(interp,
+ Tcl_GetObjResult(interp),
+ Tcl_NewStringObj(*tmps, -1));
+
+ fs_give((void **)&s);
+ return(TCL_OK);
+ }
+ else if(!strcmp(s1, "saveconf")){
+ write_pinerc(ps_global, Main, WRP_NOUSER);
+ return(TCL_OK);
+ }
+ else if(!strucmp(s1, "sort")){
+ return(peAppendDefaultSort(interp));
+ }
+ else if(!strcmp(s1, "ldapenabled")){
+ /*
+ * CMD: ldapenabled
+ *
+ * Returns: 1 if enabled 0 if not
+ */
+#ifdef ENABLE_LDAP
+ Tcl_SetResult(interp, "1", TCL_VOLATILE);
+#else
+ Tcl_SetResult(interp, "0", TCL_VOLATILE);
+#endif
+
+ return(TCL_OK);
+ }
+ else if(!strcmp(s1, "prunecheck")){
+ time_t now;
+ struct tm *tm_now;
+ char tmp[50];
+
+ if(!check_prune_time(&now, &tm_now)){
+ Tcl_SetResult(interp, "0", TCL_VOLATILE);
+ return(TCL_OK);
+ } else {
+ /*
+ * We're going to reset the last-time-pruned variable
+ * so that it asks a maximum of 1 time per month.
+ * PROs: Annoying-factor is at its lowest
+ * Can go ahead and move folders right away if
+ * pruning-rule is automatically set to do so
+ * CONs: Annoying-factor is at its lowest, if it's set
+ * later then we can ensure that the questions
+ * actually get answered or it will keep asking
+ */
+ ps_global->last_expire_year = tm_now->tm_year;
+ ps_global->last_expire_month = tm_now->tm_mon;
+ snprintf(tmp, sizeof(tmp), "%d.%d", ps_global->last_expire_year,
+ ps_global->last_expire_month + 1);
+ set_variable(V_LAST_TIME_PRUNE_QUESTION, tmp, 0, 1, Main);
+
+ Tcl_SetResult(interp, "1", TCL_VOLATILE);
+ }
+ return(TCL_OK);
+ }
+ else if(!strcmp(s1, "prunetime")){
+ time_t now;
+ struct tm *tm_now;
+ CONTEXT_S *prune_cntxt;
+ Tcl_Obj *retObj = NULL;
+ int cur_month, ok = 1;
+ char **p;
+ static int moved_fldrs = 0;
+
+ now = time((time_t *)0);
+ tm_now = localtime(&now);
+ cur_month = (1900 + tm_now->tm_year) * 12 + tm_now->tm_mon;
+
+ if(!(prune_cntxt = default_save_context(ps_global->context_list)))
+ prune_cntxt = ps_global->context_list;
+
+ if(prune_cntxt){
+ if(ps_global->VAR_DEFAULT_FCC && *ps_global->VAR_DEFAULT_FCC
+ && context_isambig(ps_global->VAR_DEFAULT_FCC))
+ if((retObj = wp_prune_folders(prune_cntxt,
+ ps_global->VAR_DEFAULT_FCC,
+ cur_month, "sent",
+ ps_global->pruning_rule, &ok,
+ moved_fldrs, interp)) != NULL)
+ Tcl_ListObjAppendElement(interp,
+ Tcl_GetObjResult(interp),
+ retObj);
+
+ if(ok && ps_global->VAR_READ_MESSAGE_FOLDER
+ && *ps_global->VAR_READ_MESSAGE_FOLDER
+ && context_isambig(ps_global->VAR_READ_MESSAGE_FOLDER))
+ if((retObj = wp_prune_folders(prune_cntxt,
+ ps_global->VAR_READ_MESSAGE_FOLDER,
+ cur_month, "read",
+ ps_global->pruning_rule, &ok,
+ moved_fldrs, interp)) != NULL)
+ Tcl_ListObjAppendElement(interp,
+ Tcl_GetObjResult(interp),
+ retObj);
+ if(ok && (p = ps_global->VAR_PRUNED_FOLDERS)){
+ for(; ok && *p; p++)
+ if(**p && context_isambig(*p))
+ if((retObj = wp_prune_folders(prune_cntxt,
+ *p, cur_month, "",
+ ps_global->pruning_rule, &ok,
+ moved_fldrs, interp)) != NULL)
+ Tcl_ListObjAppendElement(interp,
+ Tcl_GetObjResult(interp),
+ retObj);
+ }
+ }
+ moved_fldrs = 1;
+ return(TCL_OK);
+ }
+ else if(!strcmp(s1, "authrequestor")){
+ Tcl_SetResult(interp, peCredentialRequestor, TCL_STATIC);
+ return(TCL_OK);
+ }
+ else if(!strcmp(s1, "noop")){
+ /* tickle the imap server too */
+ if(ps_global->mail_stream)
+ pine_mail_ping(ps_global->mail_stream);
+
+ Tcl_SetResult(interp, "NOOP", TCL_STATIC);
+ return(TCL_OK);
+ }
+ else if(!strcmp(s1, "inputtimeout")){
+ Tcl_SetResult(interp, int2string(get_input_timeout()), TCL_VOLATILE);
+ return(TCL_OK);
+ }
+ }
+ else if(objc == 3){
+ if(!strcmp(s1, "feature")){
+ char *featurename;
+ int i, isset = 0;
+ FEATURE_S *feature;
+
+ /*
+ * CMD: feature
+ *
+ * ARGS: featurename -
+ *
+ * Returns: 1 if named feature set, 0 otherwise
+ *
+ */
+ if((featurename = Tcl_GetStringFromObj(objv[2], NULL)) != NULL)
+ for(i = 0; (feature = feature_list(i)); i++)
+ if(!strucmp(featurename, feature->name)){
+ isset = F_ON(feature->id, ps_global);
+ break;
+ }
+
+ Tcl_SetResult(interp, int2string(isset), TCL_VOLATILE);
+ return(TCL_OK);
+ }
+ else if(!strcmp(s1, "colorget")){
+ char *varname;
+ char tvname[256], hexcolor[256];
+ struct variable *vtmp;
+ if(!(varname = Tcl_GetStringFromObj(objv[2], NULL))){
+ return(TCL_ERROR);
+ }
+ if(strcmp("viewer-hdr-colors", varname) == 0){
+ SPEC_COLOR_S *hcolors, *thc;
+ Tcl_Obj *resObj;
+ char hexcolor[256], *tstr = NULL;
+
+ hcolors = spec_colors_from_varlist(ps_global->VAR_VIEW_HDR_COLORS, 0);
+ for(thc = hcolors; thc; thc = thc->next){
+ resObj = Tcl_NewListObj(0,NULL);
+ Tcl_ListObjAppendElement(interp, resObj,
+ Tcl_NewStringObj(thc->spec, -1));
+ hex_colorstr(hexcolor, thc->fg);
+ Tcl_ListObjAppendElement(interp, resObj,
+ Tcl_NewStringObj(hexcolor, -1));
+ hex_colorstr(hexcolor, thc->bg);
+ Tcl_ListObjAppendElement(interp, resObj,
+ Tcl_NewStringObj(hexcolor, -1));
+ Tcl_ListObjAppendElement(interp, resObj,
+ Tcl_NewStringObj(thc->val
+ ? tstr = pattern_to_string(thc->val)
+ : "", -1));
+ if(tstr) fs_give((void **)&tstr);
+ Tcl_ListObjAppendElement(interp, Tcl_GetObjResult(interp),
+ resObj);
+ }
+ fs_give((void **)&hcolors);
+ return(TCL_OK);
+ }
+ else {
+ snprintf(tvname, sizeof(tvname), "%.200s%.50s", varname, "-foreground-color");
+ for(vtmp = &ps_global->vars[V_NORM_FORE_COLOR];
+ vtmp->name && strucmp(vtmp->name, tvname);
+ vtmp++);
+ if(!vtmp->name) return(TCL_ERROR);
+ if(vtmp->is_list) return(TCL_ERROR);
+ if(!vtmp->current_val.p)
+ Tcl_ListObjAppendElement(interp, Tcl_GetObjResult(interp),
+ Tcl_NewStringObj("", -1));
+ else{
+ hex_colorstr(hexcolor, vtmp->current_val.p);
+ Tcl_ListObjAppendElement(interp, Tcl_GetObjResult(interp),
+ Tcl_NewStringObj(hexcolor, -1));
+ }
+ snprintf(tvname, sizeof(tvname), "%.200s%.50s", varname, "-background-color");
+ vtmp++;
+ if((vtmp->name && strucmp(vtmp->name, tvname)) || !vtmp->name)
+ for(vtmp = &ps_global->vars[V_NORM_FORE_COLOR];
+ vtmp->name && strucmp(vtmp->name, tvname);
+ vtmp++)
+ ;
+
+ if(!vtmp->name) return(TCL_ERROR);
+ if(vtmp->is_list) return(TCL_ERROR);
+ if(!vtmp->current_val.p)
+ Tcl_ListObjAppendElement(interp, Tcl_GetObjResult(interp),
+ Tcl_NewStringObj("", -1));
+ else{
+ hex_colorstr(hexcolor, vtmp->current_val.p);
+ Tcl_ListObjAppendElement(interp, Tcl_GetObjResult(interp),
+ Tcl_NewStringObj(hexcolor, -1));
+ }
+ }
+ return(TCL_OK);
+ }
+ else if(!strcmp(s1, "varget")){
+ struct variable *vtmp;
+ Tcl_Obj *itemObj, *resObj, *secObj;
+ char *vallist, *varname, tmperrmsg[256];
+ int i;
+ NAMEVAL_S *tmpnv;
+
+ /*
+ * CMD: varget
+ *
+ * Returns: get the values for the requested variable
+ *
+ * The list returned follows this general form:
+ *
+ * char *; variable name
+ * char **; list of set values
+ * char *; display type (listbox, text, textarea, ...)
+ * char **; list of possible values
+ * (so far this is only useful for listboxes)
+ */
+ if(!(varname = Tcl_GetStringFromObj(objv[2], NULL))){
+ Tcl_SetResult(interp, "Can't Tcl_GetStringFromObj",
+ TCL_VOLATILE);
+ return(TCL_ERROR);
+ }
+
+ for(vtmp = ps_global->vars;
+ vtmp->name && strucmp(vtmp->name, varname);
+ vtmp++)
+ ;
+
+ if(!vtmp->name){
+ snprintf(tmperrmsg, sizeof(tmperrmsg), "Can't find variable named %s",
+ strlen(varname) < 200 ? varname : "");
+ Tcl_SetResult(interp, tmperrmsg, TCL_VOLATILE);
+ return(TCL_ERROR);
+ }
+ if((itemObj = Tcl_NewStringObj(vtmp->name, -1)) != NULL){
+ Tcl_ListObjAppendElement(interp,
+ Tcl_GetObjResult(interp),
+ itemObj);
+ resObj = Tcl_NewListObj(0, NULL);
+ if(vtmp->is_list){
+ for(i = 0 ; vtmp->current_val.l && vtmp->current_val.l[i]; i++){
+ vallist = vtmp->current_val.l[i];
+ if(*(vallist))
+ itemObj = Tcl_NewStringObj(vallist, -1);
+ else
+ itemObj = Tcl_NewStringObj("", -1);
+ Tcl_ListObjAppendElement(interp, resObj, itemObj);
+ }
+ }
+ else{
+ itemObj = Tcl_NewStringObj(vtmp->current_val.p ?
+ vtmp->current_val.p : "", -1);
+ Tcl_ListObjAppendElement(interp, resObj, itemObj);
+ }
+ Tcl_ListObjAppendElement(interp,
+ Tcl_GetObjResult(interp),
+ resObj);
+ secObj = Tcl_NewListObj(0, NULL);
+ if(vtmp->is_list)
+ itemObj = Tcl_NewStringObj("textarea", -1);
+ else{
+ NAMEVAL_S *(*tmpf)(int);
+ switch(vtmp - ps_global->vars){
+ case V_SAVED_MSG_NAME_RULE:
+ tmpf = save_msg_rules;
+ break;
+ case V_FCC_RULE:
+ tmpf = fcc_rules;
+ break;
+ case V_SORT_KEY:
+ tmpf = sort_key_rules;
+ break;
+ case V_AB_SORT_RULE:
+ tmpf = ab_sort_rules;
+ break;
+ case V_FLD_SORT_RULE:
+ tmpf = fld_sort_rules;
+ break;
+ case V_GOTO_DEFAULT_RULE:
+ tmpf = goto_rules;
+ break;
+ case V_INCOMING_STARTUP:
+ tmpf = incoming_startup_rules;
+ break;
+ case V_PRUNING_RULE:
+ tmpf = pruning_rules;
+ break;
+ case V_WP_INDEXHEIGHT:
+ tmpf = wp_indexheight_rules;
+ break;
+ default:
+ tmpf = NULL;
+ break;
+ }
+ if(tmpf){
+ for(i = 0; (tmpnv = (tmpf)(i)); i++){
+ itemObj = Tcl_NewListObj(0, NULL);
+ Tcl_ListObjAppendElement(interp, itemObj,
+ Tcl_NewStringObj(tmpnv->name, -1));
+ if(tmpnv->shortname)
+ Tcl_ListObjAppendElement(interp, itemObj,
+ Tcl_NewStringObj(tmpnv->shortname, -1));
+ Tcl_ListObjAppendElement(interp, secObj, itemObj);
+ }
+ itemObj = Tcl_NewStringObj("listbox", -1);
+ }
+ else
+ itemObj = Tcl_NewStringObj("text", -1);
+ }
+ Tcl_ListObjAppendElement(interp,
+ Tcl_GetObjResult(interp),
+ itemObj);
+ Tcl_ListObjAppendElement(interp,
+ Tcl_GetObjResult(interp),
+ secObj);
+ }
+ return(TCL_OK);
+ }
+ else if(!strcmp(s1, "rawsig")){
+
+ if(ps_global->VAR_LITERAL_SIG){
+ char *cstring_version, *sig, *line;
+ int i, nSig;
+ Tcl_Obj **objSig;
+
+ tmp_20k_buf[0] = '\0';
+ Tcl_ListObjGetElements(interp, objv[2], &nSig, &objSig);
+ for(i = 0; i < nSig && i < SIG_MAX_LINES; i++)
+ if((line = Tcl_GetStringFromObj(objSig[i], NULL)) != NULL)
+ snprintf(tmp_20k_buf + strlen(tmp_20k_buf), SIZEOF_20KBUF - strlen(tmp_20k_buf), "%.*s\n", SIG_MAX_COLS, line);
+
+ sig = cpystr(tmp_20k_buf);
+
+ if((cstring_version = string_to_cstring(sig)) != NULL){
+ set_variable(V_LITERAL_SIG, cstring_version, 0, 0, Main);
+ fs_give((void **)&cstring_version);
+ }
+
+ fs_give((void **) &sig);
+ return(TCL_OK);
+ }
+ else
+ return(peWriteSig(interp, ps_global->VAR_SIGNATURE_FILE,
+ &((Tcl_Obj **)objv)[2]));
+ }
+ else if(!strcmp(s1, "statmsg")){
+ char *msg;
+
+ /*
+ * CMD: statmsg
+ *
+ * ARGS: msg - text to set
+ *
+ * Returns: nothing, but with global status message
+ * buf set to given msg
+ *
+ */
+ if((msg = Tcl_GetStringFromObj(objv[2], NULL)) != NULL)
+ sml_addmsg(0, msg);
+
+ return(TCL_OK);
+ }
+ else if(!strcmp(s1, "mode")){
+ char *mode;
+ int rv = 0;
+
+ /*
+ * CMD: mode
+ *
+ * ARGS: <mode>
+ *
+ * Returns: return value of given binary mode
+ *
+ */
+ if((mode = Tcl_GetStringFromObj(objv[2], NULL)) != NULL){
+ if(!strcmp(mode, "full-header-mode"))
+ rv = ps_global->full_header;
+ }
+
+ Tcl_SetResult(interp, int2string(rv), TCL_VOLATILE);
+ return(TCL_OK);
+ }
+ else if(!strcmp(s1, "indexlines")){
+ int n;
+ char *p;
+
+ if(Tcl_GetIntFromObj(interp, objv[2], &n) == TCL_OK){
+ set_variable(V_WP_INDEXLINES, p = int2string(n), 0, 0, Main);
+ Tcl_SetResult(interp, p, TCL_VOLATILE);
+ }
+ return(TCL_OK);
+ }
+ else if(!strcmp(s1, "aggtabstate")){
+ int n;
+ char *p;
+
+ if(Tcl_GetIntFromObj(interp, objv[2], &n) == TCL_OK){
+ set_variable(V_WP_AGGSTATE, p = int2string(n), 0, 0, Main);
+ Tcl_SetResult(interp, p, TCL_VOLATILE);
+ }
+ return(TCL_OK);
+ }
+ else if(!strcmp(s1, "alpinestate")){
+ char *wps, *p, *q, *twps = NULL;
+ int dollars = 0;
+
+ if((wps = Tcl_GetStringFromObj(objv[2], NULL)) != NULL){
+ for(p = wps; *p; p++)
+ if(*p == '$')
+ dollars++;
+
+ if(dollars){
+ twps = (char *) fs_get(((p - wps) + (dollars + 1)) * sizeof(char));
+ p = wps;
+ q = twps;
+ do{
+ if(*p == '$')
+ *q++ = '\\';
+ }
+ while((*q++ = *p++) != '\0');
+ }
+
+ set_variable(V_WP_STATE, twps ? twps : wps, 0, 1, Main);
+ Tcl_SetResult(interp, wps, TCL_VOLATILE);
+ if(twps)
+ fs_give((void **) &twps);
+ }
+
+ return(TCL_OK);
+ }
+ else if(!strcmp(s1, "set")){
+ Tcl_Obj *rObj;
+
+ if((rObj = Tcl_ObjGetVar2(interp, objv[2], NULL, TCL_LEAVE_ERR_MSG)) != NULL){
+ Tcl_SetObjResult(interp, rObj);
+ return(TCL_OK);
+ }
+ else
+ return(TCL_ERROR);
+ }
+ else if(!strcmp(s1, "unset")){
+ char *varname;
+
+ return((varname = Tcl_GetStringFromObj(objv[2], NULL)) ? Tcl_UnsetVar2(interp, varname, NULL, TCL_LEAVE_ERR_MSG) : TCL_ERROR);
+ }
+ }
+ else if(objc == 4){
+ if(!strcmp(s1, "feature")){
+ char *featurename;
+ int i, set, wasset = 0;
+ FEATURE_S *feature;
+
+ /*
+ * CMD: feature
+ *
+ * ARGS: featurename -
+ * value - new value to assign flag
+ *
+ * Returns: 1 if named feature set, 0 otherwise
+ *
+ */
+ if((featurename = Tcl_GetStringFromObj(objv[2], NULL))
+ && Tcl_GetIntFromObj(interp, objv[3], &set) != TCL_ERROR)
+ for(i = 0; (feature = feature_list(i)); i++)
+ if(!strucmp(featurename, feature->name)){
+ if(set != F_ON(feature->id, ps_global)){
+ toggle_feature(ps_global,
+ &ps_global->vars[V_FEATURE_LIST],
+ feature, TRUE, Main);
+
+ if(ps_global->prc)
+ ps_global->prc->outstanding_pinerc_changes = 1;
+ }
+
+ break;
+ }
+
+ Tcl_SetResult(interp, int2string(wasset), TCL_VOLATILE);
+ return(TCL_OK);
+ }
+ else if(!strucmp(s1, "help")){
+ HelpType text;
+ int i;
+ char **help_text, **ptext, *helpname, tmperrmsg[256],
+ *function;
+ Tcl_Obj *itemObj;
+ struct variable *vtmp;
+ FEATURE_S *ftmp;
+
+ if(!(helpname = Tcl_GetStringFromObj(objv[2], NULL))){
+ Tcl_SetResult(interp,
+ "Can't Tcl_GetStringFromObj for helpname",
+ TCL_VOLATILE);
+ return(TCL_ERROR);
+ }
+ if(!(function = Tcl_GetStringFromObj(objv[3], NULL))){
+ Tcl_SetResult(interp,
+ "Can't Tcl_GetStringFromObj for function",
+ TCL_VOLATILE);
+ return(TCL_ERROR);
+ }
+ if(strucmp(function, "plain") == 0){
+ if((text = help_name2section(helpname, strlen(helpname)))
+ == NO_HELP)
+ return(TCL_OK);
+ }
+ else if(strucmp(function, "variable") == 0){
+ for(vtmp = ps_global->vars;
+ vtmp->name && strucmp(vtmp->name, helpname);
+ vtmp++);
+ if(!vtmp->name) {
+ snprintf(tmperrmsg, sizeof(tmperrmsg), "Can't find variable named %s",
+ strlen(helpname) < 200 ? helpname : "");
+ Tcl_SetResult(interp, tmperrmsg, TCL_VOLATILE);
+ return(TCL_ERROR);
+ }
+ text = config_help(vtmp - ps_global->vars, 0);
+ if(text == NO_HELP)
+ return(TCL_OK);
+ }
+ else if(strucmp(function, "feature") == 0){
+ for(i = 0; (ftmp = feature_list(i)); i++){
+ if(!strucmp(helpname, ftmp->name)){
+ text = ftmp->help;
+ break;
+ }
+ }
+ if(!ftmp || text == NO_HELP){
+ return(TCL_OK);
+ }
+ }
+ else {
+ snprintf(tmperrmsg, sizeof(tmperrmsg), "Invalid function: %s",
+ strlen(helpname) < 200 ? function : "");
+ Tcl_SetResult(interp, tmperrmsg, TCL_VOLATILE);
+ return(TCL_ERROR);
+ }
+ /* assumption here is that HelpType is char ** */
+ help_text = text;
+ for(ptext = help_text; *ptext; ptext++){
+ itemObj = Tcl_NewStringObj(*ptext, -1);
+ Tcl_ListObjAppendElement(interp,
+ Tcl_GetObjResult(interp),
+ itemObj);
+ }
+ return(TCL_OK);
+ }
+ else if(!strcmp(s1, "varset")){
+ char *varname, **tmpstrlist, *line;
+ struct variable *vtmp;
+ Tcl_Obj **objVal;
+ int i, numlistvals = 0, strlistpos;
+
+ if((varname = Tcl_GetStringFromObj(objv[2], NULL))
+ && (Tcl_ListObjGetElements(interp, objv[3], &numlistvals,
+ &objVal) == TCL_OK)){
+ for(vtmp = ps_global->vars;
+ vtmp->name && strucmp(vtmp->name, varname);
+ vtmp++);
+ if(!vtmp->name){
+ return(TCL_ERROR);
+ }
+ else{
+ /* found the variable */
+ if(vtmp->is_list){
+ for(i = 0; vtmp->main_user_val.l && vtmp->main_user_val.l[i]; i++)
+ fs_give((void **)&vtmp->main_user_val.l[i]);
+ if(vtmp->main_user_val.l)
+ fs_give((void **)&vtmp->main_user_val.l);
+ if(numlistvals > 0){
+ tmpstrlist = (char **)fs_get((numlistvals + 1) * sizeof(char *));
+ for(i = 0, strlistpos = 0; i < numlistvals; i++){
+ if((line = Tcl_GetStringFromObj(objVal[i], 0)) != NULL){
+ removing_leading_and_trailing_white_space(line);
+ if(*line)
+ tmpstrlist[strlistpos++] = cpystr(line);
+ }
+ }
+ tmpstrlist[strlistpos] = NULL;
+ vtmp->main_user_val.l = (char **)fs_get((strlistpos+1) *
+ sizeof(char *));
+ for(i = 0; i <= strlistpos; i++)
+ vtmp->main_user_val.l[i] = tmpstrlist[i];
+ fs_give((void **)&tmpstrlist);
+ }
+ set_current_val(vtmp, FALSE, FALSE);
+ return(TCL_OK);
+ }
+ else{
+ if((line = Tcl_GetStringFromObj(objVal[0], NULL)) != NULL){
+ if(strucmp(vtmp->name, "reply-indent-string"))
+ removing_leading_and_trailing_white_space(line);
+ if(vtmp->main_user_val.p)
+ fs_give((void **)&vtmp->main_user_val.p);
+ if(*line)
+ vtmp->main_user_val.p = cpystr(line);
+ set_current_val(vtmp, FALSE, FALSE);
+ return(TCL_OK);
+ }
+ }
+ }
+ }
+ return(TCL_ERROR);
+ }
+ else if(!strcmp(s1, "mode")){
+ char *mode;
+ int value, rv = 0;
+
+ /*
+ * CMD: mode
+ *
+ * ARGS: <mode> <value>
+ *
+ * Returns: old value of binary mode we were told to set
+ *
+ */
+ if((mode = Tcl_GetStringFromObj(objv[2], NULL))
+ && Tcl_GetIntFromObj(interp, objv[3], &value) != TCL_ERROR){
+ if(!strcmp(mode, "full-header-mode")){
+ rv = ps_global->full_header;
+ ps_global->full_header = value;
+ }
+ }
+
+ Tcl_SetResult(interp, int2string(rv), TCL_VOLATILE);
+ return(TCL_OK);
+ }
+ else if(!strcmp(s1, "set")){
+ Tcl_Obj *rObj;
+
+ if((rObj = Tcl_ObjSetVar2(interp, objv[2], NULL, objv[3], TCL_LEAVE_ERR_MSG)) != NULL){
+ Tcl_SetObjResult(interp, rObj);
+ return(TCL_OK);
+ }
+ else
+ return(TCL_ERROR);
+ }
+ }
+ else
+ err = "PEInfo: Too many arguments";
+ }
+ }
+
+ Tcl_SetResult(interp, err, TCL_STATIC);
+ return(TCL_ERROR);
+}
+
+
+/*
+ * PEConfigCmd - edit various alpine config variables
+ *
+ * The goal here is to remember what's changed, but not write to pinerc
+ * until the user's actually chosen to save.
+ */
+int
+PEConfigCmd(ClientData clientData, Tcl_Interp *interp, int objc, Tcl_Obj *CONST objv[])
+{
+ char *err = "Unknown PEConfig request";
+ char *s1;
+
+ dprint((2, "PEConfigCmd"));
+
+ if(objc == 1){
+ Tcl_WrongNumArgs(interp, 1, objv, "cmd ?args?");
+ Tcl_SetResult(interp, err, TCL_STATIC);
+ return(TCL_ERROR);
+ }
+ s1 = Tcl_GetStringFromObj(objv[1], NULL);
+
+ if(s1){
+ if(!strcmp(s1, "colorset")){
+ char *varname, *fghex, *bghex;
+ char tvname[256], asciicolor[256];
+ struct variable *vtmp;
+ Tcl_Obj **cObj;
+ int cObjc;
+ SPEC_COLOR_S *hcolors, *thc;
+
+ if(!(varname = Tcl_GetStringFromObj(objv[2], NULL))){
+ Tcl_SetResult(interp, "colorset: can't read variable name", TCL_STATIC);
+ return(TCL_ERROR);
+ }
+
+ if(!strcmp(varname, "viewer-hdr-colors")){
+ char *newhdr = NULL, *newpat = NULL, *utype;
+ int hindex, i;
+
+ if(objc < 5){
+ Tcl_SetResult(interp, "colorset: too few view-hdr args", TCL_STATIC);
+ return(TCL_ERROR);
+ }
+
+ if(ps_global->vars[V_VIEW_HDR_COLORS].is_changed_val)
+ hcolors = spec_colors_from_varlist(ps_global->vars[V_VIEW_HDR_COLORS].changed_val.l, 0);
+ else
+ hcolors = spec_colors_from_varlist(ps_global->VAR_VIEW_HDR_COLORS, 0);
+ if(!(utype = Tcl_GetStringFromObj(objv[3], NULL))){
+ Tcl_SetResult(interp, "colorset: can't read operation", TCL_STATIC);
+ return(TCL_ERROR);
+ }
+
+ if(!strcmp(utype, "delete")){
+ if(!hcolors){
+ Tcl_SetResult(interp, "colorset: no viewer-hdrs to delete", TCL_STATIC);
+ return(TCL_ERROR);
+ }
+
+ if(Tcl_GetIntFromObj(interp, objv[4], &hindex) == TCL_ERROR){
+ Tcl_SetResult(interp, "colorset: can't read index", TCL_STATIC);
+ return(TCL_ERROR);
+ }
+
+ if(hindex == 0){
+ thc = hcolors;
+ hcolors = hcolors->next;
+ thc->next = NULL;
+ free_spec_colors(&thc);
+ }
+ else{
+ /* zero based */
+ for(thc = hcolors, i = 1; thc && i < hindex; thc = thc->next, i++)
+ ;
+
+ if(thc && thc->next){
+ SPEC_COLOR_S *thc2 = thc->next;
+
+ thc->next = thc2->next;
+ thc2->next = NULL;
+ free_spec_colors(&thc2);
+ }
+ else{
+ Tcl_SetResult(interp, "colorset: invalid index", TCL_STATIC);
+ return(TCL_ERROR);
+ }
+ }
+ }
+ else if(!strcmp(utype, "add")){
+ if(objc != 6){
+ Tcl_SetResult(interp, "colorset: wrong number of view-hdr add args", TCL_STATIC);
+ return(TCL_ERROR);
+ }
+
+ if(Tcl_ListObjGetElements(interp, objv[4], &cObjc, &cObj) != TCL_OK)
+ return (TCL_ERROR);
+
+ if(cObjc != 2){
+ Tcl_SetResult(interp, "colorset: wrong number of hdrs for view-hdr add", TCL_STATIC);
+ return(TCL_ERROR);
+ }
+
+ newhdr = Tcl_GetStringFromObj(cObj[0], NULL);
+ newpat = Tcl_GetStringFromObj(cObj[1], NULL);
+ if(Tcl_ListObjGetElements(interp, objv[5], &cObjc, &cObj) != TCL_OK)
+ return (TCL_ERROR);
+
+ if(cObjc != 2){
+ Tcl_SetResult(interp, "colorset: wrong number of colors for view-hdr add", TCL_STATIC);
+ return(TCL_ERROR);
+ }
+
+ fghex = Tcl_GetStringFromObj(cObj[0], NULL);
+ bghex = Tcl_GetStringFromObj(cObj[1], NULL);
+ if(newhdr && newpat && fghex && bghex){
+ SPEC_COLOR_S **hcp;
+
+ for(hcp = &hcolors; *hcp != NULL; hcp = &(*hcp)->next)
+ ;
+
+ *hcp = (SPEC_COLOR_S *)fs_get(sizeof(SPEC_COLOR_S));
+ (*hcp)->inherit = 0;
+ (*hcp)->spec = cpystr(newhdr);
+ (*hcp)->fg = cpystr((ascii_colorstr(asciicolor, fghex) == 0) ? asciicolor : "black");
+ (*hcp)->bg = cpystr((ascii_colorstr(asciicolor, bghex) == 0) ? asciicolor : "white");
+
+ if(newpat && *newpat)
+ (*hcp)->val = string_to_pattern(newpat);
+ else
+ (*hcp)->val = NULL;
+
+ (*hcp)->next = NULL;
+ }
+ else{
+ Tcl_SetResult(interp, "colorset: invalid args for view-hdr add", TCL_STATIC);
+ return(TCL_ERROR);
+ }
+ }
+ else if(!strcmp(utype, "update")){
+ if(objc != 6){
+ Tcl_SetResult(interp, "colorset: wrong number of view-hdr update args", TCL_STATIC);
+ return(TCL_ERROR);
+ }
+
+ if(!(Tcl_ListObjGetElements(interp, objv[4], &cObjc, &cObj) == TCL_OK
+ && cObjc == 3
+ && Tcl_GetIntFromObj(interp, cObj[0], &hindex) == TCL_OK
+ && (newhdr = Tcl_GetStringFromObj(cObj[1], NULL))
+ && (newpat = Tcl_GetStringFromObj(cObj[2], NULL)))){
+ Tcl_SetResult(interp, "colorset: view-hdr update can't read index or header", TCL_STATIC);
+ return (TCL_ERROR);
+ }
+
+ if(!(Tcl_ListObjGetElements(interp, objv[5], &cObjc, &cObj) == TCL_OK
+ && cObjc == 2
+ && (fghex = Tcl_GetStringFromObj(cObj[0], NULL))
+ && (bghex = Tcl_GetStringFromObj(cObj[1], NULL)))){
+ Tcl_SetResult(interp, "colorset: view-hdr update can't read colors", TCL_STATIC);
+ return (TCL_ERROR);
+ }
+
+ for(thc = hcolors, i = 0; thc && i < hindex; thc = thc->next, i++)
+ ;
+
+ if(!thc){
+ Tcl_SetResult(interp, "colorset: view-hdr update invalid index", TCL_STATIC);
+ return (TCL_ERROR);
+ }
+
+ if(thc->spec)
+ fs_give((void **)&thc->spec);
+
+ thc->spec = cpystr(newhdr);
+ if(ascii_colorstr(asciicolor, fghex) == 0) {
+ if(thc->fg)
+ fs_give((void **)&thc->fg);
+
+ thc->fg = cpystr(asciicolor);
+ }
+ else{
+ snprintf(tmp_20k_buf, SIZEOF_20KBUF, "colorset: invalid foreground color value %.100s", fghex);
+ Tcl_SetResult(interp, tmp_20k_buf, TCL_VOLATILE);
+ return(TCL_ERROR);
+ }
+
+ if(ascii_colorstr(asciicolor, bghex) == 0) {
+ if(thc->bg)
+ fs_give((void **)&thc->bg);
+
+ thc->bg = cpystr(asciicolor);
+ }
+ else{
+ snprintf(tmp_20k_buf, SIZEOF_20KBUF, "colorset: invalid background color value %.100s", bghex);
+ Tcl_SetResult(interp, tmp_20k_buf, TCL_VOLATILE);
+ return(TCL_ERROR);
+ }
+
+ if(thc->val)
+ fs_give((void **)&thc->val);
+
+ if(newpat && *newpat){
+ thc->val = string_to_pattern(newpat);
+ }
+ }
+ else{
+ Tcl_SetResult(interp, "colorset: unknown operation", TCL_STATIC);
+ return(TCL_ERROR);
+ }
+
+ vtmp = &ps_global->vars[V_VIEW_HDR_COLORS];
+ for(i = 0; vtmp->changed_val.l && vtmp->changed_val.l[i]; i++)
+ fs_give((void **)&vtmp->changed_val.l[i]);
+
+ if(vtmp->changed_val.l)
+ fs_give((void **)&vtmp->changed_val.l);
+
+ vtmp->changed_val.l = varlist_from_spec_colors(hcolors);
+ vtmp->is_changed_val = 1;
+ free_spec_colors(&hcolors);
+ return(TCL_OK);
+ }
+ else {
+ if(objc != 4){
+ Tcl_SetResult(interp, "colorset: Wrong number of args", TCL_STATIC);
+ return(TCL_ERROR);
+ }
+
+ if(!(Tcl_ListObjGetElements(interp, objv[3], &cObjc, &cObj) == TCL_OK
+ && cObjc == 2
+ && (fghex = Tcl_GetStringFromObj(cObj[0], NULL))
+ && (bghex = Tcl_GetStringFromObj(cObj[1], NULL)))){
+ Tcl_SetResult(interp, "colorset: Problem reading fore/back ground colors", TCL_STATIC);
+ return (TCL_ERROR);
+ }
+
+ snprintf(tvname, sizeof(tvname), "%.200s-foreground-color", varname);
+ for(vtmp = &ps_global->vars[V_NORM_FORE_COLOR];
+ vtmp->name && strucmp(vtmp->name, tvname);
+ vtmp++)
+ ;
+
+ if(!vtmp->name || vtmp->is_list){
+ snprintf(tmp_20k_buf, SIZEOF_20KBUF, "colorset: invalid background var %.100s", varname);
+ Tcl_SetResult(interp, tmp_20k_buf, TCL_VOLATILE);
+ return(TCL_ERROR);
+ }
+
+ if(ascii_colorstr(asciicolor, fghex) == 0) {
+ if(vtmp->changed_val.p)
+ fs_give((void **)&vtmp->changed_val.p);
+
+ vtmp->changed_val.p = cpystr(asciicolor);
+ vtmp->is_changed_val = 1;
+
+ /* We need to handle this in the actual config setting
+ * if(!strucmp(varname, "normal"))
+ * pico_set_fg_color(asciicolor);
+ */
+ }
+ else{
+ snprintf(tmp_20k_buf, SIZEOF_20KBUF, "colorset: invalid color value %.100s", fghex);
+ Tcl_SetResult(interp, tmp_20k_buf, TCL_VOLATILE);
+ return(TCL_ERROR);
+ }
+
+ snprintf(tvname, sizeof(tvname), "%.200s%.50s", varname, "-background-color");
+ vtmp++;
+ if((vtmp->name && strucmp(vtmp->name, tvname)) || !vtmp->name)
+ for(vtmp = &ps_global->vars[V_NORM_FORE_COLOR];
+ vtmp->name && strucmp(vtmp->name, tvname);
+ vtmp++)
+ ;
+
+ if(!vtmp->name || vtmp->is_list){
+ snprintf(tmp_20k_buf, SIZEOF_20KBUF, "colorset: invalid background var %.100s", varname);
+ Tcl_SetResult(interp, tmp_20k_buf, TCL_VOLATILE);
+ return(TCL_ERROR);
+ }
+
+ if(ascii_colorstr(asciicolor, bghex) == 0) {
+ if(vtmp->changed_val.p)
+ fs_give((void **)&vtmp->changed_val.p);
+
+ vtmp->changed_val.p = cpystr(asciicolor);
+ vtmp->is_changed_val = 1;
+ /* again, we need to handle this when we actually set the variable
+ * if(!strucmp(varname, "normal"))
+ * pico_set_bg_color(asciicolor);
+ */
+ }
+ else{
+ snprintf(tmp_20k_buf, SIZEOF_20KBUF, "colorset: invalid background color value %.100s", bghex);
+ Tcl_SetResult(interp, tmp_20k_buf, TCL_VOLATILE);
+ return(TCL_ERROR);
+ }
+
+ Tcl_SetResult(interp, "1", TCL_STATIC);
+ return(TCL_OK);
+ }
+ }
+ else if(!strcmp(s1, "ruleset")){
+ return(peRuleSet(interp, &((Tcl_Obj **)objv)[2]));
+ }
+ else if(objc == 2){
+ if(!strcmp(s1, "featuresettings")){
+ struct variable *vtmp;
+ int i;
+ FEATURE_S *feature;
+
+ vtmp = &ps_global->vars[V_FEATURE_LIST];
+ for(i = 0; (feature = feature_list(i)); i++)
+ if(feature_list_section(feature)){
+ if(vtmp->is_changed_val ? F_CH_ON(feature->id)
+ : F_ON(feature->id, ps_global)){
+ Tcl_ListObjAppendElement(interp,
+ Tcl_GetObjResult(interp),
+ Tcl_NewStringObj(feature->name, -1));
+ }
+ }
+ return(TCL_OK);
+ }
+ else if(!strcmp(s1, "rawsig")){
+ char *err = NULL, *sig = NULL, *p, *q;
+ int i;
+ struct variable *vtmp;
+
+ vtmp = &ps_global->vars[V_LITERAL_SIG];
+ if(vtmp->is_changed_val ? vtmp->changed_val.p
+ : ps_global->VAR_LITERAL_SIG){
+ char *err = NULL;
+ char **apval;
+
+ if(ps_global->restricted){
+ err = "Alpine demo can't change config file";
+ }
+ else{
+ /* BUG: no "exceptions file" support */
+ apval = (vtmp->is_changed_val ? &vtmp->changed_val.p
+ : APVAL(&ps_global->vars[V_LITERAL_SIG], Main));
+ if(apval){
+ sig = (char *) fs_get((strlen(*apval ? *apval : "") + 1) * sizeof(char));
+ sig[0] = '\0';
+ cstring_to_string(*apval, sig);
+ }
+ else
+ err = "Problem accessing configuration";
+ }
+ }
+ else if((vtmp = &ps_global->vars[V_SIGNATURE_FILE])
+ && !IS_REMOTE(vtmp->is_changed_val ? vtmp->changed_val.p
+ : ps_global->VAR_SIGNATURE_FILE))
+ snprintf(err = tmp_20k_buf, SIZEOF_20KBUF, "Non-Remote signature file: %s",
+ vtmp->is_changed_val ? (vtmp->changed_val.p
+ ? vtmp->changed_val.p : "<null>")
+ : (ps_global->VAR_SIGNATURE_FILE
+ ? ps_global->VAR_SIGNATURE_FILE : "<null>"));
+ else if(!(peTSig || (sig = simple_read_remote_file(vtmp->is_changed_val
+ ? vtmp->changed_val.p
+ : ps_global->VAR_SIGNATURE_FILE, REMOTE_SIG_SUBTYPE))))
+ err = "Can't read remote pinerc";
+
+ if(err){
+ Tcl_SetResult(interp, err, TCL_VOLATILE);
+ return(TCL_ERROR);
+ }
+
+ if(peTSig){
+ for(i = 0; peTSig[i]; i++)
+ Tcl_ListObjAppendElement(interp, Tcl_GetObjResult(interp),
+ Tcl_NewStringObj(peTSig[i],-1));
+ }
+ else {
+ for(p = sig; (q = strindex(p, '\n')); p = q + 1)
+ Tcl_ListObjAppendElement(interp, Tcl_GetObjResult(interp),
+ Tcl_NewStringObj(p, q - p));
+
+ Tcl_ListObjAppendElement(interp, Tcl_GetObjResult(interp),
+ Tcl_NewStringObj(p, -1));
+ fs_give((void **) &sig);
+ }
+ return(TCL_OK);
+ }
+ else if(!strcmp(s1, "filters")){
+ long rflags = ROLE_DO_FILTER | PAT_USE_CHANGED;
+ PAT_STATE pstate;
+ PAT_S *pat;
+
+ close_every_pattern();
+ if(any_patterns(rflags, &pstate)){
+ for(pat = first_pattern(&pstate);
+ pat;
+ pat = next_pattern(&pstate)){
+ Tcl_ListObjAppendElement(interp, Tcl_GetObjResult(interp),
+ Tcl_NewStringObj(pat->patgrp->nick, -1));
+ }
+ }
+ return(TCL_OK);
+ }
+ else if(!strcmp(s1, "scores")){
+ long rflags = ROLE_DO_SCORES | PAT_USE_CHANGED;
+ PAT_STATE pstate;
+ PAT_S *pat;
+
+ close_every_pattern();
+ if(any_patterns(rflags, &pstate)){
+ for(pat = first_pattern(&pstate);
+ pat;
+ pat = next_pattern(&pstate)){
+ Tcl_ListObjAppendElement(interp, Tcl_GetObjResult(interp),
+ Tcl_NewStringObj(pat->patgrp->nick, -1));
+ }
+ }
+ return(TCL_OK);
+ }
+ else if(!strcmp(s1, "indexcolors")){
+ long rflags = ROLE_DO_INCOLS | PAT_USE_CHANGED;
+ PAT_STATE pstate;
+ PAT_S *pat;
+
+ close_every_pattern();
+ if(any_patterns(rflags, &pstate)){
+ for(pat = first_pattern(&pstate);
+ pat;
+ pat = next_pattern(&pstate)){
+ Tcl_ListObjAppendElement(interp, Tcl_GetObjResult(interp),
+ Tcl_NewStringObj(pat->patgrp->nick, -1));
+ }
+ }
+ return(TCL_OK);
+ }
+ else if(!strcmp(s1, "collections")){
+ struct variable *vtmp;
+ int i;
+ CONTEXT_S *new_ctxt;
+
+ vtmp = &ps_global->vars[V_FOLDER_SPEC];
+ for(i = 0; (vtmp->is_changed_val
+ ? vtmp->changed_val.l && vtmp->changed_val.l[i]
+ : vtmp->current_val.l && vtmp->current_val.l[i]);
+ i++){
+ new_ctxt = new_context(vtmp->is_changed_val
+ ? vtmp->changed_val.l[i]
+ : vtmp->current_val.l[i], NULL);
+ peAppListF(interp, Tcl_GetObjResult(interp), "%s%s",
+ new_ctxt->nickname
+ ? new_ctxt->nickname
+ : (new_ctxt->server
+ ? new_ctxt->server
+ : (new_ctxt->label
+ ? new_ctxt->label
+ : "Some Collection")),
+ new_ctxt->label ? new_ctxt->label : "");
+ free_context(&new_ctxt);
+ }
+ vtmp = &ps_global->vars[V_NEWS_SPEC];
+ for(i = 0; (vtmp->is_changed_val
+ ? vtmp->changed_val.l && vtmp->changed_val.l[i]
+ : vtmp->current_val.l && vtmp->current_val.l[i]);
+ i++){
+ new_ctxt = new_context(vtmp->is_changed_val
+ ? vtmp->changed_val.l[i]
+ : vtmp->current_val.l[i], NULL);
+ peAppListF(interp, Tcl_GetObjResult(interp), "%s%s",
+ new_ctxt->nickname
+ ? new_ctxt->nickname
+ : (new_ctxt->server
+ ? new_ctxt->server
+ : (new_ctxt->label
+ ? new_ctxt->label
+ : "Some Collection")),
+ new_ctxt->label ? new_ctxt->label : "");
+ free_context(&new_ctxt);
+ }
+
+ return(TCL_OK);
+ }
+ else if(!strcmp(s1, "newconf")){
+ struct variable *vtmp;
+ int i;
+ FEATURE_S *feature;
+
+ for(vtmp = ps_global->vars; vtmp->name; vtmp++)
+ vtmp->is_changed_val = 0;
+
+ for(i = 0; (feature = feature_list(i)); i++)
+ F_CH_SET(feature->id, F_ON(feature->id, ps_global));
+
+ if(peTSig){
+ for(i = 0; peTSig[i]; i++)
+ fs_give((void **)&peTSig[i]);
+ fs_give((void **)&peTSig);
+ }
+
+ close_patterns(ROLE_DO_FILTER | ROLE_DO_INCOLS | ROLE_DO_SCORES | PAT_USE_CHANGED);
+ return(TCL_OK);
+ }
+ else if(!strcmp(s1, "saveconf")){
+ struct variable *vtmp;
+ int i, did_change = 0, def_sort_rev;
+ FEATURE_S *feature;
+
+ if(ps_global->vars[V_FEATURE_LIST].is_changed_val){
+ ps_global->vars[V_FEATURE_LIST].is_changed_val = 0;
+ for(i = 0; (feature = feature_list(i)); i++)
+ if(feature_list_section(feature)){
+ if(F_CH_ON(feature->id) != F_ON(feature->id, ps_global)){
+ did_change = 1;
+ toggle_feature(ps_global,
+ &ps_global->vars[V_FEATURE_LIST],
+ feature, TRUE, Main);
+ }
+ }
+ }
+
+ for(vtmp = ps_global->vars; vtmp->name; vtmp++){
+ if(vtmp->is_changed_val
+ && (vtmp - ps_global->vars != V_FEATURE_LIST)){
+ if(vtmp->is_list){
+ for(i = 0; vtmp->main_user_val.l
+ && vtmp->main_user_val.l[i]; i++)
+ fs_give((void **)&vtmp->main_user_val.l[i]);
+ if(vtmp->main_user_val.l)
+ fs_give((void **)&vtmp->main_user_val.l);
+ vtmp->main_user_val.l = vtmp->changed_val.l;
+ vtmp->changed_val.l = NULL;
+ }
+ else {
+ if(vtmp->main_user_val.p)
+ fs_give((void **)&vtmp->main_user_val.p);
+ vtmp->main_user_val.p = vtmp->changed_val.p;
+ vtmp->changed_val.p = NULL;
+ }
+ set_current_val(vtmp, FALSE, FALSE);
+ vtmp->is_changed_val = 0;
+ did_change = 1;
+ switch (vtmp - ps_global->vars) {
+ case V_USER_DOMAIN:
+ init_hostname(ps_global);
+ case V_FOLDER_SPEC:
+ case V_NEWS_SPEC:
+ free_contexts(&ps_global->context_list);
+ init_folders(ps_global);
+ break;
+ case V_NORM_FORE_COLOR:
+ pico_set_fg_color(vtmp->current_val.p);
+ break;
+ case V_NORM_BACK_COLOR:
+ pico_set_bg_color(vtmp->current_val.p);
+ break;
+ case V_ADDRESSBOOK:
+ case V_GLOB_ADDRBOOK:
+#ifdef ENABLE_LDAP
+ case V_LDAP_SERVERS:
+#endif
+ case V_ABOOK_FORMATS:
+ addrbook_reset();
+ case V_INDEX_FORMAT:
+ init_index_format(ps_global->VAR_INDEX_FORMAT,
+ &ps_global->index_disp_format);
+ clear_index_cache(sp_inbox_stream(), 0);
+ break;
+ case V_PAT_FILTS:
+ close_patterns(ROLE_DO_FILTER | PAT_USE_CURRENT);
+ role_process_filters();
+ break;
+ case V_PAT_INCOLS:
+ close_patterns(ROLE_DO_INCOLS | PAT_USE_CURRENT);
+ clear_index_cache(sp_inbox_stream(), 0);
+ role_process_filters();
+ break;
+ case V_PAT_SCORES:
+ close_patterns(ROLE_DO_SCORES | PAT_USE_CURRENT);
+ role_process_filters();
+ break;
+ case V_DEFAULT_FCC:
+ case V_DEFAULT_SAVE_FOLDER:
+ init_save_defaults();
+ break;
+ case V_SORT_KEY:
+ decode_sort(ps_global->VAR_SORT_KEY, &ps_global->def_sort, &def_sort_rev);
+ break;
+ case V_VIEW_HDR_COLORS :
+ set_custom_spec_colors(ps_global);
+ break;
+ case V_POST_CHAR_SET :
+ update_posting_charset(ps_global, 1);
+ break;
+ default:
+ break;
+ }
+ }
+ }
+ if(peTSig){
+ peWriteSig(interp, ps_global->VAR_SIGNATURE_FILE, NULL);
+ }
+ if(did_change){
+ if(write_pinerc(ps_global, Main, WRP_NOUSER) == 0)
+ q_status_message(SM_ORDER, 0, 3, "Configuration changes saved!");
+ }
+ return(TCL_OK);
+ }
+ else if(!strcmp(s1, "columns")){
+ Tcl_SetResult(interp, int2string(ps_global->ttyo->screen_cols), TCL_VOLATILE);
+ return(TCL_OK);
+ }
+ else if(!strcmp(s1, "indextokens")){
+ INDEX_PARSE_T *tok;
+ int i;
+
+ for(i = 0; (tok = itoken(i)) != NULL; i++)
+ if(tok->what_for & FOR_INDEX)
+ Tcl_ListObjAppendElement(interp, Tcl_GetObjResult(interp),
+ Tcl_NewStringObj(tok->name, -1));
+
+ return(TCL_OK);
+ }
+ }
+ else if(objc == 3){
+ if(!strcmp(s1, "varget")){
+ char *varname = Tcl_GetStringFromObj(objv[2], NULL);
+ struct variable *vtmp;
+ Tcl_Obj *resObj, *secObj;
+ char *input_type;
+ int is_default, i;
+ NAMEVAL_S *tmpnv;
+
+ if(varname == NULL) return(TCL_ERROR);
+
+ for(vtmp = ps_global->vars;
+ vtmp->name && strucmp(vtmp->name, varname);
+ vtmp++)
+ ;
+
+ if(!vtmp->name){
+ Tcl_SetResult(interp, err, TCL_VOLATILE);
+ return(TCL_ERROR);
+ }
+ resObj = Tcl_NewListObj(0, NULL);
+ if(vtmp->is_list){
+ if(vtmp->is_changed_val){
+ for(i = 0; vtmp->changed_val.l && vtmp->changed_val.l[i]; i++){
+ Tcl_ListObjAppendElement(interp, resObj,
+ Tcl_NewStringObj(vtmp->changed_val.l[i], -1));
+ }
+ }
+ else {
+ for(i = 0; vtmp->current_val.l && vtmp->current_val.l[i]; i++){
+ Tcl_ListObjAppendElement(interp, resObj,
+ Tcl_NewStringObj(vtmp->current_val.l[i], -1));
+ }
+ }
+ }
+ else {
+ if(vtmp->is_changed_val){
+ if(vtmp->changed_val.p)
+ Tcl_ListObjAppendElement(interp, resObj,
+ Tcl_NewStringObj(vtmp->changed_val.p[0]
+ ? vtmp->changed_val.p
+ : "\"\"", -1));
+ }
+ else {
+ if(vtmp->current_val.p)
+ Tcl_ListObjAppendElement(interp, resObj,
+ Tcl_NewStringObj(vtmp->current_val.p[0]
+ ? vtmp->current_val.p
+ : "\"\"", -1));
+ }
+ }
+ Tcl_ListObjAppendElement(interp,
+ Tcl_GetObjResult(interp),
+ resObj);
+ secObj = Tcl_NewListObj(0, NULL);
+ if(vtmp->is_list)
+ input_type = cpystr("textarea");
+ else{
+ NAMEVAL_S *(*tmpf)(int);
+ switch(vtmp - ps_global->vars){
+ case V_SAVED_MSG_NAME_RULE:
+ tmpf = save_msg_rules;
+ break;
+ case V_FCC_RULE:
+ tmpf = fcc_rules;
+ break;
+ case V_SORT_KEY:
+ tmpf = sort_key_rules;
+ break;
+ case V_AB_SORT_RULE:
+ tmpf = ab_sort_rules;
+ break;
+ case V_FLD_SORT_RULE:
+ tmpf = fld_sort_rules;
+ break;
+ case V_GOTO_DEFAULT_RULE:
+ tmpf = goto_rules;
+ break;
+ case V_INCOMING_STARTUP:
+ tmpf = incoming_startup_rules;
+ break;
+ case V_PRUNING_RULE:
+ tmpf = pruning_rules;
+ break;
+ case V_WP_INDEXHEIGHT:
+ tmpf = wp_indexheight_rules;
+ break;
+ default:
+ tmpf = NULL;
+ break;
+ }
+ if(tmpf){
+ for(i = 0; (tmpnv = (tmpf)(i)); i++){
+ if(tmpnv->shortname)
+ peAppListF(interp, secObj, "%s%s", tmpnv->name, tmpnv->shortname);
+ else
+ Tcl_ListObjAppendElement(interp, secObj,
+ Tcl_NewStringObj(tmpnv->name, -1));
+ }
+ input_type = cpystr("listbox");
+ }
+ else
+ input_type = cpystr("text");
+ }
+ Tcl_ListObjAppendElement(interp,
+ Tcl_GetObjResult(interp),
+ Tcl_NewStringObj(input_type, -1));
+ Tcl_ListObjAppendElement(interp,
+ Tcl_GetObjResult(interp),
+ secObj);
+ if(vtmp->is_list)
+ is_default = !vtmp->is_changed_val && !vtmp->main_user_val.l;
+ else
+ is_default = !vtmp->is_changed_val && !vtmp->main_user_val.p;
+ Tcl_ListObjAppendElement(interp, Tcl_GetObjResult(interp),
+ Tcl_NewIntObj(is_default));
+ Tcl_ListObjAppendElement(interp, Tcl_GetObjResult(interp),
+ Tcl_NewIntObj(vtmp->is_fixed));
+ return(TCL_OK);
+ }
+ else if(!strcmp(s1, "filtextended")){
+ int fl, i;
+ long rflags = ROLE_DO_FILTER | PAT_USE_CHANGED;
+ PAT_STATE pstate;
+ PAT_S *pat;
+ Tcl_Obj *resObj = NULL, *tObj = NULL;
+
+ if(Tcl_GetIntFromObj(interp, objv[2], &fl) == TCL_ERROR)
+ return(TCL_ERROR);
+
+ close_every_pattern();
+ if(any_patterns(rflags, &pstate)){
+ for(pat = first_pattern(&pstate), i = 0;
+ pat && i != fl;
+ pat = next_pattern(&pstate), i++);
+
+ if(!pat)
+ return(TCL_ERROR);
+
+ /* append the pattern ID */
+ tObj = Tcl_NewListObj(0, NULL);
+ Tcl_ListObjAppendElement(interp, tObj, Tcl_NewStringObj("id", -1));
+ pePatAppendID(interp, tObj, pat);
+ Tcl_ListObjAppendElement(interp, Tcl_GetObjResult(interp), tObj);
+
+ /* append the pattern */
+ tObj = Tcl_NewListObj(0, NULL);
+ Tcl_ListObjAppendElement(interp, tObj, Tcl_NewStringObj("pattern", -1));
+ pePatAppendPattern(interp, tObj, pat);
+ Tcl_ListObjAppendElement(interp, Tcl_GetObjResult(interp), tObj);
+
+ /* now append the filter action */
+ resObj = Tcl_NewListObj(0, NULL);
+ peAppListF(interp, resObj, "%s%i", "kill", pat->action->folder ? 0 : 1);
+ peAppListF(interp, resObj, "%s%p", "folder", pat->action->folder);
+ peAppListF(interp, resObj, "%s%i", "move_only_if_not_deleted",
+ pat->action->move_only_if_not_deleted);
+ tObj = Tcl_NewListObj(0, NULL);
+ Tcl_ListObjAppendElement(interp, tObj, Tcl_NewStringObj("filtaction", -1));
+ Tcl_ListObjAppendElement(interp, tObj, resObj);
+ Tcl_ListObjAppendElement(interp, Tcl_GetObjResult(interp), tObj);
+ }
+ else return(TCL_ERROR);
+
+ return(TCL_OK);
+ }
+ else if(!strcmp(s1, "indexcolorextended")){
+ int fl, i;
+ long rflags = ROLE_DO_INCOLS | PAT_USE_CHANGED;
+ PAT_STATE pstate;
+ PAT_S *pat;
+ Tcl_Obj *resObj = NULL, *tObj = NULL;
+
+ if(Tcl_GetIntFromObj(interp, objv[2], &fl) == TCL_ERROR)
+ return(TCL_ERROR);
+
+ close_every_pattern();
+ if(any_patterns(rflags, &pstate)){
+ for(pat = first_pattern(&pstate), i = 0;
+ pat && i != fl;
+ pat = next_pattern(&pstate), i++);
+
+ if(!pat)
+ return(TCL_ERROR);
+
+ /* append the pattern ID */
+ tObj = Tcl_NewListObj(0, NULL);
+ Tcl_ListObjAppendElement(interp, tObj, Tcl_NewStringObj("id", -1));
+ pePatAppendID(interp, tObj, pat);
+ Tcl_ListObjAppendElement(interp, Tcl_GetObjResult(interp), tObj);
+
+ /* append the pattern */
+ tObj = Tcl_NewListObj(0, NULL);
+ Tcl_ListObjAppendElement(interp, tObj, Tcl_NewStringObj("pattern", -1));
+ pePatAppendPattern(interp, tObj, pat);
+ Tcl_ListObjAppendElement(interp, Tcl_GetObjResult(interp), tObj);
+
+ /* now append the pattern colors */
+ resObj = Tcl_NewListObj(0, NULL);
+ tObj = Tcl_NewListObj(0, NULL);
+ Tcl_ListObjAppendElement(interp, tObj, Tcl_NewStringObj("indexcolor", -1));
+ if(pat->action->is_a_incol){
+ char *color;
+ Tcl_Obj *colObj = Tcl_NewListObj(0, NULL);
+
+ if(!(pat->action->incol
+ && pat->action->incol->fg
+ && pat->action->incol->fg[0]
+ && (color = color_to_asciirgb(pat->action->incol->fg))
+ && (color = peColorStr(color,tmp_20k_buf))))
+ color = "";
+
+ Tcl_ListObjAppendElement(interp, colObj, Tcl_NewStringObj(color, -1));
+
+ if(!(pat->action->incol
+ && pat->action->incol->bg
+ && pat->action->incol->bg[0]
+ && (color = color_to_asciirgb(pat->action->incol->bg))
+ && (color = peColorStr(color,tmp_20k_buf))))
+ color = "";
+
+ Tcl_ListObjAppendElement(interp, colObj, Tcl_NewStringObj(color, -1));
+ Tcl_ListObjAppendElement(interp, tObj, colObj);
+ }
+ Tcl_ListObjAppendElement(interp, resObj, tObj);
+
+ tObj = Tcl_NewListObj(0, NULL);
+ Tcl_ListObjAppendElement(interp, tObj, Tcl_NewStringObj("indexcolors", -1));
+ Tcl_ListObjAppendElement(interp, tObj, resObj);
+ Tcl_ListObjAppendElement(interp, Tcl_GetObjResult(interp), tObj);
+ }
+ else return(TCL_ERROR);
+
+ return(TCL_OK);
+ }
+ else if(!strcmp(s1, "scoreextended")){
+ int fl, i;
+ long rflags = ROLE_DO_SCORES | PAT_USE_CHANGED;
+ char *hdr = NULL;
+ PAT_STATE pstate;
+ PAT_S *pat;
+ Tcl_Obj *resObj = NULL, *tObj = NULL;
+
+ if(Tcl_GetIntFromObj(interp, objv[2], &fl) == TCL_ERROR)
+ return(TCL_ERROR);
+
+ close_every_pattern();
+ if(any_patterns(rflags, &pstate)){
+ for(pat = first_pattern(&pstate), i = 0;
+ pat && i != fl;
+ pat = next_pattern(&pstate), i++);
+
+ if(!pat)
+ return(TCL_ERROR);
+
+ /* append the pattern ID */
+ tObj = Tcl_NewListObj(0, NULL);
+ Tcl_ListObjAppendElement(interp, tObj, Tcl_NewStringObj("id", -1));
+ pePatAppendID(interp, tObj, pat);
+ Tcl_ListObjAppendElement(interp, Tcl_GetObjResult(interp), tObj);
+
+ /* append the pattern */
+ tObj = Tcl_NewListObj(0, NULL);
+ Tcl_ListObjAppendElement(interp, tObj, Tcl_NewStringObj("pattern", -1));
+ pePatAppendPattern(interp, tObj, pat);
+ Tcl_ListObjAppendElement(interp, Tcl_GetObjResult(interp), tObj);
+
+ /* now append the filter action */
+ resObj = Tcl_NewListObj(0, NULL);
+ peAppListF(interp, resObj, "%s%l", "scoreval", pat->action->scoreval);
+ if(pat->action->scorevalhdrtok)
+ hdr = hdrtok_to_stringform(pat->action->scorevalhdrtok);
+
+ peAppListF(interp, resObj, "%s%s", "scorehdr", hdr ? hdr : "");
+
+ if(hdr)
+ fs_give((void **) &hdr);
+
+ tObj = Tcl_NewListObj(0, NULL);
+ Tcl_ListObjAppendElement(interp, tObj, Tcl_NewStringObj("scores", -1));
+ Tcl_ListObjAppendElement(interp, tObj, resObj);
+ Tcl_ListObjAppendElement(interp, Tcl_GetObjResult(interp), tObj);
+ }
+ else return(TCL_ERROR);
+
+ return(TCL_OK);
+ }
+ else if(!strcmp(s1, "clextended")){
+ int cl, i, j = 0, in_folder_spec = 0;
+ struct variable *vtmp;
+ char tpath[MAILTMPLEN], *p;
+ CONTEXT_S *ctxt;
+
+ vtmp = &ps_global->vars[V_FOLDER_SPEC];
+ if(Tcl_GetIntFromObj(interp, objv[2], &cl) == TCL_ERROR)
+ return(TCL_ERROR);
+ for(i = 0; i < cl && (vtmp->is_changed_val
+ ? (vtmp->changed_val.l
+ && vtmp->changed_val.l[i])
+ : (vtmp->current_val.l
+ && vtmp->current_val.l[i])); i++);
+ if(i == cl && (vtmp->is_changed_val
+ ? vtmp->changed_val.l && vtmp->changed_val.l[i]
+ : vtmp->current_val.l && vtmp->current_val.l[i]))
+ in_folder_spec = 1;
+ else {
+ vtmp = &ps_global->vars[V_NEWS_SPEC];
+ for(j = 0; i + j < cl && (vtmp->is_changed_val
+ ? (vtmp->changed_val.l
+ && vtmp->changed_val.l[j])
+ : (vtmp->current_val.l
+ && vtmp->current_val.l[j])); j++);
+ }
+ if(in_folder_spec || (i + j == cl && (vtmp->is_changed_val
+ ? vtmp->changed_val.l && vtmp->changed_val.l[j]
+ : vtmp->current_val.l && vtmp->current_val.l[j]))){
+ ctxt = new_context(vtmp->is_changed_val ? vtmp->changed_val.l[in_folder_spec ? i : j]
+ : vtmp->current_val.l[in_folder_spec ? i : j], NULL);
+ Tcl_ListObjAppendElement(interp,
+ Tcl_GetObjResult(interp),
+ Tcl_NewStringObj(ctxt->nickname ? ctxt->nickname : "", -1));
+ Tcl_ListObjAppendElement(interp,
+ Tcl_GetObjResult(interp),
+ Tcl_NewStringObj(ctxt->label ? ctxt->label : "", -1));
+ Tcl_ListObjAppendElement(interp,
+ Tcl_GetObjResult(interp),
+ Tcl_NewStringObj(ctxt->server ? ctxt->server : "", -1));
+ tpath[0] = '\0';
+ if(ctxt->context){
+ strncpy(tpath, (ctxt->context[0] == '{'
+ && (p = strchr(ctxt->context, '}')))
+ ? ++p
+ : ctxt->context, sizeof(tpath));
+ tpath[sizeof(tpath)-1] = '\0';
+ if((p = strstr(tpath, "%s")) != NULL)
+ *p = '\0';
+ }
+ Tcl_ListObjAppendElement(interp,
+ Tcl_GetObjResult(interp),
+ Tcl_NewStringObj(tpath, -1));
+ Tcl_ListObjAppendElement(interp,
+ Tcl_GetObjResult(interp),
+ Tcl_NewStringObj(ctxt->dir && ctxt->dir->view.user
+ ? ctxt->dir->view.user : "", -1));
+ free_context(&ctxt);
+
+ return(TCL_OK);
+ }
+ else
+ return(TCL_ERROR);
+ }
+ else if(!strcmp(s1, "rawsig")){
+ struct variable *vtmp;
+ char *cstring_version, *sig, *line;
+ int i, nSig;
+ Tcl_Obj **objSig;
+
+ vtmp = &ps_global->vars[V_LITERAL_SIG];
+ if(vtmp->is_changed_val ? vtmp->changed_val.p
+ : ps_global->VAR_LITERAL_SIG){
+
+ tmp_20k_buf[0] = '\0';
+ Tcl_ListObjGetElements(interp, objv[2], &nSig, &objSig);
+ for(i = 0; i < nSig && i < SIG_MAX_LINES; i++)
+ if((line = Tcl_GetStringFromObj(objSig[i], NULL)) != NULL)
+ snprintf(tmp_20k_buf + strlen(tmp_20k_buf), SIZEOF_20KBUF - strlen(tmp_20k_buf), "%.*s\n", SIG_MAX_COLS, line);
+
+ sig = cpystr(tmp_20k_buf);
+
+ if((cstring_version = string_to_cstring(sig)) != NULL){
+ if(vtmp->changed_val.p)
+ fs_give((void **)&vtmp->changed_val.p);
+ vtmp->is_changed_val = 1;
+ vtmp->changed_val.p = cstring_version;
+ }
+
+ fs_give((void **) &sig);
+ return(TCL_OK);
+ }
+ else {
+ if(peTSig){
+ for(i = 0; peTSig[i]; i++)
+ fs_give((void **)&peTSig[i]);
+ fs_give((void **)&peTSig);
+ }
+ Tcl_ListObjGetElements(interp, objv[2], &nSig, &objSig);
+ peTSig = (char **)fs_get(sizeof(char)*(nSig + 1));
+ for(i = 0; i < nSig; i++){
+ line = Tcl_GetStringFromObj(objSig[i], NULL);
+ peTSig[i] = cpystr(line ? line : "");
+ }
+ peTSig[i] = NULL;
+ return(TCL_OK);
+ }
+ }
+ else if(!strcmp(s1, "colorget")){
+ char *varname;
+ char tvname[256], hexcolor[256];
+ struct variable *vtmp;
+ if(!(varname = Tcl_GetStringFromObj(objv[2], NULL))){
+ return(TCL_ERROR);
+ }
+ if(strcmp("viewer-hdr-colors", varname) == 0){
+ SPEC_COLOR_S *hcolors, *thc;
+ Tcl_Obj *resObj;
+ char hexcolor[256], *tstr = NULL;
+
+ if(ps_global->vars[V_VIEW_HDR_COLORS].is_changed_val)
+ hcolors = spec_colors_from_varlist(ps_global->vars[V_VIEW_HDR_COLORS].changed_val.l, 0);
+ else
+ hcolors = spec_colors_from_varlist(ps_global->VAR_VIEW_HDR_COLORS, 0);
+ for(thc = hcolors; thc; thc = thc->next){
+ resObj = Tcl_NewListObj(0,NULL);
+ Tcl_ListObjAppendElement(interp, resObj,
+ Tcl_NewStringObj(thc->spec, -1));
+ hex_colorstr(hexcolor, thc->fg);
+ Tcl_ListObjAppendElement(interp, resObj,
+ Tcl_NewStringObj(hexcolor, -1));
+ hex_colorstr(hexcolor, thc->bg);
+ Tcl_ListObjAppendElement(interp, resObj,
+ Tcl_NewStringObj(hexcolor, -1));
+ Tcl_ListObjAppendElement(interp, resObj,
+ Tcl_NewStringObj(thc->val
+ ? tstr = pattern_to_string(thc->val)
+ : "", -1));
+ if(tstr) fs_give((void **)&tstr);
+ Tcl_ListObjAppendElement(interp, Tcl_GetObjResult(interp),
+ resObj);
+ }
+ fs_give((void **)&hcolors);
+ return(TCL_OK);
+ }
+ else {
+ char *colorp;
+
+ snprintf(tvname, sizeof(tvname), "%.200s%.50s", varname, "-foreground-color");
+
+ for(vtmp = &ps_global->vars[V_NORM_FORE_COLOR];
+ vtmp->name && strucmp(vtmp->name, tvname);
+ vtmp++)
+ ;
+
+ if(!vtmp->name) return(TCL_ERROR);
+ if(vtmp->is_list) return(TCL_ERROR);
+
+ colorp = (vtmp->is_changed_val && vtmp->changed_val.p)
+ ? vtmp->changed_val.p
+ : (vtmp->current_val.p) ? vtmp->current_val.p
+ : vtmp->global_val.p;
+
+ if(colorp){
+ hex_colorstr(hexcolor, colorp);
+ Tcl_ListObjAppendElement(interp, Tcl_GetObjResult(interp),
+ Tcl_NewStringObj(hexcolor, -1));
+ }
+ else
+ Tcl_ListObjAppendElement(interp, Tcl_GetObjResult(interp),
+ Tcl_NewStringObj("", -1));
+
+ snprintf(tvname, sizeof(tvname), "%.200s%.50s", varname, "-background-color");
+ vtmp++;
+ if((vtmp->name && strucmp(vtmp->name, tvname)) || !vtmp->name)
+ for(vtmp = &ps_global->vars[V_NORM_FORE_COLOR];
+ vtmp->name && strucmp(vtmp->name, tvname);
+ vtmp++)
+ ;
+
+ if(!vtmp->name) return(TCL_ERROR);
+ if(vtmp->is_list) return(TCL_ERROR);
+
+ colorp = (vtmp->is_changed_val && vtmp->changed_val.p)
+ ? vtmp->changed_val.p
+ : (vtmp->current_val.p) ? vtmp->current_val.p
+ : vtmp->global_val.p;
+
+ if(colorp){
+ hex_colorstr(hexcolor, colorp);
+ Tcl_ListObjAppendElement(interp, Tcl_GetObjResult(interp),
+ Tcl_NewStringObj(hexcolor, -1));
+ }
+ else
+ Tcl_ListObjAppendElement(interp, Tcl_GetObjResult(interp),
+ Tcl_NewStringObj("", -1));
+ }
+ return(TCL_OK);
+ }
+ else if(!strcmp(s1, "cldel")){
+ int cl, i, j, n;
+ struct variable *vtmp;
+ char **newl;
+
+ if(Tcl_GetIntFromObj(interp, objv[2], &cl) == TCL_ERROR)
+ return(TCL_ERROR);
+ vtmp = &ps_global->vars[V_FOLDER_SPEC];
+ for(i = 0; i < cl && (vtmp->is_changed_val
+ ? (vtmp->changed_val.l && vtmp->changed_val.l[i])
+ : (vtmp->current_val.l && vtmp->current_val.l[i])); i++);
+ if(!(i == cl && (vtmp->is_changed_val
+ ? (vtmp->changed_val.l && vtmp->changed_val.l[i])
+ : (vtmp->current_val.l && vtmp->current_val.l[i])))){
+ vtmp = &ps_global->vars[V_NEWS_SPEC];
+ for(j = 0; i + j < cl && (vtmp->is_changed_val
+ ? (vtmp->changed_val.l && vtmp->changed_val.l[j])
+ : (vtmp->current_val.l && vtmp->current_val.l[j]));
+ j++);
+ if(!(vtmp->is_changed_val
+ ? (vtmp->changed_val.l && vtmp->changed_val.l[j])
+ : (vtmp->current_val.l && vtmp->current_val.l[j])))
+ return(TCL_ERROR);
+ i = j;
+ }
+ for(n = 0; vtmp->is_changed_val ? (vtmp->changed_val.l && vtmp->changed_val.l[n])
+ : (vtmp->current_val.l && vtmp->current_val.l[n]); n++);
+ newl = (char **)fs_get(n*(sizeof(char *)));
+ for(n = 0; vtmp->is_changed_val ? (vtmp->changed_val.l && vtmp->changed_val.l[n])
+ : (vtmp->current_val.l && vtmp->current_val.l[n]); n++){
+ if(n < i)
+ newl[n] = cpystr(vtmp->is_changed_val ? vtmp->changed_val.l[n]
+ : vtmp->current_val.l[n]);
+ else if(n > i)
+ newl[n-1] = cpystr(vtmp->is_changed_val ? vtmp->changed_val.l[n]
+ : vtmp->current_val.l[n]);
+ }
+ newl[n-1] = NULL;
+ vtmp->is_changed_val = 1;
+ for(n = 0; vtmp->changed_val.l && vtmp->changed_val.l[n]; n++)
+ fs_give((void **) &vtmp->changed_val.l[n]);
+ if(vtmp->changed_val.l) fs_give((void **)&vtmp->changed_val.l);
+ vtmp->changed_val.l = newl;
+
+ return(TCL_OK);
+ }
+ else if(!strcmp(s1, "columns")){
+ int n;
+ char *p;
+
+ if(Tcl_GetIntFromObj(interp, objv[2], &n) != TCL_ERROR
+ && n >= MIN_SCREEN_COLS
+ && n < (MAX_SCREEN_COLS - 1)
+ && ps_global->ttyo->screen_cols != n){
+ clear_index_cache(sp_inbox_stream(), 0);
+ ps_global->ttyo->screen_cols = n;
+ set_variable(V_WP_COLUMNS, p = int2string(n), 0, 0, Main);
+ Tcl_SetResult(interp, p, TCL_VOLATILE);
+ }
+ else
+ Tcl_SetResult(interp, int2string(ps_global->ttyo->screen_cols), TCL_VOLATILE);
+
+ return(TCL_OK);
+ }
+ else if(!strcmp(s1, "reset")){
+ char *p;
+
+ if((p = Tcl_GetStringFromObj(objv[2], NULL)) != NULL){
+ if(!strcmp(p,"pinerc")){
+ struct variable *var;
+ PINERC_S *prc;
+
+ /* new pinerc structure, copy location pointers */
+ prc = new_pinerc_s(ps_global->prc->name);
+ prc->type = ps_global->prc->type;
+ prc->rd = ps_global->prc->rd;
+ prc->outstanding_pinerc_changes = 1;
+
+ /* tie off original pinerc struct and free it */
+ ps_global->prc->rd = NULL;
+ ps_global->prc->outstanding_pinerc_changes = 0;
+ free_pinerc_s(&ps_global->prc);
+
+ /* set global->prc to new struct with no pinerc_lines
+ * and fool write_pinerc into not writing changed vars
+ */
+ ps_global->prc = prc;
+
+ /*
+ * write at least one var into nearly empty pinerc
+ * and clear user's var settings. clear global cause
+ * they'll get reset in peInitVars
+ */
+ for(var = ps_global->vars; var->name != NULL; var++){
+ var->been_written = ((var - ps_global->vars) != V_LAST_VERS_USED);
+ if(var->is_list){
+ free_list_array(&var->main_user_val.l);
+ free_list_array(&var->global_val.l);
+ }
+ else{
+ fs_give((void **)&var->main_user_val.p);
+ fs_give((void **)&var->global_val.p);
+ }
+ }
+
+ write_pinerc(ps_global, Main, WRP_NOUSER | WRP_PRESERV_WRITTEN);
+
+ peInitVars(ps_global);
+ return(TCL_OK);
+ }
+ }
+ }
+ }
+ else if(objc == 4){
+ if(!strcmp(s1, "varset")){
+ char *varname = Tcl_GetStringFromObj(objv[2], NULL);
+ struct variable *vtmp;
+ char **tstrlist = NULL, *line, *tline;
+ Tcl_Obj **objVal;
+ int i, strlistpos, numlistvals;
+
+ if(varname == NULL) return(TCL_ERROR);
+ for(vtmp = ps_global->vars;
+ vtmp->name && strucmp(vtmp->name, varname);
+ vtmp++)
+ ;
+ if(!vtmp->name){
+ Tcl_SetResult(interp, err, TCL_VOLATILE);
+ return(TCL_ERROR);
+ }
+ if(Tcl_ListObjGetElements(interp, objv[3], &numlistvals,
+ &objVal) != TCL_OK)
+ return(TCL_ERROR);
+ vtmp->is_changed_val = 1;
+ if(vtmp->is_list){
+ if(vtmp->changed_val.l){
+ for(i = 0; vtmp->changed_val.l[i]; i++)
+ fs_give((void **)&vtmp->changed_val.l[i]);
+ fs_give((void **)&vtmp->changed_val.l);
+ }
+ if(numlistvals)
+ tstrlist = (char **)fs_get((numlistvals + 1) * sizeof(char *));
+ for(i = 0, strlistpos = 0; i < numlistvals; i++){
+ if((line = Tcl_GetStringFromObj(objVal[i], 0)) != NULL){
+ tline = cpystr(line);
+ removing_leading_and_trailing_white_space(tline);
+ if(*tline)
+ tstrlist[strlistpos++] = cpystr(tline);
+ fs_give((void **) &tline);
+ }
+ }
+ if(tstrlist)
+ tstrlist[strlistpos] = NULL;
+ vtmp->changed_val.l = tstrlist;
+ }
+ else {
+ if(vtmp->changed_val.p)
+ fs_give((void **)&vtmp->changed_val.p);
+ if(numlistvals){
+ if((line = Tcl_GetStringFromObj(objVal[0], 0)) != NULL){
+ tline = cpystr(line);
+ if(strucmp(vtmp->name, "reply-indent-string"))
+ removing_leading_and_trailing_white_space(tline);
+ if(!strcmp(tline, "\"\"")){
+ tline[0] = '\0';
+ }
+ else if(tline[0] == '\0'){
+ fs_give((void **)&tline);
+ }
+ if(tline){
+ vtmp->changed_val.p = cpystr(tline);
+ fs_give((void **)&tline);
+ }
+ }
+ else
+ vtmp->changed_val.p = cpystr("");
+ }
+ }
+ return(TCL_OK);
+ }
+ else if(!strcmp(s1, "feature")){
+ char *featurename;
+ int i, set, wasset = 0;
+ FEATURE_S *feature;
+
+ /*
+ * CMD: feature
+ *
+ * ARGS: featurename -
+ * value - new value to assign flag
+ *
+ * Returns: 1 if named feature set, 0 otherwise
+ *
+ */
+ if((featurename = Tcl_GetStringFromObj(objv[2], NULL))
+ && Tcl_GetIntFromObj(interp, objv[3], &set) != TCL_ERROR)
+ for(i = 0; (feature = feature_list(i)); i++)
+ if(!strucmp(featurename, feature->name)){
+ ps_global->vars[V_FEATURE_LIST].is_changed_val = 1;
+ wasset = F_CH_ON(feature->id);
+ F_CH_SET(feature->id, set);
+ break;
+ }
+
+ Tcl_SetResult(interp, int2string(wasset), TCL_VOLATILE);
+ return(TCL_OK);
+ }
+ else if(!strcmp(s1, "clshuff")){
+ char *dir, *tstr, **newl;
+ int cl, up = 0, fvarn, nvarn, icnt, i;
+ struct variable *fvar, *nvar, *vtmp;
+
+ if(!(dir = Tcl_GetStringFromObj(objv[2], NULL)))
+ return TCL_ERROR;
+ if(Tcl_GetIntFromObj(interp, objv[3], &cl) == TCL_ERROR)
+ return(TCL_ERROR);
+ if(!strcmp(dir, "up"))
+ up = 1;
+ else if(!strcmp(dir, "down"))
+ up = 0;
+ else
+ return(TCL_ERROR);
+ fvar = &ps_global->vars[V_FOLDER_SPEC];
+ nvar = &ps_global->vars[V_NEWS_SPEC];
+ for(fvarn = 0; fvar->is_changed_val ? (fvar->changed_val.l && fvar->changed_val.l[fvarn])
+ : (fvar->current_val.l && fvar->current_val.l[fvarn]); fvarn++);
+ for(nvarn = 0; nvar->is_changed_val ? (nvar->changed_val.l && nvar->changed_val.l[nvarn])
+ : (nvar->current_val.l && nvar->current_val.l[nvarn]); nvarn++);
+ if(cl < fvarn){
+ vtmp = fvar;
+ icnt = cl;
+ }
+ else if(cl >= fvarn && cl < nvarn + fvarn){
+ vtmp = nvar;
+ icnt = cl - fvarn;
+ }
+ else
+ return(TCL_ERROR);
+ if(vtmp == nvar && icnt == 0 && up){
+ newl = (char **)fs_get((fvarn + 2)*sizeof(char *));
+ for(i = 0; fvar->is_changed_val ? (fvar->changed_val.l && fvar->changed_val.l[i])
+ : (fvar->current_val.l && fvar->current_val.l[i]); i++)
+ newl[i] = cpystr(fvar->is_changed_val ? fvar->changed_val.l[i]
+ : fvar->current_val.l[i]);
+ newl[i++] = cpystr(nvar->is_changed_val ? nvar->changed_val.l[0]
+ : nvar->current_val.l[0]);
+ newl[i] = NULL;
+ fvar->is_changed_val = 1;
+ for(i = 0; fvar->changed_val.l && fvar->changed_val.l[i]; i++)
+ fs_give((void **)&fvar->changed_val.l[i]);
+ if(fvar->changed_val.l) fs_give((void **)&fvar->changed_val.l);
+ fvar->changed_val.l = newl;
+ newl = (char **)fs_get(nvarn*sizeof(char *));
+ for(i = 1; nvar->is_changed_val ? (nvar->changed_val.l && nvar->changed_val.l[i])
+ : (nvar->current_val.l && nvar->current_val.l[i]); i++)
+ newl[i-1] = cpystr(nvar->is_changed_val ? nvar->changed_val.l[i]
+ : nvar->current_val.l[i]);
+ newl[i-1] = NULL;
+ nvar->is_changed_val = 1;
+ for(i = 0; nvar->changed_val.l && nvar->changed_val.l[i]; i++)
+ fs_give((void **)&nvar->changed_val.l[i]);
+ if(nvar->changed_val.l) fs_give((void **)&nvar->changed_val.l);
+ nvar->changed_val.l = newl;
+ vtmp = fvar;
+ icnt = fvarn;
+ }
+ else if(vtmp == fvar && icnt == fvarn - 1 && !up){
+ newl = (char **)fs_get(fvarn*sizeof(char *));
+ for(i = 0; fvar->is_changed_val ? (fvar->changed_val.l && fvar->changed_val.l[i+1])
+ : (fvar->current_val.l && fvar->current_val.l[i+1]); i++)
+ newl[i] = cpystr(fvar->is_changed_val ? fvar->changed_val.l[i]
+ : fvar->current_val.l[i]);
+ newl[i] = NULL;
+ tstr = cpystr(fvar->is_changed_val ? fvar->changed_val.l[i]
+ : fvar->current_val.l[i]);
+ fvar->is_changed_val = 1;
+ for(i = 0; fvar->changed_val.l && fvar->changed_val.l[i]; i++)
+ fs_give((void **)&fvar->changed_val.l[i]);
+ if(fvar->changed_val.l) fs_give((void **)&fvar->changed_val.l);
+ fvar->changed_val.l = newl;
+ newl = (char **)fs_get((nvarn+2)*sizeof(char *));
+ newl[0] = tstr;
+ for(i = 0; nvar->is_changed_val ? (nvar->changed_val.l && nvar->changed_val.l[i])
+ : (nvar->current_val.l && nvar->current_val.l[i]); i++)
+ newl[i+1] = cpystr(nvar->is_changed_val ? nvar->changed_val.l[i]
+ : nvar->current_val.l[i]);
+ newl[i+1] = NULL;
+ nvar->is_changed_val = 1;
+ for(i = 0; nvar->changed_val.l && nvar->changed_val.l[i]; i++)
+ fs_give((void **)&nvar->changed_val.l[i]);
+ if(nvar->changed_val.l) fs_give((void **)&nvar->changed_val.l);
+ nvar->changed_val.l = newl;
+ vtmp = nvar;
+ icnt = 0;
+ }
+ else {
+ newl = (char **)fs_get(((vtmp == fvar ? fvarn : nvarn) + 1)*sizeof(char *));
+ for(i = 0; vtmp->is_changed_val ? (vtmp->changed_val.l && vtmp->changed_val.l[i])
+ : (vtmp->current_val.l && vtmp->current_val.l[i]); i++)
+ newl[i] = cpystr(vtmp->is_changed_val ? vtmp->changed_val.l[i]
+ : vtmp->current_val.l[i]);
+ newl[i] = NULL;
+ vtmp->is_changed_val = 1;
+ for(i = 0; vtmp->changed_val.l && vtmp->changed_val.l[i]; i++)
+ fs_give((void **)&vtmp->changed_val.l[i]);
+ if(vtmp->changed_val.l) fs_give((void **)&vtmp->changed_val.l);
+ vtmp->changed_val.l = newl;
+ }
+ if(up){
+ tstr = vtmp->changed_val.l[icnt-1];
+ vtmp->changed_val.l[icnt-1] = vtmp->changed_val.l[icnt];
+ vtmp->changed_val.l[icnt] = tstr;
+ }
+ else {
+ tstr = vtmp->changed_val.l[icnt+1];
+ vtmp->changed_val.l[icnt+1] = vtmp->changed_val.l[icnt];
+ vtmp->changed_val.l[icnt] = tstr;
+ }
+ return(TCL_OK);
+ }
+ }
+ else if(objc == 7){
+ if(!strcmp(s1, "cledit") || !strcmp(s1, "cladd")){
+ int add = 0, cl, quotes_needed = 0, i, j, newn;
+ char *nick, *server, *path, *view, context_buf[MAILTMPLEN*4];
+ char **newl;
+ struct variable *vtmp;
+
+ if(!strcmp(s1, "cladd")) add = 1;
+
+ if(Tcl_GetIntFromObj(interp, objv[2], &cl) == TCL_ERROR)
+ return(TCL_ERROR);
+ if(!(nick = Tcl_GetStringFromObj(objv[3], NULL)))
+ return TCL_ERROR;
+ if(!(server = Tcl_GetStringFromObj(objv[4], NULL)))
+ return TCL_ERROR;
+ if(!(path = Tcl_GetStringFromObj(objv[5], NULL)))
+ return TCL_ERROR;
+ if(!(view = Tcl_GetStringFromObj(objv[6], NULL)))
+ return TCL_ERROR;
+ removing_leading_and_trailing_white_space(nick);
+ removing_leading_and_trailing_white_space(server);
+ removing_leading_and_trailing_white_space(path);
+ removing_leading_and_trailing_white_space(view);
+ if(strchr(nick, ' '))
+ quotes_needed = 1;
+ if(strlen(nick)+strlen(server)+strlen(path)+strlen(view) >
+ MAILTMPLEN * 4 - 20) { /* for good measure */
+ Tcl_SetResult(interp, "info too long", TCL_VOLATILE);
+ return TCL_ERROR;
+ }
+ if(3 + strlen(nick) + strlen(server) + strlen(path) +
+ strlen(view) > MAILTMPLEN + 4){
+ Tcl_SetResult(interp, "collection fields too long", TCL_VOLATILE);
+ return(TCL_OK);
+ }
+ snprintf(context_buf, sizeof(context_buf), "%s%s%s%s%s%s[%s]", quotes_needed ?
+ "\"" : "", nick, quotes_needed ? "\"" : "",
+ strlen(nick) ? " " : "",
+ server, path, view);
+ if(add) {
+ vtmp = &ps_global->vars[V_NEWS_SPEC];
+ if(!(vtmp->is_changed_val ? (vtmp->changed_val.l && vtmp->changed_val.l[0])
+ : (vtmp->current_val.l && vtmp->current_val.l[0])))
+ vtmp = &ps_global->vars[V_FOLDER_SPEC];
+ for(i = 0; vtmp->is_changed_val ? (vtmp->changed_val.l && vtmp->changed_val.l[i])
+ : (vtmp->current_val.l && vtmp->current_val.l[i]); i++);
+ newn = i + 1;
+ newl = (char **)fs_get((newn + 1)*sizeof(char *));
+ for(i = 0; vtmp->is_changed_val ? (vtmp->changed_val.l && vtmp->changed_val.l[i])
+ : (vtmp->current_val.l && vtmp->current_val.l[i]); i++)
+ newl[i] = cpystr(vtmp->is_changed_val ? vtmp->changed_val.l[i]
+ : vtmp->current_val.l[i]);
+ newl[i++] = cpystr(context_buf);
+ newl[i] = NULL;
+ }
+ else {
+ vtmp = &ps_global->vars[V_FOLDER_SPEC];
+ for(i = 0; i < cl && (vtmp->is_changed_val
+ ? (vtmp->changed_val.l && vtmp->changed_val.l[i])
+ : (vtmp->current_val.l && vtmp->current_val.l[i])); i++);
+ if(!(i == cl && (vtmp->is_changed_val
+ ? (vtmp->changed_val.l && vtmp->changed_val.l[i])
+ : (vtmp->current_val.l && vtmp->current_val.l[i])))){
+ vtmp = &ps_global->vars[V_NEWS_SPEC];
+ for(j = 0; i + j < cl && (vtmp->is_changed_val
+ ? (vtmp->changed_val.l && vtmp->changed_val.l[j])
+ : (vtmp->current_val.l && vtmp->current_val.l[j]));
+ j++);
+ if(!(vtmp->is_changed_val
+ ? (vtmp->changed_val.l && vtmp->changed_val.l[j])
+ : (vtmp->current_val.l && vtmp->current_val.l[j])))
+ return(TCL_ERROR);
+ i = j;
+ }
+ for(j = 0; vtmp->is_changed_val ? (vtmp->changed_val.l && vtmp->changed_val.l[j])
+ : (vtmp->current_val.l && vtmp->current_val.l[j]); j++);
+ newl = (char **)fs_get(j * sizeof(char *));
+ for(j = 0; vtmp->is_changed_val ? (vtmp->changed_val.l && vtmp->changed_val.l[j])
+ : (vtmp->current_val.l && vtmp->current_val.l[j]); j++){
+ if(j == i)
+ newl[j] = cpystr(context_buf);
+ else
+ newl[j] = cpystr(vtmp->is_changed_val ? vtmp->changed_val.l[j]
+ : vtmp->current_val.l[j]);
+ }
+ newl[j] = NULL;
+ }
+ vtmp->is_changed_val = 1;
+ for(j = 0; vtmp->changed_val.l && vtmp->changed_val.l[j]; j++)
+ fs_give((void **)&vtmp->changed_val.l[j]);
+ if(vtmp->changed_val.l) fs_give((void **)&vtmp->changed_val.l);
+ vtmp->changed_val.l = newl;
+ return TCL_OK;
+ }
+ }
+ else
+ err = "PEInfo: Too many arguments";
+ }
+ Tcl_SetResult(interp, err, TCL_STATIC);
+ return(TCL_ERROR);
+}
+
+
+int
+peWriteSig(Tcl_Interp *interp, char *file, Tcl_Obj **objv)
+{
+ int try_cache, e, i, n, nSig;
+ char datebuf[200], *sig, *line;
+ FILE *fp;
+ REMDATA_S *rd;
+ Tcl_Obj **objSig;
+
+ if(!(file && IS_REMOTE(file))){
+ snprintf(tmp_20k_buf, SIZEOF_20KBUF, "Non-Remote signature file: %s",
+ file ? file : "<null>");
+ Tcl_SetResult(interp, tmp_20k_buf, TCL_VOLATILE);
+ return(TCL_ERROR);
+ }
+
+ /*
+ * We could parse the name here to find what type it is. So far we
+ * only have type RemImap.
+ */
+ rd = rd_create_remote(RemImap, file, (void *)REMOTE_SIG_SUBTYPE,
+ NULL, "Error: ", "Can't fetch remote signature.");
+ if(!rd){
+ snprintf(tmp_20k_buf, SIZEOF_20KBUF, "Can't create stream for sig file: %s", file);
+ Tcl_SetResult(interp, tmp_20k_buf, TCL_VOLATILE);
+ return(TCL_ERROR);
+ }
+
+ try_cache = rd_read_metadata(rd);
+
+ if(rd->access == MaybeRorW){
+ if(rd->read_status == 'R')
+ rd->access = ReadOnly;
+ else
+ rd->access = ReadWrite;
+ }
+
+ if(rd->access != NoExists){
+
+ rd_check_remvalid(rd, 1L);
+
+ /*
+ * If the cached info says it is readonly but
+ * it looks like it's been fixed now, change it to readwrite.
+ */
+ if(rd->read_status == 'R'){
+ /*
+ * We go to this trouble since readonly sigfiles
+ * are likely a mistake. They are usually supposed to be
+ * readwrite so we open it and check if it's been fixed.
+ */
+ rd_check_readonly_access(rd);
+ if(rd->read_status == 'W'){
+ rd->access = ReadWrite;
+ rd->flags |= REM_OUTOFDATE;
+ }
+ else{
+ rd_close_remdata(&rd);
+ snprintf(tmp_20k_buf, SIZEOF_20KBUF, "Readonly sig file: %s", file);
+ Tcl_SetResult(interp, tmp_20k_buf, TCL_VOLATILE);
+ return(TCL_ERROR);
+ }
+ }
+
+ if(rd->flags & REM_OUTOFDATE){
+ if(rd_update_local(rd) != 0){
+
+ dprint((1, "pinerc_remote_open: rd_update_local failed"));
+ /*
+ * Don't give up altogether. We still may be
+ * able to use a cached copy.
+ */
+ }
+ else{
+ dprint((7, "%s: copied remote to local (%ld)",
+ rd->rn, (long)rd->last_use));
+ }
+ }
+
+ if(rd->access == ReadWrite)
+ rd->flags |= DO_REMTRIM;
+ }
+
+ /* If we couldn't get to remote folder, try using the cached copy */
+ if(rd->access == NoExists || rd->flags & REM_OUTOFDATE){
+ rd_close_remdata(&rd);
+ snprintf(tmp_20k_buf, SIZEOF_20KBUF, "Unavailable sig file: %s", file);
+ Tcl_SetResult(interp, tmp_20k_buf, TCL_VOLATILE);
+ return(TCL_ERROR);
+ }
+
+ unlink(rd->lf);
+
+ sig = NULL;
+ tmp_20k_buf[0] = '\0';
+ if(objv){
+ Tcl_ListObjGetElements(interp, objv[0], &nSig, &objSig);
+ for(i = 0; i < nSig && i < SIG_MAX_LINES; i++){
+ if((line = Tcl_GetStringFromObj(objSig[i], NULL)) != NULL)
+ snprintf(tmp_20k_buf + strlen(tmp_20k_buf), SIZEOF_20KBUF - strlen(tmp_20k_buf), "%.*s\n",
+ SIG_MAX_COLS, line);
+ }
+ }
+ else if(peTSig){
+ for(i = 0; peTSig[i] && i < SIG_MAX_LINES; i++) {
+ snprintf(tmp_20k_buf + strlen(tmp_20k_buf), SIZEOF_20KBUF - strlen(tmp_20k_buf), "%.*s\n",
+ SIG_MAX_COLS, peTSig[i]);
+ }
+ for(i = 0; peTSig[i]; i++)
+ fs_give((void **)&peTSig[i]);
+ fs_give((void **)&peTSig);
+ }
+ else
+ return(TCL_ERROR);
+
+ sig = cpystr(tmp_20k_buf);
+
+ if((fp = fopen(rd->lf, "w")) != NULL)
+ n = fwrite(sig, strlen(sig), 1, fp);
+
+ fs_give((void **) &sig);
+
+ if(fp){
+ if(n != 1){
+ snprintf(tmp_20k_buf, SIZEOF_20KBUF, "Sig copy failure1: %s: %s",
+ rd->lf, error_description(errno));
+ Tcl_SetResult(interp, tmp_20k_buf, TCL_VOLATILE);
+ rd_close_remdata(&rd);
+ }
+
+ fclose(fp);
+ if(n != 1)
+ return(TCL_ERROR);
+ }
+ else {
+ rd_close_remdata(&rd);
+ snprintf(tmp_20k_buf, SIZEOF_20KBUF, "Sig copy open failure2: %s: %s",
+ rd->lf, error_description(errno));
+ Tcl_SetResult(interp, tmp_20k_buf, TCL_VOLATILE);
+ return(TCL_ERROR);
+ }
+
+ datebuf[0] = '\0';
+
+ if(!rd->t.i.stream){
+ long retflags = 0;
+
+ rd->t.i.stream = context_open(NULL, NULL, rd->rn, 0L, &retflags);
+ }
+
+ if((e = rd_update_remote(rd, datebuf)) != 0){
+ snprintf(tmp_20k_buf, SIZEOF_20KBUF, "Sig update failure: %s: %s",
+ rd->lf, error_description(errno));
+ Tcl_SetResult(interp, tmp_20k_buf, TCL_VOLATILE);
+ rd_close_remdata(&rd);
+ return(TCL_ERROR);
+ }
+
+ rd_update_metadata(rd, datebuf);
+ rd->read_status = 'W';
+ rd_close_remdata(&rd);
+ return(TCL_OK);
+}
+
+
+
+NAMEVAL_S *sort_key_rules(index)
+ int index;
+{
+ static NAMEVAL_S is_rules[] = {
+ {"Arrival", 0},
+ {"Date", 0},
+ {"Subject", 0},
+ {"Cc", 0},
+ {"From", 0},
+ {"To", 0},
+ {"size", 0},
+ {"OrderedSubj", 0},
+ {"tHread", 0},
+ {"Arrival/Reverse", 0},
+ {"Date/Reverse", 0},
+ {"Subject/Reverse", 0},
+ {"Cc/Reverse", 0},
+ {"From/Reverse", 0},
+ {"To/Reverse", 0},
+ {"size/Reverse", 0},
+ {"tHread/Reverse", 0},
+ {"OrderedSubj/Reverse", 0}
+ };
+
+ return((index >= 0 && index < (sizeof(is_rules)/sizeof(is_rules[0])))
+ ? &is_rules[index] : NULL);
+}
+
+NAMEVAL_S *wp_indexheight_rules(index)
+ int index;
+{
+ static NAMEVAL_S is_rules[] = {
+ {"normal font", "24", 0},
+ {"smallest font", "20", 0},
+ {"small font", "22", 0},
+ {"large font", "28", 0},
+ {"largest font", "30", 0}
+ };
+
+ return((index >= 0 && index < (sizeof(is_rules)/sizeof(is_rules[0])))
+ ? &is_rules[index] : NULL);
+}
+
+
+/*
+ * PEDebugCmd - turn on/off and set various debugging options
+ */
+int
+PEDebugCmd(ClientData clientData, Tcl_Interp *interp, int objc, Tcl_Obj *CONST objv[])
+{
+ char *s;
+
+ if(!--objc){ /* only one arg? */
+ Tcl_WrongNumArgs(interp, 1, objv, "?args?");
+ }
+ else if((s = Tcl_GetStringFromObj(objv[1], NULL)) != NULL){
+ if(!strucmp(s, "level")){
+ if(objc == 2){
+ int level;
+
+ if(Tcl_GetIntFromObj(interp, objv[2], &level) != TCL_OK)
+ return(TCL_ERROR);
+
+ if(level > 0){
+ if(level > 10)
+ level = 10;
+
+ debug = level;
+ dprint((1, "Debug level %d", level));
+ }
+ else{
+ dprint((1, "PEDebug ending"));
+ debug = 0;
+ }
+ }
+
+ Tcl_SetResult(interp, int2string(debug), TCL_VOLATILE);
+ return(TCL_OK);
+ }
+ else if(!strucmp(s, "write")){
+ if(objc == 2 && (s = Tcl_GetStringFromObj(objv[2], NULL))){
+ /*
+ * script debugging has a high priority since
+ * statements can be added/removed on the fly
+ * AND are NOT present by default
+ */
+ dprint((SYSDBG_INFO, "SCRIPT: %s", s));
+ }
+
+ return(TCL_OK);
+ }
+ else if(!strucmp(s, "imap")){
+ int level;
+
+ if(Tcl_GetIntFromObj(interp, objv[2], &level) != TCL_OK)
+ return(TCL_ERROR);
+
+ if(level == 0){
+ if(ps_global){
+ ps_global->debug_imap = 0;
+ if(ps_global->mail_stream)
+ mail_nodebug(ps_global->mail_stream);
+ }
+ }
+ else if(level > 0 && level < 5){
+ if(ps_global){
+ ps_global->debug_imap = level;
+ if(ps_global->mail_stream)
+ mail_debug(ps_global->mail_stream);
+ }
+ }
+
+ return(TCL_OK);
+ }
+ else
+ Tcl_SetResult(interp, "Unknown PEDebug request", TCL_STATIC);
+ }
+
+ return(TCL_ERROR);
+}
+
+
+/*
+ * PESessionCmd - Export TCL Session-wide command set
+ */
+int
+PESessionCmd(ClientData clientData, Tcl_Interp *interp, int objc, Tcl_Obj *CONST objv[])
+{
+ char *op, *err = "Unknown PESession option";
+ char *pe_user, *pe_host;
+ int pe_alt, l;
+
+ dprint((2, "PESessionCmd"));
+
+ if((op = Tcl_GetStringFromObj(objv[1], NULL)) != NULL){
+ if(!strcmp(op, "open")){
+ char *s, *pinerc, *pineconf = NULL;
+
+ /*
+ * CMD: open user remote-pinerc local-default-config
+ *
+ * Initiate a session
+ *
+ * Returns: error string on error, nothing otherwise
+ */
+
+ if(objc < 4 || objc > 5){
+ Tcl_WrongNumArgs(interp, 1, objv, "user password pinerc");
+ return(TCL_ERROR);
+ }
+
+ if(!(s = Tcl_GetStringFromObj(objv[2], &l))){
+ Tcl_SetResult(interp, "Unknown User", TCL_STATIC);
+ return(TCL_ERROR);
+ }
+ else{
+ int rv;
+
+ pe_user = cpystr(s);
+
+#if defined(HAVE_SETENV)
+ rv = setenv("WPUSER", pe_user, 1);
+#elif defined(HAVE_PUTENV)
+ {
+ static char putenvbuf[PUTENV_MAX];
+
+ if(l + 8 < PUTENV_MAX){
+ if(putenvbuf[0]) /* only called once, but you never know */
+ snprintf(putenvbuf + 7, PUTENV_MAX - 7, "%s", pe_user);
+ else
+ snprintf(putenvbuf, PUTENV_MAX, "WPUSER=%s", pe_user);
+
+ rv = putenv(putenvbuf);
+ }
+ else
+ rv = 1;
+ }
+#endif
+
+ if(rv){
+ fs_give((void **) &pe_user);
+ Tcl_SetResult(interp, (errno == ENOMEM)
+ ? "Insufficient Environment Space"
+ : "Cannot set WPUSER in environment", TCL_STATIC);
+ return(TCL_ERROR);
+ }
+ }
+
+ if((pinerc = Tcl_GetStringFromObj(objv[3], NULL)) != NULL){
+ NETMBX mb;
+
+ if(mail_valid_net_parse(pinerc, &mb)){
+ pe_host = cpystr(mb.host);
+ pe_alt = (mb.sslflag || mb.tlsflag);
+ }
+ else {
+ snprintf(tmp_20k_buf, SIZEOF_20KBUF, "Non-Remote Config: %s", pinerc);
+ Tcl_SetResult(interp, tmp_20k_buf, TCL_VOLATILE);
+ return(TCL_ERROR);
+ }
+ }
+ else {
+ Tcl_SetResult(interp, "Unknown config location", TCL_STATIC);
+ return(TCL_ERROR);
+ }
+
+ if(objc == 5 && !(pineconf = Tcl_GetStringFromObj(objv[4], NULL))){
+ Tcl_SetResult(interp, "Can't determine global config", TCL_STATIC);
+ return(TCL_ERROR);
+ }
+
+ dprint((SYSDBG_INFO, "session (%s) %s - %s",
+ pe_user, pinerc, pineconf ? pineconf : "<none>"));
+
+ /* credential cache MUST already be seeded */
+
+ /* destroy old user context */
+ if(ps_global){
+ /* destroy open stream */
+ peDestroyStream(ps_global);
+
+ /* destroy old user context */
+ peDestroyUserContext(&ps_global);
+ }
+
+ /* Establish a user context */
+ if((s = peCreateUserContext(interp, pe_user, pinerc, pineconf)) != NULL){
+ Tcl_SetResult(interp, s, TCL_VOLATILE);
+ return(TCL_ERROR);
+ }
+
+ fs_give((void **) &pe_user);
+ fs_give((void **) &pe_host);
+
+ return(TCL_OK);
+ }
+ else if(!strcmp(op, "close")){
+ if(ps_global){
+ /* destroy any open stream */
+ peDestroyStream(ps_global);
+
+ /* destroy user context */
+ peDestroyUserContext(&ps_global);
+ }
+
+ Tcl_SetResult(interp, "BYE", TCL_STATIC);
+ return(TCL_OK);
+ }
+ else if(!strcmp(op, "creds")){
+ char *folder;
+ int colid;
+
+ if(objc < 4){
+ err = "creds: insufficient args";
+ }
+ else if(Tcl_GetIntFromObj(interp,objv[2],&colid) != TCL_ERROR
+ && (folder = Tcl_GetStringFromObj(objv[3], NULL))){
+ int i;
+ CONTEXT_S *cp;
+
+ /*
+ * CMD: creds <collection-index> <folder> [user passwd]
+ *
+ * Test for valid credentials to access given folder
+ *
+ * Returns: 1 if so, 0 otherwise
+ */
+
+ for(i = 0, cp = ps_global ? ps_global->context_list : NULL;
+ i < 1 || cp != NULL ;
+ i++, cp = cp->next)
+ if(i == colid){
+ int rv = 0;
+ char tmp[MAILTMPLEN], *p;
+
+ if(cp){
+ if(folder[0] == '\0'){
+ if(cp->use & CNTXT_INCMNG)
+ rv = 1;
+ else
+ folder = "fake-fake";
+ }
+ else if((cp->use & CNTXT_INCMNG)
+ && (p = folder_is_nick(folder, FOLDERS(cp), FN_NONE)))
+ folder = p;
+ }
+
+ if(!rv && context_allowed(context_apply(tmp, cp, folder, sizeof(tmp)))){
+ NETMBX mb;
+
+ if(mail_valid_net_parse(tmp, &mb)){
+ if(objc == 4){ /* check creds */
+ if(!*mb.user && (p = alpine_get_user(mb.host, (mb.sslflag || mb.tlsflag))))
+ strcpy(mb.user, p);
+
+ if(alpine_have_passwd(mb.user, mb.host, (mb.sslflag || mb.tlsflag)))
+ rv = 1;
+ }
+ else if(objc == 6){ /* set creds */
+ char *user, *passwd;
+
+ if((user = Tcl_GetStringFromObj(objv[4], NULL))
+ && (passwd = Tcl_GetStringFromObj(objv[5], NULL))){
+ if(*mb.user && strcmp(mb.user, user)){
+ err = "creds: mismatched user names";
+ break;
+ }
+
+ alpine_set_passwd(user, passwd, mb.host,
+ mb.sslflag
+ || mb.tlsflag
+ || (ps_global ? F_ON(F_PREFER_ALT_AUTH, ps_global) : 0));
+ rv = 1;
+ }
+ else {
+ err = "creds: unable to read credentials";
+ break;
+ }
+ }
+ else{
+ err = "creds: invalid args";
+ break;
+ }
+ }
+ }
+
+ (void) Tcl_ListObjAppendElement(interp,
+ Tcl_GetObjResult(interp),
+ Tcl_NewIntObj(rv));
+ return(TCL_OK);
+ }
+
+ err = "creds: Unrecognized collection ID";
+ }
+ else
+ err = "creds: failure to acquire folder and collection ID";
+ }
+ else if(!strcmp(op, "nocred")){
+ char *folder;
+ int colid;
+
+ if(!ps_global){
+ err = "No Session active";
+ }
+ else if(objc != 4){
+ err = "nocred: wrong number of args";
+ }
+ else if(Tcl_GetIntFromObj(interp,objv[2],&colid) != TCL_ERROR
+ && (folder = Tcl_GetStringFromObj(objv[3], NULL))){
+ int i;
+ CONTEXT_S *cp;
+
+ /*
+ * CMD: nocred <collection-index> <folder>
+ *
+ * Test for valid credentials to access given folder
+ *
+ * Returns: 1 if so, 0 otherwise
+ */
+
+ for(i = 0, cp = ps_global->context_list; cp ; i++, cp = cp->next)
+ if(i == colid){
+ int rv = 0;
+ char tmp[MAILTMPLEN], *p;
+
+ if((cp->use & CNTXT_INCMNG)
+ && (p = folder_is_nick(folder, FOLDERS(cp), FN_NONE)))
+ folder = p;
+
+ if(context_allowed(context_apply(tmp, cp, folder, sizeof(tmp)))){
+ NETMBX mb;
+
+ if(mail_valid_net_parse(tmp, &mb)){
+ if(!*mb.user && (p = alpine_get_user(mb.host, (mb.sslflag || mb.tlsflag))))
+ strcpy(mb.user, p);
+
+ alpine_clear_passwd(mb.user, mb.host);
+ }
+ }
+
+ (void) Tcl_ListObjAppendElement(interp,
+ Tcl_GetObjResult(interp),
+ Tcl_NewIntObj(rv));
+ return(TCL_OK);
+ }
+
+ err = "creds: Unrecognized collection ID";
+ }
+ else
+ err = "creds: failure to acquire folder and collection ID";
+ }
+ else if(!strcmp(op, "acceptcert")){
+ char *certhost;
+ STRLIST_S **p;
+
+ if((certhost = Tcl_GetStringFromObj(objv[2], NULL))){
+ for(p = &peCertHosts; *p; p = &(*p)->next)
+ ;
+
+ *p = new_strlist(certhost);
+ }
+
+ err = "PESession: no server name";
+ }
+ else if(!strcmp(op, "random")){
+ if(objc != 3){
+ err = "PESession: random <length>";
+ } else {
+ char s[1025];
+ int l;
+
+ if(Tcl_GetIntFromObj(interp,objv[2],&l) != TCL_ERROR){
+ if(l <= 1024){
+ Tcl_SetResult(interp, peRandomString(s,l,PRS_MIXED_CASE), TCL_STATIC);
+ return(TCL_OK);
+ }
+ else
+ err = "PESession: random length too long";
+ }
+ else
+ err = "PESession: can't get random length";
+ }
+ }
+ else if(!strcmp(op, "authdriver")){
+ if(objc != 4){
+ err = "PESession: authdriver {add | remove} drivername";
+ } else {
+ char *cmd, *driver;
+
+ if((cmd = Tcl_GetStringFromObj(objv[2], NULL)) != NULL){
+ if((driver = Tcl_GetStringFromObj(objv[3], NULL)) != NULL){
+ if(!strcmp(cmd,"enable")){
+ err = "PESession: authdriver enable disabled for the nonce";
+ }
+ else if(!strcmp(cmd,"disable")){
+ if(mail_parameters(NULL, DISABLE_AUTHENTICATOR, (void *) driver)){
+ snprintf(tmp_20k_buf, SIZEOF_20KBUF, "Authentication driver %.30s disabled", driver);
+ Tcl_SetResult(interp, tmp_20k_buf, TCL_VOLATILE);
+ return(TCL_OK);
+ }
+ else{
+ snprintf(tmp_20k_buf, SIZEOF_20KBUF, "PESession: Can't disable %.30s", driver);
+ Tcl_SetResult(interp, tmp_20k_buf, TCL_VOLATILE);
+ return(TCL_ERROR);
+ }
+ }
+ else
+ err = "PESession: unknown authdriver operation";
+ }
+ else
+ err = "PESession: Can't read driver name";
+ }
+ else
+ err = "PESesions: Can't read authdriver operation";
+ }
+ }
+ else if(!strcmp(op, "abandon")){
+ /*
+ * CMD: abandon [timeout]
+ *
+ * Returns: nothing
+ */
+
+ if(objc != 3){
+ err = "PESession: abandon [timeout]";
+ } else {
+ long t;
+
+ if(Tcl_GetLongFromObj(interp, objv[2], &t) == TCL_OK){
+ /* ten second minimum and max of default */
+ if(t > 0 && t <= PE_INPUT_TIMEOUT){
+ gPEAbandonTimeout = t;
+ return(TCL_OK);
+ }
+ else
+ err = "unrecognized timeout";
+ }
+ else
+ err = "Can't read timeout";
+ }
+ }
+ else if(!strcmp(op, "noexpunge")){
+ /*
+ * CMD: noexpunge <state>
+ *
+ * Returns: nothing
+ */
+
+ if(objc != 3){
+ err = "PESession: noexpunge <state>";
+ } else {
+ int onoff;
+
+ if(Tcl_GetIntFromObj(interp, objv[2], &onoff) == TCL_OK){
+ if(onoff == 0 || onoff == 1){
+ ps_global->noexpunge_on_close = onoff;
+ return(TCL_OK);
+ }
+
+ err = "unrecognized on/off state";
+ }
+ else
+ err = "Can't read on/off state";
+ }
+ }
+ else if(!strcmp(op, "setpassphrase")){
+#ifdef SMIME
+ char *passphrase;
+
+ if(objc != 3){
+ err = "PESession: setpassphrase <state>";
+ }
+ else if((passphrase = Tcl_GetStringFromObj(objv[2], NULL))){
+ if(ps_global && ps_global->smime){
+ strncpy((char *) ps_global->smime->passphrase, passphrase,
+ sizeof(ps_global->smime->passphrase));
+ ps_global->smime->passphrase[sizeof(ps_global->smime->passphrase)-1] = '\0';
+ ps_global->smime->entered_passphrase = 1;
+ ps_global->smime->need_passphrase = 0;
+ peED.uid = 0;
+ return(TCL_OK);
+ }
+ }
+#else
+ err = "S/MIME not configured for this server";
+#endif /* SMIME */
+ }
+ else if(!strcmp(op, "expungecheck")) {
+ /*
+ * Return open folders and how many deleted messages they have
+ *
+ * return looks something like a list of these:
+ * {folder-name number-deleted isinbox isincoming}
+ */
+ char *type;
+ long delete_count;
+ Tcl_Obj *resObj;
+
+ if(objc != 3){
+ err = "PESession: expungecheck <type>";
+ }
+ else {
+ type = Tcl_GetStringFromObj(objv[2], NULL);
+ if(type && (strcmp(type, "current") == 0 || strcmp(type, "quit") == 0)){
+
+ if(ps_global->mail_stream != sp_inbox_stream()
+ || strcmp(type, "current") == 0){
+ delete_count = count_flagged(ps_global->mail_stream, F_DEL);
+ resObj = Tcl_NewListObj(0, NULL);
+ Tcl_ListObjAppendElement(interp, resObj,
+ Tcl_NewStringObj(pretty_fn(ps_global->cur_folder), -1));
+ Tcl_ListObjAppendElement(interp, resObj,
+ Tcl_NewIntObj(delete_count));
+ Tcl_ListObjAppendElement(interp, resObj,
+ Tcl_NewIntObj((ps_global->mail_stream
+ == sp_inbox_stream())
+ ? 1 : 0));
+ Tcl_ListObjAppendElement(interp, resObj,
+ Tcl_NewIntObj((ps_global->context_current->use & CNTXT_INCMNG)
+ ? 1 : 0));
+ Tcl_ListObjAppendElement(interp, Tcl_GetObjResult(interp),
+ resObj);
+ }
+ if(strcmp(type, "quit") == 0){
+ delete_count = count_flagged(sp_inbox_stream(), F_DEL);
+ resObj = Tcl_NewListObj(0, NULL);
+ Tcl_ListObjAppendElement(interp, resObj,
+ Tcl_NewStringObj("INBOX", -1));
+ Tcl_ListObjAppendElement(interp, resObj,
+ Tcl_NewIntObj(delete_count));
+ Tcl_ListObjAppendElement(interp, resObj,
+ Tcl_NewIntObj(1));
+ Tcl_ListObjAppendElement(interp, resObj,
+ Tcl_NewIntObj(1));
+ Tcl_ListObjAppendElement(interp,
+ Tcl_GetObjResult(interp), resObj);
+ }
+ return(TCL_OK);
+ }
+ else
+ err = "PESession: expungecheck unknown type";
+ }
+ }
+ else if(!strcmp(op, "mailcheck")) {
+ /*
+ * CMD: mailcheck
+ *
+ * ARGS: reload -- "1" if we're reloading
+ * (vs. just checking newmail as a side effect
+ * of building a new page)
+ *
+ * Return list of folders with new or expunged messages
+ *
+ * return looks something like a list of these:
+ * {new-count newest-uid announcement-msg}
+ */
+ int reload, force = UFU_NONE, rv;
+ time_t now = time(0);
+
+ if(objc <= 3){
+ if(objc < 3 || Tcl_GetIntFromObj(interp, objv[2], &reload) == TCL_ERROR)
+ reload = 0;
+
+ /* minimum 10 second between IMAP pings */
+ if(!time_of_last_input() || now - time_of_last_input() > 10){
+ force = UFU_FORCE;
+ if(!reload)
+ peMarkInputTime();
+ }
+
+ peED.interp = interp;
+
+ /* check for new mail */
+ new_mail(force, reload ? GoodTime : VeryBadTime, NM_STATUS_MSG);
+
+ if(!reload){ /* announced */
+ zero_new_mail_count();
+ }
+
+ return(TCL_OK);
+ }
+ else
+ err = "PESession: mailcheck <reload>";
+ }
+ }
+
+ Tcl_SetResult(interp, err, TCL_STATIC);
+ return(TCL_ERROR);
+}
+
+
+
+/*
+ * PEFolderChange - create context's directory chain
+ * corresponding to list of given obj's
+ *
+ * NOTE: caller should call reset_context_folders(cp) to
+ * clean up data structures this creates before returning
+ */
+int
+PEFolderChange(Tcl_Interp *interp, CONTEXT_S *cp, int objc, Tcl_Obj *CONST objv[])
+{
+ int i;
+ FDIR_S *fp;
+ char *folder;
+
+ for(i = 0; i < objc; i++) {
+ folder = Tcl_GetStringFromObj(objv[i], NULL);
+ if(!folder) {
+ Tcl_SetResult(interp, "PEFolderChange: Can't read folder", TCL_VOLATILE);
+ reset_context_folders(cp);
+ return(TCL_ERROR);
+ }
+
+ fp = next_folder_dir(cp, folder, 0, NULL); /* BUG: mail_stream? */
+ fp->desc = folder_lister_desc(cp, fp);
+ fp->delim = cp->dir->delim;
+ fp->prev = cp->dir;
+ fp->status |= CNTXT_SUBDIR;
+ cp->dir = fp;
+ }
+
+ return(TCL_OK);
+}
+
+/*
+ * PEMakeFolderString:
+ */
+int
+PEMakeFolderString(Tcl_Interp *interp, CONTEXT_S *cp, int objc, Tcl_Obj *CONST objv[], char **ppath)
+{
+ int i;
+ unsigned long size,len;
+ char *portion,*path;
+
+ size = 0;
+ for(i = 0; i < objc; i++) {
+ portion = Tcl_GetStringFromObj(objv[i], NULL);
+ if(!portion) {
+ Tcl_SetResult(interp, "PEMakeFolderString: Can't read folder",
+ TCL_VOLATILE);
+ return(TCL_ERROR);
+ }
+ if(i) size++;
+ size += strlen(portion);
+ }
+
+ path = (char*) fs_get(size + 1);
+ size = 0;
+ for(i = 0; i < objc; i++) {
+ portion = Tcl_GetStringFromObj(objv[i], NULL);
+ len = strlen(portion);
+ if(i) path[size++] = cp->dir->delim;
+ memcpy(path + size, portion, len);
+ size += len;
+ }
+ path[size] = '\0';
+ if(ppath) *ppath = path; else fs_give((void**) &path);
+ return(TCL_OK);
+}
+
+
+/*
+ * PEFolderCmd - export various bits of folder information
+ */
+int
+PEFolderCmd(ClientData clientData, Tcl_Interp *interp, int objc, Tcl_Obj *CONST objv[])
+{
+ char *op, errbuf[256], *err = "Unknown PEFolder request";
+
+ dprint((2, "PEFolderCmd"));
+
+ if(objc == 1){
+ Tcl_WrongNumArgs(interp, 1, objv, "cmd ?args?");
+ }
+ else if((op = Tcl_GetStringFromObj(objv[1], NULL)) != NULL){
+ if(ps_global){
+ if(objc == 2){
+ if(!strcmp(op, "current")){
+ CONTEXT_S *cp;
+ int i;
+
+ /*
+ * CMD: current
+ *
+ * Returns: string representing the name of the
+ * current mailbox
+ */
+
+ for(i = 0, cp = ps_global->context_list; cp && cp != ps_global->context_current; i++, cp = cp->next)
+ ;
+
+ if(Tcl_ListObjAppendElement(interp, Tcl_GetObjResult(interp), Tcl_NewIntObj(cp ? i : 0)) == TCL_OK
+ && Tcl_ListObjAppendElement(interp, Tcl_GetObjResult(interp), Tcl_NewStringObj(ps_global->cur_folder,-1)) == TCL_OK)
+ return(TCL_OK);
+
+ return(TCL_ERROR);
+ }
+ else if(!strcmp(op, "collections")){
+ CONTEXT_S *cp;
+ int i;
+
+ /*
+ * CMD: collections
+ *
+ * Returns: List of currently configured collections
+ */
+ for(i = 0, cp = ps_global->context_list; cp ; i++, cp = cp->next){
+ Tcl_Obj *objv[3];
+
+ objv[0] = Tcl_NewIntObj(i);
+ objv[1] = Tcl_NewStringObj(cp->nickname ? cp->nickname : "", -1);
+ objv[2] = Tcl_NewStringObj(cp->label ? cp->label : "", -1);
+
+ Tcl_ListObjAppendElement(interp, Tcl_GetObjResult(interp),
+ Tcl_NewListObj(3, objv));
+ }
+
+ return(TCL_OK);
+ }
+ else if(!strcmp(op, "defaultcollection")){
+ int i;
+ CONTEXT_S *cp;
+
+ for(i = 0, cp = ps_global->context_list; cp ; i++, cp = cp->next)
+ if(cp->use & CNTXT_SAVEDFLT){
+ Tcl_SetResult(interp, int2string(i), TCL_STATIC);
+ return(TCL_OK);
+ }
+
+ err = "PEFolder: isincoming: Invalid collection ID";
+ }
+ else if(!strcmp(op, "clextended")){
+ CONTEXT_S *cp;
+ int i;
+ char tpath[MAILTMPLEN], *p;
+
+ /*
+ * CMD: clextended
+ *
+ * Returns: Extended list of current collections
+ *
+ * Format:
+ * 0) Collection Number
+ * 1) Nickname
+ * 2) Label
+ * 3) Basically this is a flag to say if we can edit
+ * 4) Server
+ * 5) Path
+ * 6) View
+ */
+ /*
+ * had to get rid of this cause the args are changed
+ *
+ * if(strcmp("extended",
+ * Tcl_GetStringFromObj(objv[2], NULL))){
+ * Tcl_SetResult(interp, "invalid argument", TCL_VOLATILE);
+ * return(TCL_ERROR);
+ * }
+ */
+ for(i = 0, cp = ps_global->context_list; cp ;
+ i++, cp = cp->next){
+ Tcl_Obj *objv[7];
+
+ objv[0] = Tcl_NewIntObj(i);
+ objv[1] = Tcl_NewStringObj(cp->nickname ?
+ cp->nickname : "", -1);
+ objv[2] = Tcl_NewStringObj(cp->label ?
+ cp->label : "", -1);
+ objv[3] = Tcl_NewIntObj(cp->var.v ? 1 : 0);
+ objv[4] = Tcl_NewStringObj(cp->server ?
+ cp->server : "", -1);
+ tpath[0] = '\0';
+ if(cp->context){
+ strncpy(tpath, (cp->context[0] == '{'
+ && (p = strchr(cp->context, '}')))
+ ? ++p
+ : cp->context, sizeof(tpath));
+ tpath[sizeof(tpath)-1] = '\0';
+ if((p = strstr(tpath, "%s")) != NULL)
+ *p = '\0';
+ }
+ objv[5] = Tcl_NewStringObj(tpath, -1);
+ objv[6] = Tcl_NewStringObj(cp->dir &&
+ cp->dir->view.user ?
+ cp->dir->view.user :
+ "", -1);
+ Tcl_ListObjAppendElement(interp,
+ Tcl_GetObjResult(interp),
+ Tcl_NewListObj(7, objv));
+ }
+
+ return(TCL_OK);
+ }
+ }
+ else if(objc == 3 && !strcmp(op, "delimiter")){
+ int colid, i;
+ char delim[2] = {'\0', '\0'};
+ CONTEXT_S *cp;
+
+ if(Tcl_GetIntFromObj(interp,objv[2],&colid) != TCL_ERROR){
+ for(i = 0, cp = ps_global->context_list; cp ; i++, cp = cp->next)
+ if(i == colid){
+ if(cp->dir && cp->dir->delim)
+ delim[0] = cp->dir->delim;
+
+ break;
+ }
+
+ Tcl_SetResult(interp, delim[0] ? delim : "/", TCL_VOLATILE);
+ return(TCL_OK);
+ }
+ else
+ err = "PEFolder: delimiter: Can't read collection ID";
+ }
+ else if(objc == 3 && !strcmp(op, "isincoming")){
+ int colid, i;
+ CONTEXT_S *cp;
+
+ if(Tcl_GetIntFromObj(interp,objv[2],&colid) != TCL_ERROR){
+ for(i = 0, cp = ps_global->context_list; cp ; i++, cp = cp->next)
+ if(i == colid){
+ Tcl_SetResult(interp, int2string(((cp->use & CNTXT_INCMNG) != 0)), TCL_STATIC);
+ return(TCL_OK);
+ }
+
+ err = "PEFolder: isincoming: Invalid collection ID";
+ }
+ else
+ err = "PEFolder: isincoming: Can't read collection ID";
+ }
+ else if(objc == 4 && !strcmp(op, "unread")){
+ char *folder, tmp[MAILTMPLEN];
+ MAILSTREAM *mstream;
+ CONTEXT_S *cp;
+ long colid, i, count = 0, flags = (F_UNSEEN | F_UNDEL);
+ int our_stream = 0;
+ /*
+ * CMD: unread
+ *
+ * Returns: number of unread messages in given
+ * folder
+ */
+ if(Tcl_GetLongFromObj(interp,objv[2],&colid) != TCL_ERROR){
+ for(i = 0, cp = ps_global->context_list; cp ; i++, cp = cp->next)
+ if(i == colid)
+ break;
+
+ if(cp){
+ if((folder = Tcl_GetStringFromObj(objv[3], NULL)) != NULL){
+ /* short circuit INBOX */
+ if(colid == 0 && !strucmp(folder, "inbox")){
+ count = count_flagged(sp_inbox_stream(), flags);
+ }
+ else{
+ /*
+ * BUG: some sort of caching to prevent open() fore each call?
+ * does stream cache offset this?
+ */
+ if(!(context_allowed(context_apply(tmp, cp, folder, sizeof(tmp)))
+ && (mstream = same_stream_and_mailbox(tmp, ps_global->mail_stream)))){
+ long retflags = 0;
+
+ ps_global->noshow_error = 1;
+ our_stream = 1;
+ mstream = context_open(cp, NULL, folder,
+ SP_USEPOOL | SP_TEMPUSE| OP_READONLY | OP_SHORTCACHE,
+ &retflags);
+ ps_global->noshow_error = 0;
+ }
+
+ count = count_flagged(mstream, flags);
+
+ if(our_stream)
+ pine_mail_close(mstream);
+ }
+
+ Tcl_SetResult(interp, long2string(count), TCL_VOLATILE);
+ return(TCL_OK);
+ }
+ }
+ else
+ err = "PEFolder: unread: Invalid collection ID";
+ }
+ else
+ err = "PEFolder: unread: Can't read collection ID";
+ }
+ else if(objc == 5 && !strcmp(op, "empty")){
+ /*
+ * CMD: empty
+ *
+ * Returns: number of expunge messages
+ *
+ * Arguments: <colnum> <folder> <what>
+ * where <what> is either <uid>, 'selected', or 'all'
+ */
+ CONTEXT_S *cp;
+ MAILSTREAM *stream = NULL;
+ MESSAGECACHE *mc;
+ MSGNO_S *msgmap;
+ int colid, i, our_stream = 0;
+ long uid, raw, count = 0L;
+ char *errstr = NULL, *what, *folder, *p, tmp[MAILTMPLEN];
+
+ if(Tcl_GetIntFromObj(interp,objv[2],&colid) != TCL_ERROR){
+ for(i = 0, cp = ps_global->context_list; cp ; i++, cp = cp->next)
+ if(i == colid) break;
+ }
+
+ if(cp){
+ if((folder = Tcl_GetStringFromObj(objv[3], NULL)) != NULL){
+ if((what = Tcl_GetStringFromObj(objv[4], NULL)) != NULL){
+ /* need to open? */
+ if(!((context_allowed(context_apply(tmp, cp, folder, sizeof(tmp)))
+ && (stream = same_stream_and_mailbox(tmp, ps_global->mail_stream)))
+ || (stream = same_stream_and_mailbox(tmp, sp_inbox_stream())))){
+ long retflags = 0;
+
+ our_stream = 1;
+ stream = context_open(cp, NULL, folder, SP_USEPOOL | SP_TEMPUSE | OP_SHORTCACHE, &retflags);
+ }
+
+ if(stream){
+ msgmap = sp_msgmap(stream);
+
+ if(!strucmp(what, "all")){
+ if(mn_get_total(msgmap)){
+ agg_select_all(stream, msgmap, NULL, 1);
+ errstr = peApplyFlag(stream, msgmap, 'd', 0, &count);
+ if(!errstr)
+ (void) cmd_expunge_work(stream, msgmap);
+ }
+ }
+ else{
+ /* little complicated since we don't display deleted state and
+ * don't want to expunge what's not intended.
+ * remember what's deleted and restore state on the ones left
+ * when we're done. shouldn't happen much.
+ * NOTE: "uid" is NOT a UID in this loop
+ */
+ for(uid = 1L; uid <= mn_get_total(msgmap); uid++){
+ raw = mn_m2raw(msgmap, uid);
+ if(!get_lflag(stream, msgmap, uid, MN_EXLD)
+ && (mc = mail_elt(stream, raw)) != NULL
+ && mc->deleted){
+ set_lflag(stream, msgmap, uid, MN_STMP, 1);
+ mail_flag(stream, long2string(raw), "\\DELETED", 0L);
+ }
+ else
+ set_lflag(stream, msgmap, uid, MN_STMP, 0);
+ }
+
+ if(!strucmp(what,"selected")){
+ if(any_lflagged(msgmap, MN_SLCT)){
+ if(!(errstr = peApplyFlag(stream, msgmap, 'd', 0, &count)))
+ (void) cmd_expunge_work(stream, msgmap);
+ }
+ else
+ count = 0L;
+ }
+ else{
+ uid = 0;
+ for(p = what; *p; p++)
+ if(isdigit((unsigned char) *p)){
+ uid = (uid * 10) + (*p - '0');
+ }
+ else{
+ errstr = "Invalid uid value";
+ break;
+ }
+
+ if(!errstr && uid){
+ /* uid is a UID here */
+ mail_flag(stream, long2string(uid), "\\DELETED", ST_SET | ST_UID);
+ (void) cmd_expunge_work(stream, msgmap);
+ count = 1L;
+ }
+ }
+
+ /* restore deleted on what didn't get expunged */
+ for(uid = 1L; uid <= mn_get_total(msgmap); uid++){
+ raw = mn_m2raw(msgmap, uid);
+ if(get_lflag(stream, msgmap, uid, MN_STMP)){
+ set_lflag(stream, msgmap, uid, MN_STMP, 0);
+ mail_flag(stream, long2string(raw), "\\DELETED", ST_SET);
+ }
+ }
+ }
+
+ if(our_stream)
+ pine_mail_close(stream);
+ }
+ else
+ errstr = "no stream";
+ }
+ else
+ errstr = "Cannot get which ";
+ }
+ else
+ errstr = "Cannot get folder";
+ }
+ else
+ errstr = "Invalid collection";
+
+ if(errstr){
+ Tcl_SetResult(interp, errstr, TCL_VOLATILE);
+ return(TCL_ERROR);
+ }
+
+ Tcl_SetResult(interp, long2string(count), TCL_VOLATILE);
+ return(TCL_OK);
+ }
+ else if(!strcmp(op, "export")){
+ /*
+ * CMD: export
+ *
+ * Returns: success or failure after writing given
+ * folder to given local file.
+ *
+ * Format:
+ * 0) Collection Number
+ * 1) Folder
+ * 2) Destination file
+ */
+ if(objc == 5){
+ CONTEXT_S *cp;
+ MAILSTREAM *src;
+ APPEND_PKG pkg;
+ STRING msg;
+ long colid, i;
+ char *folder, *dfile, seq[64], tmp[MAILTMPLEN];
+ int our_stream = 0;
+
+ if(Tcl_GetLongFromObj(interp,objv[2],&colid) != TCL_ERROR){
+ for(i = 0, cp = ps_global->context_list; cp ; i++, cp = cp->next)
+ if(i == colid)
+ break;
+
+ if(cp){
+ if((folder = Tcl_GetStringFromObj(objv[3], NULL)) != NULL){
+ if((dfile = Tcl_GetStringFromObj(objv[4], NULL)) != NULL){
+ if(mail_parameters(NULL, ENABLE_DRIVER, "unix")){
+
+ snprintf(tmp, sizeof(tmp), "#driver.unix/%s", dfile);
+
+ if(pine_mail_create(NULL, tmp)){
+
+ err = NULL; /* reset error condition */
+
+ /*
+ * if not current folder, open a stream, setup the
+ * stuff to write the raw header/text by hand
+ * with berkeley delimiters since we don't want
+ * a local mailbox driver lunk in.
+ *
+ * comments:
+ * - BUG: what about logins?
+ *
+ */
+ if(!(context_allowed(context_apply(tmp, cp, folder, sizeof(tmp)))
+ && (src = same_stream_and_mailbox(tmp, ps_global->mail_stream)))){
+ long retflags = 0;
+
+ our_stream = 1;
+ src = context_open(cp, NULL, folder,
+ SP_USEPOOL | SP_TEMPUSE | OP_READONLY | OP_SHORTCACHE,
+ &retflags);
+ }
+
+ if(src && src->nmsgs){
+ /* Go to work...*/
+ pkg.stream = src;
+ pkg.msgno = 0;
+ pkg.msgmax = src->nmsgs;
+ pkg.flags = pkg.date = NIL;
+ pkg.message = &msg;
+
+ snprintf (seq,sizeof(seq),"1:%lu",src->nmsgs);
+ mail_fetchfast (src, seq);
+
+ ps_global->noshow_error = 1;
+ if(!mail_append_multiple (NULL, dfile,
+ peAppendMsg, (void *) &pkg)){
+ snprintf(err = errbuf, sizeof(errbuf), "PEFolder: export: %.200s",
+ ps_global->c_client_error);
+ }
+
+ ps_global->noshow_error = 0;
+
+ if(our_stream)
+ pine_mail_close(src);
+ }
+ else
+ err = "PEFolder: export: can't open mail folder";
+
+ if(!err)
+ return(TCL_OK);
+ }
+ else
+ err = "PEFolder: export: can't create destination";
+
+ if(!mail_parameters(NULL, DISABLE_DRIVER, "unix"))
+ err = "PEFolder: export: can't disable driver";
+ }
+ else
+ err = "PEFolder: export: can't enable driver";
+ }
+ else
+ err = "PEFolder: export: can't read file name";
+ }
+ else
+ err = "PEFolder: export: can't read folder name";
+ }
+ else
+ err = "PEFolder: export: Invalid collection ID";
+ }
+ else
+ err = "PEFolder:export: Can't read collection ID";
+ }
+ else
+ err = "PEFolder: export <colid> <folder> <file>";
+ }
+ else if(!strcmp(op, "import")){
+ /*
+ * CMD: import
+ *
+ * Returns: success or failure after writing given
+ * folder to given local file.
+ *
+ * Format:
+ * 0) source file
+ * 1) destination collection number
+ * 2) destination folder
+ */
+ if(objc == 5){
+ CONTEXT_S *cp;
+ MAILSTREAM *src, *dst;
+ APPEND_PKG pkg;
+ STRING msg;
+ long colid, i;
+ char *folder, *sfile, seq[64];
+
+ /* get source file with a little sanity check */
+ if((sfile = Tcl_GetStringFromObj(objv[2], NULL))
+ && *sfile == '/' && !strstr(sfile, "..")){
+ if(mail_parameters(NULL, ENABLE_DRIVER, "unix")){
+
+ ps_global->noshow_error = 1; /* don't queue error msg */
+ err = NULL; /* reset error condition */
+
+ /* make sure sfile contains valid mail */
+ if((src = mail_open(NULL, sfile, 0L)) != NULL){
+
+ if(Tcl_GetLongFromObj(interp,objv[3],&colid) != TCL_ERROR){
+ for(i = 0, cp = ps_global->context_list; cp ; i++, cp = cp->next)
+ if(i == colid)
+ break;
+
+ if(cp){
+ if((folder = Tcl_GetStringFromObj(objv[4], NULL)) != NULL){
+ long retflags = 0;
+
+ if(context_create(cp, NULL, folder)
+ && (dst = context_open(cp, NULL, folder, SP_USEPOOL | SP_TEMPUSE, &retflags))){
+
+ if(src->nmsgs){
+ /* Go to work...*/
+ pkg.stream = src;
+ pkg.msgno = 0;
+ pkg.msgmax = src->nmsgs;
+ pkg.flags = pkg.date = NIL;
+ pkg.message = &msg;
+
+ snprintf (seq,sizeof(seq),"1:%lu",src->nmsgs);
+ mail_fetchfast (src, seq);
+
+ if(!context_append_multiple(cp, dst, folder,
+ peAppendMsg, (void *) &pkg,
+ ps_global->mail_stream)){
+ snprintf(err = errbuf, sizeof(errbuf), "PEFolder: import: %.200s",
+ ps_global->c_client_error);
+ }
+
+ }
+
+ pine_mail_close(dst);
+ }
+ else
+ snprintf(err = errbuf, sizeof(errbuf), "PEFolder: import: %.200s",
+ ps_global->c_client_error);
+ }
+ else
+ err = "PEFolder: import: can't read folder name";
+ }
+ else
+ err = "PEFolder:import: invalid collection id";
+ }
+ else
+ err = "PEFolder: import: can't read collection id";
+
+ mail_close(src);
+
+ }
+ else
+ snprintf(err = errbuf, sizeof(errbuf), "PEFolder: import: %.200s",
+ ps_global->c_client_error);
+
+ ps_global->noshow_error = 0;
+
+ if(!mail_parameters(NULL, DISABLE_DRIVER, "unix") && !err)
+ err = "PEFolder: import: can't disable driver";
+
+ if(!err)
+ return(TCL_OK);
+ }
+ else
+ err = "PEFolder: import: can't enable driver";
+ }
+ else
+ err = "PEFolder: import: can't read file name";
+ }
+ else
+ err = "PEFolder: import <file> <colid> <folder>";
+ }
+ else {
+ int i, colid;
+ char *aes, *colstr;
+ CONTEXT_S *cp;
+
+ /*
+ * 3 or more arguments, 3rd is the collection ID, rest
+ * are a folder name
+ */
+
+ if(Tcl_GetIntFromObj(interp,objv[2],&colid) != TCL_ERROR){
+ for(i = 0, cp = ps_global->context_list; cp ; i++, cp = cp->next)
+ if(i == colid) break;
+ }
+ else if((colstr = Tcl_GetStringFromObj(objv[2], NULL)) != NULL){
+ if(!strcmp("default", colstr))
+ cp = default_save_context(ps_global->context_list);
+ else
+ cp = NULL;
+ }
+ else
+ cp = NULL;
+
+ if(cp){
+ if(!strcmp(op, "list")){
+ int i, fcount, bflags = BFL_NONE;
+
+ if(PEFolderChange(interp, cp, objc - 3, objv + 3) == TCL_ERROR)
+ return TCL_ERROR;
+
+ if(cp->use & CNTXT_NEWS)
+ bflags |= BFL_LSUB;
+
+ ps_global->c_client_error[0] = ps_global->last_error[0] = '\0';
+
+ pePrepareForAuthException();
+
+ build_folder_list(NULL, cp, "*", NULL, bflags);
+
+ if((aes = peAuthException()) != NULL){
+ Tcl_SetResult(interp, aes, TCL_VOLATILE);
+ reset_context_folders(cp);
+ return(TCL_ERROR);
+ }
+
+ if((fcount = folder_total(FOLDERS(cp))) != 0){
+ for(i = 0; i < fcount; i++){
+ char type[3], *p;
+ FOLDER_S *f = folder_entry(i, FOLDERS(cp));
+
+ p = type;
+ if(f->isdir){
+ *p++ = 'D';
+
+ if(f->hasnochildren && !f->haschildren)
+ *p++ = 'E';
+ }
+
+ if(f->isfolder
+ || f->nickname
+ || (cp->use & CNTXT_INCMNG))
+ *p++ = 'F';
+
+ *p = '\0';
+
+ peAppListF(interp, Tcl_GetObjResult(interp), "%s%s", type,
+ f->nickname ? f->nickname : f->name);
+ }
+ }
+
+ reset_context_folders(cp);
+ return(TCL_OK);
+ }
+ else if(!strucmp(op, "exists")){
+ char *folder, *errstr = NULL;
+ int rv;
+
+ if(objc < 4) {
+ Tcl_SetResult(interp, "PEFolder exists: No folder specified", TCL_VOLATILE);
+ return(TCL_ERROR);
+ }
+ folder = Tcl_GetStringFromObj(objv[objc - 1], NULL);
+ if(!folder) {
+ Tcl_SetResult(interp, "PEFolder exists: Can't read folder", TCL_VOLATILE);
+ return(TCL_ERROR);
+ }
+
+ if(PEFolderChange(interp, cp, objc - 4, objv + 3) == TCL_ERROR)
+ return TCL_ERROR;
+
+ ps_global->c_client_error[0] = '\0';
+ pePrepareForAuthException();
+
+ rv = folder_name_exists(cp, folder, NULL);
+
+ if(rv & FEX_ERROR){
+ if((errstr = peAuthException()) == NULL){
+ if(ps_global->c_client_error[0])
+ errstr = ps_global->c_client_error;
+ else
+ errstr = "Indeterminate Error";
+ }
+ }
+
+ Tcl_SetResult(interp, errstr ? errstr : int2string((int)(rv & FEX_ISFILE)), TCL_VOLATILE);
+ return(errstr ? TCL_ERROR : TCL_OK);
+ }
+ else if(!strucmp(op, "fullname")){
+ char *folder, *fullname;
+
+ if(objc < 4) {
+ Tcl_SetResult(interp, "PEFolder fullname: No folder specified", TCL_VOLATILE);
+ return(TCL_ERROR);
+ }
+ folder = Tcl_GetStringFromObj(objv[objc - 1], NULL);
+ if(!folder) {
+ Tcl_SetResult(interp, "PEFolder fullname: Can't read folder", TCL_VOLATILE);
+ return(TCL_ERROR);
+ }
+
+ if(PEFolderChange(interp, cp, objc - 4, objv + 3) == TCL_ERROR)
+ return TCL_ERROR;
+
+#if 0
+ Tcl_Obj *obj = Tcl_NewStringObj((fullname = folder_is_nick(folder, FOLDERS(cp)))
+ ? fullname : folder, -1);
+ (void) Tcl_ListObjAppendElement(interp,
+ Tcl_GetObjResult(interp),
+ obj);
+#else
+ Tcl_SetResult(interp,
+ (fullname = folder_is_nick(folder, FOLDERS(cp), FN_NONE)) ? fullname : folder,
+ TCL_VOLATILE);
+#endif
+
+ return(TCL_OK);
+ }
+ else if(!strucmp(op, "create")){
+ char *aes, *folder;
+
+ folder = Tcl_GetStringFromObj(objv[objc - 1], NULL);
+ if(!folder) {
+ Tcl_SetResult(interp, "PEFolder create: Can't read folder", TCL_VOLATILE);
+ return(TCL_ERROR);
+ }
+
+ if(PEFolderChange(interp, cp, objc - 4, objv + 3) == TCL_ERROR)
+ return TCL_ERROR;
+
+ ps_global->c_client_error[0] = ps_global->last_error[0] = '\0';
+ pePrepareForAuthException();
+
+ if(!context_create(cp, NULL, folder)){
+ if((aes = peAuthException()) != NULL){
+ Tcl_SetResult(interp, aes, TCL_VOLATILE);
+ }
+ else{
+ Tcl_SetResult(interp,
+ (ps_global->last_error[0])
+ ? ps_global->last_error
+ : (ps_global->c_client_error[0])
+ ? ps_global->c_client_error
+ : "Unable to create folder",
+ TCL_VOLATILE);
+ }
+
+ reset_context_folders(cp);
+ return(TCL_ERROR);
+ }
+
+ Tcl_SetResult(interp, "OK", TCL_STATIC);
+ reset_context_folders(cp);
+ return(TCL_OK);
+ }
+ else if(!strucmp(op, "delete")){
+ int fi, readonly, close_opened = 0;
+ char *folder, *fnamep, *target = NULL, *aes;
+ MAILSTREAM *del_stream = NULL, *strm = NULL;
+ EditWhich ew;
+ PINERC_S *prc = NULL;
+ FOLDER_S *fp;
+
+ folder = Tcl_GetStringFromObj(objv[objc - 1], NULL);
+ if(!folder) {
+ Tcl_SetResult(interp, "PEFolder delete: Can't read folder", TCL_VOLATILE);
+ return(TCL_ERROR);
+ }
+
+ if(PEFolderChange(interp, cp, objc - 4, objv + 3) == TCL_ERROR)
+ return TCL_ERROR;
+
+ /* so we can check for folder's various properties */
+ build_folder_list(NULL, cp, folder, NULL, BFL_NONE);
+
+ ps_global->c_client_error[0] = ps_global->last_error[0] = '\0';
+
+ pePrepareForAuthException();
+
+ /* close open folder, then delete */
+
+ if((fi = folder_index(folder, cp, FI_FOLDER)) < 0
+ || (fp = folder_entry(fi, FOLDERS(cp))) == NULL){
+ Tcl_SetResult(interp, "Cannot find folder to delete", TCL_STATIC);
+ reset_context_folders(cp);
+ return(TCL_ERROR);
+ }
+
+ if(!((cp->use & CNTXT_INCMNG) && fp->name
+ && check_for_move_mbox(fp->name, NULL, 0, &target))){
+ target = NULL;
+ }
+
+ dprint((4, "=== delete_folder(%s) ===\n", folder ? folder : "?"));
+
+ ew = config_containing_inc_fldr(fp);
+ if(ps_global->restricted)
+ readonly = 1;
+ else{
+ switch(ew){
+ case Main:
+ prc = ps_global->prc;
+ break;
+ case Post:
+ prc = ps_global->post_prc;
+ break;
+ case None:
+ break;
+ }
+
+ readonly = prc ? prc->readonly : 1;
+ }
+
+ if(prc && prc->quit_to_edit && (cp->use & CNTXT_INCMNG)){
+ Tcl_SetResult(interp, "Must Exit Alpine to Change Configuration", TCL_STATIC);
+ reset_context_folders(cp);
+ return(TCL_ERROR);
+ }
+
+ if(cp == ps_global->context_list
+ && !(cp->dir && cp->dir->ref)
+ && strucmp(folder, ps_global->inbox_name) == 0){
+ Tcl_SetResult(interp, "Cannot delete special folder", TCL_STATIC);
+ reset_context_folders(cp);
+ return(TCL_ERROR);
+ }
+ else if(readonly && (cp->use & CNTXT_INCMNG)){
+ Tcl_SetResult(interp, "Folder not in editable config file", TCL_STATIC);
+ reset_context_folders(cp);
+ return(TCL_ERROR);
+ }
+ else if((fp->name
+ && (strm=context_already_open_stream(cp,fp->name,AOS_NONE)))
+ ||
+ (target
+ && (strm=context_already_open_stream(NULL,target,AOS_NONE)))){
+ if(strm == ps_global->mail_stream)
+ close_opened++;
+ }
+ else if(fp->isdir || fp->isdual){ /* NO DELETE if directory isn't EMPTY */
+ FDIR_S *fdirp = next_folder_dir(cp,folder,TRUE,NULL);
+ int ret;
+
+ if(fp->haschildren)
+ ret = 1;
+ else if(fp->hasnochildren)
+ ret = 0;
+ else{
+ ret = folder_total(fdirp->folders) > 0;
+ free_fdir(&fdirp, 1);
+ }
+
+ if(ret){
+ Tcl_SetResult(interp, "Cannot delete non-empty directory", TCL_STATIC);
+ reset_context_folders(cp);
+ return(TCL_ERROR);
+ }
+
+ /*
+ * Folder by the same name exist, so delete both...
+ if(fp->isdual){
+ Tcl_SetResult(interp, "Cannot delete: folder is also a directory", TCL_STATIC);
+ reset_context_folders(cp);
+ return(TCL_ERROR);
+ }
+ */
+ }
+
+ if(cp->use & CNTXT_INCMNG){
+ Tcl_SetResult(interp, "Cannot delete incoming folder", TCL_STATIC);
+ reset_context_folders(cp);
+ return(TCL_ERROR);
+ }
+
+ dprint((2,"deleting \"%s\" (%s) in context \"%s\"\n",
+ fp->name ? fp->name : "?",
+ fp->nickname ? fp->nickname : "",
+ cp->context ? cp->context : "?"));
+ if(strm){
+ /*
+ * Close it, NULL the pointer, and let do_broach_folder fixup
+ * the rest...
+ */
+ pine_mail_actually_close(strm);
+ if(close_opened){
+ do_broach_folder(ps_global->inbox_name,
+ ps_global->context_list,
+ NULL, DB_INBOXWOCNTXT);
+ }
+ }
+
+ /*
+ * Use fp->name since "folder" may be a nickname...
+ */
+ if(ps_global->mail_stream
+ && context_same_stream(cp, fp->name, ps_global->mail_stream))
+ del_stream = ps_global->mail_stream;
+
+ fnamep = fp->name;
+
+ if(!context_delete(cp, del_stream, fnamep)){
+ if((aes = peAuthException()) != NULL){
+ Tcl_SetResult(interp, aes, TCL_VOLATILE);
+ }
+ else{
+ Tcl_SetResult(interp,
+ (ps_global->last_error[0])
+ ? ps_global->last_error
+ : (ps_global->c_client_error[0])
+ ? ps_global->c_client_error
+ : "Unable to delete folder",
+ TCL_VOLATILE);
+ }
+
+ reset_context_folders(cp);
+ return(TCL_ERROR);
+ }
+
+
+ Tcl_SetResult(interp, "OK", TCL_STATIC);
+ reset_context_folders(cp);
+ return(TCL_OK);
+ }
+ /*
+ * must be at least 5 arguments for the next set of commands
+ */
+ else if(objc < 5) {
+ Tcl_SetResult(interp, "PEFolder: not enough arguments", TCL_VOLATILE);
+ return(TCL_ERROR);
+ }
+ else if(!strucmp(op, "rename")){
+ char *folder,*newfolder, *aes;
+
+ folder = Tcl_GetStringFromObj(objv[objc - 2], NULL);
+ if(!folder) {
+ Tcl_SetResult(interp, "PEFolder rename: Can't read folder", TCL_VOLATILE);
+ return(TCL_ERROR);
+ }
+
+ newfolder = Tcl_GetStringFromObj(objv[objc - 1], NULL);
+ if(!newfolder) {
+ Tcl_SetResult(interp, "PEFolder rename: Can't read folder", TCL_VOLATILE);
+ return(TCL_ERROR);
+ }
+
+ if(PEFolderChange(interp, cp, objc - 5, objv + 3) == TCL_ERROR)
+ return TCL_ERROR;
+
+ ps_global->c_client_error[0] = ps_global->last_error[0] = '\0';
+ pePrepareForAuthException();
+
+ if(!context_rename(cp, NULL, folder, newfolder)){
+ if((aes = peAuthException()) != NULL){
+ Tcl_SetResult(interp, aes, TCL_VOLATILE);
+ }
+ else{
+ Tcl_SetResult(interp,
+ (ps_global->last_error[0])
+ ? ps_global->last_error
+ : (ps_global->c_client_error[0])
+ ? ps_global->c_client_error
+ : "Unable to rename folder",
+ TCL_VOLATILE);
+ }
+ reset_context_folders(cp);
+ return(TCL_ERROR);
+ }
+ Tcl_SetResult(interp, "OK", TCL_STATIC);
+ reset_context_folders(cp);
+ return(TCL_OK);
+ }
+ }
+ else
+ err = "PEFolder: Unrecognized collection ID";
+ }
+ }
+ else
+ err = "No User Context Established";
+ }
+
+ Tcl_SetResult(interp, err, TCL_VOLATILE);
+ return(TCL_ERROR);
+}
+
+
+/*
+ * PEMailboxCmd - export various bits of mailbox information
+ */
+int
+PEMailboxCmd(ClientData clientData, Tcl_Interp *interp, int objc, Tcl_Obj *CONST objv[])
+{
+ char *op, errbuf[256], *err = "Unknown PEMailbox operation";
+
+ dprint((5, "PEMailboxCmd"));
+
+ if(objc == 1){
+ Tcl_WrongNumArgs(interp, 1, objv, "cmd ?args?");
+ }
+ else if((op = Tcl_GetStringFromObj(objv[1], NULL)) != NULL){
+ if(!strucmp(op, "open")){
+ int i, colid;
+ char *folder;
+ CONTEXT_S *cp;
+
+ peED.uid = 0; /* forget cached embedded data */
+
+ /*
+ * CMD: open <context-index> <folder>
+ *
+ *
+ */
+ if(objc == 2){
+ Tcl_SetResult(interp, (!sp_dead_stream(ps_global->mail_stream)) ? "0" : "1", TCL_VOLATILE);
+ return(TCL_OK);
+ }
+
+ if(Tcl_GetIntFromObj(interp,objv[2],&colid) != TCL_ERROR){
+ if((folder = Tcl_GetStringFromObj(objv[objc - 1], NULL)) != NULL) {
+ for(i = 0, cp = ps_global->context_list; cp ; i++, cp = cp->next)
+ if(i == colid) {
+ if(PEMakeFolderString(interp, cp, objc - 3, objv + 3,
+ &folder))
+ return TCL_ERROR;
+
+ dprint((1, "* PEMailbox open dir=%s folder=%s",cp->dir->ref,folder));
+
+ return(peCreateStream(interp, cp, folder, FALSE));
+ }
+
+ err = "open: Unrecognized collection ID";
+ }
+ else
+ err = "open: Can't read folder";
+ }
+ else
+ err = "open: Can't get collection ID";
+ }
+ else if(!strcmp(op, "indexformat")){
+ /*
+ * CMD: indexformat
+ *
+ * Returns: list of lists where:
+ * * the first element is the name of the
+ * field which may be "From", "Subject"
+ * "Date" or the emtpy string.
+ * * the second element which is either
+ * the percentage width or empty string
+ */
+ if(objc == 2)
+ return(peIndexFormat(interp));
+ }
+ else if(ps_global && ps_global->mail_stream){
+ if(!strcmp(op, "select")){
+ return(peSelect(interp, objc - 2, &((Tcl_Obj **) objv)[2], MN_SLCT));
+ }
+ else if(!strcmp(op, "search")){
+ return(peSelect(interp, objc - 2, &((Tcl_Obj **) objv)[2], MN_SRCH));
+ }
+ else if(!strucmp(op, "apply")){
+ return(peApply(interp, objc - 2, &((Tcl_Obj **) objv)[2]));
+ }
+ else if(!strcmp(op, "expunge")){
+ /*
+ * CMD: expunge
+ *
+ * Returns: OK after having removed deleted messages
+ */
+ char *streamstr = NULL;
+ MAILSTREAM *stream;
+ MSGNO_S *msgmap;
+
+ if(objc == 3) streamstr = Tcl_GetStringFromObj(objv[2], NULL);
+ if(!streamstr
+ || (streamstr && (strcmp(streamstr, "current") == 0))){
+ stream = ps_global->mail_stream;
+ msgmap = sp_msgmap(stream);
+ }
+ else if(streamstr && (strcmp(streamstr, "inbox") == 0)){
+ stream = sp_inbox_stream();
+ msgmap = sp_msgmap(stream);
+ }
+ else return(TCL_ERROR);
+ ps_global->last_error[0] = '\0';
+ if(IS_NEWS(stream)
+ && stream->rdonly){
+ msgno_exclude_deleted(stream, msgmap);
+ clear_index_cache(sp_inbox_stream(), 0);
+
+ /*
+ * This is kind of surprising at first. For most sort
+ * orders, if the whole set is sorted, then any subset
+ * is also sorted. Not so for OrderedSubject sort.
+ * If you exclude the first message of a subject group
+ * then you change the date that group is to be sorted on.
+ */
+ if(mn_get_sort(msgmap) == SortSubject2)
+ refresh_sort(ps_global->mail_stream, msgmap, FALSE);
+ }
+ else
+ (void) cmd_expunge_work(stream, msgmap);
+
+ Tcl_SetResult(interp, ps_global->last_error, TCL_VOLATILE);
+ return(TCL_OK);
+ }
+ else if(!strcmp(op, "trashdeleted")){
+ /*
+ * CMD: trashdeleted
+ *
+ * Returns: OK after moving deleted messages to Trash and expunging
+ */
+ MAILSTREAM *stream;
+ MESSAGECACHE *mc;
+ CONTEXT_S *cp;
+ MSGNO_S *msgmap;
+ char *streamstr = NULL, tmp[MAILTMPLEN];
+ long n, tomove = 0L;
+
+ if(objc == 3) streamstr = Tcl_GetStringFromObj(objv[2], NULL);
+ if(!streamstr
+ || (streamstr && (strcmp(streamstr, "current") == 0))){
+ stream = ps_global->mail_stream;
+ msgmap = sp_msgmap(stream);
+ }
+ else if(streamstr && (strcmp(streamstr, "inbox") == 0)){
+ stream = sp_inbox_stream();
+ msgmap = sp_msgmap(stream);
+ }
+ else return(TCL_ERROR);
+
+ ps_global->last_error[0] = '\0';
+ if(IS_NEWS(stream) && stream->rdonly){
+ msgno_exclude_deleted(stream, msgmap);
+ clear_index_cache(sp_inbox_stream(), 0);
+
+ /*
+ * This is kind of surprising at first. For most sort
+ * orders, if the whole set is sorted, then any subset
+ * is also sorted. Not so for OrderedSubject sort.
+ * If you exclude the first message of a subject group
+ * then you change the date that group is to be sorted on.
+ */
+ if(mn_get_sort(msgmap) == SortSubject2)
+ refresh_sort(ps_global->mail_stream, msgmap, FALSE);
+ }
+ else{
+ if(!(cp = default_save_context(ps_global->context_list)))
+ cp = ps_global->context_list;
+
+ /* copy to trash if we're not in trash */
+ if(ps_global->VAR_TRASH_FOLDER
+ && ps_global->VAR_TRASH_FOLDER[0]
+ && context_allowed(context_apply(tmp, cp, ps_global->VAR_TRASH_FOLDER, sizeof(tmp)))
+ && !same_stream_and_mailbox(tmp, stream)){
+
+ /* save real selected set, and */
+ for(n = 1L; n <= mn_get_total(msgmap); n++){
+ set_lflag(stream, msgmap, n, MN_STMP, get_lflag(stream, msgmap, n, MN_SLCT));
+ /* select deleted */
+ if(!get_lflag(stream, msgmap, n, MN_EXLD)
+ && (mc = mail_elt(stream, mn_m2raw(msgmap,n))) != NULL && mc->deleted){
+ tomove++;
+ set_lflag(stream, msgmap, n, MN_SLCT, 1);
+ }
+ else
+ set_lflag(stream, msgmap, n, MN_SLCT, 0);
+ }
+
+ if(tomove && pseudo_selected(stream, msgmap)){
+
+ /* save delted to Trash */
+ n = save(ps_global, stream,
+ cp, ps_global->VAR_TRASH_FOLDER,
+ msgmap, SV_FOR_FILT | SV_FIX_DELS);
+
+ /* then remove them */
+ if(n == tomove){
+ (void) cmd_expunge_work(stream, msgmap);
+ }
+
+ restore_selected(msgmap);
+ }
+
+ /* restore selected set */
+ for(n = 1L; n <= mn_get_total(msgmap); n++)
+ set_lflag(stream, msgmap, n, MN_SLCT,
+ get_lflag(stream, msgmap, n, MN_STMP));
+ }
+ }
+
+ Tcl_SetResult(interp, ps_global->last_error, TCL_VOLATILE);
+ return(TCL_OK);
+ }
+ else if(!strcmp(op, "nextvector")){
+ long msgno, count, countdown;
+ int i, aObjN = 0;
+ char *errstr = NULL, *s;
+ Tcl_Obj *rvObj, *vObj, *avObj, **aObj;
+
+ /*
+ * CMD: nextvector
+ *
+ * ARGS: msgno - message number "next" is relative to
+ * count - how many msgno slots to return
+ * attrib - (optional) attributes to be returned with each message in vector
+ *
+ * Returns: vector containing next <count> messagenumbers (and optional attributes)
+ */
+ if(objc == 4 || objc == 5){
+ if(Tcl_GetLongFromObj(interp, objv[2], &msgno) == TCL_OK){
+ if(Tcl_GetLongFromObj(interp, objv[3], &count) == TCL_OK){
+
+ /* set index range for efficiency */
+ if(msgno > 0L && msgno <= mn_get_total(sp_msgmap(ps_global->mail_stream))){
+ gPeITop = msgno;
+ gPeICount = count;
+ }
+
+ if(objc == 4 || Tcl_ListObjGetElements(interp, objv[4], &aObjN, &aObj) == TCL_OK){
+
+ if((rvObj = Tcl_NewListObj(0, NULL)) != NULL && count > 0
+ && !(msgno < 1L || msgno > mn_get_total(sp_msgmap(ps_global->mail_stream)))){
+ mn_set_cur(sp_msgmap(ps_global->mail_stream), msgno);
+
+ for(countdown = count; countdown > 0; countdown--){
+ imapuid_t uid = mail_uid(ps_global->mail_stream, mn_m2raw(sp_msgmap(ps_global->mail_stream), msgno));
+ int fetched = 0;
+
+ if((vObj = Tcl_NewListObj(0, NULL)) != NULL){
+ Tcl_ListObjAppendElement(interp, vObj, Tcl_NewLongObj(msgno));
+ peAppListF(interp, vObj, "%lu", uid);
+
+ if(aObjN){
+ if((avObj = Tcl_NewListObj(0, NULL)) != NULL){
+ for(i = 0; i < aObjN; i++){
+ if((s = Tcl_GetStringFromObj(aObj[i], NULL)) != NULL){
+ if(!strcmp(s, "statusbits")){
+ char *s = peMsgStatBitString(ps_global, ps_global->mail_stream,
+ sp_msgmap(ps_global->mail_stream), peMessageNumber(uid),
+ gPeITop, gPeICount, &fetched);
+ Tcl_ListObjAppendElement(interp, avObj, Tcl_NewStringObj(s, -1));
+ }
+ else if(!strcmp(s, "statuslist")){
+ Tcl_Obj *nObj = peMsgStatNameList(interp, ps_global, ps_global->mail_stream,
+ sp_msgmap(ps_global->mail_stream), peMessageNumber(uid),
+ gPeITop, gPeICount, &fetched);
+ Tcl_ListObjAppendElement(interp, avObj, nObj);
+ }
+ else if(!strcmp(s, "status")){
+ long raw;
+ char stat[3];
+ MESSAGECACHE *mc;
+
+ raw = peSequenceNumber(uid);
+
+ if(!((mc = mail_elt(ps_global->mail_stream, raw)) && mc->valid)){
+ mail_fetch_flags(ps_global->mail_stream,
+ ulong2string(uid), FT_UID);
+ mc = mail_elt(ps_global->mail_stream, raw);
+ }
+
+ stat[0] = mc->deleted ? '1' : '0';
+ stat[1] = mc->recent ? '1' : '0';
+ stat[2] = mc->seen ? '1' : '0';
+
+ Tcl_ListObjAppendElement(interp, avObj, Tcl_NewStringObj(stat,3));
+ }
+ else if(!strcmp(s, "indexparts")){
+ Tcl_Obj *iObj;
+
+ if((iObj = Tcl_NewListObj(0, NULL)) != NULL
+ && peAppendIndexParts(interp, uid, iObj, &fetched) == TCL_OK
+ && Tcl_ListObjAppendElement(interp, avObj, iObj) == TCL_OK){
+ }
+ else
+ return(TCL_ERROR);
+ }
+ else if(!strucmp(s, "indexcolor")){
+ if(peAppendIndexColor(interp, uid, avObj, &fetched) != TCL_OK)
+ return(TCL_ERROR);
+ }
+ }
+ else{
+ errstr = "nextvector: can't read attributes";
+ break;
+ }
+ }
+
+ Tcl_ListObjAppendElement(interp, vObj, avObj);
+ }
+ else{
+ errstr = "nextvector: can't allocate attribute return vector";
+ break;
+ }
+ }
+ }
+ else{
+ errstr = "nextvector: can't allocate new vector";
+ break;
+ }
+
+ Tcl_ListObjAppendElement(interp, rvObj, vObj);
+
+ for(++msgno; msgno <= mn_get_total(sp_msgmap(ps_global->mail_stream)) && msgline_hidden(ps_global->mail_stream, sp_msgmap(ps_global->mail_stream), msgno, MN_NONE); msgno++)
+ ;
+
+ if(msgno > mn_get_total(sp_msgmap(ps_global->mail_stream)))
+ break;
+ }
+ }
+
+ if(!errstr){
+ /* append result vector */
+ Tcl_ListObjAppendElement(interp, Tcl_GetObjResult(interp), rvObj);
+ /* Everything is coerced to UTF-8 */
+ Tcl_ListObjAppendElement(interp, Tcl_GetObjResult(interp),
+ Tcl_NewStringObj("UTF-8", -1));
+ return(TCL_OK);
+ }
+ }
+ else
+ errstr = "nextvector: can't read attribute list";
+ }
+ else
+ errstr = "nextvector: can't read count";
+ }
+ else
+ errstr = "nextvector: can't read message number";
+ }
+ else
+ errstr = "nextvector: Incorrect number of arguments";
+
+ if(errstr)
+ Tcl_SetResult(interp, errstr, TCL_STATIC);
+
+ return(TCL_ERROR);
+ }
+ else if(objc == 2){
+ if(!strcmp(op, "messagecount")){
+ /*
+ * CMD: messagecount
+ *
+ * Returns: count of messsages in open mailbox
+ */
+ Tcl_SetResult(interp,
+ long2string(mn_get_total(sp_msgmap(ps_global->mail_stream))),
+ TCL_VOLATILE);
+ return(TCL_OK);
+ }
+ else if(!strcmp(op, "firstinteresting")){
+ /*
+ * CMD: firstinteresting
+ *
+ * Returns: message number associated with
+ * "incoming-startup-rule" which had better
+ * be the "current" message since it was set
+ * in do_broach_folder and shouldn't have been
+ * changed otherwise (expunged screw us?)
+ */
+ Tcl_SetResult(interp,
+ long2string(mn_get_cur(sp_msgmap(ps_global->mail_stream))),
+ TCL_VOLATILE);
+ return(TCL_OK);
+ }
+ else if(!strcmp(op, "selected")){
+ /*
+ * CMD: selected
+ *
+ * Returns: count of selected messsages in open mailbox
+ */
+
+ Tcl_SetResult(interp,
+ long2string(any_lflagged(sp_msgmap(ps_global->mail_stream), MN_SLCT)),
+ TCL_VOLATILE);
+ return(TCL_OK);
+ }
+ else if(!strcmp(op, "searched")){
+ /*
+ * CMD: searched
+ *
+ * Returns: count of searched messsages in open mailbox
+ */
+
+ Tcl_SetResult(interp,
+ long2string(any_lflagged(sp_msgmap(ps_global->mail_stream), MN_SRCH)),
+ TCL_VOLATILE);
+ return(TCL_OK);
+ }
+ else if(!strcmp(op, "mailboxname")){
+ /*
+ * CMD: name
+ *
+ * Returns: string representing the name of the
+ * current mailbox
+ */
+ Tcl_SetResult(interp, ps_global->cur_folder, TCL_VOLATILE);
+ return(TCL_OK);
+ }
+ else if(!strcmp(op, "close")){
+ /*
+ * CMD: close
+ *
+ * Returns: with global mail_stream closed
+ */
+ peDestroyStream(ps_global);
+ return(TCL_OK);
+ }
+ else if(!strcmp(op, "newmailreset")){
+ sml_seen();
+ zero_new_mail_count();
+ sp_set_mail_box_changed(ps_global->mail_stream, 0);
+ sp_set_expunge_count(ps_global->mail_stream, 0L);
+ peMarkInputTime();
+ return(TCL_OK);
+ }
+ else if(!strcmp(op, "newmailstatmsg")){
+ long newest, count;
+ char subject[500], subjtxt[500], from[500], intro[500], *s = "";
+
+ /*
+ * CMD: newmailstatmsg
+ *
+ * ARGS: none
+ *
+ * Returns: text for new mail message
+ *
+ */
+
+ if(sp_mail_box_changed(ps_global->mail_stream)
+ && (count = sp_mail_since_cmd(ps_global->mail_stream))){
+
+ for(newest = ps_global->mail_stream->nmsgs; newest > 1L; newest--)
+ if(!get_lflag(ps_global->mail_stream, NULL, newest, MN_EXLD))
+ break;
+
+ if(newest){
+ format_new_mail_msg(NULL, count,
+ pine_mail_fetchstructure(ps_global->mail_stream,
+ newest, NULL),
+ intro, from, subject, subjtxt, sizeof(subject));
+
+ snprintf(s = tmp_20k_buf, SIZEOF_20KBUF, "%s %s %s", intro, from, subjtxt);
+ }
+ }
+
+ Tcl_SetResult(interp, s, TCL_VOLATILE);
+ return(TCL_OK);
+ }
+ else if(!strcmp(op, "savedefault")){
+ return(peSaveDefault(interp, 0L, 0, NULL));
+ }
+ else if(!strcmp(op, "gotodefault")){
+ return(peGotoDefault(interp, 0L, NULL));
+ }
+ else if(!strcmp(op, "zoom")){
+ Tcl_SetResult(interp,
+ long2string((any_lflagged(sp_msgmap(ps_global->mail_stream), MN_HIDE) > 0L)
+ ? any_lflagged(sp_msgmap(ps_global->mail_stream), MN_SLCT) : 0L),
+ TCL_VOLATILE);
+ return(TCL_OK);
+ }
+ else if(!strcmp(op, "focus")){
+ Tcl_SetResult(interp,
+ long2string((any_lflagged(sp_msgmap(ps_global->mail_stream), MN_HIDE) > 0L)
+ ? any_lflagged(sp_msgmap(ps_global->mail_stream), MN_SRCH) : 0L),
+ TCL_VOLATILE);
+ return(TCL_OK);
+ }
+ else if(!strcmp(op, "first")){
+ if(any_lflagged(sp_msgmap(ps_global->mail_stream), MN_HIDE)){
+ long n;
+
+ for(n = 1L; n <= mn_get_total(sp_msgmap(ps_global->mail_stream)); n++)
+ if(!get_lflag(ps_global->mail_stream, sp_msgmap(ps_global->mail_stream), n, MN_HIDE)){
+ Tcl_SetResult(interp, long2string(n), TCL_VOLATILE);
+ return(TCL_OK);
+ }
+
+ unzoom_index(ps_global, ps_global->mail_stream, sp_msgmap(ps_global->mail_stream));
+
+ }
+
+ Tcl_SetResult(interp, int2string(1), TCL_VOLATILE);
+ return(TCL_OK);
+ }
+ else if(!strucmp(op, "current")){
+ long n = 0;
+ unsigned long u = 0;
+
+ /*
+ * CMD: current
+ *
+ * ARGS:
+ *
+ * Returns: list of current msg {<sequence> <uid>}
+ */
+
+ if(mn_total_cur(sp_msgmap(ps_global->mail_stream)) <= 0
+ || ((n = mn_get_cur(sp_msgmap(ps_global->mail_stream))) > 0
+ && (u = mail_uid(ps_global->mail_stream, mn_m2raw(sp_msgmap(ps_global->mail_stream), n))) > 0)){
+ Tcl_ListObjAppendElement(interp, Tcl_GetObjResult(interp), Tcl_NewStringObj(long2string(n), -1));
+ Tcl_ListObjAppendElement(interp, Tcl_GetObjResult(interp), Tcl_NewStringObj(ulong2string(u), -1));
+ return(TCL_OK);
+ }
+ else
+ err = "Cannot get current";
+ }
+ else if(!strcmp(op, "last")){
+ if(any_lflagged(sp_msgmap(ps_global->mail_stream), MN_HIDE)){
+ long n;
+
+ for(n = mn_get_total(sp_msgmap(ps_global->mail_stream)); n > 0L; n--)
+ if(!get_lflag(ps_global->mail_stream, sp_msgmap(ps_global->mail_stream), n, MN_HIDE)){
+ Tcl_SetResult(interp, long2string(n), TCL_VOLATILE);
+ return(TCL_OK);
+ }
+ }
+ else{
+ Tcl_SetResult(interp, long2string(mn_get_total(sp_msgmap(ps_global->mail_stream))), TCL_VOLATILE);
+ return(TCL_OK);
+ }
+
+ Tcl_SetResult(interp, "Can't set last message number", TCL_STATIC);
+ return(TCL_ERROR);
+ }
+ else if(!strucmp(op, "sortstyles")){
+ int i;
+ /*
+ * CMD: sortstyles
+ *
+ * Returns: list of supported sort styles
+ */
+
+ for(i = 0; ps_global->sort_types[i] != EndofList; i++)
+ if(Tcl_ListObjAppendElement(interp,
+ Tcl_GetObjResult(interp),
+ Tcl_NewStringObj(sort_name(ps_global->sort_types[i]), -1)) != TCL_OK)
+ return(TCL_ERROR);
+
+ return(TCL_OK);
+ }
+ else if(!strucmp(op, "sort")){
+ return(peAppendCurrentSort(interp));
+ }
+ else if(!strucmp(op, "state")){
+ if(!ps_global->mail_stream || sp_dead_stream(ps_global->mail_stream))
+ Tcl_SetResult(interp, "closed", TCL_STATIC);
+ else if(ps_global->mail_stream->rdonly && !IS_NEWS(ps_global->mail_stream))
+ Tcl_SetResult(interp, "readonly", TCL_STATIC);
+ else
+ Tcl_SetResult(interp, "ok", TCL_STATIC);
+
+ return(TCL_OK);
+ }
+ else if(!strucmp(op, "excludedeleted")){
+ msgno_exclude_deleted(ps_global->mail_stream, sp_msgmap(ps_global->mail_stream));
+ return(TCL_OK);
+ }
+ }
+ else if(objc == 3){
+ if(!strcmp(op, "uid")){
+ long msgno, raw;
+
+ /*
+ * Return uid of given message number
+ *
+ * CMD: uid <msgnumber>
+ */
+
+ if(Tcl_GetLongFromObj(interp, objv[2], &msgno) != TCL_OK)
+ return(TCL_ERROR); /* conversion problem? */
+
+ if((raw = mn_m2raw(sp_msgmap(ps_global->mail_stream), msgno)) > 0L){
+ raw = mail_uid(ps_global->mail_stream, raw);
+ Tcl_SetResult(interp, long2string(raw), TCL_VOLATILE);
+ return(TCL_OK);
+ }
+
+ snprintf(tmp_20k_buf, SIZEOF_20KBUF, "Invalid UID for message %ld", msgno);
+ Tcl_SetResult(interp, tmp_20k_buf, TCL_VOLATILE);
+ return(TCL_ERROR);
+ }
+ else if(!strcmp(op, "newmail")){
+ int reload, force = UFU_NONE, rv;
+ time_t now = time(0);
+
+ /*
+ * CMD: newmail
+ *
+ * ARGS: reload -- "1" if we're reloading
+ * (vs. just checking newmail as a side effect
+ * of building a new page)
+ *
+ * Returns: count -
+ * mostrecent -
+ */
+ if(Tcl_GetIntFromObj(interp, objv[2], &reload) == TCL_ERROR)
+ reload = 0;
+
+ /* minimum 10 second between IMAP pings */
+ if(!time_of_last_input() || now - time_of_last_input() > 10){
+ force = UFU_FORCE;
+ peMarkInputTime();
+ }
+
+ /* check for new mail */
+ new_mail(force, reload ? GoodTime : VeryBadTime, NM_NONE);
+
+ rv = peNewMailResult(interp);
+
+ if(!reload) /* announced */
+ zero_new_mail_count();
+
+ return(rv);
+ }
+ else if(!strcmp(op, "flagcount")){
+ char *flag;
+ long count = 0L;
+ long flags = 0L;
+ int objlc;
+ Tcl_Obj **objlv;
+
+
+ /*
+ * CMD: flagcount
+ *
+ * ARGS: flags -
+ *
+ * Returns: count - number of message thusly flagged
+ * mostrecent -
+ */
+ if(Tcl_ListObjGetElements(interp, objv[2], &objlc, &objlv) == TCL_OK){
+ while(objlc--)
+ if((flag = Tcl_GetStringFromObj(*objlv++, NULL)) != NULL){
+ if(!strucmp(flag, "deleted")){
+ flags |= F_DEL;
+ }
+ if(!strucmp(flag, "undeleted")){
+ flags |= F_UNDEL;
+ }
+ else if(!strucmp(flag, "seen")){
+ flags |= F_SEEN;
+ }
+ else if(!strucmp(flag, "unseen")){
+ flags |= F_UNSEEN;
+ }
+ else if(!strucmp(flag, "flagged")){
+ flags |= F_FLAG;
+ }
+ else if(!strucmp(flag, "unflagged")){
+ flags |= F_UNFLAG;
+ }
+ else if(!strucmp(flag, "answered")){
+ flags |= F_ANS;
+ }
+ else if(!strucmp(flag, "unanswered")){
+ flags |= F_UNANS;
+ }
+ else if(!strucmp(flag, "recent")){
+ flags |= F_RECENT;
+ }
+ else if(!strucmp(flag, "unrecent")){
+ flags |= F_UNRECENT;
+ }
+ }
+
+ if(flags)
+ count = count_flagged(ps_global->mail_stream, flags);
+ }
+
+ Tcl_SetResult(interp, long2string(count), TCL_VOLATILE);
+ return(TCL_OK);
+ }
+ else if(!strcmp(op, "zoom")){
+ int newstate;
+ long n, zoomed = 0L;
+
+ /*
+ * CMD: zoom
+ *
+ * Set/clear HID bits of non SLCT messages as requested.
+ * PEMailbox [first | last | next] are senstive to these flags.
+ *
+ * ARGS: newstate - 1 or 0
+ *
+ * Returns: count of zoomed messages
+ */
+
+ if(Tcl_GetIntFromObj(interp, objv[2], &newstate) != TCL_ERROR){
+ if(newstate > 0){
+ if(any_lflagged(sp_msgmap(ps_global->mail_stream), MN_HIDE) != (mn_get_total(sp_msgmap(ps_global->mail_stream)) - (n = any_lflagged(sp_msgmap(ps_global->mail_stream), MN_SLCT)))){
+ zoom_index(ps_global, ps_global->mail_stream, sp_msgmap(ps_global->mail_stream), MN_SLCT);
+ zoomed = n;
+ }
+ }
+ else{
+ if(any_lflagged(sp_msgmap(ps_global->mail_stream), MN_HIDE))
+ unzoom_index(ps_global, ps_global->mail_stream, sp_msgmap(ps_global->mail_stream));
+ }
+ }
+
+ Tcl_SetResult(interp, long2string(zoomed), TCL_VOLATILE);
+ return(TCL_OK);
+ }
+ else if(!strcmp(op, "focus")){
+ int newstate;
+ long n, zoomed = 0L;
+
+ /*
+ * CMD: focus
+ *
+ * Set/clear HID bits of non MN_SRCH messages as requested.
+ * PEMailbox [first | last | next] are senstive to MN_HIDE flag
+ *
+ * ARGS: newstate - 1 or 0
+ *
+ * Returns: count of zoomed messages
+ */
+
+ if(Tcl_GetIntFromObj(interp, objv[2], &newstate) != TCL_ERROR){
+ if(newstate > 0){
+ if(any_lflagged(sp_msgmap(ps_global->mail_stream), MN_HIDE) != (mn_get_total(sp_msgmap(ps_global->mail_stream)) - (n = any_lflagged(sp_msgmap(ps_global->mail_stream), MN_SRCH))))
+ zoom_index(ps_global, ps_global->mail_stream, sp_msgmap(ps_global->mail_stream), MN_SRCH);
+
+ zoomed = n;
+ }
+ else{
+ if(any_lflagged(sp_msgmap(ps_global->mail_stream), MN_HIDE))
+ unzoom_index(ps_global, ps_global->mail_stream, sp_msgmap(ps_global->mail_stream));
+ }
+ }
+
+ Tcl_SetResult(interp, long2string(zoomed), TCL_VOLATILE);
+ return(TCL_OK);
+ }
+ else if(!strcmp(op, "next")){
+ long msgno;
+
+ /*
+ * CMD: next <msgno>
+ *
+ * ARGS: msgno - message number "next" is relative to
+ *
+ * Returns: previous state
+ */
+
+ if(Tcl_GetLongFromObj(interp, objv[2], &msgno) != TCL_ERROR){
+ mn_set_cur(sp_msgmap(ps_global->mail_stream), msgno);
+ mn_inc_cur(ps_global->mail_stream, sp_msgmap(ps_global->mail_stream), MH_NONE);
+ Tcl_SetResult(interp, long2string(mn_get_cur(sp_msgmap(ps_global->mail_stream))), TCL_VOLATILE);
+ return(TCL_OK);
+ }
+
+ Tcl_SetResult(interp, "next can't read message number", TCL_STATIC);
+ return(TCL_ERROR);
+ }
+ }
+ else if(objc == 4){
+ if(!strucmp(op, "sort")){
+ int i, reversed = 0;
+ char *sort;
+
+ /*
+ * CMD: sort sortstyle reversed
+ *
+ * Returns: OK with the side-effect of message
+ * numbers now reflecting the requested
+ * sort order.
+ */
+
+ if((sort = Tcl_GetStringFromObj(objv[2], NULL))
+ && Tcl_GetIntFromObj(interp, objv[3], &reversed) != TCL_ERROR){
+ /* convert sort string into */
+ for(i = 0; ps_global->sort_types[i] != EndofList; i++)
+ if(strucmp(sort_name(ps_global->sort_types[i]), sort) == 0){
+ if(sp_unsorted_newmail(ps_global->mail_stream)
+ || !(ps_global->sort_types[i] == mn_get_sort(sp_msgmap(ps_global->mail_stream))
+ && mn_get_revsort(sp_msgmap(ps_global->mail_stream)) == reversed))
+ sort_folder(ps_global->mail_stream, sp_msgmap(ps_global->mail_stream),
+ ps_global->sort_types[i],
+ reversed, 0);
+
+ break;
+ }
+ }
+
+ return(peAppendCurrentSort(interp));
+ }
+ else if(!strucmp(op, "selected")){
+ return(peSelected(interp, objc - 2, &((Tcl_Obj **) objv)[2], MN_SLCT));
+ }
+ else if(!strucmp(op, "searched")){
+ return(peSelected(interp, objc - 2, &((Tcl_Obj **) objv)[2], MN_SRCH));
+ }
+ else if(!strcmp(op, "next")){
+ long msgno, count;
+
+ /*
+ * CMD: next
+ *
+ * ARGS: msgno - message number "next" is relative to
+ * count - how many to increment it
+ *
+ * Returns: previous state
+ */
+
+ if(Tcl_GetLongFromObj(interp, objv[2], &msgno) != TCL_ERROR
+ && Tcl_GetLongFromObj(interp, objv[3], &count) != TCL_ERROR){
+ mn_set_cur(sp_msgmap(ps_global->mail_stream), msgno);
+ while(count)
+ if(count > 0){
+ mn_inc_cur(ps_global->mail_stream, sp_msgmap(ps_global->mail_stream), MH_NONE);
+ count--;
+ }
+ else{
+ mn_dec_cur(ps_global->mail_stream, sp_msgmap(ps_global->mail_stream), MH_NONE);
+ count++;
+ }
+
+ Tcl_SetResult(interp, long2string(mn_get_cur(sp_msgmap(ps_global->mail_stream))), TCL_VOLATILE);
+ return(TCL_OK);
+ }
+
+ Tcl_SetResult(interp, "next can't read message number", TCL_STATIC);
+ return(TCL_ERROR);
+ }
+ else if(!strcmp(op, "x-nextvector")){
+ long msgno, count;
+
+ /*
+ * CMD: nextvector
+ *
+ * ARGS: msgno - message number "next" is relative to
+ * count - how many msgno slots to return
+ *
+ * Returns: vector containing next <count> messagenumbers
+ */
+
+ if(Tcl_GetLongFromObj(interp, objv[2], &msgno) != TCL_ERROR
+ && Tcl_GetLongFromObj(interp, objv[3], &count) != TCL_ERROR){
+ if(count > 0 && !(msgno < 1L || msgno > mn_get_total(sp_msgmap(ps_global->mail_stream)))){
+ mn_set_cur(sp_msgmap(ps_global->mail_stream), msgno);
+
+ while(count--){
+ long n = mn_get_cur(sp_msgmap(ps_global->mail_stream));
+
+ if(peAppListF(interp, Tcl_GetObjResult(interp),
+ "%l%l", n, mail_uid(ps_global->mail_stream,
+ mn_m2raw(sp_msgmap(ps_global->mail_stream), n))) != TCL_OK)
+ return(TCL_ERROR);
+
+ mn_inc_cur(ps_global->mail_stream, sp_msgmap(ps_global->mail_stream), MH_NONE);
+
+ if(n == mn_get_cur(sp_msgmap(ps_global->mail_stream)))
+ break;
+ }
+ }
+
+ return(TCL_OK);
+ }
+
+ Tcl_SetResult(interp, "next can't read message number", TCL_STATIC);
+ return(TCL_ERROR);
+ }
+ else if(!strcmp(op, "messagecount")){
+ char *relative;
+ long msgno, n, count = 0L;
+
+ /*
+ * CMD: messagecount
+ *
+ * ARGS: [before | after] relative to
+ * msgno
+ *
+ * Returns: count of messsages before or after given message number
+ */
+
+ if((relative = Tcl_GetStringFromObj(objv[2], NULL))
+ && Tcl_GetLongFromObj(interp, objv[3], &msgno) != TCL_ERROR){
+ if(msgno < 1L || msgno > mn_get_total(sp_msgmap(ps_global->mail_stream))){
+ Tcl_SetResult(interp, "relative msgno out of range", TCL_STATIC);
+ return(TCL_ERROR);
+ }
+
+ if(!strucmp(relative, "before")){
+ for(n = msgno - 1; n > 0L; n--)
+ if(!get_lflag(ps_global->mail_stream, sp_msgmap(ps_global->mail_stream), n, MN_HIDE))
+ count++;
+
+ Tcl_SetResult(interp, long2string(count), TCL_VOLATILE);
+ return(TCL_OK);
+ }
+ else if(!strucmp(relative, "after")){
+ for(n = msgno + 1; n <= mn_get_total(sp_msgmap(ps_global->mail_stream)); n++)
+ if(!get_lflag(ps_global->mail_stream, sp_msgmap(ps_global->mail_stream), n, MN_HIDE))
+ count++;
+
+ Tcl_SetResult(interp, long2string(count), TCL_VOLATILE);
+ return(TCL_OK);
+ }
+ }
+
+ Tcl_SetResult(interp, "can't read range for count", TCL_STATIC);
+ return(TCL_ERROR);
+ }
+ else if(!strcmp(op, "selectvector")){
+ long msgno, count;
+
+ /*
+ * CMD: selectvector
+ *
+ * ARGS: msgno - message number "next" is relative to
+ * count - how many msgno slots to return
+ *
+ * Returns: vector containing next <count> messagenumbers
+ */
+
+ if(Tcl_GetLongFromObj(interp, objv[2], &msgno) != TCL_ERROR
+ && Tcl_GetLongFromObj(interp, objv[3], &count) != TCL_ERROR){
+ if(msgno > 0L){
+ mn_set_cur(sp_msgmap(ps_global->mail_stream), msgno);
+ while(count--){
+ msgno = mn_get_cur(sp_msgmap(ps_global->mail_stream));
+
+ if(get_lflag(ps_global->mail_stream, sp_msgmap(ps_global->mail_stream), msgno, MN_SLCT))
+ if(Tcl_ListObjAppendElement(interp,
+ Tcl_GetObjResult(interp),
+ Tcl_NewLongObj((long) mail_uid(ps_global->mail_stream, msgno))) != TCL_OK)
+ return(TCL_ERROR);
+
+ mn_inc_cur(ps_global->mail_stream, sp_msgmap(ps_global->mail_stream), MH_NONE);
+
+ if(msgno == mn_get_cur(sp_msgmap(ps_global->mail_stream)))
+ break;
+ }
+ }
+
+ return(TCL_OK);
+ }
+
+ Tcl_SetResult(interp, "selectvector: no message number", TCL_STATIC);
+ return(TCL_ERROR);
+ }
+ else if(!strucmp(op, "current")){
+ char *which;
+ long x, n = 0, u = 0;
+
+ /*
+ * CMD: current
+ *
+ * ARGS: (number|uid) <msgno>
+ *
+ * Returns: list of current msg {<sequence> <uid>}
+ */
+ if((which = Tcl_GetStringFromObj(objv[2], NULL)) != NULL){
+ if(Tcl_GetLongFromObj(interp, objv[3], &x) == TCL_OK){
+ if(!strucmp(which,"uid")){
+ u = x;
+ n = peMessageNumber(u);
+ }
+ else if(!strucmp(which,"number")){
+ n = x;
+ u = mail_uid(ps_global->mail_stream, mn_m2raw(sp_msgmap(ps_global->mail_stream), n));
+ }
+
+ if(n && u){
+ mn_set_cur(sp_msgmap(ps_global->mail_stream), n);
+ Tcl_ListObjAppendElement(interp, Tcl_GetObjResult(interp), Tcl_NewStringObj(long2string(n), -1));
+ Tcl_ListObjAppendElement(interp, Tcl_GetObjResult(interp), Tcl_NewStringObj(long2string(u), -1));
+ return(TCL_OK);
+ }
+ else
+ err = "PEMailbox current: invalid number/uid";
+ }
+ else
+ err = "PEMailbox current: cannot get number";
+ }
+ else
+ err = "PEMailbox current: cannot get which";
+ }
+ }
+ else
+ err = "PEMailbox: Too many arguments";
+ }
+ else if(!strucmp(op, "name") || !strcmp(op, "close")){
+ Tcl_SetResult(interp, "", TCL_STATIC);
+ return(TCL_OK);
+ }
+ else
+ snprintf(err = errbuf, sizeof(errbuf), "%s: %s: No open mailbox",
+ Tcl_GetStringFromObj(objv[0], NULL), op);
+ }
+
+ Tcl_SetResult(interp, err, TCL_VOLATILE);
+ return(TCL_ERROR);
+}
+
+
+int
+peAppendCurrentSort(Tcl_Interp *interp)
+{
+ return((Tcl_ListObjAppendElement(interp,
+ Tcl_GetObjResult(interp),
+ Tcl_NewStringObj(sort_name(mn_get_sort(sp_msgmap(ps_global->mail_stream))), -1)) == TCL_OK
+ && Tcl_ListObjAppendElement(interp,
+ Tcl_GetObjResult(interp),
+ Tcl_NewStringObj(mn_get_revsort(sp_msgmap(ps_global->mail_stream)) ? "1" : "0", 1)) == TCL_OK)
+ ? TCL_OK : TCL_ERROR);
+}
+
+
+int
+peAppendDefaultSort(Tcl_Interp *interp)
+{
+ return((Tcl_ListObjAppendElement(interp,
+ Tcl_GetObjResult(interp),
+ Tcl_NewStringObj(sort_name(ps_global->def_sort), -1)) == TCL_OK
+ && Tcl_ListObjAppendElement(interp,
+ Tcl_GetObjResult(interp),
+ Tcl_NewStringObj(ps_global->def_sort_rev ? "1" : "0", 1)) == TCL_OK)
+ ? TCL_OK : TCL_ERROR);
+}
+
+
+int
+peSelect(Tcl_Interp *interp, int objc, Tcl_Obj **objv, int matchflag)
+{
+ char *subcmd;
+ long n, i, diff, msgno;
+ int narrow, hidden;
+ MESSAGECACHE *mc;
+ extern MAILSTREAM *mm_search_stream;
+ extern long mm_search_count;
+
+ hidden = any_lflagged(sp_msgmap(ps_global->mail_stream), MN_HIDE) > 0L;
+ mm_search_stream = ps_global->mail_stream;
+ mm_search_count = 0L;
+
+ for(n = 1L; n <= ps_global->mail_stream->nmsgs; n++)
+ if((mc = mail_elt(ps_global->mail_stream, n)) != NULL){
+ mc->searched = 0;
+ mc->spare7 = 1;
+ }
+
+ /*
+ * CMD: select
+ *
+ * ARGS: subcmd subcmdargs
+ *
+ * Returns: flip "matchflag" private bit on all or none
+ * of the messages in the mailbox
+ */
+ if((subcmd = Tcl_GetStringFromObj(objv[0], NULL)) != NULL){
+ if(!strucmp(subcmd, "all")){
+ /*
+ * Args: <none>
+ */
+ if(matchflag & MN_SLCT){
+ if(objc != 1)
+ return(peSelectError(interp, subcmd));
+
+ agg_select_all(ps_global->mail_stream, sp_msgmap(ps_global->mail_stream), NULL, 1);
+ }
+ else if(matchflag & MN_SRCH){
+ for(n = 1L; n <= ps_global->mail_stream->nmsgs; n++)
+ set_lflag(ps_global->mail_stream, sp_msgmap(ps_global->mail_stream), n, matchflag, 1);
+ }
+
+ Tcl_SetResult(interp, "All", TCL_VOLATILE);
+ }
+ else if(!strucmp(subcmd, "none")){
+ /*
+ * Args: <none>
+ */
+ n = 0L;
+
+ if(matchflag & MN_SLCT){
+ if(objc != 1)
+ return(peSelectError(interp, subcmd));
+
+ agg_select_all(ps_global->mail_stream, sp_msgmap(ps_global->mail_stream), &n, 0);
+ }
+ else if(matchflag & MN_SRCH){
+ for(n = 1L; n <= ps_global->mail_stream->nmsgs; n++)
+ set_lflag(ps_global->mail_stream, sp_msgmap(ps_global->mail_stream), n, matchflag, 0);
+ }
+
+ Tcl_SetResult(interp, long2string(n), TCL_VOLATILE);
+ }
+ else if(!strucmp(subcmd, "searched")){
+ /*
+ * Args: <none>
+ */
+ for(n = 1L, i = 0; n <= ps_global->mail_stream->nmsgs; n++)
+ if(get_lflag(ps_global->mail_stream, sp_msgmap(ps_global->mail_stream), n, MN_SRCH)){
+ i++;
+ set_lflag(ps_global->mail_stream, sp_msgmap(ps_global->mail_stream), n, MN_SLCT, 1);
+ }
+
+ Tcl_SetResult(interp, long2string(i), TCL_VOLATILE);
+ }
+ else if(!strucmp(subcmd, "unsearched")){
+ /*
+ * Args: <none>
+ */
+ for(n = 1L, i = 0; n <= ps_global->mail_stream->nmsgs; n++)
+ if(get_lflag(ps_global->mail_stream, sp_msgmap(ps_global->mail_stream), n, MN_SRCH)){
+ i++;
+ set_lflag(ps_global->mail_stream, sp_msgmap(ps_global->mail_stream), n, MN_SLCT, 0);
+ }
+
+ Tcl_SetResult(interp, long2string(i), TCL_VOLATILE);
+ }
+ else{
+ if(!strucmp(subcmd, "narrow"))
+ narrow = 1;
+ else if(!strucmp(subcmd, "broad"))
+ narrow = 0;
+ else
+ return(peSelectError(interp, "invalid scope request"));
+
+ if(!(subcmd = Tcl_GetStringFromObj(objv[1], NULL)))
+ return(peSelectError(interp, "missing subcommand"));
+
+ if(!strucmp(subcmd, "num")){
+ if((i = peSelectNumber(interp, objc - 2, &objv[2], matchflag)) != TCL_OK)
+ return(i);
+ }
+ else if(!strucmp(subcmd, "date")){
+ if((i = peSelectDate(interp, objc - 2, &objv[2], matchflag)) != TCL_OK)
+ return(i);
+ }
+ else if(!strucmp(subcmd, "text")){
+ if((i = peSelectText(interp, objc - 2, &objv[2], matchflag)) != TCL_OK)
+ return(i);
+ }
+ else if(!strucmp(subcmd, "status")){
+ if((i = peSelectStatus(interp, objc - 2, &objv[2], matchflag)) != TCL_OK)
+ return(i);
+ }
+ else if(!strucmp(subcmd, "compound")){
+ char *s;
+ int nSearchList, nSearch;
+ Tcl_Obj **oSearchList, **oSearch;
+
+ /* BUG: should set up one SEARCHPGM to fit criteria and issue single search */
+
+ if(Tcl_ListObjGetElements(interp, objv[2], &nSearchList, &oSearchList) == TCL_OK){
+ for(i = 0; i < nSearchList; i++){
+ if(Tcl_ListObjGetElements(interp, oSearchList[i], &nSearch, &oSearch) == TCL_OK){
+ if((s = Tcl_GetStringFromObj(oSearch[0], NULL)) != NULL){
+ if(!strucmp(s,"date")){
+ if((n = peSelectDate(interp, nSearch - 1, &oSearch[1], matchflag)) != TCL_OK)
+ return(n);
+ }
+ else if(!strucmp(s,"text")){
+ if((n = peSelectText(interp, nSearch - 1, &oSearch[1], matchflag)) != TCL_OK)
+ return(n);
+ }
+ else if(!strucmp(s,"status")){
+ if((n = peSelectStatus(interp, nSearch - 1, &oSearch[1], matchflag)) != TCL_OK)
+ return(n);
+ }
+ else
+ return(peSelectError(interp, "unknown compound search"));
+
+ /* logical AND the results */
+ mm_search_count = 0L;
+ for(n = 1L; n <= ps_global->mail_stream->nmsgs; n++)
+ if((mc = mail_elt(ps_global->mail_stream, n)) != NULL){
+ if(mc->searched && mc->spare7)
+ mm_search_count++;
+ else
+ mc->searched = mc->spare7 = 0;
+ }
+ }
+ else
+ return(peSelectError(interp, "malformed compound search"));
+ }
+ else
+ return(peSelectError(interp, "malformed compound search"));
+ }
+ }
+ else
+ return(peSelectError(interp, "malformed compound search"));
+ }
+ else
+ return(peSelectError(interp, "cmd cmdargs"));
+
+ /*
+ * at this point all interesting messages should
+ * have searched bit lit
+ */
+
+ if(narrow) /* make sure something was selected */
+ for(i = 1L; i <= mn_get_total(sp_msgmap(ps_global->mail_stream)); i++)
+ if(mail_elt(ps_global->mail_stream,
+ mn_m2raw(sp_msgmap(ps_global->mail_stream), i))->searched){
+ if(get_lflag(ps_global->mail_stream, sp_msgmap(ps_global->mail_stream), i, matchflag))
+ break;
+ else
+ mm_search_count--;
+ }
+
+ diff = 0L;
+ if(mm_search_count){
+ /*
+ * loop thru all the messages, adjusting local flag bits
+ * based on their "searched" bit...
+ */
+ for(i = 1L, msgno = 0L; i <= mn_get_total(sp_msgmap(ps_global->mail_stream)); i++)
+ if(narrow){
+ /* turning OFF selectedness if the "searched" bit isn't lit. */
+ if(get_lflag(ps_global->mail_stream, sp_msgmap(ps_global->mail_stream), i, matchflag)){
+ if(!mail_elt(ps_global->mail_stream,
+ mn_m2raw(sp_msgmap(ps_global->mail_stream), i))->searched){
+ diff--;
+ set_lflag(ps_global->mail_stream, sp_msgmap(ps_global->mail_stream), i, matchflag, 0);
+ if(hidden)
+ set_lflag(ps_global->mail_stream, sp_msgmap(ps_global->mail_stream), i, MN_HIDE, 1);
+ }
+ else if(msgno < mn_get_cur(sp_msgmap(ps_global->mail_stream)))
+ msgno = i;
+ }
+ }
+ else if(mail_elt(ps_global->mail_stream,mn_m2raw(sp_msgmap(ps_global->mail_stream),i))->searched){
+ /* turn ON selectedness if "searched" bit is lit. */
+ if(!get_lflag(ps_global->mail_stream, sp_msgmap(ps_global->mail_stream), i, matchflag)){
+ diff++;
+ set_lflag(ps_global->mail_stream, sp_msgmap(ps_global->mail_stream), i, matchflag, 1);
+ if(hidden)
+ set_lflag(ps_global->mail_stream, sp_msgmap(ps_global->mail_stream), i, MN_HIDE, 0);
+ }
+ }
+
+ /* if we're zoomed and the current message was unselected */
+ if(narrow && msgno
+ && get_lflag(ps_global->mail_stream,sp_msgmap(ps_global->mail_stream),mn_get_cur(sp_msgmap(ps_global->mail_stream)),MN_HIDE))
+ mn_reset_cur(sp_msgmap(ps_global->mail_stream), msgno);
+ }
+
+ Tcl_SetResult(interp, long2string(diff), TCL_VOLATILE);
+ }
+
+ return(TCL_OK);
+ }
+
+ Tcl_SetResult(interp, "Can't read select option", TCL_STATIC);
+ return(TCL_ERROR);
+}
+
+int
+peSelectNumber(Tcl_Interp *interp, int objc, Tcl_Obj **objv, int matchflag)
+{
+ /*
+ * Args: [broad | narrow] firstnumber lastnumber
+ */
+
+ long first = 0L, last = 0L, n;
+
+ if(objc == 2){
+ if(Tcl_GetLongFromObj(interp, objv[0], &first) == TCL_OK
+ && Tcl_GetLongFromObj(interp, objv[1], &last) == TCL_OK){
+ if(last && last < first){
+ n = last;
+ last = first;
+ first = n;
+ }
+
+ if(first >= 1L && first <= mn_get_total(sp_msgmap(ps_global->mail_stream))){
+ if(last){
+ if(last >= 1L && last <= mn_get_total(sp_msgmap(ps_global->mail_stream))){
+ for(n = first; n <= last; n++)
+ mm_searched(ps_global->mail_stream,
+ mn_m2raw(sp_msgmap(ps_global->mail_stream), n));
+ }
+ else
+ return(peSelectError(interp, "last out of range"));
+ }
+ else{
+ mm_searched(ps_global->mail_stream,
+ mn_m2raw(sp_msgmap(ps_global->mail_stream), first));
+ }
+ }
+ else
+ return(peSelectError(interp, "first out of range"));
+ }
+ else
+ return(peSelectError(interp, "can't read first/last"));
+ }
+ else
+ return(peSelectError(interp, "num first last"));
+
+ return(TCL_OK);
+}
+
+int
+peSelectDate(Tcl_Interp *interp, int objc, Tcl_Obj **objv, int matchflag)
+{
+ /*
+ * Args: [broad | narrow]
+ * tense - "on", "since", "before"
+ * year - 4 digit year
+ * month - abbreviated month "jan", "feb"...
+ * day - day number
+ */
+
+ char *tense, *year, *month, *day, buf[256];
+
+ if(objc == 4){
+ if((tense = peSelValTense(objv[0])) != NULL){
+ if((year = peSelValYear(objv[1])) != NULL){
+ if((month = peSelValMonth(objv[2])) != NULL){
+ if((day = peSelValDay(objv[3])) != NULL){
+ snprintf(buf, sizeof(buf), "%s %s-%s-%s", tense, day, month, year);
+ pine_mail_search_full(ps_global->mail_stream, NULL,
+ mail_criteria(buf),
+ SE_NOPREFETCH | SE_FREE);
+ }
+ else
+ return(peSelectError(interp, "<with valid day>"));
+ }
+ else
+ return(peSelectError(interp, "<with valid month>"));
+ }
+ else
+ return(peSelectError(interp, "<with valid year>"));
+ }
+ else
+ return(peSelectError(interp, "<with valid tense>"));
+ }
+ else
+ return(peSelectError(interp, "date tense year monthabbrev daynum"));
+
+ return(TCL_OK);
+}
+
+int
+peSelectText(Tcl_Interp *interp, int objc, Tcl_Obj **objv, int matchflag)
+{
+ /*
+ * Args: [broad | narrow]
+ * case - in not
+ * field - to from cc recip partic subj any
+ * text - free text search string
+ */
+ int not;
+ char field, *text;
+
+ if(objc == 3){
+ if((not = peSelValCase(objv[0])) >= 0){
+ if((field = peSelValField(objv[1])) != '\0'){
+ if((text = Tcl_GetStringFromObj(objv[2], NULL))
+ && strlen(text) < 1024){
+ /* BUG: fix charset not to be NULL below */
+ if(agg_text_select(ps_global->mail_stream,
+ sp_msgmap(ps_global->mail_stream),
+ field, NULL, not, 0, text, NULL, NULL))
+ /* BUG: plug in "charset" above? */
+ return(peSelectError(interp, "programmer botch"));
+ }
+ else
+ return(peSelectError(interp, "<with search string < 1024>"));
+ }
+ else
+ return(peSelectError(interp, "<with valid field>"));
+ }
+ else
+ return(peSelectError(interp, "<with valid case>"));
+ }
+ else
+ return(peSelectError(interp, "text case field text"));
+
+ return(TCL_OK);
+}
+
+int
+peSelectStatus(Tcl_Interp *interp, int objc, Tcl_Obj **objv, int matchflag)
+{
+ /*
+ * Args: [broad | narrow]
+ * case - on not
+ * status - imp new ans del
+ */
+ int not;
+ char flag;
+
+ if(objc == 2){
+ if((not = peSelValCase(objv[0])) >= 0){
+ if((flag = peSelValFlag(objv[1])) != '\0'){
+ if(agg_flag_select(ps_global->mail_stream, not, flag, NULL))
+ return(peSelectError(interp, "programmer botch"));
+ }
+ else
+ return(peSelectError(interp, "<with valid flag>"));
+ }
+ else
+ return(peSelectError(interp, "<with valid case>"));
+ }
+ else
+ return(peSelectError(interp, "status focus case flag"));
+
+ return(TCL_OK);
+}
+
+char *
+peSelValTense(Tcl_Obj *objp)
+{
+ char *tense, **pp;
+
+ if((tense = Tcl_GetStringFromObj(objp, NULL)) != NULL){
+ static char *tenses[] = {"on", "since", "before", NULL};
+
+ for(pp = tenses; *pp; pp++)
+ if(!strucmp(*pp, tense))
+ return(tense);
+ }
+
+ return(NULL);
+}
+
+
+char *
+peSelValYear(Tcl_Obj *objp)
+{
+ char *year;
+
+ return((year = Tcl_GetStringFromObj(objp, NULL))
+ && strlen(year) == 4
+ && isdigit((unsigned char) year[0])
+ && isdigit((unsigned char) year[0])
+ && isdigit((unsigned char) year[0])
+ ? year
+ : NULL);
+}
+
+
+char *
+peSelValMonth(Tcl_Obj *objp)
+{
+ char *month, **pp;
+ static char *mons[] = {"jan","feb","mar","apr",
+ "may","jun","jul","aug",
+ "sep","oct","nov","dec", NULL};
+
+ if((month = Tcl_GetStringFromObj(objp, NULL)) && strlen(month) == 3)
+ for(pp = mons; *pp; pp++)
+ if(!strucmp(month, *pp))
+ return(*pp);
+
+ return(NULL);
+}
+
+
+char *
+peSelValDay(Tcl_Obj *objp)
+{
+ char *day;
+
+ return(((day = Tcl_GetStringFromObj(objp, NULL))
+ && (day[0] == '0' || day[0] == '1'
+ || day[0] == '2' || day[0] == '3')
+ && isdigit((unsigned char) day[1])
+ && day[2] == '\0')
+ ? day
+ : NULL);
+}
+
+
+int
+peSelValCase(Tcl_Obj *objp)
+{
+ char *not;
+
+ if((not = Tcl_GetStringFromObj(objp, NULL)) != NULL){
+ if(!strucmp(not, "ton"))
+ return(0);
+ else if(!strucmp(not, "not"))
+ return(1);
+ }
+
+ return(-1);
+}
+
+
+int
+peSelValField(Tcl_Obj *objp)
+{
+ char *field;
+ int i;
+ static struct {
+ char *field;
+ int type;
+ } fields[] = {{"from", 'f'},
+ {"to", 't'},
+ {"cc", 'c'},
+ {"subj", 's'},
+ {"any", 'a'},
+ {"recip", 'r'},
+ {"partic", 'p'},
+ {"body", 'b'},
+ {NULL,0}};
+
+ if((field = Tcl_GetStringFromObj(objp, NULL)) != NULL)
+ for(i = 0; fields[i].field ; i++)
+ if(!strucmp(fields[i].field, field))
+ return(fields[i].type);
+
+ return(0);
+}
+
+
+int
+peSelValFlag(Tcl_Obj *objp)
+{
+ char *flag;
+ int i;
+ static struct {
+ char *flag;
+ int type;
+ } flags[] = {{"imp", '*'},
+ {"new", 'n'},
+ {"ans", 'a'},
+ {"del", 'd'},
+ {NULL,0}};
+
+ if((flag = Tcl_GetStringFromObj(objp, NULL)) != NULL)
+ for(i = 0; flags[i].flag ; i++)
+ if(!strucmp(flags[i].flag, flag))
+ return(flags[i].type);
+
+ return(0);
+}
+
+int
+peSelected(Tcl_Interp *interp, int objc, Tcl_Obj **objv, int matchflag)
+{
+ int rv = 0;
+ long i, n;
+ char *range;
+
+ /*
+ * CMD: searched [before | after] #
+ *
+ * Returns: 1 if criteria is true, 0 otherwise
+ */
+
+ if((range = Tcl_GetStringFromObj(objv[0], NULL))
+ && Tcl_GetLongFromObj(interp, objv[1], &n) != TCL_ERROR){
+ if(!strucmp(range, "before")){
+ for(i = 1L; i < n && i <= mn_get_total(sp_msgmap(ps_global->mail_stream)); i++)
+ if(get_lflag(ps_global->mail_stream, sp_msgmap(ps_global->mail_stream), i, matchflag)){
+ rv = 1;
+ break;
+ }
+
+ Tcl_SetResult(interp, int2string(rv), TCL_STATIC);
+ return(TCL_OK);
+ }
+ else if(!strucmp(range, "after")){
+ for(i = n + 1L; i <= mn_get_total(sp_msgmap(ps_global->mail_stream)); i++)
+ if(get_lflag(ps_global->mail_stream, sp_msgmap(ps_global->mail_stream), i, matchflag)){
+ rv = 1;
+ break;
+ }
+
+ Tcl_SetResult(interp, int2string(rv), TCL_STATIC);
+ return(TCL_OK);
+ }
+ }
+
+ Tcl_SetResult(interp, "searched test failed", TCL_STATIC);
+ return(TCL_ERROR);
+}
+
+
+int
+peSelectError(Tcl_Interp *interp, char *usage)
+{
+ char buf[256];
+
+ snprintf(buf, sizeof(buf), "should be select %.128s", usage);
+ Tcl_SetResult(interp, buf, TCL_VOLATILE);
+ return(TCL_ERROR);
+}
+
+
+int
+peApply(Tcl_Interp *interp, int objc, Tcl_Obj **objv)
+{
+ char *subcmd;
+ long n;
+
+ if(!(n = any_lflagged(sp_msgmap(ps_global->mail_stream), MN_SLCT))){
+ Tcl_SetResult(interp, "No messages selected", TCL_STATIC);
+ return(TCL_ERROR);
+ }
+ else if((subcmd = Tcl_GetStringFromObj(objv[0], NULL)) != NULL){
+ if(objc == 1){
+ if(!strucmp(subcmd, "delete")){
+ /* BUG: is CmdWhere arg always right? */
+ (void) cmd_delete(ps_global, sp_msgmap(ps_global->mail_stream), MCMD_AGG | MCMD_SILENT, NULL);
+ Tcl_SetResult(interp, long2string(n), TCL_STATIC);
+ return(TCL_OK);
+ }
+ else if(!strucmp(subcmd, "undelete")){
+ (void) cmd_undelete(ps_global, sp_msgmap(ps_global->mail_stream), MCMD_AGG | MCMD_SILENT);
+ Tcl_SetResult(interp, long2string(n), TCL_STATIC);
+ return(TCL_OK);
+ }
+ }
+ else if(objc == 2){
+ if(!strucmp(subcmd, "count")){
+ /*
+ * Args: flag
+ */
+ char *flagname;
+ long n, rawno, count = 0;
+
+ if((flagname = Tcl_GetStringFromObj(objv[1], NULL)) != NULL){
+ for(n = 1L; n <= mn_get_total(sp_msgmap(ps_global->mail_stream)); n++){
+ rawno = mn_m2raw(sp_msgmap(ps_global->mail_stream), n);
+ if(get_lflag(ps_global->mail_stream, NULL, rawno, MN_SLCT)
+ && peIsFlagged(ps_global->mail_stream,
+ mail_uid(ps_global->mail_stream, rawno),
+ flagname)){
+ count++;
+ }
+ }
+ }
+
+ Tcl_SetResult(interp, long2string(count), TCL_VOLATILE);
+ return(TCL_OK);
+ }
+ }
+ else if(objc == 3){
+ if(!strucmp(subcmd, "flag")){
+ /*
+ * Args: case - on not
+ * flag - imp new ans del
+ */
+ char flag, *result;
+ int not;
+ long flagged;
+
+ if((not = peSelValCase(objv[1])) >= 0){
+ if((flag = peSelValFlag(objv[2])) != '\0'){
+ result = peApplyFlag(ps_global->mail_stream, sp_msgmap(ps_global->mail_stream), flag, not, &flagged);
+ if(!result){
+ Tcl_SetResult(interp, int2string(flagged), TCL_VOLATILE);
+ return(TCL_OK);
+ }
+ else
+ return(peApplyError(interp, result));
+ }
+ else
+ return(peApplyError(interp, "invalid flag"));
+ }
+ else
+ return(peApplyError(interp, "invalid case"));
+ }
+ else if(!strucmp(subcmd, "save")){
+ /*
+ * Args: colid -
+ * folder - imp new ans del
+ */
+
+ int colid, flgs = 0, i;
+ char *folder, *err;
+ CONTEXT_S *cp;
+
+ if(Tcl_GetIntFromObj(interp, objv[1], &colid) != TCL_ERROR){
+
+ for(i = 0, cp = ps_global->context_list; cp ; i++, cp = cp->next)
+ if(i == colid){
+ if((folder = Tcl_GetStringFromObj(objv[2], NULL)) != NULL){
+ if(pseudo_selected(ps_global->mail_stream, sp_msgmap(ps_global->mail_stream))){
+
+ if(!READONLY_FOLDER(ps_global->mail_stream)
+ && F_OFF(F_SAVE_WONT_DELETE, ps_global))
+ flgs |= SV_DELETE;
+
+ if(colid == 0 && !strucmp(folder, "inbox"))
+ flgs |= SV_INBOXWOCNTXT;
+
+ i = save(ps_global, ps_global->mail_stream,
+ cp, folder, sp_msgmap(ps_global->mail_stream), flgs);
+
+ err = (i == mn_total_cur(sp_msgmap(ps_global->mail_stream))) ? NULL : "problem saving";
+
+ restore_selected(sp_msgmap(ps_global->mail_stream));
+ if(err)
+ return(peApplyError(interp, err));
+
+ Tcl_SetResult(interp, long2string(i), TCL_VOLATILE);
+ return(TCL_OK);
+ }
+ else
+ return(peApplyError(interp, "can't select"));
+ }
+ else
+ return(peApplyError(interp, "no folder name"));
+ }
+
+ return(peApplyError(interp, "bad colid"));
+ }
+ else
+ return(peApplyError(interp, "invalid case"));
+ }
+ else if(!strucmp(subcmd, "copy")){
+ /*
+ * Args: colid -
+ * folder - imp new ans del
+ */
+
+ int colid, flgs = 0, i;
+ char *folder, *err;
+ CONTEXT_S *cp;
+
+ if(Tcl_GetIntFromObj(interp, objv[1], &colid) != TCL_ERROR){
+
+ for(i = 0, cp = ps_global->context_list; cp ; i++, cp = cp->next)
+ if(i == colid){
+ if((folder = Tcl_GetStringFromObj(objv[2], NULL)) != NULL){
+ if(pseudo_selected(ps_global->mail_stream, sp_msgmap(ps_global->mail_stream))){
+
+ if(colid == 0 && !strucmp(folder, "inbox"))
+ flgs |= SV_INBOXWOCNTXT;
+
+ i = save(ps_global, ps_global->mail_stream,
+ cp, folder, sp_msgmap(ps_global->mail_stream), flgs);
+
+ err = (i == mn_total_cur(sp_msgmap(ps_global->mail_stream))) ? NULL : "problem copying";
+
+ restore_selected(sp_msgmap(ps_global->mail_stream));
+ if(err)
+ return(peApplyError(interp, err));
+
+ Tcl_SetResult(interp, long2string(i), TCL_VOLATILE);
+ return(TCL_OK);
+ }
+ else
+ return(peApplyError(interp, "can't select"));
+ }
+ else
+ return(peApplyError(interp, "no folder name"));
+ }
+
+ return(peApplyError(interp, "bad colid"));
+ }
+ else
+ return(peApplyError(interp, "invalid case"));
+ }
+ else if(!strucmp(subcmd, "move")){
+ /*
+ * Args: colid -
+ * folder - imp new ans del
+ */
+
+ int colid, flgs = 0, i;
+ char *folder, *err;
+ CONTEXT_S *cp;
+
+ if(Tcl_GetIntFromObj(interp, objv[1], &colid) != TCL_ERROR){
+
+ for(i = 0, cp = ps_global->context_list; cp ; i++, cp = cp->next)
+ if(i == colid){
+ if((folder = Tcl_GetStringFromObj(objv[2], NULL)) != NULL){
+ if(pseudo_selected(ps_global->mail_stream, sp_msgmap(ps_global->mail_stream))){
+
+ flgs = SV_DELETE;
+
+ if(colid == 0 && !strucmp(folder, "inbox"))
+ flgs |= SV_INBOXWOCNTXT;
+
+ i = save(ps_global, ps_global->mail_stream,
+ cp, folder, sp_msgmap(ps_global->mail_stream), flgs);
+
+ err = (i == mn_total_cur(sp_msgmap(ps_global->mail_stream))) ? NULL : "problem moving";
+
+ restore_selected(sp_msgmap(ps_global->mail_stream));
+ if(err)
+ return(peApplyError(interp, err));
+
+ Tcl_SetResult(interp, long2string(i), TCL_VOLATILE);
+ return(TCL_OK);
+ }
+ else
+ return(peApplyError(interp, "can't select"));
+ }
+ else
+ return(peApplyError(interp, "no folder name"));
+ }
+
+ return(peApplyError(interp, "bad colid"));
+ }
+ else
+ return(peApplyError(interp, "invalid case"));
+ }
+ else if(!strucmp(subcmd, "spam")){
+ /*
+ * Args: spamaddr -
+ * spamsubj -
+ */
+ char *spamaddr, *spamsubj = NULL;
+ long n, rawno;
+
+ if((spamaddr = Tcl_GetStringFromObj(objv[1], NULL))
+ && (spamsubj = Tcl_GetStringFromObj(objv[2], NULL))){
+ for(n = 1L; n <= mn_get_total(sp_msgmap(ps_global->mail_stream)); n++){
+ rawno = mn_m2raw(sp_msgmap(ps_global->mail_stream), n);
+ if(get_lflag(ps_global->mail_stream, NULL, rawno, MN_SLCT)){
+ char errbuf[WP_MAX_POST_ERROR + 1], *rs = NULL;
+
+ if((rs = peSendSpamReport(rawno, spamaddr, spamsubj, errbuf)) != NULL){
+ Tcl_SetResult(interp, rs, TCL_VOLATILE);
+ return(TCL_ERROR);
+ }
+ }
+ }
+ }
+
+ Tcl_SetResult(interp, "OK", TCL_VOLATILE);
+ return(TCL_OK);
+ }
+ }
+ }
+
+ return(peApplyError(interp, "unknown option"));
+}
+
+
+char *
+peApplyFlag(MAILSTREAM *stream, MSGNO_S *msgmap, char flag, int not, long *flagged)
+{
+ char *seq, *flagstr;
+ long flags, flagid;
+
+ switch (flag) {
+ case '*' :
+ flagstr = "\\FLAGGED";
+ flags = not ? 0L : ST_SET;
+ flagid = not ? F_FLAG : F_UNFLAG;
+ break;
+ case 'n' :
+ flagstr = "\\SEEN";
+ flags = not ? ST_SET : 0L;
+ flagid = not ? F_UNSEEN : F_SEEN;
+ break;
+ case 'a' :
+ flagstr = "\\ANSWERED";
+ flags = not ? 0L : ST_SET;
+ flagid = not ? F_ANS : F_UNANS;
+ break;
+ case 'd':
+ flagstr = "\\DELETED";
+ flags = not ? 0L : ST_SET;
+ flagid = not ? F_DEL : F_UNDEL;
+ break;
+ default :
+ return("unknown flag");
+ break;
+ }
+
+ if(pseudo_selected(stream, msgmap)){
+ if((seq = currentf_sequence(stream, msgmap, flagid, flagged, 1, NULL, NULL)) != NULL){
+ mail_flag(stream, seq, flagstr, flags);
+ fs_give((void **) &seq);
+ }
+
+ restore_selected(msgmap);
+ return(NULL);
+ }
+ else
+ return("can't select");
+}
+
+
+int
+peApplyError(Tcl_Interp *interp, char *usage)
+{
+ char buf[256];
+
+ snprintf(buf, sizeof(buf), "apply error: %.128s", usage);
+ Tcl_SetResult(interp, buf, TCL_VOLATILE);
+ return(TCL_ERROR);
+}
+
+
+/*
+ * peIndexFormat - Return with interp's result object set to
+ * represent the index line's format as a list of
+ * index-field-name, percentage-width pairs
+ */
+int
+peIndexFormat(Tcl_Interp *interp)
+{
+ INDEX_COL_S *cdesc = NULL;
+ char *name, wbuf[4], *dname;
+
+ for(cdesc = ps_global->index_disp_format;
+ cdesc->ctype != iNothing;
+ cdesc++) {
+ dname = NULL;
+ switch(cdesc->ctype){
+ case iFStatus:
+ case iIStatus:
+ case iSIStatus:
+ dname = "iStatus";
+ case iStatus:
+ name = "Status";
+ break;
+
+ case iMessNo:
+ name = "Number";
+ break;
+
+ case iPrio:
+ case iPrioAlpha:
+ case iPrioBang:
+ name = "Priority";
+ break;
+
+ case iDate: case iSDate: case iSTime: case iLDate:
+ case iS1Date: case iS2Date: case iS3Date: case iS4Date: case iDateIso:
+ case iDateIsoS:
+ case iSDateIso: case iSDateIsoS:
+ case iSDateS1: case iSDateS2:
+ case iSDateS3: case iSDateS4:
+ case iSDateTime:
+ case iSDateTimeIso: case iSDateTimeIsoS:
+ case iSDateTimeS1: case iSDateTimeS2:
+ case iSDateTimeS3: case iSDateTimeS4:
+ case iSDateTime24:
+ case iSDateTimeIso24: case iSDateTimeIsoS24:
+ case iSDateTimeS124: case iSDateTimeS224:
+ case iSDateTimeS324: case iSDateTimeS424:
+ case iCurDate: case iCurDateIso: case iCurDateIsoS:
+ case iCurTime24: case iCurTime12:
+ case iCurPrefDate:
+ name = "Date";
+ break;
+
+ case iCurDay: case iCurDay2Digit:
+ case iCurDayOfWeek: case iCurDayOfWeekAbb:
+ name = "Day";
+ break;
+
+ case iCurMon: case iCurMon2Digit:
+ case iCurMonLong: case iCurMonAbb:
+ name= "Month";
+ break;
+
+ case iTime24: case iTime12: case iTimezone:
+ case iCurPrefTime:
+ name = "Time";
+ break;
+
+ case iDay2Digit: case iDayOfWeek: case iDayOfWeekAbb:
+ name = "Day";
+ break;
+
+ case iMonAbb: case iMon2Digit:
+ name = "Month";
+ break;
+
+ case iYear: case iYear2Digit:
+ case iCurYear: case iCurYear2Digit:
+ name = "Year";
+ break;
+
+ case iScore :
+ name = "Score";
+ break;
+
+ case iFromTo:
+ case iFromToNotNews:
+ case iFrom:
+ name = "From";
+ break;
+
+ case iTo:
+ case iToAndNews :
+ name = "To";
+ break;
+
+ case iCc:
+ name = "Cc";
+ break;
+
+ case iRecips:
+ name = "Recipients";
+ break;
+
+ case iSender:
+ name = "Sender";
+ break;
+
+ case iSize :
+ case iSizeComma :
+ case iSizeNarrow :
+ case iDescripSize:
+ case iKSize :
+ name = "Size";
+ break;
+
+ case iAtt:
+ name = "Attachments";
+ break;
+
+ case iAddress :
+ name = "Address";
+ break;
+
+ case iMailbox :
+ name = "Mailbox";
+ break;
+
+ case iSubject :
+ case iSubjKey :
+ case iSubjKeyInit :
+ case iSubjectText :
+ case iSubjKeyText :
+ case iSubjKeyInitText :
+ name = "Subject";
+ break;
+
+ case iNews:
+ case iNewsAndTo :
+ name = "News";
+ break;
+
+ case iNewsAndRecips:
+ name = "News/Recip";
+ break;
+
+ case iRecipsAndNews:
+ name = "Recip/News";
+ break;
+
+ default :
+ name = "";
+ break;
+ }
+
+ if(cdesc->width > 0){
+ int p = ((cdesc->width * 100) / FAKE_SCREEN_WIDTH);
+
+ snprintf(wbuf, sizeof(wbuf), "%d%%", p);
+ }
+ else
+ wbuf[0] = '\0';
+
+ if(peAppListF(interp, Tcl_GetObjResult(interp), "%s%s%s", name, wbuf, dname) != TCL_OK)
+ return(TCL_ERROR);
+ }
+
+ return(TCL_OK);
+}
+
+
+int
+peNewMailResult(Tcl_Interp *interp)
+{
+ unsigned long n, uid;
+
+ if(sp_mail_box_changed(ps_global->mail_stream)){
+ if((n = sp_mail_since_cmd(ps_global->mail_stream)) != 0L){
+ /* first element is count of new messages */
+ if(Tcl_ListObjAppendElement(interp,
+ Tcl_GetObjResult(interp),
+ Tcl_NewLongObj(n)) != TCL_OK)
+ return(TCL_ERROR);
+
+ /* second element is UID of most recent message */
+ for(uid = ps_global->mail_stream->nmsgs; uid > 1L; uid--)
+ if(!get_lflag(ps_global->mail_stream, NULL, uid, MN_EXLD))
+ break;
+
+ if(!uid){
+ Tcl_ResetResult(interp);
+ Tcl_SetResult(interp, "0 0 0", TCL_STATIC);
+ return(TCL_ERROR);
+ }
+
+ uid = mail_uid(ps_global->mail_stream, uid);
+
+ if(Tcl_ListObjAppendElement(interp,
+ Tcl_GetObjResult(interp),
+ Tcl_NewLongObj(uid)) != TCL_OK)
+ return(TCL_ERROR);
+ }
+ else {
+ if(Tcl_ListObjAppendElement(interp,
+ Tcl_GetObjResult(interp),
+ Tcl_NewIntObj(0)) != TCL_OK)
+ return(TCL_ERROR);
+
+ /* zero is UID of new message */
+ if(Tcl_ListObjAppendElement(interp,
+ Tcl_GetObjResult(interp),
+ Tcl_NewIntObj(0)) != TCL_OK)
+ return(TCL_ERROR);
+ }
+
+ /* third element is expunge count */
+ if(Tcl_ListObjAppendElement(interp,
+ Tcl_GetObjResult(interp),
+ Tcl_NewLongObj(sp_expunge_count(ps_global->mail_stream)
+ ? sp_expunge_count(ps_global->mail_stream)
+ : 0L)) != TCL_OK)
+ return(TCL_ERROR);
+
+ }
+ else
+ Tcl_SetResult(interp, "0 0 0", TCL_STATIC);
+
+ return(TCL_OK);
+}
+
+
+/* * * * * * * * Start of Per-Thread/SubThread access functions * * * * * * * */
+
+
+/*
+ * PEThreadCmd - access/manipulate various pieces of thread state
+ */
+int
+PEThreadCmd(ClientData clientData, Tcl_Interp *interp, int objc, Tcl_Obj *CONST objv[])
+{
+ char *err, errbuf[256], *cmd, *op;
+ imapuid_t uid;
+
+ dprint((2, "PEThreadCmd"));
+
+ snprintf(err = errbuf, sizeof(errbuf), "Unknown %s request",
+ Tcl_GetStringFromObj(objv[0], NULL));
+
+ if(!(ps_global && ps_global->mail_stream)){
+ snprintf(err = errbuf, sizeof(errbuf), "%s: No open mailbox",
+ Tcl_GetStringFromObj(objv[0], NULL));
+ }
+ else if(objc < 2){
+ Tcl_WrongNumArgs(interp, 1, objv, "uid cmd ?args?");
+ }
+ else if(Tcl_GetLongFromObj(interp, objv[1], &uid) != TCL_OK){
+ return(TCL_ERROR); /* conversion problem? */
+ }
+ else if(!peSequenceNumber(uid)){
+ snprintf(err = errbuf, sizeof(errbuf), "%s: UID %ld doesn't exist",
+ Tcl_GetStringFromObj(objv[0], NULL), uid);
+ }
+ else if((cmd = Tcl_GetStringFromObj(objv[2], NULL)) != NULL){
+ if(objc == 3){
+ if(!strucmp(cmd,"info")){
+#define WP_MAX_THRD_PREFIX 256
+ long raw;
+ PINETHRD_S *pthrd;
+ char tstr[WP_MAX_THRD_PREFIX];
+
+ if((raw = peSequenceNumber(uid)) != 0L){
+ /*
+ * translate PINETHRD_S data into
+ */
+ if((pthrd = msgno_thread_info(ps_global->mail_stream, raw, NULL, THD_TOP)) != NULL){
+
+ tstr[0] = '\0';
+/* BUG: build tstr form pthrd */
+
+
+ Tcl_ListObjAppendElement(interp, Tcl_GetObjResult(interp),
+ Tcl_NewStringObj(tstr, -1));
+ }
+ }
+ else
+ Tcl_SetResult(interp, "0", TCL_STATIC);
+
+ return(TCL_OK);
+ }
+
+ }
+ else if(objc == 5){
+ if(!strucmp(cmd,"flag")){
+ if((op = Tcl_GetStringFromObj(objv[3], NULL)) != NULL){
+ if(!strucmp(op,"deleted")){
+ int value;
+
+ if(Tcl_GetIntFromObj(interp, objv[4], &value) != TCL_ERROR){
+ long n;
+ PINETHRD_S *pthrd;
+ char *flag;
+
+ while(1){
+ if(!(n = peSequenceNumber(uid))){
+ Tcl_SetResult(interp, "Unrecognized UID", TCL_STATIC);
+ return(TCL_ERROR);
+ }
+
+ flag = cpystr("\\DELETED");
+ mail_flag(ps_global->mail_stream, long2string(n), flag, (value ? ST_SET : 0L));
+ fs_give((void **) &flag);
+
+ Tcl_ListObjAppendElement(interp, Tcl_GetObjResult(interp),
+ Tcl_NewStringObj(ulong2string(uid), -1));
+
+ if(++n <= ps_global->mail_stream->nmsgs){
+ uid = mail_uid(ps_global->mail_stream, n);
+ }
+ else
+ break;
+
+ if((pthrd = msgno_thread_info(ps_global->mail_stream, n, NULL,THD_TOP)) != NULL){
+ }
+ else
+ break;
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+
+ Tcl_SetResult(interp, err, TCL_STATIC);
+ return(TCL_ERROR);
+}
+
+
+
+/* * * * * * * * Start of Per-Message access functions * * * * * * * */
+
+
+
+static struct _message_cmds {
+ char *cmd;
+ int hcount;
+ struct {
+ int argcount;
+ int (*f)(Tcl_Interp *, imapuid_t, int, Tcl_Obj **);
+ } h[3];
+} message_cmds[] = {
+ {"size", 1, {{3, peMessageSize}}},
+ {"date", 2, {{3, peMessageDate}, {4, peMessageDate}}},
+ {"subject", 1, {{3, peMessageSubject}}},
+ {"fromaddr", 1, {{3, peMessageFromAddr}}},
+ {"toaddr", 1, {{3, peMessageToAddr}}},
+ {"ccaddr", 1, {{3, peMessageCcAddr}}},
+ {"status", 1, {{3, peMessageStatus}}},
+ {"statusbits", 1, {{3, peMessageStatusBits}}},
+ {"charset", 1, {{3, peMessageCharset}}},
+ {"number", 1, {{3, peMsgnoFromUID}}},
+ {"envelope", 0},
+ {"rawenvelope", 0},
+ {"text", 1, {{3, peMessageText}}},
+ {"header", 1, {{3, peMessageHeader}}},
+ {"attachments", 1, {{3, peMessageAttachments}}},
+ {"body", 3, {{3, peMessageBody}, {4, peMessageBody}}},
+ {"cid", 1, {{4, peMessagePartFromCID}}},
+ {"flag", 2, {{4, peGetFlag}, {5, peSetFlag}}},
+ {"replyheaders", 2, {{3, peReplyHeaders},{4, peReplyHeaders}}},
+ {"replytext", 2, {{4, peReplyText}, {5, peReplyText}}},
+ {"forwardheaders", 2, {{3, peForwardHeaders}, {4, peForwardHeaders}}},
+ {"forwardtext", 2, {{3, peForwardText}, {4, peForwardText}}},
+ {"rawbody", 0},
+ {"select", 2, {{3, peMsgSelect}, {4, peMsgSelect}}},
+ {"detach", 1, {{5, peDetach}}},
+ {"attachinfo", 1, {{4, peAttachInfo}}},
+ {"savedefault", 1, {{3, peSaveDefault}}},
+ {"save", 1, {{5, peSave}}},
+ {"copy", 1, {{5, peCopy}}},
+ {"move", 1, {{5, peMove}}},
+ {"takeaddr", 1, {{3, peTakeaddr}}},
+ {"takefrom", 1, {{3, peTakeFrom}}},
+ {"replyquote", 1, {{3, peReplyQuote}}},
+ {"bounce", 2, {{4, peMessageBounce},{5, peMessageBounce}}},
+ {"spam", 1, {{5, peMessageSpamNotice}}},
+ {"needpasswd", 1, {{3, peMessageNeedPassphrase}}},
+ {NULL, 0}
+};
+
+
+
+
+/*
+ * PEMessageCmd - export various bits of message information
+ *
+ * NOTE: all exported commands are of the form:
+ *
+ * PEMessage <uid> <cmd> <args>
+ */
+int
+PEMessageCmd(ClientData clientData, Tcl_Interp *interp, int objc, Tcl_Obj *CONST objv[])
+{
+ char *err, errbuf[256], *cmd;
+ int i, j;
+ imapuid_t uid;
+
+ dprint((5, "PEMessageCmd"));
+
+ snprintf(err = errbuf, sizeof(errbuf), "Unknown %s request",
+ Tcl_GetStringFromObj(objv[0], NULL));
+
+ if(!(ps_global && ps_global->mail_stream)){
+ snprintf(err = errbuf, sizeof(errbuf), "%s: No open mailbox",
+ Tcl_GetStringFromObj(objv[0], NULL));
+ }
+ else if(objc < 3){
+ Tcl_WrongNumArgs(interp, 0, objv, "PEMessage <uid> cmd ?args?");
+ }
+ else if(Tcl_GetLongFromObj(interp, objv[1], &uid) != TCL_OK){
+ return(TCL_ERROR); /* conversion problem? */
+ }
+ else if(!peMessageNumber(uid)){
+ snprintf(err = errbuf, sizeof(errbuf), "%s: UID %ld doesn't exist",
+ Tcl_GetStringFromObj(objv[0], NULL), uid);
+ }
+ else if((cmd = Tcl_GetStringFromObj(objv[2], NULL)) != NULL){
+ for(i = 0; message_cmds[i].cmd; i++)
+ if(!strcmp(cmd, message_cmds[i].cmd)){
+ for(j = 0; j < message_cmds[i].hcount; j++)
+ if(message_cmds[i].h[j].argcount == objc)
+ return((*message_cmds[i].h[j].f)(interp, uid, objc - 3,
+ &((Tcl_Obj **)objv)[3]));
+
+ snprintf(err = errbuf, sizeof(errbuf),
+ "PEMessage: %s: mismatched argument count", cmd);
+ break;
+ }
+ }
+
+ Tcl_SetResult(interp, err, TCL_STATIC);
+ return(TCL_ERROR);
+}
+
+
+/*
+ * return the uid's ordinal number within the CURRENT SORT
+ */
+long
+peMessageNumber(imapuid_t uid)
+{
+ return(mn_raw2m(sp_msgmap(ps_global->mail_stream), peSequenceNumber(uid)));
+}
+
+/*
+ * return the uid's RAW message number (for c-client reference, primarily)
+ */
+long
+peSequenceNumber(imapuid_t uid)
+{
+ return(mail_msgno(ps_global->mail_stream, uid));
+}
+
+
+int
+peMessageSize(Tcl_Interp *interp, imapuid_t uid, int objc, Tcl_Obj **objv)
+{
+ long raw;
+
+ if((raw = peSequenceNumber(uid))
+ && pine_mail_fetchstructure(ps_global->mail_stream, raw, NULL)){
+ Tcl_SetResult(interp,
+ long2string(mail_elt(ps_global->mail_stream,
+ raw)->rfc822_size),
+ TCL_VOLATILE);
+ }
+ else
+ Tcl_SetResult(interp, "0", TCL_STATIC);
+
+ return(TCL_OK);
+}
+
+
+int
+peMessageDate(Tcl_Interp *interp, imapuid_t uid, int objc, Tcl_Obj **objv)
+{
+ char *cmd;
+ long raw;
+ ENVELOPE *env;
+ MESSAGECACHE mc;
+
+ if((raw = peSequenceNumber(uid))
+ && (env = pine_mail_fetchstructure(ps_global->mail_stream, raw, NULL))){
+ if(objc == 1 && objv[0]){
+ if(mail_parse_date(&mc, env->date)){
+ if((cmd = Tcl_GetStringFromObj(objv[0], NULL)) != NULL){
+ if(!strucmp(cmd,"day")){
+ snprintf(tmp_20k_buf, SIZEOF_20KBUF, "%02d", mc.day);
+ Tcl_SetResult(interp, tmp_20k_buf, TCL_VOLATILE);
+ return(TCL_OK);
+ }
+ else if(!strucmp(cmd,"month")){
+ Tcl_SetResult(interp, month_abbrev(mc.month), TCL_VOLATILE);
+ return(TCL_OK);
+ }
+ else if(!strucmp(cmd,"year")){
+ Tcl_SetResult(interp, int2string(mc.year + BASEYEAR), TCL_VOLATILE);
+ return(TCL_OK);
+ }
+ else{
+ snprintf(tmp_20k_buf, SIZEOF_20KBUF, "peMessageDate cmd: %.20s", cmd);
+ Tcl_SetResult(interp, tmp_20k_buf, TCL_VOLATILE);
+ }
+ }
+ else
+ Tcl_SetResult(interp, "peMessageDate: can't get command", TCL_STATIC);
+ }
+ else
+ Tcl_SetResult(interp, "peMessageDate: can't parse date", TCL_STATIC);
+ }
+ else{
+ Tcl_SetResult(interp, env->date ? (char *) env->date : "", TCL_VOLATILE);
+ return(TCL_OK);
+ }
+ }
+ else
+ Tcl_SetResult(interp, "Can't get message structure", TCL_STATIC);
+
+ return(TCL_ERROR);
+}
+
+
+int
+peMessageFromAddr(Tcl_Interp *interp, imapuid_t uid, int objc, Tcl_Obj **objv)
+{
+ return(peMessageField(interp, uid, "from"));
+}
+
+
+int
+peMessageToAddr(Tcl_Interp *interp, imapuid_t uid, int objc, Tcl_Obj **objv)
+{
+ return(peMessageField(interp, uid, "to"));
+}
+
+
+int
+peMessageCcAddr(Tcl_Interp *interp, imapuid_t uid, int objc, Tcl_Obj **objv)
+{
+ return(peMessageField(interp, uid, "cc"));
+}
+
+
+int
+peMessageSubject(Tcl_Interp *interp, imapuid_t uid, int objc, Tcl_Obj **objv)
+{
+ return(peMessageField(interp, uid, "subject"));
+}
+
+
+int
+peMessageField(Tcl_Interp *interp, imapuid_t uid, char *field)
+{
+ long raw;
+ char *s = "";
+ ENVELOPE *env;
+
+ if((raw = peSequenceNumber(uid))
+ && (env = pine_mail_fetchstructure(ps_global->mail_stream, raw, NULL))){
+ if(!strucmp(field, "from")){
+ if(env->from && env->from->mailbox)
+ snprintf(s = tmp_20k_buf, SIZEOF_20KBUF, "%.256s%s%.256s", env->from->mailbox,
+ (env->from->host) ? "@" : "", (env->from->host) ? env->from->host : "");
+ }
+ else if(!strucmp(field, "to")){
+ if(env->to && env->to->mailbox)
+ snprintf(s = tmp_20k_buf, SIZEOF_20KBUF, "%.256s%s%.256s", env->to->mailbox,
+ (env->to->host) ? "@" : "", (env->to->host) ? env->to->host : "");
+ }
+ else if(!strucmp(field, "cc")){
+ if(env->cc && env->cc->mailbox)
+ snprintf(s = tmp_20k_buf, SIZEOF_20KBUF, "%.256s%s%.256s", env->cc->mailbox,
+ (env->cc->host) ? "@" : "", (env->cc->host) ? env->cc->host : "");
+ }
+ else if(!strucmp(field, "subject")){
+ if(env->subject)
+ snprintf(s = tmp_20k_buf, SIZEOF_20KBUF, "%.256s", env->subject);
+ }
+ else{
+ snprintf(tmp_20k_buf, SIZEOF_20KBUF, "Unknown message field: %.20s", field);
+ Tcl_SetResult(interp, tmp_20k_buf, TCL_VOLATILE);
+ return(TCL_ERROR);
+ }
+
+ Tcl_SetResult(interp, s, TCL_VOLATILE);
+ return(TCL_OK);
+ }
+
+ Tcl_SetResult(interp, "Can't read message envelope", TCL_STATIC);
+ return(TCL_ERROR);
+}
+
+
+int
+peMessageStatus(Tcl_Interp *interp, imapuid_t uid, int objc, Tcl_Obj **objv)
+{
+ long raw;
+ MESSAGECACHE *mc;
+
+ if((raw = peSequenceNumber(uid)) != 0L){
+ if(!((mc = mail_elt(ps_global->mail_stream, raw))
+ && mc->valid)){
+ mail_fetch_flags(ps_global->mail_stream,
+ ulong2string(uid), FT_UID);
+ mc = mail_elt(ps_global->mail_stream, raw);
+ }
+
+ if (mc->deleted)
+ Tcl_ListObjAppendElement(interp,
+ Tcl_GetObjResult(interp),
+ Tcl_NewStringObj("Deleted", -1));
+
+ if (mc->answered)
+ Tcl_ListObjAppendElement(interp,
+ Tcl_GetObjResult(interp),
+ Tcl_NewStringObj("Answered", -1));
+
+ if (!mc->seen)
+ Tcl_ListObjAppendElement(interp,
+ Tcl_GetObjResult(interp),
+ Tcl_NewStringObj("New", -1));
+
+ if (mc->flagged)
+ Tcl_ListObjAppendElement(interp,
+ Tcl_GetObjResult(interp),
+ Tcl_NewStringObj("Important", -1));
+ }
+
+ return(TCL_OK);
+}
+
+
+int
+peMessageCharset(Tcl_Interp *interp, imapuid_t uid, int objc, Tcl_Obj **objv)
+{
+ /* everthing coming out of pith better be utf-8 */
+ Tcl_SetResult(interp, "UTF-8", TCL_STATIC);
+ return(TCL_OK);
+}
+
+
+int
+peMessageNeedPassphrase(Tcl_Interp *interp, imapuid_t uid, int objc, Tcl_Obj **objv)
+{
+#ifdef SMIME
+ return((ps_global && ps_global->smime && ps_global->smime->need_passphrase) ? TCL_OK : TCL_ERROR);
+#else
+ return(TCL_ERROR);
+#endif /* SMIME */
+}
+
+
+int
+peMsgnoFromUID(Tcl_Interp *interp, imapuid_t uid, int objc, Tcl_Obj **objv)
+{
+ Tcl_SetResult(interp, long2string(peMessageNumber(uid)), TCL_VOLATILE);
+ return(TCL_OK);
+}
+
+
+/*
+ * peInterpWritec - collect filtered output, appending to the
+ * command's result list on each EOL
+ */
+int
+peInterpWritec(int c)
+{
+ unsigned char ch = (unsigned char) (0xff & c);
+
+ if(ch == '\n')
+ return(peInterpFlush() == TCL_OK);
+ else
+ so_writec(ch, peED.store);
+
+ return(1);
+}
+
+
+/*
+ * peInterpFlush - write accumulated line to result object mapping
+ * embedded data into exportable tcl list members
+ *
+ */
+int
+peInterpFlush(void)
+{
+ char *line, *p, *tp, *tp2, col1[32], col2[32];
+ Tcl_Obj *lobjp, *objColor, *objPair;
+
+ line = (char *) so_text(peED.store);
+
+ if((lobjp = Tcl_NewListObj(0, NULL)) != NULL){
+ if((p = strindex(line, TAG_EMBED)) != NULL){
+ do{
+ *p = '\0';
+
+ if(p - line)
+ peAppListF(peED.interp, lobjp, "%s%s", "t", line);
+
+ switch(*++p){
+ case TAG_HANDLE :
+ {
+ int i, n;
+ HANDLE_S *h;
+
+
+ for(n = 0, i = *++p; i > 0; i--)
+ n = (n * 10) + (*++p - '0');
+
+ line = ++p; /* prepare for next section of line */
+
+ if(!peED.inhandle){
+ peED.inhandle = 1;
+
+ if((h = get_handle(peED.handles, n)) != NULL)
+ switch(h->type){
+ case IMG :
+ {
+ Tcl_Obj *llObj, *rObj;
+
+ llObj = Tcl_NewListObj(0, NULL);
+ Tcl_ListObjAppendElement(peED.interp, llObj, Tcl_NewStringObj("img", -1));
+
+ rObj = Tcl_NewListObj(0, NULL);
+ Tcl_ListObjAppendElement(peED.interp, rObj, Tcl_NewStringObj(h->h.img.src ? h->h.img.src : "", -1));
+ Tcl_ListObjAppendElement(peED.interp, rObj, Tcl_NewStringObj(h->h.img.alt ? h->h.img.alt : "", -1));
+
+ Tcl_ListObjAppendElement(peED.interp, llObj, rObj);
+
+ Tcl_ListObjAppendElement(peED.interp, lobjp, llObj);
+ peED.inhandle = 0;
+ }
+
+ break;
+
+ case URL :
+ {
+ Tcl_Obj *llObj, *rObj;
+
+ llObj = Tcl_NewListObj(0, NULL);
+ Tcl_ListObjAppendElement(peED.interp, llObj, Tcl_NewStringObj("urlstart", -1));
+
+ rObj = Tcl_NewListObj(0, NULL);
+ Tcl_ListObjAppendElement(peED.interp, rObj, Tcl_NewStringObj(h->h.url.path ? h->h.url.path : "", -1));
+ Tcl_ListObjAppendElement(peED.interp, rObj, Tcl_NewStringObj(h->h.url.name ? h->h.url.name : "", -1));
+
+ Tcl_ListObjAppendElement(peED.interp, llObj, rObj);
+
+ Tcl_ListObjAppendElement(peED.interp, lobjp, llObj);
+ }
+
+ break;
+
+ case Attach :
+ {
+ Tcl_Obj *alObj, *rObj, *tObj, *stObj, *fnObj, *eObj;
+
+ alObj = Tcl_NewListObj(0, NULL);
+ Tcl_ListObjAppendElement(peED.interp, alObj, Tcl_NewStringObj("attach", -1));
+
+ peGetMimeTyping(mail_body(ps_global->mail_stream,
+ peSequenceNumber(peED.uid),
+ (unsigned char *) h->h.attach->number),
+ &tObj, &stObj, &fnObj, &eObj);
+
+
+ rObj = Tcl_NewListObj(0, NULL);
+ Tcl_ListObjAppendElement(peED.interp, rObj, Tcl_NewLongObj(peED.uid));
+ Tcl_ListObjAppendElement(peED.interp, rObj, Tcl_NewStringObj(h->h.attach->number, -1));
+ Tcl_ListObjAppendElement(peED.interp, rObj, tObj);
+ Tcl_ListObjAppendElement(peED.interp, rObj, stObj);
+ Tcl_ListObjAppendElement(peED.interp, rObj, fnObj);
+ Tcl_ListObjAppendElement(peED.interp, rObj, eObj);
+
+ Tcl_ListObjAppendElement(peED.interp, alObj, rObj);
+
+ Tcl_ListObjAppendElement(peED.interp, lobjp, alObj);
+ }
+
+ break;
+
+ default :
+ break;
+ }
+ }
+ }
+
+ break;
+
+ case TAG_FGCOLOR :
+ if((tp = peColorStr(++p, col1)) && (strcmp(tp, peED.color.fg) || strcmp(tp, peED.color.fgdef))){
+ /* look ahead */
+ if(p[11] == TAG_EMBED
+ && p[12] == TAG_BGCOLOR
+ && (tp2 = peColorStr(p + 13, col2))){
+ objColor = Tcl_NewListObj(0, NULL);
+ objPair = Tcl_NewListObj(0, NULL);
+ Tcl_ListObjAppendElement(peED.interp, objColor, Tcl_NewStringObj("color", -1));
+ Tcl_ListObjAppendElement(peED.interp, objPair, Tcl_NewStringObj(tp, -1));
+ Tcl_ListObjAppendElement(peED.interp, objPair, Tcl_NewStringObj(tp2, -1));
+ Tcl_ListObjAppendElement(peED.interp, objColor, objPair);
+ Tcl_ListObjAppendElement(peED.interp, lobjp, objColor);
+ strcpy(peED.color.bg, tp2);
+ p += 13;
+ }
+ else if(strcmp(peED.color.bg, peED.color.bgdef)){
+ objColor = Tcl_NewListObj(0, NULL);
+ objPair = Tcl_NewListObj(0, NULL);
+ Tcl_ListObjAppendElement(peED.interp, objColor, Tcl_NewStringObj("color", -1));
+ Tcl_ListObjAppendElement(peED.interp, objPair, Tcl_NewStringObj(tp, -1));
+ Tcl_ListObjAppendElement(peED.interp, objPair, Tcl_NewStringObj(peED.color.bgdef, -1));
+ Tcl_ListObjAppendElement(peED.interp, objColor, objPair);
+ Tcl_ListObjAppendElement(peED.interp, lobjp, objColor);
+ strcpy(peED.color.bg, peED.color.bgdef);
+ }
+ else
+ peAppListF(peED.interp, lobjp, "%s%s", "fgcolor", tp);
+
+ strcpy(peED.color.fg, tp);
+ }
+
+ line = p + 11;
+ break;
+
+ case TAG_BGCOLOR :
+ if((tp = peColorStr(++p, col1)) && (strcmp(tp, peED.color.bg) || strcmp(tp, peED.color.bgdef))){
+ /* look ahead */
+ if(p[11] == TAG_EMBED
+ && p[12] == TAG_FGCOLOR
+ && (tp2 = peColorStr(p + 13, col2))){
+ objColor = Tcl_NewListObj(0, NULL);
+ objPair = Tcl_NewListObj(0, NULL);
+ Tcl_ListObjAppendElement(peED.interp, objColor, Tcl_NewStringObj("color", -1));
+ Tcl_ListObjAppendElement(peED.interp, objPair, Tcl_NewStringObj(tp2, -1));
+ Tcl_ListObjAppendElement(peED.interp, objPair, Tcl_NewStringObj(tp, -1));
+ Tcl_ListObjAppendElement(peED.interp, objColor, objPair);
+ Tcl_ListObjAppendElement(peED.interp, lobjp, objColor);
+ strcpy(peED.color.fg, tp2);
+ p += 13;
+ }
+ else if(strcmp(peED.color.fg, peED.color.fgdef)){
+ objColor = Tcl_NewListObj(0, NULL);
+ objPair = Tcl_NewListObj(0, NULL);
+ Tcl_ListObjAppendElement(peED.interp, objColor, Tcl_NewStringObj("color", -1));
+ Tcl_ListObjAppendElement(peED.interp, objPair, Tcl_NewStringObj(peED.color.fgdef, -1));
+ Tcl_ListObjAppendElement(peED.interp, objPair, Tcl_NewStringObj(tp, -1));
+ Tcl_ListObjAppendElement(peED.interp, objColor, objPair);
+ Tcl_ListObjAppendElement(peED.interp, lobjp, objColor);
+ strcpy(peED.color.fg, peED.color.fgdef);
+ }
+ else
+ peAppListF(peED.interp, lobjp, "%s%s", "bgcolor", tp);
+
+ strcpy(peED.color.bg, tp);
+ }
+
+ line = p + 11;
+ break;
+
+ case TAG_ITALICON :
+ peAppListF(peED.interp, lobjp, "%s%s", "italic", "on");
+ line = p + 1;
+ break;
+
+ case TAG_ITALICOFF :
+ peAppListF(peED.interp, lobjp, "%s%s", "italic", "off");
+ line = p + 1;
+ break;
+
+ case TAG_BOLDON :
+ peAppListF(peED.interp, lobjp, "%s%s", "bold", "on");
+ line = p + 1;
+ break;
+
+ case TAG_BOLDOFF :
+ peAppListF(peED.interp, lobjp, "%s%s", "bold", "off");
+ line = p + 1;
+ break;
+
+ case TAG_ULINEON :
+ peAppListF(peED.interp, lobjp, "%s%s", "underline", "on");
+ line = p + 1;
+ break;
+
+ case TAG_ULINEOFF :
+ peAppListF(peED.interp, lobjp, "%s%s", "underline", "off");
+ line = p + 1;
+ break;
+
+ case TAG_STRIKEON :
+ peAppListF(peED.interp, lobjp, "%s%s", "strikethru", "on");
+ line = p + 1;
+ break;
+
+ case TAG_STRIKEOFF :
+ peAppListF(peED.interp, lobjp, "%s%s", "strikethru", "off");
+ line = p + 1;
+ break;
+
+ case TAG_BIGON :
+ peAppListF(peED.interp, lobjp, "%s%s", "bigfont", "on");
+ line = p + 1;
+ break;
+
+ case TAG_BIGOFF :
+ peAppListF(peED.interp, lobjp, "%s%s", "bigfont", "off");
+ line = p + 1;
+ break;
+
+ case TAG_SMALLON :
+ peAppListF(peED.interp, lobjp, "%s%s", "smallfont", "on");
+ line = p + 1;
+ break;
+
+ case TAG_SMALLOFF :
+ peAppListF(peED.interp, lobjp, "%s%s", "smallfont", "off");
+ line = p + 1;
+ break;
+
+ case TAG_INVOFF :
+ case TAG_HANDLEOFF :
+ if(peED.inhandle){
+ peAppListF(peED.interp, lobjp, "%s%s", "urlend", "");
+ peED.inhandle = 0;
+ }
+ /* fall thru and advance "line" */
+
+ default :
+ line = p + 1;
+ break;
+ }
+
+ }
+ while((p = strindex(line, TAG_EMBED)) != NULL);
+
+ if(*line)
+ peAppListF(peED.interp, lobjp, "%s%s", "t", line);
+ }
+ else
+ peAppListF(peED.interp, lobjp, "%s%s", "t", line);
+ }
+ else
+ peAppListF(peED.interp, lobjp, "%s%s", "t", "");
+
+ if(Tcl_ListObjAppendElement(peED.interp, peED.obj, lobjp) == TCL_OK){
+ so_truncate(peED.store, 0L);
+ return(TCL_OK);
+ }
+
+ return(TCL_ERROR);
+}
+
+
+
+/*
+ * peInterpWritec - collect filtered output, appending to the
+ * command's result list on each EOL
+ */
+int
+peNullWritec(int c)
+{
+ return(1);
+}
+
+
+char *
+peColorStr(char *s, char *b)
+{
+ int i, j, color;
+
+ i = 0;
+ b[0] = '\0';
+ while(1){
+ color = 0;
+ for(j = 0; j < 3; j++, s++)
+ if(isdigit((unsigned char) *s))
+ color = (color * 10) + (*s - '0');
+
+ s++; /* advance past ',' */
+ if(color < 256)
+ sprintf(b + strlen(b), "%2.2x", color);
+ else
+ break;
+
+ if(++i == 3)
+ return(b);
+ }
+
+
+ return(NULL);
+}
+
+
+/*
+ * returns a list of elements
+ */
+int
+peMessageHeader(Tcl_Interp *interp, imapuid_t uid, int objc, Tcl_Obj **objv)
+{
+ MESSAGECACHE *mc;
+ HEADER_S h;
+ int flags, rv = TCL_OK;
+ long raw;
+#if 0
+ char *color;
+#endif
+
+ /*
+ * ONLY full header mode (raw) output should get written to the
+ * writec function we pass format_header. If there's something
+ * in the store after formatting ,we'll write it to the Tcl result
+ * then, not as its accumulated
+ */
+ peED.interp = interp;
+ peED.obj = Tcl_NewStringObj("", -1);
+
+ if(peED.store)
+ so_seek(peED.store, 0L, 0);
+ else
+ peED.store = so_get(CharStar, NULL, EDIT_ACCESS);
+
+ flags = FM_DISPLAY | FM_NEW_MESS | FM_NOEDITORIAL | FM_NOHTMLREL | FM_HTMLRELATED;
+
+#if 0
+ peED.color.fg[0] = '\0';
+ if((color = pico_get_last_fg_color()) && (color = color_to_asciirgb(color))){
+ peInterpWritec(TAG_EMBED);
+ peInterpWritec(TAG_FGCOLOR);
+ gf_puts(color, peInterpWritec);
+ strcpy(peED.color.fgdef, peColorStr(color, tmp_20k_buf));
+ }
+
+ peED.color.bg[0] = '\0';
+ if((color = pico_get_last_bg_color()) && (color = color_to_asciirgb(color))){
+ peInterpWritec(TAG_EMBED);
+ peInterpWritec(TAG_BGCOLOR);
+ gf_puts(color, peInterpWritec);
+ strcpy(peED.color.bgdef, peColorStr(color,tmp_20k_buf));
+ }
+
+ peInterpFlush();
+#endif
+
+ raw = peSequenceNumber(uid);
+ if(peED.uid != uid){
+ peED.uid = uid;
+ peED.body = NULL;
+
+ ps_global->c_client_error[0] = ps_global->last_error[0] = '\0';
+ if(!((peED.env = pine_mail_fetchstructure(ps_global->mail_stream, raw, &peED.body))
+ && (mc = mail_elt(ps_global->mail_stream, raw)))){
+ char buf[256];
+
+ snprintf(buf, sizeof(buf), "Error getting message %ld: %s", peMessageNumber(uid),
+ ps_global->last_error[0] ? ps_global->last_error : "Indeterminate");
+
+ dprint((1, "ERROR fetching %s of msg %ld: %s",
+ peED.env ? "elt" : "env", mn_get_cur(sp_msgmap(ps_global->mail_stream)),
+ ps_global->last_error[0] ? ps_global->last_error : "Indeterminate"));
+
+ Tcl_SetResult(interp, buf, TCL_VOLATILE);
+ rv = TCL_ERROR;
+ }
+ else{
+ zero_atmts(ps_global->atmts);
+#ifdef SMIME
+ if(ps_global && ps_global->smime && ps_global->smime->need_passphrase)
+ ps_global->smime->need_passphrase = 0;
+
+ fiddle_smime_message(peED.body, raw);
+#endif
+ describe_mime(peED.body, "", 1, 1, 0, flags);
+ }
+ }
+
+ /* NO HANDLES init_handles(&peED.handles);*/
+
+ /*
+ * Collect header pieces into lists via the passed custom formatter. Collect
+ * everything else in the storage object passed. The latter should only end up
+ * with raw header data.
+ *
+ * BUG: DEAL WITH COLORS
+ */
+ if(rv == TCL_OK){
+ HD_INIT(&h, ps_global->VAR_VIEW_HEADERS, ps_global->view_all_except, FE_DEFAULT);
+ if(format_header(ps_global->mail_stream, raw, NULL, peED.env, &h,
+ NULL, NULL, flags, peFormatEnvelope, peInterpWritec) != 0){
+ char buf[256];
+
+ snprintf(buf, sizeof(buf), "Error formatting header %ld", peMessageNumber(uid));
+ dprint((1, buf));
+
+ Tcl_SetResult(interp, buf, TCL_VOLATILE);
+ rv = TCL_ERROR;
+ }
+ }
+
+ peInterpFlush();
+ peAppListF(peED.interp, Tcl_GetObjResult(peED.interp), "%s%s%o", "raw", "", peED.obj);
+
+ so_give(&peED.store);
+ return(rv);
+}
+
+void
+peFormatEnvelope(MAILSTREAM *s, long int n, char *sect, ENVELOPE *e, gf_io_t pc, long int which, char *oacs, int flags)
+{
+ char *p2, buftmp[MAILTMPLEN];
+ Tcl_Obj *objHdr;
+
+ if(!e)
+ return;
+
+ if((which & FE_DATE) && e->date) {
+ if((objHdr = Tcl_NewListObj(0, NULL)) != NULL){
+ snprintf(buftmp, sizeof(buftmp), "%s", (char *) e->date);
+ buftmp[sizeof(buftmp)-1] = '\0';
+ p2 = (char *)rfc1522_decode_to_utf8((unsigned char *) tmp_20k_buf, SIZEOF_20KBUF, buftmp);
+ peFormatEnvelopeText("Date", p2);
+ }
+ /* BUG: how does error feedback bubble back up? */
+ }
+
+ if((which & FE_FROM) && e->from)
+ peFormatEnvelopeAddress(s, n, sect, "From", e->from, flags, oacs, pc);
+
+ if((which & FE_REPLYTO) && e->reply_to && (!e->from || !address_is_same(e->reply_to, e->from)))
+ peFormatEnvelopeAddress(s, n, sect, "Reply-To", e->reply_to, flags, oacs, pc);
+
+ if((which & FE_TO) && e->to)
+ peFormatEnvelopeAddress(s, n, sect, "To", e->to, flags, oacs, pc);
+
+ if((which & FE_CC) && e->cc)
+ peFormatEnvelopeAddress(s, n, sect, "Cc", e->cc, flags, oacs, pc);
+
+ if((which & FE_BCC) && e->bcc)
+ peFormatEnvelopeAddress(s, n, sect, "Bcc", e->bcc, flags, oacs, pc);
+
+ if((which & FE_RETURNPATH) && e->return_path)
+ peFormatEnvelopeAddress(s, n, sect, "Return-Path", e->return_path, flags, oacs, pc);
+
+ if((which & FE_NEWSGROUPS) && e->newsgroups)
+ peFormatEnvelopeNewsgroups("Newsgroups", e->newsgroups, flags, pc);
+
+ if((which & FE_FOLLOWUPTO) && e->followup_to)
+ peFormatEnvelopeNewsgroups("Followup-To", e->followup_to, flags, pc);
+
+ if((which & FE_SUBJECT) && e->subject && e->subject[0]){
+ if((objHdr = Tcl_NewListObj(0, NULL)) != NULL){
+ char *freeme = NULL;
+
+ p2 = iutf8ncpy((char *)(tmp_20k_buf+10000),
+ (char *)rfc1522_decode_to_utf8((unsigned char *)tmp_20k_buf, 10000, e->subject),
+ SIZEOF_20KBUF-10000);
+
+ if(flags & FM_DISPLAY
+ && (ps_global->display_keywords_in_subject
+ || ps_global->display_keywordinits_in_subject)){
+
+ /* don't bother if no keywords are defined */
+ if(some_user_flags_defined(s))
+ p2 = freeme = prepend_keyword_subject(s, n, p2,
+ ps_global->display_keywords_in_subject ? KW : KWInit,
+ NULL, ps_global->VAR_KW_BRACES);
+ }
+
+ peFormatEnvelopeText("Subject", p2);
+
+ if(freeme)
+ fs_give((void **) &freeme);
+ }
+ }
+
+ if((which & FE_SENDER) && e->sender && (!e->from || !address_is_same(e->sender, e->from)))
+ peFormatEnvelopeAddress(s, n, sect, "Sender", e->sender, flags, oacs, pc);
+
+ if((which & FE_MESSAGEID) && e->message_id){
+ p2 = iutf8ncpy((char *)(tmp_20k_buf+10000), (char *)rfc1522_decode_to_utf8((unsigned char *)tmp_20k_buf, 10000, e->message_id), SIZEOF_20KBUF-10000);
+ peFormatEnvelopeText("Message-ID", p2);
+ }
+
+ if((which & FE_INREPLYTO) && e->in_reply_to){
+ p2 = iutf8ncpy((char *)(tmp_20k_buf+10000), (char *)rfc1522_decode_to_utf8((unsigned char *)tmp_20k_buf, 10000, e->in_reply_to), SIZEOF_20KBUF-10000);
+ peFormatEnvelopeText("In-Reply-To", p2);
+ }
+
+ if((which & FE_REFERENCES) && e->references) {
+ p2 = iutf8ncpy((char *)(tmp_20k_buf+10000), (char *)rfc1522_decode_to_utf8((unsigned char *)tmp_20k_buf, 10000, e->references), SIZEOF_20KBUF-10000);
+ peFormatEnvelopeText("References", p2);
+ }
+}
+
+
+/*
+ * appends caller's result with: {"text" field_name {field_value}}
+ */
+void
+peFormatEnvelopeText(char *field_name, char *field_value)
+{
+ peAppListF(peED.interp, Tcl_GetObjResult(peED.interp), "%s%s%s", "text", field_name, field_value);
+}
+
+
+/*
+ * appends caller's result with: {"addr" field_name {{{personal} {mailbox}} ... }}
+ * {"rawaddr" field_name {{raw_address} ... }}
+ */
+void
+peFormatEnvelopeAddress(MAILSTREAM *stream, long int msgno, char *section, char *field_name,
+ struct mail_address *addr, int flags, char *oacs, gf_io_t pc)
+{
+ char *ptmp, *mtmp, *atype = "addr";
+ int group = 0;
+ ADDRESS *atmp;
+ Tcl_Obj *objAddrList = NULL;
+ STORE_S *tso;
+ gf_io_t tpc;
+ extern const char *rspecials;
+ extern const char *rspecials_minus_quote_and_dot;
+
+ if(!addr)
+ return;
+
+ /*
+ * quickly run down address list to make sure none are patently bogus.
+ * If so, just blat raw field out.
+ */
+ for(atmp = addr; stream && atmp; atmp = atmp->next)
+ if(atmp->host && atmp->host[0] == '.'){
+ char *field, *fields[2];
+
+ atype = "rawaddr";
+ if((objAddrList = Tcl_NewListObj(0,NULL)) == NULL)
+ return; /* BUG: handle list creation failure */
+
+ fields[1] = NULL;
+ fields[0] = cpystr(field_name);
+ if((ptmp = strchr(fields[0], ':')) != NULL)
+ *ptmp = '\0';
+
+ if((field = pine_fetchheader_lines(stream, msgno, section, fields)) != NULL){
+ char *h, *t;
+
+ for(t = h = field; *h ; t++)
+ if(*t == '\015' && *(t+1) == '\012'){
+ *t = '\0'; /* tie off line */
+
+ Tcl_ListObjAppendElement(peED.interp, objAddrList, Tcl_NewStringObj(h,-1));
+
+ if(!*(h = (++t) + 1)) /* set new h and skip CRLF */
+ break; /* no more to write */
+ }
+ else if(!*t){ /* shouldn't happen much */
+ if(h != t)
+ Tcl_ListObjAppendElement(peED.interp, objAddrList, Tcl_NewStringObj(h,-1));
+
+ break;
+ }
+
+ fs_give((void **)&field);
+ }
+
+ fs_give((void **)&fields[0]);
+ }
+
+ if(!objAddrList){
+ if((objAddrList = Tcl_NewListObj(0,NULL)) == NULL || (tso = so_get(CharStar, NULL, EDIT_ACCESS)) == NULL)
+ return; /* BUG: handle list creation failure */
+
+ gf_set_so_writec(&tpc, tso);
+
+ while(addr){
+
+ atmp = addr->next; /* remember what's next */
+ addr->next = NULL;
+ if(!addr->host && addr->mailbox){
+ mtmp = addr->mailbox;
+ addr->mailbox = cpystr((char *)rfc1522_decode_to_utf8(
+ (unsigned char *)tmp_20k_buf,
+ SIZEOF_20KBUF, addr->mailbox));
+ }
+
+ ptmp = addr->personal; /* RFC 1522 personal name? */
+ addr->personal = iutf8ncpy((char *)tmp_20k_buf, (char *)rfc1522_decode_to_utf8((unsigned char *)(tmp_20k_buf+10000), SIZEOF_20KBUF-10000, addr->personal), 10000);
+ tmp_20k_buf[10000-1] = '\0';
+
+
+ /* Logic taken from: pine_rfc822_write_address_noquote(addr, pc, &group); */
+ if (addr->host) { /* ordinary address? */
+ if (!(addr->personal || addr->adl)){
+ so_seek(tso, 0L, 0);
+ pine_rfc822_address (addr, tpc);
+ peAppListF(peED.interp, objAddrList, "%s%o", "", Tcl_NewStringObj((char *) so_text(tso), so_tell(tso)));
+ }
+ else { /* no, must use phrase <route-addr> form */
+ Tcl_Obj *objTmp;
+
+ if (addr->personal){
+ so_seek(tso, 0L, 0);
+ pine_rfc822_cat (addr->personal, rspecials_minus_quote_and_dot, tpc);
+ objTmp = Tcl_NewStringObj((char *) so_text(tso), so_tell(tso));
+ }
+
+ so_seek(tso, 0L, 0);
+ pine_rfc822_address(addr, tpc);
+ peAppListF(peED.interp, objAddrList, "%o%o", objTmp, Tcl_NewStringObj((char *) so_text(tso), so_tell(tso)));
+ }
+
+ if(group)
+ group++;
+ }
+ else if (addr->mailbox) { /* start of group? */
+ so_seek(tso, 0L, 0);
+ /* yes, write group name */
+ pine_rfc822_cat (addr->mailbox, rspecials, tpc);
+ peAppListF(peED.interp, objAddrList, "%o%s", Tcl_NewStringObj((char *) so_text(tso), so_tell(tso)), "");
+ group = 1; /* in a group */
+ }
+ else if (group) { /* must be end of group (but be paranoid) */
+ peAppListF(peED.interp, objAddrList, "%s%s", "", ";");
+ group = 0; /* no longer in that group */
+ }
+
+ addr->personal = ptmp; /* restore old personal ptr */
+ if(!addr->host && addr->mailbox){
+ fs_give((void **)&addr->mailbox);
+ addr->mailbox = mtmp;
+ }
+
+ addr->next = atmp;
+ addr = atmp;
+ }
+
+ gf_clear_so_writec(tso);
+ so_give(&tso);
+ }
+
+ peAppListF(peED.interp, Tcl_GetObjResult(peED.interp), "%s%s%o", atype, field_name, objAddrList);
+}
+
+
+/*
+ * appends caller's result with: {"news" field_name {{newsgroup1} {newsgroup2} ... }}
+ */
+void
+peFormatEnvelopeNewsgroups(char *field_name, char *newsgrps, int flags, gf_io_t pc)
+{
+ char buf[MAILTMPLEN];
+ int llen;
+ char *next_ng;
+ Tcl_Obj *objNewsgroups;
+
+ /* BUG: handle list creation failure */
+ if(!newsgrps || !*newsgrps || (objNewsgroups = Tcl_NewListObj(0,NULL)) == NULL)
+ return;
+
+ llen = strlen(field_name);
+ while(*newsgrps){
+ for(next_ng = newsgrps; *next_ng && *next_ng != ','; next_ng++)
+ ;
+
+ strncpy(buf, newsgrps, MIN(next_ng - newsgrps, sizeof(buf)-1));
+ buf[MIN(next_ng - newsgrps, sizeof(buf)-1)] = '\0';
+
+ Tcl_ListObjAppendElement(peED.interp, objNewsgroups, Tcl_NewStringObj(buf,-1));
+
+ newsgrps = next_ng;
+ if(*newsgrps)
+ newsgrps++;
+ }
+
+ peAppListF(peED.interp, Tcl_GetObjResult(peED.interp), "news", field_name, objNewsgroups);
+}
+
+
+int
+peMessageAttachments(Tcl_Interp *interp, imapuid_t uid, int objc, Tcl_Obj **objv)
+{
+ MESSAGECACHE *mc;
+ ATTACH_S *a;
+ BODY *body;
+ Tcl_Obj *objAtt, *tObj, *stObj, *fnObj;
+ int flags, rv = TCL_OK;
+ long raw;
+
+ peED.interp = interp;
+ peED.obj = Tcl_GetObjResult(interp);
+
+ flags = FM_DISPLAY | FM_NEW_MESS | FM_NOEDITORIAL | FM_HTML | FM_NOHTMLREL | FM_HTMLRELATED | FM_HIDESERVER;
+
+ raw = peSequenceNumber(uid);
+
+ if(peED.uid != uid){
+ memset(&peED, 0, sizeof(peED));
+
+ peED.uid = uid;
+
+ ps_global->c_client_error[0] = ps_global->last_error[0] = '\0';
+ if(!((peED.env = pine_mail_fetchstructure(ps_global->mail_stream, raw, &peED.body))
+ && (mc = mail_elt(ps_global->mail_stream, raw)))){
+ char buf[256];
+
+ snprintf(buf, sizeof(buf), "Error getting message %ld: %s", peMessageNumber(uid),
+ ps_global->last_error[0] ? ps_global->last_error : "Indeterminate");
+
+ dprint((1, "ERROR fetching %s of msg %ld: %s",
+ peED.env ? "elt" : "env", mn_get_cur(sp_msgmap(ps_global->mail_stream)),
+ ps_global->last_error[0] ? ps_global->last_error : "Indeterminate"));
+
+ Tcl_SetResult(interp, buf, TCL_VOLATILE);
+ rv = TCL_ERROR;
+ }
+ else{
+ zero_atmts(ps_global->atmts);
+#ifdef SMIME
+ if(ps_global && ps_global->smime && ps_global->smime->need_passphrase)
+ ps_global->smime->need_passphrase = 0;
+
+ fiddle_smime_message(peED.body, raw);
+#endif
+ describe_mime(peED.body, "", 1, 1, 0, flags);
+ }
+ }
+
+ /* package up attachment list */
+ for(a = ps_global->atmts; rv == TCL_OK && a->description != NULL; a++)
+ if((objAtt = Tcl_NewListObj(0, NULL)) != NULL
+ && (body = mail_body(ps_global->mail_stream, raw, (unsigned char *) a->number)) != NULL){
+ peGetMimeTyping(body, &tObj, &stObj, &fnObj, NULL);
+
+ if(!(peAppListF(interp, objAtt, "%s", a->number ? a->number : "") == TCL_OK
+ && peAppListF(interp, objAtt, "%s", a->shown ? "shown" : "") == TCL_OK
+ && Tcl_ListObjAppendElement(interp, objAtt, tObj) == TCL_OK
+ && Tcl_ListObjAppendElement(interp, objAtt, stObj) == TCL_OK
+ && Tcl_ListObjAppendElement(interp, objAtt, fnObj) == TCL_OK
+ && peAppListF(interp, objAtt, "%s", a->body->description) == TCL_OK
+ && Tcl_ListObjAppendElement(interp, Tcl_GetObjResult(interp), objAtt) == TCL_OK))
+ rv = TCL_ERROR;
+ }
+ else
+ rv = TCL_ERROR;
+
+ return(rv);
+}
+
+
+int
+peMessageBody(Tcl_Interp *interp, imapuid_t uid, int objc, Tcl_Obj **objv)
+{
+ MESSAGECACHE *mc;
+ int flags, rv = TCL_OK;
+ long raw;
+ char *color;
+
+ peED.interp = interp;
+ peED.obj = Tcl_GetObjResult(interp);
+
+ if(peED.store)
+ so_seek(peED.store, 0L, 0);
+ else
+ peED.store = so_get(CharStar, NULL, EDIT_ACCESS);
+
+ flags = FM_DISPLAY | FM_NEW_MESS | FM_NOEDITORIAL | FM_NOHTMLREL | FM_HTMLRELATED;
+
+ if(objc == 1 && objv[0]){ /* flags */
+ int i, nFlags;
+ Tcl_Obj **objFlags;
+ char *flagstr;
+
+ Tcl_ListObjGetElements(interp, objv[0], &nFlags, &objFlags);
+ for(i = 0; i < nFlags; i++){
+ if((flagstr = Tcl_GetStringFromObj(objFlags[i], NULL)) == NULL){
+ rv = TCL_ERROR;
+ }
+
+ if(!strucmp(flagstr, "html"))
+ flags |= (FM_HTML | FM_HIDESERVER);
+ else if(!strucmp(flagstr, "images"))
+ flags |= (FM_HTMLIMAGES);
+ }
+ }
+
+ peED.color.fg[0] = '\0';
+ if((color = pico_get_last_fg_color()) && (color = color_to_asciirgb(color))){
+ peInterpWritec(TAG_EMBED);
+ peInterpWritec(TAG_FGCOLOR);
+ gf_puts(color, peInterpWritec);
+ strcpy(peED.color.fgdef, peColorStr(color, tmp_20k_buf));
+ }
+
+ peED.color.bg[0] = '\0';
+ if((color = pico_get_last_bg_color()) && (color = color_to_asciirgb(color))){
+ peInterpWritec(TAG_EMBED);
+ peInterpWritec(TAG_BGCOLOR);
+ gf_puts(color, peInterpWritec);
+ strcpy(peED.color.bgdef, peColorStr(color,tmp_20k_buf));
+ }
+
+ peInterpFlush();
+
+ init_handles(&peED.handles);
+
+ raw = peSequenceNumber(uid);
+
+ if(peED.uid != uid){
+ peED.uid = uid;
+ peED.body = NULL;
+
+ ps_global->c_client_error[0] = ps_global->last_error[0] = '\0';
+ if(!((peED.env = pine_mail_fetchstructure(ps_global->mail_stream, raw, &peED.body))
+ && (mc = mail_elt(ps_global->mail_stream, raw)))){
+ char buf[256];
+
+ snprintf(buf, sizeof(buf), "Error getting message %ld: %s", peMessageNumber(uid),
+ ps_global->last_error[0] ? ps_global->last_error : "Indeterminate");
+
+ dprint((1, "ERROR fetching %s of msg %ld: %s",
+ peED.env ? "elt" : "env", mn_get_cur(sp_msgmap(ps_global->mail_stream)),
+ ps_global->last_error[0] ? ps_global->last_error : "Indeterminate"));
+
+ Tcl_SetResult(interp, buf, TCL_VOLATILE);
+ rv = TCL_ERROR;
+ }
+ else{
+ zero_atmts(ps_global->atmts);
+#ifdef SMIME
+ if(ps_global && ps_global->smime && ps_global->smime->need_passphrase)
+ ps_global->smime->need_passphrase = 0;
+
+ fiddle_smime_message(peED.body, raw);
+#endif
+ describe_mime(peED.body, "", 1, 1, 0, flags);
+ }
+ }
+
+ /* format message body */
+ if(rv == TCL_OK){
+ HEADER_S h;
+ char *errstr;
+
+ HD_INIT(&h, ps_global->VAR_VIEW_HEADERS, ps_global->view_all_except, FE_DEFAULT);
+#ifdef SMIME
+ /* kind of a hack, the description maybe shouldn't be in the editorial stuff */
+ if(ps_global->smime && ps_global->smime->need_passphrase)
+ flags &= ~FM_NOEDITORIAL;
+#endif
+ if((errstr = format_body(raw, peED.body, &peED.handles, &h, flags, FAKE_SCREEN_WIDTH, peInterpWritec)) != NULL){
+ gf_puts(errstr, peInterpWritec);
+ rv = TCL_ERROR;
+ }
+ }
+
+ peInterpFlush();
+
+ so_give(&peED.store);
+ free_handles(&peED.handles);
+ return(rv);
+}
+
+
+int
+peMessageText(Tcl_Interp *interp, imapuid_t uid, int objc, Tcl_Obj **objv)
+{
+ MESSAGECACHE *mc;
+ ENVELOPE *env;
+ BODY *body;
+ int flags;
+ long raw;
+ char *color;
+
+ memset(&peED, 0, sizeof(peED));
+ peED.interp = interp;
+ peED.obj = Tcl_GetObjResult(interp);
+ peED.store = so_get(CharStar, NULL, EDIT_ACCESS);
+
+ peED.color.fg[0] = '\0';
+ if((color = pico_get_last_fg_color()) && (color = color_to_asciirgb(color))){
+ peInterpWritec(TAG_EMBED);
+ peInterpWritec(TAG_FGCOLOR);
+ gf_puts(color, peInterpWritec);
+ strcpy(peED.color.fgdef, peColorStr(color, tmp_20k_buf));
+ }
+
+ peED.color.bg[0] = '\0';
+ if((color = pico_get_last_bg_color()) && (color = color_to_asciirgb(color))){
+ peInterpWritec(TAG_EMBED);
+ peInterpWritec(TAG_BGCOLOR);
+ gf_puts(color, peInterpWritec);
+ strcpy(peED.color.bgdef, peColorStr(color,tmp_20k_buf));
+ }
+
+ raw = peSequenceNumber(peED.uid = uid);
+ body = NULL;
+ ps_global->c_client_error[0] = ps_global->last_error[0] = '\0';
+ if(!((env = pine_mail_fetchstructure(ps_global->mail_stream, raw, &body))
+ && (mc = mail_elt(ps_global->mail_stream, raw)))){
+ char buf[256];
+
+ snprintf(buf, sizeof(buf), "Error getting message %ld: %s", peMessageNumber(uid),
+ ps_global->last_error[0] ? ps_global->last_error : "Indeterminate");
+
+ dprint((1, "ERROR fetching %s of msg %ld: %s",
+ env ? "elt" : "env", mn_get_cur(sp_msgmap(ps_global->mail_stream)),
+ ps_global->last_error[0] ? ps_global->last_error : "Indeterminate"));
+
+ Tcl_SetResult(interp, buf, TCL_VOLATILE);
+ return(TCL_ERROR);
+ }
+
+ flags = FM_DISPLAY | FM_NEW_MESS | FM_NOEDITORIAL | FM_NOHTMLREL | FM_HTMLRELATED;
+
+ init_handles(&peED.handles);
+
+ (void) format_message(raw, env, body, &peED.handles, flags, peInterpWritec);
+
+ peInterpFlush();
+
+ so_give(&peED.store);
+ free_handles(&peED.handles);
+ return(TCL_OK);
+}
+
+
+/*
+ * peMessagePartFromCID - return part number assoc'd with given uid and CID
+ */
+int
+peMessagePartFromCID(Tcl_Interp *interp, imapuid_t uid, int objc, Tcl_Obj **objv)
+{
+ char *cid, sect_buf[256];
+ long raw;
+ ENVELOPE *env;
+ BODY *body;
+
+ raw = peSequenceNumber(peED.uid = uid);
+ ps_global->c_client_error[0] = ps_global->last_error[0] = '\0';
+
+ if(objv[0] && (cid = Tcl_GetStringFromObj(objv[0], NULL)) && *cid != '\0'){
+ if((env = pine_mail_fetchstructure(ps_global->mail_stream, raw, &body)) != NULL){
+ sect_buf[0] = '\0';
+ if(peLocateBodyByCID(cid, sect_buf, body)){
+ Tcl_SetResult(interp, sect_buf, TCL_VOLATILE);
+ }
+ }
+ else{
+ Tcl_SetResult(interp, ps_global->last_error[0] ? ps_global->last_error : "Error getting CID", TCL_VOLATILE);
+ return(TCL_ERROR);
+ }
+ }
+
+ return(TCL_OK);
+}
+
+
+int
+peLocateBodyByCID(char *cid, char *section, BODY *body)
+{
+ if(body->type == TYPEMULTIPART){
+ char subsection[256], *subp;
+ int n;
+ PART *part = body->nested.part;
+
+ if(!(part = body->nested.part))
+ return(0);
+
+ subp = subsection;
+ if(section && *section){
+ for(n = 0;
+ n < sizeof(subsection)-20 && (*subp = section[n]); n++, subp++)
+ ;
+
+ *subp++ = '.';
+ }
+
+ n = 1;
+ do {
+ sprintf(subp, "%d", n++);
+ if(peLocateBodyByCID(cid, subsection, &part->body)){
+ strcpy(section, subsection);
+ return(1);
+ }
+ }
+ while((part = part->next) != NULL);
+
+ return(0);
+ }
+
+ return((body && body->id) ? !strcmp(cid, body->id) : 0);
+}
+
+
+/*
+ * peGetFlag - Return 1 or 0 based on requested flags current state
+ *
+ * Params: argv[0] == flagname
+ */
+int
+peGetFlag(Tcl_Interp *interp, imapuid_t uid, int objc, Tcl_Obj **objv)
+{
+ char *flagname;
+
+ Tcl_SetResult(interp,
+ int2string(((flagname = Tcl_GetStringFromObj(objv[0], NULL)) != NULL)
+ ? peIsFlagged(ps_global->mail_stream, uid, flagname)
+ : 0),
+ TCL_VOLATILE);
+ return(TCL_OK);
+}
+
+
+int
+peIsFlagged(MAILSTREAM *stream, imapuid_t uid, char *flagname)
+{
+ MESSAGECACHE *mc;
+ long raw = peSequenceNumber(uid);
+
+ if(!((mc = mail_elt(stream, raw)) && mc->valid)){
+ mail_fetch_flags(stream, ulong2string(uid), FT_UID);
+ mc = mail_elt(stream, raw);
+ }
+
+ if(!strucmp(flagname, "deleted"))
+ return(mc->deleted);
+
+ if(!strucmp(flagname, "new"))
+ return(!mc->seen);
+
+ if(!strucmp(flagname, "important"))
+ return(mc->flagged);
+
+ if(!strucmp(flagname, "answered"))
+ return(mc->answered);
+
+ if(!strucmp(flagname, "recent"))
+ return(mc->recent);
+
+ return(0);
+}
+
+
+/*
+ * peSetFlag - Set requested flags value to 1 or 0
+ *
+ * Params: abjv[0] == flagname
+ * objv[1] == newvalue
+ */
+int
+peSetFlag(Tcl_Interp *interp, imapuid_t uid, int objc, Tcl_Obj **objv)
+{
+ char *flagname, *flagstr = NULL;
+ int value;
+
+ if((flagname = Tcl_GetStringFromObj(objv[0], NULL))
+ && Tcl_GetIntFromObj(interp, objv[1], &value) != TCL_ERROR){
+ if(!strucmp(flagname, "deleted")){
+ flagstr = "\\DELETED";
+ }
+ else if(!strucmp(flagname, "new")){
+ flagstr = "\\SEEN";
+ value = !value;
+ }
+ else if(!strucmp(flagname, "important")){
+ flagstr = "\\FLAGGED";
+ }
+ else if(!strucmp(flagname, "answered")){
+ flagstr = "\\ANSWERED";
+ }
+ else if(!strucmp(flagname, "recent")){
+ flagstr = "\\RECENT";
+ }
+
+ if(flagstr){
+ ps_global->c_client_error[0] = '\0';
+ mail_flag(ps_global->mail_stream,
+ ulong2string(uid),
+ flagstr, (value ? ST_SET : 0L) | ST_UID);
+ if(ps_global->c_client_error[0] != '\0'){
+ snprintf(tmp_20k_buf, SIZEOF_20KBUF, "peSetFlag: %.40s",
+ ps_global->c_client_error);
+ Tcl_SetResult(interp, tmp_20k_buf, TCL_VOLATILE);
+ return(TCL_ERROR);
+ }
+ }
+ }
+
+ Tcl_SetResult(interp, value ? "1" : "0", TCL_STATIC);
+ return(TCL_OK);
+}
+
+
+/*
+ * peMsgSelect - Return 1 or 0 based on whether given UID is selected
+ *
+ * Params: argv[0] == selected
+ */
+int
+peMsgSelect(Tcl_Interp *interp, imapuid_t uid, int objc, Tcl_Obj **objv)
+{
+ int value;
+
+ if(objc == 1 && objv[0]){
+ if(Tcl_GetIntFromObj(interp, objv[0], &value) != TCL_ERROR){
+ if(value){
+ set_lflag(ps_global->mail_stream, sp_msgmap(ps_global->mail_stream),
+ peMessageNumber(uid), MN_SLCT, 1);
+ set_lflag(ps_global->mail_stream, sp_msgmap(ps_global->mail_stream),
+ peMessageNumber(uid), MN_HIDE, 0);
+ } else {
+ set_lflag(ps_global->mail_stream, sp_msgmap(ps_global->mail_stream),
+ peMessageNumber(uid), MN_SLCT, 0);
+ /* if zoomed, lite hidden bit */
+ if(any_lflagged(sp_msgmap(ps_global->mail_stream), MN_HIDE))
+ set_lflag(ps_global->mail_stream, sp_msgmap(ps_global->mail_stream),
+ peMessageNumber(uid), MN_HIDE, 1);
+
+ }
+ }
+ else{
+ Tcl_SetResult(interp, "peMsgSelect: can't get value", TCL_STATIC);
+ return(TCL_ERROR);
+ }
+ }
+
+ Tcl_SetResult(interp,
+ (get_lflag(ps_global->mail_stream, NULL,
+ peSequenceNumber(uid),
+ MN_SLCT))
+ ? "1" : "0",
+ TCL_VOLATILE);
+ return(TCL_OK);
+}
+
+
+/*
+ * peAppendIndexParts - append list of digested index pieces to given object
+ *
+ * Params:
+ *
+ */
+int
+peAppendIndexParts(Tcl_Interp *interp, imapuid_t uid, Tcl_Obj *aObj, int *fetched)
+{
+ Tcl_Obj *objField, *objElement, *objp;
+ ICE_S *h;
+ IFIELD_S *f;
+ IELEM_S *ie;
+
+
+ if((h = build_header_work(ps_global, ps_global->mail_stream,
+ sp_msgmap(ps_global->mail_stream), peMessageNumber(uid),
+ gPeITop, gPeICount, fetched)) != NULL){
+ for(f = h->ifield; f; f = f->next){
+
+ if((objField = Tcl_NewListObj(0, NULL)) == NULL)
+ return(TCL_ERROR);
+
+ for(ie = f->ielem; ie ; ie = ie->next){
+
+ if((objElement = Tcl_NewListObj(0, NULL)) == NULL)
+ return(TCL_ERROR);
+
+ if(ie->datalen){
+ /* FIRST: DATA */
+#if INTERNAL_INDEX_TRUNCATE
+ char *ep;
+
+ ep = (char *) fs_get((ie->datalen + 1) * sizeof(char));
+ sprintf(ep, "%.*s", ie->wid, ie->data);
+
+ /* and other stuff to pack trunc'd element into a new object */
+#endif
+
+ objp = Tcl_NewStringObj(ie->data, ie->datalen);
+ }
+ else
+ objp = Tcl_NewStringObj("", -1);
+
+ if(Tcl_ListObjAppendElement(interp, objElement, objp) != TCL_OK)
+ return(TCL_ERROR);
+
+ if(ie->color){
+ Tcl_Obj *objColor;
+ char hexcolor[32];
+
+ if((objp = Tcl_NewListObj(0, NULL)) == NULL)
+ return(TCL_ERROR);
+
+ hex_colorstr(hexcolor, ie->color->fg);
+ objColor = Tcl_NewStringObj(hexcolor, -1);
+ if(Tcl_ListObjAppendElement(interp, objp, objColor) != TCL_OK)
+ return(TCL_ERROR);
+
+ hex_colorstr(hexcolor, ie->color->bg);
+ objColor = Tcl_NewStringObj(hexcolor, -1);
+ if(Tcl_ListObjAppendElement(interp, objp, objColor) != TCL_OK)
+ return(TCL_ERROR);
+ }
+ else
+ objp = Tcl_NewStringObj("", -1);
+
+ if(Tcl_ListObjAppendElement(interp, objElement, objp) != TCL_OK)
+ return(TCL_ERROR);
+
+ /*
+ * IF we ever want to map the thread characters into nice
+ * graphical symbols or take advantage of features like clicking
+ * on a thread element to collapse and such, we need to have
+ * element tagging. That's what the object creation and append
+ * are placeholders for
+ */
+ switch(ie->type){
+ case eThreadInfo :
+ objp = Tcl_NewStringObj("threadinfo", -1);
+ break;
+ case eText :
+ objp = NULL;
+ break;
+ default :
+ objp = Tcl_NewStringObj(int2string(ie->type), -1);
+ break;
+ }
+
+ if(objp && Tcl_ListObjAppendElement(interp, objElement, objp) != TCL_OK)
+ return(TCL_ERROR);
+
+ if(Tcl_ListObjAppendElement(interp, objField, objElement) != TCL_OK)
+ return(TCL_ERROR);
+ }
+
+ if(Tcl_ListObjAppendElement(interp, aObj, objField) != TCL_OK){
+ return(TCL_ERROR);
+ }
+ }
+ }
+
+ return(TCL_OK);
+}
+
+
+/*
+ * peAppendIndexColor - append index line's foreground/background color
+ *
+ * Params:
+ *
+ */
+int
+peAppendIndexColor(Tcl_Interp *interp, imapuid_t uid, Tcl_Obj *aObj, int *fetched)
+{
+ char hexfg[32], hexbg[32];
+ ICE_S *h;
+
+ if((h = build_header_work(ps_global, ps_global->mail_stream,
+ sp_msgmap(ps_global->mail_stream), peMessageNumber(uid),
+ gPeITop, gPeICount, fetched))
+ && h->color_lookup_done
+ && h->linecolor){
+
+ hex_colorstr(hexfg, h->linecolor->fg);
+ hex_colorstr(hexbg, h->linecolor->bg);
+
+ return(peAppListF(interp, aObj, "%s%s", hexfg, hexbg));
+ }
+
+ return(peAppListF(interp, aObj, "%s", ""));
+}
+
+
+/*
+ * peMessageStatusBits - return list flags indicating pine status bits
+ *
+ * Params:
+ *
+ * Returns: list of lists where:
+ * * the first element is the list of
+ * field elements data
+ * * the second element is a two element
+ * list containing the lines foreground
+ * and background colors
+ */
+int
+peMessageStatusBits(Tcl_Interp *interp, imapuid_t uid, int objc, Tcl_Obj **objv)
+{
+ Tcl_SetResult(interp,
+ peMsgStatBitString(ps_global, ps_global->mail_stream,
+ sp_msgmap(ps_global->mail_stream), peMessageNumber(uid),
+ gPeITop, gPeICount, NULL),
+ TCL_STATIC);
+ return(TCL_OK);
+}
+
+
+char *
+peMsgStatBitString(struct pine *state,
+ MAILSTREAM *stream,
+ MSGNO_S *msgmap,
+ long msgno,
+ long top_msgno,
+ long msgcount,
+ int *fetched)
+{
+ static char buf[36];
+ int i;
+ long raw;
+ MESSAGECACHE *mc;
+ ICE_S *h;
+
+ raw = mn_m2raw(msgmap, msgno);
+ if((h = build_header_work(state, stream, msgmap,
+ msgno, top_msgno, msgcount, fetched))
+ && (mc = mail_elt(stream, raw))){
+ /* return a string representing a bit field where:
+ index meaning
+ ----- -------
+ 0 "New"
+ 1 deleted
+ 2 answered
+ 3 flagged
+ 4 to us
+ 5 cc us
+ 6 recent
+ 7 forwarded
+ 8 attachments
+ */
+ i = 0;
+ buf[i++] = (mc->seen) ? '0' : '1';
+ buf[i++] = (mc->deleted) ? '1' : '0';
+ buf[i++] = (mc->answered) ? '1' : '0';
+ buf[i++] = (mc->flagged) ? '1' : '0';
+ buf[i++] = (h->to_us) ? '1' : '0';
+ buf[i++] = (h->cc_us) ? '1' : '0';
+ buf[i++] = (mc->recent) ? '1' : '0';
+ buf[i++] = (user_flag_is_set(stream, raw, FORWARDED_FLAG)) ? '1' : '0';
+ buf[i++] = '0';
+ buf[i++] = '\0';
+
+ return(buf);
+ }
+
+ return("100000000");
+}
+
+
+Tcl_Obj *
+peMsgStatNameList(Tcl_Interp *interp,
+ struct pine *state,
+ MAILSTREAM *stream,
+ MSGNO_S *msgmap,
+ long msgno,
+ long top_msgno,
+ long msgcount,
+ int *fetched)
+{
+ Tcl_Obj *objList;
+ long raw;
+ MESSAGECACHE *mc;
+ ICE_S *h;
+
+ objList = Tcl_NewListObj(0, NULL);
+ raw = mn_m2raw(msgmap, msgno);
+ if((h = build_header_work(state, stream, msgmap,
+ msgno, top_msgno, msgcount, fetched))
+ && (mc = mail_elt(stream, raw))){
+ if(!mc->seen)
+ Tcl_ListObjAppendElement(interp, objList, Tcl_NewStringObj("new", -1));
+
+ if(mc->deleted)
+ Tcl_ListObjAppendElement(interp, objList, Tcl_NewStringObj("deleted", -1));
+
+ if(mc->answered)
+ Tcl_ListObjAppendElement(interp, objList, Tcl_NewStringObj("answered", -1));
+
+ if(mc->flagged)
+ Tcl_ListObjAppendElement(interp, objList, Tcl_NewStringObj("flagged", -1));
+
+ if(h->to_us)
+ Tcl_ListObjAppendElement(interp, objList, Tcl_NewStringObj("to_us", -1));
+
+ if(h->cc_us)
+ Tcl_ListObjAppendElement(interp, objList, Tcl_NewStringObj("cc_us", -1));
+
+ if(mc->recent)
+ Tcl_ListObjAppendElement(interp, objList, Tcl_NewStringObj("recent", -1));
+
+ if(user_flag_is_set(stream, raw, FORWARDED_FLAG))
+ Tcl_ListObjAppendElement(interp, objList, Tcl_NewStringObj("forwarded", -1));
+
+ if(get_lflag(ps_global->mail_stream, sp_msgmap(ps_global->mail_stream), msgno, MN_SLCT))
+ Tcl_ListObjAppendElement(interp, objList, Tcl_NewStringObj("selected", -1));
+ }
+
+ return(objList);
+}
+
+
+/*
+ * peReplyHeaders - return subject used in reply to given message
+ *
+ * Params:
+ *
+ * Returns: list of header value pairs where headers are:
+ * In-Reply-To:, Subject:, Cc:
+ *
+ */
+int
+peReplyHeaders(Tcl_Interp *interp, imapuid_t uid, int objc, Tcl_Obj **objv)
+{
+ long raw;
+ int flags = RSF_FORCE_REPLY_TO | RSF_FORCE_REPLY_ALL, err = FALSE;
+ char *errmsg = NULL, *fcc = NULL, *sect = NULL;
+ ENVELOPE *env, *outgoing;
+ BODY *body = NULL;
+ ADDRESS *saved_from, *saved_to, *saved_cc, *saved_resent;
+
+ saved_from = (ADDRESS *) NULL;
+ saved_to = (ADDRESS *) NULL;
+ saved_cc = (ADDRESS *) NULL;
+ saved_resent = (ADDRESS *) NULL;
+
+ raw = peSequenceNumber(uid);
+
+ /* if we're given a valid section number that
+ * corresponds to a valid msg/rfc822 body part
+ * then set up headers in attached message.
+ */
+ if(objc == 1 && objv[0]
+ && (sect = Tcl_GetStringFromObj(objv[0], NULL)) && *sect != '\0'
+ && (body = mail_body(ps_global->mail_stream, raw, (unsigned char *) sect))
+ && body->type == TYPEMESSAGE
+ && !strucmp(body->subtype, "rfc822")){
+ env = body->nested.msg->env;
+ }
+ else{
+ sect = NULL;
+ env = mail_fetchstructure(ps_global->mail_stream, raw, NULL);
+ }
+
+ if(env){
+ if(!reply_harvest(ps_global, raw, sect, env,
+ &saved_from, &saved_to, &saved_cc,
+ &saved_resent, &flags)){
+
+ Tcl_SetResult(interp, "", TCL_STATIC);
+ return(TCL_ERROR);
+ }
+
+ outgoing = mail_newenvelope();
+
+ reply_seed(ps_global, outgoing, env,
+ saved_from, saved_to, saved_cc, saved_resent,
+ &fcc, flags, &errmsg);
+ if(errmsg){
+ if(*errmsg){
+ q_status_message1(SM_ORDER, 3, 3, "%.200s", errmsg);
+ }
+
+ fs_give((void **)&errmsg);
+ }
+
+ env = pine_mail_fetchstructure(ps_global->mail_stream, raw, NULL);
+
+ outgoing->subject = reply_subject(env->subject, NULL, 0);
+ outgoing->in_reply_to = reply_in_reply_to(env);
+
+ err = !(peAppListF(interp, Tcl_GetObjResult(interp),
+ "%s%a", "to", outgoing->to) == TCL_OK
+ && peAppListF(interp, Tcl_GetObjResult(interp),
+ "%s%a", "cc", outgoing->cc) == TCL_OK
+ && peAppListF(interp, Tcl_GetObjResult(interp),
+ "%s%s", "in-reply-to", outgoing->in_reply_to) == TCL_OK
+ && peAppListF(interp, Tcl_GetObjResult(interp),
+ "%s%s", "subject",
+ rfc1522_decode_to_utf8((unsigned char *) tmp_20k_buf,
+ SIZEOF_20KBUF, outgoing->subject)) == TCL_OK
+ && (fcc ? peFccAppend(interp, Tcl_GetObjResult(interp), fcc, -1) : TRUE));
+
+
+ /* Fill in x-reply-uid data and append it */
+ if(!err && ps_global->mail_stream->uid_validity){
+ char *prefix = reply_quote_str(env);
+
+ snprintf(tmp_20k_buf, SIZEOF_20KBUF, "(%d %s)(1 %lu %lu)%s",
+ strlen(prefix), prefix,
+ ps_global->mail_stream->uid_validity, uid,
+ ps_global->mail_stream->mailbox);
+
+ fs_give((void **) &prefix);
+
+ err = peAppListF(interp, Tcl_GetObjResult(interp), "%s%s",
+ "x-reply-uid", tmp_20k_buf) != TCL_OK;
+ }
+
+ mail_free_envelope(&outgoing);
+
+ if(err)
+ return(TCL_ERROR);
+ }
+ else
+ Tcl_SetResult(interp, "", TCL_VOLATILE);
+
+ return(TCL_OK);
+}
+
+
+
+/*
+ * peReplyText - return subject used in reply to given message
+ *
+ * Params:
+ *
+ * Returns:
+ *
+ */
+int
+peReplyText(Tcl_Interp *interp, imapuid_t uid, int objc, Tcl_Obj **objv)
+{
+ long msgno;
+ char *prefix, *sect = NULL;
+ int rv = TCL_OK;
+ ENVELOPE *env;
+ BODY *body = NULL, *orig_body;
+ STORE_S *msgtext;
+ REDRAFT_POS_S *redraft_pos = NULL;
+ Tcl_Obj *objBody = NULL, *objAttach = NULL;
+
+ msgno = peSequenceNumber(uid);
+
+ if((msgtext = (void *) so_get(CharStar, NULL, EDIT_ACCESS)) == NULL){
+ Tcl_SetResult(interp, "Unable to create storage for reply text", TCL_VOLATILE);
+ return(TCL_ERROR);
+ }
+
+ /*--- Grab current envelope ---*/
+ /* if we're given a valid section number that
+ * corresponds to a valid msg/rfc822 body part
+ * then set up to reply the attached message's
+ * text.
+ */
+ if(objc == 2 && objv[1]
+ && (sect = Tcl_GetStringFromObj(objv[1], NULL)) && *sect != '\0'
+ && (body = mail_body(ps_global->mail_stream, msgno, (unsigned char *) sect))
+ && body->type == TYPEMESSAGE
+ && !strucmp(body->subtype, "rfc822")){
+ env = body->nested.msg->env;
+ orig_body = body->nested.msg->body;
+ }
+ else{
+ sect = NULL;
+ env = mail_fetchstructure(ps_global->mail_stream, msgno, &orig_body);
+ if(!(env && orig_body)){
+ Tcl_SetResult(interp, "Unable to fetch message parts", TCL_VOLATILE);
+ return(TCL_ERROR);
+ }
+ }
+
+ if((prefix = Tcl_GetStringFromObj(objv[0], NULL)) != NULL)
+ prefix = cpystr(prefix);
+ else
+ prefix = reply_quote_str(env);
+
+ /*
+ * BUG? Should there be some way to signal to reply_bddy
+ * that we'd like it to produced format=flowed body text?
+ * right now it's hardwired to in pine/reply.c
+ */
+
+ if((body = reply_body(ps_global->mail_stream, env, orig_body,
+ msgno, sect, msgtext, prefix,
+ TRUE, NULL, TRUE, &redraft_pos)) != NULL){
+
+ objBody = Tcl_NewListObj(0, NULL);
+
+ peSoStrToList(interp, objBody, msgtext);
+
+ Tcl_ListObjAppendElement(interp, Tcl_GetObjResult(interp), objBody);
+
+ /* sniff for attachments */
+ objAttach = peMsgAttachCollector(interp, body);
+
+ Tcl_ListObjAppendElement(interp, Tcl_GetObjResult(interp), objAttach);
+
+
+ pine_free_body(&body);
+ }
+ else{
+ Tcl_SetResult(interp, "Can't create body text", TCL_VOLATILE);
+ rv = TCL_ERROR;
+ }
+
+ fs_give((void **) &prefix);
+
+ return(rv);
+}
+
+
+int
+peSoStrToList(Tcl_Interp *interp, Tcl_Obj *obj, STORE_S *so)
+{
+ char *sp, *ep;
+ Tcl_Obj *objp;
+
+ for(ep = (char *) so_text(so); *ep; ep++){
+ sp = ep;
+
+ while(*ep && *ep != '\n')
+ ep++;
+
+ objp = Tcl_NewStringObj(sp, ep - sp);
+
+ if(Tcl_ListObjAppendElement(interp, obj, objp) != TCL_OK)
+ return(FALSE);
+ }
+
+ return(TRUE);
+}
+
+
+/*
+ * peForwardHeaders - return subject used in forward of given message
+ *
+ * Params:
+ *
+ * Returns:
+ *
+ */
+int
+peForwardHeaders(Tcl_Interp *interp, imapuid_t uid, int objc, Tcl_Obj **objv)
+{
+
+ int result;
+ long raw;
+ char *tmp, *sect = NULL;
+ ENVELOPE *env;
+ BODY *body;
+
+ raw = peSequenceNumber(uid);
+
+ /* if we're given a valid section number that
+ * corresponds to a valid msg/rfc822 body part
+ * then set up headers in attached message.
+ */
+ if(objc == 1 && objv[0]
+ && (sect = Tcl_GetStringFromObj(objv[0], NULL)) && *sect != '\0'
+ && (body = mail_body(ps_global->mail_stream, raw, (unsigned char *) sect))
+ && body->type == TYPEMESSAGE
+ && !strucmp(body->subtype, "rfc822")){
+ env = body->nested.msg->env;
+ }
+ else{
+ sect = NULL;
+ env = mail_fetchstructure(ps_global->mail_stream, raw, NULL);
+ }
+
+ if(env){
+ tmp = forward_subject(env, FS_NONE);
+ result = peAppListF(interp, Tcl_GetObjResult(interp),
+ "%s%s", "subject", tmp);
+ fs_give((void **) &tmp);
+
+ /* Fill in x-reply-uid data and append it */
+ if(result == TCL_OK && ps_global->mail_stream->uid_validity){
+ snprintf(tmp_20k_buf, SIZEOF_20KBUF, "()(1 %lu %lu)%s",
+ ps_global->mail_stream->uid_validity, uid,
+ ps_global->mail_stream->mailbox);
+ result = peAppListF(interp, Tcl_GetObjResult(interp), "%s%s",
+ "x-reply-uid", tmp_20k_buf) != TCL_OK;
+ }
+
+ return(result);
+ }
+
+ Tcl_SetResult(interp, ps_global->last_error, TCL_VOLATILE);
+ return(TCL_ERROR);
+}
+
+
+
+/*
+ * peForwardText - return body of message used in
+ * forward of given message
+ *
+ * Params:
+ *
+ * Returns:
+ *
+ */
+int
+peForwardText(Tcl_Interp *interp, imapuid_t uid, int objc, Tcl_Obj **objv)
+{
+ long msgno;
+ char *bodtext, *p, *sect = NULL;
+ int rv = TCL_OK;
+ ENVELOPE *env;
+ BODY *body, *orig_body;
+ STORE_S *msgtext;
+ Tcl_Obj *objBody = NULL, *objAttach = NULL;
+
+ msgno = peSequenceNumber(uid);
+
+ if(objc == 1 && objv[0])
+ sect = Tcl_GetStringFromObj(objv[0], NULL);
+
+ if((msgtext = (void *) so_get(CharStar, NULL, EDIT_ACCESS)) == NULL){
+ Tcl_SetResult(interp, "Unable to create storage for forward text", TCL_VOLATILE);
+ return(TCL_ERROR);
+ }
+
+
+ if(F_ON(F_FORWARD_AS_ATTACHMENT, ps_global)){
+ PART **pp;
+ long totalsize = 0L;
+
+ /*---- New Body to start with ----*/
+ body = mail_newbody();
+ body->type = TYPEMULTIPART;
+
+ /*---- The TEXT part/body ----*/
+ body->nested.part = mail_newbody_part();
+ body->nested.part->body.type = TYPETEXT;
+ body->nested.part->body.contents.text.data = (unsigned char *) msgtext;
+
+ pp = &(body->nested.part->next);
+
+ /*---- The Message body subparts ----*/
+ env = pine_mail_fetchstructure(ps_global->mail_stream, msgno, NULL);
+
+ if(forward_mime_msg(ps_global->mail_stream, msgno,
+ (sect && *sect != '\0') ? sect : NULL, env, pp, msgtext)){
+ totalsize = (*pp)->body.size.bytes;
+ pp = &((*pp)->next);
+ }
+ }
+ else{
+ /*--- Grab current envelope ---*/
+ /* if we're given a valid section number that
+ * corresponds to a valid msg/rfc822 body part
+ * then set up to forward the attached message's
+ * text.
+ */
+
+ if(sect && *sect != '\0'
+ && (body = mail_body(ps_global->mail_stream, msgno, (unsigned char *) sect))
+ && body->type == TYPEMESSAGE
+ && !strucmp(body->subtype, "rfc822")){
+ env = body->nested.msg->env;
+ orig_body = body->nested.msg->body;
+ }
+ else{
+ sect = NULL;
+ env = mail_fetchstructure(ps_global->mail_stream, msgno, &orig_body);
+ if(!(env && orig_body)){
+ Tcl_SetResult(interp, "Unable to fetch message parts", TCL_VOLATILE);
+ return(TCL_ERROR);
+ }
+ }
+
+ body = forward_body(ps_global->mail_stream, env, orig_body,
+ msgno, sect, msgtext, FWD_NONE);
+ }
+
+ if(body){
+ bodtext = (char *) so_text(msgtext);
+
+ objBody = Tcl_NewListObj(0, NULL);
+
+ for(p = bodtext; *p; p++){
+ Tcl_Obj *objp;
+
+ bodtext = p;
+ while(*p && *p != '\n')
+ p++;
+
+ objp = Tcl_NewStringObj(bodtext, p - bodtext);
+
+ Tcl_ListObjAppendElement(interp, objBody, objp);
+ }
+
+ Tcl_ListObjAppendElement(interp, Tcl_GetObjResult(interp), objBody);
+
+ /* sniff for attachments */
+ objAttach = peMsgAttachCollector(interp, body);
+ Tcl_ListObjAppendElement(interp, Tcl_GetObjResult(interp), objAttach);
+
+ pine_free_body(&body);
+ }
+ else{
+ Tcl_SetResult(interp, "Can't create body text", TCL_VOLATILE);
+ rv = TCL_ERROR;
+ }
+
+ return(rv);
+}
+
+
+
+/*
+ * peDetach -
+ *
+ * Params: argv[0] == attachment part number
+ * argv[1] == directory to hold tmp file
+ *
+ * Returns: list containing:
+ *
+ * 0) response: OK or ERROR
+ * if OK
+ * 1) attachment's mime type
+ * 2) attachment's mime sub-type
+ * 3) attachment's size in bytes (decoded)
+ * 4) attachment's given file name (if any)
+ * 5) tmp file holding raw attachment data
+ */
+int
+peDetach(Tcl_Interp *interp, imapuid_t uid, int objc, Tcl_Obj **objv)
+{
+ char *part, *err, *tfd, *tfn = NULL, *filename;
+ long raw;
+ gf_io_t pc;
+ BODY *body;
+ STORE_S *store;
+ Tcl_Obj *rvobj, *tObj, *stObj, *fnObj;
+
+ if((part = Tcl_GetStringFromObj(objv[0], NULL))
+ && (raw = peSequenceNumber(uid))
+ && (body = mail_body(ps_global->mail_stream, raw, (unsigned char *) part))){
+
+ peGetMimeTyping(body, &tObj, &stObj, &fnObj, NULL);
+
+ err = NULL;
+ if(!(tfd = Tcl_GetStringFromObj(objv[1], NULL)) || *tfd == '\0'){
+ tfn = temp_nam(tfd = NULL, "pd");
+ }
+ else if(is_writable_dir(tfd) == 0){
+ tfn = temp_nam(tfd, "pd");
+ }
+ else
+ tfn = tfd;
+
+ filename = Tcl_GetStringFromObj(fnObj, NULL);
+ dprint((5, "PEDetach(name: %s, tmpfile: %s)",
+ filename ? filename : "<null>", tfn));
+
+ if((store = so_get(FileStar, tfn, WRITE_ACCESS|OWNER_ONLY)) != NULL){
+ gf_set_so_writec(&pc, store);
+ err = detach(ps_global->mail_stream, raw, part, 0L, NULL, pc, NULL, 0);
+ gf_clear_so_writec(store);
+ so_give(&store);
+ }
+ else
+ err = "Can't allocate internal storage";
+ }
+ else
+ err = "Can't get message data";
+
+ if(err){
+ if(tfn)
+ unlink(tfn);
+
+ dprint((1, "PEDetach FAIL: %d: %s", errno, err));
+ snprintf(tmp_20k_buf, SIZEOF_20KBUF, "Detach (%d): %s", errno, err);
+ Tcl_SetResult(interp, tmp_20k_buf, TCL_VOLATILE);
+ return(TCL_ERROR);
+ }
+
+ /* package up response */
+ Tcl_ListObjAppendElement(interp, Tcl_GetObjResult(interp),
+ Tcl_NewListObj(1, &tObj));
+
+ Tcl_ListObjAppendElement(interp, Tcl_GetObjResult(interp),
+ Tcl_NewListObj(1, &stObj));
+
+ rvobj = Tcl_NewLongObj(name_file_size(tfn));
+ Tcl_ListObjAppendElement(interp, Tcl_GetObjResult(interp),
+ Tcl_NewListObj(1, &rvobj));
+ Tcl_ListObjAppendElement(interp, Tcl_GetObjResult(interp),
+ Tcl_NewListObj(1, &fnObj));
+ rvobj = Tcl_NewStringObj(tfn, -1);
+ Tcl_ListObjAppendElement(interp, Tcl_GetObjResult(interp),
+ Tcl_NewListObj(1, &rvobj));
+
+ return(TCL_OK);
+}
+
+
+/*
+ * peAttachInfo -
+ *
+ * Params: argv[0] == attachment part number
+ *
+ * Returns: list containing:
+ *
+ * 0) response: OK or ERROR
+ * if OK
+ * 1) attachment's mime type
+ * 2) attachment's mime sub-type
+ * 3) attachment's size in bytes (decoded)
+ * 4) attachment's given file name (if any)
+ * 5) tmp file holding raw attachment data
+ */
+int
+peAttachInfo(Tcl_Interp *interp, imapuid_t uid, int objc, Tcl_Obj **objv)
+{
+ char *part;
+ long raw;
+ BODY *body;
+ PARMLIST_S *plist;
+ Tcl_Obj *tObj, *stObj, *fnObj;
+
+ if((part = Tcl_GetStringFromObj(objv[0], NULL))
+ && (raw = peSequenceNumber(uid))
+ && (body = mail_body(ps_global->mail_stream, raw, (unsigned char *) part))){
+
+ peGetMimeTyping(body, &tObj, &stObj, &fnObj, NULL);
+ }
+ else{
+ Tcl_SetResult(interp, "Can't get message data", TCL_STATIC);
+ return(TCL_ERROR);
+ }
+
+ /* package up response */
+
+ /* filename */
+ Tcl_ListObjAppendElement(interp, Tcl_GetObjResult(interp), fnObj);
+
+ /* type */
+ Tcl_ListObjAppendElement(interp, Tcl_GetObjResult(interp), tObj);
+
+ /* subtype */
+ Tcl_ListObjAppendElement(interp, Tcl_GetObjResult(interp), stObj);
+
+ /* encoding */
+ Tcl_ListObjAppendElement(interp, Tcl_GetObjResult(interp),
+ Tcl_NewStringObj((body->encoding < ENCMAX)
+ ? body_encodings[body->encoding]
+ : "Unknown", -1));
+
+ /* parameters */
+ if((plist = rfc2231_newparmlist(body->parameter)) != NULL){
+ Tcl_Obj *lObj = Tcl_NewListObj(0, NULL);
+ Tcl_Obj *pObj[2];
+
+ while(rfc2231_list_params(plist)){
+ pObj[0] = Tcl_NewStringObj(plist->attrib, -1);
+ pObj[1] = Tcl_NewStringObj(plist->value, -1);
+ Tcl_ListObjAppendElement(interp, lObj,
+ Tcl_NewListObj(2, pObj));
+ }
+
+ Tcl_ListObjAppendElement(interp, Tcl_GetObjResult(interp), lObj);
+ rfc2231_free_parmlist(&plist);
+ }
+
+ /* size guesstimate */
+ Tcl_ListObjAppendElement(interp, Tcl_GetObjResult(interp),
+ Tcl_NewStringObj(comatose((body->encoding == ENCBASE64)
+ ? ((body->size.bytes * 3)/4)
+ : body->size.bytes), -1));
+
+ return(TCL_OK);
+}
+
+
+/*
+ * peSaveDefault - Default saved file name for the given message
+ * specified collection/folder
+ *
+ * Params:
+ *
+ * Returns: name of saved message folder or empty string
+ *
+ */
+int
+peSaveDefault(Tcl_Interp *interp, imapuid_t uid, int objc, Tcl_Obj **objv)
+{
+ char *folder;
+ CONTEXT_S *cntxt, *cp;
+ int colid;
+ long rawno;
+ ENVELOPE *env;
+
+ if(uid){
+ if(!(env = pine_mail_fetchstructure(ps_global->mail_stream,
+ rawno = peSequenceNumber(uid),
+ NULL))){
+ Tcl_SetResult(interp, ps_global->last_error, TCL_VOLATILE);
+ return(TCL_ERROR);
+ }
+ }
+ else
+ env = NULL;
+
+ if(!(folder = save_get_default(ps_global, env, rawno, NULL, &cntxt))){
+ Tcl_SetResult(interp, "Message expunged!", TCL_VOLATILE);
+ return(TCL_ERROR); /* message expunged! */
+ }
+
+ for(colid = 0, cp = ps_global->context_list; cp && cp != cntxt ; colid++, cp = cp->next)
+ ;
+
+ if(!cp)
+ colid = 0;
+
+ (void) Tcl_ListObjAppendElement(interp, Tcl_GetObjResult(interp),
+ Tcl_NewIntObj(colid));
+ (void) Tcl_ListObjAppendElement(interp, Tcl_GetObjResult(interp),
+ Tcl_NewStringObj(folder, -1));
+ return(TCL_OK);
+}
+
+
+/*
+ * peSaveWork - Save message with given UID in current folder to
+ * specified collection/folder
+ *
+ * Params: argv[0] == destination context number
+ * argv[1] == testination foldername
+ *
+ *
+ */
+int
+peSaveWork(Tcl_Interp *interp, imapuid_t uid, int objc, Tcl_Obj **objv, long sflags)
+{
+ int flgs = 0, i, colid;
+ char *folder, *err = NULL;
+ CONTEXT_S *cp;
+
+ if(Tcl_GetIntFromObj(interp, objv[0], &colid) != TCL_ERROR){
+ if((folder = Tcl_GetStringFromObj(objv[1], NULL)) != NULL){
+ mn_set_cur(sp_msgmap(ps_global->mail_stream), peMessageNumber(uid));
+ for(i = 0, cp = ps_global->context_list; cp ; i++, cp = cp->next)
+ if(i == colid)
+ break;
+
+ if(cp){
+ if(!READONLY_FOLDER(ps_global->mail_stream)
+ && (sflags & PSW_COPY) != PSW_COPY
+ && ((sflags & PSW_MOVE) == PSW_MOVE || F_OFF(F_SAVE_WONT_DELETE, ps_global)))
+ flgs |= SV_DELETE;
+
+ if(colid == 0 && !strucmp(folder, "inbox"))
+ flgs |= SV_INBOXWOCNTXT;
+
+ if(sflags & (PSW_COPY | PSW_MOVE))
+ flgs |= SV_FIX_DELS;
+
+ i = save(ps_global, ps_global->mail_stream,
+ cp, folder, sp_msgmap(ps_global->mail_stream), flgs);
+
+ if(i == mn_total_cur(sp_msgmap(ps_global->mail_stream))){
+ if(mn_total_cur(sp_msgmap(ps_global->mail_stream)) <= 1L){
+ if(ps_global->context_list->next
+ && context_isambig(folder)){
+ char *tag = (cp->nickname && strlen(cp->nickname)) ? cp->nickname : (cp->label && strlen(cp->label)) ? cp->label : "Folders";
+ snprintf(tmp_20k_buf, SIZEOF_20KBUF,
+ "Message %s %s to \"%.15s%s\" in <%.15s%s>",
+ long2string(mn_get_cur(sp_msgmap(ps_global->mail_stream))),
+ (sflags & PSW_MOVE) ? "moved" : "copied",
+ folder,
+ (strlen(folder) > 15) ? "..." : "",
+ tag,
+ (strlen(tag) > 15) ? "..." : "");
+ }
+ else
+ snprintf(tmp_20k_buf, SIZEOF_20KBUF,
+ "Message %s %s to folder \"%.27s%s\"",
+ long2string(mn_get_cur(sp_msgmap(ps_global->mail_stream))),
+ (sflags & PSW_MOVE) ? "moved" : "copied",
+ folder,
+ (strlen(folder) > 27) ? "..." : "");
+ }
+ else{
+ /* with mn_set_cur above, this *should not* happen */
+ Tcl_SetResult(interp, "TOO MANY MESSAGES COPIED", TCL_VOLATILE);
+ return(TCL_ERROR);
+ }
+
+ if(sflags == PSW_NONE && (flgs & SV_DELETE)){
+ strncat(tmp_20k_buf, " and deleted", SIZEOF_20KBUF-strlen(tmp_20k_buf)-1);
+ tmp_20k_buf[SIZEOF_20KBUF-1] = '\0';
+ }
+
+ q_status_message(SM_ORDER, 0, 3, tmp_20k_buf);
+ return(TCL_OK);
+ }
+
+ err = ps_global->last_error;
+ }
+ else
+ err = "open: Unrecognized collection ID";
+ }
+ else
+ err = "open: Can't read folder";
+ }
+ else
+ err = "open: Can't get collection ID";
+
+ Tcl_SetResult(interp, err, TCL_VOLATILE);
+ return(TCL_ERROR);
+}
+
+/*
+ * peSave - Save message with given UID in current folder to
+ * specified collection/folder
+ *
+ * Params: argv[0] == destination context number
+ * argv[1] == testination foldername
+ *
+ * NOTE: just a wrapper around peSaveWork
+ */
+int
+peSave(Tcl_Interp *interp, imapuid_t uid, int objc, Tcl_Obj **objv)
+{
+ return(peSaveWork(interp, uid, objc, objv, PSW_NONE));
+}
+
+
+/*
+ * peCopy - Copy message with given UID in current folder to
+ * specified collection/folder
+ *
+ * Params: argv[0] == destination context number
+ * argv[1] == testination foldername
+ *
+ * NOTE: just a wrapper around peSaveWork that makes sure
+ * delete-on-save is NOT set
+ */
+int
+peCopy(Tcl_Interp *interp, imapuid_t uid, int objc, Tcl_Obj **objv)
+{
+ return(peSaveWork(interp, uid, objc, objv, PSW_COPY));
+}
+
+
+/*
+ * peMove - Move message with given UID in current folder to
+ * specified collection/folder
+ *
+ * Params: argv[0] == destination context number
+ * argv[1] == testination foldername
+ *
+ * NOTE: just a wrapper around peSaveWork that makes sure
+ * delete-on-save IS set so it can be expunged
+ */
+int
+peMove(Tcl_Interp *interp, imapuid_t uid, int objc, Tcl_Obj **objv)
+{
+ return(peSaveWork(interp, uid, objc, objv, PSW_MOVE));
+}
+
+
+/*
+ * peGotoDefault - Default Goto command file name for the given message
+ * specified collection/folder
+ *
+ * Params:
+ *
+ * Returns: name of Goto command default folder or empty string
+ *
+ */
+int
+peGotoDefault(Tcl_Interp *interp, imapuid_t uid, Tcl_Obj **objv)
+{
+ char *folder = NULL;
+ CONTEXT_S *cntxt, *cp;
+ int colid, inbox;
+
+ cntxt = broach_get_folder(ps_global->context_current, &inbox, &folder);
+
+ for(colid = 0, cp = ps_global->context_list; cp != cntxt ; colid++, cp = cp->next)
+ ;
+
+ if(!cp)
+ colid = 0;
+
+ (void) Tcl_ListObjAppendElement(interp, Tcl_GetObjResult(interp),
+ Tcl_NewIntObj(colid));
+ (void) Tcl_ListObjAppendElement(interp, Tcl_GetObjResult(interp),
+ Tcl_NewStringObj(folder ? folder : "", -1));
+ return(TCL_OK);
+}
+
+
+/*
+ * peReplyQuote -
+ *
+ * Params: argv[0] == attachment part number
+ *
+ * Returns: list containing:
+ *
+ */
+int
+peReplyQuote(Tcl_Interp *interp, imapuid_t uid, int objc, Tcl_Obj **objv)
+{
+ char *quote;
+ ENVELOPE *env;
+
+ if(uid){
+ if((env = pine_mail_fetchstructure(ps_global->mail_stream, peSequenceNumber(uid), NULL)) != NULL){
+ quote = reply_quote_str(env);
+ Tcl_SetResult(interp, quote, TCL_VOLATILE);
+ fs_give((void **) &quote);
+ }
+ else{
+ Tcl_SetResult(interp, ps_global->last_error, TCL_VOLATILE);
+ return(TCL_ERROR);
+ }
+ }
+ else
+ Tcl_SetResult(interp, "> ", TCL_VOLATILE);
+
+ return(TCL_OK);
+}
+
+
+void
+peGetMimeTyping(BODY *body, Tcl_Obj **tObjp, Tcl_Obj **stObjp, Tcl_Obj **fnObjp, Tcl_Obj **extObjp)
+{
+ char *ptype = NULL, *psubtype = NULL, *pfile = NULL;
+
+ /*------- Figure out suggested file name ----*/
+ if(body){
+ if((pfile = get_filename_parameter(NULL, 0, body, NULL)) != NULL){
+ /*
+ * if part is generic, see if we can get anything
+ * more from the suggested filename's extension...
+ */
+ if(body->type == TYPEAPPLICATION
+ && (!body->subtype
+ || !strucmp(body->subtype, "octet-stream"))){
+ BODY *fakebody = mail_newbody();
+
+ if(set_mime_type_by_extension(fakebody, pfile)){
+ ptype = body_type_names(fakebody->type);
+ psubtype = cpystr(fakebody->subtype);
+ }
+
+ mail_free_body(&fakebody);
+ }
+ }
+
+ if(!ptype) {
+ ptype = body_type_names(body->type);
+ psubtype = cpystr(body->subtype
+ ? body->subtype
+ : (body->type == TYPETEXT)
+ ? "plain"
+ : (body->type == TYPEAPPLICATION)
+ ? "octet-stream"
+ : "");
+ }
+ }
+ else{
+ ptype = body_type_names(TYPETEXT);
+ psubtype = cpystr("plain");
+ }
+
+ if(extObjp){
+ *extObjp = Tcl_NewStringObj("", 0);
+
+ if(ptype && psubtype && pfile){
+ size_t l;
+ char *mtype;
+ char extbuf[32]; /* mailcap.c limits to three */
+
+ l = strlen(ptype) + strlen(psubtype) + 1;
+ mtype = (char *) fs_get((l+1) * sizeof(char));
+
+ snprintf(mtype, l+1, "%s/%s", ptype, psubtype);
+
+ if(!set_mime_extension_by_type(extbuf, mtype)){
+ char *dotp, *p;
+
+ for(dotp = NULL, p = pfile; *p; p++)
+ if(*p == '.')
+ dotp = p + 1;
+
+ if(dotp)
+ Tcl_SetStringObj(*extObjp, dotp, -1);
+ }
+ else
+ Tcl_SetStringObj(*extObjp, extbuf, -1);
+
+ fs_give((void **) &mtype);
+ }
+ }
+
+ if(tObjp)
+ *tObjp = Tcl_NewStringObj(ptype, -1);
+
+ if(psubtype){
+ if(stObjp)
+ *stObjp = Tcl_NewStringObj(psubtype, -1);
+
+ fs_give((void **) &psubtype);
+ }
+ else if(stObjp)
+ *stObjp = Tcl_NewStringObj("", 0);
+
+ if(pfile){
+ if(fnObjp)
+ *fnObjp = Tcl_NewStringObj(pfile, -1);
+
+ fs_give((void **) &pfile);
+ }
+ else if(fnObjp)
+ *fnObjp = Tcl_NewStringObj("", 0);
+}
+
+
+/*
+ * peAppListF - generate a list of elements based on fmt string,
+ * then append it to the given list object
+ *
+ */
+int
+peAppListF(Tcl_Interp *interp, Tcl_Obj *lobjp, char *fmt, ...)
+{
+ va_list args;
+ char *p, *sval, nbuf[128];
+ int ival, err = 0;
+ unsigned int uval;
+ long lval;
+ unsigned long luval;
+ PATTERN_S *pval;
+ ADDRESS *aval;
+ INTVL_S *vval;
+ Tcl_Obj *lObj = NULL, *sObj;
+
+ if((lObj = Tcl_NewListObj(0, NULL)) != NULL){
+ va_start(args, fmt);
+ for(p = fmt; *p && !err; p++){
+ sObj = NULL;
+
+ if(*p == '%')
+ switch(*++p){
+ case 'i' : /* int value */
+ ival = va_arg(args, int);
+ if((sObj = Tcl_NewIntObj(ival)) == NULL)
+ err++;
+
+ break;
+
+ case 'u' : /* unsigned int value */
+ uval = va_arg(args, unsigned int);
+ snprintf(nbuf, sizeof(nbuf), "%u", uval);
+ if((sObj = Tcl_NewStringObj(nbuf, -1)) == NULL)
+ err++;
+
+ break;
+
+ case 'l' : /* long value */
+ if(*(p+1) == 'u'){
+ p++;
+ luval = va_arg(args, unsigned long);
+ snprintf(nbuf, sizeof(nbuf), "%lu", luval);
+ if((sObj = Tcl_NewStringObj(nbuf, -1)) == NULL)
+ err++;
+ }
+ else{
+ lval = va_arg(args, long);
+ if((sObj = Tcl_NewLongObj(lval)) == NULL)
+ err++;
+ }
+
+ break;
+
+ case 's' : /* string value */
+ sval = va_arg(args, char *);
+ sObj = Tcl_NewStringObj(sval ? sval : "", -1);
+ if(sObj == NULL)
+ err++;
+
+ break;
+
+ case 'a': /* ADDRESS list */
+ aval = va_arg(args, ADDRESS *);
+ if(aval){
+ char *tmp, *p;
+ RFC822BUFFER rbuf;
+ size_t len;
+
+ len = est_size(aval);
+ tmp = (char *) fs_get(len * sizeof(char));
+ tmp[0] = '\0';
+ rbuf.f = dummy_soutr;
+ rbuf.s = NULL;
+ rbuf.beg = tmp;
+ rbuf.cur = tmp;
+ rbuf.end = tmp+len-1;
+ rfc822_output_address_list(&rbuf, aval, 0L, NULL);
+ *rbuf.cur = '\0';
+ p = (char *) rfc1522_decode_to_utf8((unsigned char *) tmp_20k_buf, SIZEOF_20KBUF, tmp);
+ sObj = Tcl_NewStringObj(p, strlen(p));
+ fs_give((void **) &tmp);
+ }
+ else
+ sObj = Tcl_NewStringObj("", -1);
+
+ break;
+
+ case 'p': /* PATTERN_S * */
+ pval = va_arg(args, PATTERN_S *);
+ sval = pattern_to_string(pval);
+ sObj = Tcl_NewStringObj(sval ? sval : "", -1);
+ break;
+
+ case 'v': /* INTVL_S * */
+ vval = va_arg(args, INTVL_S *);
+ if(vval){
+ for(; vval != NULL; vval = vval->next){
+ peAppListF(interp, sObj, "%l%l", vval->imin, vval->imax);
+ }
+ }
+ else
+ sObj = Tcl_NewListObj(0, NULL);
+
+ break;
+
+ case 'o': /* Tcl_Obj * */
+ sObj = va_arg(args, Tcl_Obj *);
+ break;
+ }
+
+ if(sObj)
+ Tcl_ListObjAppendElement(interp, lObj, sObj);
+ }
+
+ va_end(args);
+ }
+
+ return(lObj ? Tcl_ListObjAppendElement(interp, lobjp, lObj) : TCL_ERROR);
+}
+
+/*
+ * pePatAppendID - append list of pattern identity variables to given object
+ */
+void
+pePatAppendID(Tcl_Interp *interp, Tcl_Obj *patObj, PAT_S *pat)
+{
+ Tcl_Obj *resObj;
+
+ resObj = Tcl_NewListObj(0, NULL);
+ peAppListF(interp, resObj, "%s%s", "nickname", pat->patgrp->nick);
+ peAppListF(interp, resObj, "%s%s", "comment", pat->patgrp->comment);
+ Tcl_ListObjAppendElement(interp, patObj, resObj);
+}
+
+
+/*
+ * pePatAppendPattern - append list of pattern variables to given object
+ */
+void
+pePatAppendPattern(Tcl_Interp *interp, Tcl_Obj *patObj, PAT_S *pat)
+{
+ ARBHDR_S *ah;
+ Tcl_Obj *resObj;
+
+ resObj = Tcl_NewListObj(0, NULL);
+ peAppListF(interp, resObj, "%s%p", "to", pat->patgrp->to);
+ peAppListF(interp, resObj, "%s%p", "from", pat->patgrp->from);
+ peAppListF(interp, resObj, "%s%p", "sender", pat->patgrp->sender);
+ peAppListF(interp, resObj, "%s%p", "cc", pat->patgrp->cc);
+ peAppListF(interp, resObj, "%s%p", "recip", pat->patgrp->recip);
+ peAppListF(interp, resObj, "%s%p", "partic", pat->patgrp->partic);
+ peAppListF(interp, resObj, "%s%p", "news", pat->patgrp->news);
+ peAppListF(interp, resObj, "%s%p", "subj", pat->patgrp->subj);
+ peAppListF(interp, resObj, "%s%p", "alltext", pat->patgrp->alltext);
+ peAppListF(interp, resObj, "%s%p", "bodytext", pat->patgrp->bodytext);
+ peAppListF(interp, resObj, "%s%p", "keyword", pat->patgrp->keyword);
+ peAppListF(interp, resObj, "%s%p", "charset", pat->patgrp->charsets);
+
+ peAppListF(interp, resObj, "%s%v", "score", pat->patgrp->score);
+ peAppListF(interp, resObj, "%s%v", "age", pat->patgrp->age);
+ peAppListF(interp, resObj, "%s%v", "size", pat->patgrp->size);
+
+ if((ah = pat->patgrp->arbhdr) != NULL){
+ Tcl_Obj *hlObj, *hObj;
+
+ hlObj = Tcl_NewListObj(0, NULL);
+ Tcl_ListObjAppendElement(interp, hlObj, Tcl_NewStringObj("headers", -1));
+
+ for(; ah; ah = ah->next){
+ hObj = Tcl_NewListObj(0, NULL);
+ peAppListF(interp, hObj, "%s%p", ah->field ? ah->field : "", ah->p);
+ Tcl_ListObjAppendElement(interp, hlObj, hObj);
+ }
+
+ Tcl_ListObjAppendElement(interp, resObj, hlObj);
+ }
+
+ switch(pat->patgrp->fldr_type){
+ case FLDR_ANY:
+ peAppListF(interp, resObj, "%s%s", "ftype", "any");
+ break;
+ case FLDR_NEWS:
+ peAppListF(interp, resObj, "%s%s", "ftype", "news");
+ break;
+ case FLDR_EMAIL:
+ peAppListF(interp, resObj, "%s%s", "ftype", "email");
+ break;
+ case FLDR_SPECIFIC:
+ peAppListF(interp, resObj, "%s%s", "ftype", "specific");
+ break;
+ }
+
+ peAppListF(interp, resObj, "%s%p", "folder", pat->patgrp->folder);
+ peAppListF(interp, resObj, "%s%s", "stat_new", pePatStatStr(pat->patgrp->stat_new));
+ peAppListF(interp, resObj, "%s%s", "stat_rec", pePatStatStr(pat->patgrp->stat_rec));
+ peAppListF(interp, resObj, "%s%s", "stat_del", pePatStatStr(pat->patgrp->stat_del));
+ peAppListF(interp, resObj, "%s%s", "stat_imp", pePatStatStr(pat->patgrp->stat_imp));
+ peAppListF(interp, resObj, "%s%s", "stat_ans", pePatStatStr(pat->patgrp->stat_ans));
+ peAppListF(interp, resObj, "%s%s", "stat_8bitsubj", pePatStatStr(pat->patgrp->stat_8bitsubj));
+ peAppListF(interp, resObj, "%s%s", "stat_bom", pePatStatStr(pat->patgrp->stat_bom));
+ peAppListF(interp, resObj, "%s%s", "stat_boy", pePatStatStr(pat->patgrp->stat_boy));
+
+ Tcl_ListObjAppendElement(interp, patObj, resObj);
+}
+
+
+char *
+pePatStatStr(int value)
+{
+ switch(value){
+ case PAT_STAT_EITHER:
+ return("either");
+ break;
+
+ case PAT_STAT_YES:
+ return("yes");
+ break;
+
+ default :
+ return("no");
+ break;
+ }
+}
+
+
+/*
+ * peCreateUserContext - create new ps_global and set it up
+ */
+char *
+peCreateUserContext(Tcl_Interp *interp, char *user, char *config, char *defconf)
+{
+ if(ps_global)
+ peDestroyUserContext(&ps_global);
+
+ set_collation(1, 1);
+
+ ps_global = new_pine_struct();
+
+ /*----------------------------------------------------------------------
+ Place any necessary constraints on pith processing
+ ----------------------------------------------------------------------*/
+
+ /* got thru close procedure without expunging */
+ ps_global->noexpunge_on_close = 1;
+
+ /* do NOT let user set path to local executable */
+ ps_global->vars[V_SENDMAIL_PATH].is_user = 0;
+
+
+ /*----------------------------------------------------------------------
+ Proceed with reading acquiring user settings
+ ----------------------------------------------------------------------*/
+ if(ps_global->pinerc)
+ fs_give((void **) &ps_global->pinerc);
+
+ if(ps_global->prc)
+ free_pinerc_s(&ps_global->prc);
+
+ if(!IS_REMOTE(config)){
+ snprintf(tmp_20k_buf, SIZEOF_20KBUF, "Non-Remote config: %s", config);
+ return(tmp_20k_buf);
+ }
+
+ ps_global->prc = new_pinerc_s(config);
+
+ if(defconf){
+ if(ps_global->pconf)
+ free_pinerc_s(&ps_global->pconf);
+
+ ps_global->pconf = new_pinerc_s(defconf);
+ }
+
+ /*
+ * Fake up some user information
+ */
+ ps_global->ui.login = cpystr(user);
+
+#ifdef DEBUG
+ /*
+ * Prep for IMAP debugging
+ */
+ setup_imap_debug();
+#endif
+
+ /* CHECK FOR AND PASS BACK ANY INIT ERRORS */
+ return(peLoadConfig(ps_global));
+}
+
+
+
+void
+peDestroyUserContext(struct pine **pps)
+{
+
+ completely_done_with_adrbks();
+
+ free_pinerc_strings(pps);
+#if 0
+ imap_flush_passwd_cache(TRUE);
+#endif
+ clear_index_cache(sp_inbox_stream(), 0);
+ free_newsgrp_cache();
+ mailcap_free();
+ close_patterns(0L);
+ free_extra_hdrs();
+ free_contexts(&ps_global->context_list);
+
+ pico_endcolor();
+
+ free_strlist(&peCertHosts);
+
+ free_pine_struct(pps);
+}
+
+
+char *
+peLoadConfig(struct pine *pine_state)
+{
+ int rv;
+ char *s, *db = NULL;
+ extern void init_signals(void); /* in signal.c */
+
+ if(!pine_state)
+ return("No global state present");
+
+#if 0
+/*turned off because we don't care about local user*/
+ /* need home directory early */
+ get_user_info(&pine_state->ui);
+
+ pine_state->home_dir = cpystr((getenv("HOME") != NULL)
+ ? getenv("HOME")
+ : pine_state->ui.homedir);
+#endif
+
+ init_pinerc(pine_state, &db);
+
+ fs_give((void **) &db);
+
+ /*
+ * Initial allocation of array of stream pool pointers.
+ * We do this before init_vars so that we can re-use streams used for
+ * remote config files. These sizes may get changed later.
+ */
+ ps_global->s_pool.max_remstream = 2;
+
+ ps_global->c_client_error[0] = '\0';
+
+ pePrepareForAuthException();
+
+ peInitVars(pine_state);
+
+ if(s = peAuthException())
+ return(s);
+ else if(ps_global->c_client_error[0])
+ return(ps_global->c_client_error);
+
+ mail_parameters(NULL, SET_SENDCOMMAND, (void *) pine_imap_cmd_happened);
+ mail_parameters(NULL, SET_FREESTREAMSPAREP, (void *) sp_free_callback);
+ mail_parameters(NULL, SET_FREEELTSPAREP, (void *) free_pine_elt);
+
+ /*
+ * Install callback to handle certificate validation failures,
+ * allowing the user to continue if they wish.
+ */
+ mail_parameters(NULL, SET_SSLCERTIFICATEQUERY, (void *) alpine_sslcertquery);
+ mail_parameters(NULL, SET_SSLFAILURE, (void *) alpine_sslfailure);
+
+ /*
+ * Set up a c-client read timeout and timeout handler. In general,
+ * it shouldn't happen, but a server crash or dead link can cause
+ * pine to appear wedged if we don't set this up...
+ */
+ mail_parameters(NULL, SET_OPENTIMEOUT,
+ (void *)((pine_state->VAR_TCPOPENTIMEO
+ && (rv = atoi(pine_state->VAR_TCPOPENTIMEO)) > 4)
+ ? (long) rv : 30L));
+ mail_parameters(NULL, SET_TIMEOUT, (void *) alpine_tcptimeout);
+
+ if(pine_state->VAR_RSHOPENTIMEO
+ && ((rv = atoi(pine_state->VAR_RSHOPENTIMEO)) == 0 || rv > 4))
+ mail_parameters(NULL, SET_RSHTIMEOUT, (void *) rv);
+
+ if(pine_state->VAR_SSHOPENTIMEO
+ && ((rv = atoi(pine_state->VAR_SSHOPENTIMEO)) == 0 || rv > 4))
+ mail_parameters(NULL, SET_SSHTIMEOUT, (void *) rv);
+
+ /*
+ * Tell c-client not to be so aggressive about uid mappings
+ */
+ mail_parameters(NULL, SET_UIDLOOKAHEAD, (void *) 20);
+
+ /*
+ * Setup referral handling
+ */
+ mail_parameters(NULL, SET_IMAPREFERRAL, (void *) imap_referral);
+ mail_parameters(NULL, SET_MAILPROXYCOPY, (void *) imap_proxycopy);
+
+ /*
+ * Install extra headers to fetch along with all the other stuff
+ * mail_fetch_structure and mail_fetch_overview requests.
+ */
+ calc_extra_hdrs();
+
+ if(get_extra_hdrs())
+ (void) mail_parameters(NULL, SET_IMAPEXTRAHEADERS, (void *) get_extra_hdrs());
+
+ (void) init_username(pine_state);
+
+ (void) init_hostname(ps_global);
+
+#ifdef ENABLE_LDAP
+ (void) init_ldap_pname(ps_global);
+#endif /* ENABLE_LDAP */
+
+ if(ps_global->prc && ps_global->prc->type == Loc &&
+ can_access(ps_global->pinerc, ACCESS_EXISTS) == 0 &&
+ can_access(ps_global->pinerc, EDIT_ACCESS) != 0)
+ ps_global->readonly_pinerc = 1;
+
+ /*
+ * c-client needs USR2 and we might as well
+ * do something sensible with HUP and TERM
+ */
+ init_signals();
+
+ strncpy(pine_state->inbox_name, INBOX_NAME, sizeof(pine_state->inbox_name));
+ pine_state->inbox_name[sizeof(pine_state->inbox_name)-1] = '\0';
+
+ init_folders(pine_state); /* digest folder spec's */
+
+ /*
+ * Various options we want to make sure are set OUR way
+ */
+ F_TURN_ON(F_QUELL_IMAP_ENV_CB, pine_state);
+ F_TURN_ON(F_SLCTBL_ITEM_NOBOLD, pine_state);
+ F_TURN_OFF(F_USE_SYSTEM_TRANS, pine_state);
+
+ /*
+ * Fake screen dimensions for index formatting and
+ * message display wrap...
+ */
+ ps_global->ttyo = (struct ttyo *) fs_get(sizeof(struct ttyo));
+ ps_global->ttyo->screen_rows = FAKE_SCREEN_LENGTH;
+ ps_global->ttyo->screen_cols = FAKE_SCREEN_WIDTH;
+ if(ps_global->VAR_WP_COLUMNS){
+ int w = atoi(ps_global->VAR_WP_COLUMNS);
+ if(w >= 20 && w <= 128)
+ ps_global->ttyo->screen_cols = w;
+ }
+
+ ps_global->ttyo->header_rows = 0;
+ ps_global->ttyo->footer_rows = 0;
+
+
+ /* init colors */
+ if(ps_global->VAR_NORM_FORE_COLOR)
+ pico_nfcolor(ps_global->VAR_NORM_FORE_COLOR);
+
+ if(ps_global->VAR_NORM_BACK_COLOR)
+ pico_nbcolor(ps_global->VAR_NORM_BACK_COLOR);
+
+ if(ps_global->VAR_REV_FORE_COLOR)
+ pico_rfcolor(ps_global->VAR_REV_FORE_COLOR);
+
+ if(ps_global->VAR_REV_BACK_COLOR)
+ pico_rbcolor(ps_global->VAR_REV_BACK_COLOR);
+
+ pico_set_normal_color();
+
+ return(NULL);
+}
+
+
+int
+peCreateStream(Tcl_Interp *interp, CONTEXT_S *context, char *mailbox, int do_inbox)
+{
+ unsigned long flgs = 0L;
+ char *s;
+
+ ps_global->c_client_error[0] = ps_global->last_error[0] = '\0';
+
+ pePrepareForAuthException();
+
+ if(do_inbox)
+ flgs |= DB_INBOXWOCNTXT;
+
+ if(do_broach_folder(mailbox, context, NULL, flgs) && ps_global->mail_stream){
+ dprint((SYSDBG_INFO, "Mailbox open: %s",
+ ps_global->mail_stream->mailbox ? ps_global->mail_stream->mailbox : "<UNKNOWN>"));
+ return(TCL_OK);
+ }
+
+ Tcl_SetResult(interp,
+ (s = peAuthException())
+ ? s
+ : (*ps_global->last_error)
+ ? ps_global->last_error
+ : "Login Error",
+ TCL_VOLATILE);
+ return(TCL_ERROR);
+}
+
+
+void
+peDestroyStream(struct pine *ps)
+{
+ int cur_is_inbox;
+
+ if(ps){
+ cur_is_inbox = (sp_inbox_stream() == ps_global->mail_stream);
+
+ /* clean up open streams */
+ if(ps->mail_stream){
+ expunge_and_close(ps->mail_stream, NULL, EC_NONE);
+ ps_global->mail_stream = NULL;
+ ps_global->cur_folder[0] = '\0';
+ }
+
+ if(ps->msgmap)
+ mn_give(&ps->msgmap);
+
+ if(sp_inbox_stream() && !cur_is_inbox){
+ ps->mail_stream = sp_inbox_stream();
+ ps->msgmap = sp_msgmap(ps->mail_stream);
+ sp_set_expunge_count(ps_global->mail_stream, 0L);
+ expunge_and_close(sp_inbox_stream(), NULL, EC_NONE);
+ mn_give(&ps->msgmap);
+ }
+ }
+}
+
+
+/*
+ * pePrepareForAuthException - set globals to get feedback from bowels of c-client
+ */
+void
+pePrepareForAuthException(void)
+{
+ peNoPassword = peCredentialError = peCertFailure = peCertQuery = 0;
+}
+
+/*
+ * pePrepareForAuthException - check globals getting feedback from bowels of c-client
+ */
+char *
+peAuthException()
+{
+ static char buf[CRED_REQ_SIZE];
+
+ if(peCertQuery){
+ snprintf(buf, CRED_REQ_SIZE, "%s %s", CERT_QUERY_STRING, peCredentialRequestor);
+ return(buf);
+ }
+
+ if(peCertFailure){
+ snprintf(buf, CRED_REQ_SIZE, "%s %s", CERT_FAILURE_STRING, peCredentialRequestor);
+ return(buf);
+ }
+
+ if(peNoPassword){
+ snprintf(buf, CRED_REQ_SIZE, "%s %s", AUTH_EMPTY_STRING, peCredentialRequestor);
+ return(buf);
+ }
+
+ if(peCredentialError){
+ snprintf(buf, CRED_REQ_SIZE, "%s %s", AUTH_FAILURE_STRING, peCredentialRequestor);
+ return(buf);
+ }
+
+ return(NULL);
+}
+
+
+void
+peInitVars(struct pine *ps)
+{
+ init_vars(ps, NULL);
+
+ /*
+ * fix display/keyboard-character-set to utf-8
+ *
+ *
+ */
+
+ if(ps->display_charmap)
+ fs_give((void **) &ps->display_charmap);
+
+ ps->display_charmap = cpystr(WP_INTERNAL_CHARSET);
+
+ if(ps->keyboard_charmap)
+ fs_give((void **) &ps->keyboard_charmap);
+
+ ps->keyboard_charmap = cpystr(WP_INTERNAL_CHARSET);
+
+ (void) setup_for_input_output(FALSE, &ps->display_charmap, &ps->keyboard_charmap, &ps->input_cs, NULL);;
+}
+
+
+
+int
+peMessageBounce(Tcl_Interp *interp, imapuid_t uid, int objc, Tcl_Obj **objv)
+{
+ char *errstr = NULL, *to, *subj = NULL, errbuf[WP_MAX_POST_ERROR + 1];
+ long rawno;
+ ENVELOPE *env, *outgoing = NULL;
+ METAENV *metaenv;
+ PINEFIELD *custom;
+ BODY *body = NULL;
+
+ if(uid){
+ rawno = peSequenceNumber(uid);
+
+ if(objc > 0 && objv[0] && (to = Tcl_GetStringFromObj(objv[0], NULL))){
+ if(objc == 2 && objv[1]){
+ subj = Tcl_GetStringFromObj(objv[1], NULL);
+ }
+ else if((env = mail_fetchstructure(ps_global->mail_stream, rawno, NULL)) != NULL){
+ subj = env->subject;
+ }
+ else{
+ Tcl_SetResult(interp, ps_global->last_error, TCL_VOLATILE);
+ return(TCL_ERROR);
+ }
+
+ if((errstr = bounce_msg_body(ps_global->mail_stream, rawno, NULL,
+ &to, subj, &outgoing, &body, NULL))){
+ Tcl_SetResult(interp, errstr, TCL_VOLATILE);
+ return(TCL_ERROR);
+ }
+
+ metaenv = pine_new_env(outgoing, NULL, NULL, custom = peCustomHdrs());
+
+ if(!outgoing->from)
+ outgoing->from = generate_from();
+
+ rfc822_date(tmp_20k_buf);
+ outgoing->date = (unsigned char *) cpystr(tmp_20k_buf);
+
+ outgoing->return_path = rfc822_cpy_adr(outgoing->from);
+ if(!outgoing->message_id)
+ outgoing->message_id = generate_message_id();
+
+ /* NO FCC */
+
+ if(peDoPost(metaenv, body, NULL, NULL, errbuf) != TCL_OK)
+ errstr = errbuf;
+
+ pine_free_body(&body);
+
+ pine_free_env(&metaenv);
+
+ if(custom)
+ free_customs(custom);
+
+ mail_free_envelope(&outgoing);
+ pine_free_body(&body);
+
+ }
+ }
+
+ Tcl_SetResult(interp, (errstr) ? errstr : "OK", TCL_VOLATILE);
+ return(errstr ? TCL_ERROR : TCL_OK);
+}
+
+
+int
+peMessageSpamNotice(interp, uid, objc, objv)
+ Tcl_Interp *interp;
+ imapuid_t uid;
+ int objc;
+ Tcl_Obj **objv;
+{
+ char *to, *subj = NULL, errbuf[WP_MAX_POST_ERROR + 1], *rs = NULL;
+ long rawno;
+
+ if(uid){
+ rawno = peSequenceNumber(uid);
+
+ if(objv[0] && (to = Tcl_GetStringFromObj(objv[0], NULL)) && strlen(to)){
+
+ if(objv[1])
+ subj = Tcl_GetStringFromObj(objv[1], NULL);
+
+ rs = peSendSpamReport(rawno, to, subj, errbuf);
+ }
+ }
+
+ Tcl_SetResult(interp, (rs) ? rs : "OK", TCL_VOLATILE);
+ return(rs ? TCL_ERROR : TCL_OK);
+}
+
+
+char *
+peSendSpamReport(long rawno, char *to, char *subj, char *errbuf)
+{
+ char *errstr = NULL, *tmp_a_string;
+ ENVELOPE *env, *outgoing;
+ METAENV *metaenv;
+ PINEFIELD *custom;
+ BODY *body;
+ static char *fakedomain = "@";
+ void *msgtext;
+
+
+ if((env = mail_fetchstructure(ps_global->mail_stream, rawno, NULL)) == NULL){
+ return(ps_global->last_error);
+ }
+
+ /* empty subject gets "spam" subject */
+ if(!(subj && *subj))
+ subj = env->subject;
+
+ /*---- New Body to start with ----*/
+ body = mail_newbody();
+ body->type = TYPEMULTIPART;
+
+ /*---- The TEXT part/body ----*/
+ body->nested.part = mail_newbody_part();
+ body->nested.part->body.type = TYPETEXT;
+
+ if((msgtext = (void *)so_get(CharStar, NULL, EDIT_ACCESS)) == NULL){
+ pine_free_body(&body);
+ return("peSendSpamReport: Can't allocate text");
+ }
+ else{
+ sprintf(tmp_20k_buf,
+ "The attached message is being reported to <%s> as Spam\n",
+ to);
+ so_puts((STORE_S *) msgtext, tmp_20k_buf);
+ body->nested.part->body.contents.text.data = msgtext;
+ }
+
+ /*---- Attach the raw message ----*/
+ if(forward_mime_msg(ps_global->mail_stream, rawno, NULL, env,
+ &(body->nested.part->next), msgtext)){
+ outgoing = mail_newenvelope();
+ metaenv = pine_new_env(outgoing, NULL, NULL, custom = peCustomHdrs());
+ }
+ else{
+ pine_free_body(&body);
+ return("peSendSpamReport: Can't generate forwarded message");
+ }
+
+ /* rfc822_parse_adrlist feels free to destroy input so copy */
+ tmp_a_string = cpystr(to);
+ rfc822_parse_adrlist(&outgoing->to, tmp_a_string,
+ (F_ON(F_COMPOSE_REJECTS_UNQUAL, ps_global))
+ ? fakedomain : ps_global->maildomain);
+ fs_give((void **) &tmp_a_string);
+
+ outgoing->from = generate_from();
+ outgoing->subject = cpystr(subj);
+ outgoing->return_path = rfc822_cpy_adr(outgoing->from);
+ outgoing->message_id = generate_message_id();
+
+ rfc822_date(tmp_20k_buf);
+ outgoing->date = (unsigned char *) cpystr(tmp_20k_buf);
+
+ /* NO FCC for Spam Reporting */
+
+ if(peDoPost(metaenv, body, NULL, NULL, errbuf) != TCL_OK)
+ errstr = errbuf;
+
+ pine_free_body(&body);
+
+ pine_free_env(&metaenv);
+
+ if(custom)
+ free_customs(custom);
+
+ mail_free_envelope(&outgoing);
+ pine_free_body(&body);
+
+ return(errstr);
+}
+
+
+/* * * * * * * * * * * * * Start of Composer Routines * * * * * * * * * * * */
+
+
+/*
+ * PEComposeCmd - export various bits of alpine state
+ */
+int
+PEComposeCmd(ClientData clientData, Tcl_Interp *interp, int objc, Tcl_Obj *CONST objv[])
+{
+ char *err = "PECompose: Unknown request";
+
+ dprint((2, "PEComposeCmd"));
+
+ if(objc == 1){
+ Tcl_WrongNumArgs(interp, 1, objv, "cmd ?args?");
+ }
+ else if(!ps_global){
+ Tcl_SetResult(interp, "peCompose: no config present", TCL_STATIC);
+ return(TCL_ERROR);
+ }
+ else{
+ char *s1 = Tcl_GetStringFromObj(objv[1], NULL);
+
+ if(s1){
+ if(!strcmp(s1, "post")){
+ long flags = PMC_NONE;
+ if(F_ON(F_COMPOSE_REJECTS_UNQUAL, ps_global))
+ flags |= PMC_FORCE_QUAL;
+
+ return(peMsgCollector(interp, objc - 2, (Tcl_Obj **) &objv[2], peDoPost, flags));
+ }
+ else if(objc == 2){
+ if(!strcmp(s1, "userhdrs")){
+ int i;
+ char *p;
+ PINEFIELD *custom, *cp;
+ ADDRESS *from;
+ static char *standard[] = {"To", "Cc", "Bcc", "Fcc", "Attach", "Subject", NULL};
+
+ custom = peCustomHdrs();
+
+ for(i = 0; standard[i]; i++){
+ p = NULL;
+ for(cp = custom; cp; cp = cp->next)
+ if(!strucmp(cp->name, standard[i]))
+ p = cp->textbuf;
+
+ peAppListF(interp, Tcl_GetObjResult(interp), "%s%s", standard[i], p);
+ }
+
+ for(cp = custom; cp != NULL; cp = cp->next){
+ if(!strucmp(cp->name, "from")){
+ if(F_OFF(F_ALLOW_CHANGING_FROM, ps_global))
+ continue;
+
+ if(cp->textbuf && strlen(cp->textbuf)){
+ p = cp->textbuf;
+ }
+ else{
+ RFC822BUFFER rbuf;
+
+ tmp_20k_buf[0] = '\0';
+ rbuf.f = dummy_soutr;
+ rbuf.s = NULL;
+ rbuf.beg = tmp_20k_buf;
+ rbuf.cur = tmp_20k_buf;
+ rbuf.end = tmp_20k_buf+SIZEOF_20KBUF-1;
+ rfc822_output_address_list(&rbuf, from = generate_from(), 0L, NULL);
+ *rbuf.cur = '\0';
+ mail_free_address(&from);
+ p = tmp_20k_buf;
+ }
+ }
+ else{
+ p = cp->textbuf;
+ for(i = 0; standard[i]; i++)
+ if(!strucmp(standard[i], cp->name))
+ p = NULL;
+
+ if(!p)
+ continue;
+ }
+
+ peAppListF(interp, Tcl_GetObjResult(interp), "%s%s", cp->name, p);
+ }
+
+ if(custom)
+ free_customs(custom);
+
+ return(TCL_OK);
+ }
+ else if(!strcmp(s1, "syshdrs")){
+ int i;
+ static char *extras[] = {"In-Reply-To", "X-Reply-UID", NULL};
+
+ for(i = 0; extras[i]; i++)
+ peAppListF(interp, Tcl_GetObjResult(interp), "%s%s", extras[i], NULL);
+
+ return(TCL_OK);
+ }
+ else if(!strcmp(s1, "composehdrs")){
+ char **p, *q;
+
+ if((p = ps_global->VAR_COMP_HDRS) && *p){
+ for(; *p; p++)
+ Tcl_ListObjAppendElement(interp,
+ Tcl_GetObjResult(interp),
+ Tcl_NewStringObj(*p, (q = strchr(*p, ':'))
+ ? (q - *p) : -1));
+ }
+ else
+ Tcl_SetResult(interp, "", TCL_STATIC);
+
+ return(TCL_OK);
+ }
+ else if(!strcmp(s1, "fccdefault")){
+ int ci = 0;
+ CONTEXT_S *c = default_save_context(ps_global->context_list), *c2;
+
+ for(c2 = ps_global->context_list; c && c != c2; c2 = c2->next)
+ ci++;
+
+ Tcl_ListObjAppendElement(interp,
+ Tcl_GetObjResult(interp),
+ Tcl_NewIntObj(ci));
+ Tcl_ListObjAppendElement(interp,
+ Tcl_GetObjResult(interp),
+ Tcl_NewStringObj(ps_global->VAR_DEFAULT_FCC
+ ? ps_global->VAR_DEFAULT_FCC
+ : "", -1));
+ return(TCL_OK);
+ }
+ else if(!strcmp(s1, "noattach")){
+ peFreeAttach(&peCompAttach);
+ Tcl_SetResult(interp, "OK", TCL_VOLATILE);
+ return(TCL_OK);
+ }
+ else if(!strcmp(s1, "from")){
+ RFC822BUFFER rbuf;
+ ADDRESS *from = generate_from();
+ tmp_20k_buf[0] = '\0';
+ rbuf.f = dummy_soutr;
+ rbuf.s = NULL;
+ rbuf.beg = tmp_20k_buf;
+ rbuf.cur = tmp_20k_buf;
+ rbuf.end = tmp_20k_buf+SIZEOF_20KBUF-1;
+ rfc822_output_address_list(&rbuf, from, 0L, NULL);
+ *rbuf.cur = '\0';
+ mail_free_address(&from);
+ Tcl_SetResult(interp, tmp_20k_buf, TCL_VOLATILE);
+ return(TCL_OK);
+ }
+ else if(!strcmp(s1, "attachments")){
+ COMPATT_S *p;
+ Tcl_Obj *objAttach;
+
+ for(p = peCompAttach; p; p = p->next)
+ if(p->file){
+ objAttach = Tcl_NewListObj(0, NULL);
+
+ /* id */
+ Tcl_ListObjAppendElement(interp, objAttach, Tcl_NewStringObj(p->id,-1));
+
+ /* file name */
+ Tcl_ListObjAppendElement(interp, objAttach, Tcl_NewStringObj(p->l.f.remote,-1));
+
+ /* file size */
+ Tcl_ListObjAppendElement(interp, objAttach, Tcl_NewLongObj(p->l.f.size));
+
+ /* type/subtype */
+ snprintf(tmp_20k_buf, SIZEOF_20KBUF, "%s/%s", p->l.f.type, p->l.f.subtype);
+ Tcl_ListObjAppendElement(interp, objAttach, Tcl_NewStringObj(tmp_20k_buf,-1));
+
+ /* append to list */
+ Tcl_ListObjAppendElement(interp, Tcl_GetObjResult(interp), objAttach);
+ }
+ else if(p->body){
+ char *name;
+
+ objAttach = Tcl_NewListObj(0, NULL);
+
+ /* id */
+ Tcl_ListObjAppendElement(interp, objAttach, Tcl_NewStringObj(p->id,-1));
+
+ /* file name */
+ if((name = get_filename_parameter(NULL, 0, p->l.b.body, NULL)) != NULL){
+ Tcl_ListObjAppendElement(interp, objAttach, Tcl_NewStringObj(name, -1));
+ fs_give((void **) &name);
+ }
+ else
+ Tcl_ListObjAppendElement(interp, objAttach, Tcl_NewStringObj("Unknown", -1));
+
+ /* file size */
+ Tcl_ListObjAppendElement(interp, objAttach,
+ Tcl_NewLongObj((p->l.b.body->encoding == ENCBASE64)
+ ? ((p->l.b.body->size.bytes * 3)/4)
+ : p->l.b.body->size.bytes));
+
+ /* type/subtype */
+ snprintf(tmp_20k_buf, SIZEOF_20KBUF, "%s/%s",
+ body_type_names(p->l.b.body->type),
+ p->l.b.body->subtype ? p->l.b.body->subtype : "Unknown");
+ Tcl_ListObjAppendElement(interp, objAttach, Tcl_NewStringObj(tmp_20k_buf, -1));
+
+ Tcl_ListObjAppendElement(interp, Tcl_GetObjResult(interp), objAttach);
+ }
+
+ return(TCL_OK);
+ }
+ }
+ else if(objc == 3){
+ if(!strcmp(s1, "unattach")){
+ char *id;
+
+ if((id = Tcl_GetStringFromObj(objv[2], NULL)) != NULL){
+ if(peClearAttachID(id)){
+ Tcl_SetResult(interp, "OK", TCL_STATIC);
+ return(TCL_OK);
+ }
+ else
+ err = "Can't access attachment id";
+ }
+ else
+ err = "Can't read attachment id";
+ }
+ else if(!strcmp(s1, "attachinfo")){
+ COMPATT_S *a;
+ char *id, *s;
+
+ /* return: remote-filename size "type/subtype" */
+ if((id = Tcl_GetStringFromObj(objv[2], NULL)) != NULL){
+ if((a = peGetAttachID(id)) != NULL){
+ if(a->file){
+ /* file name */
+ Tcl_ListObjAppendElement(interp, Tcl_GetObjResult(interp), Tcl_NewStringObj(a->l.f.remote,-1));
+
+ /* file size */
+ Tcl_ListObjAppendElement(interp, Tcl_GetObjResult(interp), Tcl_NewLongObj(a->l.f.size));
+
+ /* type/subtype */
+ snprintf(tmp_20k_buf, SIZEOF_20KBUF, "%s/%s", a->l.f.type, a->l.f.subtype);
+ Tcl_ListObjAppendElement(interp, Tcl_GetObjResult(interp), Tcl_NewStringObj(tmp_20k_buf,-1));
+
+ /* description */
+ Tcl_ListObjAppendElement(interp, Tcl_GetObjResult(interp),
+ Tcl_NewStringObj((a->l.f.description) ? a->l.f.description : "",-1));
+ return(TCL_OK);
+ }
+ else if(a->body){
+ char *name;
+
+ /* file name */
+ if((name = get_filename_parameter(NULL, 0, a->l.b.body, NULL)) != NULL){
+ Tcl_ListObjAppendElement(interp, Tcl_GetObjResult(interp), Tcl_NewStringObj(name, -1));
+ fs_give((void **) &name);
+ }
+ else
+ Tcl_ListObjAppendElement(interp, Tcl_GetObjResult(interp), Tcl_NewStringObj("Unknown", -1));
+
+ /* file size */
+ Tcl_ListObjAppendElement(interp, Tcl_GetObjResult(interp),
+ Tcl_NewLongObj((a->l.b.body->encoding == ENCBASE64)
+ ? ((a->l.b.body->size.bytes * 3)/4)
+ : a->l.b.body->size.bytes));
+
+ /* type/subtype */
+ snprintf(tmp_20k_buf, SIZEOF_20KBUF, "%s/%s",
+ body_type_names(a->l.b.body->type),
+ a->l.b.body->subtype ? a->l.b.body->subtype : "Unknown");
+
+ Tcl_ListObjAppendElement(interp, Tcl_GetObjResult(interp), Tcl_NewStringObj(tmp_20k_buf, -1));
+
+ /* description */
+ if(a->l.b.body->description){
+ snprintf(tmp_20k_buf, SIZEOF_20KBUF, "%.*s", 256, a->l.b.body->description);
+ }
+ else if((s = parameter_val(a->l.b.body->parameter, "description")) != NULL){
+ snprintf(tmp_20k_buf, SIZEOF_20KBUF, "%.*s", 256, s);
+ fs_give((void **) &s);
+ }
+ else
+ tmp_20k_buf[0] = '\0';
+
+ Tcl_ListObjAppendElement(interp, Tcl_GetObjResult(interp), Tcl_NewStringObj(tmp_20k_buf, -1));
+
+ return(TCL_OK);
+ }
+
+ err = "Unknown attachment type";
+ }
+ else
+ err = "Can't access attachment id";
+ }
+ else
+ err = "Can't read attachment id";
+ }
+ }
+ else if(objc == 7){
+ if(!strcmp(s1, "attach")){
+ char *file, *remote, *type, *subtype, *desc;
+
+ if((file = Tcl_GetStringFromObj(objv[2], NULL))
+ && (type = Tcl_GetStringFromObj(objv[3], NULL))
+ && (subtype = Tcl_GetStringFromObj(objv[4], NULL))
+ && (remote = Tcl_GetStringFromObj(objv[5], NULL))){
+ int dl;
+
+ desc = Tcl_GetStringFromObj(objv[6], &dl);
+
+ if(desc){
+ Tcl_SetResult(interp, peFileAttachID(file, type, subtype, remote, desc, dl), TCL_VOLATILE);
+ return(TCL_OK);
+ }
+ else
+ err = "Can't read file description";
+ }
+ else
+ err = "Can't read file name";
+ }
+ }
+ }
+ }
+
+ Tcl_SetResult(interp, err, TCL_STATIC);
+ return(TCL_ERROR);
+}
+
+
+COMPATT_S *
+peNewAttach(void)
+{
+ COMPATT_S *p = (COMPATT_S *) fs_get(sizeof(COMPATT_S));
+ memset(p, 0, sizeof(COMPATT_S));
+ return(p);
+}
+
+
+void
+peFreeAttach(COMPATT_S **a)
+{
+ if(a && *a){
+ fs_give((void **) &(*a)->id);
+
+ if((*a)->file){
+ if((*a)->l.f.type)
+ fs_give((void **) &(*a)->l.f.type);
+
+ if((*a)->l.f.subtype)
+ fs_give((void **) &(*a)->l.f.subtype);
+
+ if((*a)->l.f.remote)
+ fs_give((void **) &(*a)->l.f.remote);
+
+ if((*a)->l.f.local){
+ (void) unlink((*a)->l.f.local);
+ fs_give((void **) &(*a)->l.f.local);
+ }
+
+ if((*a)->l.f.description)
+ fs_give((void **) &(*a)->l.f.description);
+ }
+ else if((*a)->body){
+ pine_free_body(&(*a)->l.b.body);
+ }
+
+ peFreeAttach(&(*a)->next);
+ fs_give((void **) a);
+ }
+}
+
+
+char *
+peFileAttachID(char *f, char *t, char *st, char *r, char *d, int dl)
+{
+ COMPATT_S *ap = peNewAttach(), *p;
+ long hval;
+
+ ap->file = TRUE;
+ ap->l.f.local = cpystr(f);
+ ap->l.f.size = name_file_size(f);
+
+ hval = line_hash(f);
+ while(1) /* collisions? */
+ if(peGetAttachID(ap->id = cpystr(long2string(hval)))){
+ fs_give((void **) &ap->id);
+ hval += 1;
+ }
+ else
+ break;
+
+ ap->l.f.remote = cpystr(r ? r : "");
+ ap->l.f.type = cpystr(t ? t : "Text");
+ ap->l.f.subtype = cpystr(st ? st : "Plain");
+
+ ap->l.f.description = fs_get(dl + 1);
+ snprintf(ap->l.f.description, dl + 1, "%s", d);
+
+ if((p = peCompAttach) != NULL){
+ do
+ if(!p->next){
+ p->next = ap;
+ break;
+ }
+ while((p = p->next) != NULL);
+ }
+ else
+ peCompAttach = ap;
+
+ return(ap->id);
+}
+
+
+char *
+peBodyAttachID(BODY *b)
+{
+ COMPATT_S *ap = peNewAttach(), *p;
+ unsigned long hval;
+
+ ap->body = TRUE;
+ ap->l.b.body = copy_body(NULL, b);
+
+ hval = b->id ? line_hash(b->id) : time(0);
+ while(1) /* collisions? */
+ if(peGetAttachID(ap->id = cpystr(ulong2string(hval)))){
+ fs_give((void **) &ap->id);
+ hval += 1;
+ }
+ else
+ break;
+
+ /* move contents pointer to copy */
+ peBodyMoveContents(b, ap->l.b.body);
+
+ if((p = peCompAttach) != NULL){
+ do
+ if(!p->next){
+ p->next = ap;
+ break;
+ }
+ while((p = p->next) != NULL);
+ }
+ else
+ peCompAttach = ap;
+
+ return(ap->id);
+}
+
+
+void
+peBodyMoveContents(BODY *bs, BODY *bd)
+{
+ if(bs && bd){
+ if(bs->type == TYPEMULTIPART && bd->type == TYPEMULTIPART){
+ PART *ps = bs->nested.part,
+ *pd = bd->nested.part;
+ do /* for each part */
+ peBodyMoveContents(&ps->body, &pd->body);
+ while ((ps = ps->next) && (pd = pd->next)); /* until done */
+ }
+ else if(bs->contents.text.data){
+ bd->contents.text.data = bs->contents.text.data;
+ bs->contents.text.data = NULL;
+ }
+ }
+}
+
+
+
+COMPATT_S *
+peGetAttachID(char *h)
+{
+ COMPATT_S *p;
+
+ for(p = peCompAttach; p; p = p->next)
+ if(!strcmp(p->id, h))
+ return(p);
+
+ return(NULL);
+}
+
+
+int
+peClearAttachID(char *h)
+{
+ COMPATT_S *pp, *pt = NULL;
+
+ for(pp = peCompAttach; pp; pp = pp->next){
+ if(!strcmp(pp->id, h)){
+ if(pt)
+ pt->next = pp->next;
+ else
+ peCompAttach = pp->next;
+
+ pp->next = NULL;
+ peFreeAttach(&pp);
+ return(TRUE);
+ }
+
+ pt = pp;
+ }
+
+ return(FALSE);
+}
+
+
+/*
+ * peDoPost - handle preparing header and body text for posting, prepare
+ * for any Fcc, then call the mailer to send it out
+ */
+int
+peDoPost(METAENV *metaenv, BODY *body, char *fcc, CONTEXT_S **fcc_cntxtp, char *errp)
+{
+ int rv = TCL_OK, recipients;
+ char *s;
+
+ if(commence_fcc(fcc, fcc_cntxtp, TRUE)){
+
+ ps_global->c_client_error[0] = ps_global->last_error[0] = '\0';
+ pePrepareForAuthException();
+
+ if((recipients = (metaenv->env->to || metaenv->env->cc || metaenv->env->bcc))
+ && call_mailer(metaenv, body, NULL, 0, NULL, NULL) < 0){
+ if(s = peAuthException()){
+ strcpy(errp, s);
+ }
+ else if(ps_global->last_error[0]){
+ sprintf(errp, "Send Error: %.*s", 64, ps_global->last_error);
+ }
+ else if(ps_global->c_client_error[0]){
+ sprintf(errp, "Send Error: %.*s", 64, ps_global->c_client_error);
+ }
+ else
+ strcpy(errp, "Sending Failure");
+
+ rv = TCL_ERROR;
+ dprint((1, "call_mailer failed!"));
+ }
+ else if(fcc && fcc_cntxtp && !wrapup_fcc(fcc, *fcc_cntxtp, recipients ? NULL : metaenv, body)){
+ strcpy(errp, "Fcc Failed!. No message saved.");
+ rv = TCL_ERROR;
+ dprint((1, "explicit fcc write failed!"));
+ }
+ else{
+ PINEFIELD *pf;
+ REPLY_S *reply = NULL;
+
+ /* success, now look for x-reply-uid to flip answered flag for? */
+
+ for(pf = metaenv->local; pf && pf->name; pf = pf->next)
+ if(!strucmp(pf->name, "x-reply-uid")){
+ if(pf->textbuf){
+ if((reply = (REPLY_S *) build_reply_uid(pf->textbuf)) != NULL){
+
+ update_answered_flags(reply);
+
+ if(reply->mailbox)
+ fs_give((void **) &reply->mailbox);
+
+ if(reply->prefix)
+ fs_give((void **) &reply->prefix);
+
+ if(reply->data.uid.msgs)
+ fs_give((void **) &reply->data.uid.msgs);
+
+ fs_give((void **) &reply);
+ }
+ }
+
+ break;
+ }
+ }
+ }
+ else{
+ dprint((1,"can't open fcc, cont"));
+
+ strcpy(errp, "Can't open Fcc");
+ rv = TCL_ERROR;
+ }
+
+ return(rv);
+}
+
+
+
+/*
+ * pePostponeCmd - export various bits of alpine state
+ */
+int
+PEPostponeCmd(ClientData clientData, Tcl_Interp *interp, int objc, Tcl_Obj *CONST objv[])
+{
+ char *err = "PEPostpone: unknown request";
+ imapuid_t uid;
+
+ dprint((2, "PEPostponeCmd"));
+
+ if(objc == 1){
+ Tcl_WrongNumArgs(interp, 1, objv, "cmd ?args?");
+ }
+ else if(!ps_global){
+ Tcl_SetResult(interp, "pePostpone: no config present", TCL_STATIC);
+ return(TCL_ERROR);
+ }
+ else{
+ char *s1 = Tcl_GetStringFromObj(objv[1], NULL);
+
+ if(s1){
+ if(!strcmp(s1, "extract")){
+ if(Tcl_GetLongFromObj(interp, objv[2], &uid) == TCL_OK){
+ Tcl_Obj *objHdr = NULL, *objBod = NULL, *objAttach = NULL, *objOpts = NULL;
+ MAILSTREAM *stream;
+ BODY *b;
+ ENVELOPE *env = NULL;
+ PINEFIELD *custom = NULL, *cp;
+ REPLY_S *reply = NULL;
+ ACTION_S *role = NULL;
+ STORE_S *so;
+ long n;
+ int rv = TCL_OK;
+ char *fcc = NULL, *lcc = NULL;
+ unsigned flags = REDRAFT_DEL | REDRAFT_PPND;
+
+ if(objc > 3){ /* optional flags */
+ int i, nFlags;
+ Tcl_Obj **objFlags;
+ char *flagstr;
+
+ Tcl_ListObjGetElements(interp, objv[3], &nFlags, &objFlags);
+ for(i = 0; i < nFlags; i++){
+ if((flagstr = Tcl_GetStringFromObj(objFlags[i], NULL)) == NULL){
+ rv = TCL_ERROR;
+ }
+
+ if(!strucmp(flagstr, "html"))
+ flags |= REDRAFT_HTML;
+ }
+ }
+ /* BUG: should probably complain if argc > 4 */
+
+ if(rv == TCL_OK
+ && postponed_stream(&stream, ps_global->VAR_POSTPONED_FOLDER, "Postponed", 0)
+ && stream){
+ if((so = so_get(CharStar, NULL, EDIT_ACCESS)) != NULL){
+ if((n = mail_msgno(stream, uid)) > 0L){
+ if(redraft_work(&stream, n, &env, &b, &fcc, &lcc, &reply,
+ NULL, &custom, &role, /* should role be NULL? */
+ flags, so)){
+ char *charset = NULL;
+
+ /* prepare to package up for caller */
+ objHdr = Tcl_NewListObj(0, NULL);
+
+ /* determine body part's charset */
+ if((charset = parameter_val(b->parameter,"charset")) != NULL){
+ objOpts = Tcl_NewListObj(0, NULL);
+ peAppListF(interp, objOpts, "%s%s", "charset", charset);
+ fs_give((void **) &charset);
+ }
+
+ /* body part's MIME subtype */
+ if(b->subtype && strucmp(b->subtype,"plain")){
+ if(!objOpts)
+ objOpts = Tcl_NewListObj(0, NULL);
+
+ peAppListF(interp, objOpts, "%s%s", "subtype", b->subtype);
+ }
+
+ peAppListF(interp, objHdr, "%s%a", "from",
+ role && role->from ? role->from : env->from);
+ peAppListF(interp, objHdr, "%s%a", "to", env->to);
+ peAppListF(interp, objHdr, "%s%a", "cc", env->cc);
+ peAppListF(interp, objHdr, "%s%a", "bcc", env->bcc);
+ peAppListF(interp, objHdr, "%s%s", "in-reply-to", env->in_reply_to);
+ peAppListF(interp, objHdr, "%s%s", "subject",
+ rfc1522_decode_to_utf8((unsigned char *) tmp_20k_buf,
+ SIZEOF_20KBUF, env->subject));
+
+ if(fcc)
+ peFccAppend(interp, objHdr, fcc, -1);
+
+ for(cp = custom; cp && cp->name; cp = cp->next)
+ switch(cp->type){
+ case Address :
+ strncpy(tmp_20k_buf, cp->name, SIZEOF_20KBUF);
+ tmp_20k_buf[SIZEOF_20KBUF-1] = '\0';
+ peAppListF(interp, objHdr, "%s%a",
+ lcase((unsigned char *) tmp_20k_buf), *cp->addr);
+ break;
+
+ case Attachment :
+ break;
+
+ case Fcc :
+ case Subject :
+ break; /* ignored */
+
+ default :
+ strncpy(tmp_20k_buf, cp->name, SIZEOF_20KBUF);
+ tmp_20k_buf[SIZEOF_20KBUF-1] = '\0';
+ peAppListF(interp, objHdr, "%s%s",
+ lcase((unsigned char *) tmp_20k_buf), cp->textbuf ? cp->textbuf : cp->val);
+ break;
+ }
+
+ if(reply){
+ /* blat x-Reply-UID: for possible use? */
+ if(reply->uid){
+ char uidbuf[MAILTMPLEN], *p;
+ long i;
+
+ for(i = 0L, p = tmp_20k_buf; reply->data.uid.msgs[i]; i++){
+ if(i)
+ sstrncpy(&p, ",", SIZEOF_20KBUF-(p-tmp_20k_buf));
+
+ sstrncpy(&p,ulong2string(reply->data.uid.msgs[i]), SIZEOF_20KBUF-(p-tmp_20k_buf));
+ }
+
+ tmp_20k_buf[SIZEOF_20KBUF-1] = '\0';
+
+ snprintf(uidbuf, sizeof(uidbuf), "(%s%s%s)(%ld %lu %s)%s",
+ reply->prefix ? int2string(strlen(reply->prefix))
+ : (reply->forwarded) ? "" : "0 ",
+ reply->prefix ? " " : "",
+ reply->prefix ? reply->prefix : "",
+ i, reply->data.uid.validity,
+ tmp_20k_buf, reply->mailbox);
+
+ peAppListF(interp, objHdr, "%s%s", "x-reply-uid", uidbuf);
+ }
+
+ fs_give((void **) &reply->mailbox);
+ fs_give((void **) &reply->prefix);
+ fs_give((void **) &reply->data.uid.msgs);
+ fs_give((void **) &reply);
+ }
+
+ objBod = Tcl_NewListObj(0, NULL);
+ peSoStrToList(interp, objBod, so);
+
+ Tcl_ListObjAppendElement(interp, Tcl_GetObjResult(interp), objHdr);
+ Tcl_ListObjAppendElement(interp, Tcl_GetObjResult(interp), objBod);
+
+ objAttach = peMsgAttachCollector(interp, b);
+
+ Tcl_ListObjAppendElement(interp, Tcl_GetObjResult(interp), objAttach);
+
+ if(objOpts){
+ Tcl_ListObjAppendElement(interp, Tcl_GetObjResult(interp),objOpts);
+ }
+
+ /* clean up */
+ if(fcc)
+ fs_give((void **) &fcc);
+
+ if(lcc)
+ fs_give((void **) &lcc);
+
+ mail_free_envelope(&env);
+ pine_free_body(&b);
+ free_action(&role);
+
+ /* if Drafts got whacked, open INBOX */
+ if(!ps_global->mail_stream)
+ do_broach_folder(ps_global->inbox_name,
+ ps_global->context_list,
+ NULL, DB_INBOXWOCNTXT);
+
+ return(TCL_OK);
+ }
+
+ so_give(&so);
+ }
+ else
+ err = "Unknown UID";
+ }
+ else
+ err = "No internal storage";
+
+ /* redraft_work cleaned up the "stream" */
+ }
+ else
+ err = "No Postponed stream";
+ }
+ else
+ err = "Malformed extract request";
+ }
+ else if(objc == 2){
+ if(!strcmp(s1, "any")){
+ MAILSTREAM *stream;
+
+ if(postponed_stream(&stream, ps_global->VAR_POSTPONED_FOLDER, "Postponed", 0) && stream){
+ Tcl_SetResult(interp, "1", TCL_STATIC);
+
+ if(stream != ps_global->mail_stream)
+ pine_mail_close(stream);
+ }
+ else
+ Tcl_SetResult(interp, "0", TCL_STATIC);
+
+ return(TCL_OK);
+ }
+ else if(!strcmp(s1, "count")){
+ MAILSTREAM *stream;
+
+ if(postponed_stream(&stream, ps_global->VAR_POSTPONED_FOLDER, "Postponed", 0) && stream){
+ Tcl_SetResult(interp, long2string(stream->nmsgs), TCL_STATIC);
+
+ if(stream != ps_global->mail_stream)
+ pine_mail_close(stream);
+ }
+ else
+ Tcl_SetResult(interp, "-1", TCL_STATIC);
+
+ return(TCL_OK);
+ }
+ else if(!strcmp(s1, "list")){
+ MAILSTREAM *stream;
+ ENVELOPE *env;
+ Tcl_Obj *objEnv = NULL, *objEnvList;
+ long n;
+
+ if(postponed_stream(&stream, ps_global->VAR_POSTPONED_FOLDER, "Postponed", 0) && stream){
+ if(!stream->nmsgs){
+ (void) redraft_cleanup(&stream, FALSE, REDRAFT_PPND);
+ Tcl_SetResult(interp, "", TCL_STATIC);
+ return(TCL_OK);
+ }
+
+ objEnvList = Tcl_NewListObj(0, NULL);
+
+ for(n = 1; n <= stream->nmsgs; n++){
+ if((env = pine_mail_fetchstructure(stream, n, NULL)) != NULL){
+ objEnv = Tcl_NewListObj(0, NULL);
+
+ peAppListF(interp, objEnv, "%s%s", "uid",
+ ulong2string(mail_uid(stream, n)));
+
+ peAppListF(interp, objEnv, "%s%a", "to", env->to);
+
+ date_str((char *)env->date, iSDate, 1, tmp_20k_buf, SIZEOF_20KBUF, 0);
+
+ peAppListF(interp, objEnv, "%s%s", "date", tmp_20k_buf);
+
+ peAppListF(interp, objEnv, "%s%s", "subj",
+ rfc1522_decode_to_utf8((unsigned char *) tmp_20k_buf,
+ SIZEOF_20KBUF, env->subject));
+
+ Tcl_ListObjAppendElement(interp, objEnvList, objEnv);
+ }
+ }
+
+ Tcl_ListObjAppendElement(interp, Tcl_GetObjResult(interp), objEnvList);
+
+ Tcl_ListObjAppendElement(interp, Tcl_GetObjResult(interp),
+ Tcl_NewStringObj("utf-8", -1));
+
+ if(stream != ps_global->mail_stream)
+ pine_mail_close(stream);
+ }
+
+ return(TCL_OK);
+ }
+ }
+ else if(objc == 3){
+ if(!strcmp(s1, "append")){
+ int rv;
+
+ if((rv = peMsgCollector(interp, objc - 2, (Tcl_Obj **) &objv[2], peDoPostpone, PMC_NONE)) == TCL_OK)
+ Tcl_SetResult(interp, ulong2string(get_last_append_uid()), TCL_VOLATILE);
+
+ return(rv);
+ }
+ else if(!strcmp(s1, "draft")){
+ int rv;
+
+ if((rv = peMsgCollector(interp, objc - 2, (Tcl_Obj **) &objv[2], peDoPostpone, PMC_PRSRV_ATT)) == TCL_OK)
+ Tcl_SetResult(interp, ulong2string(get_last_append_uid()), TCL_VOLATILE);
+
+ return(rv);
+ }
+ else if(!strcmp(s1, "delete")){
+ if(Tcl_GetLongFromObj(interp, objv[2], &uid) == TCL_OK){
+ MAILSTREAM *stream;
+ long rawno;
+
+ if(postponed_stream(&stream, ps_global->VAR_POSTPONED_FOLDER, "Postponed", 0) && stream){
+ if((rawno = mail_msgno(stream, uid)) > 0L){
+ mail_flag(stream, long2string(rawno), "\\DELETED", ST_SET);
+ ps_global->expunge_in_progress = 1;
+ mail_expunge(stream);
+ ps_global->expunge_in_progress = 0;
+ if(stream != ps_global->mail_stream)
+ pine_mail_actually_close(stream);
+
+ return(TCL_OK);
+ }
+ else
+ err = "PEPostpone delete: UID no longer exists";
+ }
+ else
+ err = "PEPostpone delete: No Postponed stream";
+ }
+ else
+ err = "PEPostpone delete: No uid provided";
+ }
+ }
+ }
+ }
+
+ Tcl_SetResult(interp, err, TCL_STATIC);
+ return(TCL_ERROR);
+}
+
+
+/*
+ * peDoPostpone - handle postponing after message collection
+ */
+int
+peDoPostpone(METAENV *metaenv, BODY *body, char *fcc, CONTEXT_S **fcc_cntxtp, char *errp)
+{
+ PINEFIELD *pf;
+ int rv;
+ appenduid_t *au;
+
+ /*
+ * resolve fcc and store it in fcc custom header field data
+ */
+ if(fcc && *fcc && fcc_cntxtp && *fcc_cntxtp)
+ for(pf = metaenv->local; pf && pf->name; pf = pf->next)
+ if(!strucmp("fcc", pf->name)){
+ char *name, *rs, path_in_context[MAILTMPLEN];
+
+ if(pf->textbuf) /* free old value */
+ fs_give((void **) &pf->textbuf);
+
+ /* replace nickname with full name */
+ if(!(name = folder_is_nick(fcc, FOLDERS(*fcc_cntxtp), FN_NONE)))
+ name = fcc;
+
+ if(context_isambig(name) && !(((*fcc_cntxtp)->use) & CNTXT_SAVEDFLT)){
+ context_apply(path_in_context, *fcc_cntxtp, name, sizeof(path_in_context));
+ rs = IS_REMOTE(path_in_context) ? path_in_context : NULL;
+ }
+ else
+ rs = cpystr(name);
+
+ if(rs){
+ pf->textbuf = cpystr(rs);
+ pf->text = &pf->textbuf;
+ }
+
+ break;
+ }
+
+ au = mail_parameters(NIL, GET_APPENDUID, NIL);
+ mail_parameters(NIL, SET_APPENDUID, (void *) appenduid_cb);
+
+ rv = write_postponed(metaenv, body);
+
+ mail_parameters(NIL, SET_APPENDUID, (void *) au);
+
+ return((rv < 0) ? TCL_ERROR : TCL_OK);
+}
+
+
+/*
+ * peMsgCollector - Collect message parts and call specified handler
+ */
+int
+peMsgCollector(Tcl_Interp *interp,
+ int objc,
+ Tcl_Obj **objv,
+ int (*postfunc)(METAENV *, BODY *, char *, CONTEXT_S **, char *),
+ long flags)
+{
+ Tcl_Obj **objMsg, **objField, **objBody;
+ int i, j, vl, nMsg, nField, nBody;
+ char *field, *value, *err = NULL;
+ MSG_COL_S md;
+ PINEFIELD *pf;
+ STRLIST_S *tp, *lp;
+ static char *fakedomain = "@";
+
+ memset(&md, 0, sizeof(MSG_COL_S));
+ md.postop_fcc_no_attach = -1;
+ md.postfunc = postfunc;
+ md.qualified_addrs = ((flags & PMC_FORCE_QUAL) == PMC_FORCE_QUAL);
+
+ if(objc != 1){
+ Tcl_SetResult(interp, "Malformed message data", TCL_STATIC);
+ return(TCL_ERROR);
+ }
+ else if(!ps_global){
+ Tcl_SetResult(interp, "No open folder", TCL_STATIC);
+ return(TCL_ERROR);
+ }
+
+ md.outgoing = mail_newenvelope();
+
+ md.metaenv = pine_new_env(md.outgoing, NULL, NULL, md.custom = peCustomHdrs());
+
+ Tcl_ListObjGetElements(interp, objv[0], &nMsg, &objMsg);
+ for(i = 0; i < nMsg; i++){
+ if(Tcl_ListObjGetElements(interp, objMsg[i], &nField, &objField) != TCL_OK){
+ err = ""; /* interp's result object has error message */
+ return(peMsgCollected(interp, &md, err, flags));
+ }
+
+ if(nField && (field = Tcl_GetStringFromObj(objField[0], NULL))){
+ if(!strcmp(field, "body")){
+ if(md.msgtext){
+ err = "Too many bodies";
+ return(peMsgCollected(interp, &md, err, flags));
+ }
+ else if((md.msgtext = so_get(CharStar, NULL, EDIT_ACCESS)) != NULL){
+ /* mark storage object as user edited */
+ (void) so_attr(md.msgtext, "edited", "1");
+
+ Tcl_ListObjGetElements(interp, objField[1], &nBody, &objBody);
+ for(j = 0; j < nBody; j++){
+ value = Tcl_GetStringFromObj(objBody[j], &vl);
+ if(value){
+ so_nputs(md.msgtext, value, vl);
+ so_puts(md.msgtext, "\n");
+ }
+ else{
+ err = "Value read failure";
+ return(peMsgCollected(interp, &md, err, flags));
+ }
+ }
+ }
+ else {
+ err = "Can't acquire body storage";
+ return(peMsgCollected(interp, &md, err, flags));
+ }
+ }
+ else if(!strucmp(field, "attach")){
+ char *id;
+ COMPATT_S *a;
+
+ if(nField == 2
+ && (id = Tcl_GetStringFromObj(objField[1], NULL))
+ && (a = peGetAttachID(id))){
+ tp = new_strlist(id);
+ if((lp = md.attach) != NULL){
+ do
+ if(!lp->next){
+ lp->next = tp;
+ break;
+ }
+ while((lp = lp->next) != NULL);
+ }
+ else
+ md.attach = tp;
+ }
+ else{
+ strcpy(err = tmp_20k_buf, "Unknown attachment ID");
+ return(peMsgCollected(interp, &md, err, flags));
+ }
+ }
+ else if(!strucmp(field, "fcc")){
+ Tcl_Obj **objFcc;
+ int nFcc;
+
+ if(Tcl_ListObjGetElements(interp, objField[1], &nFcc, &objFcc) == TCL_OK
+ && nFcc == 2
+ && Tcl_GetIntFromObj(interp, objFcc[0], &md.fcc_colid) == TCL_OK
+ && (value = Tcl_GetStringFromObj(objFcc[1], NULL))){
+ if(md.fcc)
+ fs_give((void **) &md.fcc);
+
+ md.fcc = cpystr(value);
+ }
+ else {
+ strcpy(err = tmp_20k_buf, "Unrecognized Fcc specification");
+ return(peMsgCollected(interp, &md, err, flags));
+ }
+ }
+ else if(!strucmp(field, "postoption")){
+ Tcl_Obj **objPO;
+ int nPO, ival;
+
+ value = NULL;
+ if(Tcl_ListObjGetElements(interp, objField[1], &nPO, &objPO) == TCL_OK
+ && nPO == 2
+ && (value = Tcl_GetStringFromObj(objPO[0], NULL))){
+ if(!strucmp(value,"fcc-without-attachments")){
+ if(Tcl_GetIntFromObj(interp, objPO[1], &ival) == TCL_OK){
+ md.postop_fcc_no_attach = (ival != 0);
+ }
+ else{
+ sprintf(err = tmp_20k_buf, "Malformed Post Option: fcc-without-attachments");
+ return(peMsgCollected(interp, &md, err, flags));
+ }
+ }
+ else if(!strucmp(value, "charset")){
+ if((value = Tcl_GetStringFromObj(objPO[1], NULL)) != NULL){
+ char *p;
+
+ for(p = value; ; p++){ /* sanity check */
+ if(!*p){
+ md.charset = cpystr(value);
+ break;
+ }
+
+ if(isspace((unsigned char ) *p)
+ || !isprint((unsigned char) *p))
+ break;
+
+ if(p - value > 255)
+ break;
+ }
+ }
+ else{
+ err = "Post option read failure";
+ return(peMsgCollected(interp, &md, err, flags));
+ }
+ }
+ else if(!strucmp(value, "flowed")){
+ if(F_OFF(F_QUELL_FLOWED_TEXT,ps_global)){
+ if((value = Tcl_GetStringFromObj(objPO[1], NULL)) != NULL){
+ if(!strucmp(value, "yes"))
+ md.flowed = 1;
+ }
+ else{
+ err = "Post option read failure";
+ return(peMsgCollected(interp, &md, err, flags));
+ }
+ }
+ }
+ else if(!strucmp(value, "subtype")){
+ if((value = Tcl_GetStringFromObj(objPO[1], NULL)) != NULL){
+ if(!strucmp(value, "html"))
+ md.html = 1;
+ }
+ else{
+ err = "Post option read failure";
+ return(peMsgCollected(interp, &md, err, flags));
+ }
+ }
+ else if(!strucmp(value, "priority")){
+ if((value = Tcl_GetStringFromObj(objPO[1], NULL)) != NULL){
+ char *priority = NULL;
+
+ if(!strucmp(value, "highest"))
+ priority = "Highest";
+ else if(!strucmp(value, "high"))
+ priority = "High";
+ else if(!strucmp(value, "normal"))
+ priority = "Normal";
+ else if(!strucmp(value, "low"))
+ priority = "Low";
+ else if(!strucmp(value, "lowest"))
+ priority = "Lowest";
+
+ if(priority){
+ if(pf = set_priority_header(md.metaenv, priority))
+ pf->text = &pf->textbuf;
+ }
+ }
+ else{
+ err = "Post option read failure";
+ return(peMsgCollected(interp, &md, err, flags));
+ }
+ }
+ else{
+ sprintf(err = tmp_20k_buf, "Unknown Post Option: %s", value);
+ return(peMsgCollected(interp, &md, err, flags));
+ }
+ }
+ else{
+ sprintf(err = tmp_20k_buf, "Malformed Post Option");
+ return(peMsgCollected(interp, &md, err, flags));
+ }
+ }
+ else {
+ if(nField != 2){
+ sprintf(err = tmp_20k_buf, "Malformed header (%s)", field);
+ return(peMsgCollected(interp, &md, err, flags));
+ }
+
+ if((value = Tcl_GetStringFromObj(objField[1], &vl)) != NULL){
+ ADDRESS **addrp = NULL;
+ char **valp = NULL, *valcpy;
+
+ if(!strucmp(field, "from")){
+ addrp = &md.outgoing->from;
+ }
+ else if(!strucmp(field, "reply-to")){
+ addrp = &md.outgoing->reply_to;
+ }
+ else if(!strucmp(field, "to")){
+ addrp = &md.outgoing->to;
+ }
+ else if(!strucmp(field, "cc")){
+ addrp = &md.outgoing->cc;
+ }
+ else if(!strucmp(field, "bcc")){
+ addrp = &md.outgoing->bcc;
+ }
+ else if(!strucmp(field, "subject")){
+ valp = &md.outgoing->subject;
+ }
+ else if(!strucmp(field, "in-reply-to")){
+ valp = &md.outgoing->in_reply_to;
+ }
+ else if(!strucmp(field, "newsgroups")){
+ valp = &md.outgoing->newsgroups;
+ }
+ else if(!strucmp(field, "followup-to")){
+ valp = &md.outgoing->followup_to;
+ }
+ else if(!strucmp(field, "references")){
+ valp = &md.outgoing->references;
+ }
+ else if(!strucmp(field, "x-reply-uid")){
+ for(pf = md.metaenv->local; pf && pf->name; pf = pf->next)
+ if(!strucmp(pf->name, "x-reply-uid")){
+ valp = pf->text = &pf->textbuf;
+ break;
+ }
+ }
+ else if(!strucmp(field, "x-auth-received")){
+ for(pf = md.metaenv->local; pf && pf->name; pf = pf->next)
+ if(!strucmp(pf->name, "x-auth-received")){
+ valp = pf->text = &pf->textbuf;
+ break;
+ }
+ }
+ else{
+ for(pf = md.metaenv->custom; pf && pf->name; pf = pf->next)
+ if(!strucmp(field, pf->name)){
+ if(pf->type == Address)
+ addrp = pf->addr;
+ else if(vl)
+ valp = &pf->textbuf;
+ else if(pf->textbuf)
+ fs_give((void **) &pf->textbuf);
+
+ break;
+ }
+
+ if(!pf)
+ dprint((2, "\nPOST: unrecognized field - %s\n", field));
+ }
+
+ if(valp){
+ if(*valp)
+ fs_give((void **) valp);
+
+ sprintf(*valp = fs_get((vl + 1) * sizeof(char)), "%.*s", vl, value);
+ }
+
+ if(addrp){
+ sprintf(valcpy = fs_get((vl + 1) * sizeof(char)), "%.*s", vl, value);
+
+ for(; *addrp; addrp = &(*addrp)->next)
+ ;
+
+ rfc822_parse_adrlist(addrp, valcpy,
+ (flags & PMC_FORCE_QUAL)
+ ? fakedomain : ps_global->maildomain);
+ fs_give((void **) &valcpy);
+ }
+ }
+ else{
+ err = "Value read failure";
+ return(peMsgCollected(interp, &md, err, flags));
+ }
+ }
+ }
+ }
+
+ return(peMsgCollected(interp, &md, err, flags));
+}
+
+
+/*
+ * peMsgCollected - Dispatch collected message data and cleanup
+ */
+int
+peMsgCollected(Tcl_Interp *interp, MSG_COL_S *md, char *err, long flags)
+{
+ int rv = TCL_OK, non_ascii = FALSE;
+ unsigned char c;
+ BODY *body = NULL, *tbp = NULL;
+ char errbuf[WP_MAX_POST_ERROR + 1], *charset;
+ STRLIST_S *lp;
+
+ if(err){
+ if(md->msgtext)
+ so_give(&md->msgtext);
+
+ rv = TCL_ERROR;
+ }
+ else if(md->qualified_addrs && check_addresses(md->metaenv) == CA_BAD){
+ sprintf(err = tmp_20k_buf, "Address must be fully qualified.");
+ rv = TCL_ERROR;
+ }
+ else{
+ /* sniff body for possible multipart wrapping to protect encoding */
+ so_seek(md->msgtext, 0L, 0);
+
+ while(so_readc(&c, md->msgtext))
+ if(!c || c & 0x80){
+ non_ascii = TRUE;
+ break;
+ }
+
+ if(!md->outgoing->from)
+ md->outgoing->from = generate_from();
+
+ rfc822_date(tmp_20k_buf);
+ md->outgoing->date = (unsigned char *) cpystr(tmp_20k_buf);
+ md->outgoing->return_path = rfc822_cpy_adr(md->outgoing->from);
+ md->outgoing->message_id = generate_message_id();
+
+ body = mail_newbody();
+
+ /* wire any attachments to body */
+ if(md->attach || (non_ascii && F_OFF(F_COMPOSE_ALWAYS_DOWNGRADE, ps_global))){
+ PART **np;
+ PARAMETER **pp;
+ COMPATT_S *a;
+
+ /* setup slot for message text */
+ body->type = TYPEMULTIPART;
+ body->nested.part = mail_newbody_part();
+ tbp = &body->nested.part->body;
+
+ /* link in attachments */
+ for(lp = md->attach, np = &body->nested.part->next; lp; lp = lp->next, np = &(*np)->next){
+ if(!(a = peGetAttachID(lp->name))){
+ err = "Unknown Attachment ID";
+ rv = TCL_ERROR;
+ break;
+ }
+
+ *np = mail_newbody_part();
+
+ if(a->file){
+ (*np)->body.id = generate_message_id();
+ (*np)->body.description = cpystr(a->l.f.description);
+
+ /* set name parameter */
+ for(pp = &(*np)->body.parameter; *pp; )
+ if(!struncmp((*pp)->attribute, "name", 4)
+ && (!*((*pp)->attribute + 4)
+ || *((*pp)->attribute + 4) == '*')){
+ PARAMETER *free_me = *pp;
+ *pp = (*pp)->next;
+ free_me->next = NULL;
+ mail_free_body_parameter(&free_me);
+ }
+ else
+ pp = &(*pp)->next;
+
+ *pp = NULL;
+ set_parameter(pp, "name", a->l.f.remote);
+
+ /* Then set the Content-Disposition ala RFC1806 */
+ if(!(*np)->body.disposition.type){
+ (*np)->body.disposition.type = cpystr("attachment");
+ for(pp = &(*np)->body.disposition.parameter; *pp; )
+ if(!struncmp((*pp)->attribute, "filename", 4)
+ && (!*((*pp)->attribute + 4)
+ || *((*pp)->attribute + 4) == '*')){
+ PARAMETER *free_me = *pp;
+ *pp = (*pp)->next;
+ free_me->next = NULL;
+ mail_free_body_parameter(&free_me);
+ }
+ else
+ pp = &(*pp)->next;
+
+ *pp = NULL;
+ set_parameter(pp, "filename", a->l.f.remote);
+ }
+
+ if(((*np)->body.contents.text.data = (void *) so_get(FileStar, a->l.f.local, READ_ACCESS)) != NULL){
+ (*np)->body.type = mt_translate_type(a->l.f.type);
+ (*np)->body.subtype = cpystr(a->l.f.subtype);
+ (*np)->body.encoding = ENCBINARY;
+ (*np)->body.size.bytes = name_file_size(a->l.f.local);
+
+ if((*np)->body.type == TYPEOTHER
+ && !set_mime_type_by_extension(&(*np)->body, a->l.f.local))
+ set_mime_type_by_grope(&(*np)->body);
+
+ so_release((STORE_S *)(*np)->body.contents.text.data);
+ }
+ else{
+ /* unravel here */
+ err = "Can't open uploaded attachment";
+ rv = TCL_ERROR;
+ break;
+ }
+ }
+ else if(a->body){
+ BODY *newbody = copy_body(NULL, a->l.b.body);
+ (*np)->body = *newbody;
+ fs_give((void **) &newbody);
+ peBodyMoveContents(a->l.b.body, &(*np)->body);
+ }
+ else{
+ err = "BOTCH: Unknown attachment type";
+ rv = TCL_ERROR;
+ break;
+ }
+ }
+ }
+ else
+ tbp = body;
+
+ /* assign MIME parameters to text body part */
+ tbp->type = TYPETEXT;
+ if(md->html) tbp->subtype = cpystr("HTML");
+
+ tbp->contents.text.data = (void *) md->msgtext;
+ tbp->encoding = ENCOTHER;
+
+ /* set any text flowed param */
+ if(md->flowed)
+ peMsgSetParm(&tbp->parameter, "format", "flowed");
+
+ if(rv == TCL_OK){
+ CONTEXT_S *fcc_cntxt = ps_global->context_list;
+
+ while(md->fcc_colid--)
+ if(fcc_cntxt->next)
+ fcc_cntxt = fcc_cntxt->next;
+
+ if(md->postop_fcc_no_attach >= 0){
+ int oldval = F_ON(F_NO_FCC_ATTACH, ps_global);
+ F_SET(F_NO_FCC_ATTACH, ps_global, md->postop_fcc_no_attach);
+ md->postop_fcc_no_attach = oldval;
+ }
+
+ pine_encode_body(body);
+
+ rv = (*md->postfunc)(md->metaenv, body, md->fcc, &fcc_cntxt, errbuf);
+
+ if(md->postop_fcc_no_attach >= 0){
+ F_SET(F_NO_FCC_ATTACH, ps_global, md->postop_fcc_no_attach);
+ }
+
+ if(rv == TCL_OK){
+ if((flags & PMC_PRSRV_ATT) == 0)
+ peFreeAttach(&peCompAttach);
+ }
+ else{
+ /* maintain pointers to attachments */
+ (void) peMsgAttachCollector(NULL, body);
+ err = errbuf;
+ }
+ }
+
+ pine_free_body(&body);
+ }
+
+ if(md->charset)
+ fs_give((void **) &md->charset);
+
+ free_strlist(&md->attach);
+
+ pine_free_env(&md->metaenv);
+
+ if(md->custom)
+ free_customs(md->custom);
+
+ mail_free_envelope(&md->outgoing);
+
+ if(err && *err)
+ Tcl_SetResult(interp, err, TCL_VOLATILE);
+
+ return(rv);
+}
+
+
+void
+peMsgSetParm(PARAMETER **pp, char *pa, char *pv)
+{
+ for(; *pp; pp = &(*pp)->next)
+ if(!strucmp(pa, (*pp)->attribute)){
+ if((*pp)->value)
+ fs_give((void **) &(*pp)->value);
+
+ break;
+ }
+
+ if(!*pp){
+ *pp = mail_newbody_parameter();
+ (*pp)->attribute = cpystr(pa);
+ }
+
+ (*pp)->value = cpystr(pv);
+}
+
+
+Tcl_Obj *
+peMsgAttachCollector(Tcl_Interp *interp, BODY *b)
+{
+ char *id, *name = NULL;
+ PART *part;
+ Tcl_Obj *aListObj = NULL, *aObj = NULL;
+
+ peFreeAttach(&peCompAttach);
+
+ if(interp)
+ aListObj = Tcl_NewListObj(0, NULL);
+
+ if(b->type == TYPEMULTIPART){
+ /*
+ * Walk first level, clipping branches and adding them
+ * to the attachment list...
+ */
+ for(part = b->nested.part->next; part; part = part->next) {
+ id = peBodyAttachID(&part->body);
+ aObj = Tcl_NewListObj(0, NULL);
+
+ if(interp){
+ Tcl_ListObjAppendElement(interp, aObj, Tcl_NewStringObj(id, -1));
+
+ /* name */
+ if((name = get_filename_parameter(NULL, 0, &part->body, NULL)) != NULL){
+ Tcl_ListObjAppendElement(interp, aObj, Tcl_NewStringObj(name, -1));
+ fs_give((void **) &name);
+ }
+ else
+ Tcl_ListObjAppendElement(interp, aObj, Tcl_NewStringObj("Unknown", -1));
+
+ /* size */
+ Tcl_ListObjAppendElement(interp, aObj,
+ Tcl_NewLongObj((part->body.encoding == ENCBASE64)
+ ? ((part->body.size.bytes * 3)/4)
+ : part->body.size.bytes));
+
+ /* type */
+ snprintf(tmp_20k_buf, SIZEOF_20KBUF, "%s/%s",
+ body_type_names(part->body.type),
+ part->body.subtype ? part->body.subtype : rfc822_default_subtype (part->body.type));
+ Tcl_ListObjAppendElement(interp, aObj, Tcl_NewStringObj(tmp_20k_buf, -1));
+ Tcl_ListObjAppendElement(interp, aListObj, aObj);
+ }
+ }
+ }
+
+ return (aListObj);
+}
+
+
+int
+peFccAppend(Tcl_Interp *interp, Tcl_Obj *obj, char *fcc, int colid)
+{
+ Tcl_Obj *objfcc = NULL;
+
+ if(colid < 0)
+ colid = (ps_global->context_list && (ps_global->context_list->use & CNTXT_INCMNG)) ? 1 : 0;
+
+ return((objfcc = Tcl_NewListObj(0, NULL))
+ && Tcl_ListObjAppendElement(interp, objfcc, Tcl_NewStringObj("fcc", -1)) == TCL_OK
+ && peAppListF(interp, objfcc, "%i%s", colid, fcc) == TCL_OK
+ && Tcl_ListObjAppendElement(interp, obj, objfcc) == TCL_OK);
+}
+
+
+/* * * * * * * * * * * * * Start of Address Management Routines * * * * * * * * * * * */
+
+
+/*
+ * PEAddressCmd - export various bits of address book/directory access
+ */
+int
+PEAddressCmd(ClientData clientData, Tcl_Interp *interp, int objc, Tcl_Obj *CONST objv[])
+{
+ char *op;
+
+ dprint((2, "PEAddressCmd"));
+
+ if(objc == 1){
+ Tcl_WrongNumArgs(interp, 1, objv, "cmd ?args?");
+ return(TCL_ERROR);
+ }
+ else if(!ps_global){
+ Tcl_SetResult(interp, "PEAddress: no open folder", TCL_STATIC);
+ return(TCL_ERROR);
+ }
+ else if((op = Tcl_GetStringFromObj(objv[1], NULL)) != NULL){
+ if(objc == 2){
+ if(!strcmp(op, "safecheck")){
+ if(peInitAddrbooks(interp, 1) != TCL_OK)
+ return(TCL_ERROR);
+ return(TCL_OK);
+ }
+ else if(!strcmp(op, "books")){
+ int i;
+
+ /*
+ * return the list of configured address books
+ */
+
+ if(peInitAddrbooks(interp, 0) != TCL_OK)
+ return(TCL_ERROR);
+
+ for(i = 0; i < as.n_addrbk; i++){
+ Tcl_Obj *objmv[4];
+
+ objmv[0] = Tcl_NewIntObj(i);
+ if(as.adrbks[i].abnick){
+ objmv[1] = Tcl_NewStringObj(as.adrbks[i].abnick, -1);
+ }
+ else {
+ char buf[256];
+
+ snprintf(buf, sizeof(buf), "Address book number %d", i + 1);
+ objmv[1] = Tcl_NewStringObj(buf, -1);
+ }
+
+ objmv[2] = Tcl_NewStringObj(as.adrbks[i].filename ? as.adrbks[i].filename : "", -1);
+
+ objmv[3] = Tcl_NewIntObj(as.adrbks[i].access == ReadWrite);
+
+ Tcl_ListObjAppendElement(interp, Tcl_GetObjResult(interp),
+ Tcl_NewListObj(4, objmv));
+ }
+
+ return(TCL_OK);
+ }
+ }
+ else if(objc == 3){
+ if(!strcmp(op, "parselist")){
+ char *addrstr;
+ ADDRESS *addrlist = NULL, *atmp, *anextp;
+ static char *fakedomain = "@";
+
+ if((addrstr = Tcl_GetStringFromObj(objv[2], NULL)) != NULL)
+ addrstr = cpystr(addrstr); /* can't munge tcl copy */
+
+ ps_global->c_client_error[0] = '\0';
+ rfc822_parse_adrlist(&addrlist, addrstr,
+ (F_ON(F_COMPOSE_REJECTS_UNQUAL, ps_global))
+ ? fakedomain : ps_global->maildomain);
+
+ fs_give((void **) &addrstr);
+ if(ps_global->c_client_error[0]){
+ Tcl_SetResult(interp, ps_global->c_client_error, TCL_STATIC);
+ return(TCL_ERROR);
+ }
+
+ for(atmp = addrlist; atmp; ){
+ RFC822BUFFER rbuf;
+
+ anextp = atmp->next;
+ atmp->next = NULL;
+ tmp_20k_buf[0] = '\0';
+ rbuf.f = dummy_soutr;
+ rbuf.s = NULL;
+ rbuf.beg = tmp_20k_buf;
+ rbuf.cur = tmp_20k_buf;
+ rbuf.end = tmp_20k_buf+SIZEOF_20KBUF-1;
+ rfc822_output_address_list(&rbuf, atmp, 0L, NULL);
+ *rbuf.cur = '\0';
+ Tcl_ListObjAppendElement(interp,
+ Tcl_GetObjResult(interp),
+ Tcl_NewStringObj(tmp_20k_buf, -1));
+ atmp = anextp;
+ }
+
+ mail_free_address(&addrlist);
+ return(TCL_OK);
+ }
+ else if(!strcmp(op, "xlookup")){
+ char *addrstr;
+ ADDRESS *addrlist = NULL, *atmp, *anextp;
+ static char *fakedomain = "@";
+
+ if((addrstr = Tcl_GetStringFromObj(objv[2], NULL)) != NULL)
+ addrstr = cpystr(addrstr); /* can't munge tcl copy */
+
+ ps_global->c_client_error[0] = '\0';
+ rfc822_parse_adrlist(&addrlist, addrstr,
+ (F_ON(F_COMPOSE_REJECTS_UNQUAL, ps_global))
+ ? fakedomain : ps_global->maildomain);
+
+ fs_give((void **) &addrstr);
+ if(ps_global->c_client_error[0]){
+ Tcl_SetResult(interp, ps_global->c_client_error, TCL_STATIC);
+ return(TCL_ERROR);
+ }
+
+ for(atmp = addrlist; atmp; ){
+ anextp = atmp->next;
+ atmp->next = NULL;
+ tmp_20k_buf[0] = '\0';
+ if(atmp->host){
+ if(atmp->host[0] == '@'){
+ /* leading ampersand means "missing-hostname" */
+ }
+ else{
+ RFC822BUFFER rbuf;
+
+ rbuf.f = dummy_soutr;
+ rbuf.s = NULL;
+ rbuf.beg = tmp_20k_buf;
+ rbuf.cur = tmp_20k_buf;
+ rbuf.end = tmp_20k_buf+SIZEOF_20KBUF-1;
+ rfc822_output_address_list(&rbuf, atmp, 0L, NULL);
+ *rbuf.cur = '\0';
+ Tcl_ListObjAppendElement(interp,
+ Tcl_GetObjResult(interp),
+ Tcl_NewStringObj(tmp_20k_buf, -1));
+ }
+ } /* else group syntax, move on */
+
+ atmp = anextp;
+ }
+
+ mail_free_address(&addrlist);
+ return(TCL_OK);
+ }
+ else if(!strcmp(op, "format")){
+ int i, booknum;
+ char buf[256], *s;
+
+ if(peInitAddrbooks(interp, 0) != TCL_OK)
+ return(TCL_ERROR);
+
+ /*
+ *
+ */
+ if(Tcl_GetIntFromObj(interp, objv[2], &booknum) == TCL_OK)
+ for(i = 0; i < as.n_addrbk; i++)
+ if(i == booknum){
+ addrbook_new_disp_form(&as.adrbks[booknum], ps_global->VAR_ABOOK_FORMATS, booknum, NULL);
+
+ for(i = 0; i < NFIELDS && as.adrbks[booknum].disp_form[i].type != Notused; i++){
+ switch(as.adrbks[booknum].disp_form[i].type){
+ case Nickname :
+ s = "nick";
+ break;
+ case Fullname :
+ s = "full";
+ break;
+ case Addr :
+ s = "addr";
+ break;
+ case Filecopy :
+ s = "fcc";
+ break;
+ case Comment :
+ s = "comment";
+ break;
+ default :
+ s = NULL;
+ break;
+ }
+
+ if(s){
+ Tcl_Obj *objmv[2];
+
+ objmv[0] = Tcl_NewStringObj(s, -1);
+ objmv[1] = Tcl_NewIntObj((100 * as.adrbks[booknum].disp_form[i].width) / ps_global->ttyo->screen_cols);
+ Tcl_ListObjAppendElement(interp, Tcl_GetObjResult(interp),
+ Tcl_NewListObj(2, objmv));
+ }
+ }
+
+
+ return(TCL_OK);
+ }
+
+ snprintf(buf, sizeof(buf), "PEAddress list: unknown address book number \"%d\"", booknum);
+ Tcl_SetResult(interp, buf, TCL_VOLATILE);
+ return(TCL_ERROR);
+ }
+ else if(!strcmp(op, "list")){
+ int i, j, k, n, booknum;
+ char buf[256], *s;
+ AdrBk_Entry *ae;
+ Tcl_Obj *objev[NFIELDS + 1], *objhv[2];
+
+ if(peInitAddrbooks(interp, 0) != TCL_OK)
+ return(TCL_ERROR);
+
+ /*
+ *
+ */
+ if(Tcl_GetIntFromObj(interp, objv[2], &booknum) == TCL_OK)
+ for(i = 0; i < as.n_addrbk; i++)
+ if(i == booknum){
+ addrbook_new_disp_form(&as.adrbks[booknum], ps_global->VAR_ABOOK_FORMATS, booknum, NULL);
+
+ for(i = 0;
+ (ae = adrbk_get_ae(as.adrbks[booknum].address_book, i));
+ i++){
+
+ /* first member is type: Single, List or Lookup */
+ switch(ae->tag){
+ case Single :
+ s = "single";
+ break;
+ case List :
+ s = "list";
+ break;
+ default : /* not set!?! */
+ continue;
+ }
+
+ if(!ae->nickname)
+ continue;
+
+ objhv[0] = Tcl_NewStringObj(ae->nickname, -1);
+ objhv[1] = Tcl_NewStringObj(s, -1);
+ objev[n = 0] = Tcl_NewListObj(2, objhv);
+
+ /*
+ * set fields based on VAR_ABOOK_FORMATS
+ */
+
+ for(j = 0; j < NFIELDS && as.adrbks[booknum].disp_form[j].type != Notused; j++){
+ switch(as.adrbks[booknum].disp_form[j].type){
+ case Nickname :
+ objev[++n] = Tcl_NewStringObj(ae->nickname, -1);
+ break;
+ case Fullname :
+ objev[++n] = Tcl_NewStringObj(ae->fullname, -1);
+ break;
+ case Addr :
+ if(ae->tag == Single){
+ objev[++n] = Tcl_NewStringObj(ae->addr.addr, -1);
+ }
+ else{
+ Tcl_Obj **objav;
+
+ for(k = 0; ae->addr.list[k]; k++)
+ ;
+
+ objav = (Tcl_Obj **) fs_get(k * sizeof(Tcl_Obj *));
+ for(k = 0; ae->addr.list[k]; k++)
+ objav[k] = Tcl_NewStringObj(ae->addr.list[k], -1);
+
+ objev[++n] = Tcl_NewListObj(k, objav);
+ fs_give((void **) &objav);
+ }
+ break;
+ case Filecopy :
+ objev[++n] = Tcl_NewStringObj(ae->fcc ? ae->fcc : "", -1);
+ break;
+ case Comment :
+ objev[++n] = Tcl_NewStringObj(ae->extra ? ae->extra : "", -1);
+ break;
+ default :
+ s = NULL;
+ break;
+ }
+ }
+
+ Tcl_ListObjAppendElement(interp, Tcl_GetObjResult(interp),
+ Tcl_NewListObj(n + 1, objev));
+ }
+
+ return(TCL_OK);
+ }
+
+ snprintf(buf, sizeof(buf), "PEAddress list: unknown address book number \"%d\"", booknum);
+ Tcl_SetResult(interp, buf, TCL_VOLATILE);
+ return(TCL_ERROR);
+ }
+ }
+ else if(objc == 4){
+ if(!strcmp(op, "verify")){
+ /*
+ * The object here is to check the following list of field values
+ * to see that they are valid address list, expanding if necessary.
+ * The first argument is the list of field values, with "to" being
+ * first. The second arg is the current fcc value.
+ *
+ * The return value is of the following form:
+ *
+ * { {{errstr {{oldstr newstr {ldap-opts ...}} ...}} ...} newfcc}
+ */
+ Tcl_Obj **objVal;
+ char *addrstr, *newaddr = NULL, *error = NULL,
+ *tstr1, *tstr2, *fcc, *newfcc = NULL;
+ BuildTo toaddr;
+ int rv, badadrs, i , numlistvals,
+ numldapqueries = 0;
+ Tcl_Obj *resObj = NULL, *secObj, *strObj, *adrObj, *res2Obj;
+#ifdef ENABLE_LDAP
+ WPLDAPRES_S **tsl;
+
+ wpldap_global->query_no++;
+ if(wpldap_global->ldap_search_list){
+ wpldap_global->ldap_search_list =
+ free_wpldapres(wpldap_global->ldap_search_list);
+ }
+
+ tsl = &(wpldap_global->ldap_search_list);
+#endif /* ENABLE_LDAP */
+
+ if(Tcl_ListObjGetElements(interp, objv[2], &numlistvals,
+ &objVal) == TCL_OK){
+ if((fcc = Tcl_GetStringFromObj(objv[3], NULL)) == NULL)
+ return TCL_ERROR;
+ res2Obj = Tcl_NewListObj(0, NULL);
+ for(i = 0; i < numlistvals; i++){
+ size_t l;
+
+ if((addrstr = Tcl_GetStringFromObj(objVal[i], NULL)) == NULL)
+ return TCL_ERROR;
+
+ addrstr = cpystr(addrstr); /* can't munge tcl copy */
+ toaddr.type = Str;
+ toaddr.arg.str = cpystr(addrstr);
+ l = strlen(addrstr);
+ badadrs = 0;
+ resObj = Tcl_NewListObj(0, NULL);
+ secObj = Tcl_NewListObj(0, NULL);
+ for(tstr1 = addrstr; tstr1; tstr1 = tstr2){
+ tstr2 = strqchr(tstr1, ',', 0, -1);
+ if(tstr2)
+ *tstr2 = '\0';
+
+ strncpy(toaddr.arg.str, tstr1, l);
+ toaddr.arg.str[l] = '\0';
+
+ removing_leading_and_trailing_white_space(toaddr.arg.str);
+ if(*toaddr.arg.str){
+ if(i == 0 && tstr1 == addrstr)
+ newfcc = cpystr(fcc);
+
+ rv = our_build_address(toaddr, &newaddr, &error, &newfcc, NULL);
+
+ if(rv == 0){
+ strObj = Tcl_NewListObj(0, NULL);
+ Tcl_ListObjAppendElement(interp, strObj,
+ Tcl_NewStringObj(toaddr.arg.str, -1));
+ Tcl_ListObjAppendElement(interp, strObj,
+ Tcl_NewStringObj(newaddr,-1));
+ /* append whether or not ldap stuff
+ * was returned
+ */
+ adrObj = Tcl_NewListObj(0,NULL);
+#ifdef ENABLE_LDAP
+ if(*tsl) {
+ LDAP_CHOOSE_S *tres;
+ LDAP_SERV_RES_S *trl;
+ LDAPMessage *e;
+ ADDRESS *newadr;
+ char *ret_to;
+
+ tres = (LDAP_CHOOSE_S *)fs_get(sizeof(LDAP_CHOOSE_S));
+ for(trl = (*tsl)->reslist; trl;
+ trl = trl->next){
+ for(e = ldap_first_entry(trl->ld,
+ trl->res);
+ e != NULL;
+ e = ldap_next_entry(trl->ld, e)){
+ tres->ld = trl->ld;
+ tres->selected_entry = e;
+ tres->info_used = trl->info_used;
+ tres->serv = trl->serv;
+ if((newadr = address_from_ldap(tres)) != NULL){
+ if(newadr->mailbox && newadr->host){
+ RFC822BUFFER rbuf;
+ size_t len;
+
+ len = est_size(newadr);
+ ret_to = (char *)fs_get(len * sizeof(char));
+ ret_to[0] = '\0';
+ rbuf.f = dummy_soutr;
+ rbuf.s = NULL;
+ rbuf.beg = ret_to;
+ rbuf.cur = ret_to;
+ rbuf.end = ret_to+len-1;
+ rfc822_output_address_list(&rbuf, newadr, 0L, NULL);
+ *rbuf.cur = '\0';
+ Tcl_ListObjAppendElement(interp,
+ adrObj, Tcl_NewStringObj(ret_to, -1));
+ fs_give((void **)&ret_to);
+ }
+ mail_free_address(&newadr);
+ }
+ }
+ }
+ fs_give((void **)&tres);
+ numldapqueries++;
+ tsl = &((*tsl)->next);
+ }
+#endif /* ENABLE_LDAP */
+ Tcl_ListObjAppendElement(interp, strObj, adrObj);
+ Tcl_ListObjAppendElement(interp, secObj, strObj);
+ }
+ else {
+ badadrs = 1;
+ break;
+ }
+ }
+ if(tstr2){
+ *tstr2 = ',';
+ tstr2++;
+ }
+ }
+ resObj = Tcl_NewListObj(0, NULL);
+ Tcl_ListObjAppendElement(interp, resObj,
+ Tcl_NewStringObj(badadrs
+ ? (error ? error : "Unknown")
+ : "", -1));
+ Tcl_ListObjAppendElement(interp, resObj, secObj);
+ Tcl_ListObjAppendElement(interp, res2Obj, resObj);
+ fs_give((void **) &addrstr);
+ fs_give((void **) &toaddr.arg.str);
+ }
+ Tcl_ListObjAppendElement(interp, Tcl_GetObjResult(interp), res2Obj);
+ Tcl_ListObjAppendElement(interp, Tcl_GetObjResult(interp),
+ Tcl_NewStringObj(newfcc ? newfcc
+ : (fcc ? fcc : ""), -1));
+ if(newfcc) fs_give((void **)&newfcc);
+ return(TCL_OK);
+ }
+ return(TCL_ERROR);
+ }
+ else if(!strcmp(op, "expand")){
+ BuildTo toaddr;
+ char *addrstr, *newaddr = NULL, *error = NULL, *fcc, *newfcc = NULL;
+ int rv;
+
+ /*
+ * Return value will be of the form:
+ * {"addrstr",
+ * ldap-query-number,
+ * "fcc"
+ * }
+ *
+ * ldap-query-number will be nonzero if
+ * there is something interesting to display as a result
+ * of an ldap query.
+ */
+
+ /*
+ * Given what looks like an rfc822 address line, parse the
+ * contents and expand any tokens that look like they ought
+ * to be.
+ */
+
+ if((addrstr = Tcl_GetStringFromObj(objv[2], NULL)) != NULL){
+ toaddr.type = Str;
+ toaddr.arg.str = cpystr(addrstr); /* can't munge tcl copy */
+ fcc = Tcl_GetStringFromObj(objv[3], NULL);
+#ifdef ENABLE_LDAP
+ wpldap_global->query_no++;
+ if(wpldap_global->ldap_search_list){
+ wpldap_global->ldap_search_list =
+ free_wpldapres(wpldap_global->ldap_search_list);
+ }
+#endif /* ENABLE_LDAP */
+ newfcc = cpystr(fcc);
+ rv = our_build_address(toaddr, &newaddr, &error, &newfcc, NULL);
+ fs_give((void **) &toaddr.arg.str);
+ if(rv == 0){
+#ifdef ENABLE_LDAP
+ /*
+ * c-client quotes results with spaces in them, so we'll go
+ * through and unquote them.
+ */
+ if(wpldap_global->ldap_search_list){
+ WPLDAPRES_S *tres;
+ char *tstr1, *tstr2;
+ char *qstr1, *newnewaddr;
+ int qstr1len;
+
+ for(tres = wpldap_global->ldap_search_list;
+ tres; tres = tres->next){
+ if(strqchr(tres->str, ' ', 0, -1)){
+ qstr1len = strlen(tres->str) + 3;
+ qstr1 = (char *)fs_get(qstr1len*sizeof(char));
+ snprintf(qstr1, qstr1len, "\"%.*s\"", qstr1len, tres->str);
+ for(tstr1 = newaddr; tstr1; tstr1 = tstr2){
+ tstr2 = strqchr(tstr1, ',', 0, -1);
+ if(strncmp(qstr1, tstr1, tstr2 ? tstr2 - tstr1
+ : strlen(tstr1)) == 0){
+ size_t l;
+ l = strlen(newaddr) + strlen(tres->str) + 2
+ + (tstr2 ? strlen(tstr2) : 0);
+ newnewaddr = (char *) fs_get(l * sizeof(char));
+ snprintf(newnewaddr, l, "%.*s%s%s", tstr1 - newaddr,
+ newaddr, tres->str, tstr2 ? tstr2 : "");
+ fs_give((void **)&newaddr);
+ newaddr = newnewaddr;
+ break;
+ }
+ if(tstr2)
+ tstr2++;
+ if(tstr2 && *tstr2 == ' ')
+ tstr2++;
+ }
+ if(qstr1)
+ fs_give((void **) &qstr1);
+ }
+ }
+ }
+#endif /* ENABLE_LDAP */
+ if(Tcl_ListObjAppendElement(interp, Tcl_GetObjResult(interp),
+ Tcl_NewStringObj(newaddr, -1)) != TCL_OK)
+ return(TCL_ERROR);
+#ifdef ENABLE_LDAP
+ if(wpldap_global->ldap_search_list){
+ if(Tcl_ListObjAppendElement(interp, Tcl_GetObjResult(interp),
+ Tcl_NewIntObj(wpldap_global->query_no)) != TCL_OK)
+ return(TCL_ERROR);
+ }
+ else
+#endif /* ENABLE_LDAP */
+ if(Tcl_ListObjAppendElement(interp, Tcl_GetObjResult(interp),
+ Tcl_NewIntObj(0)) != TCL_OK)
+ return(TCL_ERROR);
+ if(Tcl_ListObjAppendElement(interp, Tcl_GetObjResult(interp),
+ Tcl_NewStringObj(newfcc ? newfcc
+ : (fcc ? fcc : ""), -1)) != TCL_OK)
+ return(TCL_ERROR);
+ if(newfcc) fs_give((void **)&newfcc);
+
+ return(TCL_OK);
+ }
+ else{
+ Tcl_SetResult(interp, error ? error : "Indeterminate error", TCL_VOLATILE);
+ if(newfcc) fs_give((void **)&newfcc);
+ return(TCL_ERROR);
+ }
+ }
+ }
+ else if(!strcmp(op, "complete")){
+ /*
+ * CMD: complete uid
+ *
+ * Look for possible completions for
+ * given query_string.
+ *
+ * ARGS: <query_string> <uid>
+ *
+ * Returns: candidate list: {nickname {personal mailbox}}
+ */
+ char *query, *errstr;
+ long uid;
+ COMPLETE_S *completions, *cp;
+
+ if(peInitAddrbooks(interp, 0) == TCL_OK){
+ if((query = Tcl_GetStringFromObj(objv[2], NULL)) != NULL){
+ if(Tcl_GetLongFromObj(interp, objv[3], &uid) == TCL_OK){
+
+ completions = adrbk_list_of_completions(query,
+ ps_global->mail_stream,
+ uid,
+#ifdef ENABLE_LDAP
+ ((strlen(query) >= 5) ? ALC_INCLUDE_LDAP : 0) |
+#endif /* ENABLE_LDAP */
+ 0);
+
+ if(completions){
+ for(cp = completions; cp; cp = cp->next)
+ peAppListF(interp, Tcl_GetObjResult(interp), "%s %s %s",
+ cp->nickname ? cp->nickname : "",
+ cp->full_address ? cp->full_address : "",
+ cp->fcc ? cp->fcc : "");
+
+ free_complete_s(&completions);
+ }
+ else
+ Tcl_SetResult(interp, "", TCL_STATIC);
+
+ return(TCL_OK);
+ }
+ else
+ errstr = "PEAddress: Cannot read UID";
+ }
+ else
+ errstr = "PEAddress: Cannot get completion query";
+ }
+ else
+ errstr = "PEAddress: Address Book initialization failed";
+
+ Tcl_SetResult(interp, errstr, TCL_STATIC);
+ return(TCL_ERROR);
+ }
+ }
+ else if(objc == 5){
+ if(!strcmp(op, "entry")){
+ int booknum, i, aindex;
+ char *nick, *astr = NULL, *errstr = NULL, *fccstr = NULL, buf[128];
+ AdrBk_Entry *ae;
+ BuildTo bldto;
+
+ if(peInitAddrbooks(interp, 0) != TCL_OK)
+ return(TCL_ERROR);
+
+ /*
+ * Given an address book handle and nickname, return address
+ */
+ if(Tcl_GetIntFromObj(interp, objv[2], &booknum) == TCL_OK)
+ for(i = 0; i < as.n_addrbk; i++)
+ if(i == booknum){
+ if((nick = Tcl_GetStringFromObj(objv[3], NULL)) == NULL){
+ Tcl_SetResult(interp, "PEAddress list: Can't get nickname", TCL_STATIC);
+ return(TCL_ERROR);
+ }
+ if(Tcl_GetIntFromObj(interp, objv[4], &aindex) != TCL_OK
+ || (*nick == '\0' && aindex < 0)){
+ Tcl_SetResult(interp, "PEAddress list: Can't get aindex", TCL_STATIC);
+ return(TCL_ERROR);
+ }
+ if((*nick)
+ ? (ae = adrbk_lookup_by_nick(as.adrbks[booknum].address_book, nick, NULL))
+ : (ae = adrbk_get_ae(as.adrbks[booknum].address_book, aindex))){
+ bldto.type = Abe;
+ bldto.arg.abe = ae;
+
+ (void) our_build_address(bldto, &astr, &errstr, &fccstr, NULL);
+
+ if(errstr){
+ if(astr)
+ fs_give((void **) &astr);
+
+ Tcl_SetResult(interp, errstr, TCL_VOLATILE);
+ return(TCL_ERROR);
+ }
+
+ if(astr){
+ char *p;
+ int l;
+
+ l = (4*strlen(astr) + 1) * sizeof(char);
+ p = (char *) fs_get(l);
+ if(rfc1522_decode_to_utf8((unsigned char *) p, l, astr) == (unsigned char *) p){
+ fs_give((void **) &astr);
+ astr = p;
+ }
+ else
+ fs_give((void **)&p);
+ }
+ }
+
+ if(astr){
+ Tcl_ListObjAppendElement(interp, Tcl_GetObjResult(interp),
+ Tcl_NewStringObj(astr, -1));
+ fs_give((void **) &astr);
+
+ if(fccstr){
+ Tcl_ListObjAppendElement(interp, Tcl_GetObjResult(interp),
+ Tcl_NewStringObj(*fccstr ? fccstr : "\"\"", -1));
+ fs_give((void **) &fccstr);
+ }
+ else
+ Tcl_ListObjAppendElement(interp, Tcl_GetObjResult(interp),
+ Tcl_NewStringObj("", -1));
+ }
+ else
+ Tcl_SetResult(interp, "", TCL_STATIC);
+
+ return(TCL_OK);
+ }
+
+ snprintf(buf, sizeof(buf), "PEAddress list: unknown address book ID %d", booknum);
+ Tcl_SetResult(interp, buf, TCL_VOLATILE);
+ return(TCL_ERROR);
+ }
+ else if(!strcmp(op, "fullentry")){
+ int booknum, j, aindex;
+ char *nick;
+ AdrBk_Entry *ae;
+ Tcl_Obj *resObj;
+
+ if(peInitAddrbooks(interp, 0) != TCL_OK)
+ return(TCL_ERROR);
+
+ /*
+ * Given an address book handle and nickname, return
+ * nickname, fullname, address(es), fcc, and comments
+ */
+ if(Tcl_GetIntFromObj(interp, objv[2], &booknum) == TCL_OK){
+ if(booknum >= 0 && booknum < as.n_addrbk){
+ if((nick = Tcl_GetStringFromObj(objv[3], NULL)) == NULL)
+ return(TCL_ERROR);
+ if(Tcl_GetIntFromObj(interp, objv[4], &aindex) != TCL_OK
+ || (*nick == '\0' && aindex < 0))
+ return(TCL_ERROR);
+ if((*nick)
+ ? (ae = adrbk_lookup_by_nick(as.adrbks[booknum].address_book, nick, NULL))
+ : (ae = adrbk_get_ae(as.adrbks[booknum].address_book, aindex))){
+ Tcl_ListObjAppendElement(interp,
+ Tcl_GetObjResult(interp),
+ Tcl_NewStringObj(ae->nickname ? ae->nickname : "", -1));
+ Tcl_ListObjAppendElement(interp,
+ Tcl_GetObjResult(interp),
+ Tcl_NewStringObj(ae->fullname ? ae->fullname : "", -1));
+ resObj = Tcl_NewListObj(0,NULL);
+ if(ae->tag == Single)
+ Tcl_ListObjAppendElement(interp,
+ resObj,
+ Tcl_NewStringObj(ae->addr.addr ? ae->addr.addr : "", -1));
+ else {
+ for(j = 0; ae->addr.list[j]; j++)
+ Tcl_ListObjAppendElement(interp, resObj,
+ Tcl_NewStringObj(ae->addr.list[j] ? ae->addr.list[j] : "", -1));
+ }
+ Tcl_ListObjAppendElement(interp, Tcl_GetObjResult(interp), resObj);
+ Tcl_ListObjAppendElement(interp,
+ Tcl_GetObjResult(interp),
+ Tcl_NewStringObj(ae->fcc ? ae->fcc : "", -1));
+ Tcl_ListObjAppendElement(interp,
+ Tcl_GetObjResult(interp),
+ Tcl_NewStringObj(ae->extra ? ae->extra : "", -1));
+ return(TCL_OK);
+ }
+ }
+ }
+ return(TCL_ERROR);
+ }
+ else if(!strcmp(op, "delete")){
+ char *nick, buf[256];
+ int booknum, aindex;
+ adrbk_cntr_t old_entry;
+ AdrBk *ab;
+ if(peInitAddrbooks(interp, 0) != TCL_OK){
+ snprintf(buf, sizeof(buf), "PEAddress delete: couldn't init addressbooks");
+ Tcl_SetResult(interp, buf, TCL_VOLATILE);
+ return(TCL_ERROR);
+ }
+ if(Tcl_GetIntFromObj(interp, objv[2], &booknum) == TCL_OK){
+ nick = Tcl_GetStringFromObj(objv[3], NULL);
+ removing_leading_and_trailing_white_space(nick);
+ }
+ else
+ return(TCL_ERROR);
+ if(booknum >= 0 && booknum < as.n_addrbk) {
+ if(as.adrbks[booknum].access != ReadWrite) return TCL_ERROR;
+ ab = as.adrbks[booknum].address_book;
+ }
+ else{
+ snprintf(buf, sizeof(buf), "PEAddress delete: Book number out of range");
+ Tcl_SetResult(interp, buf, TCL_VOLATILE);
+ return(TCL_ERROR);
+ }
+ if((Tcl_GetIntFromObj(interp, objv[4], &aindex) != TCL_OK)
+ || (*nick == '\0' && aindex < 0))
+ return(TCL_ERROR);
+ adrbk_check_validity(ab, 1L);
+ if(ab->flags & FILE_OUTOFDATE ||
+ (ab->rd && ab->rd->flags & REM_OUTOFDATE)){
+ Tcl_SetResult(interp,
+ "Address book out of sync. Cannot update at this moment",
+ TCL_VOLATILE);
+ return(TCL_ERROR);
+ }
+ if(!nick){
+ snprintf(buf, sizeof(buf), "PEAddress delete: No nickname");
+ Tcl_SetResult(interp, buf, TCL_VOLATILE);
+ return(TCL_ERROR);
+ }
+ if((*nick)
+ ? (!adrbk_lookup_by_nick(ab, nick, &old_entry))
+ : ((old_entry = (adrbk_cntr_t)aindex) == -1)){
+ snprintf(buf, sizeof(buf), "PEAddress delete: Nickname \"%.128s\" not found", nick);
+ Tcl_SetResult(interp, buf, TCL_VOLATILE);
+ return(TCL_ERROR);
+ }
+ if(adrbk_delete(ab, old_entry, 0, 0, 1, 1)){
+ snprintf(buf, sizeof(buf), "PEAddress delete: Couldn't delete addressbook entry");
+ Tcl_SetResult(interp, buf, TCL_VOLATILE);
+ return(TCL_ERROR);
+ }
+ return(TCL_OK);
+ }
+ }
+ else if((objc == 10 || objc == 11) && !strcmp(op, "edit")){
+ if(!strcmp(op, "edit")){
+ int booknum, adri, add, rv, aindex;
+ char *nick, *fn, *fcc, *comment, *addrfield,
+ buf[256], **addrs, *orignick = NULL;
+ AdrBk_Entry *ae = NULL;
+ AdrBk *ab;
+ adrbk_cntr_t old_entry = NO_NEXT, new_entry;
+ Tag tag;
+ ADDRESS *adr = NULL;
+
+
+ if(peInitAddrbooks(interp, 0) != TCL_OK)
+ return(TCL_ERROR);
+ if(Tcl_GetIntFromObj(interp, objv[2], &booknum) == TCL_OK){
+ if(as.adrbks[booknum].access != ReadWrite) return TCL_ERROR;
+ nick = Tcl_GetStringFromObj(objv[3], NULL);
+ removing_leading_and_trailing_white_space(nick);
+ if(Tcl_GetIntFromObj(interp, objv[4], &aindex) != TCL_OK){
+ Tcl_SetResult(interp, "No Address Handle", TCL_VOLATILE);
+ return(TCL_ERROR);
+ }
+ fn = Tcl_GetStringFromObj(objv[5], NULL);
+ removing_leading_and_trailing_white_space(fn);
+ if(!*fn) fn = NULL;
+ addrfield = Tcl_GetStringFromObj(objv[6], NULL);
+ removing_leading_and_trailing_white_space(addrfield);
+ if(!*addrfield) addrfield = NULL;
+ /*
+ if(Tcl_ListObjGetElements(interp, objv[7], &numlistvals, &objVal) != TCL_OK)
+ return(TCL_ERROR);
+ */
+ fcc = Tcl_GetStringFromObj(objv[7], NULL);
+ removing_leading_and_trailing_white_space(fcc);
+ if(!*fcc) fcc = NULL;
+ comment = Tcl_GetStringFromObj(objv[8], NULL);
+ removing_leading_and_trailing_white_space(comment);
+ if(!*comment) comment = NULL;
+ if(Tcl_GetIntFromObj(interp, objv[9], &add) != TCL_OK)
+ return(TCL_ERROR);
+ if(objc == 11) {
+ /*
+ * if objc == 11 then that means that they changed the
+ * value of nick to something else, and this one is the
+ * original nick
+ */
+ orignick = Tcl_GetStringFromObj(objv[10], NULL);
+ removing_leading_and_trailing_white_space(orignick);
+ }
+ if((addrs = parse_addrlist(addrfield)) != NULL){
+ int tbuflen = strlen(addrfield);
+ char *tbuf;
+ if(!(tbuf = (char *) fs_get(sizeof(char) * (tbuflen+128)))){
+ Tcl_SetResult(interp, "malloc error", TCL_VOLATILE);
+ fs_give((void **) &addrs);
+ return(TCL_ERROR);
+ }
+ for(adri = 0; addrs[adri]; adri++){
+ if(*(addrs[adri])){
+ ps_global->c_client_error[0] = '\0';
+ strncpy(tbuf, addrs[adri], tbuflen+128);
+ tbuf[tbuflen+128-1] = '\0';
+ rfc822_parse_adrlist(&adr, tbuf, "@");
+ if(adr) mail_free_address(&adr);
+ adr = NULL;
+ if(ps_global->c_client_error[0]){
+ snprintf(buf, sizeof(buf),"Problem with address %.10s%s: %s",
+ addrs[adri], strlen(addrs[adri]) > 10 ?
+ "..." : "", ps_global->c_client_error);
+ Tcl_SetResult(interp, buf, TCL_VOLATILE);
+ if(tbuf)
+ fs_give((void **) &tbuf);
+ fs_give((void **) &addrs);
+ return(TCL_ERROR);
+ }
+ }
+ }
+ if(tbuf) fs_give((void **)&tbuf);
+ }
+ else adri = 0;
+
+ /* addrs[adri] = NULL; */
+
+ if(adri > 1) tag = List;
+ else tag = Single;
+
+ if(booknum >= 0 && booknum < as.n_addrbk) {
+ ab = as.adrbks[booknum].address_book;
+ }
+ else{
+ if(addrs)
+ fs_give((void **) &addrs);
+ return(TCL_ERROR);
+ }
+ adrbk_check_validity(ab, 1L);
+ if(ab->flags & FILE_OUTOFDATE ||
+ (ab->rd && ab->rd->flags & REM_OUTOFDATE)){
+ Tcl_SetResult(interp,
+ "Address book out of sync. Cannot update at this moment",
+ TCL_VOLATILE);
+ return(TCL_ERROR);
+ }
+ if(aindex >= 0){
+ ae = adrbk_get_ae(as.adrbks[booknum].address_book, aindex);
+ if(ae){
+ old_entry = (adrbk_cntr_t) aindex;
+ }
+ else{
+ Tcl_SetResult(interp, "No Address Handle!", TCL_VOLATILE);
+ return(TCL_ERROR);
+ }
+ }
+ else if(nick && *nick && adrbk_lookup_by_nick(ab, nick, NULL)){
+ snprintf(buf, sizeof(buf), "Entry with nickname %.128s already exists.",
+ nick);
+ Tcl_SetResult(interp, buf, TCL_VOLATILE);
+ if(addrs)
+ fs_give((void **) &addrs);
+ return(TCL_ERROR);
+ }
+ if(ae &&
+ ((tag == List && ae->tag == Single) ||
+ (tag == Single && ae->tag == List))){
+ if(adrbk_delete(ab, old_entry, 0,0,1,0)){
+ snprintf(buf, sizeof(buf), "Problem updating from %s to %s.",
+ ae->tag == Single ? "Single" : "List",
+ tag == List ? "List" : "Single");
+ Tcl_SetResult(interp, buf, TCL_VOLATILE);
+ if(addrs)
+ fs_give((void **) &addrs);
+ return(TCL_ERROR);
+ }
+ old_entry = NO_NEXT;
+ }
+ if((rv = adrbk_add(ab, old_entry,
+ nick ? nick : "",
+ fn ? fn : "",
+ tag == List ? (char *)addrs :
+ (addrs && *addrs) ? *addrs : "",
+ fcc ? fcc : "",
+ comment ? comment : "",
+ tag, &new_entry, NULL, 0, 1,
+ tag == List ? 0 : 1)) != 0){
+ snprintf(buf, sizeof(buf), "Couldn't add entry! rv=%d.", rv);
+ Tcl_SetResult(interp, buf, TCL_VOLATILE);
+ if(addrs)
+ fs_give((void **) &addrs);
+ return(TCL_ERROR);
+ }
+ if(tag == List) {
+ adrbk_listdel_all(ab, new_entry);
+ adrbk_nlistadd(ab, new_entry, NULL, NULL, addrs, 0, 1, 1);
+ }
+ return(TCL_OK);
+ }
+ snprintf(buf, sizeof(buf), "Unknown address book ID %d", booknum);
+ Tcl_SetResult(interp, buf, TCL_VOLATILE);
+ return(TCL_ERROR);
+ }
+ }
+ }
+
+ Tcl_SetResult(interp, "PEAddress: unrecognized command", TCL_STATIC);
+ return(TCL_ERROR);
+}
+
+
+int
+peInitAddrbooks(Tcl_Interp *interp, int safe)
+{
+ if(ps_global->remote_abook_validity > 0)
+ (void)adrbk_check_and_fix_all(safe, 0, 0);
+
+ if(!init_addrbooks(NoDisplay, 1, 1, 0)){
+ Tcl_SetResult(interp, "No Address Book Configured", TCL_STATIC);
+ return(TCL_ERROR);
+ }
+
+ return(TCL_OK);
+}
+
+
+
+int
+peRuleStatVal(char *str, int *n)
+{
+ if(!str)
+ return(1);
+
+ if(!strcmp(str, "either"))
+ *n = PAT_STAT_EITHER;
+ else if(!strcmp(str, "yes"))
+ *n = PAT_STAT_YES;
+ else if(!strcmp(str, "no"))
+ *n = PAT_STAT_NO;
+ else
+ return 1;
+
+ return 0;
+}
+
+
+#define RS_RULE_EDIT 0x0001
+#define RS_RULE_ADD 0x0002
+#define RS_RULE_DELETE 0x0004
+#define RS_RULE_SHUFFUP 0x0008
+#define RS_RULE_SHUFFDOWN 0x0010
+#define RS_RULE_GETPAT 0x0100
+#define RS_RULE_FINDPAT 0x0200
+
+int
+peRuleSet(Tcl_Interp *interp, Tcl_Obj **objv)
+{
+ char *rule, *patvar, *patval, *actvar, *actval, *tstr, *ruleaction;
+ int rno, nPat, nPatEmnt, nAct, nActEmnt, i, rv = 0;
+ Tcl_Obj **objPat, **objPatEmnt, **objAct, **objActEmnt;
+ long rflags = PAT_USE_CHANGED, aflags = 0;
+ PAT_STATE pstate;
+ PAT_S *pat, *new_pat;
+
+ if(!(rule = Tcl_GetStringFromObj(objv[0], NULL)))
+ return(TCL_ERROR);
+
+ if(!(ruleaction = Tcl_GetStringFromObj(objv[1], NULL)))
+ return(TCL_ERROR);
+
+ if(Tcl_GetIntFromObj(interp, objv[2], &rno) == TCL_ERROR)
+ return(TCL_ERROR);
+
+ if(!(strcmp(rule, "filter")))
+ rflags |= ROLE_DO_FILTER;
+ else if(!(strcmp(rule, "score")))
+ rflags |= ROLE_DO_SCORES;
+ else if(!(strcmp(rule, "indexcolor")))
+ rflags |= ROLE_DO_INCOLS;
+ else
+ return(TCL_ERROR);
+
+ if(!(strcmp(ruleaction, "edit"))){
+ aflags |= RS_RULE_EDIT;
+ aflags |= RS_RULE_GETPAT;
+ aflags |= RS_RULE_FINDPAT;
+ }
+ else if(!(strcmp(ruleaction, "add"))){
+ aflags |= RS_RULE_ADD;
+ aflags |= RS_RULE_GETPAT;
+ }
+ else if(!(strcmp(ruleaction, "delete"))){
+ aflags |= RS_RULE_DELETE;
+ aflags |= RS_RULE_FINDPAT;
+ }
+ else if(!(strcmp(ruleaction, "shuffup"))){
+ aflags |= RS_RULE_SHUFFUP;
+ aflags |= RS_RULE_FINDPAT;
+ }
+ else if(!(strcmp(ruleaction, "shuffdown"))){
+ aflags |= RS_RULE_SHUFFDOWN;
+ aflags |= RS_RULE_FINDPAT;
+ }
+ else return(TCL_ERROR);
+
+ if(aflags & RS_RULE_FINDPAT){
+ if(any_patterns(rflags, &pstate)){
+ for(pat = first_pattern(&pstate), i = 0;
+ pat && i != rno;
+ pat = next_pattern(&pstate), i++);
+ if(i != rno) return(TCL_ERROR);
+ }
+ }
+ if(aflags & RS_RULE_GETPAT){
+ int tcl_error = 0;
+
+ Tcl_ListObjGetElements(interp, objv[3], &nPat, &objPat);
+ Tcl_ListObjGetElements(interp, objv[4], &nAct, &objAct);
+
+ new_pat = (PAT_S *)fs_get(sizeof(PAT_S));
+ memset(new_pat, 0, sizeof(PAT_S));
+ new_pat->patgrp = (PATGRP_S *)fs_get(sizeof(PATGRP_S));
+ memset(new_pat->patgrp, 0, sizeof(PATGRP_S));
+ new_pat->action = (ACTION_S *)fs_get(sizeof(ACTION_S));
+ memset(new_pat->action, 0, sizeof(ACTION_S));
+
+ /* Set up the pattern group */
+ for(i = 0; i < nPat; i++){
+ Tcl_ListObjGetElements(interp, objPat[i], &nPatEmnt, &objPatEmnt);
+ if(nPatEmnt != 2) return(TCL_ERROR);
+ patvar = Tcl_GetStringFromObj(objPatEmnt[0], NULL);
+ patval = Tcl_GetStringFromObj(objPatEmnt[1], NULL);
+ if(!patvar || !patval) return(TCL_ERROR);
+
+ tstr = NULL;
+ if(*patval){
+ tstr = cpystr(patval);
+ removing_leading_and_trailing_white_space(tstr);
+ if(!(*tstr))
+ fs_give((void **) &tstr);
+ }
+
+ if(!(strcmp(patvar, "nickname"))){
+ new_pat->patgrp->nick = tstr;
+ tstr = NULL;
+ }
+ else if(!(strcmp(patvar, "comment"))){
+ new_pat->patgrp->comment = tstr;
+ tstr = NULL;
+ }
+ else if(!(strcmp(patvar, "to"))){
+ new_pat->patgrp->to = string_to_pattern(tstr);
+ }
+ else if(!(strcmp(patvar, "from"))){
+ new_pat->patgrp->from = string_to_pattern(tstr);
+ }
+ else if(!(strcmp(patvar, "sender"))){
+ new_pat->patgrp->sender = string_to_pattern(tstr);
+ }
+ else if(!(strcmp(patvar, "cc"))){
+ new_pat->patgrp->cc = string_to_pattern(tstr);
+ }
+ else if(!(strcmp(patvar, "recip"))){
+ new_pat->patgrp->recip = string_to_pattern(tstr);
+ }
+ else if(!(strcmp(patvar, "partic"))){
+ new_pat->patgrp->partic = string_to_pattern(tstr);
+ }
+ else if(!(strcmp(patvar, "news"))){
+ new_pat->patgrp->news = string_to_pattern(tstr);
+ }
+ else if(!(strcmp(patvar, "subj"))){
+ new_pat->patgrp->subj = string_to_pattern(tstr);
+ }
+ else if(!(strcmp(patvar, "bodytext"))){
+ new_pat->patgrp->bodytext = string_to_pattern(tstr);
+ }
+ else if(!(strcmp(patvar, "alltext"))){
+ new_pat->patgrp->alltext = string_to_pattern(tstr);
+ }
+ else if(!(strcmp(patvar, "keyword"))){
+ new_pat->patgrp->keyword = string_to_pattern(tstr);
+ }
+ else if(!(strcmp(patvar, "charset"))){
+ new_pat->patgrp->charsets = string_to_pattern(tstr);
+ }
+ else if(!(strcmp(patvar, "ftype"))){
+ if(!tstr) return(TCL_ERROR);
+
+ if(!(strcmp(tstr, "any")))
+ new_pat->patgrp->fldr_type = FLDR_ANY;
+ else if(!(strcmp(tstr, "news")))
+ new_pat->patgrp->fldr_type = FLDR_NEWS;
+ else if(!(strcmp(tstr, "email")))
+ new_pat->patgrp->fldr_type = FLDR_EMAIL;
+ else if(!(strcmp(tstr, "specific")))
+ new_pat->patgrp->fldr_type = FLDR_SPECIFIC;
+ else{
+ free_pat(&new_pat);
+ return(TCL_ERROR);
+ }
+ }
+ else if(!(strcmp(patvar, "folder"))){
+ new_pat->patgrp->folder = string_to_pattern(tstr);
+ }
+ else if(!(strcmp(patvar, "stat_new"))){
+ if(peRuleStatVal(tstr, &new_pat->patgrp->stat_new)){
+ free_pat(&new_pat);
+ tcl_error++;
+ }
+ }
+ else if(!(strcmp(patvar, "stat_rec"))){
+ if(peRuleStatVal(tstr, &new_pat->patgrp->stat_rec)){
+ free_pat(&new_pat);
+ tcl_error++;
+ }
+ }
+ else if(!(strcmp(patvar, "stat_del"))){
+ if(peRuleStatVal(tstr, &new_pat->patgrp->stat_del)){
+ free_pat(&new_pat);
+ tcl_error++;
+ }
+ }
+ else if(!(strcmp(patvar, "stat_imp"))){
+ if(peRuleStatVal(tstr, &new_pat->patgrp->stat_imp)){
+ free_pat(&new_pat);
+ tcl_error++;
+ }
+ }
+ else if(!(strcmp(patvar, "stat_ans"))){
+ if(peRuleStatVal(tstr, &new_pat->patgrp->stat_ans)){
+ free_pat(&new_pat);
+ tcl_error++;
+ }
+ }
+ else if(!(strcmp(patvar, "stat_8bitsubj"))){
+ if(peRuleStatVal(tstr, &new_pat->patgrp->stat_8bitsubj)){
+ free_pat(&new_pat);
+ tcl_error++;
+ }
+ }
+ else if(!(strcmp(patvar, "stat_bom"))){
+ if(peRuleStatVal(tstr, &new_pat->patgrp->stat_bom)){
+ free_pat(&new_pat);
+ tcl_error++;
+ }
+ }
+ else if(!(strcmp(patvar, "stat_boy"))){
+ if(peRuleStatVal(tstr, &new_pat->patgrp->stat_boy)){
+ free_pat(&new_pat);
+ tcl_error++;
+ }
+ }
+ else if(!(strcmp(patvar, "age"))){
+ new_pat->patgrp->age = parse_intvl(tstr);
+ }
+ else if(!(strcmp(patvar, "size"))){
+ new_pat->patgrp->size = parse_intvl(tstr);
+ }
+ else if(!(strcmp(patvar, "score"))){
+ new_pat->patgrp->score = parse_intvl(tstr);
+ }
+ else if(!(strcmp(patvar, "addrbook"))){
+ if(tstr){
+ if(!strcmp(tstr, "either"))
+ new_pat->patgrp->inabook = IAB_EITHER;
+ else if(!strcmp(tstr, "yes"))
+ new_pat->patgrp->inabook = IAB_YES;
+ else if(!strcmp(tstr, "no"))
+ new_pat->patgrp->inabook = IAB_NO;
+ else if(!strcmp(tstr, "yesspecific"))
+ new_pat->patgrp->inabook = IAB_SPEC_YES;
+ else if(!strcmp(tstr, "nospecific"))
+ new_pat->patgrp->inabook = IAB_SPEC_NO;
+ else
+ tcl_error++;
+ }
+ else
+ tcl_error++;
+
+ if(tcl_error)
+ free_pat(&new_pat);
+ }
+ else if(!(strcmp(patvar, "specificabook"))){
+ new_pat->patgrp->abooks = string_to_pattern(tstr);
+ }
+ else if(!(strcmp(patvar, "headers"))){
+ ARBHDR_S **ahp;
+ int nHdrList, nHdrPair, n;
+ Tcl_Obj **objHdrList, **objHdrPair;
+
+ Tcl_ListObjGetElements(interp, objPatEmnt[1], &nHdrList, &objHdrList);
+
+ for(ahp = &new_pat->patgrp->arbhdr; *ahp; ahp = &(*ahp)->next)
+ ;
+
+ for (n = 0; n < nHdrList; n++){
+ char *hdrfld;
+ char *hdrval;
+
+ Tcl_ListObjGetElements(interp, objHdrList[n], &nHdrPair, &objHdrPair);
+ if(nHdrPair != 2)
+ continue;
+
+ hdrfld = Tcl_GetStringFromObj(objHdrPair[0], NULL);
+ hdrval = Tcl_GetStringFromObj(objHdrPair[1], NULL);
+
+ if(hdrfld){
+ *ahp = (ARBHDR_S *) fs_get(sizeof(ARBHDR_S));
+ memset(*ahp, 0, sizeof(ARBHDR_S));
+
+ (*ahp)->field = cpystr(hdrfld);
+ if(hdrval){
+ (*ahp)->p = string_to_pattern(hdrval);
+ }
+ else
+ (*ahp)->isemptyval = 1;
+
+ ahp = &(*ahp)->next;
+ }
+ }
+ }
+ else{
+ free_pat(&new_pat);
+ tcl_error++;
+ }
+
+ if(tstr)
+ fs_give((void **) &tstr);
+
+ if(tcl_error)
+ return(TCL_ERROR);
+ }
+
+ if((new_pat->patgrp->inabook & (IAB_SPEC_YES | IAB_SPEC_NO)) == 0
+ && new_pat->patgrp->abooks)
+ free_pattern(&new_pat->patgrp->abooks);
+
+ if(new_pat->patgrp->fldr_type != FLDR_SPECIFIC && new_pat->patgrp->folder)
+ free_pattern(&new_pat->patgrp->folder);
+
+ /* set up the action */
+ if(!(strcmp(rule, "filter")))
+ new_pat->action->is_a_filter = 1;
+ else if(!(strcmp(rule, "role")))
+ new_pat->action->is_a_role = 1;
+ else if(!(strcmp(rule, "score")))
+ new_pat->action->is_a_score = 1;
+ else if(!(strcmp(rule, "indexcolor")))
+ new_pat->action->is_a_incol = 1;
+ else{
+ free_pat(&new_pat);
+ return(TCL_ERROR);
+ }
+
+ for(i = 0; i < nAct; i++){
+ Tcl_ListObjGetElements(interp, objAct[i], &nActEmnt, &objActEmnt);
+ if(nActEmnt !=2){
+ free_pat(&new_pat);
+ return(TCL_ERROR);
+ }
+
+ actvar = Tcl_GetStringFromObj(objActEmnt[0], NULL);
+ actval = Tcl_GetStringFromObj(objActEmnt[1], NULL);
+ if(!actvar || !actval){
+ free_pat(&new_pat);
+ return(TCL_ERROR);
+ }
+
+ if(new_pat->action->is_a_filter && !(strcmp(actvar, "action"))){
+ if(!strcmp(actval, "delete"))
+ new_pat->action->kill = 1;
+ else if(!strcmp(actval, "move"))
+ new_pat->action->kill = 0;
+ else{
+ free_pat(&new_pat);
+ return(TCL_ERROR);
+ }
+ }
+ else if(new_pat->action->is_a_filter && !(strcmp(actvar, "folder"))){
+ tstr = cpystr(actval);
+ removing_leading_and_trailing_white_space(tstr);
+ if(!(*tstr)) fs_give((void **)&tstr);
+ new_pat->action->folder = string_to_pattern(tstr);
+ if(tstr) fs_give((void **)&tstr);
+ }
+ else if(new_pat->action->is_a_filter && !(strcmp(actvar, "moind"))){
+ if(!strcmp(actval, "1"))
+ new_pat->action->move_only_if_not_deleted = 1;
+ else if(!strcmp(actval, "0"))
+ new_pat->action->move_only_if_not_deleted = 0;
+ else{
+ free_pat(&new_pat);
+ return(TCL_ERROR);
+ }
+ }
+ else if(new_pat->action->is_a_incol && !(strcmp(actvar, "fg"))){
+ char asciicolor[256];
+
+ if(ascii_colorstr(asciicolor, actval) == 0) {
+ if(!new_pat->action->incol){
+ new_pat->action->incol = new_color_pair(asciicolor,NULL);
+ }
+ else
+ snprintf(new_pat->action->incol->fg,
+ sizeof(new_pat->action->incol->fg), "%s", asciicolor);
+ }
+ }
+ else if(new_pat->action->is_a_incol && !(strcmp(actvar, "bg"))){
+ char asciicolor[256];
+
+ if(ascii_colorstr(asciicolor, actval) == 0) {
+ if(!new_pat->action->incol){
+ new_pat->action->incol = new_color_pair(NULL, asciicolor);
+ }
+ else
+ snprintf(new_pat->action->incol->bg,
+ sizeof(new_pat->action->incol->bg), "%s", asciicolor);
+ }
+ }
+ else if(new_pat->action->is_a_score && !(strcmp(actvar, "scoreval"))){
+ long scoreval = (long) atoi(actval);
+
+ if(scoreval >= SCORE_MIN && scoreval <= SCORE_MAX)
+ new_pat->action->scoreval = scoreval;
+ }
+ else if(new_pat->action->is_a_score && !(strcmp(actvar, "scorehdr"))){
+ HEADER_TOK_S *hdrtok;
+
+ if((hdrtok = stringform_to_hdrtok(actval)) != NULL)
+ new_pat->action->scorevalhdrtok = hdrtok;
+ }
+ else{
+ free_pat(&new_pat);
+ return(TCL_ERROR);
+ }
+ }
+
+ if(new_pat->action->is_a_filter && new_pat->action->kill && new_pat->action->folder)
+ fs_give((void **)&new_pat->action->folder);
+ else if(new_pat->action->is_a_filter && new_pat->action->kill == 0 && new_pat->action->folder == 0){
+ free_pat(&new_pat);
+ Tcl_SetResult(interp, "No folder set for Move", TCL_VOLATILE);
+ return(TCL_OK);
+ }
+ }
+
+ if(aflags & RS_RULE_EDIT)
+ rv = edit_pattern(new_pat, rno, rflags);
+ else if(aflags & RS_RULE_ADD)
+ rv = add_pattern(new_pat, rflags);
+ else if(aflags & RS_RULE_DELETE)
+ rv = delete_pattern(rno, rflags);
+ else if(aflags & RS_RULE_SHUFFUP)
+ rv = shuffle_pattern(rno, 1, rflags);
+ else if(aflags & RS_RULE_SHUFFDOWN)
+ rv = shuffle_pattern(rno, -1, rflags);
+ else
+ rv = 1;
+
+ return(rv ? TCL_ERROR : TCL_OK);
+}
+
+
+#if 0
+ADDRESS *
+peAEToAddress(AdrBk_Entry *ae)
+{
+ char *list, *l1, *l2;
+ int length;
+ BuildTo bldto;
+ ADDRESS *addr = NULL;
+
+ if(ae->tag == List){
+ length = 0;
+ for(l2 = ae->addr.list; *l2; l2++)
+ length += (strlen(*l2) + 1);
+
+ list = (char *) fs_get(length + 1);
+ list[0] = '\0';
+ l1 = list;
+ for(l2 = ae->addr.list; *l2; l2++){
+ if(l1 != list && l1-list < length+1)
+ *l1++ = ',';
+
+ strncpy(l1, *l2, length+1-(l1-list));
+ l1 += strlen(l1);
+ }
+
+ list[length] = '\0';
+
+ bldto.type = Str;
+ bldto.arg.str = list;
+ adr2 = expand_address(bldto, userdomain, localdomain,
+ loop_detected, fcc, did_set,
+ lcc, error, 1, simple_verify,
+ mangled);
+
+ fs_give((void **) &list);
+ }
+ else if(ae->tag == Single){
+ if(strucmp(ae->addr.addr, a->mailbox)){
+ bldto.type = Str;
+ bldto.arg.str = ae->addr.addr;
+ adr2 = expand_address(bldto, userdomain,
+ localdomain, loop_detected,
+ fcc, did_set, lcc,
+ error, 1, simple_verify,
+ mangled);
+ }
+ else{
+ /*
+ * A loop within plain single entry is ignored.
+ * Set up so later code thinks we expanded.
+ */
+ adr2 = mail_newaddr();
+ adr2->mailbox = cpystr(ae->addr.addr);
+ adr2->host = cpystr(userdomain);
+ adr2->adl = cpystr(a->adl);
+ }
+ }
+
+ /*
+ * Personal names: If the expanded address has a personal
+ * name and the address book entry is a list with a fullname,
+ * tack the full name from the address book on in front.
+ * This mainly occurs with a distribution list where the
+ * list has a full name, and the first person in the list also
+ * has a full name.
+ *
+ * This algorithm doesn't work very well if lists are
+ * included within lists, but it's not clear what would
+ * be better.
+ */
+ if(ae->fullname && ae->fullname[0]){
+ if(adr2->personal && adr2->personal[0]){
+ if(ae->tag == List){
+ /* combine list name and existing name */
+ char *name;
+
+ if(!simple_verify){
+ size_t l;
+ l = strlen(adr2->personal) + strlen(ae->fullname) + 4;
+ name = (char *)fs_get((l+1) * sizeof(char));
+ snprintf(name, l+1, "%s -- %s", ae->fullname,
+ adr2->personal);
+ fs_give((void **)&adr2->personal);
+ adr2->personal = name;
+ }
+ }
+ else{
+ /* replace with nickname fullname */
+ fs_give((void **)&adr2->personal);
+ adr2->personal = adrbk_formatname(ae->fullname,
+ NULL, NULL);
+ }
+ }
+ else{
+ if(abe-p>tag != List || !simple_verify){
+ if(adr2->personal)
+ fs_give((void **)&adr2->personal);
+
+ adr2->personal = adrbk_formatname(abe->fullname,
+ NULL, NULL);
+ }
+ }
+ }
+
+ return(addr);
+}
+
+
+
+char *
+peAEFcc(AdrBk_Entry *ae)
+{
+ char *fcc = NULL;
+
+ if(ae->fcc && ae->fcc[0]){
+
+ if(!strcmp(ae->fcc, "\"\""))
+ fcc = cpystr("");
+ else
+ fcc = cpystr(ae->fcc);
+
+ }
+ else if(ae->nickname && ae->nickname[0] &&
+ (ps_global->fcc_rule == FCC_RULE_NICK ||
+ ps_global->fcc_rule == FCC_RULE_NICK_RECIP)){
+ /*
+ * else if fcc-rule=fcc-by-nickname, use that
+ */
+
+ fcc = cpystr(ae->nickname);
+ }
+
+ return(fcc);
+}
+#endif
+
+
+PINEFIELD *
+peCustomHdrs(void)
+{
+ extern PINEFIELD *parse_custom_hdrs(char **, CustomType);
+
+ return(parse_custom_hdrs(ps_global->VAR_CUSTOM_HDRS, UseAsDef));
+}
+
+
+
+/*
+ * PEClistCmd - Collection list editing tools
+ */
+int
+PEClistCmd(ClientData clientData, Tcl_Interp *interp, int objc, Tcl_Obj *CONST objv[])
+{
+ char *err = "Unknown PEClist request";
+
+ dprint((2, "PEClistCmd"));
+
+ if(objc == 1){
+ Tcl_WrongNumArgs(interp, 1, objv, "cmd ?args?");
+ }
+ else{
+ char *s1 = Tcl_GetStringFromObj(objv[1], NULL);
+
+ if(s1){
+ if(objc == 3){ /* delete */
+ if(!strcmp(s1, "delete")){
+ int cl, i, n, deln;
+ char **newl;
+ CONTEXT_S *del_ctxt, *tmp_ctxt, *new_ctxt;
+
+ if(Tcl_GetIntFromObj(interp, objv[2], &cl) == TCL_ERROR){
+ Tcl_SetResult(interp,
+ "cledit malformed: first arg must be int",
+ TCL_VOLATILE);
+ return(TCL_ERROR);
+ }
+ for(i = 0, del_ctxt = ps_global->context_list;
+ del_ctxt && i < cl; i++, del_ctxt = del_ctxt->next);
+ if(!del_ctxt) return(TCL_ERROR);
+ for(n = 0; del_ctxt->var.v->current_val.l[n]; n++);
+ n--;
+ newl = (char **) fs_get((n + 1) * sizeof(char *));
+ newl[n] = NULL;
+ deln = del_ctxt->var.i;
+ for(i = 0; del_ctxt->var.v->current_val.l[i]; i++){
+ if(i < deln)
+ newl[i] = cpystr(del_ctxt->var.v->current_val.l[i]);
+ else if(i > deln)
+ newl[i-1] = cpystr(del_ctxt->var.v->current_val.l[i]);
+ }
+ n = set_variable_list(del_ctxt->var.v - ps_global->vars,
+ *newl ? newl : NULL, TRUE, Main);
+ free_list_array(&newl);
+ set_current_val(del_ctxt->var.v, TRUE, FALSE);
+ if(n){
+ Tcl_SetResult(interp,
+ "Error saving changes",
+ TCL_VOLATILE);
+ return TCL_OK;
+ }
+ for(tmp_ctxt = del_ctxt->next; tmp_ctxt && tmp_ctxt->var.v ==
+ del_ctxt->var.v; tmp_ctxt = tmp_ctxt->next)
+ tmp_ctxt->var.i--;
+ if((tmp_ctxt = del_ctxt->next) != NULL)
+ tmp_ctxt->prev = del_ctxt->prev;
+ if((tmp_ctxt = del_ctxt->prev) != NULL)
+ tmp_ctxt->next= del_ctxt->next;
+ if(!del_ctxt->prev && !del_ctxt->next){
+ new_ctxt = new_context(del_ctxt->var.v->current_val.l[0], NULL);
+ ps_global->context_list = new_ctxt;
+ if(!new_ctxt->var.v)
+ new_ctxt->var = del_ctxt->var;
+ }
+ else if(ps_global->context_list == del_ctxt){
+ ps_global->context_list = del_ctxt->next;
+ if(!ps_global->context_list)
+ return TCL_ERROR; /* this shouldn't happen */
+ }
+ if(ps_global->context_last == del_ctxt)
+ ps_global->context_last = NULL;
+ if(ps_global->context_current == del_ctxt){
+ strncpy(ps_global->cur_folder,
+ ps_global->mail_stream->mailbox,
+ sizeof(ps_global->cur_folder));
+ ps_global->cur_folder[sizeof(ps_global->cur_folder)-1] = '\0';
+ ps_global->context_current = ps_global->context_list;
+ }
+ del_ctxt->prev = NULL;
+ del_ctxt->next = NULL;
+ free_context(&del_ctxt);
+ init_inbox_mapping(ps_global->VAR_INBOX_PATH,
+ ps_global->context_list);
+ return TCL_OK;
+ }
+ else if(!strcmp(s1, "shuffdown")){
+ int cl, i, shn, n;
+ CONTEXT_S *sh_ctxt, *nsh_ctxt, *tctxt;
+ char **newl, *tmpch;
+
+ if(Tcl_GetIntFromObj(interp, objv[2], &cl) == TCL_ERROR){
+ Tcl_SetResult(interp,
+ "cledit malformed: first arg must be int",
+ TCL_VOLATILE);
+ return(TCL_ERROR);
+ }
+ for(sh_ctxt = ps_global->context_list, i = 0;
+ sh_ctxt && i < cl ; i++, sh_ctxt = sh_ctxt->next);
+ if(!sh_ctxt || !sh_ctxt->next){
+ Tcl_SetResult(interp,
+ "invalid context list number",
+ TCL_VOLATILE);
+ return TCL_ERROR;
+ }
+ if(sh_ctxt->var.v == sh_ctxt->next->var.v){
+ shn = sh_ctxt->var.i;
+ for(n = 0; sh_ctxt->var.v->current_val.l[n]; n++);
+ newl = (char **) fs_get((n + 1) * sizeof(char *));
+ newl[n] = NULL;
+ for(i = 0; sh_ctxt->var.v->current_val.l[i]; i++){
+ if(i == shn)
+ newl[i] = cpystr(sh_ctxt->var.v->current_val.l[i+1]);
+ else if(i == shn + 1)
+ newl[i] = cpystr(sh_ctxt->var.v->current_val.l[i-1]);
+ else
+ newl[i] = cpystr(sh_ctxt->var.v->current_val.l[i]);
+ }
+ n = set_variable_list(sh_ctxt->var.v - ps_global->vars,
+ newl, TRUE, Main);
+ free_list_array(&newl);
+ set_current_val(sh_ctxt->var.v, TRUE, FALSE);
+ if(n){
+ Tcl_SetResult(interp,
+ "Error saving changes",
+ TCL_VOLATILE);
+ return TCL_OK;
+ }
+ nsh_ctxt = sh_ctxt->next;
+ nsh_ctxt->var.i--;
+ sh_ctxt->var.i++;
+ }
+ else{
+ nsh_ctxt = sh_ctxt->next;
+ shn = sh_ctxt->var.i;
+ tmpch = cpystr(sh_ctxt->var.v->current_val.l[shn]);
+ for(n = 0; sh_ctxt->var.v->current_val.l[n]; n++);
+ n--;
+ newl = (char **) fs_get((n + 1) * sizeof(char *));
+ newl[n] = NULL;
+ for(i = 0; sh_ctxt->var.v->current_val.l[i+1]; i++)
+ newl[i] = cpystr(sh_ctxt->var.v->current_val.l[i]);
+ n = set_variable_list(sh_ctxt->var.v - ps_global->vars,
+ newl, FALSE, Main);
+ free_list_array(&newl);
+ set_current_val(sh_ctxt->var.v, TRUE, FALSE);
+ for(n = 0; nsh_ctxt->var.v->current_val.l[n]; n++);
+ n++;
+ newl = (char **) fs_get((n + 1) * sizeof(char *));
+ newl[n] = NULL;
+ newl[0] = cpystr(nsh_ctxt->var.v->current_val.l[0]);
+ newl[1] = tmpch;
+ for(i = 2; nsh_ctxt->var.v->current_val.l[i-1]; i++)
+ newl[i] = cpystr(nsh_ctxt->var.v->current_val.l[i-1]);
+ n = set_variable_list(nsh_ctxt->var.v - ps_global->vars,
+ newl, TRUE, Main);
+ free_list_array(&newl);
+ set_current_val(nsh_ctxt->var.v, TRUE, FALSE);
+ sh_ctxt->var.v = nsh_ctxt->var.v;
+ sh_ctxt->var.i = 1;
+ /* this for loop assumes that there are only two variable lists,
+ * folder-collections and news-collections, a little more will
+ * have to be done if we want to accomodate for the INHERIT
+ * option introduced in 4.30.
+ */
+ for(tctxt = nsh_ctxt->next; tctxt; tctxt = tctxt->next)
+ tctxt->var.i++;
+ }
+ if(sh_ctxt->prev) sh_ctxt->prev->next = nsh_ctxt;
+ nsh_ctxt->prev = sh_ctxt->prev;
+ sh_ctxt->next = nsh_ctxt->next;
+ nsh_ctxt->next = sh_ctxt;
+ sh_ctxt->prev = nsh_ctxt;
+ if(sh_ctxt->next) sh_ctxt->next->prev = sh_ctxt;
+ if(ps_global->context_list == sh_ctxt)
+ ps_global->context_list = nsh_ctxt;
+ init_inbox_mapping(ps_global->VAR_INBOX_PATH,
+ ps_global->context_list);
+ return TCL_OK;
+ }
+ else if(!strcmp(s1, "shuffup")){
+ int cl, i, shn, n;
+ CONTEXT_S *sh_ctxt, *psh_ctxt, *tctxt;
+ char **newl, *tmpch;
+
+ if(Tcl_GetIntFromObj(interp, objv[2], &cl) == TCL_ERROR){
+ Tcl_SetResult(interp,
+ "cledit malformed: first arg must be int",
+ TCL_VOLATILE);
+ return(TCL_ERROR);
+ }
+ for(sh_ctxt = ps_global->context_list, i = 0;
+ sh_ctxt && i < cl ; i++, sh_ctxt = sh_ctxt->next);
+ if(!sh_ctxt || !sh_ctxt->prev){
+ Tcl_SetResult(interp,
+ "invalid context list number",
+ TCL_VOLATILE);
+ return TCL_ERROR;
+ }
+ if(sh_ctxt->var.v == sh_ctxt->prev->var.v){
+ shn = sh_ctxt->var.i;
+ for(n = 0; sh_ctxt->var.v->current_val.l[n]; n++);
+ newl = (char **) fs_get((n + 1) * sizeof(char *));
+ newl[n] = NULL;
+ for(i = 0; sh_ctxt->var.v->current_val.l[i]; i++){
+ if(i == shn)
+ newl[i] = cpystr(sh_ctxt->var.v->current_val.l[i-1]);
+ else if(i == shn - 1)
+ newl[i] = cpystr(sh_ctxt->var.v->current_val.l[i+1]);
+ else
+ newl[i] = cpystr(sh_ctxt->var.v->current_val.l[i]);
+ }
+ i = set_variable_list(sh_ctxt->var.v - ps_global->vars,
+ newl, TRUE, Main);
+ free_list_array(&newl);
+ set_current_val(sh_ctxt->var.v, TRUE, FALSE);
+ if(i){
+ Tcl_SetResult(interp,
+ "Error saving changes",
+ TCL_VOLATILE);
+ return TCL_OK;
+ }
+ psh_ctxt = sh_ctxt->prev;
+ psh_ctxt->var.i++;
+ sh_ctxt->var.i--;
+ }
+ else{
+ psh_ctxt = sh_ctxt->prev;
+ shn = sh_ctxt->var.i;
+ tmpch = cpystr(sh_ctxt->var.v->current_val.l[shn]);
+ for(n = 0; sh_ctxt->var.v->current_val.l[n]; n++);
+ n--;
+ newl = (char **) fs_get((n + 1) * sizeof(char *));
+ newl[n] = NULL;
+ for(i = 1; sh_ctxt->var.v->current_val.l[i]; i++)
+ newl[i-1] = cpystr(sh_ctxt->var.v->current_val.l[i]);
+ i = set_variable_list(sh_ctxt->var.v - ps_global->vars,
+ newl, FALSE, Main);
+ free_list_array(&newl);
+ if(i){
+ Tcl_SetResult(interp,
+ "Error saving changes",
+ TCL_VOLATILE);
+ return TCL_OK;
+ }
+ set_current_val(sh_ctxt->var.v, TRUE, FALSE);
+ for(n = 0; psh_ctxt->var.v->current_val.l[n]; n++);
+ n++;
+ newl = (char **) fs_get((n + 1) * sizeof(char *));
+ newl[n] = NULL;
+ for(i = 0; psh_ctxt->var.v->current_val.l[i+1]; i++)
+ newl[i] = cpystr(psh_ctxt->var.v->current_val.l[i]);
+ newl[i++] = tmpch;
+ newl[i] = cpystr(psh_ctxt->var.v->current_val.l[i-1]);
+ i = set_variable_list(psh_ctxt->var.v - ps_global->vars,
+ newl, TRUE, Main);
+ free_list_array(&newl);
+ if(i){
+ Tcl_SetResult(interp,
+ "Error saving changes",
+ TCL_VOLATILE);
+ return TCL_OK;
+ }
+ set_current_val(psh_ctxt->var.v, TRUE, FALSE);
+ for(tctxt = sh_ctxt->next ; tctxt; tctxt = tctxt->next)
+ tctxt->var.i--;
+ sh_ctxt->var.v = psh_ctxt->var.v;
+ sh_ctxt->var.i = n - 2;
+ /* There MUST be at least 2 collections in the list */
+ psh_ctxt->var.i++;
+ }
+ if(sh_ctxt->next) sh_ctxt->next->prev = psh_ctxt;
+ psh_ctxt->next = sh_ctxt->next;
+ sh_ctxt->prev = psh_ctxt->prev;
+ psh_ctxt->prev = sh_ctxt;
+ sh_ctxt->next = psh_ctxt;
+ if(sh_ctxt->prev) sh_ctxt->prev->next = sh_ctxt;
+ if(ps_global->context_list == psh_ctxt)
+ ps_global->context_list = sh_ctxt;
+ init_inbox_mapping(ps_global->VAR_INBOX_PATH,
+ ps_global->context_list);
+ return TCL_OK;
+ }
+ }
+ else if(objc == 7){
+ if(!strcmp(s1, "edit") || !strcmp(s1, "add")){
+ int cl, quotes_needed = 0, i, add = 0, n = 0;
+ char *nick, *server, *path, *view,
+ context_buf[MAILTMPLEN*4], **newl;
+ CONTEXT_S *new_ctxt, *tmp_ctxt;
+
+ if(!strcmp(s1, "add")) add = 1;
+
+ if(Tcl_GetIntFromObj(interp, objv[2], &cl) == TCL_ERROR){
+ Tcl_SetResult(interp,
+ "cledit malformed: first arg must be int",
+ TCL_VOLATILE);
+ return(TCL_ERROR);
+ }
+ if(!(nick = Tcl_GetStringFromObj(objv[3], NULL))){
+ Tcl_SetResult(interp,
+ "Error1",
+ TCL_VOLATILE);
+ return TCL_ERROR;
+ }
+ if(!(server = Tcl_GetStringFromObj(objv[4], NULL))){
+ Tcl_SetResult(interp,
+ "Error2",
+ TCL_VOLATILE);
+ return TCL_ERROR;
+ }
+ if(!(path = Tcl_GetStringFromObj(objv[5], NULL))){
+ Tcl_SetResult(interp,
+ "Error3",
+ TCL_VOLATILE);
+ return TCL_ERROR;
+ }
+ if(!(view = Tcl_GetStringFromObj(objv[6], NULL))){
+ Tcl_SetResult(interp,
+ "Error4",
+ TCL_VOLATILE);
+ return TCL_ERROR;
+ }
+ removing_leading_and_trailing_white_space(nick);
+ removing_leading_and_trailing_white_space(server);
+ removing_leading_and_trailing_white_space(path);
+ removing_leading_and_trailing_white_space(view);
+ if(strchr(nick, ' '))
+ quotes_needed = 1;
+ if(strlen(nick)+strlen(server)+strlen(path)+strlen(view) >
+ MAILTMPLEN * 4 - 20) { /* for good measure */
+ Tcl_SetResult(interp,
+ "info too long",
+ TCL_VOLATILE);
+
+ return TCL_ERROR;
+ }
+ if(3 + strlen(nick) + strlen(server) + strlen(path) +
+ strlen(view) > MAILTMPLEN + 4){
+ Tcl_SetResult(interp,
+ "collection fields too long",
+ TCL_VOLATILE);
+ return(TCL_OK);
+ }
+ snprintf(context_buf, sizeof(context_buf), "%s%s%s%s%s%s[%s]", quotes_needed ?
+ "\"" : "", nick, quotes_needed ? "\"" : "",
+ strlen(nick) ? " " : "",
+ server, path, view);
+ new_ctxt = new_context(context_buf, NULL);
+ if(!add){
+ for(tmp_ctxt = ps_global->context_list, i = 0;
+ tmp_ctxt && i < cl; i++, tmp_ctxt = tmp_ctxt->next);
+ if(!tmp_ctxt){
+ Tcl_SetResult(interp,
+ "invalid context list number",
+ TCL_VOLATILE);
+ return TCL_ERROR;
+ }
+ new_ctxt->next = tmp_ctxt->next;
+ new_ctxt->prev = tmp_ctxt->prev;
+ if(tmp_ctxt->prev && tmp_ctxt->prev->next == tmp_ctxt)
+ tmp_ctxt->prev->next = new_ctxt;
+ if(tmp_ctxt->next && tmp_ctxt->next->prev == tmp_ctxt)
+ tmp_ctxt->next->prev = new_ctxt;
+ if(ps_global->context_list == tmp_ctxt)
+ ps_global->context_list = new_ctxt;
+ if(ps_global->context_current == tmp_ctxt){
+ strncpy(ps_global->cur_folder,
+ ps_global->mail_stream->mailbox,
+ sizeof(ps_global->cur_folder));
+ ps_global->cur_folder[sizeof(ps_global->cur_folder)-1] = '\0';
+ ps_global->context_current = new_ctxt;
+ }
+ if(ps_global->context_last == tmp_ctxt)
+ ps_global->context_last = new_ctxt;
+ new_ctxt->var = tmp_ctxt->var;
+ tmp_ctxt->next = tmp_ctxt->prev = NULL;
+ free_context(&tmp_ctxt);
+ }
+ else {
+ for(tmp_ctxt = ps_global->context_list;
+ tmp_ctxt->next; tmp_ctxt = tmp_ctxt->next);
+ new_ctxt->prev = tmp_ctxt;
+ tmp_ctxt->next = new_ctxt;
+ new_ctxt->var.v = tmp_ctxt->var.v;
+ new_ctxt->var.i = tmp_ctxt->var.i + 1;
+ }
+ if(!new_ctxt->var.v){
+ Tcl_SetResult(interp,
+ "Error5",
+ TCL_VOLATILE);
+ return TCL_ERROR;
+ }
+ for(n = 0; new_ctxt->var.v->current_val.l[n]; n++);
+ if(add) n++;
+ newl = (char **) fs_get((n + 1) * sizeof(char *));
+ newl[n] = NULL;
+ for(n = 0; new_ctxt->var.v->current_val.l[n]; n++)
+ newl[n] = (n == new_ctxt->var.i)
+ ? cpystr(context_buf)
+ : cpystr(new_ctxt->var.v->current_val.l[n]);
+ if(add) newl[n++] = cpystr(context_buf);
+ n = set_variable_list(new_ctxt->var.v - ps_global->vars,
+ newl, TRUE, Main);
+ free_list_array(&newl);
+ set_current_val(new_ctxt->var.v, TRUE, FALSE);
+ init_inbox_mapping(ps_global->VAR_INBOX_PATH,
+ ps_global->context_list);
+ if(n){
+ Tcl_SetResult(interp,
+ "Error saving changes",
+ TCL_VOLATILE);
+ return TCL_OK;
+ }
+ return TCL_OK;
+
+ }
+ }
+ }
+ }
+ Tcl_SetResult(interp, err, TCL_STATIC);
+ return(TCL_ERROR);
+}
+
+
+/*
+ * peTakeaddr - Take Address
+ */
+int
+peTakeaddr(Tcl_Interp *interp, imapuid_t uid, int objc, Tcl_Obj **objv)
+{
+ TA_S *talist = NULL, *current, *head;
+ Tcl_Obj *itemObj, *secObj = NULL, *resObj = NULL;
+ int anum = 0;
+
+ mn_set_cur(sp_msgmap(ps_global->mail_stream), peMessageNumber(uid));
+
+ if(set_up_takeaddr('a', ps_global, sp_msgmap(ps_global->mail_stream),
+ &talist, &anum, TA_NOPROMPT, NULL) < 0
+ || (talist == NULL)){
+ Tcl_SetResult(interp,
+ "Take address failed to set up",
+ TCL_VOLATILE);
+ return(TCL_ERROR);
+ }
+
+ for(head = talist ; head->prev; head = head->prev);
+ /*
+ * Return value will be of the form:
+ * {
+ * { "line to print",
+ * {"personal", "mailbox", "host"} # addr
+ * {"nick", "fullname", "fcc", "comment"} # suggested
+ * }
+ * ...
+ * }
+ *
+ * The two list items will be empty if that line is
+ * just informational.
+ */
+ itemObj = Tcl_NewListObj(0, NULL);
+ for(current = head; current ; current = current->next){
+ if(current->skip_it && !current->print) continue;
+ secObj = Tcl_NewListObj(0, NULL);
+ if(Tcl_ListObjAppendElement(interp, secObj,
+ Tcl_NewStringObj(current->strvalue,-1)) != TCL_OK)
+ return(TCL_ERROR);
+ resObj = Tcl_NewListObj(0, NULL);
+ /* append the address information */
+ if(current->addr && !current->print){
+ if(Tcl_ListObjAppendElement(interp, resObj,
+ Tcl_NewStringObj(current->addr->personal
+ ? current->addr->personal
+ : "", -1)) != TCL_OK)
+ return(TCL_ERROR);
+ if(Tcl_ListObjAppendElement(interp, resObj,
+ Tcl_NewStringObj(current->addr->mailbox
+ ? current->addr->mailbox
+ : "", -1)) != TCL_OK)
+ return(TCL_ERROR);
+ if(Tcl_ListObjAppendElement(interp, resObj,
+ Tcl_NewStringObj(current->addr->host
+ ? current->addr->host
+ : "", -1)) != TCL_OK)
+ return(TCL_ERROR);
+ }
+ if(Tcl_ListObjAppendElement(interp, secObj,
+ resObj) != TCL_OK)
+ return(TCL_ERROR);
+ resObj = Tcl_NewListObj(0, NULL);
+ /* append the suggested possible entries */
+ if(!current->print
+ && (current->nickname || current->fullname
+ || current->fcc || current->comment)){
+ if(Tcl_ListObjAppendElement(interp, resObj,
+ Tcl_NewStringObj(current->nickname
+ ? current->nickname
+ : "", -1)) != TCL_OK)
+ return(TCL_ERROR);
+ if(Tcl_ListObjAppendElement(interp, resObj,
+ Tcl_NewStringObj(current->fullname
+ ? current->fullname
+ : "", -1)) != TCL_OK)
+ return(TCL_ERROR);
+ if(Tcl_ListObjAppendElement(interp, resObj,
+ Tcl_NewStringObj(current->fcc
+ ? current->fcc
+ : "", -1)) != TCL_OK)
+ return(TCL_ERROR);
+ if(Tcl_ListObjAppendElement(interp, resObj,
+ Tcl_NewStringObj(current->comment
+ ? current->comment
+ : "", -1)) != TCL_OK)
+ return(TCL_ERROR);
+ }
+ if(Tcl_ListObjAppendElement(interp, secObj, resObj) != TCL_OK)
+ return(TCL_ERROR);
+ if(Tcl_ListObjAppendElement(interp, Tcl_GetObjResult(interp),
+ secObj) != TCL_OK)
+ return(TCL_ERROR);
+ }
+
+ free_talines(&talist);
+ return(TCL_OK);
+}
+
+
+/*
+ * peTakeFrom - Take only From Address
+ */
+int
+peTakeFrom(Tcl_Interp *interp, imapuid_t uid, int objc, Tcl_Obj **objv)
+{
+ char *err = NULL;
+ Tcl_Obj *objItem;
+ ADDRESS *ap;
+ ENVELOPE *env;
+ long rawno;
+
+ /*
+ * Return value will be of the form:
+ * {
+ * { "line to print",
+ * {"personal", "mailbox", "host"} # addr
+ * {"nick", "fullname", "fcc", "comment"} # suggested
+ * }
+ * ...
+ * }
+ *
+ * The two list items will be empty if that line is
+ * just informational.
+ */
+
+ if(uid){
+ if((env = pine_mail_fetchstructure(ps_global->mail_stream,
+ rawno = peSequenceNumber(uid),
+ NULL))){
+ /* append the address information */
+ for(ap = env->from; ap; ap = ap->next){
+ objItem = Tcl_NewListObj(0, NULL);
+ /* append EMPTY "line to print" */
+ if(Tcl_ListObjAppendElement(interp, objItem, Tcl_NewStringObj("",-1)) != TCL_OK)
+ return(TCL_ERROR);
+
+ /* append address info */
+ peAppListF(interp, objItem, "%s%s%s",
+ ap->personal ? (char *) rfc1522_decode_to_utf8((unsigned char *) tmp_20k_buf, SIZEOF_20KBUF, ap->personal) : "",
+ ap->mailbox ? ap->mailbox : "",
+ ap->host ? ap->host : "");
+
+ /* append suggested info */
+ peAddSuggestedContactInfo(interp, objItem, ap);
+
+ if(Tcl_ListObjAppendElement(interp, Tcl_GetObjResult(interp), objItem) != TCL_OK)
+ return(TCL_ERROR);
+ }
+
+ return(TCL_OK);
+ }
+ else
+ err = ps_global->last_error;
+ }
+ else
+ err = "Invalid UID";
+
+ return(TCL_ERROR);
+}
+
+
+int
+peAddSuggestedContactInfo(Tcl_Interp *interp, Tcl_Obj *lobjp, ADDRESS *addr)
+{
+ char *nick = NULL, *full = NULL, *fcc = NULL, *comment = NULL;
+
+ get_contactinfo_from_addr(addr, &nick, &full, &fcc, &comment);
+
+ peAppListF(interp, lobjp, "%s%s%s%s",
+ nick ? nick : "",
+ full ? full : "",
+ fcc ? fcc : "",
+ comment ? comment : "");
+
+ if(nick)
+ fs_give((void **) &nick);
+
+ if(full)
+ fs_give((void **) &full);
+
+ if(fcc)
+ fs_give((void **) &fcc);
+
+ if(comment)
+ fs_give((void **) &comment);
+}
+
+
+/* * * * * * * * * Status message ring management * * * * * * * * * * * * */
+
+STATMSG_S *
+sml_newmsg(int priority, char *text)
+{
+ static long id = 1;
+ STATMSG_S *smp;
+
+ smp = (STATMSG_S *) fs_get(sizeof(STATMSG_S));
+ memset(smp, 0, sizeof(STATMSG_S));
+ smp->id = id++;
+ smp->posted = time(0);
+ smp->type = priority;
+ smp->text = cpystr(text);
+ return(smp);
+}
+
+
+void
+sml_addmsg(int priority, char *text)
+{
+ STATMSG_S *smp = sml_newmsg(priority, text);
+
+ if(peStatList){
+ smp->next = peStatList;
+ peStatList = smp;
+ }
+ else
+ peStatList = smp;
+}
+
+
+char **
+sml_getmsgs(void)
+{
+ int n;
+ STATMSG_S *smp;
+ char **retstrs = NULL, **tmpstrs;
+
+ for(n = 0, smp = peStatList; smp && !smp->seen; n++, smp = smp->next)
+ ;
+
+ if(n == 0) return NULL;
+ retstrs = (char **)fs_get((n+1)*sizeof(char *));
+ for(tmpstrs = retstrs, smp = peStatList; smp && !smp->seen; smp = smp->next){
+ *tmpstrs = smp->text;
+ tmpstrs++;
+ }
+
+ *tmpstrs = NULL;
+ return(retstrs);
+}
+
+
+char *
+sml_getmsg(void)
+{
+ return(peStatList ? peStatList->text : "");
+}
+
+void
+sml_seen(void)
+{
+ STATMSG_S *smp;
+
+ for(smp = peStatList; smp; smp = smp->next)
+ smp->seen = 1;
+}
+
+
+
+/* * * * * * * * * LDAP Support Routines * * * * * * * * * * * */
+
+
+/*
+ * PELdapCmd - LDAP TCL interface
+ */
+int
+PELdapCmd(ClientData clientData, Tcl_Interp *interp, int objc, Tcl_Obj *CONST objv[])
+{
+#ifndef ENABLE_LDAP
+ char *err = "Call to PELdap when LDAP not enabled";
+#else
+ char *err = "Unknown PELdap request";
+ char *s1;
+
+ dprint((2, "PELdapCmd"));
+
+ if(objc == 1){
+ Tcl_WrongNumArgs(interp, 1, objv, "cmd ?args?");
+ Tcl_SetResult(interp, err, TCL_STATIC);
+ return(TCL_ERROR);
+ }
+ s1 = Tcl_GetStringFromObj(objv[1], NULL);
+
+ if(s1){
+ int qn;
+ if(!strcmp(s1, "directories")){
+ int i;
+ LDAP_SERV_S *info;
+ Tcl_Obj *secObj;
+
+ if(objc != 2){
+ Tcl_WrongNumArgs(interp, 1, objv, "cmd ?args?");
+ Tcl_SetResult(interp, err, TCL_STATIC);
+ return(TCL_ERROR);
+ }
+ if(ps_global->VAR_LDAP_SERVERS){
+ for(i = 0; ps_global->VAR_LDAP_SERVERS[i] &&
+ ps_global->VAR_LDAP_SERVERS[i][0]; i++){
+ info = break_up_ldap_server(ps_global->VAR_LDAP_SERVERS[i]);
+ secObj = Tcl_NewListObj(0, NULL);
+ if(Tcl_ListObjAppendElement(interp, secObj,
+ Tcl_NewStringObj(info->nick ? info->nick
+ : "", -1)) != TCL_OK)
+ return(TCL_ERROR);
+ if(Tcl_ListObjAppendElement(interp, secObj,
+ Tcl_NewStringObj(info->serv ? info->serv
+ : "", -1)) != TCL_OK)
+ return(TCL_ERROR);
+
+ if(info)
+ free_ldap_server_info(&info);
+ if(Tcl_ListObjAppendElement(interp, Tcl_GetObjResult(interp),
+ secObj) != TCL_OK)
+ return(TCL_ERROR);
+ }
+ }
+ else
+ Tcl_SetResult(interp, "", TCL_STATIC);
+
+ return(TCL_OK);
+ }
+ else if(!strcmp(s1, "query")){
+ int dir;
+ char *srchstr, *filtstr;
+ LDAP_CHOOSE_S *winning_e = NULL;
+ LDAP_SERV_RES_S *results = NULL;
+ WP_ERR_S wp_err;
+ CUSTOM_FILT_S *filter = NULL;
+
+ if(objc != 5){
+ Tcl_WrongNumArgs(interp, 1, objv, "cmd ?args?");
+ Tcl_SetResult(interp, err, TCL_STATIC);
+ return(TCL_ERROR);
+ }
+ if(Tcl_GetIntFromObj(interp, objv[2], &dir) == TCL_ERROR){
+ Tcl_SetResult(interp,
+ "PELdap results malformed: first arg must be int",
+ TCL_VOLATILE);
+ return(TCL_ERROR);
+ }
+ wpldap_global->query_no++;
+ if(wpldap_global->ldap_search_list){
+ wpldap_global->ldap_search_list =
+ free_wpldapres(wpldap_global->ldap_search_list);
+ }
+ srchstr = Tcl_GetStringFromObj(objv[3], NULL);
+ filtstr = Tcl_GetStringFromObj(objv[4], NULL);
+ if(!srchstr) return(TCL_ERROR);
+ if(!filtstr) return(TCL_ERROR);
+ if(*filtstr){
+ filter = (CUSTOM_FILT_S *)fs_get(sizeof(CUSTOM_FILT_S));
+ filter->filt = cpystr(filtstr);
+ filter->combine = 0;
+ }
+ memset(&wp_err, 0, sizeof(wp_err));
+ ldap_lookup_all(srchstr, dir, 0, AlwaysDisplay, filter, &winning_e,
+ &wp_err, &results);
+ if(filter){
+ fs_give((void **)&filter->filt);
+ fs_give((void **)&filter);
+ }
+ Tcl_SetResult(interp, int2string(wpldap_global->ldap_search_list
+ ? wpldap_global->query_no : 0),
+ TCL_VOLATILE);
+ return(TCL_OK);
+ }
+ /*
+ * First argument has always got to be the query number for now.
+ * Might need to rething that when setting up queries.
+ */
+ if(objc == 2){
+ Tcl_WrongNumArgs(interp, 1, objv, "cmd ?args?");
+ Tcl_SetResult(interp, err, TCL_STATIC);
+ return(TCL_ERROR);
+ }
+ if(Tcl_GetIntFromObj(interp, objv[2], &qn) == TCL_ERROR){
+ Tcl_SetResult(interp,
+ "PELdap results malformed: first arg must be int",
+ TCL_VOLATILE);
+ return(TCL_ERROR);
+ }
+ if(qn != wpldap_global->query_no){
+ Tcl_SetResult(interp,
+ "Query is no longer valid", TCL_VOLATILE);
+ return(TCL_ERROR);
+ }
+ if(objc == 3){
+ if(!strcmp(s1, "results")){
+ return(peLdapQueryResults(interp));
+ }
+ }
+ else if(objc == 4){
+ if(!strcmp(s1, "ldapext")){
+ /*
+ * Returns a list of the form:
+ * {"dn" {{attrib {val, ...}}, ...}}
+ */
+ char *whichrec = Tcl_GetStringFromObj(objv[3], NULL);
+ char *tmpstr, *tmp, *tmp2, **vals, *a;
+ WPLDAPRES_S *curres;
+ LDAP_CHOOSE_S *winning_e = NULL;
+ LDAP_SERV_RES_S *trl;
+ Tcl_Obj *secObj = NULL, *resObj = NULL, *itemObj;
+ BerElement *ber;
+ LDAPMessage *e;
+ int i, j, whichi, whichj;
+
+ if(whichrec == NULL){
+ Tcl_SetResult(interp, "Ldap ldapext error 1", TCL_VOLATILE);
+ return TCL_ERROR;
+ }
+ tmpstr = cpystr(whichrec);
+ tmp = tmpstr;
+ for(tmp2 = tmp; *tmp2 >= '0' && *tmp2 <= '9'; tmp2++);
+ if(*tmp2 != '.'){
+ Tcl_SetResult(interp, "Ldap ldapext error 2", TCL_VOLATILE);
+ return TCL_ERROR;
+ }
+ *tmp2 = '\0';
+ whichi = atoi(tmp);
+ *tmp2 = '.';
+ tmp2++;
+ for(tmp = tmp2; *tmp2 >= '0' && *tmp2 <= '9'; tmp2++);
+ if(*tmp2 != '\0'){
+ Tcl_SetResult(interp, "Ldap ldapext error 3", TCL_VOLATILE);
+ return TCL_ERROR;
+ }
+ whichj = atoi(tmp);
+ fs_give((void **)&tmpstr);
+ for(curres = wpldap_global->ldap_search_list, i = 0;
+ i < whichi && curres; i++, curres = curres->next);
+ if(!curres){
+ Tcl_SetResult(interp, "Ldap ldapext error 4", TCL_VOLATILE);
+ return TCL_ERROR;
+ }
+ for(trl = curres->reslist, j = 0; trl; trl = trl->next){
+ for(e = ldap_first_entry(trl->ld, trl->res);
+ e != NULL && j < whichj;
+ e = ldap_next_entry(trl->ld, e), j++);
+ if(e != NULL && j == whichj)
+ break;
+ }
+ if(e == NULL || trl == NULL){
+ Tcl_SetResult(interp, "Ldap ldapext error 5", TCL_VOLATILE);
+ return TCL_ERROR;
+ }
+ winning_e = (LDAP_CHOOSE_S *)fs_get(sizeof(LDAP_CHOOSE_S));
+ winning_e->ld = trl->ld;
+ winning_e->selected_entry = e;
+ winning_e->info_used = trl->info_used;
+ winning_e->serv = trl->serv;
+ a = ldap_get_dn(winning_e->ld, winning_e->selected_entry);
+ if(Tcl_ListObjAppendElement(interp, Tcl_GetObjResult(interp),
+ Tcl_NewStringObj(a ? a : "", -1)) != TCL_OK)
+ return(TCL_ERROR);
+ if(a)
+ our_ldap_dn_memfree(a);
+
+ itemObj = Tcl_NewListObj(0, NULL);
+ for(a = ldap_first_attribute(winning_e->ld, winning_e->selected_entry, &ber);
+ a != NULL;
+ a = ldap_next_attribute(winning_e->ld, winning_e->selected_entry, ber)){
+ if(a && *a){
+ secObj = Tcl_NewListObj(0, NULL);
+ if(Tcl_ListObjAppendElement(interp, secObj,
+ Tcl_NewStringObj(ldap_translate(a,
+ winning_e->info_used), -1)) != TCL_OK)
+ return(TCL_ERROR);
+ resObj = Tcl_NewListObj(0, NULL);
+ vals = ldap_get_values(winning_e->ld, winning_e->selected_entry, a);
+ if(vals){
+ for(i = 0; vals[i]; i++){
+ if(Tcl_ListObjAppendElement(interp, resObj,
+ Tcl_NewStringObj(vals[i], -1)) != TCL_OK)
+ return(TCL_ERROR);
+ }
+ ldap_value_free(vals);
+ if(Tcl_ListObjAppendElement(interp, secObj, resObj) != TCL_OK)
+ return(TCL_ERROR);
+ }
+ if(!strcmp(a,"objectclass")){
+ if(Tcl_ListObjAppendElement(interp, secObj,
+ Tcl_NewStringObj("objectclass", -1)) != TCL_OK)
+ return(TCL_ERROR);
+ }
+ if(Tcl_ListObjAppendElement(interp, itemObj, secObj) != TCL_OK)
+ return(TCL_ERROR);
+ }
+ our_ldap_memfree(a);
+ }
+
+ if(Tcl_ListObjAppendElement(interp, Tcl_GetObjResult(interp),
+ itemObj) != TCL_OK)
+ return(TCL_ERROR);
+
+ fs_give((void **)&winning_e);
+ return(TCL_OK);
+ }
+ }
+ else if(objc == 6){
+ if(!strcmp(s1, "setaddrs")){
+ char *listset = Tcl_GetStringFromObj(objv[3], NULL);
+ char *addrstr = Tcl_GetStringFromObj(objv[4], NULL);
+ char *tmp, *tmp2, *tmplistset, was_char, *ret_to,
+ *tmpaddrstr;
+ int **lset, noreplace = 0;
+ ADDRESS *adr = NULL, *curadr, *prevadr, *newadr,
+ *curnewadr, *newadrs;
+ int curi, i, j, numsrchs, numset, setit;
+ LDAP_CHOOSE_S *tres;
+ LDAP_SERV_RES_S *trl;
+ WPLDAPRES_S *curres;
+ LDAPMessage *e;
+ RFC822BUFFER rbuf;
+ size_t len;
+
+ if(Tcl_GetIntFromObj(interp, objv[5], &noreplace) == TCL_ERROR){
+ Tcl_SetResult(interp,
+ "PELdap results malformed: first arg must be int",
+ TCL_VOLATILE);
+ return(TCL_ERROR);
+ }
+ if(listset == NULL || addrstr == NULL) return TCL_ERROR;
+ tmpaddrstr = cpystr(addrstr);
+
+ if(!noreplace){
+ mail_parameters(NIL, SET_PARSEPHRASE, (void *)massage_phrase_addr);
+ rfc822_parse_adrlist(&adr, tmpaddrstr, "@");
+ mail_parameters(NIL, SET_PARSEPHRASE, NULL);
+ }
+
+ tmplistset = cpystr(listset);
+ for(curres = wpldap_global->ldap_search_list, numsrchs = 0;
+ curres; curres = curres->next, numsrchs++);
+ lset = (int **)fs_get((numsrchs+1)*sizeof(int *));
+ for(i = 0; i < numsrchs; i++){
+ for(tmp = tmplistset, numset = 0; *tmp;){
+ for(tmp2 = tmp; *tmp2 >= '0' && *tmp2 <= '9'; tmp2++);
+ if(*tmp2 != '.'){
+ Tcl_SetResult(interp, "Ldap error 1", TCL_VOLATILE);
+ return TCL_ERROR;
+ }
+ if(atoi(tmp) == i) numset++;
+ tmp2++;
+ for(tmp = tmp2; *tmp2 >= '0' && *tmp2 <= '9'; tmp2++);
+ if(*tmp2 != ',' && *tmp2 != '\0'){
+ Tcl_SetResult(interp, "Ldap error 2", TCL_VOLATILE);
+ return TCL_ERROR;
+ }
+ if(*tmp2) tmp2++;
+ tmp = tmp2;
+ }
+ lset[i] = (int *)fs_get((numset+1)*sizeof(int));
+ for(tmp = tmplistset, j = 0; *tmp && j < numset;){
+ setit = 0;
+ for(tmp2 = tmp; *tmp2 >= '0' && *tmp2 <= '9'; tmp2++);
+ if(*tmp2 != '.'){
+ Tcl_SetResult(interp, "Ldap error 3", TCL_VOLATILE);
+ return TCL_ERROR;
+ }
+ *tmp2 = '\0';
+ if(atoi(tmp) == i) setit++;
+ *tmp2 = '.';
+ tmp2++;
+ for(tmp = tmp2; *tmp2 >= '0' && *tmp2 <= '9'; tmp2++);
+ if(*tmp2 != ',' && *tmp2 != '\0'){
+ Tcl_SetResult(interp, "Ldap error 4", TCL_VOLATILE);
+ return TCL_ERROR;
+ }
+ if(setit){
+ was_char = *tmp2;
+ *tmp2 = '\0';
+ lset[i][j++] = atoi(tmp);
+ *tmp2 = was_char;
+ }
+ if(*tmp2) tmp2++;
+ tmp = tmp2;
+ }
+ lset[i][j] = -1;
+ }
+ lset[i] = NULL;
+ for(i = 0, curres = wpldap_global->ldap_search_list;
+ i < numsrchs && curres; i++, curres = curres->next){
+ prevadr = NULL;
+ for(curadr = adr; curadr; curadr = curadr->next){
+ if(strcmp(curadr->mailbox, curres->str) == 0
+ && curadr->host && *curadr->host == '@')
+ break;
+ prevadr = curadr;
+ }
+ if(!curadr && !noreplace){
+ Tcl_SetResult(interp, "Ldap error 5", TCL_VOLATILE);
+ return TCL_ERROR;
+ }
+ newadrs = newadr = curnewadr = NULL;
+ for(trl = curres->reslist, j = 0, curi = 0; trl; trl = trl->next){
+ for(e = ldap_first_entry(trl->ld, trl->res);
+ e != NULL && lset[i][curi] != -1;
+ e = ldap_next_entry(trl->ld, e), j++){
+ if(j == lset[i][curi]){
+ tres = (LDAP_CHOOSE_S *)fs_get(sizeof(LDAP_CHOOSE_S));
+ tres->ld = trl->ld;
+ tres->selected_entry = e;
+ tres->info_used = trl->info_used;
+ tres->serv = trl->serv;
+ newadr = address_from_ldap(tres);
+ fs_give((void **)&tres);
+
+ if(newadrs == NULL){
+ newadrs = curnewadr = newadr;
+ }
+ else {
+ curnewadr->next = newadr;
+ curnewadr = newadr;
+ }
+ curi++;
+ }
+ }
+ }
+ if(newadrs == NULL || curnewadr == NULL){
+ snprintf(tmp_20k_buf, SIZEOF_20KBUF, "No Result Selected for \"%s\"", curadr->mailbox ? curadr->mailbox : "noname");
+ q_status_message(SM_ORDER, 0, 3, tmp_20k_buf);
+ newadr = copyaddr(curadr);
+ if(newadrs == NULL){
+ newadrs = curnewadr = newadr;
+ }
+ else {
+ curnewadr->next = newadr;
+ curnewadr = newadr;
+ }
+ }
+ curnewadr->next = curadr ? curadr->next : NULL;
+ if(curadr) curadr->next = NULL;
+ if(curadr == adr)
+ adr = newadrs;
+ else{
+ prevadr->next = newadrs;
+ if(curadr)
+ mail_free_address(&curadr);
+ }
+ }
+
+ len = est_size(adr);
+ ret_to = (char *)fs_get(len * sizeof(char));
+ ret_to[0] = '\0';
+ strip_personal_quotes(adr);
+ rbuf.f = dummy_soutr;
+ rbuf.s = NULL;
+ rbuf.beg = ret_to;
+ rbuf.cur = ret_to;
+ rbuf.end = ret_to+len-1;
+ rfc822_output_address_list(&rbuf, adr, 0L, NULL);
+ *rbuf.cur = '\0';
+ Tcl_SetResult(interp, ret_to, TCL_VOLATILE);
+ fs_give((void **)&ret_to);
+ fs_give((void **)&tmpaddrstr);
+ fs_give((void **)&tmplistset);
+ for(i = 0; lset[i]; i++)
+ fs_give((void **)&lset[i]);
+ fs_give((void **)&lset);
+ if(adr)
+ mail_free_address(&adr);
+
+ return(TCL_OK);
+ }
+ }
+ }
+#endif /* ENABLE_LDAP */
+ Tcl_SetResult(interp, err, TCL_STATIC);
+ return(TCL_ERROR);
+}
+
+
+#ifdef ENABLE_LDAP
+int
+peLdapQueryResults(Tcl_Interp *interp)
+{
+ WPLDAPRES_S *tsl;
+ Tcl_Obj *secObj = NULL, *resObj = NULL, *itemObj;
+ LDAPMessage *e;
+ LDAP_SERV_RES_S *trl;
+ /* returned list will be of the form:
+ *
+ * {
+ * {search-string
+ * {name, {title, ...}, {unit, ...},
+ * {org, ...}, {email, ...}},
+ * ...
+ * },
+ * ...
+ * }
+ */
+
+ for(tsl = wpldap_global->ldap_search_list;
+ tsl; tsl = tsl->next){
+ secObj = Tcl_NewListObj(0, NULL);
+ if(Tcl_ListObjAppendElement(interp, secObj,
+ Tcl_NewStringObj(tsl->str ? tsl->str
+ : "", -1)) != TCL_OK)
+ return(TCL_ERROR);
+ resObj = Tcl_NewListObj(0, NULL);
+ for(trl = tsl->reslist; trl; trl = trl->next){
+ for(e = ldap_first_entry(trl->ld, trl->res);
+ e != NULL;
+ e = ldap_next_entry(trl->ld, e)){
+ char *dn;
+ char **cn, **org, **unit, **title, **mail, **sn;
+
+ dn = NULL;
+ cn = org = title = unit = mail = sn = NULL;
+
+ itemObj = Tcl_NewListObj(0, NULL);
+ peLdapEntryParse(trl, e, &cn, &org, &unit, &title,
+ &mail, &sn);
+ if(cn){
+ if(Tcl_ListObjAppendElement(interp, itemObj,
+ Tcl_NewStringObj(cn[0], -1)) != TCL_OK)
+ return(TCL_ERROR);
+ ldap_value_free(cn);
+ }
+ else if(sn){
+ if(Tcl_ListObjAppendElement(interp, itemObj,
+ Tcl_NewStringObj(sn[0], -1)) != TCL_OK)
+ return(TCL_ERROR);
+ ldap_value_free(sn);
+ }
+ else{
+ dn = ldap_get_dn(trl->ld, e);
+
+ if(dn && !dn[0]){
+ our_ldap_dn_memfree(dn);
+ dn = NULL;
+ }
+
+ if(Tcl_ListObjAppendElement(interp, itemObj,
+ Tcl_NewStringObj(dn ? dn : "", -1)) != TCL_OK)
+ return(TCL_ERROR);
+
+ if(dn)
+ our_ldap_dn_memfree(dn);
+ }
+ if(peLdapStrlist(interp, itemObj, title) == TCL_ERROR)
+ return(TCL_ERROR);
+ if(peLdapStrlist(interp, itemObj, unit) == TCL_ERROR)
+ return(TCL_ERROR);
+ if(peLdapStrlist(interp, itemObj, org) == TCL_ERROR)
+ return(TCL_ERROR);
+ if(peLdapStrlist(interp, itemObj, mail) == TCL_ERROR)
+ return(TCL_ERROR);
+ if(Tcl_ListObjAppendElement(interp, resObj, itemObj) != TCL_OK)
+ return(TCL_ERROR);
+ if(title)
+ ldap_value_free(title);
+ if(unit)
+ ldap_value_free(unit);
+ if(org)
+ ldap_value_free(org);
+ if(mail)
+ ldap_value_free(mail);
+ }
+ }
+ if(Tcl_ListObjAppendElement(interp, secObj, resObj) != TCL_OK)
+ return(TCL_ERROR);
+ if(Tcl_ListObjAppendElement(interp, Tcl_GetObjResult(interp),
+ secObj) != TCL_OK)
+ return(TCL_ERROR);
+ }
+ return(TCL_OK);
+}
+
+int
+peLdapStrlist(Tcl_Interp *interp, Tcl_Obj *itemObj, char **strl)
+{
+ Tcl_Obj *strlObj;
+ int i;
+
+ strlObj = Tcl_NewListObj(0, NULL);
+ if(strl){
+ for(i = 0; strl[i] && strl[i][0]; i++){
+ if(Tcl_ListObjAppendElement(interp, strlObj,
+ Tcl_NewStringObj(strl[i], -1)) != TCL_OK)
+ return(TCL_ERROR);
+ }
+ }
+ if(Tcl_ListObjAppendElement(interp, itemObj, strlObj) != TCL_OK)
+ return(TCL_ERROR);
+ return(TCL_OK);
+}
+
+
+int
+init_ldap_pname(struct pine *ps)
+{
+ if(!ps_global->VAR_PERSONAL_NAME
+ || ps_global->VAR_PERSONAL_NAME[0] == '\0'){
+ char *pname;
+ struct variable *vtmp;
+
+ if(ps->maildomain && *ps->maildomain
+ && ps->VAR_USER_ID && *ps->VAR_USER_ID){
+ pname = peLdapPname(ps->VAR_USER_ID, ps->maildomain);
+ if(pname){
+ vtmp = &ps->vars[V_PERSONAL_NAME];
+ if((vtmp->fixed_val.p && vtmp->fixed_val.p[0] == '\0')
+ || (vtmp->is_fixed && !vtmp->fixed_val.p)){
+ if(vtmp->fixed_val.p)
+ fs_give((void **)&vtmp->fixed_val.p);
+ vtmp->fixed_val.p = cpystr(pname);
+ }
+ else {
+ if(vtmp->global_val.p)
+ fs_give((void **)&vtmp->global_val.p);
+ vtmp->global_val.p = cpystr(pname);
+ }
+ fs_give((void **)&pname);
+ set_current_val(vtmp, FALSE, FALSE);
+ }
+ }
+ }
+ return 0;
+}
+#endif /* ENABLE_LDAP */
+
+/*
+ * Note: this is taken straight out of pico/composer.c
+ *
+ * strqchr - returns pointer to first non-quote-enclosed occurance of ch in
+ * the given string. otherwise NULL.
+ * s -- the string
+ * ch -- the character we're looking for
+ * q -- q tells us if we start out inside quotes on entry and is set
+ * correctly on exit.
+ * m -- max characters we'll check for ch (set to -1 for no check)
+ */
+char *
+strqchr(char *s, int ch, int *q, int m)
+{
+ int quoted = (q) ? *q : 0;
+
+ for(; s && *s && m != 0; s++, m--){
+ if(*s == '"'){
+ quoted = !quoted;
+ if(q)
+ *q = quoted;
+ }
+
+ if(!quoted && *s == ch)
+ return(s);
+ }
+
+ return(NULL);
+}
+
+
+Tcl_Obj *
+wp_prune_folders(CONTEXT_S *ctxt,
+ char *fcc,
+ int cur_month,
+ char *type,
+ unsigned pr,
+ int *ok,
+ int moved_fldrs,
+ Tcl_Interp *interp)
+{
+ Tcl_Obj *resObj = NULL, *secObj = NULL;
+ char path2[MAXPATH+1], tmp[21];
+ int exists, month_to_use;
+ struct sm_folder *mail_list, *sm;
+
+ mail_list = get_mail_list(ctxt, fcc);
+
+ for(sm = mail_list; sm != NULL && sm->name != NULL; sm++)
+ if(sm->month_num == cur_month - 1)
+ break; /* matched a month */
+
+ month_to_use = (sm == NULL || sm->name == NULL) ? cur_month - 1 : 0;
+
+ if(!(month_to_use == 0 || pr == PRUNE_NO_AND_ASK || pr == PRUNE_NO_AND_NO)){
+ strncpy(path2, fcc, sizeof(path2)-1);
+ path2[sizeof(path2)-1] = '\0';
+ strncpy(tmp, month_abbrev((month_to_use % 12)+1), sizeof(tmp)-1);
+ tmp[sizeof(tmp)-1] = '\0';
+ lcase((unsigned char *) tmp);
+ snprintf(path2 + strlen(path2), sizeof(path2)-strlen(path2), "-%.20s-%d", tmp, month_to_use/12);
+
+ if((exists = folder_exists(ctxt, fcc)) == FEX_ERROR){
+ (*ok) = 0;
+ return(NULL);
+ }
+ else if(exists & FEX_ISFILE){
+ if(pr == PRUNE_YES_AND_ASK || (pr == PRUNE_YES_AND_NO && !moved_fldrs)){
+ prune_move_folder(fcc, path2, ctxt);
+ } else {
+ resObj = Tcl_NewListObj(0, NULL);
+ Tcl_ListObjAppendElement(interp, resObj, Tcl_NewStringObj(type, -1));
+ secObj = Tcl_NewListObj(0, NULL);
+ Tcl_ListObjAppendElement(interp, secObj, Tcl_NewStringObj(fcc, -1));
+ Tcl_ListObjAppendElement(interp, secObj, Tcl_NewStringObj(path2, -1));
+ Tcl_ListObjAppendElement(interp, resObj, secObj);
+ }
+ }
+ }
+ if(pr == PRUNE_ASK_AND_ASK || pr == PRUNE_YES_AND_ASK
+ || pr == PRUNE_NO_AND_ASK){
+ sm = mail_list;
+ if(!resObj && sm && sm->name && sm->name[0] != '\0'){
+ resObj = Tcl_NewListObj(0, NULL);
+ Tcl_ListObjAppendElement(interp, resObj, Tcl_NewStringObj(type, -1));
+ Tcl_ListObjAppendElement(interp, resObj, Tcl_NewListObj(0, NULL));
+ }
+ if(resObj)
+ secObj = Tcl_NewListObj(0, NULL);
+ for(sm = mail_list; sm != NULL && sm->name != NULL; sm++){
+ if(sm->name[0] == '\0') /* can't happen */
+ continue;
+ Tcl_ListObjAppendElement(interp, secObj, Tcl_NewStringObj(sm->name, -1));
+ }
+ if(resObj)
+ Tcl_ListObjAppendElement(interp, resObj, secObj);
+ } else if(resObj)
+ Tcl_ListObjAppendElement(interp, resObj, Tcl_NewListObj(0, NULL));
+
+ free_folder_list(ctxt);
+
+ if((sm = mail_list) != NULL){
+ while(sm->name){
+ fs_give((void **)&(sm->name));
+ sm++;
+ }
+
+ fs_give((void **)&mail_list);
+ }
+
+ return(resObj);
+}
+
+
+int
+hex_colorstr(char *hexcolor, char *str)
+{
+ char *tstr, *p, *p2, tbuf[256];
+ int i;
+
+ strcpy(hexcolor, "000000");
+ tstr = color_to_asciirgb(str);
+ p = tstr;
+ p2 = strindex(p, ',');
+ if(p2 == NULL) return 0;
+ strncpy(tbuf, p, min(50, p2-p));
+ i = atoi(tbuf);
+ sprintf(hexcolor, "%2.2x", i);
+ p = p2+1;
+ p2 = strindex(p, ',');
+ if(p2 == NULL) return 0;
+ strncpy(tbuf, p, min(50, p2-p));
+ i = atoi(tbuf);
+ sprintf(hexcolor+2, "%2.2x", i);
+ p = p2+1;
+ strncpy(tbuf, p, 50);
+ i = atoi(tbuf);
+ sprintf(hexcolor+4, "%2.2x", i);
+
+ return 0;
+}
+
+int
+hexval(char ch)
+{
+ if(ch >= '0' && ch <= '9')
+ return (ch - '0');
+ else if (ch >= 'A' && ch <= 'F')
+ return (10 + (ch - 'A'));
+ else if (ch >= 'a' && ch <= 'f')
+ return (10 + (ch - 'a'));
+ return -1;
+}
+
+int
+ascii_colorstr(char *acolor, char *hexcolor)
+{
+ int i, hv;
+
+ if(strlen(hexcolor) > 6) return 1;
+ /* red value */
+ if((hv = hexval(hexcolor[0])) == -1) return 1;
+ i = 16 * hv;
+ if((hv = hexval(hexcolor[1])) == -1) return 1;
+ i += hv;
+ sprintf(acolor, "%3.3d,", i);
+ /* green value */
+ if((hv = hexval(hexcolor[2])) == -1) return 1;
+ i = 16 * hv;
+ if((hv = hexval(hexcolor[3])) == -1) return 1;
+ i += hv;
+ sprintf(acolor+4, "%3.3d,", i);
+ /* blue value */
+ if((hv = hexval(hexcolor[4])) == -1) return 1;
+ i = 16 * hv;
+ if((hv = hexval(hexcolor[5])) == -1) return 1;
+ i += hv;
+ sprintf(acolor+8, "%3.3d", i);
+
+ return 0;
+}
+
+
+char *
+peRandomString(char *b, int l, int f)
+{
+ static char *kb = "ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789";
+ char *s = b;
+ int j;
+ long n;
+
+ while(1){
+ n = random();
+ for(j = 0; j < ((sizeof(long) * 8) / 5); j++){
+ if(l-- <= 0){
+ *s = '\0';
+ return(b);
+ }
+
+ switch(f){
+ case PRS_LOWER_CASE :
+ *s++ = (char) tolower((unsigned char) kb[(n & 0x1F)]);
+ break;
+
+ case PRS_MIXED_CASE :
+ if(random() % 2){
+ *s++ = (char) tolower((unsigned char) kb[(n & 0x1F)]);
+ break;
+ }
+
+ default :
+ *s++ = kb[(n & 0x1F)];
+ break;
+ }
+
+ n = n >> 5;
+ }
+ }
+}
+
+
+long
+peAppendMsg(MAILSTREAM *stream, void *data, char **flags, char **date, STRING **message)
+{
+ char *t,*t1,tmp[MAILTMPLEN];
+ unsigned long u;
+ MESSAGECACHE *elt;
+ APPEND_PKG *ap = (APPEND_PKG *) data;
+ *flags = *date = NIL; /* assume no flags or date */
+ if (ap->flags) fs_give ((void **) &ap->flags);
+ if (ap->date) fs_give ((void **) &ap->date);
+ mail_gc (ap->stream,GC_TEXTS);
+ if (++ap->msgno <= ap->msgmax) {
+ /* initialize flag string */
+ memset (t = tmp,0,MAILTMPLEN);
+ /* output system flags */
+ if ((elt = mail_elt (ap->stream,ap->msgno))->seen) {strncat (t," \\Seen", sizeof(tmp)-(t-tmp)-1); tmp[sizeof(tmp)-1] = '\0';}
+ if (elt->deleted) {strncat (t," \\Deleted", sizeof(tmp)-(t-tmp)-1); tmp[sizeof(tmp)-1] = '\0';}
+ if (elt->flagged) {strncat (t," \\Flagged", sizeof(tmp)-(t-tmp)-1); tmp[sizeof(tmp)-1] = '\0';}
+ if (elt->answered) {strncat (t," \\Answered", sizeof(tmp)-(t-tmp)-1); tmp[sizeof(tmp)-1] = '\0';}
+ if (elt->draft) {strncat (t," \\Draft", sizeof(tmp)-(t-tmp)-1); tmp[sizeof(tmp)-1] = '\0';}
+ if ((u = elt->user_flags) != 0L) do /* any user flags? */
+ if ((MAILTMPLEN - ((t += strlen (t)) - tmp)) > (long)
+ (2 + strlen
+ (t1 = ap->stream->user_flags[find_rightmost_bit (&u)]))) {
+ if(t-tmp < sizeof(tmp))
+ *t++ = ' '; /* space delimiter */
+ strncpy (t,t1,sizeof(tmp)-(t-tmp)); /* copy the user flag */
+ }
+ while (u); /* until no more user flags */
+ tmp[sizeof(tmp)-1] = '\0';
+ *flags = ap->flags = cpystr (tmp + 1);
+ *date = ap->date = cpystr (mail_date (tmp,elt));
+ *message = ap->message; /* message stringstruct */
+ INIT (ap->message,mstring,(void *) ap,elt->rfc822_size);
+ }
+ else *message = NIL; /* all done */
+ return LONGT;
+}
+
+
+/* Initialize file string structure for file stringstruct
+* Accepts: string structure
+ * pointer to message data structure
+ * size of string
+ */
+
+void
+ms_init(STRING *s, void *data, unsigned long size)
+{
+ APPEND_PKG *md = (APPEND_PKG *) data;
+ s->data = data; /* note stream/msgno and header length */
+ mail_fetchheader_full (md->stream,md->msgno,NIL,&s->data1,FT_PREFETCHTEXT);
+ mail_fetchtext_full (md->stream,md->msgno,&s->size,NIL);
+ s->size += s->data1; /* header + body size */
+ SETPOS (s,0);
+}
+
+
+/* Get next character from file stringstruct
+ * Accepts: string structure
+ * Returns: character, string structure chunk refreshed
+ */
+char
+ms_next(STRING *s)
+{
+ char c = *s->curpos++; /* get next byte */
+ SETPOS (s,GETPOS (s)); /* move to next chunk */
+ return c; /* return the byte */
+}
+
+
+/* Set string pointer position for file stringstruct
+ * Accepts: string structure
+ * new position
+ */
+void
+ms_setpos(STRING *s, unsigned long i)
+{
+ APPEND_PKG *md = (APPEND_PKG *) s->data;
+ if (i < s->data1) { /* want header? */
+ s->chunk = mail_fetchheader (md->stream,md->msgno);
+ s->chunksize = s->data1; /* header length */
+ s->offset = 0; /* offset is start of message */
+ }
+ else if (i < s->size) { /* want body */
+ s->chunk = mail_fetchtext (md->stream,md->msgno);
+ s->chunksize = s->size - s->data1;
+ s->offset = s->data1; /* offset is end of header */
+ }
+ else { /* off end of message */
+ s->chunk = NIL; /* make sure that we crack on this then */
+ s->chunksize = 1; /* make sure SNX cracks the right way... */
+ s->offset = i;
+ }
+ /* initial position and size */
+ s->curpos = s->chunk + (i -= s->offset);
+ s->cursize = s->chunksize - i;
+}
+
+
+int
+remote_pinerc_failure(void)
+{
+ snprintf(ps_global->last_error, sizeof(ps_global->last_error), "%s",
+ ps_global->c_client_error[0]
+ ? ps_global->c_client_error
+ : _("Unable to read remote configuration"));
+
+ return(TRUE);
+}
+
+char *
+peWebAlpinePrefix(void)
+{
+ return("Web ");
+}
+
+
+void peNewMailAnnounce(MAILSTREAM *stream, long n, long t_nm_count){
+ char subject[MAILTMPLEN+1], subjtext[MAILTMPLEN+1], from[MAILTMPLEN+1],
+ *folder = NULL, intro[MAILTMPLEN+1];
+ long number;
+ ENVELOPE *e = NULL;
+ Tcl_Obj *resObj;
+
+ if(n && (resObj = Tcl_NewListObj(0, NULL)) != NULL){
+
+ Tcl_ListObjAppendElement(peED.interp, resObj, Tcl_NewLongObj(number = sp_mail_since_cmd(stream)));
+ Tcl_ListObjAppendElement(peED.interp, resObj, Tcl_NewLongObj(mail_uid(stream, n)));
+
+ if(stream){
+ e = pine_mail_fetchstructure(stream, n, NULL);
+
+ if(sp_flagged(stream, SP_INBOX))
+ folder = NULL;
+ else{
+ folder = STREAMNAME(stream);
+ if(folder[0] == '?' && folder[1] == '\0')
+ folder = NULL;
+ }
+ }
+
+ format_new_mail_msg(folder, number, e, intro, from, subject, subjtext, sizeof(intro));
+
+ snprintf(tmp_20k_buf, SIZEOF_20KBUF,
+ "%s%s%s%.80s%.80s", intro,
+ from ? ((number > 1L) ? " Most recent f" : " F") : "",
+ from ? "rom " : "",
+ from ? from : "",
+ subjtext);
+
+ Tcl_ListObjAppendElement(peED.interp, resObj, Tcl_NewStringObj(tmp_20k_buf,-1));
+
+ Tcl_ListObjAppendElement(peED.interp, Tcl_GetObjResult(peED.interp), resObj);
+ }
+}
+
+
+/* * * * * * * * * RSS 2.0 Support Routines * * * * * * * * * * * */
+
+/*
+ * PERssCmd - RSS TCL interface
+ */
+int
+PERssCmd(ClientData clientData, Tcl_Interp *interp, int objc, Tcl_Obj *CONST objv[])
+{
+ char *s1;
+
+ dprint((2, "PERssCmd"));
+
+ if(objc == 1){
+ Tcl_WrongNumArgs(interp, 1, objv, "cmd ?args?");
+ return(TCL_ERROR);
+ }
+ s1 = Tcl_GetStringFromObj(objv[1], NULL);
+
+ if(s1){
+ if(!strcmp(s1, "news")){
+ return(peRssReturnFeed(interp, "news", ps_global->VAR_RSS_NEWS));
+ }
+ else if(!strcmp(s1, "weather")){
+ return(peRssReturnFeed(interp, "weather", ps_global->VAR_RSS_WEATHER));
+ }
+ }
+
+ Tcl_SetResult(interp, "Unknown PERss command", TCL_STATIC);
+ return(TCL_ERROR);
+}
+
+/*
+ * peRssReturnFeed - fetch feed contents and package Tcl response
+ */
+int
+peRssReturnFeed(Tcl_Interp *interp, char *type, char *link)
+{
+ RSS_FEED_S *feed;
+ char *errstr = "UNKNOWN";
+
+ if(link){
+ ps_global->c_client_error[0] = '\0';
+
+ if((feed = peRssFeed(interp, type, link)) != NULL)
+ return(peRssPackageFeed(interp, feed));
+
+ if(ps_global->mm_log_error)
+ errstr = ps_global->c_client_error;
+ }
+ else
+ errstr = "missing setting";
+
+ snprintf(tmp_20k_buf, SIZEOF_20KBUF, "%s feed fail: %s", type, errstr);
+ Tcl_SetResult(interp, tmp_20k_buf, TCL_VOLATILE);
+ return(TCL_ERROR);
+}
+
+/*
+ * peRssPackageFeed - build a list of feed item elements
+ *
+ * LIST ORDER: {title} {link} {description} {image}
+ */
+int
+peRssPackageFeed(Tcl_Interp *interp, RSS_FEED_S *feed)
+{
+ RSS_ITEM_S *item;
+
+ for(item = feed->items; item; item = item->next)
+ if(peAppListF(interp, Tcl_GetObjResult(interp), "%s %s %s %s",
+ (item->title && *item->title)? item->title : "Feed Provided No Title",
+ item->link ? item->link : "",
+ item->description ? item->description : "",
+ feed->image ? feed->image : "") != TCL_OK)
+ return(TCL_ERROR);
+
+ return(TCL_OK);
+}
+
+
+/*
+ * peRssFeed - return cached feed struct or fetch a new one
+ */
+RSS_FEED_S *
+peRssFeed(Tcl_Interp *interp, char *type, char *link)
+{
+ int i, cache_l, cp_ref;
+ time_t now = time(0);
+ RSS_FEED_S *feed = NULL;
+ RSS_CACHE_S *cache, *cp;
+ static RSS_CACHE_S news_cache[RSS_NEWS_CACHE_SIZE], weather_cache[RSS_WEATHER_CACHE_SIZE];
+
+ if(!strucmp(type,"news")){
+ cache = &news_cache[0];
+ cache_l = RSS_NEWS_CACHE_SIZE;
+ }
+ else{
+ cache = &weather_cache[0];
+ cache_l = RSS_WEATHER_CACHE_SIZE;
+ }
+
+ /* search/purge cache */
+ for(i = 0; i < cache_l; i++)
+ if(cache[i].link){
+ if(now > cache[i].stale){
+ peRssClearCacheEntry(&cache[i]);
+ }
+ else if(!strcmp(link, cache[i].link)){
+ cache[i].referenced++;
+ return(cache[i].feed); /* HIT! */
+ }
+ }
+
+ if((feed = peRssFetch(interp, link)) != NULL){
+ /* find cache slot, and insert feed into cache */
+ for(i = 0, cp_ref = 0; i < cache_l; i++)
+ if(!cache[i].feed){
+ cp = &cache[i];
+ break;
+ }
+ else if(cache[i].referenced >= cp_ref)
+ cp = &cache[i];
+
+ if(!cp)
+ cp = &cache[0]; /* failsafe */
+
+ peRssClearCacheEntry(cp); /* make sure */
+
+ cp->link = cpystr(link);
+ cp->feed = feed;
+ cp->referenced = 0;
+ cp->stale = now + (((feed->ttl > 0) ? feed->ttl : 60) * 60);
+ }
+
+ return(feed);
+}
+
+/*
+ * peRssFetch - follow the provided link an return the resulting struct
+ */
+RSS_FEED_S *
+peRssFetch(Tcl_Interp *interp, char *link)
+{
+ char *scheme = NULL, *loc = NULL, *path = NULL, *parms = NULL, *query = NULL, *frag = NULL;
+ char *buffer = NULL, *bp, *p, *q;
+ int ttl = 60;
+ unsigned long port = 0L, buffer_len = 0L;
+ time_t theirdate = 0;
+ STORE_S *feed_so = NULL;
+ TCPSTREAM *tcp_stream;
+
+ if(link){
+ /* grok url */
+ rfc1808_tokens(link, &scheme, &loc, &path, &parms, &query, &frag);
+ if(scheme && loc && path){
+ if((p = strchr(loc,':')) != NULL){
+ *p++ = '\0';
+ while(*p && isdigit((unsigned char) *p))
+ port = ((port * 10) + (*p++ - '0'));
+
+ if(*p){
+ Tcl_SetResult(interp, "Bad RSS port number", TCL_STATIC);
+ peRssComponentFree(&scheme,&loc,&path,&parms,&query,&frag);
+ return(NULL);
+ }
+ }
+
+ if(scheme && !strucmp(scheme, "feed")){
+ fs_give((void **) &scheme);
+ scheme = cpystr("http");
+ }
+
+ mail_parameters(NULL, SET_OPENTIMEOUT, (void *)(long) 5);
+ tcp_stream = tcp_open (loc, scheme, port | NET_NOOPENTIMEOUT);
+ mail_parameters(NULL, SET_OPENTIMEOUT, (void *)(long) 30);
+
+ if(tcp_stream != NULL){
+ char rev[128];
+
+ snprintf(tmp_20k_buf, SIZEOF_20KBUF, "GET /%s%s%s%s%s HTTP/1.1\r\nHost: %s\r\nAccept: application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8\r\nAccept-Charset: ISO-8859-1,utf-8;q=0.7,*;q=0.7\r\nUser-Agent: Web-Alpine/%s (%s %s)\r\n\r\n",
+ path, parms ? ":" : "", parms ? parms : "",
+ query ? "?" : "", query ? query : "", loc,
+ ALPINE_VERSION, SYSTYPE, get_alpine_revision_string(rev, sizeof(rev)));
+
+ mail_parameters(NULL, SET_WRITETIMEOUT, (void *)(long) 5);
+ mail_parameters(NULL, SET_READTIMEOUT, (void *)(long) 5);
+
+ if(tcp_sout(tcp_stream, tmp_20k_buf, strlen(tmp_20k_buf))){
+ int ok = 0, chunked = FALSE;
+
+ while((p = tcp_getline(tcp_stream)) != NULL){
+ if(!ok){
+ ok++;
+ if(strucmp(p,"HTTP/1.1 200 OK")){
+ fs_give((void **) &p);
+ break; /* bail */
+ }
+ }
+ else if(*p == '\0'){ /* first blank line, start of body */
+ if(buffer || feed_so){
+ fs_give((void **) &p);
+ break; /* bail */
+ }
+
+ if(buffer_len){
+ buffer = fs_get(buffer_len + 16);
+ if(!tcp_getbuffer(tcp_stream, buffer_len, buffer))
+ fs_give((void **) &buffer);
+
+ fs_give((void **) &p);
+ break; /* bail */
+ }
+ else if((feed_so = so_get(CharStar, NULL, EDIT_ACCESS)) == NULL){
+ fs_give((void **) &p);
+ break; /* bail */
+ }
+ }
+ else if(feed_so){ /* collect body */
+ if(chunked){
+ int chunk_len = 0, gotbuf;
+
+ /* first line is chunk size in hex */
+ for(q = p; *q && isxdigit((unsigned char) *q); q++)
+ chunk_len = (chunk_len * 16) + XDIGIT2C(*q);
+
+ if(chunk_len > 0){ /* collect chunk */
+ char *tbuf = fs_get(chunk_len + 16);
+ gotbuf = tcp_getbuffer(tcp_stream, chunk_len, tbuf);
+ if(gotbuf)
+ so_nputs(feed_so, tbuf, chunk_len);
+
+ fs_give((void **) &tbuf);
+
+ if(!gotbuf){
+ fs_give((void **) &p);
+ break; /* bail */
+ }
+ }
+
+ /* collect trailing CRLF */
+ gotbuf = ((q = tcp_getline(tcp_stream)) != NULL && *q == '\0');
+ if(q)
+ fs_give((void **) &q);
+
+ if(chunk_len == 0 || !gotbuf){
+ fs_give((void **) &p);
+ break; /* bail */
+ }
+ }
+ else
+ so_puts(feed_so, p);
+ }
+ else{ /* in header, grok fields */
+ if(q = strchr(p,':')){
+ int l = q - p;
+
+ *q++ = '\0';
+ while(isspace((unsigned char ) *q))
+ q++;
+
+ /* content-length */
+ if(l == 4 && !strucmp(p, "date")){
+ theirdate = date_to_local_time_t(q);
+ }
+ else if(l == 7 && !strucmp(p, "expires")){
+ time_t expires = date_to_local_time_t(q) - ((theirdate > 0) ? theirdate : time(0));
+
+ if(expires > 0 && expires < (8 * 60 * 60))
+ ttl = expires;
+ }
+ else if(l == 12 && !strucmp(p, "content-type")
+ && struncmp(q,"text/xml", 8)
+ && struncmp(q,"application/xhtml+xml", 21)
+ && struncmp(q,"application/rss+xml", 19)
+ && struncmp(q,"application/xml", 15)){
+ fs_give((void **) &p);
+ break; /* bail */
+ }
+ else if(l == 13 && !strucmp(p, "cache-control")){
+ if(!struncmp(q,"max-age=",8)){
+ int secs = 0;
+
+ for(q += 8; *q && isdigit((unsigned char) *q); q++)
+ secs = ((secs * 10) + (*q - '0'));
+
+ if(secs > 0)
+ ttl = secs;
+ }
+ }
+ else if(l == 14 && !strucmp(p,"content-length")){
+ while(*q && isdigit((unsigned char) *q))
+ buffer_len = ((buffer_len * 10) + (*q++ - '0'));
+
+ if(*q){
+ fs_give((void **) &p);
+ break; /* bail */
+ }
+ }
+ else if(l == 17 && !strucmp(p, "transfer-encoding")){
+ if(!struncmp(q,"chunked", 7)){
+ chunked = TRUE;
+ }
+ else{ /* unknown encoding */
+ fs_give((void **) &p);
+ break; /* bail */
+ }
+ }
+ }
+ }
+
+ fs_give((void **) &p);
+ }
+ }
+ else{
+ Tcl_SetResult(interp, "RSS send failure", TCL_STATIC);
+ peRssComponentFree(&scheme,&loc,&path,&parms,&query,&frag);
+ }
+
+ tcp_close(tcp_stream);
+ mail_parameters(NULL, SET_READTIMEOUT, (void *)(long) 60);
+ mail_parameters(NULL, SET_WRITETIMEOUT, (void *)(long) 60);
+ peRssComponentFree(&scheme,&loc,&path,&parms,&query,&frag);
+
+ if(feed_so){
+ buffer = (char *) so_text(feed_so);
+ buffer_len = (int) so_tell(feed_so);
+ }
+
+ if(buffer && buffer_len){
+ RSS_FEED_S *feed;
+ char *err;
+ STORE_S *bucket;
+ gf_io_t gc, pc;
+
+ /* grok response */
+ bucket = so_get(CharStar, NULL, EDIT_ACCESS);
+ gf_set_readc(&gc, buffer, buffer_len, CharStar, 0);
+ gf_set_so_writec(&pc, bucket);
+ gf_filter_init();
+ gf_link_filter(gf_html2plain, gf_html2plain_rss_opt(&feed,0));
+ if((err = gf_pipe(gc, pc)) != NULL){
+ gf_html2plain_rss_free(&feed);
+ Tcl_SetResult(interp, "RSS connection failure", TCL_STATIC);
+ }
+
+ so_give(&bucket);
+
+ if(feed_so)
+ so_give(&feed_so);
+ else
+ fs_give((void **) &buffer);
+
+ return(feed);
+ }
+ else
+ Tcl_SetResult(interp, "RSS response error", TCL_STATIC);
+ }
+ else
+ Tcl_SetResult(interp, "RSS connection failure", TCL_STATIC);
+ }
+ else
+ Tcl_SetResult(interp, "RSS feed missing scheme", TCL_STATIC);
+ }
+ else
+ Tcl_SetResult(interp, "No RSS Feed Defined", TCL_STATIC);
+
+ return(NULL);
+}
+
+
+void
+peRssComponentFree(char **scheme,char **loc,char **path,char **parms,char **query,char **frag)
+{
+ if(scheme) fs_give((void **) scheme);
+ if(loc) fs_give((void **) loc);
+ if(path) fs_give((void **) path);
+ if(parms) fs_give((void **) parms);
+ if(query) fs_give((void **) query);
+ if(frag) fs_give((void **) frag);
+}
+
+void
+peRssClearCacheEntry(RSS_CACHE_S *entry)
+{
+ if(entry){
+ if(entry->link)
+ fs_give((void **) &entry->link);
+
+ gf_html2plain_rss_free(&entry->feed);
+ memset(entry, 0, sizeof(RSS_CACHE_S));
+ }
+}
diff --git a/web/src/alpined.d/alpined.h b/web/src/alpined.d/alpined.h
new file mode 100644
index 00000000..b16fd5bc
--- /dev/null
+++ b/web/src/alpined.d/alpined.h
@@ -0,0 +1,49 @@
+/*-----------------------------------------------------------------------
+ $Id: alpined.h 1142 2008-08-13 17:22:21Z hubert@u.washington.edu $
+ -----------------------------------------------------------------------*/
+
+/* ========================================================================
+ * Copyright 2006-2007 University of Washington
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * ========================================================================
+ */
+
+/*
+ * Various constants
+ */
+#define WP_MAXSTATUS 1024
+
+/*
+ * Seconds afterwhich we bail on imap connections
+ */
+#define WP_TCP_TIMEOUT (10 * 60)
+
+/*
+ * buf to hold hostname for auth/cert
+ */
+#define CRED_REQ_SIZE 256
+
+/*
+ * Various external definitions
+ */
+extern int peNoPassword;
+extern int peCredentialError;
+extern char peCredentialRequestor[];
+extern int peCertQuery;
+extern int peCertFailure;
+extern char *peSocketName;
+extern STRLIST_S *peCertHosts;
+
+/*
+ * Protoypes for various functions
+ */
+
+/* alpined.c */
+void sml_addmsg(int, char *);
+
diff --git a/web/src/alpined.d/alpineldap.c b/web/src/alpined.d/alpineldap.c
new file mode 100644
index 00000000..c29ef7f4
--- /dev/null
+++ b/web/src/alpined.d/alpineldap.c
@@ -0,0 +1,181 @@
+#if !defined(lint) && !defined(DOS)
+static char rcsid[] = "$Id: alpineldap.c 1204 2009-02-02 19:54:23Z hubert@u.washington.edu $";
+#endif
+
+/* ========================================================================
+ * Copyright 2006-2007 University of Washington
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * ========================================================================
+ */
+
+#include <system.h>
+#include <general.h>
+
+#include "../../../c-client/c-client.h"
+#include "../../../c-client/imap4r1.h"
+
+#include "../../../pith/osdep/color.h" /* color support library */
+#include "../../../pith/osdep/canaccess.h"
+#include "../../../pith/osdep/temp_nam.h"
+
+#include "../../../pith/stream.h"
+#include "../../../pith/context.h"
+#include "../../../pith/state.h"
+#include "../../../pith/msgno.h"
+#include "../../../pith/debug.h"
+#include "../../../pith/init.h"
+#include "../../../pith/conf.h"
+#include "../../../pith/conftype.h"
+#include "../../../pith/detoken.h"
+#include "../../../pith/flag.h"
+#include "../../../pith/help.h"
+#include "../../../pith/remote.h"
+#include "../../../pith/status.h"
+#include "../../../pith/mailcmd.h"
+#include "../../../pith/savetype.h"
+#include "../../../pith/save.h"
+#include "../../../pith/reply.h"
+#include "../../../pith/sort.h"
+#include "../../../pith/ldap.h"
+#include "../../../pith/addrbook.h"
+#include "../../../pith/takeaddr.h"
+#include "../../../pith/bldaddr.h"
+#include "../../../pith/copyaddr.h"
+#include "../../../pith/thread.h"
+#include "../../../pith/folder.h"
+#include "../../../pith/mailview.h"
+#include "../../../pith/indxtype.h"
+#include "../../../pith/mailindx.h"
+#include "../../../pith/mailpart.h"
+#include "../../../pith/mimedesc.h"
+#include "../../../pith/detach.h"
+#include "../../../pith/newmail.h"
+#include "../../../pith/charset.h"
+#include "../../../pith/util.h"
+#include "../../../pith/rfc2231.h"
+#include "../../../pith/string.h"
+#include "../../../pith/send.h"
+
+#include "alpined.h"
+#include "ldap.h"
+
+struct pine *ps_global; /* THE global variable! */
+char tmp_20k_buf[20480];
+
+char *peSocketName;
+
+#ifdef ENABLE_LDAP
+WPLDAP_S *wpldap_global;
+#endif
+
+int peNoPassword, peCredentialError;
+int peCertQuery, peCertFailure;
+char peCredentialRequestor[CRED_REQ_SIZE];
+STRLIST_S *peCertHosts;
+
+void
+sml_addmsg(priority, text)
+ int priority;
+ char *text;
+{
+}
+
+void
+peDestroyUserContext(pps)
+ struct pine **pps;
+{
+}
+
+int
+main(argc, argv)
+ int argc;
+ char *argv[];
+{
+#ifdef ENABLE_LDAP
+ struct pine *pine_state;
+ char *p = NULL, *userid = NULL, *domain = NULL, *pname;
+ struct variable *vars;
+ int i, usage = 0, rv = 0;
+
+ pine_state = new_pine_struct();
+ ps_global = pine_state;
+ vars = ps_global->vars;
+ debug = 0;
+
+ for(i = 1 ; i < argc; i++){
+ if(*argv[i] == '-'){
+ switch (argv[i++][1]) {
+ case 'p':
+ p = argv[i];
+ break;
+ case 'u':
+ userid = argv[i];
+ break;
+ case 'd':
+ domain = argv[i];
+ break;
+ default:
+ usage = rv = 1;
+ break;
+ }
+ }
+ else
+ usage = rv = 1;
+ if(usage == 1) break;
+ }
+ if(argc == 1) usage = rv = 1;
+ if (usage == 1 || !p || !userid){
+ usage = rv = 1;
+ goto done;
+ }
+ wpldap_global = (WPLDAP_S *)fs_get(sizeof(WPLDAP_S));
+ wpldap_global->query_no = 0;
+ wpldap_global->ldap_search_list = NULL;
+
+ ps_global->pconf = new_pinerc_s(p);
+ if(ps_global->pconf)
+ read_pinerc(ps_global->pconf, vars, ParseGlobal);
+ else {
+ fprintf(stderr, "Failed to read pineconf\n");
+ rv = 1;
+ goto done;
+ }
+ set_current_val(&ps_global->vars[V_LDAP_SERVERS], FALSE, FALSE);
+ set_current_val(&ps_global->vars[V_USER_DOMAIN], FALSE, FALSE);
+ if(!ps_global->VAR_USER_DOMAIN && !domain){
+ fprintf(stderr, "No domain set in pineconf\n");
+ usage = 1;
+ goto done;
+ }
+ if((pname = peLdapPname(userid, domain ? domain : ps_global->VAR_USER_DOMAIN)) != NULL){
+ fprintf(stdout, "%s\n", pname);
+ fs_give((void **)&pname);
+ }
+ else
+ fprintf(stdout, "\n");
+
+done:
+ if(usage)
+ fprintf(stderr, "usage: pineldap -u userid -p pineconf [-d domain]\n");
+ if(wpldap_global){
+ if(wpldap_global->ldap_search_list)
+ free_wpldapres(wpldap_global->ldap_search_list);
+ fs_give((void **)&wpldap_global);
+ }
+ if(ps_global->pconf)
+ free_pinerc_s(&ps_global->pconf);
+ free_pine_struct(&pine_state);
+
+ exit(rv);
+#else
+ fprintf(stderr, "%s: Not built with LDAP support\n", argv[0]);
+ exit(-1);
+#endif
+}
+
diff --git a/web/src/alpined.d/busy.c b/web/src/alpined.d/busy.c
new file mode 100644
index 00000000..6f10fa7f
--- /dev/null
+++ b/web/src/alpined.d/busy.c
@@ -0,0 +1,49 @@
+#if !defined(lint) && !defined(DOS)
+static char rcsid[] = "$Id: signal.c 91 2006-07-28 19:02:07Z mikes@u.washington.edu $";
+#endif
+
+/* ========================================================================
+ * Copyright 2006-2007 University of Washington
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * ========================================================================
+ */
+
+#include <system.h>
+#include <general.h>
+
+#include "../../../pith/status.h"
+#include "../../../pith/busy.h"
+
+
+/*
+ * Turn on a busy alarm.
+ */
+int
+busy_cue(char *msg, percent_done_t pc_func, int init_msg)
+{
+ if(msg && !strncmp("Moving", msg, 6)){
+ strncpy(msg+1, "Moved", 5);
+ q_status_message(SM_ORDER, 3, 3, msg+1);
+ }
+
+ return(0);
+}
+
+
+/*
+ * If final_message was set when busy_cue was called:
+ * and message_pri = -1 -- no final message queued
+ * else final message queued with min equal to message_pri
+ */
+void
+cancel_busy_cue(int message_pri)
+{
+}
+
+
diff --git a/web/src/alpined.d/color.c b/web/src/alpined.d/color.c
new file mode 100644
index 00000000..e8d0af86
--- /dev/null
+++ b/web/src/alpined.d/color.c
@@ -0,0 +1,678 @@
+#if !defined(lint) && !defined(DOS)
+static char rcsid[] = "$Id: color.c 769 2007-10-24 00:15:40Z hubert@u.washington.edu $";
+#endif
+
+/* ========================================================================
+ * Copyright 2006-2007 University of Washington
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * ========================================================================
+ */
+
+#include <system.h>
+#include <general.h>
+
+#include "../../../pith/osdep/color.h"
+#include "../../../pith/osdep/collate.h"
+
+
+static COLOR_PAIR *the_rev_color;
+static char *_nfcolor, *_nbcolor, *_rfcolor, *_rbcolor;
+static char *_last_fg_color, *_last_bg_color;
+static int _force_fg_color_change, _force_bg_color_change;
+
+
+/* * * * * * * PITH-REQUIRED COLOR ROUTINES * * * * * * */
+
+/* internal prototypes */
+char *alpine_color_name(char *);
+int alpine_valid_rgb(char *s);
+
+int
+pico_usingcolor(void)
+{
+ return(TRUE);
+}
+
+
+int
+pico_hascolor(void)
+{
+ return(TRUE);
+}
+
+
+/*
+ * Web Alpine Color Table
+ */
+static struct color_table {
+ int number;
+ char *rgb;
+ struct {
+ char *s,
+ l;
+ } name;
+} webcoltab[] = {
+ {COL_BLACK, " 0, 0, 0", {"black", 5}},
+ {COL_RED, "255, 0, 0", {"red", 3}},
+ {COL_GREEN, " 0,255, 0", {"green", 5}},
+ {COL_YELLOW, "255,255, 0", {"yellow", 6}},
+ {COL_BLUE, " 0, 0,255", {"blue", 4}},
+ {COL_MAGENTA, "255, 0,255", {"magenta", 7}},
+ {COL_CYAN, " 0,255,255", {"cyan", 4}},
+ {COL_WHITE, "255,255,255", {"white", 5}},
+ {8, "192,192,192", {"color008", 8}}, /* light gray */
+ {9, "128,128,128", {"color009", 8}}, /* gray */
+ {10, " 64, 64, 64", {"color010", 8}}, /* dark gray */
+ {COL_YELLOW, "255,255, 0", {"color011", 8}},
+ {COL_BLUE, " 0, 0,255", {"color012", 8}},
+ {COL_MAGENTA, "255, 0,255", {"color013", 8}},
+ {COL_CYAN, " 0,255,255", {"color014", 8}},
+ {COL_WHITE, "255,255,255", {"color015", 8}},
+ {8, "192,192,192", {"colorlgr", 8}}, /* light gray */
+ {9, "128,128,128", {"colormgr", 8}}, /* gray */
+ {10, " 64, 64, 64", {"colordgr", 8}}, /* dark gray */
+ {16, "000,000,000", {"color016", 8}},
+ {17, "000,000,095", {"color017", 8}},
+ {18, "000,000,135", {"color018", 8}},
+ {19, "000,000,175", {"color019", 8}},
+ {20, "000,000,215", {"color020", 8}},
+ {21, "000,000,255", {"color021", 8}},
+ {22, "000,095,000", {"color022", 8}},
+ {23, "000,095,095", {"color023", 8}},
+ {24, "000,095,135", {"color024", 8}},
+ {25, "000,095,175", {"color025", 8}},
+ {26, "000,095,215", {"color026", 8}},
+ {27, "000,095,255", {"color027", 8}},
+ {28, "000,135,000", {"color028", 8}},
+ {29, "000,135,095", {"color029", 8}},
+ {30, "000,135,135", {"color030", 8}},
+ {31, "000,135,175", {"color031", 8}},
+ {32, "000,135,215", {"color032", 8}},
+ {33, "000,135,255", {"color033", 8}},
+ {34, "000,175,000", {"color034", 8}},
+ {35, "000,175,095", {"color035", 8}},
+ {36, "000,175,135", {"color036", 8}},
+ {37, "000,175,175", {"color037", 8}},
+ {38, "000,175,215", {"color038", 8}},
+ {39, "000,175,255", {"color039", 8}},
+ {40, "000,215,000", {"color040", 8}},
+ {41, "000,215,095", {"color041", 8}},
+ {42, "000,215,135", {"color042", 8}},
+ {43, "000,215,175", {"color043", 8}},
+ {44, "000,215,215", {"color044", 8}},
+ {45, "000,215,255", {"color045", 8}},
+ {46, "000,255,000", {"color046", 8}},
+ {47, "000,255,095", {"color047", 8}},
+ {48, "000,255,135", {"color048", 8}},
+ {49, "000,255,175", {"color049", 8}},
+ {50, "000,255,215", {"color050", 8}},
+ {51, "000,255,255", {"color051", 8}},
+ {52, "095,000,000", {"color052", 8}},
+ {53, "095,000,095", {"color053", 8}},
+ {54, "095,000,135", {"color054", 8}},
+ {55, "095,000,175", {"color055", 8}},
+ {56, "095,000,215", {"color056", 8}},
+ {57, "095,000,255", {"color057", 8}},
+ {58, "095,095,000", {"color058", 8}},
+ {59, "095,095,095", {"color059", 8}},
+ {60, "095,095,135", {"color060", 8}},
+ {61, "095,095,175", {"color061", 8}},
+ {62, "095,095,215", {"color062", 8}},
+ {63, "095,095,255", {"color063", 8}},
+ {64, "095,135,000", {"color064", 8}},
+ {65, "095,135,095", {"color065", 8}},
+ {66, "095,135,135", {"color066", 8}},
+ {67, "095,135,175", {"color067", 8}},
+ {68, "095,135,215", {"color068", 8}},
+ {69, "095,135,255", {"color069", 8}},
+ {70, "095,175,000", {"color070", 8}},
+ {71, "095,175,095", {"color071", 8}},
+ {72, "095,175,135", {"color072", 8}},
+ {73, "095,175,175", {"color073", 8}},
+ {74, "095,175,215", {"color074", 8}},
+ {75, "095,175,255", {"color075", 8}},
+ {76, "095,215,000", {"color076", 8}},
+ {77, "095,215,095", {"color077", 8}},
+ {78, "095,215,135", {"color078", 8}},
+ {79, "095,215,175", {"color079", 8}},
+ {80, "095,215,215", {"color080", 8}},
+ {81, "095,215,255", {"color081", 8}},
+ {82, "095,255,000", {"color082", 8}},
+ {83, "095,255,095", {"color083", 8}},
+ {84, "095,255,135", {"color084", 8}},
+ {85, "095,255,175", {"color085", 8}},
+ {86, "095,255,215", {"color086", 8}},
+ {87, "095,255,255", {"color087", 8}},
+ {88, "135,000,000", {"color088", 8}},
+ {89, "135,000,095", {"color089", 8}},
+ {90, "135,000,135", {"color090", 8}},
+ {91, "135,000,175", {"color091", 8}},
+ {92, "135,000,215", {"color092", 8}},
+ {93, "135,000,255", {"color093", 8}},
+ {94, "135,095,000", {"color094", 8}},
+ {95, "135,095,095", {"color095", 8}},
+ {96, "135,095,135", {"color096", 8}},
+ {97, "135,095,175", {"color097", 8}},
+ {98, "135,095,215", {"color098", 8}},
+ {99, "135,095,255", {"color099", 8}},
+ {100, "135,135,000", {"color100", 8}},
+ {101, "135,135,095", {"color101", 8}},
+ {102, "135,135,135", {"color102", 8}},
+ {103, "135,135,175", {"color103", 8}},
+ {104, "135,135,215", {"color104", 8}},
+ {105, "135,135,255", {"color105", 8}},
+ {106, "135,175,000", {"color106", 8}},
+ {107, "135,175,095", {"color107", 8}},
+ {108, "135,175,135", {"color108", 8}},
+ {109, "135,175,175", {"color109", 8}},
+ {110, "135,175,215", {"color110", 8}},
+ {111, "135,175,255", {"color111", 8}},
+ {112, "135,215,000", {"color112", 8}},
+ {113, "135,215,095", {"color113", 8}},
+ {114, "135,215,135", {"color114", 8}},
+ {115, "135,215,175", {"color115", 8}},
+ {116, "135,215,215", {"color116", 8}},
+ {117, "135,215,255", {"color117", 8}},
+ {118, "135,255,000", {"color118", 8}},
+ {119, "135,255,095", {"color119", 8}},
+ {120, "135,255,135", {"color120", 8}},
+ {121, "135,255,175", {"color121", 8}},
+ {122, "135,255,215", {"color122", 8}},
+ {123, "135,255,255", {"color123", 8}},
+ {124, "175,000,000", {"color124", 8}},
+ {125, "175,000,095", {"color125", 8}},
+ {126, "175,000,135", {"color126", 8}},
+ {127, "175,000,175", {"color127", 8}},
+ {128, "175,000,215", {"color128", 8}},
+ {129, "175,000,255", {"color129", 8}},
+ {130, "175,095,000", {"color130", 8}},
+ {131, "175,095,095", {"color131", 8}},
+ {132, "175,095,135", {"color132", 8}},
+ {133, "175,095,175", {"color133", 8}},
+ {134, "175,095,215", {"color134", 8}},
+ {135, "175,095,255", {"color135", 8}},
+ {136, "175,135,000", {"color136", 8}},
+ {137, "175,135,095", {"color137", 8}},
+ {138, "175,135,135", {"color138", 8}},
+ {139, "175,135,175", {"color139", 8}},
+ {140, "175,135,215", {"color140", 8}},
+ {141, "175,135,255", {"color141", 8}},
+ {142, "175,175,000", {"color142", 8}},
+ {143, "175,175,095", {"color143", 8}},
+ {144, "175,175,135", {"color144", 8}},
+ {145, "175,175,175", {"color145", 8}},
+ {146, "175,175,215", {"color146", 8}},
+ {147, "175,175,255", {"color147", 8}},
+ {148, "175,215,000", {"color148", 8}},
+ {149, "175,215,095", {"color149", 8}},
+ {150, "175,215,135", {"color150", 8}},
+ {151, "175,215,175", {"color151", 8}},
+ {152, "175,215,215", {"color152", 8}},
+ {153, "175,215,255", {"color153", 8}},
+ {154, "175,255,000", {"color154", 8}},
+ {155, "175,255,095", {"color155", 8}},
+ {156, "175,255,135", {"color156", 8}},
+ {157, "175,255,175", {"color157", 8}},
+ {158, "175,255,215", {"color158", 8}},
+ {159, "175,255,255", {"color159", 8}},
+ {160, "215,000,000", {"color160", 8}},
+ {161, "215,000,095", {"color161", 8}},
+ {162, "215,000,135", {"color162", 8}},
+ {163, "215,000,175", {"color163", 8}},
+ {164, "215,000,215", {"color164", 8}},
+ {165, "215,000,255", {"color165", 8}},
+ {166, "215,095,000", {"color166", 8}},
+ {167, "215,095,095", {"color167", 8}},
+ {168, "215,095,135", {"color168", 8}},
+ {169, "215,095,175", {"color169", 8}},
+ {170, "215,095,215", {"color170", 8}},
+ {171, "215,095,255", {"color171", 8}},
+ {172, "215,135,000", {"color172", 8}},
+ {173, "215,135,095", {"color173", 8}},
+ {174, "215,135,135", {"color174", 8}},
+ {175, "215,135,175", {"color175", 8}},
+ {176, "215,135,215", {"color176", 8}},
+ {177, "215,135,255", {"color177", 8}},
+ {178, "215,175,000", {"color178", 8}},
+ {179, "215,175,095", {"color179", 8}},
+ {180, "215,175,135", {"color180", 8}},
+ {181, "215,175,175", {"color181", 8}},
+ {182, "215,175,215", {"color182", 8}},
+ {183, "215,175,255", {"color183", 8}},
+ {184, "215,215,000", {"color184", 8}},
+ {185, "215,215,095", {"color185", 8}},
+ {186, "215,215,135", {"color186", 8}},
+ {187, "215,215,175", {"color187", 8}},
+ {188, "215,215,215", {"color188", 8}},
+ {189, "215,215,255", {"color189", 8}},
+ {190, "215,255,000", {"color190", 8}},
+ {191, "215,255,095", {"color191", 8}},
+ {192, "215,255,135", {"color192", 8}},
+ {193, "215,255,175", {"color193", 8}},
+ {194, "215,255,215", {"color194", 8}},
+ {195, "215,255,255", {"color195", 8}},
+ {196, "255,000,000", {"color196", 8}},
+ {197, "255,000,095", {"color197", 8}},
+ {198, "255,000,135", {"color198", 8}},
+ {199, "255,000,175", {"color199", 8}},
+ {200, "255,000,215", {"color200", 8}},
+ {201, "255,000,255", {"color201", 8}},
+ {202, "255,095,000", {"color202", 8}},
+ {203, "255,095,095", {"color203", 8}},
+ {204, "255,095,135", {"color204", 8}},
+ {205, "255,095,175", {"color205", 8}},
+ {206, "255,095,215", {"color206", 8}},
+ {207, "255,095,255", {"color207", 8}},
+ {208, "255,135,000", {"color208", 8}},
+ {209, "255,135,095", {"color209", 8}},
+ {210, "255,135,135", {"color210", 8}},
+ {211, "255,135,175", {"color211", 8}},
+ {212, "255,135,215", {"color212", 8}},
+ {213, "255,135,255", {"color213", 8}},
+ {214, "255,175,000", {"color214", 8}},
+ {215, "255,175,095", {"color215", 8}},
+ {216, "255,175,135", {"color216", 8}},
+ {217, "255,175,175", {"color217", 8}},
+ {218, "255,175,215", {"color218", 8}},
+ {219, "255,175,255", {"color219", 8}},
+ {220, "255,215,000", {"color220", 8}},
+ {221, "255,215,095", {"color221", 8}},
+ {222, "255,215,135", {"color222", 8}},
+ {223, "255,215,175", {"color223", 8}},
+ {224, "255,215,215", {"color224", 8}},
+ {225, "255,215,255", {"color225", 8}},
+ {226, "255,255,000", {"color226", 8}},
+ {227, "255,255,095", {"color227", 8}},
+ {228, "255,255,135", {"color228", 8}},
+ {229, "255,255,175", {"color229", 8}},
+ {230, "255,255,215", {"color230", 8}},
+ {231, "255,255,255", {"color231", 8}}
+};
+
+
+char *
+colorx(int color)
+{
+ int i;
+ static char cbuf[12];
+
+ for(i = 0; i < sizeof(webcoltab) / sizeof(struct color_table); i++)
+ if(color == webcoltab[i].number)
+ return(webcoltab[i].rgb);
+
+ sprintf(cbuf, "color%3.3d", color);
+ return(cbuf);
+}
+
+
+/*
+ * Return a pointer to an rgb string for the input color. The output is 11
+ * characters long and looks like rrr,ggg,bbb.
+ *
+ * Args colorName -- The color to convert to ascii rgb.
+ *
+ * Returns Pointer to a static buffer containing the rgb string.
+ */
+char *
+color_to_asciirgb(char *colorName)
+{
+ int i;
+ static char c_to_a_buf[3][RGBLEN+1];
+ static int whichbuf = 0;
+
+ whichbuf = (whichbuf + 1) % 3;
+
+ for(i = 0; i < sizeof(webcoltab) / sizeof(struct color_table); i++)
+ if(!strucmp(webcoltab[i].name.s, colorName))
+ return(webcoltab[i].rgb);
+
+ /*
+ * If we didn't find the color it could be that it is the
+ * normal color (MATCH_NORM_COLOR) or the none color
+ * (MATCH_NONE_COLOR). If that is the case, this strncpy thing
+ * will work out correctly because those two strings are
+ * RGBLEN long. Otherwise we're in a bit of trouble. This
+ * most likely means that the user is using the same pinerc on
+ * two terminals, one with more colors than the other. We didn't
+ * find a match because this color isn't present on this terminal.
+ * Since the return value of this function is assumed to be
+ * RGBLEN long, we'd better make it that long.
+ * It still won't work correctly because colors will be screwed up,
+ * but at least the embedded colors in filter.c will get properly
+ * sucked up when they're encountered.
+ */
+ strncpy(c_to_a_buf[whichbuf], "xxxxxxxxxxx", RGBLEN); /* RGBLEN is 11 */
+ i = strlen(colorName);
+ strncpy(c_to_a_buf[whichbuf], colorName, (i < RGBLEN) ? i : RGBLEN);
+ c_to_a_buf[whichbuf][RGBLEN] = '\0';
+ return(c_to_a_buf[whichbuf]);
+}
+
+
+
+int
+pico_is_good_color(char *s)
+{
+ return(alpine_color_name(s) != NULL || alpine_valid_rgb(s));
+}
+
+
+int
+alpine_valid_rgb(char *s)
+{
+ int i, j;
+
+ /* has to be three spaces or decimal digits followed by a dot.*/
+
+ for(i = 0; i < 3; i++){
+ int n = 0;
+
+ for(j = 0; j < 3; j++, s++) {
+ if(*s == ' '){
+ if(n)
+ return(FALSE);
+ }
+ else if(isdigit((unsigned char) *s)){
+ n = (n * 10) + (*s - '0');
+ }
+ else
+ return(FALSE);
+ }
+
+ if (i < 2 && *s++ != ',')
+ return(FALSE);
+ }
+
+ return (TRUE);
+}
+
+
+
+char *
+alpine_color_name(char *s)
+{
+ if(s){
+ int i;
+
+ if(!struncmp(s, MATCH_NORM_COLOR, RGBLEN) || !struncmp(s, MATCH_NONE_COLOR, RGBLEN))
+ return(s);
+ else if(*s == ' ' || isdigit(*s)){
+ /* check for rgb string instead of name */
+ for(i = 0; i < sizeof(webcoltab) / sizeof(struct color_table); i++)
+ if(!strncmp(webcoltab[i].rgb, s, RGBLEN))
+ return(webcoltab[i].name.s);
+ }
+ else{
+ for(i = 0; i < sizeof(webcoltab) / sizeof(struct color_table); i++)
+ if(!struncmp(webcoltab[i].name.s, s, webcoltab[i].name.l))
+ return(webcoltab[i].name.s);
+ }
+ }
+
+ return(NULL);
+}
+
+
+/*
+ * Sets color to (fg,bg).
+ * Flags == PSC_NONE No alternate default if fg,bg fails.
+ * == PSC_NORM Set it to Normal color on failure.
+ * == PSC_REV Set it to Reverse color on failure.
+ *
+ * If flag PSC_RET is set, returns an allocated copy of the previous
+ * color pair, otherwise returns NULL.
+ */
+COLOR_PAIR *
+pico_set_colors(char *fg, char *bg, int flags)
+{
+ int uc;
+ COLOR_PAIR *cp = NULL, *rev = NULL;
+
+ if(flags & PSC_RET)
+ cp = pico_get_cur_color();
+
+ if(!((uc = pico_usingcolor())
+ && fg && bg
+ && pico_set_fg_color(fg) && pico_set_bg_color(bg))){
+
+ if(uc && flags & PSC_NORM){
+ pico_set_normal_color();
+ }
+ else if(flags & PSC_REV){
+ if((rev = pico_get_rev_color()) != NULL){
+ pico_set_fg_color(rev->fg); /* these will succeed */
+ pico_set_bg_color(rev->bg);
+ }
+ }
+ }
+
+ return(cp);
+}
+
+
+
+void
+pico_nfcolor(char *s)
+{
+ if(_nfcolor){
+ free(_nfcolor);
+ _nfcolor = NULL;
+ }
+
+ if(s){
+ _nfcolor = (char *)malloc(strlen(s)+1);
+ if(_nfcolor)
+ strcpy(_nfcolor, s);
+ }
+}
+
+
+void
+pico_nbcolor(char *s)
+{
+ if(_nbcolor){
+ free(_nbcolor);
+ _nbcolor = NULL;
+ }
+
+ if(s){
+ _nbcolor = (char *)malloc(strlen(s)+1);
+ if(_nbcolor)
+ strcpy(_nbcolor, s);
+ }
+}
+
+void
+pico_rfcolor(char *s)
+{
+ if(_rfcolor){
+ free(_rfcolor);
+ _rfcolor = NULL;
+ }
+
+ if(s){
+ _rfcolor = (char *)malloc(strlen(s)+1);
+ if(_rfcolor)
+ strcpy(_rfcolor, s);
+
+ if(the_rev_color)
+ strcpy(the_rev_color->fg, _rfcolor);
+ }
+ else if(the_rev_color)
+ free_color_pair(&the_rev_color);
+}
+
+void
+pico_rbcolor(char *s)
+{
+ if(_rbcolor){
+ free(_rbcolor);
+ _rbcolor = NULL;
+ }
+
+ if(s){
+ _rbcolor = (char *)malloc(strlen(s)+1);
+ if(_rbcolor)
+ strcpy(_rbcolor, s);
+
+ if(the_rev_color)
+ strcpy(the_rev_color->bg, _rbcolor);
+ }
+ else if(the_rev_color)
+ free_color_pair(&the_rev_color);
+}
+
+
+void
+pico_endcolor(void)
+{
+ if(_nfcolor){
+ free(_nfcolor);
+ _nfcolor = NULL;
+ }
+
+ if(_nbcolor){
+ free(_nbcolor);
+ _nbcolor = NULL;
+ }
+
+ if(_rfcolor){
+ free(_rfcolor);
+ _rfcolor = NULL;
+ }
+
+ if(_rbcolor){
+ free(_rbcolor);
+ _rbcolor = NULL;
+ }
+
+ if(the_rev_color)
+ free_color_pair(&the_rev_color);
+}
+
+
+COLOR_PAIR *
+pico_get_cur_color(void)
+{
+ return(new_color_pair(_last_fg_color, _last_bg_color));
+}
+
+/*
+ * If inverse is a color, returns a pointer to that color.
+ * If not, returns NULL.
+ *
+ * NOTE: Don't free this!
+ */
+COLOR_PAIR *
+pico_get_rev_color(void)
+{
+ if(pico_usingcolor() && _rfcolor && _rbcolor &&
+ pico_is_good_color(_rfcolor) && pico_is_good_color(_rbcolor)){
+ if(!the_rev_color)
+ the_rev_color = new_color_pair(_rfcolor, _rbcolor);
+
+ return(the_rev_color);
+ }
+ else
+ return(NULL);
+}
+
+
+int
+pico_set_fg_color(char *s)
+{
+ if(pico_is_good_color(s)){
+ if(!struncmp(s, MATCH_NORM_COLOR, RGBLEN))
+ s = _nfcolor;
+ else if(!struncmp(s, MATCH_NONE_COLOR, RGBLEN))
+ return(TRUE);
+
+ /* already set correctly */
+ if(!_force_fg_color_change
+ && _last_fg_color
+ && !strcmp(_last_fg_color, s))
+ return(TRUE);
+
+ _force_fg_color_change = 0;
+ if(_last_fg_color)
+ free(_last_fg_color);
+
+ if((_last_fg_color = (char *) malloc(strlen(s) + 1)) != NULL)
+ strcpy(_last_fg_color, s);
+
+ return(TRUE);
+ }
+
+ return(FALSE);
+}
+
+
+int
+pico_set_bg_color(char *s)
+{
+ if(pico_is_good_color(s)){
+ if(!struncmp(s, MATCH_NORM_COLOR, RGBLEN))
+ s = _nbcolor;
+ else if(!struncmp(s, MATCH_NONE_COLOR, RGBLEN))
+ return(TRUE);
+
+ /* already set correctly */
+ if(!_force_bg_color_change
+ && _last_bg_color
+ && !strcmp(_last_bg_color, s))
+ return(TRUE);
+
+ _force_bg_color_change = 0;
+ if(_last_bg_color)
+ free(_last_bg_color);
+
+ if((_last_bg_color = (char *) malloc(strlen(s) + 1)) != NULL)
+ strcpy(_last_bg_color, s);
+
+ return(TRUE);
+ }
+
+ return(FALSE);
+}
+
+
+void
+pico_set_normal_color(void)
+{
+ if(!_nfcolor || !_nbcolor ||
+ !pico_set_fg_color(_nfcolor) || !pico_set_bg_color(_nbcolor)){
+ (void)pico_set_fg_color(DEFAULT_NORM_FORE_RGB);
+ (void)pico_set_bg_color(DEFAULT_NORM_BACK_RGB);
+ }
+}
+
+
+char *
+pico_get_last_fg_color(void)
+{
+ char *ret = NULL;
+
+ if(_last_fg_color)
+ if((ret = (char *)malloc(strlen(_last_fg_color)+1)) != NULL)
+ strcpy(ret, _last_fg_color);
+
+ return(ret);
+}
+
+char *
+pico_get_last_bg_color(void)
+{
+ char *ret = NULL;
+
+ if(_last_bg_color)
+ if((ret = (char *)malloc(strlen(_last_bg_color)+1)) != NULL)
+ strcpy(ret, _last_bg_color);
+
+ return(ret);
+}
diff --git a/web/src/alpined.d/color.h b/web/src/alpined.d/color.h
new file mode 100644
index 00000000..3040ffbc
--- /dev/null
+++ b/web/src/alpined.d/color.h
@@ -0,0 +1,25 @@
+/*-----------------------------------------------------------------------
+ $Id: color.h 764 2007-10-23 23:44:49Z hubert@u.washington.edu $
+ -----------------------------------------------------------------------*/
+
+/* ========================================================================
+ * Copyright 2006-2007 University of Washington
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * ========================================================================
+ */
+
+#ifndef _WEB_ALPINE_COLOR_INCLUDED
+#define _WEB_ALPINE_COLOR_INCLUDED
+
+void pico_endcolor(void);
+
+#endif /* _WEB_ALPINE_COLOR_INCLUDED */
+
+
+
diff --git a/web/src/alpined.d/debug.c b/web/src/alpined.d/debug.c
new file mode 100644
index 00000000..d3c4d2b7
--- /dev/null
+++ b/web/src/alpined.d/debug.c
@@ -0,0 +1,151 @@
+#if !defined(lint) && !defined(DOS)
+static char rcsid[] = "$Id: wimap.c 73 2006-06-13 16:46:59Z hubert@u.washington.edu $";
+#endif
+
+/* ========================================================================
+ * Copyright 2006-2007 University of Washington
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * ========================================================================
+ */
+
+/*======================================================================
+ debug.c
+ Provide debug support routines
+ ====*/
+
+#include <system.h>
+#include <general.h>
+
+#include "../../../c-client/c-client.h"
+
+#include "../../../pith/state.h"
+#include "../../../pith/debug.h"
+
+#include "debug.h"
+
+
+#define MAX_DEBUG_FMT 1024
+
+
+#ifndef DEBUG
+/*
+ * Preserve debug for syslog trace
+ */
+int debug;
+#endif
+
+
+void
+debug_init(void)
+{
+#if HAVE_SYSLOG
+ openlog("alpined", LOG_PID, LOG_MAIL);
+#endif
+}
+
+
+void
+output_debug_msg(int dlevel, char *fmt, ...)
+{
+ /* always write SYSDBG */
+ if((dlevel & SYSDBG) || dlevel <= debug){
+#if HAVE_SYSLOG
+ va_list args;
+ char fmt2[MAX_DEBUG_FMT], *p, *q, *trailing = NULL;
+ int priority = LOG_DEBUG, leading = 1;
+
+ /* whack nl's */
+ for(p = fmt, q = fmt2; *p && p - fmt < MAX_DEBUG_FMT - 2; p++){
+ if(*p == '\n'){
+ if(!leading && !trailing)
+ trailing = q;
+ }
+ else{
+ leading = 0;
+ if(trailing){
+ *q++ = '_';
+ trailing = NULL;
+ }
+
+ *q++ = *p;
+ }
+ }
+
+ *q = '\0';
+ if(trailing)
+ *trailing = '\0';
+
+ if(dlevel & SYSDBG)
+ switch(dlevel){
+ case SYSDBG_ALERT : priority = LOG_ALERT; break;
+ case SYSDBG_ERR : priority = LOG_ERR; break;
+ case SYSDBG_INFO : priority = LOG_INFO; break;
+ default : priority = LOG_DEBUG; break;
+ }
+
+
+ va_start(args, fmt);
+ vsyslog(priority, fmt2, args);
+ va_end(args);
+#else
+# error Write something to record error/debugging output
+#endif
+ }
+}
+
+#ifdef DEBUG
+
+void
+dump_configuration(int brief)
+{
+ dprint((8, "asked to dump_configuration"));
+}
+
+
+void
+dump_contexts(void)
+{
+ dprint((8, "asked to dump_contexts"));
+}
+
+
+void
+setup_imap_debug(void)
+{
+ int olddebug;
+
+ olddebug = debug;
+
+ if(debug > 7)
+ ps_global->debug_imap = 4;
+ else if(debug > 6)
+ ps_global->debug_imap = 3;
+ else if(debug > 4)
+ ps_global->debug_imap = 2;
+ else if(debug > 2)
+ ps_global->debug_imap = 1;
+ else
+ ps_global->debug_imap = 0;
+
+ if(ps_global->mail_stream){
+ if(ps_global->debug_imap > 0){
+ mail_debug(ps_global->mail_stream);
+ }
+ else{
+ mail_nodebug(ps_global->mail_stream);
+ }
+ }
+
+ if(debug > 7 && olddebug <= 7)
+ mail_parameters(NULL, SET_TCPDEBUG, (void *) TRUE);
+ else if(debug <= 7 && olddebug > 7 && !ps_global->debugmem)
+ mail_parameters(NULL, SET_TCPDEBUG, (void *) FALSE);
+
+}
+#endif /* DEBUG */
diff --git a/web/src/alpined.d/debug.h b/web/src/alpined.d/debug.h
new file mode 100644
index 00000000..9a44ba81
--- /dev/null
+++ b/web/src/alpined.d/debug.h
@@ -0,0 +1,52 @@
+/*-----------------------------------------------------------------------
+ $Id: debug.h 130 2006-09-22 04:39:36Z mikes@u.washington.edu $
+ -----------------------------------------------------------------------*/
+
+/* ========================================================================
+ * Copyright 2006-2007 University of Washington
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * ========================================================================
+ */
+
+#ifndef _WEB_ALPINE_DEBUG_INCLUDED
+#define _WEB_ALPINE_DEBUG_INCLUDED
+
+
+#ifndef DEBUG
+/*
+ * support dprint regardless so we leave at least a few
+ * footsteps in syslog
+ */
+#undef dprint
+#define dprint(x) { output_debug_msg x ; }
+
+/* alpined-scoped debugging level */
+extern int debug;
+
+void output_debug_msg(int, char *fmt, ...);
+#endif
+
+
+/*
+ * Use these to for dprint() debug level arg to force
+ * debug output (typically to syslog())
+ */
+#define SYSDBG 0x8000
+#define SYSDBG_ALERT SYSDBG+1
+#define SYSDBG_ERR SYSDBG+2
+#define SYSDBG_INFO SYSDBG+3
+#define SYSDBG_DEBUG SYSDBG+4
+
+
+/* exported prototypes */
+void debug_init(void);
+void setup_imap_debug(void);
+
+
+#endif /* _WEB_ALPINE_DEBUG_INCLUDED */
diff --git a/web/src/alpined.d/imap.c b/web/src/alpined.d/imap.c
new file mode 100644
index 00000000..6872d085
--- /dev/null
+++ b/web/src/alpined.d/imap.c
@@ -0,0 +1,516 @@
+#if !defined(lint) && !defined(DOS)
+static char rcsid[] = "$Id: imap.c 1142 2008-08-13 17:22:21Z hubert@u.washington.edu $";
+#endif
+
+/* ========================================================================
+ * Copyright 2006-2007 University of Washington
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * ========================================================================
+ */
+
+/*======================================================================
+ imap.c
+ The call back routines for the c-client/imap
+ - handles error messages and other notification
+ - handles prelimirary notification of new mail and expunged mail
+ - prompting for imap server login and password
+
+ ====*/
+
+#include <system.h>
+#include <general.h>
+
+#include "../../../c-client/c-client.h"
+
+#include "../../../pith/state.h"
+#include "../../../pith/debug.h"
+#include "../../../pith/string.h"
+#include "../../../pith/flag.h"
+#include "../../../pith/imap.h"
+#include "../../../pith/status.h"
+#include "../../../pith/osdep/collate.h"
+
+#include "debug.h"
+#include "alpined.h"
+
+
+
+/*
+ * Internal prototypes
+ */
+long imap_seq_exec(MAILSTREAM *, char *,long (*)(MAILSTREAM *, long, void *), void *);
+long imap_seq_exec_append(MAILSTREAM *, long, void *);
+
+
+/*
+ * Exported globals setup by searching functions to tell mm_searched
+ * where to put message numbers that matched the search criteria,
+ * and to allow mm_searched to return number of matches.
+ */
+MAILSTREAM *mm_search_stream;
+
+MM_LIST_S *mm_list_info;
+
+
+
+/*----------------------------------------------------------------------
+ Queue imap log message for display in the message line
+
+ Args: string -- The message
+ errflg -- flag set to 1 if pertains to an error
+
+ Result: Message queued for display
+
+ The c-client/imap reports most of it's status and errors here
+ ---*/
+void
+mm_log(char *string, long errflg)
+{
+ char message[300];
+ char *occurance;
+ int was_capitalized;
+ time_t now;
+ struct tm *tm_now;
+
+ if(errflg == ERROR){
+ dprint((SYSDBG_ERR, "%.*s (%ld)", 128, string, errflg));
+ }
+
+ now = time((time_t *)0);
+ tm_now = localtime(&now);
+
+ dprint((ps_global->debug_imap ? 0 : (errflg == ERROR ? 1 : 2),
+ "IMAP %2.2d:%2.2d:%2.2d %d/%d mm_log %s: %s\n",
+ tm_now->tm_hour, tm_now->tm_min, tm_now->tm_sec, tm_now->tm_mon+1,
+ tm_now->tm_mday,
+ (errflg == ERROR)
+ ? "ERROR"
+ : (errflg == WARN)
+ ? "warn"
+ : (errflg == PARSE)
+ ? "parse"
+ : "babble",
+ string));
+
+ if(errflg == ERROR && !strncmp(string, "[TRYCREATE]", 11)){
+ ps_global->try_to_create = 1;
+ return;
+ }
+ else if(ps_global->try_to_create
+ || (sp_dead_stream(ps_global->mail_stream)
+ && (!strncmp(string, "[CLOSED]", 8) || strstr(string, "No-op"))))
+ /*
+ * Don't display if creating new folder OR
+ * warning about a dead stream ...
+ */
+ return;
+
+ /*---- replace all "mailbox" with "folder" ------*/
+ strncpy(message, string, sizeof(message));
+ message[sizeof(message) - 1] = '\0';
+ occurance = srchstr(message, "mailbox");
+ while(occurance) {
+ if(!*(occurance+7) || isspace((unsigned char)*(occurance+7))){
+ was_capitalized = isupper((unsigned char)*occurance);
+ rplstr(occurance, 7, 7, (errflg == PARSE ? "address" : "folder"));
+ if(was_capitalized)
+ *occurance = (errflg == PARSE ? 'A' : 'F');
+ }
+ else
+ occurance += 7;
+
+ occurance = srchstr(occurance, "mailbox");
+ }
+
+ if(errflg == ERROR)
+ ps_global->mm_log_error = 1;
+
+ if(errflg == PARSE || (errflg == ERROR && ps_global->noshow_error)){
+ strncpy(ps_global->c_client_error, message, sizeof(ps_global->c_client_error));
+ ps_global->c_client_error[sizeof(ps_global->c_client_error)-1] = '\0';
+ }
+
+ if(ps_global->noshow_error
+ || (ps_global->noshow_warn && errflg == WARN)
+ || !(errflg == ERROR || errflg == WARN))
+ return; /* Only care about errors; don't print when asked not to */
+
+ /*---- Display the message ------*/
+ q_status_message((errflg == ERROR) ? (SM_ORDER | SM_DING) : SM_ORDER,
+ 3, 5, message);
+ if(errflg == ERROR){
+ strncpy(ps_global->last_error, message, sizeof(ps_global->last_error));
+ ps_global->last_error[sizeof(ps_global->last_error)-1] = '\0';
+ }
+}
+
+
+
+/*----------------------------------------------------------------------
+ recieve notification from IMAP
+
+ Args: stream -- Mail stream message is relavant to
+ string -- The message text
+ errflag -- Set if it is a serious error
+
+ Result: message displayed in status line
+
+ The facility is for general notices, such as connection to server;
+ server shutting down etc... It is used infrequently.
+ ----------------------------------------------------------------------*/
+void
+mm_notify(MAILSTREAM *stream, char *string, long errflag)
+{
+ if(errflag == ERROR){
+ dprint((SYSDBG_ERR, "mm_notify: %s (%ld)", string, errflag));
+ }
+
+ /* be sure to log the message... */
+#ifdef DEBUG
+ if(ps_global->debug_imap)
+ dprint((0, "IMAP mm_notify %s : %s (%s) : %s\n",
+ (!errflag) ? "NIL" :
+ (errflag == ERROR) ? "error" :
+ (errflag == WARN) ? "warning" :
+ (errflag == BYE) ? "bye" : "unknown",
+ (stream && stream->mailbox) ? stream->mailbox : "-no folder-",
+ (stream && stream == sp_inbox_stream()) ? "inboxstream" :
+ (stream && stream == ps_global->mail_stream) ? "mailstream" :
+ (stream) ? "abookstream?" : "nostream",
+ string));
+#endif
+
+ strncpy(ps_global->last_error, string, 500);
+ ps_global->last_error[499] = '\0';
+
+ /*
+ * Then either set special bits in the pine struct or
+ * display the message if it's tagged as an "ALERT" or
+ * its errflag > NIL (i.e., WARN, or ERROR)
+ */
+ if(errflag == BYE){
+ if(stream == ps_global->mail_stream){
+ if(sp_dead_stream(ps_global->mail_stream))
+ return;
+ else
+ sp_set_dead_stream(ps_global->mail_stream, 1);
+ }
+ else if(stream && stream == sp_inbox_stream()){
+ if(sp_dead_stream(stream))
+ return;
+ else
+ sp_set_dead_stream(stream, 1);
+ }
+ }
+ else if(!strncmp(string, "[TRYCREATE]", 11))
+ ps_global->try_to_create = 1;
+ else if(!strncmp(string, "[ALERT]", 7))
+ q_status_message2(SM_MODAL, 3, 3, "Alert received while accessing \"%s\": %s",
+ (stream && stream->mailbox)
+ ? stream->mailbox : "-no folder-",
+ rfc1522_decode_to_utf8((unsigned char *) tmp_20k_buf, SIZEOF_20KBUF, string));
+ else if(!strncmp(string, "[UNSEEN ", 8)){
+ char *p;
+ long n = 0;
+
+ for(p = string + 8; isdigit(*p); p++)
+ n = (n * 10) + (*p - '0');
+
+ sp_set_first_unseen(ps_global->mail_stream, n);
+ }
+ else if(!strncmp(string, "[READ-ONLY]", 11)
+ && !(stream && stream->mailbox && IS_NEWS(stream)))
+ q_status_message2(SM_ORDER | SM_DING, 3, 3, "%s : %s",
+ (stream && stream->mailbox)
+ ? stream->mailbox : "-no folder-",
+ string + 11);
+ else if(errflag && (errflag == WARN || errflag == ERROR))
+ q_status_message(SM_ORDER | ((errflag == ERROR) ? SM_DING : 0),
+ 3, 6, ps_global->last_error);
+}
+
+
+
+/*----------------------------------------------------------------------
+ Do work of getting login and password from user for IMAP login
+
+ Args: mb -- The mail box property struct
+ user -- Buffer to return the user name in
+ passwd -- Buffer to return the passwd in
+ trial -- The trial number or number of attempts to login
+
+ Result: username and password passed back to imap
+ ----*/
+void
+mm_login_work(NETMBX *mb, char *user, char *pwd, long trial, char *usethisprompt, char *altuserforcache)
+{
+ STRLIST_S hostlist[2];
+ NETMBX cmb;
+ int l;
+
+ pwd[0] = '\0';
+
+ if((l = strlen(mb->orighost)) > 0 && l < CRED_REQ_SIZE)
+ strcpy(peCredentialRequestor, mb->orighost);
+
+ if(trial){ /* one shot only! */
+ user[0] = '\0';
+ peCredentialError = 1;
+ return;
+ }
+
+#if 0
+ if(ps_global && ps_global->anonymous) {
+ /*------ Anonymous login mode --------*/
+ if(trial < 1) {
+ strcpy(user, "anonymous");
+ sprintf(pwd, "%s@%s", ps_global->VAR_USER_ID,
+ ps_global->hostname);
+ }
+ else
+ user[0] = pwd[0] = '\0';
+
+ return;
+ }
+#endif
+
+#if WEB_REQUIRE_SECURE_IMAP
+ /* we *require* secure authentication */
+ if(!(mb->sslflag || mb->tlsflag) && strcmp("localhost",mb->host)){
+ user[0] = pwd[0] = '\0';
+ return;
+ }
+#endif
+
+ /*
+ * heavily paranoid about offering password to server
+ * that the users hasn't also indicated the remote user
+ * name
+ */
+ if(*mb->user){
+ strcpy(user, mb->user);
+ }
+ else if(ps_global->prc
+ && ps_global->prc->name
+ && mail_valid_net_parse(ps_global->prc->name,&cmb)
+ && cmb.user){
+ strcpy(user, cmb.user);
+ }
+ else{
+ /*
+ * don't blindly offer user/pass
+ */
+ user[0] = pwd[0] = '\0';
+ return;
+ }
+
+ /*
+ * set up host list for sybil servers...
+ */
+ hostlist[0].name = mb->host;
+ if(mb->orighost[0] && strucmp(mb->host, mb->orighost)){
+ hostlist[0].next = &hostlist[1];
+ hostlist[1].name = mb->orighost;
+ hostlist[1].next = NULL;
+ }
+ else
+ hostlist[0].next = NULL;
+
+ /* try last working password associated with this host. */
+ if(!imap_get_passwd(mm_login_list, pwd, user, hostlist, (mb->sslflag || mb->tlsflag))){
+ peNoPassword = 1;
+ user[0] = pwd[0] = '\0';
+ }
+}
+
+
+
+/*----------------------------------------------------------------------
+ Receive notification of an error writing to disk
+
+ Args: stream -- The stream the error occured on
+ errcode -- The system error code (errno)
+ serious -- Flag indicating error is serious (mail may be lost)
+
+Result: If error is non serious, the stream is marked as having an error
+ and deletes are disallowed until error clears
+ If error is serious this returns with syslogging if possible
+ ----*/
+long
+mm_diskerror (MAILSTREAM *stream, long errcode, long serious)
+{
+ if(!serious && stream == ps_global->mail_stream) {
+ sp_set_io_error_on_stream(ps_global->mail_stream, 1);
+ }
+
+ dprint((SYSDBG_ERR, "mm_diskerror: mailbox: %s, errcode: %ld, serious: %ld\n",
+ (stream && stream->mailbox) ? stream->mailbox : "", errcode, serious));
+
+ return(1);
+}
+
+
+/*
+ * alpine_tcptimeout - C-client callback to handle tcp-related timeouts.
+ */
+long
+alpine_tcptimeout(long elapsed, long sincelast)
+{
+ long rv = 1L; /* keep trying by default */
+
+ dprint((SYSDBG_INFO, "tcptimeout: waited %s seconds\n", long2string(elapsed)));
+
+ if(elapsed > WP_TCP_TIMEOUT){
+ dprint((SYSDBG_ERR, "tcptimeout: BAIL after %s seconds\n", long2string(elapsed)));
+ rv = 0L;
+ }
+
+ return(rv);
+}
+
+
+/*
+ * C-client callback to handle SSL/TLS certificate validation failures
+ *
+ * Returning 0 means error becomes fatal
+ * Non-zero means certificate problem is ignored and SSL session is
+ * established
+ *
+ * We remember the answer and won't re-ask for subsequent open attempts to
+ * the same hostname.
+ */
+long
+alpine_sslcertquery(char *reason, char *host, char *cert)
+{
+ static char buf[256];
+ STRLIST_S *p;
+
+ for(p = peCertHosts; p; p = p->next)
+ if(!strucmp(p->name, host))
+ return(1);
+
+ peCertQuery = 1;
+ snprintf(peCredentialRequestor, CRED_REQ_SIZE, "%s++%s", host ? host : "?", reason ? reason : "UNKNOWN");
+ q_status_message(SM_ORDER, 0, 3, "SSL Certificate Problem");
+ dprint((SYSDBG_INFO, "sslcertificatequery: host=%s reason=%s cert=%s\n",
+ host ? host : "?", reason ? reason : "?",
+ cert ? cert : "?"));
+ return(0);
+}
+
+
+/*
+ * C-client callback to handle SSL/TLS certificate validation failures
+ */
+void
+alpine_sslfailure(char *host, char *reason, unsigned long flags)
+{
+ peCertFailure = 1;
+ snprintf(peCredentialRequestor, CRED_REQ_SIZE, "%s++%s", host ? host : "?", reason ? reason : "UNKNOWN");
+ q_status_message1(SM_ORDER, 0, 3, "SSL Certificate Failure: %s", reason ? reason : "?");
+ dprint((SYSDBG_INFO, "SSL Invalid Cert (%s) : %s", host, reason));
+}
+
+
+
+/*----------------------------------------------------------------------
+ This can be used to prevent the flickering of the check_cue char
+ caused by numerous (5000+) fetches by c-client. Right now, the only
+ practical use found is newsgroup subsciption.
+
+ check_cue_display will check if this global is set, and won't clear
+ the check_cue_char if set.
+ ----*/
+void
+set_read_predicted(int i)
+{
+}
+
+/*----------------------------------------------------------------------
+ Exported method to display status of mail check
+
+ Args: putstr -- should be NO LONGER THAN 2 bytes
+
+ Result: putstr displayed at upper-left-hand corner of screen
+ ----*/
+void
+check_cue_display(char *putstr)
+{
+}
+
+
+
+void
+alpine_set_passwd(char *user, char *passwd, char *host, int altflag)
+{
+ STRLIST_S hostlist[1];
+
+ hostlist[0].name = host;
+ hostlist[0].next = NULL;
+
+ imap_set_passwd(&mm_login_list, passwd, user, hostlist, altflag, 1, 0);
+}
+
+
+void
+alpine_clear_passwd(char *user, char *host)
+{
+ MMLOGIN_S **lp, *l;
+ STRLIST_S hostlist[1];
+
+ hostlist[0].name = host;
+ hostlist[0].next = NULL;
+
+ for(lp = &mm_login_list; *lp; lp = &(*lp)->next)
+ if(imap_same_host((*lp)->hosts, hostlist)
+ && (!*user || !strcmp(user, (*lp)->user))){
+ l = *lp;
+ *lp = (*lp)->next;
+
+ if(l->user)
+ fs_give((void **) &l->user);
+
+ free_strlist(&l->hosts);
+
+ if(l->passwd){
+ char *p = l->passwd;
+
+ while(*p)
+ *p++ = '\0';
+ }
+
+ fs_give((void **) &l);
+
+ break;
+ }
+}
+
+
+int
+alpine_have_passwd(char *user, char *host, int altflag)
+{
+ STRLIST_S hostlist[1];
+
+ hostlist[0].name = host;
+ hostlist[0].next = NULL;
+
+ return(imap_get_passwd(mm_login_list, NULL, user, hostlist, altflag));
+}
+
+
+char *
+alpine_get_user(char *host)
+{
+ STRLIST_S hostlist[1];
+
+ hostlist[0].name = host;
+ hostlist[0].next = NULL;
+
+ return(imap_get_user(mm_login_list, hostlist));
+}
diff --git a/web/src/alpined.d/imap.h b/web/src/alpined.d/imap.h
new file mode 100644
index 00000000..024e1781
--- /dev/null
+++ b/web/src/alpined.d/imap.h
@@ -0,0 +1,23 @@
+/*-----------------------------------------------------------------------
+ $Id: imap.h 82 2006-07-12 23:36:59Z mikes@u.washington.edu $
+ -----------------------------------------------------------------------*/
+
+#ifndef _WEB_ALPINE_IMAP_INCLUDED
+#define _WEB_ALPINE_IMAP_INCLUDED
+
+
+#include "../../../pith/imap.h"
+
+
+/* exported protoypes */
+long alpine_tcptimeout(long, long);
+long alpine_sslcertquery(char *, char *, char *);
+void alpine_sslfailure(char *, char *, unsigned long);
+void alpine_set_passwd(char *, char *, char *, int);
+void alpine_clear_passwd(char *, char *);
+int alpine_have_passwd(char *, char *, int);
+char *alpine_get_user(char *, int);
+
+
+
+#endif /* _WEB_ALPINE_IMAP_INCLUDED */
diff --git a/web/src/alpined.d/ldap.c b/web/src/alpined.d/ldap.c
new file mode 100644
index 00000000..3e6bf99f
--- /dev/null
+++ b/web/src/alpined.d/ldap.c
@@ -0,0 +1,241 @@
+#if !defined(lint) && !defined(DOS)
+static char rcsid[] = "$Id: ldap.c 1266 2009-07-14 18:39:12Z hubert@u.washington.edu $";
+#endif
+
+/* ========================================================================
+ * Copyright 2006-2008 University of Washington
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * ========================================================================
+ */
+
+#include <system.h>
+#include <general.h>
+
+#include "../../../c-client/c-client.h"
+
+#include "../../../pith/state.h"
+#include "../../../pith/debug.h"
+#include "../../../pith/adrbklib.h"
+#include "../../../pith/ldap.h"
+
+#include "ldap.h"
+
+
+#ifdef ENABLE_LDAP
+
+int
+ldap_addr_select(ps, ac, result, style, wp_err, srchstr)
+ struct pine *ps;
+ ADDR_CHOOSE_S *ac;
+ LDAP_CHOOSE_S **result;
+ LDAPLookupStyle style;
+ WP_ERR_S *wp_err;
+ char *srchstr;
+{
+ LDAP_SERV_RES_S *res_list, *tmp_rl;
+ LDAPMessage *e, *tmp_e;
+ char **mail = NULL, *a;
+ int got_n_entries = 0, retval = -5;
+ BerElement *ber;
+
+ dprint((7, "ldap_addr_select, srchstr: %s", srchstr));
+ for(res_list = ac->res_head; res_list; res_list = res_list->next){
+ tmp_rl = res_list;
+ for(e = ldap_first_entry(res_list->ld, res_list->res);
+ e != NULL;
+ e = ldap_next_entry(res_list->ld, e)){
+ tmp_e = e;
+ got_n_entries++;
+ }
+ }
+ if(got_n_entries == 1){
+ for(a = ldap_first_attribute(tmp_rl->ld, tmp_e, &ber);
+ a != NULL;
+ a = ldap_next_attribute(tmp_rl->ld, tmp_e, ber)){
+ if(strcmp(a, tmp_rl->info_used->mailattr) == 0){
+ mail = ldap_get_values(tmp_rl->ld, tmp_e, a);
+ break;
+ }
+ }
+ if(mail && mail[0] && mail[0][0]){
+ retval = 0;
+ if(result){
+ (*result) =
+ (LDAP_CHOOSE_S *)fs_get(sizeof(LDAP_CHOOSE_S));
+ (*result)->ld = tmp_rl->ld;
+ (*result)->selected_entry = tmp_e;
+ (*result)->info_used = tmp_rl->info_used;
+ (*result)->serv = tmp_rl->serv;
+ }
+ }
+ else{
+ retval = -2;
+ }
+ }
+ else
+ retval = -3;
+
+ return(retval);
+}
+
+
+char *
+peLdapPname(mailbox, host)
+ char *mailbox;
+ char *host;
+{
+ char *retstr = NULL;
+ char adrstr[1024], **cn;
+ int ecnt;
+ CUSTOM_FILT_S *filter;
+ WP_ERR_S wp_err;
+ LDAP_CHOOSE_S *winning_e = NULL;
+ LDAP_SERV_RES_S *results = NULL;
+ LDAP_SERV_RES_S *trl;
+ LDAPMessage *e;
+
+ sprintf(adrstr, "(mail=%.500s@%.500s)", mailbox, host);
+ filter = (CUSTOM_FILT_S *)fs_get(sizeof(CUSTOM_FILT_S));
+ filter->filt = cpystr(adrstr);
+ filter->combine = 0;
+ memset(&wp_err, 0, sizeof(wp_err));
+ wpldap_global->query_no++;
+ if(wpldap_global->ldap_search_list){
+ wpldap_global->ldap_search_list =
+ free_wpldapres(wpldap_global->ldap_search_list);
+ }
+ ldap_lookup_all("", 0, 0, AlwaysDisplay, filter, &winning_e,
+ &wp_err, &results);
+ if(filter){
+ fs_give((void **)&filter->filt);
+ fs_give((void **)&filter);
+ }
+ if(wpldap_global->ldap_search_list){
+ trl = wpldap_global->ldap_search_list->reslist;
+ for(ecnt = 0, e = ldap_first_entry(trl->ld, trl->res);
+ e != NULL; e = ldap_next_entry(trl->ld, e), ecnt++);
+ if(ecnt == 1) { /* found the one true name */
+ e = ldap_first_entry(trl->ld, trl->res);
+ peLdapEntryParse(trl, e, &cn, NULL, NULL, NULL,
+ NULL, NULL);
+ if(cn){
+ retstr = cpystr(cn[0]);
+ ldap_value_free(cn);
+ }
+ }
+ }
+ return(retstr);
+}
+
+int
+peLdapEntryParse(trl, e, ret_cn, ret_org, ret_unit,
+ ret_title, ret_mail, ret_sn)
+ LDAP_SERV_RES_S *trl;
+ LDAPMessage *e;
+ char ***ret_cn;
+ char ***ret_org;
+ char ***ret_unit;
+ char ***ret_title;
+ char ***ret_mail;
+ char ***ret_sn;
+{
+ char *a, **cn, **org, **unit, **title, **mail, **sn;
+ BerElement *ber;
+
+ cn = org = title = unit = mail = sn = NULL;
+
+ for(a = ldap_first_attribute(trl->ld, e, &ber);
+ a != NULL;
+ a = ldap_next_attribute(trl->ld, e, ber)){
+ dprint((9, " %s", a));
+ if(strcmp(a, trl->info_used->cnattr) == 0){
+ if(!cn)
+ cn = ldap_get_values(trl->ld, e, a);
+
+ if(cn && !(cn[0] && cn[0][0])){
+ ldap_value_free(cn);
+ cn = NULL;
+ }
+ }
+ else if(strcmp(a, trl->info_used->mailattr) == 0){
+ if(!mail)
+ mail = ldap_get_values(trl->ld, e, a);
+ }
+ else if(strcmp(a, "o") == 0){
+ if(!org)
+ org = ldap_get_values(trl->ld, e, a);
+ }
+ else if(strcmp(a, "ou") == 0){
+ if(!unit)
+ unit = ldap_get_values(trl->ld, e, a);
+ }
+ else if(strcmp(a, "title") == 0){
+ if(!title)
+ title = ldap_get_values(trl->ld, e, a);
+ }
+
+ our_ldap_memfree(a);
+ }
+
+ if(!cn){
+ for(a = ldap_first_attribute(trl->ld, e, &ber);
+ a != NULL;
+ a = ldap_next_attribute(trl->ld, e, ber)){
+
+ if(strcmp(a, trl->info_used->snattr) == 0){
+ if(!sn)
+ sn = ldap_get_values(trl->ld, e, a);
+
+ if(sn && !(sn[0] && sn[0][0])){
+ ldap_value_free(sn);
+ sn = NULL;
+ }
+ }
+ our_ldap_memfree(a);
+ }
+ }
+ if(ret_cn)
+ (*ret_cn) = cn;
+ else if(cn) ldap_value_free(cn);
+ if(ret_org)
+ (*ret_org) = org;
+ else if(org) ldap_value_free(org);
+ if(ret_unit)
+ (*ret_unit) = unit;
+ else if(unit) ldap_value_free(unit);
+ if(ret_title)
+ (*ret_title) = title;
+ else if(title) ldap_value_free(title);
+ if(ret_mail)
+ (*ret_mail) = mail;
+ else if(mail) ldap_value_free(mail);
+ if(ret_sn)
+ (*ret_sn) = sn;
+ else if(sn) ldap_value_free(sn);
+
+ return 0;
+}
+
+WPLDAPRES_S *
+free_wpldapres(wpldapr)
+ WPLDAPRES_S *wpldapr;
+{
+ WPLDAPRES_S *tmp1, *tmp2;
+
+ for(tmp1 = wpldapr; tmp1; tmp1 = tmp2){
+ tmp2 = tmp1->next;
+ if(tmp1->str)
+ fs_give((void **)&tmp1->str);
+ if(tmp1->reslist)
+ free_ldap_result_list(&tmp1->reslist);
+ }
+ fs_give((void **)&wpldapr);
+ return(NULL);
+}
+#endif /* ENABLE_LDAP */
diff --git a/web/src/alpined.d/ldap.h b/web/src/alpined.d/ldap.h
new file mode 100644
index 00000000..2ada2908
--- /dev/null
+++ b/web/src/alpined.d/ldap.h
@@ -0,0 +1,48 @@
+/*-----------------------------------------------------------------------
+ $Id: ldap.h 5 2006-01-04 17:53:54Z hubert $
+ -----------------------------------------------------------------------*/
+
+/* ========================================================================
+ * Copyright 2006-2008 University of Washington
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * ========================================================================
+ */
+
+#ifndef _WEB_ALPINE_LDAP_INCLUDED
+#define _WEB_ALPINE_LDAP_INCLUDED
+
+
+#ifdef ENABLE_LDAP
+
+#include "../../../pith/ldap.h"
+
+typedef struct wpldapres {
+ char *str;
+ LDAP_SERV_RES_S *reslist;
+ struct wpldapres *next;
+} WPLDAPRES_S;
+
+typedef struct wpldap {
+ int query_no;
+ WPLDAPRES_S *ldap_search_list;
+} WPLDAP_S;
+
+
+extern WPLDAP_S *wpldap_global;
+
+
+char *peLdapPname(char *, char *);
+int peLdapEntryParse(LDAP_SERV_RES_S *, LDAPMessage *,
+ char ***, char ***, char ***, char ***,
+ char ***, char ***);
+WPLDAPRES_S *free_wpldapres(WPLDAPRES_S *);
+
+#endif /* ENABLE_LDAP */
+
+#endif /* _WEB_ALPINE_LDAP_INCLUDED */
diff --git a/web/src/alpined.d/remote.c b/web/src/alpined.d/remote.c
new file mode 100644
index 00000000..3269a978
--- /dev/null
+++ b/web/src/alpined.d/remote.c
@@ -0,0 +1,78 @@
+#if !defined(lint) && !defined(DOS)
+static char rcsid[] = "$Id: remote.c 101 2006-08-10 22:53:04Z mikes@u.washington.edu $";
+#endif
+
+/* ========================================================================
+ * Copyright 2006-2007 University of Washington
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * ========================================================================
+ */
+
+#include <system.h>
+#include <general.h>
+
+#include "../../../c-client/c-client.h"
+
+#include "../../../pith/remote.h"
+#include "../../../pith/msgno.h"
+#include "../../../pith/filter.h"
+#include "../../../pith/util.h"
+#include "../../../pith/debug.h"
+#include "../../../pith/osdep/collate.h"
+
+
+/*
+ * Internal prototypes
+ */
+
+int
+rd_prompt_about_forged_remote_data(reason, rd, extra)
+ int reason;
+ REMDATA_S *rd;
+ char *extra;
+{
+ char tmp[2000];
+ char *unknown = "<unknown>";
+ char *foldertype, *foldername, *special;
+
+ /*
+ * Since we're web based the user doesn't have much recourse in the event of one of these
+ * weird errors, so we just report what happened and forge ahead
+ */
+
+ foldertype = (rd && rd->t.i.special_hdr && !strucmp(rd->t.i.special_hdr, REMOTE_ABOOK_SUBTYPE))
+ ? "address book"
+ : (rd && rd->t.i.special_hdr && !strucmp(rd->t.i.special_hdr, REMOTE_PINERC_SUBTYPE))
+ ? "configuration"
+ : "data";
+ foldername = (rd && rd->rn) ? rd->rn : unknown;
+ special = (rd && rd->t.i.special_hdr) ? rd->t.i.special_hdr : unknown;
+
+ dprint((1, "rd_check_out_forged_remote_data: reason: %d, type: $s, name: %s",
+ reason, foldertype ? foldertype : "?", foldername ? foldername : "?"));
+
+ if(rd && rd->flags & USER_SAID_NO)
+ return(-1);
+
+ if(reason == -2){
+ snprintf(tmp, sizeof(tmp), _("Missing \"%s\" header in remote pinerc"), special);
+ tmp[sizeof(tmp)-1] = '\0';
+ }
+ else if(reason == -1){
+ snprintf(tmp, sizeof(tmp), _("Unexpected \"Received\" header in remote pinerc"));
+ tmp[sizeof(tmp)-1] = '\0';
+ }
+ else if(reason >= 0){
+ snprintf(tmp, sizeof(tmp), _("Unexpected value \"%s: %s\" in remote pinerc"), special, (extra && *extra) ? extra : "?");
+ tmp[sizeof(tmp)-1] = '\0';
+ }
+
+ rd->flags |= USER_SAID_YES;
+ return(1);
+}
diff --git a/web/src/alpined.d/signal.c b/web/src/alpined.d/signal.c
new file mode 100644
index 00000000..3bdb7b41
--- /dev/null
+++ b/web/src/alpined.d/signal.c
@@ -0,0 +1,280 @@
+#if !defined(lint) && !defined(DOS)
+static char rcsid[] = "$Id: signal.c 91 2006-07-28 19:02:07Z mikes@u.washington.edu $";
+#endif
+
+/* ========================================================================
+ * Copyright 2006-2008 University of Washington
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * ========================================================================
+ */
+
+#include <system.h>
+#include <general.h>
+
+#include "../../../c-client/c-client.h"
+
+#include "../../../pith/conf.h"
+#include "../../../pith/status.h"
+#include "../../../pith/signal.h"
+#include "../../../pith/debug.h"
+#include "../../../pith/adrbklib.h"
+#include "../../../pith/remote.h"
+#include "../../../pith/imap.h"
+
+#include "alpined.h"
+#include "debug.h"
+
+
+static int cleanup_called_from_sig_handler;
+
+static RETSIGTYPE hup_signal(int);
+static RETSIGTYPE term_signal(int);
+static RETSIGTYPE auger_in_signal(int);
+int fast_clean_up();
+void end_signals(int);
+#if defined(DEBUG) && defined(SIGUSR1) && defined(SIGUSR2)
+static RETSIGTYPE usr1_signal(int);
+static RETSIGTYPE usr2_signal(int);
+#endif
+
+
+/*----------------------------------------------------------------------
+ Install handlers for all the signals we care to catch
+ ----------------------------------------------------------------------*/
+void
+init_signals(void)
+{
+ /* prepare for unexpected exit */
+ signal(SIGHUP, hup_signal);
+ signal(SIGTERM, term_signal);
+
+ /* prepare for unforseen problems */
+ signal(SIGILL, auger_in_signal);
+ signal(SIGTRAP, auger_in_signal);
+#ifdef SIGEMT
+ signal(SIGEMT, auger_in_signal);
+#endif
+ signal(SIGBUS, auger_in_signal);
+ signal(SIGSEGV, auger_in_signal);
+ signal(SIGSYS, auger_in_signal);
+
+#if defined(DEBUG) && defined(SIGUSR1) && defined(SIGUSR2)
+ /* Set up SIGUSR2 to {in,de}crement debug level */
+ signal(SIGUSR1, usr1_signal);
+ signal(SIGUSR2, usr2_signal);
+#endif
+}
+
+
+/*----------------------------------------------------------------------
+ handle hang up signal -- SIGHUP
+
+Not much to do. Rely on periodic mail file check pointing.
+ ----------------------------------------------------------------------*/
+static RETSIGTYPE
+hup_signal(int sig)
+{
+ end_signals(1);
+ cleanup_called_from_sig_handler = 1;
+
+ while(!fast_clean_up())
+ sleep(1);
+
+ if(peSocketName) /* clean up unix domain socket */
+ (void) unlink(peSocketName);
+
+ exceptional_exit("SIGHUP received", 0);
+}
+
+
+/*----------------------------------------------------------------------
+ handle terminate signal -- SIGTERM
+
+Not much to do. Rely on periodic mail file check pointing.
+ ----------------------------------------------------------------------*/
+static RETSIGTYPE
+term_signal(int sig)
+{
+ end_signals(1);
+ cleanup_called_from_sig_handler = 1;
+
+ while(!fast_clean_up())
+ sleep(1);
+
+ if(peSocketName) /* clean up unix domain socket */
+ (void) unlink(peSocketName);
+
+ exceptional_exit("SIGTERM received", 0);
+}
+
+
+/*----------------------------------------------------------------------
+ Handle signals caused by aborts -- SIGSEGV, SIGILL, etc
+
+Call panic which cleans up tty modes and then core dumps
+ ----------------------------------------------------------------------*/
+static RETSIGTYPE
+auger_in_signal(int sig)
+{
+ end_signals(1);
+
+ if(peSocketName) /* clean up unix domain socket */
+ (void) unlink(peSocketName);
+
+ snprintf(tmp_20k_buf, SIZEOF_20KBUF, "Abort: signal %d", sig);
+ panic(tmp_20k_buf); /* clean up and get out */
+ exit(-1); /* in case panic doesn't kill us */
+}
+
+
+/*----------------------------------------------------------------------
+ Handle cleaning up mail streams and tty modes...
+Not much to do. Rely on periodic mail file check pointing. Don't try
+cleaning up screen or flushing output since stdout is likely already
+gone. To be safe, though, we'll at least restore the original tty mode.
+Also delete any remnant _DATAFILE_ from sending-filters.
+ ----------------------------------------------------------------------*/
+int
+fast_clean_up(void)
+{
+ int i;
+ MAILSTREAM *m;
+
+ if(ps_global->expunge_in_progress)
+ return(0);
+
+ /*
+ * This gets rid of temporary cache files for remote addrbooks.
+ */
+ completely_done_with_adrbks();
+
+ /*
+ * This flushes out deferred changes and gets rid of temporary cache
+ * files for remote config files.
+ */
+ if(ps_global->prc){
+ if(ps_global->prc->outstanding_pinerc_changes)
+ write_pinerc(ps_global, Main,
+ cleanup_called_from_sig_handler ? WRP_NOUSER : WRP_NONE);
+
+ if(ps_global->prc->rd)
+ rd_close_remdata(&ps_global->prc->rd);
+
+ free_pinerc_s(&ps_global->prc);
+ }
+
+ /* as does this */
+ if(ps_global->post_prc){
+ if(ps_global->post_prc->outstanding_pinerc_changes)
+ write_pinerc(ps_global, Post,
+ cleanup_called_from_sig_handler ? WRP_NOUSER : WRP_NONE);
+
+ if(ps_global->post_prc->rd)
+ rd_close_remdata(&ps_global->post_prc->rd);
+
+ free_pinerc_s(&ps_global->post_prc);
+ }
+
+ /*
+ * Can't figure out why this section is inside the ifdef, but no
+ * harm leaving it that way, I guess.
+ */
+#if !defined(DOS) && !defined(OS2)
+ for(i = 0; i < ps_global->s_pool.nstream; i++){
+ m = ps_global->s_pool.streams[i];
+ if(m && !m->lock)
+ pine_mail_actually_close(m);
+ }
+#endif /* !DOS */
+
+ imap_flush_passwd_cache(TRUE);
+
+ if(peSocketName) /* clean up unix domain socket */
+ (void) unlink(peSocketName);
+
+ dprint((1, "done with fast_clean_up\n"));
+ return(1);
+}
+
+
+
+/*----------------------------------------------------------------------
+ Return all signal handling back to normal
+ ----------------------------------------------------------------------*/
+void
+end_signals(int blockem)
+{
+#ifndef SIG_ERR
+#define SIG_ERR (RETSIGTYPE (*)())-1
+#endif
+ if(signal(SIGILL, blockem ? SIG_IGN : SIG_DFL) != SIG_ERR){
+ signal(SIGTRAP, blockem ? SIG_IGN : SIG_DFL);
+#ifdef SIGEMT
+ signal(SIGEMT, blockem ? SIG_IGN : SIG_DFL);
+#endif
+ signal(SIGBUS, blockem ? SIG_IGN : SIG_DFL);
+ signal(SIGSEGV, blockem ? SIG_IGN : SIG_DFL);
+ signal(SIGSYS, blockem ? SIG_IGN : SIG_DFL);
+ signal(SIGHUP, blockem ? SIG_IGN : SIG_DFL);
+ signal(SIGTERM, blockem ? SIG_IGN : SIG_DFL);
+ signal(SIGINT, blockem ? SIG_IGN : SIG_DFL);
+ }
+}
+
+
+#if defined(DEBUG) && defined(SIGUSR1) && defined(SIGUSR2)
+/*----------------------------------------------------------------------
+ handle -- SIGUSR1
+
+ Increment debug level
+ ----------------------------------------------------------------------*/
+static RETSIGTYPE
+usr1_signal(int sig)
+{
+ if(debug < 11)
+ debug++;
+
+ setup_imap_debug();
+
+ dprint((SYSDBG_INFO, "Debug level now %d", debug));
+}
+
+/*----------------------------------------------------------------------
+ handle -- SIGUSR2
+
+ Decrement debug level
+ ----------------------------------------------------------------------*/
+static RETSIGTYPE
+usr2_signal(int sig)
+{
+ if(debug > 0)
+ debug--;
+
+ setup_imap_debug();
+
+ dprint((SYSDBG_INFO, "Debug level now %d", debug));
+
+}
+#endif
+
+
+/*
+ * Command interrupt support.
+ */
+int
+intr_handling_on(void)
+{
+ return 0;
+}
+
+
+void
+intr_handling_off(void)
+{
+}
diff --git a/web/src/alpined.d/signal.h b/web/src/alpined.d/signal.h
new file mode 100644
index 00000000..3ae6bd50
--- /dev/null
+++ b/web/src/alpined.d/signal.h
@@ -0,0 +1,15 @@
+/*-----------------------------------------------------------------------
+ $Id: signal.h 82 2006-07-12 23:36:59Z mikes@u.washington.edu $
+ -----------------------------------------------------------------------*/
+
+#ifndef _WEB_ALPINE_SIGNAL_INCLUDED
+#define _WEB_ALPINE_SIGNAL_INCLUDED
+
+
+/* exported protoypes */
+void init_signals(void);
+
+
+
+
+#endif /* _WEB_ALPINE_SIGNAL_INCLUDED */
diff --git a/web/src/alpined.d/status.c b/web/src/alpined.d/status.c
new file mode 100644
index 00000000..3c546d6f
--- /dev/null
+++ b/web/src/alpined.d/status.c
@@ -0,0 +1,78 @@
+#if !defined(lint) && !defined(DOS)
+static char rcsid[] = "$Id: status.c 1142 2008-08-13 17:22:21Z hubert@u.washington.edu $";
+#endif
+
+/* ========================================================================
+ * Copyright 2006-2007 University of Washington
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * ========================================================================
+ */
+
+/*======================================================================
+ status.c
+ Functions that manage the status line (third from the bottom)
+ - put messages on the queue to be displayed
+ - display messages on the queue with timers
+ - check queue to figure out next timeout
+ - prompt for yes/no type of questions
+ ====*/
+
+#include <system.h>
+#include <general.h>
+
+#include "../../../pith/status.h"
+#include "../../../pith/helptext.h"
+#include "../../../pith/debug.h"
+#include "../../../pith/string.h"
+
+#include "alpined.h"
+
+
+
+
+/*----------------------------------------------------------------------
+ Put a message for the status line on the queue
+ ----------*/
+void
+q_status_message(int flags, int min_time, int max_time, char *message)
+{
+ if(!(flags & SM_INFO))
+ sml_addmsg(0, message);
+}
+
+
+/*----------------------------------------------------------------------
+ Time remaining for current message's minimum display
+ ----*/
+int
+status_message_remaining(void)
+{
+ return(0);
+}
+
+
+
+/*----------------------------------------------------------------------
+ Update status line, clearing or displaying a message
+----------------------------------------------------------------------*/
+int
+display_message(UCS command)
+{
+ return(0);
+}
+
+
+
+/*----------------------------------------------------------------------
+ Display all the messages on the queue as quickly as possible
+ ----*/
+void
+flush_status_messages(int skip_last_pause)
+{
+}
diff --git a/web/src/alpined.d/stubs.c b/web/src/alpined.d/stubs.c
new file mode 100644
index 00000000..299c7c81
--- /dev/null
+++ b/web/src/alpined.d/stubs.c
@@ -0,0 +1,169 @@
+#if !defined(lint) && !defined(DOS)
+static char rcsid[] = "$Id: stubs.c 769 2007-10-24 00:15:40Z hubert@u.washington.edu $";
+#endif
+
+/* ========================================================================
+ * Copyright 2006-2007 University of Washington
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * ========================================================================
+ */
+
+#include <system.h>
+#include <general.h>
+
+#include "../../../c-client/c-client.h"
+
+#include "../../../pith/takeaddr.h"
+#include "../../../pith/ldap.h"
+#include "../../../pith/debug.h"
+#include "../../../pith/osdep/coredump.h"
+
+#include "alpined.h"
+
+
+/* let cleanup calls know we're screwed */
+static int in_panic = 0;
+
+/* input timeout */
+static int input_timeout = 0;
+
+/* time of last user-initiated newmail check */
+static time_t time_of_input;
+
+
+void
+peMarkInputTime(void)
+{
+ time_of_input = time((time_t *)0);
+}
+
+
+/********* ../../../pith/newmail.c stub **********/
+time_t
+time_of_last_input()
+{
+ return(time_of_input);
+}
+
+
+/********* ../../../pith/conf.c stub **********/
+int
+set_input_timeout(t)
+ int t;
+{
+ int old_t = input_timeout;
+
+ input_timeout = t;
+ return(old_t);
+}
+
+
+int
+get_input_timeout()
+{
+ return(input_timeout);
+}
+
+
+int
+unexpected_pinerc_change()
+{
+ dprint((1, "Unexpected pinerc change"));
+ return(0); /* always overwrite */
+}
+
+/********* ../../../pith/mailcap.c stub **********/
+int
+exec_mailcap_test_cmd(cmd)
+ char *cmd;
+{
+ return(-1); /* never succeeds on web server */
+}
+
+
+/****** various other stuff ******/
+/*----------------------------------------------------------------------
+ panic - call on detected programmatic errors to exit pine
+
+ Args: message -- message to record in debug file and to be printed for user
+
+ Result: The various tty modes are restored
+ If debugging is active a core dump will be generated
+ Exits Pine
+
+ This is also called from imap routines and fs_get and fs_resize.
+ ----*/
+void
+panic(message)
+ char *message;
+{
+ in_panic = 1;
+
+ syslog(LOG_ERR, message); /* may not work, but try */
+
+#if 0
+ if(ps_global)
+ peDestroyUserContext(&ps_global);
+#endif
+
+#ifdef DEBUG
+ if(debug > 1)
+ coredump(); /*--- If we're debugging get a core dump --*/
+#endif
+
+ exit(-1);
+ fatal("ffo"); /* BUG -- hack to get fatal out of library in right order*/
+}
+
+
+/*----------------------------------------------------------------------
+ panicking - called to test whether we're sunk
+
+ Args: none
+
+ ----*/
+int
+panicking()
+{
+ return(in_panic);
+}
+
+
+/*----------------------------------------------------------------------
+ exceptional_exit - called to exit under unusual conditions (with no core)
+
+ Args: message -- message to record in debug file and to be printed for user
+ ev -- exit value
+
+ ----*/
+void
+exceptional_exit(message, ev)
+ char *message;
+ int ev;
+{
+ syslog(LOG_ALERT, message);
+ exit(ev);
+}
+
+
+/*----------------------------------------------------------------------
+ write argument error to the display...
+
+ Args: none
+
+ Result: prints help messages
+ ----------------------------------------------------------------------*/
+void
+display_args_err(s, a, err)
+ char *s;
+ char **a;
+ int err;
+{
+ syslog(LOG_INFO, "Arg Error: %s", s);
+}
diff --git a/web/src/alpined.d/stubs.h b/web/src/alpined.d/stubs.h
new file mode 100644
index 00000000..8128a4aa
--- /dev/null
+++ b/web/src/alpined.d/stubs.h
@@ -0,0 +1,25 @@
+/*-----------------------------------------------------------------------
+ $Id: stubs.h 130 2006-09-22 04:39:36Z mikes@u.washington.edu $
+ -----------------------------------------------------------------------*/
+
+/* ========================================================================
+ * Copyright 2006-2007 University of Washington
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * ========================================================================
+ */
+
+#ifndef _WEB_ALPINE_STUBS_INCLUDED
+#define _WEB_ALPINE_STUBS_INCLUDED
+
+
+/* exported prototypes */
+void peMarkInputTime(void);
+
+
+#endif /* _WEB_ALPINE_STUBS_INCLUDED */
diff --git a/web/src/alpined.d/wpcomm.c b/web/src/alpined.d/wpcomm.c
new file mode 100644
index 00000000..b9f17073
--- /dev/null
+++ b/web/src/alpined.d/wpcomm.c
@@ -0,0 +1,197 @@
+#if !defined(lint) && !defined(DOS)
+static char rcsid[] = "$Id: wpcomm.c 1266 2009-07-14 18:39:12Z hubert@u.washington.edu $";
+#endif
+
+/* ========================================================================
+ * Copyright 2006-2007 University of Washington
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * ========================================================================
+ */
+
+
+#include <string.h>
+#include <tcl.h>
+#include <unistd.h>
+#include <errno.h>
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <sys/un.h>
+
+
+#define VERSION "0.1"
+
+#define READBUF 4096
+#define RESULT_MAX 16
+
+int append_rbuf(char *, char *, int);
+int WPSendCmd(ClientData, Tcl_Interp *, int, Tcl_Obj *CONST []);
+
+
+/*
+ * WPComm_Init - entry point for Web Alpine servlet communications.
+ *
+ * returns: TCL defined values for success or failure
+ *
+ */
+
+int
+Wpcomm_Init(Tcl_Interp *interp)
+{
+ if(Tcl_PkgRequire(interp, "Tcl", TCL_VERSION, 0) == NULL
+ && TCL_VERSION[0] == '7'
+ && Tcl_PkgRequire(interp, "Tcl", "8.0", 0) == NULL)
+ return TCL_ERROR;
+
+ if(Tcl_PkgProvide(interp, "WPComm", VERSION) != TCL_OK)
+ return TCL_ERROR;
+
+ Tcl_CreateObjCommand(interp, "WPSend", WPSendCmd,
+ (ClientData)NULL, (Tcl_CmdDeleteProc *)NULL);
+
+ return TCL_OK;
+}
+
+
+
+/*
+ * WPSendCmd - establish communication with the specified device,
+ * send the given command and return results to caller.
+ *
+ */
+int
+WPSendCmd(ClientData clientData, Tcl_Interp *interp, int objc, Tcl_Obj *CONST objv[])
+{
+ char buf[READBUF], lbuf[32], *errbuf = NULL, rbuf[RESULT_MAX], *fname, *cmd;
+ int s, i, n, b, rs, rv = TCL_ERROR, wlen;
+ struct sockaddr_un name;
+ Tcl_Obj *lObj;
+
+ errno = 0;
+
+ if(objc == 3
+ && (fname = Tcl_GetStringFromObj(objv[1], NULL))
+ && (cmd = Tcl_GetByteArrayFromObj(objv[2], &wlen))){
+ if((s = socket(AF_UNIX, SOCK_STREAM, 0)) == -1){
+ snprintf(errbuf = buf, sizeof(buf), "WPC: socket: %s", strerror(errno));
+ }
+ else{
+ name.sun_family = AF_UNIX;
+ strcpy(name.sun_path, fname);
+
+ if(connect(s, (struct sockaddr *) &name, sizeof(name)) == -1){
+ if(errno == ECONNREFUSED || errno == ENOENT)
+ snprintf(errbuf = buf, sizeof(buf), "WPC: Inactive session");
+ else
+ snprintf(errbuf = buf, sizeof(buf), "WPC: connect: %s", strerror(errno));
+ }
+ else if((n = wlen) != 0){
+ if(n < 0x7fffffff){
+ snprintf(lbuf, sizeof(lbuf), "%d\n", n);
+ i = strlen(lbuf);
+ if(write(s, lbuf, i) == i){
+ for(i = 0; n; n = n - i)
+ if((i = write(s, cmd + i, n)) == -1){
+ snprintf(errbuf = buf, sizeof(buf), "WPC: write: %s", strerror(errno));
+ break;
+ }
+ }
+ else
+ snprintf(errbuf = buf, sizeof(buf), "Can't write command length.");
+ }
+ else
+ snprintf(errbuf = buf, sizeof(buf), "Command too long.");
+
+ rbuf[0] = '\0';
+ rs = 0;
+ lObj = NULL;
+ while((n = read(s, buf, READBUF)) > 0)
+ if(!errbuf){
+ for(i = b = 0; i < n; i++)
+ if(buf[i] == '\n'){
+ if(rs){
+ Tcl_AppendToObj(Tcl_GetObjResult(interp), &buf[b], i - b);
+ Tcl_AppendToObj(Tcl_GetObjResult(interp), " ", 1);
+ }
+ else{
+ rs = 1;
+ if(append_rbuf(rbuf, &buf[b], i - b) < 0)
+ snprintf(errbuf = buf, sizeof(buf), "WPC: Response Code Overrun");
+ else if(!strcasecmp(rbuf,"OK"))
+ rv = TCL_OK;
+ else if(!strcasecmp(rbuf,"ERROR"))
+ rv = TCL_ERROR;
+ else if(!strcasecmp(rbuf,"BREAK"))
+ rv = TCL_BREAK;
+ else if(!strcasecmp(rbuf,"RETURN"))
+ rv = TCL_RETURN;
+ else
+ snprintf(errbuf = buf, sizeof(buf), "WPC: Unexpected response: %s", rbuf);
+ }
+
+ b = i + 1;
+ }
+
+ if(i - b > 0){
+ if(rs)
+ Tcl_AppendToObj(Tcl_GetObjResult(interp), &buf[b], i - b);
+ else if(append_rbuf(rbuf, &buf[b], i - b) < 0)
+ snprintf(errbuf = buf, sizeof(buf), "WPC: Response Code Overrun");
+ }
+ }
+
+ if(!errbuf){
+ if(n < 0){
+ snprintf(errbuf = buf, sizeof(buf), "WPC: read: %s", strerror(errno));
+ rv = TCL_ERROR;
+ }
+ else if(!rs){
+ if(n == 0)
+ snprintf(errbuf = buf, sizeof(buf), "WPC: Server connection closed (%d)", errno);
+ else
+ snprintf(errbuf = buf, sizeof(buf), "WPC: Invalid Response to \"%.*s\" (len %d) (%d): %.*s", 12, cmd, wlen, errno, RESULT_MAX, rbuf);
+
+ rv = TCL_ERROR;
+ }
+ else if(rv == TCL_ERROR){
+ char *s = Tcl_GetStringFromObj(Tcl_GetObjResult(interp), NULL);
+ if(!(s && *s))
+ snprintf(errbuf = buf, sizeof(buf), "WPC: Empty ERROR Response");
+ }
+ }
+ }
+
+ close(s);
+ }
+ }
+ else
+ snprintf(errbuf = buf, sizeof(buf), "Usage: %s path cmd", Tcl_GetStringFromObj(objv[0], NULL));
+
+ if(errbuf)
+ Tcl_SetResult(interp, errbuf, TCL_VOLATILE);
+
+ return(rv);
+}
+
+
+int
+append_rbuf(char *rbuf, char *b, int l)
+{
+ int i;
+
+ if(l + (i = strlen(rbuf)) > RESULT_MAX)
+ return(-1);
+
+ rbuf += i;
+
+ while(l--)
+ *rbuf++ = *b++;
+
+ *rbuf = '\0';
+ return(0);
+}